summaryrefslogtreecommitdiffstats
path: root/fluent-bit/tests
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 02:57:58 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 02:57:58 +0000
commitbe1c7e50e1e8809ea56f2c9d472eccd8ffd73a97 (patch)
tree9754ff1ca740f6346cf8483ec915d4054bc5da2d /fluent-bit/tests
parentInitial commit. (diff)
downloadnetdata-be1c7e50e1e8809ea56f2c9d472eccd8ffd73a97.tar.xz
netdata-be1c7e50e1e8809ea56f2c9d472eccd8ffd73a97.zip
Adding upstream version 1.44.3.upstream/1.44.3upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'fluent-bit/tests')
-rw-r--r--fluent-bit/tests/include/aws_client_mock.c256
-rw-r--r--fluent-bit/tests/include/aws_client_mock.h223
-rw-r--r--fluent-bit/tests/include/aws_client_mock_client_resp.def34
-rw-r--r--fluent-bit/tests/include/flb_tests_initialize_tls.h44
-rw-r--r--fluent-bit/tests/internal/CMakeLists.txt216
-rw-r--r--fluent-bit/tests/internal/README.md3
-rw-r--r--fluent-bit/tests/internal/avro.c382
-rw-r--r--fluent-bit/tests/internal/aws/CMakeLists.txt7
-rw-r--r--fluent-bit/tests/internal/aws/placeholder.c11
-rw-r--r--fluent-bit/tests/internal/aws_compress.c489
-rw-r--r--fluent-bit/tests/internal/aws_credentials.c382
-rw-r--r--fluent-bit/tests/internal/aws_credentials_ec2.c1037
-rw-r--r--fluent-bit/tests/internal/aws_credentials_http.c382
-rw-r--r--fluent-bit/tests/internal/aws_credentials_process.c496
-rw-r--r--fluent-bit/tests/internal/aws_credentials_profile.c405
-rw-r--r--fluent-bit/tests/internal/aws_credentials_sts.c939
-rw-r--r--fluent-bit/tests/internal/aws_credentials_test_internal.h42
-rw-r--r--fluent-bit/tests/internal/aws_util.c395
-rw-r--r--fluent-bit/tests/internal/base64.c50
-rw-r--r--fluent-bit/tests/internal/bucket_queue.c248
-rw-r--r--fluent-bit/tests/internal/config_format.c86
-rw-r--r--fluent-bit/tests/internal/config_format_fluentbit.c353
-rw-r--r--fluent-bit/tests/internal/config_format_yaml.c324
-rw-r--r--fluent-bit/tests/internal/config_map.c386
-rw-r--r--fluent-bit/tests/internal/crypto.c202
-rw-r--r--fluent-bit/tests/internal/csv.c167
-rw-r--r--fluent-bit/tests/internal/data/avro/json_single_map_001.json5
-rw-r--r--fluent-bit/tests/internal/data/avro/live-sample.json25
-rw-r--r--fluent-bit/tests/internal/data/avro/multiline.json27
-rwxr-xr-xfluent-bit/tests/internal/data/aws_credentials/credential_process/aws-credential-process14
-rw-r--r--fluent-bit/tests/internal/data/aws_credentials/shared_config.ini9
-rw-r--r--fluent-bit/tests/internal/data/aws_credentials/shared_credentials_file.ini28
-rw-r--r--fluent-bit/tests/internal/data/aws_credentials/shared_credentials_file_nodefault.ini12
-rw-r--r--fluent-bit/tests/internal/data/aws_credentials/web_identity_token_file.txt1
-rw-r--r--fluent-bit/tests/internal/data/config_format/classic/fluent-bit.conf35
-rw-r--r--fluent-bit/tests/internal/data/config_format/classic/indent_level_error.conf6
-rw-r--r--fluent-bit/tests/internal/data/config_format/classic/issue6281.conf2
-rw-r--r--fluent-bit/tests/internal/data/config_format/classic/issue6281_input.conf2
-rw-r--r--fluent-bit/tests/internal/data/config_format/classic/issue6281_output.conf2
-rw-r--r--fluent-bit/tests/internal/data/config_format/classic/issue_5880.conf14
-rw-r--r--fluent-bit/tests/internal/data/config_format/classic/nolimitline.conf11
-rw-r--r--fluent-bit/tests/internal/data/config_format/classic/recursion.conf1
-rw-r--r--fluent-bit/tests/internal/data/config_format/classic/service.conf4
-rw-r--r--fluent-bit/tests/internal/data/config_format/yaml/fluent-bit.yaml29
-rw-r--r--fluent-bit/tests/internal/data/config_format/yaml/issue_7559.yaml14
-rw-r--r--fluent-bit/tests/internal/data/config_format/yaml/parsers/parsers-conf.yaml3
-rw-r--r--fluent-bit/tests/internal/data/config_format/yaml/parsers/parsers.conf6
-rw-r--r--fluent-bit/tests/internal/data/config_format/yaml/pipelines/slist/even.yaml7
-rw-r--r--fluent-bit/tests/internal/data/config_format/yaml/pipelines/slist/odd.yaml8
-rw-r--r--fluent-bit/tests/internal/data/config_format/yaml/service.yaml5
-rw-r--r--fluent-bit/tests/internal/data/config_format/yaml/test.yaml31
-rw-r--r--fluent-bit/tests/internal/data/config_format/yaml/test/dummy_pipeline.yaml13
-rw-r--r--fluent-bit/tests/internal/data/config_format/yaml/test/nested.yaml7
-rw-r--r--fluent-bit/tests/internal/data/file/empty_file.txt0
-rw-r--r--fluent-bit/tests/internal/data/file/text_file.txt5
-rw-r--r--fluent-bit/tests/internal/data/input_chunk/log/a_thousand_plus_one_bytes.log2
-rw-r--r--fluent-bit/tests/internal/data/input_chunk/log/test_buffer_drop_chunks.h27
-rw-r--r--fluent-bit/tests/internal/data/input_chunk/log/test_buffer_valid.log3
-rw-r--r--fluent-bit/tests/internal/data/input_chunk/out/a_thousand_plus_one_bytes.out0
-rw-r--r--fluent-bit/tests/internal/data/input_chunk/out/test_buffer_valid.out3
-rw-r--r--fluent-bit/tests/internal/data/input_chunk/parser.conf10
-rw-r--r--fluent-bit/tests/internal/data/mp/apache_10k.mpbin0 -> 993663 bytes
-rw-r--r--fluent-bit/tests/internal/data/pack/README.md3
-rw-r--r--fluent-bit/tests/internal/data/pack/bug342.json240
-rw-r--r--fluent-bit/tests/internal/data/pack/dup_keys_in.json1
-rw-r--r--fluent-bit/tests/internal/data/pack/dup_keys_out.json1
-rw-r--r--fluent-bit/tests/internal/data/pack/json_single_map_001.json5
-rw-r--r--fluent-bit/tests/internal/data/pack/json_single_map_002.json4
-rw-r--r--fluent-bit/tests/internal/data/pack/mixed.py31
-rw-r--r--fluent-bit/tests/internal/data/pack/mixed_001.json1
-rw-r--r--fluent-bit/tests/internal/data/pack/mixed_001.mp1
-rw-r--r--fluent-bit/tests/internal/data/pack/mixed_001.txt1
-rw-r--r--fluent-bit/tests/internal/data/pack/mixed_002.json1
-rw-r--r--fluent-bit/tests/internal/data/pack/mixed_002.mpbin0 -> 37 bytes
-rw-r--r--fluent-bit/tests/internal/data/pack/mixed_002.txt7
-rw-r--r--fluent-bit/tests/internal/data/pack/mixed_003.json1
-rw-r--r--fluent-bit/tests/internal/data/pack/mixed_003.mp1
-rw-r--r--fluent-bit/tests/internal/data/pack/mixed_003.txt1
-rw-r--r--fluent-bit/tests/internal/data/pack/utf8_bell.json1
-rw-r--r--fluent-bit/tests/internal/data/pack/utf8_bell.mp1
-rw-r--r--fluent-bit/tests/internal/data/pack/utf8_bell.txt1
-rw-r--r--fluent-bit/tests/internal/data/pack/utf8_copyright.json1
-rw-r--r--fluent-bit/tests/internal/data/pack/utf8_copyright.mp1
-rw-r--r--fluent-bit/tests/internal/data/pack/utf8_copyright.txt1
-rw-r--r--fluent-bit/tests/internal/data/pack/utf8_gen.py32
-rw-r--r--fluent-bit/tests/internal/data/pack/utf8_hokke.json1
-rw-r--r--fluent-bit/tests/internal/data/pack/utf8_hokke.mp1
-rw-r--r--fluent-bit/tests/internal/data/pack/utf8_hokke.txt1
-rw-r--r--fluent-bit/tests/internal/data/pack/utf8_relaxed.json1
-rw-r--r--fluent-bit/tests/internal/data/pack/utf8_relaxed.mp1
-rw-r--r--fluent-bit/tests/internal/data/pack/utf8_relaxed.txt1
-rw-r--r--fluent-bit/tests/internal/data/parser/json.conf200
-rw-r--r--fluent-bit/tests/internal/data/parser/regex.conf234
-rw-r--r--fluent-bit/tests/internal/data/reload/fluent-bit.conf15
-rw-r--r--fluent-bit/tests/internal/data/reload/yaml/processor.yaml41
-rw-r--r--fluent-bit/tests/internal/data/s3_local_buffer/.gitkeep1
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/LICENSE202
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/NOTICE2
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-header-key-duplicate/get-header-key-duplicate.authz1
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-header-key-duplicate/get-header-key-duplicate.creq9
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-header-key-duplicate/get-header-key-duplicate.req6
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-header-key-duplicate/get-header-key-duplicate.sreq7
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-header-key-duplicate/get-header-key-duplicate.sts4
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-header-value-multiline/get-header-value-multiline.authz1
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-header-value-multiline/get-header-value-multiline.creq9
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-header-value-multiline/get-header-value-multiline.req6
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-header-value-multiline/get-header-value-multiline.sreq7
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-header-value-multiline/get-header-value-multiline.sts4
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-header-value-order/get-header-value-order.authz1
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-header-value-order/get-header-value-order.creq9
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-header-value-order/get-header-value-order.req7
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-header-value-order/get-header-value-order.sreq8
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-header-value-order/get-header-value-order.sts4
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-header-value-trim/get-header-value-trim.authz1
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-header-value-trim/get-header-value-trim.creq10
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-header-value-trim/get-header-value-trim.req5
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-header-value-trim/get-header-value-trim.sreq6
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-header-value-trim/get-header-value-trim.sts4
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-unreserved/get-unreserved.authz1
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-unreserved/get-unreserved.creq8
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-unreserved/get-unreserved.req3
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-unreserved/get-unreserved.sreq4
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-unreserved/get-unreserved.sts4
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-utf8/get-utf8.authz1
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-utf8/get-utf8.creq8
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-utf8/get-utf8.req3
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-utf8/get-utf8.sreq4
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-utf8/get-utf8.sts4
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-empty-query-key/get-vanilla-empty-query-key.authz1
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-empty-query-key/get-vanilla-empty-query-key.creq8
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-empty-query-key/get-vanilla-empty-query-key.req3
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-empty-query-key/get-vanilla-empty-query-key.sreq4
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-empty-query-key/get-vanilla-empty-query-key.sts4
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-query-order-key-case/get-vanilla-query-order-key-case.authz1
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-query-order-key-case/get-vanilla-query-order-key-case.creq8
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-query-order-key-case/get-vanilla-query-order-key-case.req3
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-query-order-key-case/get-vanilla-query-order-key-case.sreq4
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-query-order-key-case/get-vanilla-query-order-key-case.sts4
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-query-order-key/get-vanilla-query-order-key.authz1
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-query-order-key/get-vanilla-query-order-key.creq8
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-query-order-key/get-vanilla-query-order-key.req3
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-query-order-key/get-vanilla-query-order-key.sreq4
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-query-order-key/get-vanilla-query-order-key.sts4
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-query-order-value/get-vanilla-query-order-value.authz1
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-query-order-value/get-vanilla-query-order-value.creq8
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-query-order-value/get-vanilla-query-order-value.req3
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-query-order-value/get-vanilla-query-order-value.sreq4
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-query-order-value/get-vanilla-query-order-value.sts4
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-query-unreserved/get-vanilla-query-unreserved.authz1
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-query-unreserved/get-vanilla-query-unreserved.creq8
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-query-unreserved/get-vanilla-query-unreserved.req3
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-query-unreserved/get-vanilla-query-unreserved.sreq4
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-query-unreserved/get-vanilla-query-unreserved.sts4
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-query/get-vanilla-query.authz1
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-query/get-vanilla-query.creq8
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-query/get-vanilla-query.req3
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-query/get-vanilla-query.sreq4
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-query/get-vanilla-query.sts4
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-utf8-query/get-vanilla-utf8-query.authz1
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-utf8-query/get-vanilla-utf8-query.creq8
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-utf8-query/get-vanilla-utf8-query.req3
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-utf8-query/get-vanilla-utf8-query.sreq4
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-utf8-query/get-vanilla-utf8-query.sts4
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla/get-vanilla.authz1
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla/get-vanilla.creq8
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla/get-vanilla.req3
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla/get-vanilla.sreq4
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla/get-vanilla.sts4
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-relative-relative/get-relative-relative.authz1
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-relative-relative/get-relative-relative.creq8
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-relative-relative/get-relative-relative.req3
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-relative-relative/get-relative-relative.sreq4
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-relative-relative/get-relative-relative.sts4
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-relative/get-relative.authz1
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-relative/get-relative.creq8
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-relative/get-relative.req3
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-relative/get-relative.sreq4
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-relative/get-relative.sts4
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-slash-dot-slash/get-slash-dot-slash.authz1
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-slash-dot-slash/get-slash-dot-slash.creq8
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-slash-dot-slash/get-slash-dot-slash.req3
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-slash-dot-slash/get-slash-dot-slash.sreq4
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-slash-dot-slash/get-slash-dot-slash.sts4
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-slash-pointless-dot/get-slash-pointless-dot.authz1
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-slash-pointless-dot/get-slash-pointless-dot.creq8
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-slash-pointless-dot/get-slash-pointless-dot.req3
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-slash-pointless-dot/get-slash-pointless-dot.sreq4
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-slash-pointless-dot/get-slash-pointless-dot.sts4
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-slash/get-slash.authz1
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-slash/get-slash.creq8
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-slash/get-slash.req3
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-slash/get-slash.sreq4
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-slash/get-slash.sts4
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-slashes/get-slashes.authz1
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-slashes/get-slashes.creq8
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-slashes/get-slashes.req3
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-slashes/get-slashes.sreq4
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-slashes/get-slashes.sts4
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-space/get-space.authz1
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-space/get-space.creq8
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-space/get-space.req3
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-space/get-space.sreq4
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-space/get-space.sts4
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/normalize-path.txt3
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-header-key-case/post-header-key-case.authz1
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-header-key-case/post-header-key-case.creq8
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-header-key-case/post-header-key-case.req3
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-header-key-case/post-header-key-case.sreq4
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-header-key-case/post-header-key-case.sts4
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-header-key-sort/post-header-key-sort.authz1
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-header-key-sort/post-header-key-sort.creq9
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-header-key-sort/post-header-key-sort.req4
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-header-key-sort/post-header-key-sort.sreq5
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-header-key-sort/post-header-key-sort.sts4
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-header-value-case/post-header-value-case.authz1
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-header-value-case/post-header-value-case.creq9
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-header-value-case/post-header-value-case.req4
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-header-value-case/post-header-value-case.sreq5
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-header-value-case/post-header-value-case.sts4
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-sts-token/post-sts-header-after/post-sts-header-after.authz1
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-sts-token/post-sts-header-after/post-sts-header-after.creq8
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-sts-token/post-sts-header-after/post-sts-header-after.req3
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-sts-token/post-sts-header-after/post-sts-header-after.sreq5
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-sts-token/post-sts-header-after/post-sts-header-after.sts4
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-sts-token/post-sts-header-before/post-sts-header-before.authz1
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-sts-token/post-sts-header-before/post-sts-header-before.creq9
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-sts-token/post-sts-header-before/post-sts-header-before.req4
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-sts-token/post-sts-header-before/post-sts-header-before.sreq5
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-sts-token/post-sts-header-before/post-sts-header-before.sts4
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-sts-token/readme.txt15
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-vanilla-empty-query-value/post-vanilla-empty-query-value.authz1
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-vanilla-empty-query-value/post-vanilla-empty-query-value.creq8
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-vanilla-empty-query-value/post-vanilla-empty-query-value.req3
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-vanilla-empty-query-value/post-vanilla-empty-query-value.sreq4
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-vanilla-empty-query-value/post-vanilla-empty-query-value.sts4
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-vanilla-query/post-vanilla-query.authz1
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-vanilla-query/post-vanilla-query.creq8
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-vanilla-query/post-vanilla-query.req3
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-vanilla-query/post-vanilla-query.sreq4
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-vanilla-query/post-vanilla-query.sts4
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-vanilla/post-vanilla.authz1
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-vanilla/post-vanilla.creq8
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-vanilla/post-vanilla.req3
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-vanilla/post-vanilla.sreq4
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-vanilla/post-vanilla.sts4
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-x-www-form-urlencoded-parameters/post-x-www-form-urlencoded-parameters.authz1
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-x-www-form-urlencoded-parameters/post-x-www-form-urlencoded-parameters.creq9
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-x-www-form-urlencoded-parameters/post-x-www-form-urlencoded-parameters.req6
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-x-www-form-urlencoded-parameters/post-x-www-form-urlencoded-parameters.sreq7
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-x-www-form-urlencoded-parameters/post-x-www-form-urlencoded-parameters.sts4
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-x-www-form-urlencoded/post-x-www-form-urlencoded.authz1
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-x-www-form-urlencoded/post-x-www-form-urlencoded.creq9
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-x-www-form-urlencoded/post-x-www-form-urlencoded.req4
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-x-www-form-urlencoded/post-x-www-form-urlencoded.sreq4
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-x-www-form-urlencoded/post-x-www-form-urlencoded.sts4
-rwxr-xr-xfluent-bit/tests/internal/data/stream_processor/gen_msgpack.sh40
-rw-r--r--fluent-bit/tests/internal/data/stream_processor/samples-hw/1.json4
-rw-r--r--fluent-bit/tests/internal/data/stream_processor/samples-hw/1.mpbin0 -> 257 bytes
-rw-r--r--fluent-bit/tests/internal/data/stream_processor/samples-hw/2.json4
-rw-r--r--fluent-bit/tests/internal/data/stream_processor/samples-hw/2.mpbin0 -> 268 bytes
-rw-r--r--fluent-bit/tests/internal/data/stream_processor/samples-hw/3.json3
-rw-r--r--fluent-bit/tests/internal/data/stream_processor/samples-hw/3.mpbin0 -> 201 bytes
-rw-r--r--fluent-bit/tests/internal/data/stream_processor/samples-hw/4.json4
-rw-r--r--fluent-bit/tests/internal/data/stream_processor/samples-hw/4.mpbin0 -> 260 bytes
-rw-r--r--fluent-bit/tests/internal/data/stream_processor/samples-hw/5.json4
-rw-r--r--fluent-bit/tests/internal/data/stream_processor/samples-hw/5.mpbin0 -> 272 bytes
-rw-r--r--fluent-bit/tests/internal/data/stream_processor/samples-hw/6.json3
-rw-r--r--fluent-bit/tests/internal/data/stream_processor/samples-hw/6.mpbin0 -> 204 bytes
-rw-r--r--fluent-bit/tests/internal/data/stream_processor/samples-hw/7.json4
-rw-r--r--fluent-bit/tests/internal/data/stream_processor/samples-hw/7.mpbin0 -> 262 bytes
-rw-r--r--fluent-bit/tests/internal/data/stream_processor/samples-subkeys.json9
-rw-r--r--fluent-bit/tests/internal/data/stream_processor/samples-subkeys.mpbin0 -> 479 bytes
-rw-r--r--fluent-bit/tests/internal/data/stream_processor/samples.json11
-rw-r--r--fluent-bit/tests/internal/data/stream_processor/samples.mpbin0 -> 726 bytes
-rw-r--r--fluent-bit/tests/internal/data/tls/certificate.pem22
-rw-r--r--fluent-bit/tests/internal/data/tls/private_key.pem28
-rw-r--r--fluent-bit/tests/internal/env.c91
-rw-r--r--fluent-bit/tests/internal/error_reporter.c82
-rw-r--r--fluent-bit/tests/internal/file.c48
-rw-r--r--fluent-bit/tests/internal/flb_event_loop.c592
-rw-r--r--fluent-bit/tests/internal/flb_tests_internal.h.in43
-rw-r--r--fluent-bit/tests/internal/flb_time.c335
-rw-r--r--fluent-bit/tests/internal/fstore.c84
-rw-r--r--fluent-bit/tests/internal/fuzzers/CMakeLists.txt60
-rw-r--r--fluent-bit/tests/internal/fuzzers/aws_credentials_fuzzer.c132
-rw-r--r--fluent-bit/tests/internal/fuzzers/aws_util_fuzzer.c107
-rw-r--r--fluent-bit/tests/internal/fuzzers/base64_fuzzer.c20
-rw-r--r--fluent-bit/tests/internal/fuzzers/cmetrics_decode_fuzz.c69
-rw-r--r--fluent-bit/tests/internal/fuzzers/config_fuzzer.c417
-rw-r--r--fluent-bit/tests/internal/fuzzers/config_map_fuzzer.c212
-rw-r--r--fluent-bit/tests/internal/fuzzers/config_random_fuzzer.c57
-rw-r--r--fluent-bit/tests/internal/fuzzers/config_yaml_fuzzer.c65
-rw-r--r--fluent-bit/tests/internal/fuzzers/ctrace_fuzzer.c24
-rw-r--r--fluent-bit/tests/internal/fuzzers/engine_fuzzer.c171
-rw-r--r--fluent-bit/tests/internal/fuzzers/filter_stdout_fuzzer.c62
-rw-r--r--fluent-bit/tests/internal/fuzzers/flb_fuzz_header.h42
-rw-r--r--fluent-bit/tests/internal/fuzzers/flb_json_fuzzer.c82
-rw-r--r--fluent-bit/tests/internal/fuzzers/flb_mp_fuzzer.c55
-rw-r--r--fluent-bit/tests/internal/fuzzers/fstore_fuzzer.c86
-rw-r--r--fluent-bit/tests/internal/fuzzers/http_fuzzer.c116
-rw-r--r--fluent-bit/tests/internal/fuzzers/input_fuzzer.c164
-rw-r--r--fluent-bit/tests/internal/fuzzers/local_test.c85
-rw-r--r--fluent-bit/tests/internal/fuzzers/msgpack_parse_fuzzer.c30
-rw-r--r--fluent-bit/tests/internal/fuzzers/msgpack_to_gelf_fuzzer.c25
-rw-r--r--fluent-bit/tests/internal/fuzzers/multiline_fuzzer.c180
-rw-r--r--fluent-bit/tests/internal/fuzzers/pack_json_state_fuzzer.c25
-rw-r--r--fluent-bit/tests/internal/fuzzers/parse_json_fuzzer.c69
-rw-r--r--fluent-bit/tests/internal/fuzzers/parse_logfmt_fuzzer.c50
-rw-r--r--fluent-bit/tests/internal/fuzzers/parse_ltsv_fuzzer.c35
-rw-r--r--fluent-bit/tests/internal/fuzzers/parser_fuzzer.c203
-rw-r--r--fluent-bit/tests/internal/fuzzers/record_ac_fuzzer.c101
-rw-r--r--fluent-bit/tests/internal/fuzzers/signv4_fuzzer.c111
-rw-r--r--fluent-bit/tests/internal/fuzzers/strp_fuzzer.c35
-rw-r--r--fluent-bit/tests/internal/fuzzers/utils_fuzzer.c242
-rw-r--r--fluent-bit/tests/internal/gelf.c101
-rw-r--r--fluent-bit/tests/internal/gzip.c46
-rw-r--r--fluent-bit/tests/internal/hash.c165
-rw-r--r--fluent-bit/tests/internal/hashtable.c413
-rw-r--r--fluent-bit/tests/internal/hmac.c136
-rw-r--r--fluent-bit/tests/internal/http_client.c478
-rw-r--r--fluent-bit/tests/internal/include/sp_cb_functions.h655
-rw-r--r--fluent-bit/tests/internal/include/sp_helpers.h158
-rw-r--r--fluent-bit/tests/internal/include/sp_invalid_queries.h20
-rw-r--r--fluent-bit/tests/internal/include/sp_select_keys.h131
-rw-r--r--fluent-bit/tests/internal/include/sp_select_subkeys.h84
-rw-r--r--fluent-bit/tests/internal/include/sp_snapshot.h38
-rw-r--r--fluent-bit/tests/internal/include/sp_window.h53
-rw-r--r--fluent-bit/tests/internal/input_chunk.c516
-rw-r--r--fluent-bit/tests/internal/log.c154
-rw-r--r--fluent-bit/tests/internal/log_event_decoder.c281
-rw-r--r--fluent-bit/tests/internal/lua.c325
-rw-r--r--fluent-bit/tests/internal/metrics.c83
-rw-r--r--fluent-bit/tests/internal/mp.c353
-rw-r--r--fluent-bit/tests/internal/multiline.c1478
-rw-r--r--fluent-bit/tests/internal/network.c164
-rw-r--r--fluent-bit/tests/internal/pack.c914
-rw-r--r--fluent-bit/tests/internal/parser.c528
-rw-r--r--fluent-bit/tests/internal/parser_json.c547
-rw-r--r--fluent-bit/tests/internal/parser_logfmt.c398
-rw-r--r--fluent-bit/tests/internal/parser_ltsv.c486
-rw-r--r--fluent-bit/tests/internal/parser_regex.c491
-rw-r--r--fluent-bit/tests/internal/pipe.c89
-rw-r--r--fluent-bit/tests/internal/processor.c125
-rw-r--r--fluent-bit/tests/internal/random.c33
-rw-r--r--fluent-bit/tests/internal/record_accessor.c1731
-rw-r--r--fluent-bit/tests/internal/regex.c330
-rw-r--r--fluent-bit/tests/internal/reload.c249
-rw-r--r--fluent-bit/tests/internal/ring_buffer.c168
-rw-r--r--fluent-bit/tests/internal/router.c55
-rw-r--r--fluent-bit/tests/internal/sds.c88
-rw-r--r--fluent-bit/tests/internal/sds_list.c238
-rw-r--r--fluent-bit/tests/internal/signv4.c666
-rw-r--r--fluent-bit/tests/internal/slist.c205
-rw-r--r--fluent-bit/tests/internal/stream_processor.c934
-rw-r--r--fluent-bit/tests/internal/typecast.c359
-rw-r--r--fluent-bit/tests/internal/unit_sizes.c59
-rw-r--r--fluent-bit/tests/internal/uri.c87
-rw-r--r--fluent-bit/tests/internal/utils.c622
-rw-r--r--fluent-bit/tests/lib/acutest/README.md4
-rw-r--r--fluent-bit/tests/lib/acutest/acutest.h1839
-rw-r--r--fluent-bit/tests/lib/shunit2/.gitignore3
-rw-r--r--fluent-bit/tests/lib/shunit2/.travis.yml29
-rw-r--r--fluent-bit/tests/lib/shunit2/CODE_OF_CONDUCT.md46
-rw-r--r--fluent-bit/tests/lib/shunit2/LICENSE201
-rw-r--r--fluent-bit/tests/lib/shunit2/README.md636
-rw-r--r--fluent-bit/tests/lib/shunit2/doc/CHANGES-2.1.md261
-rw-r--r--fluent-bit/tests/lib/shunit2/doc/RELEASE_NOTES-2.1.0.txt104
-rw-r--r--fluent-bit/tests/lib/shunit2/doc/RELEASE_NOTES-2.1.1.txt88
-rw-r--r--fluent-bit/tests/lib/shunit2/doc/RELEASE_NOTES-2.1.2.txt83
-rw-r--r--fluent-bit/tests/lib/shunit2/doc/RELEASE_NOTES-2.1.3.txt84
-rw-r--r--fluent-bit/tests/lib/shunit2/doc/RELEASE_NOTES-2.1.4.txt100
-rw-r--r--fluent-bit/tests/lib/shunit2/doc/RELEASE_NOTES-2.1.5.txt128
-rw-r--r--fluent-bit/tests/lib/shunit2/doc/RELEASE_NOTES-2.1.6.txt112
-rw-r--r--fluent-bit/tests/lib/shunit2/doc/RELEASE_NOTES-2.1.7.md66
-rw-r--r--fluent-bit/tests/lib/shunit2/doc/RELEASE_NOTES-2.1.8.md56
-rw-r--r--fluent-bit/tests/lib/shunit2/doc/TODO.txt13
-rw-r--r--fluent-bit/tests/lib/shunit2/doc/contributors.md15
-rw-r--r--fluent-bit/tests/lib/shunit2/doc/design_doc.txt34
-rwxr-xr-xfluent-bit/tests/lib/shunit2/examples/equality_test.sh9
-rwxr-xr-xfluent-bit/tests/lib/shunit2/examples/lineno_test.sh15
-rw-r--r--fluent-bit/tests/lib/shunit2/examples/math.inc17
-rwxr-xr-xfluent-bit/tests/lib/shunit2/examples/math_test.sh25
-rwxr-xr-xfluent-bit/tests/lib/shunit2/examples/mkdir_test.sh79
-rwxr-xr-xfluent-bit/tests/lib/shunit2/examples/mock_file.sh80
-rwxr-xr-xfluent-bit/tests/lib/shunit2/examples/mock_file_test.sh33
-rwxr-xr-xfluent-bit/tests/lib/shunit2/examples/party_test.sh16
-rwxr-xr-xfluent-bit/tests/lib/shunit2/examples/suite_test.sh32
-rw-r--r--fluent-bit/tests/lib/shunit2/lib/shflags1222
-rwxr-xr-xfluent-bit/tests/lib/shunit2/lib/versions273
-rwxr-xr-xfluent-bit/tests/lib/shunit2/shunit21343
-rwxr-xr-xfluent-bit/tests/lib/shunit2/shunit2_args_test.sh59
-rwxr-xr-xfluent-bit/tests/lib/shunit2/shunit2_asserts_test.sh258
-rwxr-xr-xfluent-bit/tests/lib/shunit2/shunit2_failures_test.sh85
-rwxr-xr-xfluent-bit/tests/lib/shunit2/shunit2_macros_test.sh265
-rwxr-xr-xfluent-bit/tests/lib/shunit2/shunit2_misc_test.sh315
-rwxr-xr-xfluent-bit/tests/lib/shunit2/shunit2_standalone_test.sh38
-rw-r--r--fluent-bit/tests/lib/shunit2/shunit2_test_helpers234
-rwxr-xr-xfluent-bit/tests/lib/shunit2/test_runner171
-rw-r--r--fluent-bit/tests/runtime/CMakeLists.txt173
-rw-r--r--fluent-bit/tests/runtime/config_map_opts.c35
-rw-r--r--fluent-bit/tests/runtime/core-timeout.c94
-rw-r--r--fluent-bit/tests/runtime/core_chunk_trace.c153
-rw-r--r--fluent-bit/tests/runtime/core_engine.c167
-rw-r--r--fluent-bit/tests/runtime/core_log.c308
-rw-r--r--fluent-bit/tests/runtime/custom_calyptia_test.c59
-rwxr-xr-xfluent-bit/tests/runtime/data/common/json_invalid.h6
-rwxr-xr-xfluent-bit/tests/runtime/data/common/json_long.h1006
-rwxr-xr-xfluent-bit/tests/runtime/data/common/json_small.h256
l---------fluent-bit/tests/runtime/data/common/parsers.conf1
-rw-r--r--fluent-bit/tests/runtime/data/datadog/json.h12
-rwxr-xr-xfluent-bit/tests/runtime/data/es/json_es.h17
-rw-r--r--fluent-bit/tests/runtime/data/in_elasticsearch/json_bulk.h25
-rw-r--r--fluent-bit/tests/runtime/data/kubernetes/log/annotations-exclude/annotations-exclude_default_text.log2
-rw-r--r--fluent-bit/tests/runtime/data/kubernetes/log/annotations-exclude/annotations-exclude_invalid_text.log2
-rw-r--r--fluent-bit/tests/runtime/data/kubernetes/log/annotations-exclude/annotations-exclude_multiple-1_container-1.log2
-rw-r--r--fluent-bit/tests/runtime/data/kubernetes/log/annotations-exclude/annotations-exclude_multiple-1_container-2.log2
-rw-r--r--fluent-bit/tests/runtime/data/kubernetes/log/annotations-exclude/annotations-exclude_multiple-1_container-3.log2
-rw-r--r--fluent-bit/tests/runtime/data/kubernetes/log/annotations-exclude/annotations-exclude_multiple-1_container-4.log2
-rw-r--r--fluent-bit/tests/runtime/data/kubernetes/log/annotations-exclude/annotations-exclude_multiple-2_container-1.log2
-rw-r--r--fluent-bit/tests/runtime/data/kubernetes/log/annotations-exclude/annotations-exclude_multiple-2_container-2.log2
-rw-r--r--fluent-bit/tests/runtime/data/kubernetes/log/annotations-exclude/annotations-exclude_multiple-2_container-3.log2
-rw-r--r--fluent-bit/tests/runtime/data/kubernetes/log/annotations-exclude/annotations-exclude_multiple-2_container-4.log2
-rw-r--r--fluent-bit/tests/runtime/data/kubernetes/log/annotations-exclude/annotations-exclude_multiple-3_container-1.log2
-rw-r--r--fluent-bit/tests/runtime/data/kubernetes/log/annotations-exclude/annotations-exclude_multiple-3_container-2.log2
-rw-r--r--fluent-bit/tests/runtime/data/kubernetes/log/annotations-exclude/annotations-exclude_multiple-3_container-3.log2
-rw-r--r--fluent-bit/tests/runtime/data/kubernetes/log/annotations-exclude/annotations-exclude_multiple-3_container-4.log2
-rw-r--r--fluent-bit/tests/runtime/data/kubernetes/log/annotations-exclude/annotations-exclude_multiple-4_container-1.log2
-rw-r--r--fluent-bit/tests/runtime/data/kubernetes/log/annotations-exclude/annotations-exclude_multiple-4_container-2.log2
-rw-r--r--fluent-bit/tests/runtime/data/kubernetes/log/annotations-exclude/annotations-exclude_multiple-4_container-3.log2
-rw-r--r--fluent-bit/tests/runtime/data/kubernetes/log/annotations-exclude/annotations-exclude_multiple-4_container-4.log2
-rw-r--r--fluent-bit/tests/runtime/data/kubernetes/log/annotations-exclude/annotations-exclude_stderr_text.log2
-rw-r--r--fluent-bit/tests/runtime/data/kubernetes/log/annotations-exclude/annotations-exclude_stdout_text.log2
-rw-r--r--fluent-bit/tests/runtime/data/kubernetes/log/annotations-parser/annotations-parser_invalid_text.log2
-rw-r--r--fluent-bit/tests/runtime/data/kubernetes/log/annotations-parser/annotations-parser_json-with-time_invalid-json-1.log1
-rw-r--r--fluent-bit/tests/runtime/data/kubernetes/log/annotations-parser/annotations-parser_json-with-time_json.log1
-rw-r--r--fluent-bit/tests/runtime/data/kubernetes/log/annotations-parser/annotations-parser_multiple-1_container-1.log2
-rw-r--r--fluent-bit/tests/runtime/data/kubernetes/log/annotations-parser/annotations-parser_multiple-1_container-2.log2
-rw-r--r--fluent-bit/tests/runtime/data/kubernetes/log/annotations-parser/annotations-parser_multiple-1_container-3.log2
-rw-r--r--fluent-bit/tests/runtime/data/kubernetes/log/annotations-parser/annotations-parser_multiple-1_container-4.log2
-rw-r--r--fluent-bit/tests/runtime/data/kubernetes/log/annotations-parser/annotations-parser_multiple-1_container-5.log2
-rw-r--r--fluent-bit/tests/runtime/data/kubernetes/log/annotations-parser/annotations-parser_multiple-2_container-1.log2
-rw-r--r--fluent-bit/tests/runtime/data/kubernetes/log/annotations-parser/annotations-parser_multiple-2_container-2.log2
-rw-r--r--fluent-bit/tests/runtime/data/kubernetes/log/annotations-parser/annotations-parser_multiple-2_container-3.log2
-rw-r--r--fluent-bit/tests/runtime/data/kubernetes/log/annotations-parser/annotations-parser_multiple-2_container-4.log2
-rw-r--r--fluent-bit/tests/runtime/data/kubernetes/log/annotations-parser/annotations-parser_multiple-2_container-5.log2
-rw-r--r--fluent-bit/tests/runtime/data/kubernetes/log/annotations-parser/annotations-parser_regex-with-time_invalid-text-1.log1
-rw-r--r--fluent-bit/tests/runtime/data/kubernetes/log/annotations-parser/annotations-parser_regex-with-time_text.log1
-rw-r--r--fluent-bit/tests/runtime/data/kubernetes/log/annotations-parser/annotations-parser_stderr_text.log2
-rw-r--r--fluent-bit/tests/runtime/data/kubernetes/log/annotations-parser/annotations-parser_stdout_text.log2
-rw-r--r--fluent-bit/tests/runtime/data/kubernetes/log/annotations/annotations_invalid_text.log1
-rw-r--r--fluent-bit/tests/runtime/data/kubernetes/log/core/core_base_fluent-bit.log1
-rw-r--r--fluent-bit/tests/runtime/data/kubernetes/log/core/core_no-meta_text.log1
-rw-r--r--fluent-bit/tests/runtime/data/kubernetes/log/core/core_unescaping_json.log1
-rw-r--r--fluent-bit/tests/runtime/data/kubernetes/log/core/core_unescaping_text.log1
-rw-r--r--fluent-bit/tests/runtime/data/kubernetes/log/options/options_k8s-logging-exclude-disabled_text.log2
-rw-r--r--fluent-bit/tests/runtime/data/kubernetes/log/options/options_k8s-logging-parser-disabled_text.log2
-rw-r--r--fluent-bit/tests/runtime/data/kubernetes/log/options/options_keep-log-disabled_json.log1
-rw-r--r--fluent-bit/tests/runtime/data/kubernetes/log/options/options_keep-log-enabled_json.log1
-rw-r--r--fluent-bit/tests/runtime/data/kubernetes/log/options/options_merge-log-disabled_json.log1
-rw-r--r--fluent-bit/tests/runtime/data/kubernetes/log/options/options_merge-log-enabled_invalid-json.log1
-rw-r--r--fluent-bit/tests/runtime/data/kubernetes/log/options/options_merge-log-enabled_json.log1
-rw-r--r--fluent-bit/tests/runtime/data/kubernetes/log/options/options_merge-log-enabled_text.log1
-rw-r--r--fluent-bit/tests/runtime/data/kubernetes/log/options/options_merge-log-key_json.log1
-rw-r--r--fluent-bit/tests/runtime/data/kubernetes/log/options/options_merge-log-trim-disabled_json.log1
-rw-r--r--fluent-bit/tests/runtime/data/kubernetes/log/options/options_merge-log-trim-enabled_json.log1
-rw-r--r--fluent-bit/tests/runtime/data/kubernetes/log/options/options_use-kubelet-disabled_fluent-bit.log1
-rw-r--r--fluent-bit/tests/runtime/data/kubernetes/log/options/options_use-kubelet-enabled_fluent-bit.log1
-rw-r--r--fluent-bit/tests/runtime/data/kubernetes/meta/annotations-exclude_default.meta7
-rw-r--r--fluent-bit/tests/runtime/data/kubernetes/meta/annotations-exclude_invalid.meta7
-rw-r--r--fluent-bit/tests/runtime/data/kubernetes/meta/annotations-exclude_multiple-1.meta9
-rw-r--r--fluent-bit/tests/runtime/data/kubernetes/meta/annotations-exclude_multiple-2.meta10
-rw-r--r--fluent-bit/tests/runtime/data/kubernetes/meta/annotations-exclude_multiple-3.meta11
-rw-r--r--fluent-bit/tests/runtime/data/kubernetes/meta/annotations-exclude_multiple-4.meta11
-rw-r--r--fluent-bit/tests/runtime/data/kubernetes/meta/annotations-exclude_stderr.meta7
-rw-r--r--fluent-bit/tests/runtime/data/kubernetes/meta/annotations-exclude_stdout.meta7
-rw-r--r--fluent-bit/tests/runtime/data/kubernetes/meta/annotations-parser_invalid.meta7
-rw-r--r--fluent-bit/tests/runtime/data/kubernetes/meta/annotations-parser_json-with-time.meta7
-rw-r--r--fluent-bit/tests/runtime/data/kubernetes/meta/annotations-parser_json-without-time.meta7
-rw-r--r--fluent-bit/tests/runtime/data/kubernetes/meta/annotations-parser_multiple-1.meta12
-rw-r--r--fluent-bit/tests/runtime/data/kubernetes/meta/annotations-parser_multiple-2.meta13
-rw-r--r--fluent-bit/tests/runtime/data/kubernetes/meta/annotations-parser_regex-with-time.meta7
-rw-r--r--fluent-bit/tests/runtime/data/kubernetes/meta/annotations-parser_regex-without-time.meta7
-rw-r--r--fluent-bit/tests/runtime/data/kubernetes/meta/annotations-parser_stderr.meta7
-rw-r--r--fluent-bit/tests/runtime/data/kubernetes/meta/annotations-parser_stdout.meta7
-rw-r--r--fluent-bit/tests/runtime/data/kubernetes/meta/annotations_invalid.meta12
-rw-r--r--fluent-bit/tests/runtime/data/kubernetes/meta/core_base.meta116
-rw-r--r--fluent-bit/tests/runtime/data/kubernetes/meta/core_no-meta.meta1
-rw-r--r--fluent-bit/tests/runtime/data/kubernetes/meta/core_unescaping.meta4
-rw-r--r--fluent-bit/tests/runtime/data/kubernetes/meta/default_kairosdb-914055854-b63vq.meta155
-rw-r--r--fluent-bit/tests/runtime/data/kubernetes/meta/options_k8s-logging-exclude-disabled.meta7
-rw-r--r--fluent-bit/tests/runtime/data/kubernetes/meta/options_k8s-logging-parser-disabled.meta7
-rw-r--r--fluent-bit/tests/runtime/data/kubernetes/meta/options_keep-log-disabled.meta4
-rw-r--r--fluent-bit/tests/runtime/data/kubernetes/meta/options_keep-log-enabled.meta4
-rw-r--r--fluent-bit/tests/runtime/data/kubernetes/meta/options_merge-log-disabled.meta4
-rw-r--r--fluent-bit/tests/runtime/data/kubernetes/meta/options_merge-log-enabled.meta4
-rw-r--r--fluent-bit/tests/runtime/data/kubernetes/meta/options_merge-log-key.meta4
-rw-r--r--fluent-bit/tests/runtime/data/kubernetes/meta/options_merge-log-trim-disabled.meta4
-rw-r--r--fluent-bit/tests/runtime/data/kubernetes/meta/options_merge-log-trim-enabled.meta4
-rw-r--r--fluent-bit/tests/runtime/data/kubernetes/meta/options_use-kubelet-disabled.meta116
-rw-r--r--fluent-bit/tests/runtime/data/kubernetes/meta/options_use-kubelet-enabled.meta109
-rw-r--r--fluent-bit/tests/runtime/data/kubernetes/out/annotations-exclude/annotations-exclude_invalid_text_stderr.out1
-rw-r--r--fluent-bit/tests/runtime/data/kubernetes/out/annotations-exclude/annotations-exclude_invalid_text_stdout.out1
-rw-r--r--fluent-bit/tests/runtime/data/kubernetes/out/annotations-exclude/annotations-exclude_multiple-1_container-2_stderr.out1
-rw-r--r--fluent-bit/tests/runtime/data/kubernetes/out/annotations-exclude/annotations-exclude_multiple-1_container-3_stdout.out1
-rw-r--r--fluent-bit/tests/runtime/data/kubernetes/out/annotations-exclude/annotations-exclude_multiple-1_container-4_stderr.out1
-rw-r--r--fluent-bit/tests/runtime/data/kubernetes/out/annotations-exclude/annotations-exclude_multiple-1_container-4_stdout.out1
-rw-r--r--fluent-bit/tests/runtime/data/kubernetes/out/annotations-exclude/annotations-exclude_multiple-2_container-2_stderr.out1
-rw-r--r--fluent-bit/tests/runtime/data/kubernetes/out/annotations-exclude/annotations-exclude_multiple-2_container-3_stdout.out1
-rw-r--r--fluent-bit/tests/runtime/data/kubernetes/out/annotations-exclude/annotations-exclude_multiple-2_container-4_stderr.out1
-rw-r--r--fluent-bit/tests/runtime/data/kubernetes/out/annotations-exclude/annotations-exclude_multiple-2_container-4_stdout.out1
-rw-r--r--fluent-bit/tests/runtime/data/kubernetes/out/annotations-exclude/annotations-exclude_multiple-3_container-2_stderr.out1
-rw-r--r--fluent-bit/tests/runtime/data/kubernetes/out/annotations-exclude/annotations-exclude_multiple-3_container-3_stdout.out1
-rw-r--r--fluent-bit/tests/runtime/data/kubernetes/out/annotations-exclude/annotations-exclude_multiple-3_container-4_stderr.out1
-rw-r--r--fluent-bit/tests/runtime/data/kubernetes/out/annotations-exclude/annotations-exclude_multiple-3_container-4_stdout.out1
-rw-r--r--fluent-bit/tests/runtime/data/kubernetes/out/annotations-exclude/annotations-exclude_multiple-4_container-2_stderr.out1
-rw-r--r--fluent-bit/tests/runtime/data/kubernetes/out/annotations-exclude/annotations-exclude_multiple-4_container-3_stdout.out1
-rw-r--r--fluent-bit/tests/runtime/data/kubernetes/out/annotations-exclude/annotations-exclude_multiple-4_container-4_stderr.out1
-rw-r--r--fluent-bit/tests/runtime/data/kubernetes/out/annotations-exclude/annotations-exclude_multiple-4_container-4_stdout.out1
-rw-r--r--fluent-bit/tests/runtime/data/kubernetes/out/annotations-exclude/annotations-exclude_stderr_text_stdout.out1
-rw-r--r--fluent-bit/tests/runtime/data/kubernetes/out/annotations-exclude/annotations-exclude_stdout_text_stderr.out1
-rw-r--r--fluent-bit/tests/runtime/data/kubernetes/out/annotations-parser/annotations-parser_invalid_text_stderr.out1
-rw-r--r--fluent-bit/tests/runtime/data/kubernetes/out/annotations-parser/annotations-parser_invalid_text_stdout.out1
-rw-r--r--fluent-bit/tests/runtime/data/kubernetes/out/annotations-parser/annotations-parser_json-with-time_invalid-json-1.out1
-rw-r--r--fluent-bit/tests/runtime/data/kubernetes/out/annotations-parser/annotations-parser_json-with-time_json.out1
-rw-r--r--fluent-bit/tests/runtime/data/kubernetes/out/annotations-parser/annotations-parser_multiple-1_container-1_stderr.out1
-rw-r--r--fluent-bit/tests/runtime/data/kubernetes/out/annotations-parser/annotations-parser_multiple-1_container-1_stdout.out1
-rw-r--r--fluent-bit/tests/runtime/data/kubernetes/out/annotations-parser/annotations-parser_multiple-1_container-2_stderr.out1
-rw-r--r--fluent-bit/tests/runtime/data/kubernetes/out/annotations-parser/annotations-parser_multiple-1_container-2_stdout.out1
-rw-r--r--fluent-bit/tests/runtime/data/kubernetes/out/annotations-parser/annotations-parser_multiple-1_container-3_stderr.out1
-rw-r--r--fluent-bit/tests/runtime/data/kubernetes/out/annotations-parser/annotations-parser_multiple-1_container-3_stdout.out1
-rw-r--r--fluent-bit/tests/runtime/data/kubernetes/out/annotations-parser/annotations-parser_multiple-1_container-4_stderr.out1
-rw-r--r--fluent-bit/tests/runtime/data/kubernetes/out/annotations-parser/annotations-parser_multiple-1_container-4_stdout.out1
-rw-r--r--fluent-bit/tests/runtime/data/kubernetes/out/annotations-parser/annotations-parser_multiple-1_container-5_stderr.out1
-rw-r--r--fluent-bit/tests/runtime/data/kubernetes/out/annotations-parser/annotations-parser_multiple-1_container-5_stdout.out1
-rw-r--r--fluent-bit/tests/runtime/data/kubernetes/out/annotations-parser/annotations-parser_multiple-2_container-1_stderr.out1
-rw-r--r--fluent-bit/tests/runtime/data/kubernetes/out/annotations-parser/annotations-parser_multiple-2_container-1_stdout.out1
-rw-r--r--fluent-bit/tests/runtime/data/kubernetes/out/annotations-parser/annotations-parser_multiple-2_container-2_stderr.out1
-rw-r--r--fluent-bit/tests/runtime/data/kubernetes/out/annotations-parser/annotations-parser_multiple-2_container-2_stdout.out1
-rw-r--r--fluent-bit/tests/runtime/data/kubernetes/out/annotations-parser/annotations-parser_multiple-2_container-3_stderr.out1
-rw-r--r--fluent-bit/tests/runtime/data/kubernetes/out/annotations-parser/annotations-parser_multiple-2_container-3_stdout.out1
-rw-r--r--fluent-bit/tests/runtime/data/kubernetes/out/annotations-parser/annotations-parser_multiple-2_container-4_stderr.out1
-rw-r--r--fluent-bit/tests/runtime/data/kubernetes/out/annotations-parser/annotations-parser_multiple-2_container-4_stdout.out1
-rw-r--r--fluent-bit/tests/runtime/data/kubernetes/out/annotations-parser/annotations-parser_multiple-2_container-5_stderr.out1
-rw-r--r--fluent-bit/tests/runtime/data/kubernetes/out/annotations-parser/annotations-parser_multiple-2_container-5_stdout.out1
-rw-r--r--fluent-bit/tests/runtime/data/kubernetes/out/annotations-parser/annotations-parser_regex-with-time_invalid-text-1.out1
-rw-r--r--fluent-bit/tests/runtime/data/kubernetes/out/annotations-parser/annotations-parser_regex-with-time_text.out1
-rw-r--r--fluent-bit/tests/runtime/data/kubernetes/out/annotations-parser/annotations-parser_stderr_text_stderr.out1
-rw-r--r--fluent-bit/tests/runtime/data/kubernetes/out/annotations-parser/annotations-parser_stderr_text_stdout.out1
-rw-r--r--fluent-bit/tests/runtime/data/kubernetes/out/annotations-parser/annotations-parser_stdout_text_stderr.out1
-rw-r--r--fluent-bit/tests/runtime/data/kubernetes/out/annotations-parser/annotations-parser_stdout_text_stdout.out1
-rw-r--r--fluent-bit/tests/runtime/data/kubernetes/out/annotations/annotations_invalid_text.out1
-rw-r--r--fluent-bit/tests/runtime/data/kubernetes/out/core/core_base_fluent-bit.out1
-rw-r--r--fluent-bit/tests/runtime/data/kubernetes/out/core/core_no-meta_text.out1
-rw-r--r--fluent-bit/tests/runtime/data/kubernetes/out/core/core_unescaping_json.out1
-rw-r--r--fluent-bit/tests/runtime/data/kubernetes/out/core/core_unescaping_text.out1
-rw-r--r--fluent-bit/tests/runtime/data/kubernetes/out/kairosdb-914055854-b63vq.out1
-rw-r--r--fluent-bit/tests/runtime/data/kubernetes/out/options/options_k8s-logging-exclude-disabled_text_stderr.out1
-rw-r--r--fluent-bit/tests/runtime/data/kubernetes/out/options/options_k8s-logging-exclude-disabled_text_stdout.out1
-rw-r--r--fluent-bit/tests/runtime/data/kubernetes/out/options/options_k8s-logging-parser-disabled_text_stderr.out1
-rw-r--r--fluent-bit/tests/runtime/data/kubernetes/out/options/options_k8s-logging-parser-disabled_text_stdout.out1
-rw-r--r--fluent-bit/tests/runtime/data/kubernetes/out/options/options_keep-log-disabled_json.out1
-rw-r--r--fluent-bit/tests/runtime/data/kubernetes/out/options/options_keep-log-enabled_json.out1
-rw-r--r--fluent-bit/tests/runtime/data/kubernetes/out/options/options_merge-log-disabled_json.out1
-rw-r--r--fluent-bit/tests/runtime/data/kubernetes/out/options/options_merge-log-enabled_invalid-json.out1
-rw-r--r--fluent-bit/tests/runtime/data/kubernetes/out/options/options_merge-log-enabled_json.out1
-rw-r--r--fluent-bit/tests/runtime/data/kubernetes/out/options/options_merge-log-enabled_text.out1
-rw-r--r--fluent-bit/tests/runtime/data/kubernetes/out/options/options_merge-log-key_json.out1
-rw-r--r--fluent-bit/tests/runtime/data/kubernetes/out/options/options_merge-log-trim-disabled_json.out1
-rw-r--r--fluent-bit/tests/runtime/data/kubernetes/out/options/options_merge-log-trim-enabled_json.out1
-rw-r--r--fluent-bit/tests/runtime/data/kubernetes/out/options/options_use-kubelet-disabled_fluent-bit.out1
-rw-r--r--fluent-bit/tests/runtime/data/kubernetes/out/options/options_use-kubelet-enabled_fluent-bit.out1
-rw-r--r--fluent-bit/tests/runtime/data/kubernetes/parsers.conf88
-rw-r--r--fluent-bit/tests/runtime/data/loki/labelmap.json10
-rw-r--r--fluent-bit/tests/runtime/data/podman/cgroupv2/42/net/dev6
-rw-r--r--fluent-bit/tests/runtime/data/podman/cgroupv2/cgroup.controllers0
-rw-r--r--fluent-bit/tests/runtime/data/podman/cgroupv2/config.json1
-rw-r--r--fluent-bit/tests/runtime/data/podman/cgroupv2/libpod-8a19d6058bfbe88cd0548eba9047d94c70161f5d74b545c7504b2f27491686d9/containers/cgroup.procs1
-rw-r--r--fluent-bit/tests/runtime/data/podman/cgroupv2/libpod-8a19d6058bfbe88cd0548eba9047d94c70161f5d74b545c7504b2f27491686d9/memory.current1
-rw-r--r--fluent-bit/tests/runtime/data/podman/garbage/42/net/dev1
-rw-r--r--fluent-bit/tests/runtime/data/podman/garbage/config.json1
-rw-r--r--fluent-bit/tests/runtime/data/podman/garbage/libpod-8a19d6058bfbe88cd0548eba9047d94c70161f5d74b545c7504b2f27491686d9/memory.current1
-rw-r--r--fluent-bit/tests/runtime/data/podman/garbage/memory/libpod-8a19d6058bfbe88cd0548eba9047d94c70161f5d74b545c7504b2f27491686d9/memory.usage_in_bytes1
-rw-r--r--fluent-bit/tests/runtime/data/podman/garbage_config/config.json1
-rw-r--r--fluent-bit/tests/runtime/data/podman/no_config/libpod-8a19d6058bfbe88cd0548eba9047d94c70161f5d74b545c7504b2f27491686d9/memory.current1
-rw-r--r--fluent-bit/tests/runtime/data/podman/no_config/libpod-8a19d6058bfbe88cd0548eba9047d94c70161f5d74b545c7504b2f27491686d9/memory.usage_in_bytes1
-rw-r--r--fluent-bit/tests/runtime/data/podman/no_config/memory/libpod-8a19d6058bfbe88cd0548eba9047d94c70161f5d74b545c7504b2f27491686d9/memory.usage_in_bytes1
-rw-r--r--fluent-bit/tests/runtime/data/podman/no_proc/config.json1
-rw-r--r--fluent-bit/tests/runtime/data/podman/no_proc/libpod-8a19d6058bfbe88cd0548eba9047d94c70161f5d74b545c7504b2f27491686d9/memory.current1
-rw-r--r--fluent-bit/tests/runtime/data/podman/no_proc/memory/libpod-8a19d6058bfbe88cd0548eba9047d94c70161f5d74b545c7504b2f27491686d9/memory.usage_in_bytes1
-rw-r--r--fluent-bit/tests/runtime/data/podman/no_sysfs/42/net/dev6
-rw-r--r--fluent-bit/tests/runtime/data/podman/no_sysfs/config.json1
-rw-r--r--fluent-bit/tests/runtime/data/podman/regular/42/net/dev6
-rw-r--r--fluent-bit/tests/runtime/data/podman/regular/config.json1
-rw-r--r--fluent-bit/tests/runtime/data/podman/regular/libpod-8a19d6058bfbe88cd0548eba9047d94c70161f5d74b545c7504b2f27491686d9/memory.current1
-rw-r--r--fluent-bit/tests/runtime/data/podman/regular/memory/libpod-8a19d6058bfbe88cd0548eba9047d94c70161f5d74b545c7504b2f27491686d9/memory.usage_in_bytes1
-rw-r--r--fluent-bit/tests/runtime/data/podman/regular/systemd/libpod-8a19d6058bfbe88cd0548eba9047d94c70161f5d74b545c7504b2f27491686d9/cgroup.procs3
-rw-r--r--fluent-bit/tests/runtime/data/podman/reversed/42/net/dev6
-rw-r--r--fluent-bit/tests/runtime/data/podman/reversed/config.json1
-rw-r--r--fluent-bit/tests/runtime/data/podman/reversed/libpod-8a19d6058bfbe88cd0548eba9047d94c70161f5d74b545c7504b2f27491686d9/memory.current1
-rw-r--r--fluent-bit/tests/runtime/data/podman/reversed/memory/libpod-8a19d6058bfbe88cd0548eba9047d94c70161f5d74b545c7504b2f27491686d9/memory.usage_in_bytes1
-rw-r--r--fluent-bit/tests/runtime/data/podman/reversed/systemd/libpod-8a19d6058bfbe88cd0548eba9047d94c70161f5d74b545c7504b2f27491686d9/cgroup.procs3
-rwxr-xr-xfluent-bit/tests/runtime/data/stackdriver/json.h12
-rw-r--r--fluent-bit/tests/runtime/data/stackdriver/stackdriver-credentials.json12
-rw-r--r--fluent-bit/tests/runtime/data/stackdriver/stackdriver_multi_entries_severity.log4
-rw-r--r--fluent-bit/tests/runtime/data/stackdriver/stackdriver_test_http_request.h134
-rw-r--r--fluent-bit/tests/runtime/data/stackdriver/stackdriver_test_insert_id.h26
-rw-r--r--fluent-bit/tests/runtime/data/stackdriver/stackdriver_test_k8s_resource.h89
-rw-r--r--fluent-bit/tests/runtime/data/stackdriver/stackdriver_test_labels.h44
-rw-r--r--fluent-bit/tests/runtime/data/stackdriver/stackdriver_test_log_name.h10
-rw-r--r--fluent-bit/tests/runtime/data/stackdriver/stackdriver_test_monitored_resource.h46
-rw-r--r--fluent-bit/tests/runtime/data/stackdriver/stackdriver_test_operation.h62
-rw-r--r--fluent-bit/tests/runtime/data/stackdriver/stackdriver_test_resource_labels.h37
-rw-r--r--fluent-bit/tests/runtime/data/stackdriver/stackdriver_test_source_location.h70
-rw-r--r--fluent-bit/tests/runtime/data/stackdriver/stackdriver_test_span_id.h6
-rw-r--r--fluent-bit/tests/runtime/data/stackdriver/stackdriver_test_timestamp.h62
-rw-r--r--fluent-bit/tests/runtime/data/stackdriver/stackdriver_test_trace.h6
-rw-r--r--fluent-bit/tests/runtime/data/stackdriver/stackdriver_test_trace_sampled.h11
-rw-r--r--fluent-bit/tests/runtime/data/tail/log/3943.log2
-rw-r--r--fluent-bit/tests/runtime/data/tail/log/dockermode.log3
-rw-r--r--fluent-bit/tests/runtime/data/tail/log/dockermode_firstline_detection.log9
-rw-r--r--fluent-bit/tests/runtime/data/tail/log/dockermode_multiple_lines.log6
-rw-r--r--fluent-bit/tests/runtime/data/tail/log/dockermode_splitted_line.log6
-rw-r--r--fluent-bit/tests/runtime/data/tail/log/dockermode_splitted_multiple_lines.log8
-rw-r--r--fluent-bit/tests/runtime/data/tail/log/multiline_001.log6
-rw-r--r--fluent-bit/tests/runtime/data/tail/out/3943.out2
-rw-r--r--fluent-bit/tests/runtime/data/tail/out/dockermode.out3
-rw-r--r--fluent-bit/tests/runtime/data/tail/out/dockermode_firstline_detection.out5
-rw-r--r--fluent-bit/tests/runtime/data/tail/out/dockermode_multiple_lines.out2
-rw-r--r--fluent-bit/tests/runtime/data/tail/out/dockermode_splitted_line.out2
-rw-r--r--fluent-bit/tests/runtime/data/tail/out/dockermode_splitted_multiple_lines.out2
-rw-r--r--fluent-bit/tests/runtime/data/tail/out/multiline_001.out1
-rw-r--r--fluent-bit/tests/runtime/data/tail/out/skip_long_lines.out2
-rw-r--r--fluent-bit/tests/runtime/data/tail/parsers.conf10
-rw-r--r--fluent-bit/tests/runtime/data/tail/parsers_multiline_json.conf24
-rwxr-xr-xfluent-bit/tests/runtime/data/td/json_td.h506
-rw-r--r--fluent-bit/tests/runtime/data/tls/certificate.pem22
-rw-r--r--fluent-bit/tests/runtime/data/tls/private_key.pem28
-rwxr-xr-xfluent-bit/tests/runtime/data/wasm/append_tag.wasmbin0 -> 512323 bytes
-rwxr-xr-xfluent-bit/tests/runtime/data/wasm/drop_record.wasmbin0 -> 36610 bytes
-rwxr-xr-xfluent-bit/tests/runtime/data/wasm/modify_record.wasmbin0 -> 512134 bytes
-rwxr-xr-xfluent-bit/tests/runtime/data/wasm/numeric_records.wasmbin0 -> 517441 bytes
-rwxr-xr-xfluent-bit/tests/runtime/data/wasm/say_hello.wasmbin0 -> 390348 bytes
-rw-r--r--fluent-bit/tests/runtime/filter_aws.c846
-rw-r--r--fluent-bit/tests/runtime/filter_checklist.c405
-rw-r--r--fluent-bit/tests/runtime/filter_ecs.c445
-rw-r--r--fluent-bit/tests/runtime/filter_expect.c555
-rw-r--r--fluent-bit/tests/runtime/filter_grep.c840
-rw-r--r--fluent-bit/tests/runtime/filter_kubernetes.c1086
-rw-r--r--fluent-bit/tests/runtime/filter_kubernetes.md69
-rw-r--r--fluent-bit/tests/runtime/filter_log_to_metrics.c698
-rw-r--r--fluent-bit/tests/runtime/filter_lua.c984
-rw-r--r--fluent-bit/tests/runtime/filter_modify.c1674
-rw-r--r--fluent-bit/tests/runtime/filter_multiline.c699
-rw-r--r--fluent-bit/tests/runtime/filter_nest.c302
-rw-r--r--fluent-bit/tests/runtime/filter_parser.c833
-rw-r--r--fluent-bit/tests/runtime/filter_record_modifier.c595
-rw-r--r--fluent-bit/tests/runtime/filter_rewrite_tag.c564
-rw-r--r--fluent-bit/tests/runtime/filter_stdout.c84
-rw-r--r--fluent-bit/tests/runtime/filter_throttle.c105
-rw-r--r--fluent-bit/tests/runtime/filter_throttle_size.c608
-rw-r--r--fluent-bit/tests/runtime/filter_type_converter.c389
-rw-r--r--fluent-bit/tests/runtime/filter_wasm.c468
-rw-r--r--fluent-bit/tests/runtime/flb_tests_runtime.h.in52
-rwxr-xr-xfluent-bit/tests/runtime/gen_data.py63
-rw-r--r--fluent-bit/tests/runtime/http_callbacks.c58
l---------fluent-bit/tests/runtime/in_cpu.c1
l---------fluent-bit/tests/runtime/in_disk.c1
l---------fluent-bit/tests/runtime/in_dummy.c1
-rw-r--r--fluent-bit/tests/runtime/in_elasticsearch.c899
-rw-r--r--fluent-bit/tests/runtime/in_event_test.c35
-rw-r--r--fluent-bit/tests/runtime/in_fluentbit_metrics.c226
-rw-r--r--fluent-bit/tests/runtime/in_forward.c579
l---------fluent-bit/tests/runtime/in_head.c1
-rw-r--r--fluent-bit/tests/runtime/in_http.c444
l---------fluent-bit/tests/runtime/in_mem.c1
-rw-r--r--fluent-bit/tests/runtime/in_mqtt.c396
-rw-r--r--fluent-bit/tests/runtime/in_netif.c350
-rw-r--r--fluent-bit/tests/runtime/in_opentelemetry.c376
-rw-r--r--fluent-bit/tests/runtime/in_podman_metrics.c222
l---------fluent-bit/tests/runtime/in_proc.c1
l---------fluent-bit/tests/runtime/in_random.c1
-rw-r--r--fluent-bit/tests/runtime/in_simple_systems.c576
-rw-r--r--fluent-bit/tests/runtime/in_splunk.c824
-rw-r--r--fluent-bit/tests/runtime/in_statsd.c324
-rw-r--r--fluent-bit/tests/runtime/in_syslog.c1023
-rw-r--r--fluent-bit/tests/runtime/in_tail.c1583
-rw-r--r--fluent-bit/tests/runtime/in_tcp.c561
-rw-r--r--fluent-bit/tests/runtime/in_udp.c440
-rw-r--r--fluent-bit/tests/runtime/out_cloudwatch.c362
-rw-r--r--fluent-bit/tests/runtime/out_counter.c126
-rw-r--r--fluent-bit/tests/runtime/out_datadog.c173
-rw-r--r--fluent-bit/tests/runtime/out_elasticsearch.c818
-rw-r--r--fluent-bit/tests/runtime/out_exit.c210
-rw-r--r--fluent-bit/tests/runtime/out_file.c824
-rw-r--r--fluent-bit/tests/runtime/out_firehose.c200
-rw-r--r--fluent-bit/tests/runtime/out_flowcounter.c317
-rw-r--r--fluent-bit/tests/runtime/out_forward.c364
-rw-r--r--fluent-bit/tests/runtime/out_http.c1060
-rw-r--r--fluent-bit/tests/runtime/out_kinesis.c200
-rw-r--r--fluent-bit/tests/runtime/out_lib.c587
-rw-r--r--fluent-bit/tests/runtime/out_loki.c623
-rw-r--r--fluent-bit/tests/runtime/out_null.c127
-rw-r--r--fluent-bit/tests/runtime/out_opensearch.c1077
-rw-r--r--fluent-bit/tests/runtime/out_plot.c157
-rw-r--r--fluent-bit/tests/runtime/out_retry.c88
-rw-r--r--fluent-bit/tests/runtime/out_s3.c241
-rw-r--r--fluent-bit/tests/runtime/out_skywalking.c55
-rw-r--r--fluent-bit/tests/runtime/out_splunk.c150
-rw-r--r--fluent-bit/tests/runtime/out_stackdriver.c6252
-rw-r--r--fluent-bit/tests/runtime/out_stdout.c138
-rw-r--r--fluent-bit/tests/runtime/out_syslog.c1644
-rw-r--r--fluent-bit/tests/runtime/out_tcp.c1004
-rw-r--r--fluent-bit/tests/runtime/out_td.c45
-rw-r--r--fluent-bit/tests/runtime/wasm/go/Makefile24
-rw-r--r--fluent-bit/tests/runtime/wasm/go/append_tag.go39
-rw-r--r--fluent-bit/tests/runtime/wasm/go/drop_record.go8
-rw-r--r--fluent-bit/tests/runtime/wasm/go/go.mod9
-rw-r--r--fluent-bit/tests/runtime/wasm/go/go.sum10
-rw-r--r--fluent-bit/tests/runtime/wasm/go/modify_record.go39
-rw-r--r--fluent-bit/tests/runtime/wasm/go/numeric_records.go44
-rw-r--r--fluent-bit/tests/runtime/wasm/go/say_hello.go14
-rw-r--r--fluent-bit/tests/runtime_shell/.gitignore2
-rw-r--r--fluent-bit/tests/runtime_shell/CMakeLists.txt29
-rw-r--r--fluent-bit/tests/runtime_shell/common.sh26
-rw-r--r--fluent-bit/tests/runtime_shell/conf/in_dummy_expect.conf25
-rw-r--r--fluent-bit/tests/runtime_shell/conf/in_expect_base.conf28
-rw-r--r--fluent-bit/tests/runtime_shell/conf/in_http_tls_expect.conf13
-rw-r--r--fluent-bit/tests/runtime_shell/conf/in_syslog_tcp_plaintext_expect.conf8
-rw-r--r--fluent-bit/tests/runtime_shell/conf/in_syslog_tcp_tls_expect.conf14
-rw-r--r--fluent-bit/tests/runtime_shell/conf/in_syslog_udp_plaintext_expect.conf8
-rw-r--r--fluent-bit/tests/runtime_shell/conf/in_syslog_uds_dgram_plaintext_expect.conf7
-rw-r--r--fluent-bit/tests/runtime_shell/conf/in_syslog_uds_stream_plaintext_expect.conf7
-rw-r--r--fluent-bit/tests/runtime_shell/conf/in_tail_expect.conf38
-rwxr-xr-xfluent-bit/tests/runtime_shell/in_dummy_expect.sh8
-rwxr-xr-xfluent-bit/tests/runtime_shell/in_http_tls_expect.sh29
-rwxr-xr-xfluent-bit/tests/runtime_shell/in_syslog_tcp_plaintext_expect.sh26
-rwxr-xr-xfluent-bit/tests/runtime_shell/in_syslog_tcp_tls_expect.sh27
-rwxr-xr-xfluent-bit/tests/runtime_shell/in_syslog_udp_plaintext_expect.sh26
-rwxr-xr-xfluent-bit/tests/runtime_shell/in_syslog_uds_dgram_plaintext_expect.sh29
-rwxr-xr-xfluent-bit/tests/runtime_shell/in_syslog_uds_stream_plaintext_expect.sh24
-rw-r--r--fluent-bit/tests/runtime_shell/in_tail/README.md126
-rw-r--r--fluent-bit/tests/runtime_shell/in_tail/conf/multiline_rotation.conf60
-rw-r--r--fluent-bit/tests/runtime_shell/in_tail/conf/normal_rotation.conf55
-rw-r--r--fluent-bit/tests/runtime_shell/in_tail/conf/rotate_link.conf20
-rw-r--r--fluent-bit/tests/runtime_shell/in_tail/conf/single_static_rotation.conf19
-rw-r--r--fluent-bit/tests/runtime_shell/in_tail/conf/truncate_link.conf19
-rw-r--r--fluent-bit/tests/runtime_shell/in_tail/conf/truncate_rotation.conf19
-rwxr-xr-xfluent-bit/tests/runtime_shell/in_tail/logger_file.py72
-rwxr-xr-xfluent-bit/tests/runtime_shell/in_tail/run_tests.sh530
-rw-r--r--fluent-bit/tests/runtime_shell/in_tail/test_rotation.sh542
-rwxr-xr-xfluent-bit/tests/runtime_shell/in_tail_expect.sh16
-rw-r--r--fluent-bit/tests/runtime_shell/runtime_shell.env.in5
-rw-r--r--fluent-bit/tests/runtime_shell/tls/certificate.pem22
-rw-r--r--fluent-bit/tests/runtime_shell/tls/private_key.pem28
754 files changed, 82725 insertions, 0 deletions
diff --git a/fluent-bit/tests/include/aws_client_mock.c b/fluent-bit/tests/include/aws_client_mock.c
new file mode 100644
index 00000000..84ee6a1e
--- /dev/null
+++ b/fluent-bit/tests/include/aws_client_mock.c
@@ -0,0 +1,256 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+#include "aws_client_mock.h"
+
+#include <fluent-bit/flb_aws_util.h>
+#include <fluent-bit/flb_http_client.h>
+
+/* Vtable mocked methods */
+static struct flb_http_client *flb_aws_client_mock_vtable_request(
+ struct flb_aws_client *aws_client, int method, const char *uri, const char *body,
+ size_t body_len, struct flb_aws_header *dynamic_headers, size_t dynamic_headers_len);
+
+/* Protected structs */
+
+/* flb_aws_client_mock pointer returned by mock_generator */
+static struct flb_aws_client_mock *flb_aws_client_mock_instance = NULL;
+
+/* Generator that returns clients with the test vtable */
+static struct flb_aws_client_generator mock_generator = {
+ .create = flb_aws_client_create_mock,
+};
+
+/* Test/mock flb_aws_client vtable */
+static struct flb_aws_client_vtable mock_client_vtable = {
+ .request = flb_aws_client_mock_vtable_request,
+};
+
+/*
+ * Configure generator
+ * Note: Automatically creates mock and wires to generator
+ * Destroys any existing mock in generator
+ */
+void flb_aws_client_mock_configure_generator(
+ struct flb_aws_client_mock_request_chain *request_chain)
+{
+ flb_aws_client_mock_destroy_generator();
+ flb_aws_client_mock_instance = flb_aws_client_mock_create(request_chain);
+}
+
+/*
+ * Clean up generator's memory
+ * Cleanup should be called on exiting generator
+ */
+void flb_aws_client_mock_destroy_generator()
+{
+ if (flb_aws_client_mock_instance != NULL) {
+ flb_aws_client_mock_destroy(flb_aws_client_mock_instance);
+ }
+}
+
+/* Create Mock of flb_aws_client */
+struct flb_aws_client_mock *flb_aws_client_mock_create(
+ struct flb_aws_client_mock_request_chain *request_chain)
+{
+ struct flb_aws_client_mock *mock = flb_calloc(1, sizeof(struct flb_aws_client_mock));
+
+ /* Create a surrogate aws_client and copy to mock client */
+ struct flb_aws_client *surrogate_aws_client = flb_aws_client_generator()->create();
+ mock->super = *surrogate_aws_client;
+ mock->surrogate = surrogate_aws_client;
+ memset(mock->surrogate, 0, sizeof(struct flb_aws_client));
+
+ /* Switch vtable to mock vtable */
+ mock->super.client_vtable = &mock_client_vtable;
+ mock->request_chain = request_chain;
+ mock->next_request_index = 0;
+ return mock;
+}
+
+/* Destroy flb_aws_client_mock */
+void flb_aws_client_mock_destroy(struct flb_aws_client_mock *mock)
+{
+ /* Remove from generator registry if stored */
+ if (flb_aws_client_mock_instance == mock) {
+ flb_aws_client_mock_instance = NULL;
+ }
+
+ /* Resurrect surrogate, and destroy flb_aws_client */
+ *mock->surrogate = mock->super;
+ flb_aws_client_destroy(mock->surrogate);
+
+ /* Destroy mock flb_aws_client */
+ flb_free(mock);
+}
+
+/* Return a Mocked flb_aws_client, ready for injection */
+struct flb_aws_client *flb_aws_client_mock_context(struct flb_aws_client_mock *mock)
+{
+ return (struct flb_aws_client *)mock;
+}
+
+/* Get the number of unused requests */
+int flb_aws_client_mock_count_unused_requests(struct flb_aws_client_mock *mock)
+{
+ return mock->request_chain->length - mock->next_request_index;
+}
+
+/* Set flb_aws_client_mock_instance used in mock generator */
+void flb_aws_client_mock_set_generator_instance(struct flb_aws_client_mock *mock)
+{
+ flb_aws_client_mock_instance = mock;
+}
+
+/* Set flb_aws_client_mock_instance used in mock generator */
+struct flb_aws_client_mock *flb_aws_client_mock_get_generator_instance(
+ struct flb_aws_client_mock *mock)
+{
+ return flb_aws_client_mock_instance = mock;
+}
+
+/* Get generator used in mock */
+struct flb_aws_client_generator *flb_aws_client_get_mock_generator()
+{
+ return &mock_generator;
+}
+
+/* Get the number of unused requests */
+int flb_aws_client_mock_generator_count_unused_requests()
+{
+ TEST_ASSERT(flb_aws_client_mock_instance != 0);
+ return flb_aws_client_mock_count_unused_requests(flb_aws_client_mock_instance);
+}
+
+/* Return the mock instance */
+struct flb_aws_client *flb_aws_client_create_mock()
+{
+ TEST_CHECK(flb_aws_client_mock_instance != NULL);
+ TEST_MSG(
+ "[aws_mock_client] Must initialize flb_aws_client_mock_instance before calling "
+ "flb_aws_client_create_mock()");
+ TEST_MSG(
+ "[aws_mock_client] This ouccurs when the generator is called, before tests are "
+ "initialized.");
+
+ return flb_aws_client_mock_context(flb_aws_client_mock_instance);
+}
+
+/* Mock request used by flb_aws_client mock */
+static struct flb_http_client *flb_aws_client_mock_vtable_request(
+ struct flb_aws_client *aws_client, int method, const char *uri, const char *body,
+ size_t body_len, struct flb_aws_header *dynamic_headers, size_t dynamic_headers_len)
+{
+ int h;
+ int i;
+ int ret;
+
+ /* Get access to mock */
+ struct flb_aws_client_mock *mock = (struct flb_aws_client_mock *)aws_client;
+
+ /* Check that a response is left in the chain */
+ ret = TEST_CHECK(mock->next_request_index < mock->request_chain->length);
+ if (!ret) {
+ TEST_MSG(
+ "[flb_aws_client_mock] %d mock responses provided. Attempting to call %d "
+ "times. Aborting.",
+ (int)mock->request_chain->length, (int)mock->next_request_index + 1);
+ return NULL;
+ }
+ struct flb_aws_client_mock_response *response =
+ &(mock->request_chain->responses[mock->next_request_index]);
+ struct flb_http_client *c = NULL;
+
+ /* create an http client so that we can set the response */
+ c = flb_calloc(1, sizeof(struct flb_http_client));
+ if (!c) {
+ flb_errno();
+ return NULL;
+ }
+ mk_list_init(&c->headers);
+
+ /* Response configuration */
+ for (i = 0; i < response->length; ++i) {
+ struct flb_aws_client_mock_response_config *response_config =
+ &(response->config_parameters[i]);
+ void *val1 = response_config->config_value;
+ void *val2 = response_config->config_value_2;
+
+ /* Expectations */
+ if (response_config->config_parameter == FLB_AWS_CLIENT_MOCK_EXPECT_HEADER) {
+ int header_found = FLB_FALSE;
+ /* Search for header in request */
+ for (h = 0; h < dynamic_headers_len; ++h) {
+ ret = strncmp(dynamic_headers[h].key, (char *)val1,
+ dynamic_headers[h].key_len);
+ if (ret == 0) {
+ /* Check header value */
+ ret = strncmp(dynamic_headers[h].val, (char *)val2,
+ dynamic_headers[h].val_len + 1);
+ TEST_CHECK(ret == 0);
+ TEST_MSG("[aws_mock_client] Expected Header: (%s: %s)", (char *)val1,
+ (char *)val2);
+ TEST_MSG("[aws_mock_client] Received Header: (%s: %s)", (char *)val1,
+ dynamic_headers[h].val);
+
+ header_found = FLB_TRUE;
+ }
+ }
+ TEST_CHECK(header_found);
+ TEST_MSG("[aws_mock_client] Expected Header: (%s: %s)", (char *)val1,
+ (char *)val2);
+ TEST_MSG("[aws_mock_client] Header not received");
+ }
+ else if (response_config->config_parameter == FLB_AWS_CLIENT_MOCK_EXPECT_METHOD) {
+ char *flb_http_methods[] = {
+ "FLB_HTTP_GET", "FLB_HTTP_POST", "FLB_HTTP_PUT",
+ "FLB_HTTP_HEAD", "FLB_HTTP_CONNECT", "FLB_HTTP_PATCH",
+ };
+
+ /*
+ * Check method is what is expected
+ * Typecast config value from void * -> int
+ */
+ TEST_CHECK(method == (int)(uintptr_t)val1);
+ TEST_MSG("[aws_mock_client] Expected HTTP Method: %s",
+ flb_http_methods[(int)(uintptr_t)val1]);
+ TEST_MSG("[aws_mock_client] Received HTTP Method: %s",
+ flb_http_methods[method]);
+ }
+ else if (response_config->config_parameter ==
+ FLB_AWS_CLIENT_MOCK_EXPECT_HEADER_COUNT) {
+ TEST_CHECK(dynamic_headers_len == (int)(uintptr_t)val1);
+ TEST_MSG("[aws_mock_client] Expected %d Headers", (int)(uintptr_t)val1);
+ TEST_MSG("[aws_mock_client] Received %d Headers",
+ (int)(uintptr_t)dynamic_headers_len);
+ }
+ else if (response_config->config_parameter == FLB_AWS_CLIENT_MOCK_EXPECT_URI) {
+ ret = strncmp(uri, (char *)val1, strlen((char *)val1) + 1);
+ TEST_CHECK(ret == 0);
+ TEST_MSG("[aws_mock_client] Expected URI: %s", (char *)val1);
+ TEST_MSG("[aws_mock_client] Received URI: %s", uri);
+ }
+
+ /* Replace response client */
+ else if (response_config->config_parameter ==
+ FLB_AWS_CLIENT_MOCK_CONFIG_REPLACE) {
+ flb_http_client_destroy(c);
+ c = (struct flb_http_client *)val1;
+ }
+
+ /*
+ * Response setters
+ * Set client fields using XMacro definitions
+ */
+#define EXPAND_CLIENT_RESPONSE_PARAMETER(lower, UPPER, type) \
+ else if (response_config->config_parameter == FLB_AWS_CLIENT_MOCK_SET_##UPPER) \
+ { \
+ c->resp.lower = CONVERT_##type((char *)val1); \
+ }
+#include "aws_client_mock_client_resp.def"
+#undef EXPAND_CLIENT_RESPONSE_PARAMETER
+ }
+
+ /* Increment request */
+ ++mock->next_request_index;
+
+ return c;
+};
diff --git a/fluent-bit/tests/include/aws_client_mock.h b/fluent-bit/tests/include/aws_client_mock.h
new file mode 100644
index 00000000..7cdfc810
--- /dev/null
+++ b/fluent-bit/tests/include/aws_client_mock.h
@@ -0,0 +1,223 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/*
+ * AWS Client Mock
+ *
+ * Note: AWS Client Mock is not intended to be used with asynchronously
+ * running tests. A mock instance is stored and retrieved by
+ * the mock generator
+ */
+
+/*
+ * FLB_AWS_CLIENT_MOCK() definition
+ * The following macro FLB_AWS_CLIENT_MOCK() translates a request definition
+ * into a c compound literal, which constructs a mock request chain
+ * at the block scope.
+
+ * The following translation might ouccur:
+
+ FLB_AWS_CLIENT_MOCK(
+ response(
+ expect("token", "aws_token")
+ expect("time", "123456")
+ set(STATUS, 200),
+ ),
+ response(
+ set(STATUS, 200),
+ )
+ )
+
+ * ^^^^^^^^^^^^^^^^^^^^^^^^^
+ * *~~~> Translates to ~~~>*
+ * *vvvvvvvvvvvvvvvvvvvvvvv*
+
+ &(struct flb_aws_client_mock_request_chain){
+ 2,
+ ((struct flb_aws_client_mock_response[]) { // Mock request chain
+ {
+ 3,
+ (struct flb_aws_client_mock_response_config[]){
+ ((struct flb_aws_client_mock_response_config) { // Response
+ configuration FLB_AWS_CLIENT_MOCK_EXPECT_HEADER, (void *) "token", (void *) "aws_token"
+ }),
+ ((struct flb_aws_client_mock_response_config)
+ { FLB_AWS_CLIENT_MOCK_EXPECT_HEADER,
+ (void *) "time",
+ (void *) "123456"
+ }
+ )
+ ((struct flb_aws_client_mock_response_config) { // Response
+ configuration FLB_AWS_CLIENT_MOCK_SET_STATUS, (void *) 200, (void *) 0
+ }),
+ }
+ },
+ {
+ 1,
+ &(struct flb_aws_client_mock_response_config) {
+ FLB_AWS_CLIENT_MOCK_SET_STATUS,
+ (void *) 200,
+ (void *) 0
+ }
+ }
+ })
+ }
+*/
+
+#ifndef AWS_CLIENT_MOCK_H
+#define AWS_CLIENT_MOCK_H
+
+/* Variadic Argument Counter, Counts up to 64 variadic args */
+#define FLB_AWS_CLIENT_MOCK_COUNT64(...) \
+ _FLB_AWS_CLIENT_MOCK_COUNT64(dummy, ##__VA_ARGS__, 63, 62, 61, 60, 59, 58, 57, 56, \
+ 55, 54, 53, 52, 51, 50, 49, 48, 47, 46, 45, 44, 43, 42, \
+ 41, 40, 39, 38, 37, 36, 35, 34, 33, 32, 31, 30, 29, 28, \
+ 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, \
+ 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0)
+#define _FLB_AWS_CLIENT_MOCK_COUNT64( \
+ x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15, x16, x17, x18, \
+ x19, x20, x21, x22, x23, x24, x25, x26, x27, x28, x29, x30, x31, x32, x33, x34, x35, \
+ x36, x37, x38, x39, x40, x41, x42, x43, x44, x45, x46, x47, x48, x49, x50, x51, x52, \
+ x53, x54, x55, x56, x57, x58, x59, x60, x61, x62, x63, count, ...) \
+ count
+
+#define FLB_AWS_CLIENT_MOCK_EVAL(...) __VA_ARGS__
+#define FLB_AWS_CLIENT_MOCK_EMPTY()
+#define FLB_AWS_CLIENT_MOCK_DIFER(...) \
+ FLB_AWS_CLIENT_MOCK_EVAL FLB_AWS_CLIENT_MOCK_EMPTY()(__VA_ARGS__)
+
+/* Make block-scope addressable compound-literal request chain */
+#define FLB_AWS_CLIENT_MOCK(...) \
+ FLB_AWS_CLIENT_MOCK_EVAL(&(struct flb_aws_client_mock_request_chain){ \
+ FLB_AWS_CLIENT_MOCK_COUNT64(__VA_ARGS__), \
+ (struct flb_aws_client_mock_response[]){__VA_ARGS__}})
+
+#define FLB_AWS_CLIENT_MOCK_RESPONSE(...) \
+ { \
+ FLB_AWS_CLIENT_MOCK_COUNT64(__VA_ARGS__), \
+ (struct flb_aws_client_mock_response_config[]) \
+ { \
+ __VA_ARGS__ \
+ } \
+ }
+
+#define FLB_AWS_CLIENT_MOCK_VFUNC___(name, n) name##n
+#define FLB_AWS_CLIENT_MOCK_VFUNC(name, n) FLB_AWS_CLIENT_MOCK_VFUNC___(name, n)
+
+#define FLB_AWS_CLIENT_MOCK_STAGE_CONFIG(mode, parameter, value, ...) \
+ ((struct flb_aws_client_mock_response_config){ \
+ FLB_AWS_CLIENT_MOCK_##mode##parameter, (void *)value, \
+ FLB_AWS_CLIENT_MOCK_VFUNC( \
+ FLB_AWS_CLIENT_MOCK_STAGE_CONFIG_OPTIONAL_VALUES_, \
+ FLB_AWS_CLIENT_MOCK_COUNT64(__VA_ARGS__))(__VA_ARGS__)})
+
+#define FLB_AWS_CLIENT_MOCK_STAGE_CONFIG_OPTIONAL_VALUES_1(value) (void *)value
+#define FLB_AWS_CLIENT_MOCK_STAGE_CONFIG_OPTIONAL_VALUES_0() (void *)0
+
+// DIFER() allows for correct arg count
+#define response(...) FLB_AWS_CLIENT_MOCK_DIFER(FLB_AWS_CLIENT_MOCK_RESPONSE(__VA_ARGS__))
+#define expect(...) \
+ FLB_AWS_CLIENT_MOCK_DIFER(FLB_AWS_CLIENT_MOCK_STAGE_CONFIG(EXPECT_, __VA_ARGS__))
+#define config(...) \
+ FLB_AWS_CLIENT_MOCK_DIFER(FLB_AWS_CLIENT_MOCK_STAGE_CONFIG(CONFIG_, __VA_ARGS__))
+#define set(...) \
+ FLB_AWS_CLIENT_MOCK_DIFER(FLB_AWS_CLIENT_MOCK_STAGE_CONFIG(SET_, __VA_ARGS__))
+
+/* Includes */
+#include <fluent-bit/flb_aws_util.h>
+
+#include "../lib/acutest/acutest.h"
+
+/* Enum */
+enum flb_aws_client_mock_response_config_parameter {
+ FLB_AWS_CLIENT_MOCK_EXPECT_METHOD, // int: FLB_HTTP_<method> where method = { "GET",
+ // "POST", "PUT", "HEAD", "CONNECT", "PATCH" }
+ FLB_AWS_CLIENT_MOCK_EXPECT_HEADER, // (string, string): (header key, header value)
+ FLB_AWS_CLIENT_MOCK_EXPECT_HEADER_COUNT, // int: header count
+ FLB_AWS_CLIENT_MOCK_EXPECT_URI, // string: uri
+ FLB_AWS_CLIENT_MOCK_CONFIG_REPLACE, // flb_http_client ptr. Client can be null if
+ // needed
+// Define all client fields using XMacro definitions
+#define EXPAND_CLIENT_RESPONSE_PARAMETER(x, UPPER, y) FLB_AWS_CLIENT_MOCK_SET_##UPPER,
+#include "aws_client_mock_client_resp.def"
+#undef EXPAND_CLIENT_RESPONSE_PARAMETER
+};
+
+/* Structs */
+struct flb_aws_client_mock_response_config {
+ enum flb_aws_client_mock_response_config_parameter config_parameter;
+ void *config_value; // Most configuration must be passed in string format.
+ void *config_value_2;
+};
+
+struct flb_aws_client_mock_response {
+ size_t length;
+ struct flb_aws_client_mock_response_config *config_parameters;
+};
+
+struct flb_aws_client_mock_request_chain {
+ size_t length;
+ struct flb_aws_client_mock_response *responses;
+};
+
+struct flb_aws_client_mock {
+ /* This member must come first in the struct's memory layout
+ * so that this struct can mock flb_aws_client context */
+ struct flb_aws_client super;
+ struct flb_aws_client *surrogate;
+
+ /* Additional data members added to mock */
+ struct flb_aws_client_mock_request_chain *request_chain;
+ size_t next_request_index;
+};
+
+/* Declarations */
+
+/*
+ * Configure mock generator to be returned by flb_aws_client_get_mock_generator()
+ * Generator is injected into credential providers and returns a mocked
+ * flb_aws_client instance.
+ *
+ * Note: Automatically creates mock and wires to generator
+ * Destroys any existing mock in generator
+ */
+void flb_aws_client_mock_configure_generator(
+ struct flb_aws_client_mock_request_chain *request_chain);
+
+/*
+ * Clean up generator memory
+ * Note: Cleanup should be called at the end of each test
+ */
+void flb_aws_client_mock_destroy_generator();
+
+/* Create Mock of flb_aws_client */
+struct flb_aws_client_mock *flb_aws_client_mock_create(
+ struct flb_aws_client_mock_request_chain *request_chain);
+
+/*
+ * Destroy flb_aws_client_mock
+ * Note: flb_aws_client_destroy must not be used prior to flb_aws_client_mock_destroy.
+ */
+void flb_aws_client_mock_destroy(struct flb_aws_client_mock *mock);
+
+/* Get the number of unused requests */
+int flb_aws_client_mock_count_unused_requests(struct flb_aws_client_mock *mock);
+
+/* Return a Mocked flb_aws_client, ready for injection */
+struct flb_aws_client *flb_aws_client_mock_context(struct flb_aws_client_mock *mock);
+
+/* Generator Methods */
+/* Get/set flb_aws_client_mock_instance used by mock generator */
+void flb_aws_client_mock_set_generator_instance(struct flb_aws_client_mock *mock);
+struct flb_aws_client_mock *flb_aws_client_mock_get_generator_instance(
+ struct flb_aws_client_mock *mock);
+
+int flb_aws_client_mock_generator_count_unused_requests();
+
+/* Substitute Methods */
+/* Get generator used in mock */
+struct flb_aws_client_generator *flb_aws_client_get_mock_generator();
+
+/* Return the mock instance */
+struct flb_aws_client *flb_aws_client_create_mock();
+
+#endif /* AWS_CLIENT_MOCK_H */
diff --git a/fluent-bit/tests/include/aws_client_mock_client_resp.def b/fluent-bit/tests/include/aws_client_mock_client_resp.def
new file mode 100644
index 00000000..225cc2ea
--- /dev/null
+++ b/fluent-bit/tests/include/aws_client_mock_client_resp.def
@@ -0,0 +1,34 @@
+/* XMacro Definition for Mock AWS Client http_client Response */
+
+/* Converters: CONVERT_<C_INT> */
+#define EVAL1(...) __VA_ARGS__
+#define CONVERT_T_INT(str) (int) (uintptr_t) str
+#define CONVERT_T_SIZE_T(str) (size_t) str
+#define CONVERT_T_LONG(str) (long) str
+#define CONVERT_T_CHAR_STAR(str) str
+
+EVAL1(EXPAND_CLIENT_RESPONSE_PARAMETER(status, STATUS, T_INT)) /* HTTP response status */
+EVAL1(EXPAND_CLIENT_RESPONSE_PARAMETER(content_length, CONTENT_LENGTH, T_INT)) /* Content length set by headers */
+EVAL1(EXPAND_CLIENT_RESPONSE_PARAMETER(chunked_encoding, CHUNKED_ENCODEING, T_INT)) /* Chunked transfer encoding ? */
+EVAL1(EXPAND_CLIENT_RESPONSE_PARAMETER(connection_close, CONNECTION_CLOSE, T_INT)) /* connection: close ? */
+EVAL1(EXPAND_CLIENT_RESPONSE_PARAMETER(chunked_cur_size, CHUNKED_CUR_SIZE, T_LONG))
+EVAL1(EXPAND_CLIENT_RESPONSE_PARAMETER(chunked_exp_size, CHUNKED_EXP_SIZE, T_LONG)) /* expected chunked size */
+EVAL1(EXPAND_CLIENT_RESPONSE_PARAMETER(chunk_processed_end, CHUNK_PROCESSED_END, T_CHAR_STAR)) /* Position to mark last chunk */
+EVAL1(EXPAND_CLIENT_RESPONSE_PARAMETER(headers_end, HEADERS_END, T_CHAR_STAR)) /* Headers end (\r\n\r\n) */
+
+/* Payload: body response: reference to 'data' */
+EVAL1(EXPAND_CLIENT_RESPONSE_PARAMETER(payload, PAYLOAD, T_CHAR_STAR))
+EVAL1(EXPAND_CLIENT_RESPONSE_PARAMETER(payload_size, PAYLOAD_SIZE, T_SIZE_T))
+
+/* Buffer to store server response */
+EVAL1(EXPAND_CLIENT_RESPONSE_PARAMETER(data, DATA, T_CHAR_STAR))
+EVAL1(EXPAND_CLIENT_RESPONSE_PARAMETER(data_len, DATA_LEN, T_SIZE_T))
+EVAL1(EXPAND_CLIENT_RESPONSE_PARAMETER(data_size, DATA_SIZE, T_SIZE_T))
+EVAL1(EXPAND_CLIENT_RESPONSE_PARAMETER(data_size_max, DATA_SIZE_MAX, T_SIZE_T))
+
+#undef CONVERT_T_INT
+#undef CONVERT_T_SIZE
+#undef CONVERT_T_LONG
+#undef CONVERT_T_CHAR_STAR
+
+#undef EVAL1
diff --git a/fluent-bit/tests/include/flb_tests_initialize_tls.h b/fluent-bit/tests/include/flb_tests_initialize_tls.h
new file mode 100644
index 00000000..bfe16f22
--- /dev/null
+++ b/fluent-bit/tests/include/flb_tests_initialize_tls.h
@@ -0,0 +1,44 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/* Fluent Bit
+ * ==========
+ * Copyright (C) 2019-2020 The Fluent Bit Authors
+ * Copyright (C) 2015-2018 Treasure Data Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef FLB_TESTS_INITALIZE_TLS_H
+#define FLB_TESTS_INITALIZE_TLS_H
+
+#include <fluent-bit/flb_config.h>
+#include <fluent-bit/flb_mem.h>
+
+struct flb_config *test_env_config = NULL;
+
+static inline void flb_test_env_config_init(void)
+{
+ test_env_config = flb_config_init();
+
+ if (test_env_config == NULL) {
+ return;
+ }
+}
+
+static inline void flb_test_env_config_destroy(void) {
+ if (test_env_config != NULL) {
+ flb_config_exit(test_env_config);
+ }
+}
+
+#endif
diff --git a/fluent-bit/tests/internal/CMakeLists.txt b/fluent-bit/tests/internal/CMakeLists.txt
new file mode 100644
index 00000000..5288004d
--- /dev/null
+++ b/fluent-bit/tests/internal/CMakeLists.txt
@@ -0,0 +1,216 @@
+find_package(Threads REQUIRED)
+
+include_directories(cutest/)
+
+set(UNIT_TESTS_FILES
+ pack.c
+ pipe.c
+ sds.c
+ sds_list.c
+ hmac.c
+ crypto.c
+ hash.c
+ slist.c
+ router.c
+ network.c
+ unit_sizes.c
+ hashtable.c
+ http_client.c
+ utils.c
+ gzip.c
+ random.c
+ config_map.c
+ mp.c
+ input_chunk.c
+ flb_time.c
+ file.c
+ csv.c
+ multiline.c
+ typecast.c
+ base64.c
+ bucket_queue.c
+ flb_event_loop.c
+ ring_buffer.c
+ regex.c
+ parser_json.c
+ parser_ltsv.c
+ parser_regex.c
+ parser_logfmt.c
+ env.c
+ log.c
+ log_event_decoder.c
+ processor.c
+ uri.c
+ )
+
+# Config format
+set(UNIT_TESTS_FILES
+ ${UNIT_TESTS_FILES}
+ config_format.c
+ config_format_fluentbit.c
+)
+
+if(FLB_HAVE_LIBYAML)
+ set(UNIT_TESTS_FILES
+ ${UNIT_TESTS_FILES}
+ config_format_yaml.c
+ )
+endif()
+
+if (NOT WIN32)
+ set(UNIT_TESTS_FILES
+ ${UNIT_TESTS_FILES}
+ gelf.c
+ fstore.c
+ reload.c
+ )
+endif()
+
+if(FLB_PARSER)
+ set(UNIT_TESTS_FILES
+ ${UNIT_TESTS_FILES}
+ parser.c
+ )
+endif()
+
+if(FLB_STREAM_PROCESSOR)
+ set(UNIT_TESTS_FILES
+ ${UNIT_TESTS_FILES}
+ stream_processor.c
+ )
+endif()
+
+if(FLB_RECORD_ACCESSOR)
+ set(UNIT_TESTS_FILES
+ ${UNIT_TESTS_FILES}
+ record_accessor.c
+ )
+endif()
+
+if(FLB_METRICS)
+ set(UNIT_TESTS_FILES
+ ${UNIT_TESTS_FILES}
+ metrics.c
+ )
+endif()
+
+if(FLB_SIGNV4)
+ set(UNIT_TESTS_FILES
+ ${UNIT_TESTS_FILES}
+ signv4.c
+ )
+endif()
+
+if(FLB_AVRO_ENCODER)
+ set(UNIT_TESTS_FILES
+ ${UNIT_TESTS_FILES}
+ avro.c
+ )
+endif()
+
+if(FLB_AWS)
+ set(UNIT_TESTS_FILES
+ ${UNIT_TESTS_FILES}
+ aws_util.c
+ aws_compress.c
+ aws_credentials.c
+ aws_credentials_ec2.c
+ aws_credentials_sts.c
+ aws_credentials_http.c
+ aws_credentials_profile.c
+ )
+ if(FLB_HAVE_AWS_CREDENTIAL_PROCESS)
+ set(UNIT_TESTS_FILES
+ ${UNIT_TESTS_FILES}
+ aws_credentials_process.c
+ )
+ endif()
+endif()
+
+if(FLB_AWS_ERROR_REPORTER)
+ set(UNIT_TESTS_FILES
+ ${UNIT_TESTS_FILES}
+ error_reporter.c
+ )
+endif()
+
+if(FLB_LUAJIT)
+ set(UNIT_TESTS_FILES
+ ${UNIT_TESTS_FILES}
+ lua.c
+ )
+endif()
+
+set(UNIT_TESTS_DATA
+ data/tls/certificate.pem
+ data/tls/private_key.pem
+ data/pack/json_single_map_001.json
+ data/pack/json_single_map_002.json
+ data/parser/json.conf
+ data/parser/regex.conf
+ data/input_chunk/log/a_thousand_plus_one_bytes.log
+ data/input_chunk/log/test_buffer_valid.log
+ )
+
+set(FLB_TESTS_DATA_PATH ${CMAKE_CURRENT_SOURCE_DIR}/)
+configure_file(
+ "${CMAKE_CURRENT_SOURCE_DIR}/flb_tests_internal.h.in"
+ "${CMAKE_CURRENT_SOURCE_DIR}/flb_tests_internal.h"
+ )
+
+# Move data files to the path the test binaries can find them
+macro(FLB_TEST_COPY_DATA path)
+ configure_file(${path} ${CMAKE_CURRENT_BINARY_DIR}/${path} COPYONLY)
+endmacro()
+
+foreach(test_data ${UNIT_TESTS_DATA})
+ FLB_TEST_COPY_DATA(${test_data})
+endforeach()
+
+# Prepare list of unit tests function
+function(prepare_unit_tests TEST_PREFIX SOURCEFILES)
+ foreach(source_file ${SOURCEFILES})
+ get_filename_component(source_file_we ${source_file} NAME_WE)
+ set(source_file_we ${TEST_PREFIX}${source_file_we})
+ if(FLB_WITHOUT_${source_file_we})
+ message("Skipping test ${source_file_we}")
+ else()
+ add_executable(
+ ${source_file_we}
+ ${source_file}
+ )
+ add_sanitizers(${source_file_we})
+
+ if(FLB_JEMALLOC)
+ target_link_libraries(${source_file_we} libjemalloc ${CMAKE_THREAD_LIBS_INIT})
+ else()
+ target_link_libraries(${source_file_we} ${CMAKE_THREAD_LIBS_INIT})
+ endif()
+
+ if(FLB_AWS)
+ target_link_libraries(${source_file_we} flb-aws)
+ endif()
+
+ if(FLB_STREAM_PROCESSOR)
+ target_link_libraries(${source_file_we} flb-sp)
+ endif()
+
+ target_link_libraries(${source_file_we} fluent-bit-static cfl-static)
+
+ if(FLB_AVRO_ENCODER)
+ target_link_libraries(${source_file_we} avro-static jansson)
+ endif()
+
+ add_test(NAME ${source_file_we}
+ COMMAND ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${source_file_we}
+ WORKING_DIRECTORY ${CMAKE_HOME_DIRECTORY}/build)
+ set_tests_properties(${source_file_we} PROPERTIES LABELS "internal")
+ endif()
+ endforeach()
+endfunction(prepare_unit_tests)
+
+prepare_unit_tests(flb-it- "${UNIT_TESTS_FILES}")
+
+if(FLB_TESTS_INTERNAL_FUZZ)
+ add_subdirectory(fuzzers)
+endif()
diff --git a/fluent-bit/tests/internal/README.md b/fluent-bit/tests/internal/README.md
new file mode 100644
index 00000000..4ea04b20
--- /dev/null
+++ b/fluent-bit/tests/internal/README.md
@@ -0,0 +1,3 @@
+# Fluent Bit Internal Tests
+
+The following directory contains unit tests to validate specific functions of Fluent Bit core (not plugins).
diff --git a/fluent-bit/tests/internal/avro.c b/fluent-bit/tests/internal/avro.c
new file mode 100644
index 00000000..45f07343
--- /dev/null
+++ b/fluent-bit/tests/internal/avro.c
@@ -0,0 +1,382 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+#include <errno.h>
+#include <string.h>
+#include <fluent-bit/flb_mem.h>
+#include <fluent-bit/flb_avro.h>
+#include <fluent-bit/flb_log.h>
+#include <fluent-bit/flb_pack.h>
+#include <msgpack.h>
+
+#include "flb_tests_internal.h"
+
+/* AVRO iteration tests */
+#define AVRO_SINGLE_MAP1 FLB_TESTS_DATA_PATH "/data/avro/json_single_map_001.json"
+#define AVRO_MULTILINE_JSON FLB_TESTS_DATA_PATH "/data/avro/live-sample.json"
+
+const char JSON_SINGLE_MAP_001_SCHEMA[] =
+"{\"type\":\"record\",\
+ \"name\":\"Map001\",\
+ \"fields\":[\
+ {\"name\": \"key001\", \"type\": \"int\"},\
+ {\"name\": \"key002\", \"type\": \"float\"},\
+ {\"name\": \"key003\", \"type\": \"string\"},\
+ {\"name\": \"key004\", \"type\":\
+ {\"type\": \"array\", \"items\":\
+ {\"type\": \"map\",\"values\": \"int\"}}}]}";
+
+msgpack_unpacked test_init(avro_value_t *aobject, avro_schema_t *aschema, const char *json_schema, const char *json_data) {
+ char *out_buf;
+ size_t out_size;
+ int root_type;
+
+ avro_value_iface_t *aclass = flb_avro_init(aobject, (char *)json_schema, strlen(json_schema), aschema);
+ TEST_CHECK(aclass != NULL);
+
+ char *data = mk_file_to_buffer(json_data);
+ TEST_CHECK(data != NULL);
+
+ size_t len = strlen(data);
+
+ TEST_CHECK(flb_pack_json(data, len, &out_buf, &out_size, &root_type, NULL) == 0);
+
+ msgpack_unpacked msg;
+ msgpack_unpacked_init(&msg);
+ TEST_CHECK(msgpack_unpack_next(&msg, out_buf, out_size, NULL) == MSGPACK_UNPACK_SUCCESS);
+
+ avro_value_iface_decref(aclass);
+ flb_free(data);
+ flb_free(out_buf);
+
+ return msg;
+}
+/* Unpack msgpack per avro schema */
+void test_unpack_to_avro()
+{
+ avro_value_t aobject;
+ avro_schema_t aschema;
+
+ msgpack_unpacked mp = test_init(&aobject, &aschema, JSON_SINGLE_MAP_001_SCHEMA, AVRO_SINGLE_MAP1);
+
+ msgpack_object_print(stderr, mp.data);
+ flb_msgpack_to_avro(&aobject, &mp.data);
+
+ avro_value_t test_value;
+ TEST_CHECK(avro_value_get_by_name(&aobject, "key001", &test_value, NULL) == 0);
+
+ int val001 = 0;
+ avro_value_get_int(&test_value, &val001);
+ TEST_CHECK(val001 == 123456789);
+
+ TEST_CHECK(avro_value_get_by_name(&aobject, "key002", &test_value, NULL) == 0);
+
+ float val002 = 0.0f;
+ // for some reason its rounding to this value
+ float val002_actual = 0.999888f;
+ avro_value_get_float(&test_value, &val002);
+ char str1[80];
+ char str2[80];
+ sprintf(str1, "%f", val002);
+ sprintf(str2, "%f", val002_actual);
+ flb_info("val002:%s:\n", str1);
+ flb_info("val002_actual:%s:\n", str2);
+ TEST_CHECK((strcmp(str1, str2) == 0));
+
+ TEST_CHECK(avro_value_get_by_name(&aobject, "key003", &test_value, NULL) == 0);
+ const char *val003 = NULL;
+ size_t val003_size = 0;
+ avro_value_get_string(&test_value, &val003, &val003_size);
+ flb_info("val003_size:%zu:\n", val003_size);
+ TEST_CHECK(val003[val003_size] == '\0');
+
+ TEST_CHECK((strcmp(val003, "abcdefghijk") == 0));
+ // avro_value_get_by_name returns ths string length plus the NUL
+ TEST_CHECK(val003_size == 12);
+
+ TEST_CHECK(avro_value_get_by_name(&aobject, "key004", &test_value, NULL) == 0);
+
+ size_t asize = 0;
+ avro_value_get_size(&test_value, &asize);
+ flb_info("asize:%zu:\n", asize);
+
+ TEST_CHECK(asize == 2);
+
+ // check the first map
+ avro_value_t k8sRecord;
+ TEST_CHECK(avro_value_get_by_index(&test_value, 0, &k8sRecord, NULL) == 0);
+
+ size_t msize = 0;
+ avro_value_get_size(&k8sRecord, &msize);
+ flb_info("msize:%zu:\n", msize);
+
+ TEST_CHECK(msize == 2);
+
+ avro_value_t obj_test;
+ const char *actual_key = NULL;
+ int actual = 0;
+
+ // check the first item in the map
+ TEST_CHECK(avro_value_get_by_index(&k8sRecord, 0, &obj_test, &actual_key) == 0);
+ flb_info("actual_key:%s:\n", actual_key);
+
+ TEST_CHECK(strcmp(actual_key, "a") == 0);
+ TEST_CHECK(avro_value_get_int(&obj_test, &actual) == 0);
+ TEST_CHECK(actual == 1);
+
+ // check the second item in the map
+ TEST_CHECK(avro_value_get_by_index(&k8sRecord, 1, &obj_test, &actual_key) == 0);
+ flb_info("actual_key:%s:\n", actual_key);
+
+ TEST_CHECK(strcmp(actual_key, "b") == 0);
+ TEST_CHECK(avro_value_get_int(&obj_test, &actual) == 0);
+ TEST_CHECK(actual == 2);
+
+ // check the second map
+ TEST_CHECK(avro_value_get_by_index(&test_value, 1, &k8sRecord, NULL) == 0);
+
+ avro_value_get_size(&k8sRecord, &msize);
+ flb_info("msize:%zu:\n", msize);
+
+ TEST_CHECK(msize == 2);
+
+ avro_value_decref(&aobject);
+ avro_schema_decref(aschema);
+ msgpack_unpacked_destroy(&mp);
+}
+
+void test_parse_reordered_schema()
+{
+ // test same schema but different order of fields
+ const char *ts1 = "{\"name\":\"qavrov2_record\",\"type\":\"record\",\"fields\":[{\"name\":\"log\",\"type\":\"string\"},{\"name\":\"capture\",\"type\":\"string\"},{\"name\":\"kubernetes\",\"type\":{\"name\":\"krec\",\"type\":\"record\",\"fields\":[{\"name\":\"pod_name\",\"type\":\"string\"},{\"name\":\"namespace_name\",\"type\":\"string\"},{\"name\":\"pod_id\",\"type\":\"string\"},{\"name\":\"labels\",\"type\":{\"type\":\"map\",\"values\":\"string\"}},{\"name\":\"annotations\",\"type\":{\"type\":\"map\",\"values\":\"string\"}},{\"name\":\"host\",\"type\":\"string\"},{\"name\":\"container_name\",\"type\":\"string\"},{\"name\":\"docker_id\",\"type\":\"string\"},{\"name\":\"container_hash\",\"type\":\"string\"},{\"name\":\"container_image\",\"type\":\"string\"}]}}]}";
+ const char *ts2 = "{\"name\":\"qavrov2_record\",\"type\":\"record\",\"fields\":[{\"name\":\"capture\",\"type\":\"string\"},{\"name\":\"log\",\"type\":\"string\"},{\"name\":\"kubernetes\",\"type\":{\"name\":\"krec\",\"type\":\"record\",\"fields\":[{\"name\":\"namespace_name\",\"type\":\"string\"},{\"name\":\"pod_name\",\"type\":\"string\"},{\"name\":\"pod_id\",\"type\":\"string\"},{\"name\":\"annotations\",\"type\":{\"type\":\"map\",\"values\":\"string\"}},{\"name\":\"labels\",\"type\":{\"type\":\"map\",\"values\":\"string\"}},{\"name\":\"host\",\"type\":\"string\"},{\"name\":\"container_name\",\"type\":\"string\"},{\"name\":\"docker_id\",\"type\":\"string\"},{\"name\":\"container_hash\",\"type\":\"string\"},{\"name\":\"container_image\",\"type\":\"string\"}]}}]}";
+ const char *ts3 = "{\"name\":\"qavrov2_record\",\"type\":\"record\",\"fields\":[{\"name\":\"newnovalue\",\"type\":\"string\"},{\"name\":\"capture\",\"type\":\"string\"},{\"name\":\"log\",\"type\":\"string\"},{\"name\":\"kubernetes\",\"type\":{\"name\":\"krec\",\"type\":\"record\",\"fields\":[{\"name\":\"namespace_name\",\"type\":\"string\"},{\"name\":\"pod_name\",\"type\":\"string\"},{\"name\":\"pod_id\",\"type\":\"string\"},{\"name\":\"annotations\",\"type\":{\"type\":\"map\",\"values\":\"string\"}},{\"name\":\"labels\",\"type\":{\"type\":\"map\",\"values\":\"string\"}},{\"name\":\"host\",\"type\":\"string\"},{\"name\":\"container_name\",\"type\":\"string\"},{\"name\":\"docker_id\",\"type\":\"string\"},{\"name\":\"container_hash\",\"type\":\"string\"},{\"name\":\"container_image\",\"type\":\"string\"}]}}]}";
+
+ const char *schemas[] = {ts1, ts2, ts3, ts2, ts1, NULL};
+
+ int i=0;
+ for (i=0; schemas[i] != NULL ; i++) {
+
+ avro_value_t aobject = {0};
+ avro_schema_t aschema = {0};
+
+ msgpack_unpacked msg = test_init(&aobject, &aschema, schemas[i], AVRO_MULTILINE_JSON);
+
+ msgpack_object_print(stderr, msg.data);
+
+ flb_msgpack_to_avro(&aobject, &msg.data);
+
+ avro_value_t log0;
+ TEST_CHECK(avro_value_get_by_name(&aobject, "log", &log0, NULL) == 0);
+
+ size_t size1 = 0;
+ const char *log_line = NULL;
+ TEST_CHECK(avro_value_get_string(&log0, &log_line, &size1) == 0);
+ char *pre = "2020-08-21T15:49:48.154291375Z";
+ TEST_CHECK((strncmp(pre, log_line, strlen(pre)) == 0));
+ flb_info("log_line len:%zu:\n", strlen(log_line));
+
+ avro_value_t kubernetes0;
+ TEST_CHECK(avro_value_get_by_name(&aobject, "kubernetes", &kubernetes0, NULL) == 0);
+
+ avro_value_get_size(&kubernetes0, &size1);
+ flb_info("asize:%zu:\n", size1);
+ TEST_CHECK(size1 == 10);
+
+ avro_value_t pn;
+ TEST_CHECK(avro_value_get_by_name(&kubernetes0, "pod_name", &pn, NULL) == 0);
+
+ const char *pod_name = NULL;
+ size_t pod_name_size = 0;
+ TEST_CHECK(avro_value_get_string(&pn, &pod_name, &pod_name_size) == 0);
+ TEST_CHECK(strcmp(pod_name, "rrrr-bert-completion-tb1-6786c9c8-wj25m") == 0);
+ TEST_CHECK(pod_name[pod_name_size] == '\0');
+ TEST_CHECK(strlen(pod_name) == (pod_name_size-1));
+
+ avro_value_t nn;
+ TEST_CHECK(avro_value_get_by_name(&kubernetes0, "namespace_name", &nn, NULL) == 0);
+
+ const char *namespace_name = NULL;
+ size_t namespace_name_size = 0;
+ TEST_CHECK(avro_value_get_string(&nn, &namespace_name, &namespace_name_size) == 0);
+ TEST_CHECK(strcmp(namespace_name, "k8s-fgg") == 0);
+
+ avro_value_t mapX;
+ TEST_CHECK(avro_value_get_by_name(&kubernetes0, "annotations", &mapX, NULL) == 0);
+
+ avro_value_get_size(&mapX, &size1);
+ flb_info("asize:%zu:\n", size1);
+
+ TEST_CHECK(size1 == 5);
+
+ // check the first item in the map
+ avro_value_t doas;
+ TEST_CHECK(avro_value_get_by_name(&mapX, "doAs", &doas, NULL) == 0);
+ const char *doaser = NULL;
+ size_t doaser_size;
+ TEST_CHECK(avro_value_get_string(&doas, &doaser, &doaser_size) == 0);
+ TEST_CHECK((strcmp(doaser, "weeb") == 0));
+
+ // check the second item in the map
+ avro_value_t iddecorator;
+ TEST_CHECK(avro_value_get_by_name(&mapX, "iddecorator.dkdk.username", &iddecorator, NULL) == 0);
+ const char *idder = NULL;
+ size_t idder_size;
+ TEST_CHECK(avro_value_get_string(&iddecorator, &idder, &idder_size) == 0);
+ TEST_CHECK((strcmp(idder, "rrrr") == 0));
+
+ avro_schema_decref(aschema);
+ msgpack_unpacked_destroy(&msg);
+ avro_value_decref(&aobject);
+ }
+
+}
+
+// int msgpack2avro(avro_value_t *val, msgpack_object *o)
+// get a schema for a type like this:
+// http://avro.apache.org/docs/current/api/c/index.html#_examples
+// ../lib/msgpack-3.2.0/include/msgpack/pack.h
+// static int msgpack_pack_nil(msgpack_packer* pk);
+void test_msgpack2avro()
+{
+ avro_value_t aobject;
+ avro_schema_t schema = avro_schema_null();
+ avro_value_iface_t *aclass = avro_generic_class_from_schema(schema);
+ avro_generic_value_new(aclass, &aobject);
+
+ msgpack_sbuffer sbuf;
+ msgpack_packer pk;
+ msgpack_zone mempool;
+ msgpack_object deserialized;
+
+ /* msgpack::sbuffer is a simple buffer implementation. */
+ msgpack_sbuffer_init(&sbuf);
+
+ /* serialize values into the buffer using msgpack_sbuffer_write callback function. */
+ msgpack_packer_init(&pk, &sbuf, msgpack_sbuffer_write);
+
+ msgpack_pack_nil(&pk);
+
+ /* deserialize the buffer into msgpack_object instance. */
+ /* deserialized object is valid during the msgpack_zone instance alive. */
+ msgpack_zone_init(&mempool, 2048);
+ msgpack_unpack(sbuf.data, sbuf.size, NULL, &mempool, &deserialized);
+
+ TEST_CHECK((msgpack2avro(&aobject, &deserialized) == FLB_TRUE));
+
+ msgpack_zone_destroy(&mempool);
+ msgpack_sbuffer_destroy(&sbuf);
+}
+const char JSON_SINGLE_MAP_001_SCHEMA_WITH_UNION[] =
+"{\"type\":\"record\",\
+ \"name\":\"Map001\",\
+ \"fields\":[\
+ {\"name\": \"key001\", \"type\": \"int\"},\
+ {\"name\": \"key002\", \"type\": \"float\"},\
+ {\"name\": \"key003\", \"type\": \"string\"},\
+ { \
+ \"name\": \"status\", \
+ \"default\": null, \
+ \"type\": [\"null\", \"string\"] \
+ }, \
+ {\"name\": \"key004\", \"type\":\
+ {\"type\": \"array\", \"items\":\
+ {\"type\": \"map\",\"values\": \"int\"}}}]}";
+void test_union_type_sanity()
+{
+ avro_value_t aobject;
+ avro_schema_t aschema;
+
+ msgpack_unpacked msg = test_init(&aobject, &aschema, JSON_SINGLE_MAP_001_SCHEMA_WITH_UNION, AVRO_SINGLE_MAP1);
+
+ msgpack_object_print(stderr, msg.data);
+ flb_msgpack_to_avro(&aobject, &msg.data);
+
+ size_t totalSize = 0;
+ avro_value_get_size(&aobject, &totalSize);
+ flb_info("totalSize:%zu:\n", totalSize);
+ // this is key001,2,3,4 and the status field which is the union type
+ TEST_CHECK(totalSize == 5);
+
+ avro_value_t test_value;
+ TEST_CHECK(avro_value_get_by_name(&aobject, "key001", &test_value, NULL) == 0);
+
+ int val001 = 0;
+ avro_value_get_int(&test_value, &val001);
+ TEST_CHECK(val001 == 123456789);
+
+ TEST_CHECK(avro_value_get_by_name(&aobject, "key002", &test_value, NULL) == 0);
+
+ float val002 = 0.0f;
+ // for some reason its rounding to this value
+ float val002_actual = 0.999888f;
+ avro_value_get_float(&test_value, &val002);
+ char str1[80];
+ char str2[80];
+ sprintf(str1, "%f", val002);
+ sprintf(str2, "%f", val002_actual);
+ flb_info("val002:%s:\n", str1);
+ flb_info("val002_actual:%s:\n", str2);
+ TEST_CHECK((strcmp(str1, str2) == 0));
+
+ TEST_CHECK(avro_value_get_by_name(&aobject, "key003", &test_value, NULL) == 0);
+ const char *val003 = NULL;
+ size_t val003_size = 0;
+ TEST_CHECK(avro_value_get_string(&test_value, &val003, &val003_size) == 0);
+ flb_info("val003_size:%zu:\n", val003_size);
+ TEST_CHECK(val003[val003_size] == '\0');
+
+ TEST_CHECK((strcmp(val003, "abcdefghijk") == 0));
+ // avro_value_get_by_name returns ths string length plus the NUL
+ TEST_CHECK(val003_size == 12);
+
+ TEST_CHECK(avro_value_get_by_name(&aobject, "key004", &test_value, NULL) == 0);
+
+ size_t asize = 0;
+ avro_value_get_size(&test_value, &asize);
+ flb_info("asize:%zu:\n", asize);
+
+ TEST_CHECK(asize == 2);
+
+ TEST_CHECK(avro_value_get_by_name(&aobject, "status", &test_value, NULL) == 0);
+
+ avro_value_decref(&aobject);
+ avro_schema_decref(aschema);
+ msgpack_unpacked_destroy(&msg);
+}
+
+void test_union_type_branches()
+{
+ avro_value_t aobject;
+ avro_schema_t aschema;
+
+ msgpack_unpacked mp = test_init(&aobject, &aschema, JSON_SINGLE_MAP_001_SCHEMA_WITH_UNION, AVRO_SINGLE_MAP1);
+
+ flb_msgpack_to_avro(&aobject, &mp.data);
+
+ avro_value_t test_value;
+ TEST_CHECK(avro_value_get_by_name(&aobject, "status", &test_value, NULL) == 0);
+ TEST_CHECK(avro_value_get_type(&test_value) == AVRO_UNION);
+
+ int discriminant = 0;
+ TEST_CHECK(avro_value_get_discriminant(&test_value, &discriminant) == 0);
+ TEST_CHECK(discriminant == -1);
+
+ avro_value_t branch;
+ TEST_CHECK(avro_value_get_current_branch(&test_value, &branch) != 0);
+
+ TEST_CHECK(avro_value_set_branch(&test_value, 0, &branch) == 0);
+ TEST_CHECK(avro_value_set_null(&branch) == 0);
+
+ TEST_CHECK(avro_value_get_null(&branch) == 0);
+
+ avro_value_decref(&aobject);
+ avro_schema_decref(aschema);
+ msgpack_unpacked_destroy(&mp);
+}
+TEST_LIST = {
+ /* Avro */
+ { "msgpack_to_avro_basic", test_unpack_to_avro},
+ { "test_parse_reordered_schema", test_parse_reordered_schema},
+ { "test_union_type_sanity", test_union_type_sanity},
+ { "test_union_type_branches", test_union_type_branches},
+ { 0 }
+};
diff --git a/fluent-bit/tests/internal/aws/CMakeLists.txt b/fluent-bit/tests/internal/aws/CMakeLists.txt
new file mode 100644
index 00000000..1685c0e2
--- /dev/null
+++ b/fluent-bit/tests/internal/aws/CMakeLists.txt
@@ -0,0 +1,7 @@
+# AWS unit tests
+set(UNIT_TESTS_FILES
+ placeholder.c
+ )
+
+# Prepare list of unit tests
+prepare_unit_tests(flb-it-aws_ "${UNIT_TESTS_FILES}")
diff --git a/fluent-bit/tests/internal/aws/placeholder.c b/fluent-bit/tests/internal/aws/placeholder.c
new file mode 100644
index 00000000..133c9a32
--- /dev/null
+++ b/fluent-bit/tests/internal/aws/placeholder.c
@@ -0,0 +1,11 @@
+#include "../flb_tests_internal.h"
+
+static void test_placeholder()
+{
+ return;
+}
+
+TEST_LIST = {
+ { "placeholder" , test_placeholder },
+ { 0 }
+};
diff --git a/fluent-bit/tests/internal/aws_compress.c b/fluent-bit/tests/internal/aws_compress.c
new file mode 100644
index 00000000..180a0985
--- /dev/null
+++ b/fluent-bit/tests/internal/aws_compress.c
@@ -0,0 +1,489 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+#include <fluent-bit/flb_info.h>
+#include <fluent-bit/flb_mem.h>
+#include <fluent-bit/flb_utils.h>
+#include <fluent-bit/flb_gzip.h>
+
+#include <fluent-bit/aws/flb_aws_compress.h>
+#include "flb_tests_internal.h"
+
+#define FLB_AWS_COMPRESS_TEST_TYPE_COMPRESS 1
+#define FLB_AWS_COMPRESS_TEST_TYPE_B64_TRUNCATE 2
+
+/* test case definition struct */
+struct flb_aws_test_case {
+ char* compression_keyword;
+ char* in_data;
+ char* expect_out_data_b64;
+ int expect_ret;
+};
+
+/* test loop function declarations */
+static unsigned char * base64_encode(const unsigned char *src, size_t len,
+ size_t *out_len);
+static unsigned char * base64_decode(const unsigned char *src, size_t len,
+ size_t *out_len);
+static void flb_aws_compress_general_test_cases(int test_type,
+ struct flb_aws_test_case *cases,
+ size_t max_out_len,
+ int(*decompress)(void *in_data,
+ size_t in_len,
+ void **out_data,
+ size_t *out_len));
+static void flb_aws_compress_test_cases(struct flb_aws_test_case *cases);
+static void flb_aws_compress_truncate_b64_test_cases__gzip_decode(
+ struct flb_aws_test_case *cases,
+ size_t max_out_len);
+
+/** ------ Test Cases ------ **/
+void test_compression_gzip()
+{
+ struct flb_aws_test_case cases[] =
+ {
+ {
+ "gzip",
+ "hello hello hello hello hello hello",
+ "H4sIAAAAAAAA/8tIzcnJV8jARwIAVzdihSMAAAA=",
+ 0
+ },
+ { 0 }
+ };
+
+ flb_aws_compress_test_cases(cases);
+}
+
+void test_b64_truncated_gzip()
+{
+struct flb_aws_test_case cases[] =
+ {
+ {
+ "gzip",
+ "hello hello hello hello hello hello",
+ "hello hello hello hello hello hello", /* Auto decoded via gzip */
+ 0 /* Expected ret */
+ },
+ { 0 }
+ };
+
+ flb_aws_compress_truncate_b64_test_cases__gzip_decode(cases,
+ 41);
+}
+
+void test_b64_truncated_gzip_truncation()
+{
+struct flb_aws_test_case cases[] =
+ {
+ {
+ "gzip",
+ "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod temp"
+ "or incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, qui"
+ "s nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequ"
+ "at. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum do"
+ "lore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proiden"
+ "t, sunt in culpa qui officia deserunt mollit anim id est laborum. xyz",
+ "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod temp"
+ "or incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, qui"
+ "s nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequ"
+ "at. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum do"
+ "lore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proiden"
+ "t, su[Truncated...]"
+ /*"nt in culpa qui officia deserunt mollit anim id est laborum. xyz",*/
+ "",
+ 0 /* Expected ret */
+ },
+ {
+ "gzip",
+ "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod temp"
+ "or incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, qui"
+ "s nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequ"
+ "at. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum do"
+ "lore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proiden"
+ "t, sunt in culpa qui officia deserunt mollit anim id est laborum.",
+ "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod temp"
+ "or incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, qui"
+ "s nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequ"
+ "at. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum do"
+ "lore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proiden"
+ "t, sunt in culpa qui officia deserunt mollit anim id est laborum.",
+ 0 /* Expected ret */
+ },
+ { 0 }
+ };
+
+ flb_aws_compress_truncate_b64_test_cases__gzip_decode(cases,
+ 381);
+}
+
+void test_b64_truncated_gzip_truncation_buffer_too_small()
+{
+struct flb_aws_test_case cases[] =
+ {
+ {
+ "gzip",
+ "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod temp"
+ "or incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, qui"
+ "s nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequ"
+ "at. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum do"
+ "lore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proiden"
+ "t, sunt in culpa qui officia deserunt mollit anim id est laborum.",
+ "",
+ -1 /* Expected ret */
+ },
+ {
+ "gzip",
+ "",
+ "",
+ -1 /* Expected ret: Buffer too small */
+ },
+ { 0 }
+ };
+
+ flb_aws_compress_truncate_b64_test_cases__gzip_decode(cases,
+ 14);
+}
+
+void test_b64_truncated_gzip_truncation_edge()
+{
+struct flb_aws_test_case cases[] =
+ {
+ /*{
+ "gzip",
+ "",
+ "",
+ 0
+ }, *//* This test case fails, because GZIP can zip empty strings but not unzip */
+ {
+ "gzip",
+ "[Truncated...]", /* Endless loop? */
+ "",
+ -1 /* Expected ret */
+ },
+ { 0 }
+ };
+
+ flb_aws_compress_truncate_b64_test_cases__gzip_decode(cases,
+ 51);
+}
+
+void test_b64_truncated_gzip_truncation_multi_rounds()
+{
+struct flb_aws_test_case cases[] =
+ {
+ {
+ "gzip",
+ "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod temp"
+ "or incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, qui"
+ "s nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequ"
+ "at. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum do"
+ "lore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proiden"
+ "t, sunt in culpa qui officia deserunt mollit anim id est laborum."
+ "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
+ "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
+ "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
+ "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
+ "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
+ "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
+ "", /* First half of the compression is heavy, the second half is light. */
+ "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod temp"
+ "or incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, qui"
+ "s nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequ"
+ "at. Duis aute irure dolor in reprehenderit in voluptate velit es"
+ "[Truncated...]", /* Bad estimation of resizing, 3 truncation iterations
+ * needed */
+ 0 /* Expected ret */
+ },
+ { 0 }
+ };
+
+ flb_aws_compress_truncate_b64_test_cases__gzip_decode(cases,
+ 300);
+}
+
+TEST_LIST = {
+ { "test_compression_gzip", test_compression_gzip },
+ { "test_b64_truncated_gzip", test_b64_truncated_gzip },
+ { "test_b64_truncated_gzip_truncation", test_b64_truncated_gzip_truncation },
+ { "test_b64_truncated_gzip_truncation_buffer_too_small",
+ test_b64_truncated_gzip_truncation_buffer_too_small },
+ { "test_b64_truncated_gzip_truncation_edge",
+ test_b64_truncated_gzip_truncation_edge },
+ { "test_b64_truncated_gzip_truncation_multi_rounds",
+ test_b64_truncated_gzip_truncation_multi_rounds },
+ { 0 }
+};
+
+/** ------ Helper Methods ------ **/
+
+/* test case loop for flb_aws_compress */
+static void flb_aws_compress_test_cases(struct flb_aws_test_case *cases)
+{
+ flb_aws_compress_general_test_cases(FLB_AWS_COMPRESS_TEST_TYPE_COMPRESS,
+ cases, 0, NULL);
+}
+
+/* test case loop for flb_aws_compress */
+static void flb_aws_compress_truncate_b64_test_cases__gzip_decode(
+ struct flb_aws_test_case *cases,
+ size_t max_out_len)
+{
+ flb_aws_compress_general_test_cases(FLB_AWS_COMPRESS_TEST_TYPE_B64_TRUNCATE,
+ cases, max_out_len, &flb_gzip_uncompress);
+}
+
+/* General test case loop flb_aws_compress */
+static void flb_aws_compress_general_test_cases(int test_type,
+ struct flb_aws_test_case *cases,
+ size_t max_out_len,
+ int(*decompress)(void *in_data,
+ size_t in_len,
+ void **out_data,
+ size_t *out_len))
+{
+ int ret;
+ size_t len;
+ int compression_type = FLB_AWS_COMPRESS_NONE;
+ unsigned char* out_data;
+ size_t out_data_len;
+ unsigned char* out_data_b64;
+ size_t out_data_b64_len;
+ struct flb_config *config;
+ struct flb_aws_test_case *tcase = cases;
+
+ config = flb_config_init();
+
+ if (config == NULL) {
+ return;
+ }
+
+ flb_config_exit(config);
+
+ while (tcase->compression_keyword != 0) {
+
+ size_t in_data_len = strlen(tcase->in_data);
+ compression_type = flb_aws_compression_get_type(tcase->compression_keyword);
+
+ TEST_CHECK(compression_type != -1);
+ TEST_MSG("| flb_aws_get_compression_type: failed to get compression type for "
+ "keyword "
+ "%s", tcase->compression_keyword);
+
+ if (test_type == FLB_AWS_COMPRESS_TEST_TYPE_COMPRESS) {
+ ret = flb_aws_compression_compress(compression_type, (void *) tcase->in_data,
+ in_data_len, (void **) &out_data,
+ &out_data_len);
+ }
+ else {
+ ret = flb_aws_compression_b64_truncate_compress(compression_type, max_out_len,
+ (void *) tcase->in_data,
+ in_data_len,
+ (void **) &out_data,
+ &out_data_len);
+ }
+
+ TEST_CHECK(ret == tcase->expect_ret);
+ TEST_MSG("| Expected return value: %i", tcase->expect_ret);
+ TEST_MSG("| Produced return value: %i", ret);
+
+ if (ret != 0) {
+ TEST_MSG("*- For input data: %s", tcase->in_data);
+ ++tcase;
+ continue;
+ }
+
+ if (test_type == FLB_AWS_COMPRESS_TEST_TYPE_COMPRESS) {
+ out_data_b64 = base64_encode(out_data, out_data_len, &out_data_b64_len);
+ /* remove newline character which is a part of this encode algo */
+ --out_data_b64_len;
+ flb_free(out_data);
+ out_data = NULL;
+ }
+ else {
+ /* decode b64 so we can compare plain text */
+ out_data_b64 = base64_decode(out_data, out_data_len, &out_data_b64_len);
+ flb_free(out_data);
+ out_data = out_data_b64;
+ out_data_len = out_data_b64_len;
+ ret = decompress(out_data, out_data_len, (void *)&out_data_b64,
+ &out_data_b64_len);
+ flb_free(out_data);
+ out_data = NULL;
+ if (!TEST_CHECK(ret == 0)) {
+ TEST_MSG("| Decompression failure");
+ out_data_b64 = flb_malloc(1); /* placeholder malloc */
+ }
+ }
+
+ ret = memcmp(tcase->expect_out_data_b64, out_data_b64, out_data_b64_len);
+ TEST_CHECK(ret == 0);
+ TEST_MSG("| Expected output(%s): %s",
+ (test_type == FLB_AWS_COMPRESS_TEST_TYPE_COMPRESS)
+ ? "b64" : "decompressed", tcase->expect_out_data_b64);
+ TEST_MSG("| Produced output(%s): %s",
+ (test_type == FLB_AWS_COMPRESS_TEST_TYPE_COMPRESS)
+ ? "b64" : "decompressed", out_data_b64);
+
+ len = strlen(tcase->expect_out_data_b64);
+ TEST_CHECK(len == out_data_b64_len);
+ TEST_MSG("| Expected length: %zu", len);
+ TEST_MSG("| Produced length: %zu", out_data_b64_len);
+
+ TEST_MSG("*- For input data: %s", tcase->in_data);
+
+ flb_free(out_data_b64);
+ ++tcase;
+ }
+}
+
+/* B64 check script copied from Monkey Auth Plugin */
+/* Change log:
+ * Removed auto new line entry from every 72 characters to make consistant with
+ * the actual base64 conversion
+ */
+/* Copied from monkey/plugins/auth/base64.c */
+
+#define __mem_alloc malloc
+#define __mem_free free
+
+static const unsigned char base64_table[65] =
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+/**
+ * base64_encode - Base64 encode
+ * @src: Data to be encoded
+ * @len: Length of the data to be encoded
+ * @out_len: Pointer to output length variable, or %NULL if not used
+ * Returns: Allocated buffer of out_len bytes of encoded data,
+ * or %NULL on failure
+ *
+ * Caller is responsible for freeing the returned buffer. Returned buffer is
+ * nul terminated to make it easier to use as a C string. The nul terminator is
+ * not included in out_len.
+ */
+static unsigned char * base64_encode(const unsigned char *src, size_t len,
+ size_t *out_len)
+{
+ unsigned char *out, *pos;
+ const unsigned char *end, *in;
+ size_t olen;
+ size_t line_len;
+
+ olen = len * 4 / 3 + 4; /* 3-byte blocks to 4-byte */
+ olen += olen / 72; /* line feeds */
+ olen++; /* nul termination */
+ if (olen < len)
+ return NULL; /* integer overflow */
+
+ out = __mem_alloc(olen);
+
+ if (out == NULL)
+ return NULL;
+
+ end = src + len;
+ in = src;
+ pos = out;
+ line_len = 0;
+ while (end - in >= 3) {
+ *pos++ = base64_table[in[0] >> 2];
+ *pos++ = base64_table[((in[0] & 0x03) << 4) | (in[1] >> 4)];
+ *pos++ = base64_table[((in[1] & 0x0f) << 2) | (in[2] >> 6)];
+ *pos++ = base64_table[in[2] & 0x3f];
+ in += 3;
+ line_len += 4;
+ }
+
+ if (end - in) {
+ *pos++ = base64_table[in[0] >> 2];
+ if (end - in == 1) {
+ *pos++ = base64_table[(in[0] & 0x03) << 4];
+ *pos++ = '=';
+ } else {
+ *pos++ = base64_table[((in[0] & 0x03) << 4) |
+ (in[1] >> 4)];
+ *pos++ = base64_table[(in[1] & 0x0f) << 2];
+ }
+ *pos++ = '=';
+ line_len += 4;
+ }
+
+ if (line_len)
+ *pos++ = '\n';
+
+ *pos = '\0';
+ if (out_len)
+ *out_len = pos - out;
+ return out;
+}
+
+/**
+ * base64_decode - Base64 decode
+ * @src: Data to be decoded
+ * @len: Length of the data to be decoded
+ * @out_len: Pointer to output length variable
+ * Returns: Allocated buffer of out_len bytes of decoded data,
+ * or %NULL on failure
+ *
+ * Caller is responsible for freeing the returned buffer.
+ */
+static unsigned char * base64_decode(const unsigned char *src, size_t len,
+ size_t *out_len)
+{
+ unsigned char dtable[256], *out, *pos, block[4], tmp;
+ size_t i, count, olen;
+ int pad = 0;
+
+ memset(dtable, 0x80, 256);
+ for (i = 0; i < sizeof(base64_table) - 1; i++)
+ dtable[base64_table[i]] = (unsigned char) i;
+ dtable['='] = 0;
+
+ count = 0;
+ for (i = 0; i < len; i++) {
+ if (dtable[src[i]] != 0x80)
+ count++;
+ }
+
+ if (count == 0 || count % 4)
+ return NULL;
+
+ olen = (count / 4 * 3) + 1;
+ pos = out = __mem_alloc(olen);
+ if (out == NULL)
+ return NULL;
+
+ count = 0;
+ for (i = 0; i < len; i++) {
+ tmp = dtable[src[i]];
+ if (tmp == 0x80)
+ continue;
+
+ if (src[i] == '=')
+ pad++;
+ block[count] = tmp;
+ count++;
+ if (count == 4) {
+ *pos++ = (block[0] << 2) | (block[1] >> 4);
+ *pos++ = (block[1] << 4) | (block[2] >> 2);
+ *pos++ = (block[2] << 6) | block[3];
+ count = 0;
+ if (pad) {
+ if (pad == 1)
+ pos--;
+ else if (pad == 2)
+ pos -= 2;
+ else {
+ /* Invalid padding */
+ __mem_free(out);
+ return NULL;
+ }
+ break;
+ }
+ }
+ }
+ *pos = '\0';
+
+ *out_len = pos - out;
+ return out;
+}
+
+/* End of copied base64.c from monkey */
diff --git a/fluent-bit/tests/internal/aws_credentials.c b/fluent-bit/tests/internal/aws_credentials.c
new file mode 100644
index 00000000..2f8a670e
--- /dev/null
+++ b/fluent-bit/tests/internal/aws_credentials.c
@@ -0,0 +1,382 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+#include <fluent-bit/flb_sds.h>
+#include <fluent-bit/flb_aws_credentials.h>
+#include <fluent-bit/flb_mem.h>
+#include <fluent-bit/flb_info.h>
+
+#include "flb_tests_internal.h"
+
+#define ACCESS_KEY "akid"
+#define SECRET_KEY "skid"
+#define TOKEN "token"
+
+/* Credentials Environment Variables */
+#define AWS_ACCESS_KEY_ID "AWS_ACCESS_KEY_ID"
+#define AWS_SECRET_ACCESS_KEY "AWS_SECRET_ACCESS_KEY"
+#define AWS_SESSION_TOKEN "AWS_SESSION_TOKEN"
+
+
+static void unsetenv_credentials()
+{
+ int ret;
+
+ ret = unsetenv(AWS_ACCESS_KEY_ID);
+ if (ret < 0) {
+ flb_errno();
+ return;
+ }
+ ret = unsetenv(AWS_SECRET_ACCESS_KEY);
+ if (ret < 0) {
+ flb_errno();
+ return;
+ }
+ ret = unsetenv(AWS_SESSION_TOKEN);
+ if (ret < 0) {
+ flb_errno();
+ return;
+ }
+}
+
+/* test for the env provider */
+static void test_environment_provider()
+{
+ struct flb_aws_provider *provider;
+ struct flb_aws_credentials *creds;
+ struct flb_config *config;
+ int ret;
+
+ config = flb_config_init();
+
+ if (config == NULL) {
+ return;
+ }
+
+ /* set environment */
+ ret = setenv(AWS_ACCESS_KEY_ID, ACCESS_KEY, 1);
+ if (ret < 0) {
+ flb_errno();
+ flb_config_exit(config);
+ return;
+ }
+ ret = setenv(AWS_SECRET_ACCESS_KEY, SECRET_KEY, 1);
+ if (ret < 0) {
+ flb_errno();
+ flb_config_exit(config);
+ return;
+ }
+ ret = setenv(AWS_SESSION_TOKEN, TOKEN, 1);
+ if (ret < 0) {
+ flb_errno();
+ flb_config_exit(config);
+ return;
+ }
+
+ provider = flb_aws_env_provider_create();
+ if (!provider) {
+ flb_errno();
+ flb_config_exit(config);
+ return;
+ }
+
+ /* repeated calls to get credentials should return the same set */
+ creds = provider->provider_vtable->get_credentials(provider);
+ if (!creds) {
+ flb_errno();
+ flb_config_exit(config);
+ return;
+ }
+ TEST_CHECK(strcmp(ACCESS_KEY, creds->access_key_id) == 0);
+ TEST_CHECK(strcmp(SECRET_KEY, creds->secret_access_key) == 0);
+ TEST_CHECK(strcmp(TOKEN, creds->session_token) == 0);
+
+ flb_aws_credentials_destroy(creds);
+
+ creds = provider->provider_vtable->get_credentials(provider);
+ if (!creds) {
+ flb_errno();
+ flb_config_exit(config);
+ return;
+ }
+ TEST_CHECK(strcmp(ACCESS_KEY, creds->access_key_id) == 0);
+ TEST_CHECK(strcmp(SECRET_KEY, creds->secret_access_key) == 0);
+ TEST_CHECK(strcmp(TOKEN, creds->session_token) == 0);
+
+ flb_aws_credentials_destroy(creds);
+
+ /* refresh should return 0 (success) */
+ ret = provider->provider_vtable->refresh(provider);
+ TEST_CHECK(ret == 0);
+
+ unsetenv_credentials();
+
+ flb_aws_provider_destroy(provider);
+ flb_config_exit(config);
+}
+
+/* token is not required */
+static void test_environment_provider_no_token()
+{
+ struct flb_aws_provider *provider;
+ struct flb_aws_credentials *creds;
+ struct flb_config *config;
+ int ret;
+
+ config = flb_config_init();
+
+ if (config == NULL) {
+ return;
+ }
+
+ /* set environment */
+ ret = setenv(AWS_ACCESS_KEY_ID, ACCESS_KEY, 1);
+ if (ret < 0) {
+ flb_errno();
+ flb_config_exit(config);
+ return;
+ }
+ ret = setenv(AWS_SECRET_ACCESS_KEY, SECRET_KEY, 1);
+ if (ret < 0) {
+ flb_errno();
+ flb_config_exit(config);
+ return;
+ }
+
+ provider = flb_aws_env_provider_create();
+ if (!provider) {
+ flb_errno();
+ flb_config_exit(config);
+ return;
+ }
+
+ /* repeated calls to get credentials should return the same set */
+ creds = provider->provider_vtable->get_credentials(provider);
+ if (!creds) {
+ flb_errno();
+ flb_config_exit(config);
+ return;
+ }
+ TEST_CHECK(strcmp(ACCESS_KEY, creds->access_key_id) == 0);
+ TEST_CHECK(strcmp(SECRET_KEY, creds->secret_access_key) == 0);
+ TEST_CHECK(creds->session_token == NULL);
+
+ flb_aws_credentials_destroy(creds);
+
+ creds = provider->provider_vtable->get_credentials(provider);
+ if (!creds) {
+ flb_errno();
+ flb_config_exit(config);
+ return;
+ }
+ TEST_CHECK(strcmp(ACCESS_KEY, creds->access_key_id) == 0);
+ TEST_CHECK(strcmp(SECRET_KEY, creds->secret_access_key) == 0);
+ TEST_CHECK(creds->session_token == NULL);
+
+ flb_aws_credentials_destroy(creds);
+
+ /* refresh should return 0 (success) */
+ ret = provider->provider_vtable->refresh(provider);
+ TEST_CHECK(ret == 0);
+
+ unsetenv_credentials();
+
+ flb_aws_provider_destroy(provider);
+ flb_config_exit(config);
+}
+
+/* access and secret key are required */
+static void test_environment_provider_only_access()
+{
+ struct flb_aws_provider *provider;
+ struct flb_aws_credentials *creds;
+ struct flb_config *config;
+ int ret;
+
+ unsetenv_credentials();
+
+ config = flb_config_init();
+
+ if (config == NULL) {
+ return;
+ }
+
+ /* set environment */
+ ret = setenv(AWS_ACCESS_KEY_ID, ACCESS_KEY, 1);
+ if (ret < 0) {
+ flb_errno();
+ flb_config_exit(config);
+ return;
+ }
+
+ provider = flb_aws_env_provider_create();
+ if (!provider) {
+ flb_errno();
+ flb_config_exit(config);
+ return;
+ }
+
+ /* repeated calls to get credentials should return the same set */
+ creds = provider->provider_vtable->get_credentials(provider);
+ TEST_CHECK(creds == NULL);
+
+ flb_aws_credentials_destroy(creds);
+
+ creds = provider->provider_vtable->get_credentials(provider);
+ TEST_CHECK(creds == NULL);
+
+ flb_aws_credentials_destroy(creds);
+
+ /* refresh should return -1 (failure) */
+ ret = provider->provider_vtable->refresh(provider);
+ TEST_CHECK(ret < 0);
+
+ unsetenv_credentials();
+
+ flb_aws_provider_destroy(provider);
+ flb_config_exit(config);
+}
+
+/* test the env provider when no cred env vars are set */
+static void test_environment_provider_unset()
+{
+ struct flb_aws_provider *provider;
+ struct flb_aws_credentials *creds;
+ struct flb_config *config;
+ int ret;
+
+ config = flb_config_init();
+
+ if (config == NULL) {
+ return;
+ }
+
+ unsetenv_credentials();
+
+ provider = flb_aws_env_provider_create();
+ if (!provider) {
+ flb_errno();
+ flb_config_exit(config);
+ return;
+ }
+
+ /* repeated calls to get credentials should return the same set */
+ creds = provider->provider_vtable->get_credentials(provider);
+ TEST_CHECK(creds == NULL);
+
+ creds = provider->provider_vtable->get_credentials(provider);
+ TEST_CHECK(creds == NULL);
+
+ /* refresh should return -1 (failure) */
+ ret = provider->provider_vtable->refresh(provider);
+ TEST_CHECK(ret < 0);
+
+ flb_aws_provider_destroy(provider);
+ flb_config_exit(config);
+}
+
+static void test_credential_expiration()
+{
+ struct tm tm = {0};
+ /* one hour in the future */
+ time_t exp_expected = time(NULL) + 3600;
+ char time_stamp[50];
+ time_t exp_actual;
+ TEST_CHECK(gmtime_r(&exp_expected, &tm) != NULL);
+
+ TEST_CHECK(strftime(time_stamp, 50, "%Y-%m-%dT%H:%M:%SZ", &tm) > 0);
+
+ exp_actual = flb_aws_cred_expiration(time_stamp);
+
+ TEST_CHECK(exp_actual == exp_expected);
+}
+
+static void test_standard_chain_provider()
+{
+ struct flb_aws_provider *provider;
+ struct flb_aws_credentials *creds;
+ struct flb_config *config;
+ int ret;
+
+ config = flb_config_init();
+
+ if (config == NULL) {
+ return;
+ }
+
+ /* set environment */
+ ret = setenv(AWS_ACCESS_KEY_ID, ACCESS_KEY, 1);
+ if (ret < 0) {
+ flb_errno();
+ flb_config_exit(config);
+ return;
+ }
+ ret = setenv(AWS_SECRET_ACCESS_KEY, SECRET_KEY, 1);
+ if (ret < 0) {
+ flb_errno();
+ flb_config_exit(config);
+ return;
+ }
+ ret = setenv(AWS_SESSION_TOKEN, TOKEN, 1);
+ if (ret < 0) {
+ flb_errno();
+ flb_config_exit(config);
+ return;
+ }
+
+ provider = flb_standard_chain_provider_create(config, NULL, "us-west-2",
+ "https://sts.us-west-2.amazonaws.com",
+ NULL,
+ flb_aws_client_generator(),
+ NULL);
+ if (!provider) {
+ flb_errno();
+ flb_config_exit(config);
+ return;
+ }
+
+ /* repeated calls to get credentials should return the same set */
+ creds = provider->provider_vtable->get_credentials(provider);
+ if (!creds) {
+ flb_errno();
+ flb_config_exit(config);
+ return;
+ }
+ TEST_CHECK(strcmp(ACCESS_KEY, creds->access_key_id) == 0);
+ TEST_CHECK(strcmp(SECRET_KEY, creds->secret_access_key) == 0);
+ TEST_CHECK(strcmp(TOKEN, creds->session_token) == 0);
+
+ flb_aws_credentials_destroy(creds);
+
+ creds = provider->provider_vtable->get_credentials(provider);
+ if (!creds) {
+ flb_errno();
+ flb_config_exit(config);
+ return;
+ }
+ TEST_CHECK(strcmp(ACCESS_KEY, creds->access_key_id) == 0);
+ TEST_CHECK(strcmp(SECRET_KEY, creds->secret_access_key) == 0);
+ TEST_CHECK(strcmp(TOKEN, creds->session_token) == 0);
+
+ flb_aws_credentials_destroy(creds);
+
+ /* refresh should return 0 (success) */
+ ret = provider->provider_vtable->refresh(provider);
+ TEST_CHECK(ret == 0);
+
+ unsetenv_credentials();
+
+ flb_aws_provider_destroy(provider);
+ flb_config_exit(config);
+}
+
+TEST_LIST = {
+ { "test_credential_expiration" , test_credential_expiration},
+ { "environment_credential_provider" , test_environment_provider},
+ { "environment_provider_no_token" , test_environment_provider_no_token},
+ { "environment_provider_only_access_key" ,
+ test_environment_provider_only_access},
+ { "environment_credential_provider_unset" ,
+ test_environment_provider_unset},
+ { "test_standard_chain_provider" , test_standard_chain_provider},
+ { 0 }
+};
diff --git a/fluent-bit/tests/internal/aws_credentials_ec2.c b/fluent-bit/tests/internal/aws_credentials_ec2.c
new file mode 100644
index 00000000..6370b156
--- /dev/null
+++ b/fluent-bit/tests/internal/aws_credentials_ec2.c
@@ -0,0 +1,1037 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+#include "../include/aws_client_mock.h"
+#include "../include/aws_client_mock.c"
+
+#include <fluent-bit.h>
+#include <fluent-bit/flb_sds.h>
+#include <fluent-bit/flb_aws_credentials.h>
+#include <fluent-bit/aws/flb_aws_imds.h>
+#include <fluent-bit/flb_mem.h>
+#include <fluent-bit/flb_info.h>
+#include <fluent-bit/flb_http_client.h>
+#include <fluent-bit/flb_aws_util.h>
+#include <fluent-bit/flb_log.h>
+
+#include <monkey/mk_core.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "flb_tests_internal.h"
+
+/* Global variables for tests */
+struct flb_aws_provider *provider;
+struct flb_aws_credentials *creds;
+struct flb_config *config;
+struct flb_config *config_fluent;
+int ret;
+
+/*
+ * Hardcoding a copy of the ec2 credential provider struct from flb_aws_credentials_ec2.c
+ * Note: this will require a change if the other copy is changed.
+ * A provider that obtains credentials from EC2 IMDS.
+ */
+struct flb_aws_provider_ec2 {
+ struct flb_aws_credentials *creds;
+ time_t next_refresh;
+
+ /* upstream connection to IMDS */
+ struct flb_aws_client *client;
+
+ /* IMDS interface */
+ struct flb_aws_imds *imds_interface;
+};
+
+/*
+ * Setup test & Initialize test environment
+ * Required for fluent bit debug logs
+ * Note: Log level definitions do not work.
+ * Example: config_fluent->verbose = FLB_LOG_OFF
+ */
+void setup_test(struct flb_aws_client_mock_request_chain *request_chain) {
+ /* Initialize test environment */
+ config_fluent = flb_config_init();
+
+ flb_aws_client_mock_configure_generator(request_chain);
+
+ /* Init provider */
+ config = flb_calloc(1, sizeof(struct flb_config));
+ TEST_ASSERT(config != NULL);
+ mk_list_init(&config->upstreams);
+ provider = flb_ec2_provider_create(config, flb_aws_client_get_mock_generator());
+ TEST_ASSERT(provider != NULL);
+}
+
+/* Test clean up */
+void cleanup_test() {
+ flb_aws_client_mock_destroy_generator();
+ if (provider != NULL) {
+ ((struct flb_aws_provider_ec2 *) (provider->implementation))->client = NULL;
+ flb_aws_provider_destroy(provider);
+ provider = NULL;
+ }
+ if (config != NULL) {
+ flb_free(config);
+ }
+ if (config_fluent != NULL) {
+ flb_config_exit(config_fluent);
+ config_fluent = NULL;
+ }
+}
+
+/*
+ * IMDSv2 -- Test Summary
+ * First call to get_credentials():
+ * -> 2 requests are made to obtain IMDSv2 token
+ * -> 2 requests are made to access credentials
+ * Second call to get_credentials() hits cache:
+ * -> 0 requests are made
+ * refresh():
+ * -> 2 requests are made to access credentials
+ */
+static void test_ec2_provider_v2()
+{
+ setup_test(FLB_AWS_CLIENT_MOCK(
+ /* First call to get_credentials() */
+ response(
+ expect(URI, "/"),
+ expect(HEADER, "X-aws-ec2-metadata-token", "INVALID"),
+ expect(METHOD, FLB_HTTP_GET),
+ set(STATUS, 401)
+ ),
+ response(
+ expect(URI, "/latest/api/token"),
+ expect(HEADER, "X-aws-ec2-metadata-token-ttl-seconds", "21600"), /* 6 hours */
+ expect(METHOD, FLB_HTTP_PUT),
+ set(STATUS, 200),
+ set(PAYLOAD, "AQAAANjUxxxxxxXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX_Q=="),
+ set(PAYLOAD_SIZE, 56)
+ ),
+ response(
+ expect(URI, "/latest/meta-data/iam/security-credentials/"),
+ expect(METHOD, FLB_HTTP_GET),
+ expect(HEADER, "X-aws-ec2-metadata-token", "AQAAANjUxxxxxxXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX_Q=="),
+ set(STATUS, 200),
+ set(PAYLOAD, "My_Instance_Name"),
+ set(PAYLOAD_SIZE, 16)
+ ),
+ response(
+ expect(URI, "/latest/meta-data/iam/security-credentials/My_Instance_Name"),
+ expect(METHOD, FLB_HTTP_GET),
+ expect(HEADER, "X-aws-ec2-metadata-token", "AQAAANjUxxxxxxXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX_Q=="),
+ set(STATUS, 200),
+ set(PAYLOAD, "{\n \"Code\" : \"Success\",\n \"LastUpdated\" : \"2021-09-16T18:29:09Z\",\n"
+ " \"Type\" : \"AWS-HMAC\",\n \"AccessKeyId\" : \"XACCESSEC2XXX\",\n \"SecretAccessKey\""
+ " : \"XSECRETEC2XXXXXXXXXXXXXX\",\n \"Token\" : \"XTOKENEC2XXXXXXXXXXXXXXX==\",\n"
+ " \"Expiration\" : \"3021-09-17T00:41:00Z\"\n}"), /* Expires Year 3021 */
+ set(PAYLOAD_SIZE, 257)
+ ),
+
+ /* Second call to get_credentials() hits cache */
+
+ /* Refresh credentials (No token refesh) */
+ response(
+ expect(URI, "/latest/meta-data/iam/security-credentials/"),
+ expect(METHOD, FLB_HTTP_GET),
+ expect(HEADER, "X-aws-ec2-metadata-token", "AQAAANjUxxxxxxXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX_Q=="),
+ set(STATUS, 200),
+ set(PAYLOAD, "My_Instance_Name_New"),
+ set(PAYLOAD_SIZE, 20)
+ ),
+ response(
+ expect(URI, "/latest/meta-data/iam/security-credentials/My_Instance_Name_New"),
+ expect(METHOD, FLB_HTTP_GET),
+ expect(HEADER, "X-aws-ec2-metadata-token", "AQAAANjUxxxxxxXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX_Q=="),
+ set(STATUS, 200),
+ set(PAYLOAD, "{\n \"Code\" : \"Success\",\n \"LastUpdated\" : \"2021-09-16T18:29:09Z\",\n"
+ " \"Type\" : \"AWS-HMAC\",\n \"AccessKeyId\" : \"YACCESSEC2XXX\",\n \"SecretAccessKey\""
+ " : \"YSECRETEC2XXXXXXXXXXXXXX\",\n \"Token\" : \"YTOKENEC2XXXXXXXXXXXXXXX==\",\n"
+ " \"Expiration\" : \"3021-09-17T00:41:00Z\"\n}"), // Expires Year 3021
+ set(PAYLOAD_SIZE, 257)
+ )
+ ));
+
+ /* Repeated calls to get credentials should return the same set */
+ creds = provider->provider_vtable->get_credentials(provider);
+ TEST_ASSERT(creds != NULL);
+ TEST_CHECK(strcmp("XACCESSEC2XXX", creds->access_key_id) == 0);
+ TEST_CHECK(strcmp("XSECRETEC2XXXXXXXXXXXXXX", creds->secret_access_key) == 0);
+ TEST_CHECK(strcmp("XTOKENEC2XXXXXXXXXXXXXXX==", creds->session_token) == 0);
+
+ flb_aws_credentials_destroy(creds);
+
+ /* Retrieve from cache */
+ creds = provider->provider_vtable->get_credentials(provider);
+ TEST_ASSERT(creds != NULL);
+ TEST_CHECK(strcmp("XACCESSEC2XXX", creds->access_key_id) == 0);
+ TEST_CHECK(strcmp("XSECRETEC2XXXXXXXXXXXXXX", creds->secret_access_key) == 0);
+ TEST_CHECK(strcmp("XTOKENEC2XXXXXXXXXXXXXXX==", creds->session_token) == 0);
+
+ flb_aws_credentials_destroy(creds);
+
+ /* refresh should return 0 (success) */
+ ret = provider->provider_vtable->refresh(provider);
+ TEST_CHECK(ret == 0);
+
+ /* Retrieve refreshed credentials from cache */
+ creds = provider->provider_vtable->get_credentials(provider);
+ TEST_ASSERT(creds != NULL);
+ TEST_CHECK(strcmp("YACCESSEC2XXX", creds->access_key_id) == 0);
+ TEST_CHECK(strcmp("YSECRETEC2XXXXXXXXXXXXXX", creds->secret_access_key) == 0);
+ TEST_CHECK(strcmp("YTOKENEC2XXXXXXXXXXXXXXX==", creds->session_token) == 0);
+
+ flb_aws_credentials_destroy(creds);
+
+ /* Check we have exhausted our response list */
+ TEST_CHECK(flb_aws_client_mock_generator_count_unused_requests() == 0);
+
+ cleanup_test();
+}
+
+/*
+ * IMDSv1 -- Fallback Test Summary
+ * First call to get_credentials():
+ * -> 1 requests is made to test for IMDSv2
+ * -> 2 requests are made to access credentials
+ * Second call to get_credentials() hits cache
+ * -> 0 requests are made
+ * refresh():
+ * -> 2 requests are made to access credentials
+ */
+static void test_ec2_provider_v1()
+{
+ setup_test(FLB_AWS_CLIENT_MOCK(
+ /* First call to get_credentials() */
+ response(
+ expect(URI, "/"),
+ expect(HEADER, "X-aws-ec2-metadata-token", "INVALID"),
+ expect(HEADER_COUNT, 1),
+ expect(METHOD, FLB_HTTP_GET),
+ set(STATUS, 200)
+ ),
+ response(
+ expect(URI, "/latest/meta-data/iam/security-credentials/"),
+ expect(METHOD, FLB_HTTP_GET),
+ expect(HEADER_COUNT, 0),
+ set(STATUS, 200),
+ set(PAYLOAD, "My_Instance_Name"),
+ set(PAYLOAD_SIZE, 16)
+ ),
+ response(
+ expect(URI, "/latest/meta-data/iam/security-credentials/My_Instance_Name"),
+ expect(METHOD, FLB_HTTP_GET),
+ expect(HEADER_COUNT, 0),
+ set(STATUS, 200),
+ set(PAYLOAD, "{\n \"Code\" : \"Success\",\n \"LastUpdated\" : \"2021-09-16T18:29:09Z\",\n"
+ " \"Type\" : \"AWS-HMAC\",\n \"AccessKeyId\" : \"XACCESSEC2XXX\",\n \"SecretAccessKey\""
+ " : \"XSECRETEC2XXXXXXXXXXXXXX\",\n \"Token\" : \"XTOKENEC2XXXXXXXXXXXXXXX==\",\n"
+ " \"Expiration\" : \"3021-09-17T00:41:00Z\"\n}"), /* Expires Year 3021 */
+ set(PAYLOAD_SIZE, 257)
+ ),
+
+ /* Second call to get_credentials() hits cache */
+
+ /* Refresh credentials (No token refesh) */
+ response(
+ expect(URI, "/latest/meta-data/iam/security-credentials/"),
+ expect(METHOD, FLB_HTTP_GET),
+ expect(HEADER_COUNT, 0),
+ set(STATUS, 200),
+ set(PAYLOAD, "My_Instance_Name_New"),
+ set(PAYLOAD_SIZE, 20)
+ ),
+ response(
+ expect(URI, "/latest/meta-data/iam/security-credentials/My_Instance_Name_New"),
+ expect(METHOD, FLB_HTTP_GET),
+ expect(HEADER_COUNT, 0),
+ set(STATUS, 200),
+ set(PAYLOAD, "{\n \"Code\" : \"Success\",\n \"LastUpdated\" : \"2021-09-16T18:29:09Z\",\n"
+ " \"Type\" : \"AWS-HMAC\",\n \"AccessKeyId\" : \"YACCESSEC2XXX\",\n \"SecretAccessKey\""
+ " : \"YSECRETEC2XXXXXXXXXXXXXX\",\n \"Token\" : \"YTOKENEC2XXXXXXXXXXXXXXX==\",\n"
+ " \"Expiration\" : \"3021-09-17T00:41:00Z\"\n}"), // Expires Year 3021
+ set(PAYLOAD_SIZE, 257)
+ )
+ ));
+
+ /* Repeated calls to get credentials should return the same set */
+ creds = provider->provider_vtable->get_credentials(provider);
+ TEST_ASSERT(creds != NULL);
+ TEST_CHECK(strcmp("XACCESSEC2XXX", creds->access_key_id) == 0);
+ TEST_CHECK(strcmp("XSECRETEC2XXXXXXXXXXXXXX", creds->secret_access_key) == 0);
+ TEST_CHECK(strcmp("XTOKENEC2XXXXXXXXXXXXXXX==", creds->session_token) == 0);
+
+ flb_aws_credentials_destroy(creds);
+
+ /* Retrieve from cache */
+ creds = provider->provider_vtable->get_credentials(provider);
+ TEST_ASSERT(creds != NULL);
+ TEST_CHECK(strcmp("XACCESSEC2XXX", creds->access_key_id) == 0);
+ TEST_CHECK(strcmp("XSECRETEC2XXXXXXXXXXXXXX", creds->secret_access_key) == 0);
+ TEST_CHECK(strcmp("XTOKENEC2XXXXXXXXXXXXXXX==", creds->session_token) == 0);
+
+ flb_aws_credentials_destroy(creds);
+
+ /* refresh should return 0 (success) */
+ ret = provider->provider_vtable->refresh(provider);
+ TEST_CHECK(ret == 0);
+
+ /* Retrieve refreshed credentials from cache */
+ creds = provider->provider_vtable->get_credentials(provider);
+ TEST_ASSERT(creds != NULL);
+ TEST_CHECK(strcmp("YACCESSEC2XXX", creds->access_key_id) == 0);
+ TEST_CHECK(strcmp("YSECRETEC2XXXXXXXXXXXXXX", creds->secret_access_key) == 0);
+ TEST_CHECK(strcmp("YTOKENEC2XXXXXXXXXXXXXXX==", creds->session_token) == 0);
+
+ flb_aws_credentials_destroy(creds);
+
+ /* Check we have exhausted our response list */
+ TEST_CHECK(flb_aws_client_mock_generator_count_unused_requests() == 0);
+
+ cleanup_test();
+}
+
+/*
+ * IMDSv1 -- IMDSv2 Timeout, Fallback Test Summary
+ * First call to get_credentials():
+ * -> 1 requests is made to test for IMDSv2 (IMDSv2)
+ * -> 1 request made to get token (Timeout failure)
+ * -> 1 request made to check IMDSv1 fallback (Failure - IMDSv1 not allowed)
+ *
+ * Second call to get_credentials():
+ * -> 1 requests is made to test for IMDSv2 (IMDSv2)
+ * -> 1 request made to get token (Timeout failure)
+ * -> 1 request made to check IMDSv1 fallback (Success)
+ * -> 2 requests are made to access credentials
+ * Second call to get_credentials() hits cache
+ * -> 0 requests are made
+ * refresh():
+ * -> 2 requests are made to access credentials
+ */
+static void test_ec2_provider_v1_v2_timeout()
+{
+ setup_test(FLB_AWS_CLIENT_MOCK(
+ /* First call to get_credentials() */
+ response(
+ expect(URI, "/"),
+ expect(HEADER, "X-aws-ec2-metadata-token", "INVALID"),
+ expect(HEADER_COUNT, 1),
+ expect(METHOD, FLB_HTTP_GET),
+ set(STATUS, 401)
+ ),
+ response(
+ expect(URI, "/latest/api/token"),
+ expect(HEADER, "X-aws-ec2-metadata-token-ttl-seconds", "21600"), /* 6 hours */
+ expect(METHOD, FLB_HTTP_PUT),
+ config(REPLACE, (struct flb_http_client *) NULL) /* Replicate timeout failure */
+ ),
+ response(
+ expect(URI, "/"),
+ expect(METHOD, FLB_HTTP_GET),
+ set(STATUS, 401) /* IMDSv1 not allowed */
+ ),
+
+ /* Second call to get_credentials() */
+ response(
+ expect(URI, "/"),
+ expect(HEADER, "X-aws-ec2-metadata-token", "INVALID"),
+ expect(HEADER_COUNT, 1),
+ expect(METHOD, FLB_HTTP_GET),
+ set(STATUS, 401)
+ ),
+ response(
+ expect(URI, "/latest/api/token"),
+ expect(HEADER, "X-aws-ec2-metadata-token-ttl-seconds", "21600"), /* 6 hours */
+ expect(METHOD, FLB_HTTP_PUT),
+ config(REPLACE, (struct flb_http_client *) NULL) /* Replicate timeout failure */
+ ),
+ response(
+ expect(URI, "/"),
+ expect(METHOD, FLB_HTTP_GET),
+ set(STATUS, 200) /* IMDSv1 is allowed */
+ ),
+ response(
+ expect(URI, "/latest/meta-data/iam/security-credentials/"),
+ expect(METHOD, FLB_HTTP_GET),
+ expect(HEADER_COUNT, 0),
+ set(STATUS, 200),
+ set(PAYLOAD, "My_Instance_Name"),
+ set(PAYLOAD_SIZE, 16)
+ ),
+ response(
+ expect(URI, "/latest/meta-data/iam/security-credentials/My_Instance_Name"),
+ expect(METHOD, FLB_HTTP_GET),
+ expect(HEADER_COUNT, 0),
+ set(STATUS, 200),
+ set(PAYLOAD, "{\n \"Code\" : \"Success\",\n \"LastUpdated\" : \"2021-09-16T18:29:09Z\",\n"
+ " \"Type\" : \"AWS-HMAC\",\n \"AccessKeyId\" : \"XACCESSEC2XXX\",\n \"SecretAccessKey\""
+ " : \"XSECRETEC2XXXXXXXXXXXXXX\",\n \"Token\" : \"XTOKENEC2XXXXXXXXXXXXXXX==\",\n"
+ " \"Expiration\" : \"3021-09-17T00:41:00Z\"\n}"), /* Expires Year 3021 */
+ set(PAYLOAD_SIZE, 257)
+ ),
+
+ /* Second call to get_credentials() hits cache */
+
+ /* Refresh credentials (No token refesh) */
+ response(
+ expect(URI, "/latest/meta-data/iam/security-credentials/"),
+ expect(METHOD, FLB_HTTP_GET),
+ expect(HEADER_COUNT, 0),
+ set(STATUS, 200),
+ set(PAYLOAD, "My_Instance_Name_New"),
+ set(PAYLOAD_SIZE, 20)
+ ),
+ response(
+ expect(URI, "/latest/meta-data/iam/security-credentials/My_Instance_Name_New"),
+ expect(METHOD, FLB_HTTP_GET),
+ expect(HEADER_COUNT, 0),
+ set(STATUS, 200),
+ set(PAYLOAD, "{\n \"Code\" : \"Success\",\n \"LastUpdated\" : \"2021-09-16T18:29:09Z\",\n"
+ " \"Type\" : \"AWS-HMAC\",\n \"AccessKeyId\" : \"YACCESSEC2XXX\",\n \"SecretAccessKey\""
+ " : \"YSECRETEC2XXXXXXXXXXXXXX\",\n \"Token\" : \"YTOKENEC2XXXXXXXXXXXXXXX==\",\n"
+ " \"Expiration\" : \"3021-09-17T00:41:00Z\"\n}"), // Expires Year 3021
+ set(PAYLOAD_SIZE, 257)
+ )
+ ));
+
+ /* First call: IMDSv1 and IMDSv2 not accessible */
+ creds = provider->provider_vtable->get_credentials(provider);
+ TEST_ASSERT(creds == NULL);
+
+ /*
+ * Second call: IMDSv2 timeout, IMDSv1 accessible
+ * Repeated calls to get credentials should return the same set
+ */
+ creds = provider->provider_vtable->get_credentials(provider);
+ TEST_ASSERT(creds != NULL);
+ TEST_CHECK(strcmp("XACCESSEC2XXX", creds->access_key_id) == 0);
+ TEST_CHECK(strcmp("XSECRETEC2XXXXXXXXXXXXXX", creds->secret_access_key) == 0);
+ TEST_CHECK(strcmp("XTOKENEC2XXXXXXXXXXXXXXX==", creds->session_token) == 0);
+
+ flb_aws_credentials_destroy(creds);
+
+ /* Retrieve from cache */
+ creds = provider->provider_vtable->get_credentials(provider);
+ TEST_ASSERT(creds != NULL);
+ TEST_CHECK(strcmp("XACCESSEC2XXX", creds->access_key_id) == 0);
+ TEST_CHECK(strcmp("XSECRETEC2XXXXXXXXXXXXXX", creds->secret_access_key) == 0);
+ TEST_CHECK(strcmp("XTOKENEC2XXXXXXXXXXXXXXX==", creds->session_token) == 0);
+
+ flb_aws_credentials_destroy(creds);
+
+ /* refresh should return 0 (success) */
+ ret = provider->provider_vtable->refresh(provider);
+ TEST_CHECK(ret == 0);
+
+ /* Retrieve refreshed credentials from cache */
+ creds = provider->provider_vtable->get_credentials(provider);
+ TEST_ASSERT(creds != NULL);
+ TEST_CHECK(strcmp("YACCESSEC2XXX", creds->access_key_id) == 0);
+ TEST_CHECK(strcmp("YSECRETEC2XXXXXXXXXXXXXX", creds->secret_access_key) == 0);
+ TEST_CHECK(strcmp("YTOKENEC2XXXXXXXXXXXXXXX==", creds->session_token) == 0);
+
+ flb_aws_credentials_destroy(creds);
+
+ /* Check we have exhausted our response list */
+ TEST_CHECK(flb_aws_client_mock_generator_count_unused_requests() == 0);
+
+ cleanup_test();
+}
+
+
+/*
+ * IMDS Version Detection Error -- Test Summary
+ * First call to get_credentials():
+ * -> 1 request made to test for IMDSv2 (Fails, 404)
+ * Second call to get_credentials():
+ * -> 1 request made to test for IMDSv2 (Fails, null http_client)
+ * Third call to get_credentials():
+ * -> 1 request made to test for IMDSv2 (Success)
+ * -> 1 request made to get token (Failure)
+ * Fourth call to get_credentials():
+ * -> 2 requests made to obtain IMDSv2 token (Success)
+ * -> 2 requests made to access credentials (Success)
+ */
+static void test_ec2_provider_version_detection_error()
+{
+ setup_test(FLB_AWS_CLIENT_MOCK(
+ /* First call to get_credentials(): Version detection failure */
+ response(
+ expect(URI, "/"),
+ expect(HEADER, "X-aws-ec2-metadata-token", "INVALID"),
+ expect(HEADER_COUNT, 1),
+ expect(METHOD, FLB_HTTP_GET),
+ set(STATUS, 404) // IMDS not found (not likely to happen)
+ ),
+
+ /* Second call to get_credentials(): Version detection failure */
+ response(
+ expect(URI, "/"),
+ expect(HEADER, "X-aws-ec2-metadata-token", "INVALID"),
+ expect(METHOD, FLB_HTTP_GET),
+ config(REPLACE, (struct flb_http_client *) NULL)
+ ),
+
+ /* Third call to get_credentials(): Version detection success */
+ response(
+ expect(URI, "/"),
+ expect(HEADER, "X-aws-ec2-metadata-token", "INVALID"),
+ expect(METHOD, FLB_HTTP_GET),
+ set(STATUS, 401)
+ ),
+ response(
+ expect(URI, "/latest/api/token"),
+ expect(HEADER, "X-aws-ec2-metadata-token-ttl-seconds", "21600"), /* 6 hours */
+ expect(METHOD, FLB_HTTP_PUT),
+ set(STATUS, 200),
+ set(PAYLOAD, "AQAAANjUxxxxxxXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX_Q=="),
+ set(PAYLOAD_SIZE, 56)
+ ),
+ response(
+ expect(URI, "/latest/meta-data/iam/security-credentials/"),
+ expect(METHOD, FLB_HTTP_GET),
+ expect(HEADER, "X-aws-ec2-metadata-token", "AQAAANjUxxxxxxXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX_Q=="),
+ set(STATUS, 200),
+ set(PAYLOAD, "My_Instance_Name"),
+ set(PAYLOAD_SIZE, 16)
+ ),
+ response(
+ expect(URI, "/latest/meta-data/iam/security-credentials/My_Instance_Name"),
+ expect(METHOD, FLB_HTTP_GET),
+ expect(HEADER, "X-aws-ec2-metadata-token", "AQAAANjUxxxxxxXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX_Q=="),
+ set(STATUS, 200),
+ set(PAYLOAD, "{\n \"Code\" : \"Success\",\n \"LastUpdated\" : \"2021-09-16T18:29:09Z\",\n"
+ " \"Type\" : \"AWS-HMAC\",\n \"AccessKeyId\" : \"XACCESSEC2XXX\",\n \"SecretAccessKey\""
+ " : \"XSECRETEC2XXXXXXXXXXXXXX\",\n \"Token\" : \"XTOKENEC2XXXXXXXXXXXXXXX==\",\n"
+ " \"Expiration\" : \"3021-09-17T00:41:00Z\"\n}"), /* Expires Year 3021 */
+ set(PAYLOAD_SIZE, 257)
+ )
+ ));
+
+ /* Version detection failure: Status 404 */
+ creds = provider->provider_vtable->get_credentials(provider);
+ TEST_CHECK(creds == NULL);
+
+ /* Version detection failure: NULL response */
+ creds = provider->provider_vtable->get_credentials(provider);
+ TEST_CHECK(creds == NULL);
+
+ /* Version detection success: IMDSv2 */
+ creds = provider->provider_vtable->get_credentials(provider);
+ TEST_ASSERT(creds != NULL);
+ TEST_CHECK(strcmp("XACCESSEC2XXX", creds->access_key_id) == 0);
+ TEST_CHECK(strcmp("XSECRETEC2XXXXXXXXXXXXXX", creds->secret_access_key) == 0);
+ TEST_CHECK(strcmp("XTOKENEC2XXXXXXXXXXXXXXX==", creds->session_token) == 0);
+
+ flb_aws_credentials_destroy(creds);
+
+ /* Retrieve from cache */
+ creds = provider->provider_vtable->get_credentials(provider);
+ TEST_ASSERT(creds != NULL);
+ TEST_CHECK(strcmp("XACCESSEC2XXX", creds->access_key_id) == 0);
+ TEST_CHECK(strcmp("XSECRETEC2XXXXXXXXXXXXXX", creds->secret_access_key) == 0);
+ TEST_CHECK(strcmp("XTOKENEC2XXXXXXXXXXXXXXX==", creds->session_token) == 0);
+
+ flb_aws_credentials_destroy(creds);
+
+ /* Check we have exhausted our response list */
+ TEST_CHECK(flb_aws_client_mock_generator_count_unused_requests() == 0);
+
+ cleanup_test();
+}
+
+/*
+ * IMDS Aquire Token Error -- Test Summary
+ * First call to get_credentials():
+ * -> 1 request made to test for IMDSv2 (Success)
+ * -> 1 request made to obtain IMDSv2 token (Fails) <-* Aquire token error
+ * -> 1 request made to check IMDSv1 fallback (Unauthorized)
+ * Second call to get_credentials():
+ * -> 1 request made to access instance name (Invalid token)
+ * -> 1 request made to obtain IMDSv2 token (Success)
+ * -> 1 request made to access instance name (Success)
+ * -> 1 request made to access credentials (Invalid token)
+ * -> 1 request made to obtain IMDSv2 token (Fails) <-* Aquire token error
+ * Third call to get_credentials():
+ * -> 1 request made to access instance name (Invalid token)
+ * -> 1 request made to obtain IMDSv2 token (Success)
+ * -> 1 request made to access instance name (Success)
+ * -> 1 request made to access credentials (Invalid token)
+ * -> 1 request made to obtain IMDSv2 token (Success)
+ * -> 1 request made to access credentials (Success, but credentials are expired as of Year 2000)
+ * Fourth call to get_credentials(): - hits expired cache
+ * -> 1 request made to access credentials (Invalid token)
+ * -> 1 request made to obtain IMDSv2 token (http_client is null) <-* Aquire token error
+ * Fifth call to get_credentials(): - hits expired cache
+ * -> 1 request made to access credentials (Invalid token)
+ * -> 1 request made to obtain IMDSv2 token (Success)
+ * -> 2 requests are made to access credentials
+ */
+static void test_ec2_provider_acquire_token_error()
+{
+ setup_test(FLB_AWS_CLIENT_MOCK(
+
+ /*
+ * First call to get_credentials():
+ * -> 1 request made to test for IMDSv2 (Success)
+ * -> 1 request made to obtain IMDSv2 token (Fails) <-* Aquire token error
+ * -> 1 request made to check IMDSv1 fallback (Unauthorized)
+ */
+ response(
+ expect(URI, "/"),
+ expect(HEADER, "X-aws-ec2-metadata-token", "INVALID"), /* Why is this not invalid_token? */
+ expect(HEADER_COUNT, 1),
+ expect(METHOD, FLB_HTTP_GET),
+ set(STATUS, 401) /* IMDSv2 */
+ ),
+ response(
+ expect(URI, "/latest/api/token"),
+ expect(HEADER, "X-aws-ec2-metadata-token-ttl-seconds", "21600"), /* 6 hours */
+ expect(METHOD, FLB_HTTP_PUT),
+ config(REPLACE, NULL) /* HTTP Client is null */
+ ),
+ response(
+ expect(URI, "/"),
+ expect(METHOD, FLB_HTTP_GET),
+ set(STATUS, 401) /* IMDSv1 not allowed */
+ ),
+
+ /*
+ * Second call to get_credentials():
+ * -> 1 request made to test for IMDSv2 (Success)
+ * -> 1 request made to obtain IMDSv2 token (Success) <-* Bad token
+ * -> 1 request made to access instance name (Invalid token)
+ * -> 1 request made to obtain IMDSv2 token (Success)
+ * -> 1 request made to access instance name (Success)
+ * -> 1 request made to access credentials (Invalid token)
+ * -> 1 request made to obtain IMDSv2 token (Fails) <-* Aquire token error
+ */
+ response(
+ expect(URI, "/"),
+ expect(HEADER, "X-aws-ec2-metadata-token", "INVALID"), /* Why is this not invalid_token? */
+ expect(HEADER_COUNT, 1),
+ expect(METHOD, FLB_HTTP_GET),
+ set(STATUS, 401) /* IMDSv2 */
+ ),
+ response(
+ expect(URI, "/latest/api/token"),
+ expect(HEADER, "X-aws-ec2-metadata-token-ttl-seconds", "21600"), /* 6 hours */
+ expect(METHOD, FLB_HTTP_PUT),
+ set(STATUS, 200),
+ set(PAYLOAD, "BAD_ANjUxxxxxxXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX_Q=="),
+ set(PAYLOAD_SIZE, 56)
+ ),
+ response(
+ expect(URI, "/latest/meta-data/iam/security-credentials/"),
+ expect(METHOD, FLB_HTTP_GET),
+ expect(HEADER, "X-aws-ec2-metadata-token", "BAD_ANjUxxxxxxXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX_Q=="), /* Token failed to be set */
+ set(STATUS, 401) /* Unauthorized, bad token */
+ ),
+ response(
+ expect(URI, "/latest/api/token"),
+ expect(HEADER, "X-aws-ec2-metadata-token-ttl-seconds", "21600"), /* 6 hours */
+ expect(METHOD, FLB_HTTP_PUT),
+ set(STATUS, 200),
+ set(PAYLOAD, "AQAAANjUxxxxxxXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX_Q=="),
+ set(PAYLOAD_SIZE, 56)
+ ),
+ response(
+ expect(URI, "/latest/meta-data/iam/security-credentials/"),
+ expect(METHOD, FLB_HTTP_GET),
+ expect(HEADER, "X-aws-ec2-metadata-token", "AQAAANjUxxxxxxXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX_Q=="),
+ set(STATUS, 200),
+ set(PAYLOAD, "My_Instance_Name"),
+ set(PAYLOAD_SIZE, 16)
+ ),
+ response(
+ expect(URI, "/latest/meta-data/iam/security-credentials/My_Instance_Name"),
+ expect(METHOD, FLB_HTTP_GET),
+ expect(HEADER, "X-aws-ec2-metadata-token", "AQAAANjUxxxxxxXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX_Q=="),
+ set(STATUS, 401) /* Unauthorized, bad token */
+ ),
+ response(
+ expect(URI, "/latest/api/token"),
+ expect(HEADER, "X-aws-ec2-metadata-token-ttl-seconds", "21600"), /* 6 hours */
+ expect(METHOD, FLB_HTTP_PUT),
+ set(STATUS, 404), /* This should never actually happen */
+ set(PAYLOAD, "Token not found"),
+ set(PAYLOAD_SIZE, 15)
+ ),
+
+ /*
+ * Third call to get_credentials():
+  * -> 1 request made to access instance name (Invalid token)
+  * -> 1 request made to obtain IMDSv2 token (Success)
+  * -> 1 request made to access instance name (Success)
+  * -> 1 request made to access credentials (Invalid token)
+  * -> 1 request made to obtain IMDSv2 token (Success)
+  * -> 1 request made to access credentials (Success, but credentials are expired as of Year 2000)
+ */
+ response(
+ expect(URI, "/latest/meta-data/iam/security-credentials/"),
+ expect(METHOD, FLB_HTTP_GET),
+ expect(HEADER, "X-aws-ec2-metadata-token", "AQAAANjUxxxxxxXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX_Q=="), /* Token failed to be set */
+ set(STATUS, 401) /* Unauthorized, bad token */
+ ),
+ response(
+ expect(URI, "/latest/api/token"),
+ expect(HEADER, "X-aws-ec2-metadata-token-ttl-seconds", "21600"), /* 6 hours */
+ expect(METHOD, FLB_HTTP_PUT),
+ set(STATUS, 200),
+ set(PAYLOAD, "AQAAANjUxxxxxxXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX_Q=="),
+ set(PAYLOAD_SIZE, 56)
+ ),
+ response(
+ expect(URI, "/latest/meta-data/iam/security-credentials/"),
+ expect(METHOD, FLB_HTTP_GET),
+ expect(HEADER, "X-aws-ec2-metadata-token", "AQAAANjUxxxxxxXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX_Q=="),
+ set(STATUS, 200),
+ set(PAYLOAD, "My_Instance_Name"),
+ set(PAYLOAD_SIZE, 16)
+ ),
+ response(
+ expect(URI, "/latest/meta-data/iam/security-credentials/My_Instance_Name"),
+ expect(METHOD, FLB_HTTP_GET),
+ expect(HEADER, "X-aws-ec2-metadata-token", "AQAAANjUxxxxxxXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX_Q=="),
+ set(STATUS, 401) /* Unauthorized, bad token */
+ ),
+ response(
+ expect(URI, "/latest/api/token"),
+ expect(HEADER, "X-aws-ec2-metadata-token-ttl-seconds", "21600"), /* 6 hours */
+ expect(METHOD, FLB_HTTP_PUT),
+ set(STATUS, 200),
+ set(PAYLOAD, "AQAAANjUxxxxxxXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX_Q=="),
+ set(PAYLOAD_SIZE, 56)
+ ),
+ response(
+ expect(URI, "/latest/meta-data/iam/security-credentials/My_Instance_Name"),
+ expect(METHOD, FLB_HTTP_GET),
+ expect(HEADER, "X-aws-ec2-metadata-token", "AQAAANjUxxxxxxXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX_Q=="),
+ set(STATUS, 200),
+ set(PAYLOAD, "{\n \"Code\" : \"Success\",\n \"LastUpdated\" : \"2021-09-16T18:29:09Z\",\n"
+ " \"Type\" : \"AWS-HMAC\",\n \"AccessKeyId\" : \"XACCESSEC2XXX\",\n \"SecretAccessKey\""
+ " : \"XSECRETEC2XXXXXXXXXXXXXX\",\n \"Token\" : \"XTOKENEC2XXXXXXXXXXXXXXX==\",\n"
+ " \"Expiration\" : \"2000-09-17T00:41:00Z\"\n}"), /* Expires Year 2000 */
+ set(PAYLOAD_SIZE, 257)
+ ),
+
+ /*
+ * Fourth call to get_credentials(): - hits expired cache
+ * -> 1 request made to access credentials (Invalid token)
+ * -> 1 request made to obtain IMDSv2 token (http_client is null) <-* Aquire token error
+ */
+ response(
+ expect(URI, "/latest/meta-data/iam/security-credentials/"),
+ expect(METHOD, FLB_HTTP_GET),
+ expect(HEADER, "X-aws-ec2-metadata-token", "AQAAANjUxxxxxxXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX_Q=="),
+ set(STATUS, 401) /* Unauthorized, bad token */
+ ),
+ response(
+ expect(URI, "/latest/api/token"),
+ expect(HEADER, "X-aws-ec2-metadata-token-ttl-seconds", "21600"), /* 6 hours */
+ expect(METHOD, FLB_HTTP_PUT),
+ config(REPLACE, NULL) /* HTTP Client is null */
+ ),
+
+ /*
+ * Fifth call to get_credentials(): - hits expired cache
+ * -> 1 request made to access credentials (Invalid token)
+ * -> 1 request made to obtain IMDSv2 token (Success)
+ * -> 2 requests are made to access credentials
+ */
+ response(
+ expect(URI, "/latest/meta-data/iam/security-credentials/"),
+ expect(METHOD, FLB_HTTP_GET),
+ expect(HEADER, "X-aws-ec2-metadata-token", "AQAAANjUxxxxxxXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX_Q=="),
+ set(STATUS, 401) /* Unauthorized, bad token */
+ ),
+ response(
+ expect(URI, "/latest/api/token"),
+ expect(HEADER, "X-aws-ec2-metadata-token-ttl-seconds", "21600"), /* 6 hours */
+ expect(METHOD, FLB_HTTP_PUT),
+ set(STATUS, 200),
+ set(PAYLOAD, "AQAAANjUxxxxxxXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX_Q=="),
+ set(PAYLOAD_SIZE, 56)
+ ),
+ response(
+ expect(URI, "/latest/meta-data/iam/security-credentials/"),
+ expect(METHOD, FLB_HTTP_GET),
+ expect(HEADER, "X-aws-ec2-metadata-token", "AQAAANjUxxxxxxXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX_Q=="),
+ set(STATUS, 200),
+ set(PAYLOAD, "My_Instance_Name"),
+ set(PAYLOAD_SIZE, 16)
+ ),
+ response(
+ expect(URI, "/latest/meta-data/iam/security-credentials/My_Instance_Name"),
+ expect(METHOD, FLB_HTTP_GET),
+ expect(HEADER, "X-aws-ec2-metadata-token", "AQAAANjUxxxxxxXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX_Q=="),
+ set(STATUS, 200),
+ set(PAYLOAD, "{\n \"Code\" : \"Success\",\n \"LastUpdated\" : \"2021-09-16T18:29:09Z\",\n"
+ " \"Type\" : \"AWS-HMAC\",\n \"AccessKeyId\" : \"YACCESSEC2XXX\",\n \"SecretAccessKey\""
+ " : \"YSECRETEC2XXXXXXXXXXXXXX\",\n \"Token\" : \"YTOKENEC2XXXXXXXXXXXXXXX==\",\n"
+ " \"Expiration\" : \"3021-09-17T00:41:00Z\"\n}"), /* Expires Year 3021 */
+ set(PAYLOAD_SIZE, 257)
+ )
+ ));
+
+ /* 1. Aquire token error */
+ creds = provider->provider_vtable->get_credentials(provider);
+ TEST_CHECK(creds == NULL);
+
+ /* 2. Aquire token error */
+ creds = provider->provider_vtable->get_credentials(provider);
+ TEST_CHECK(creds == NULL);
+
+ /* 3. Aquire token success */
+ creds = provider->provider_vtable->get_credentials(provider);
+ TEST_ASSERT(creds != NULL);
+ TEST_CHECK(strcmp("XACCESSEC2XXX", creds->access_key_id) == 0);
+ TEST_CHECK(strcmp("XSECRETEC2XXXXXXXXXXXXXX", creds->secret_access_key) == 0);
+ TEST_CHECK(strcmp("XTOKENEC2XXXXXXXXXXXXXXX==", creds->session_token) == 0);
+
+ flb_aws_credentials_destroy(creds);
+
+ /* 4. Aquire token error */
+ creds = provider->provider_vtable->get_credentials(provider);
+ TEST_ASSERT(creds != NULL);
+ TEST_CHECK(strcmp("XACCESSEC2XXX", creds->access_key_id) == 0);
+ TEST_CHECK(strcmp("XSECRETEC2XXXXXXXXXXXXXX", creds->secret_access_key) == 0);
+ TEST_CHECK(strcmp("XTOKENEC2XXXXXXXXXXXXXXX==", creds->session_token) == 0);
+
+ flb_aws_credentials_destroy(creds); /* Remains unchanged */
+
+ /* 5. Aquire token success */
+ creds = provider->provider_vtable->get_credentials(provider);
+ TEST_ASSERT(creds != NULL);
+ TEST_CHECK(strcmp("YACCESSEC2XXX", creds->access_key_id) == 0);
+ TEST_CHECK(strcmp("YSECRETEC2XXXXXXXXXXXXXX", creds->secret_access_key) == 0);
+ TEST_CHECK(strcmp("YTOKENEC2XXXXXXXXXXXXXXX==", creds->session_token) == 0);
+
+ flb_aws_credentials_destroy(creds);
+
+ /* Check we have exhausted our response list */
+ TEST_CHECK(flb_aws_client_mock_generator_count_unused_requests() == 0);
+
+ cleanup_test();
+}
+
+/*
+ * IMDS Metadata Request Failure -- Test Summary
+ * First call to get_credentials():
+ * -> 2 requests are made to obtain IMDSv2 token
+ * -> 1 request is made to access instance name (fails, null client)
+ * Second call to get_credentials(): -- token cached
+ * -> 1 request is made to access instance name (success)
+ * -> 1 request is made to access credentials (fails, 404)
+ * Third call to get_credentials(): -- token cached
+ * -> 1 request is made to access instance name
+ * -> 1 request is made to access credentials (fails, garbage fuzz input)
+ * Fourth call to get_credentials(): -- token cached
+ * -> 1 request is made to access instance name
+ * -> 1 request is made to access credentials (success)
+ */
+static void test_ec2_provider_metadata_request_error()
+{
+ setup_test(FLB_AWS_CLIENT_MOCK(
+ /* First call to get_credentials() */
+ response(
+ expect(URI, "/"),
+ expect(HEADER, "X-aws-ec2-metadata-token", "INVALID"),
+ expect(METHOD, FLB_HTTP_GET),
+ set(STATUS, 401)
+ ),
+ response(
+ expect(URI, "/latest/api/token"),
+ expect(HEADER, "X-aws-ec2-metadata-token-ttl-seconds", "21600"), /* 6 hours */
+ expect(METHOD, FLB_HTTP_PUT),
+ set(STATUS, 200),
+ set(PAYLOAD, "AQAAANjUxxxxxxXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX_Q=="),
+ set(PAYLOAD_SIZE, 56)
+ ),
+ response(
+ expect(URI, "/latest/meta-data/iam/security-credentials/"),
+ expect(METHOD, FLB_HTTP_GET),
+ expect(HEADER, "X-aws-ec2-metadata-token", "AQAAANjUxxxxxxXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX_Q=="),
+ config(REPLACE, NULL)
+ ),
+
+ /* Second call to get_credentials() */
+ response(
+ expect(URI, "/latest/meta-data/iam/security-credentials/"),
+ expect(METHOD, FLB_HTTP_GET),
+ expect(HEADER, "X-aws-ec2-metadata-token", "AQAAANjUxxxxxxXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX_Q=="),
+ set(STATUS, 200),
+ set(PAYLOAD, "My_Instance_Name"),
+ set(PAYLOAD_SIZE, 16)
+ ),
+ response(
+ expect(URI, "/latest/meta-data/iam/security-credentials/My_Instance_Name"),
+ expect(METHOD, FLB_HTTP_GET),
+ expect(HEADER, "X-aws-ec2-metadata-token", "AQAAANjUxxxxxxXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX_Q=="),
+ set(STATUS, 404),
+ set(PAYLOAD, "IMDS server not found"), /* This should never happen */
+ set(PAYLOAD_SIZE, 21)
+ ),
+
+ /* Third call to get_credentials() */
+ response(
+ expect(URI, "/latest/meta-data/iam/security-credentials/"),
+ expect(METHOD, FLB_HTTP_GET),
+ expect(HEADER, "X-aws-ec2-metadata-token", "AQAAANjUxxxxxxXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX_Q=="),
+ set(STATUS, 200),
+ set(PAYLOAD, "My_Instance_Name"),
+ set(PAYLOAD_SIZE, 16)
+ ),
+ response(
+ expect(URI, "/latest/meta-data/iam/security-credentials/My_Instance_Name"),
+ expect(METHOD, FLB_HTTP_GET),
+ expect(HEADER, "X-aws-ec2-metadata-token", "AQAAANjUxxxxxxXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX_Q=="),
+ set(STATUS, 200),
+ set(PAYLOAD, ("{tsJ@&K+Xo9?'a,uxb)=/iZg\"M4B\\&,qb"
+ "Y\\%%niHubN^I[#arpu9|A':W!JZ@?frM|\""
+ "?aVK<WS3ziAp:\"d=VD(Mu<Bl(e6I?G.3"
+ "rI!f5OxfmkJ)ePc\\5@dGIA!q${iVCF3*"
+ "#y6z<5Nal\\Wmp!0gpeoG!#iY[H&@350v"
+ "$!)i4?6!&}uQ]3%%b25I._H{!.4{42T ,"
+ "b{f\\jpAQ\"j>~<L%%<;k4uh5d+;\\%%soDl<F"
+ "+\\j|WvhX+V)Xb)&tF&'Gj:2Q8@/m3Y46"
+ "79HrM2^1sg11b94kP$)x-*A)ueiR=&L\""
+ "-Hd&sN[M[#BS;ZVIn#j7Lj{E`7 c:%%bK")), /* Random text fuzz */
+ set(PAYLOAD_SIZE, 328)
+ ),
+ response(
+ expect(URI, "/latest/meta-data/iam/security-credentials/"),
+ expect(METHOD, FLB_HTTP_GET),
+ expect(HEADER, "X-aws-ec2-metadata-token", "AQAAANjUxxxxxxXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX_Q=="),
+ set(STATUS, 200),
+ set(PAYLOAD, "My_Instance_Name"),
+ set(PAYLOAD_SIZE, 16)
+ ),
+ response(
+ expect(URI, "/latest/meta-data/iam/security-credentials/My_Instance_Name"),
+ expect(METHOD, FLB_HTTP_GET),
+ expect(HEADER, "X-aws-ec2-metadata-token", "AQAAANjUxxxxxxXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX_Q=="),
+ set(STATUS, 200),
+ set(PAYLOAD, "{\n \"Code\" : \"Success\",\n \"LastUpdated\" : \"2021-09-16T18:29:09Z\",\n"
+ " \"Type\" : \"AWS-HMAC\",\n \"AccessKeyId\" : \"XACCESSEC2XXX\",\n \"SecretAccessKey\""
+ " : \"XSECRETEC2XXXXXXXXXXXXXX\",\n \"Token\" : \"XTOKENEC2XXXXXXXXXXXXXXX==\",\n"
+ " \"Expiration\" : \"3021-09-17T00:41:00Z\"\n}"), /* Expires Year 3021 */
+ set(PAYLOAD_SIZE, 257)
+ )
+ ));
+
+ /* First call to get_credentials() */
+ creds = provider->provider_vtable->get_credentials(provider);
+ TEST_CHECK(creds == NULL);
+
+ /* Second call to get_credentials() */
+ creds = provider->provider_vtable->get_credentials(provider);
+ TEST_CHECK(creds == NULL);
+
+ /* Third call to get_credentials() */
+ creds = provider->provider_vtable->get_credentials(provider);
+ TEST_CHECK(creds == NULL);
+
+ /* Fourth call to get_credentials() */
+ creds = provider->provider_vtable->get_credentials(provider);
+ TEST_ASSERT(creds != NULL);
+ TEST_CHECK(strcmp("XACCESSEC2XXX", creds->access_key_id) == 0);
+ TEST_CHECK(strcmp("XSECRETEC2XXXXXXXXXXXXXX", creds->secret_access_key) == 0);
+ TEST_CHECK(strcmp("XTOKENEC2XXXXXXXXXXXXXXX==", creds->session_token) == 0);
+
+ flb_aws_credentials_destroy(creds);
+
+ /* Retrieve from cache */
+ creds = provider->provider_vtable->get_credentials(provider);
+ TEST_ASSERT(creds != NULL);
+ TEST_CHECK(strcmp("XACCESSEC2XXX", creds->access_key_id) == 0);
+ TEST_CHECK(strcmp("XSECRETEC2XXXXXXXXXXXXXX", creds->secret_access_key) == 0);
+ TEST_CHECK(strcmp("XTOKENEC2XXXXXXXXXXXXXXX==", creds->session_token) == 0);
+
+ flb_aws_credentials_destroy(creds);
+
+ /* Check we have exhausted our response list */
+ TEST_CHECK(flb_aws_client_mock_generator_count_unused_requests() == 0);
+
+ cleanup_test();
+}
+
+/* IMDS specific testing */
+
+/*
+ * IMDS Creation and Destruction -- Test Summary
+ * First call to flb_aws_imds_create (fail)
+ * -> upstream not set
+ * Second call to flb_aws_imds_create (fail)
+ * -> upstream set
+ * -> upstream tcp not equal to IMDS (random text)
+ * Third call to flb_aws_imds_create (fail)
+ * -> upstream set
+ * -> upstream tcp not equal to IMDS (one fewer character)
+ * Fourth call to flb_aws_imds_create (fail)
+ * -> upstream set
+ * -> upstream tcp not equal to IMDS (one extra character)
+ * Fifth call to flb_aws_imds_create (fail)
+ * -> upstream set
+ * -> upstream tcp equal to IMDS address
+ * -> upstream port not equal to IMDS port (number 0)
+ * Sixth call to flb_aws_imds_create (success)
+ * -> upstream set
+ * -> upstream tcp equal to IMDS address
+ * -> upstream port equal to IMDS port (80)
+ * First call to flb_aws_imds_destroy (success)
+ */
+static void test_ec2_imds_create_and_destroy()
+{
+ /* Full test setup not needed */
+ /* Initialize test environment */
+ config_fluent = flb_config_init();
+
+ struct flb_aws_imds_config i_config = flb_aws_imds_config_default;
+ struct flb_aws_client a_client = { 0 };
+ struct flb_aws_imds* imds;
+
+ struct flb_upstream u_stream = { 0 };
+
+ /* First call to flb_aws_imds_create */
+ imds = flb_aws_imds_create(&i_config, &a_client);
+ TEST_CHECK(imds == NULL);
+
+ /* Second call to flb_aws_imds_create */
+ a_client.upstream = &u_stream;
+ u_stream.tcp_host = "Invalid host";
+ imds = flb_aws_imds_create(&i_config, &a_client);
+ TEST_CHECK(imds == NULL);
+
+ /* Third call to flb_aws_imds_create */
+ u_stream.tcp_host = "169.254.169.254ExtraInvalid";
+ imds = flb_aws_imds_create(&i_config, &a_client);
+ TEST_CHECK(imds == NULL);
+
+ /* Fourth call to flb_aws_imds_create */
+ u_stream.tcp_host = "169.254.169.254";
+ u_stream.tcp_port = 0xBAD;
+ imds = flb_aws_imds_create(&i_config, &a_client);
+ TEST_CHECK(imds == NULL);
+
+ /* Fifth call to flb_aws_imds_create */
+ u_stream.tcp_host = "169.254.169.254";
+ u_stream.tcp_port = 80;
+ imds = flb_aws_imds_create(&i_config, &a_client);
+ TEST_CHECK(imds != NULL);
+
+ /* Destruction */
+ flb_aws_imds_destroy(imds);
+
+ flb_config_exit(config_fluent);
+}
+
+TEST_LIST = {
+ { "test_ec2_provider_v2" , test_ec2_provider_v2},
+ { "test_ec2_provider_v1" , test_ec2_provider_v1},
+ { "test_ec2_provider_v1_v2_timeout" , test_ec2_provider_v1_v2_timeout},
+ { "test_ec2_provider_version_detection_error" , test_ec2_provider_version_detection_error},
+ { "test_ec2_provider_acquire_token_error" , test_ec2_provider_acquire_token_error},
+ { "test_ec2_provider_metadata_request_error" , test_ec2_provider_metadata_request_error},
+ { "test_ec2_imds_create_and_destroy" , test_ec2_imds_create_and_destroy},
+ { 0 }
+};
diff --git a/fluent-bit/tests/internal/aws_credentials_http.c b/fluent-bit/tests/internal/aws_credentials_http.c
new file mode 100644
index 00000000..55912da3
--- /dev/null
+++ b/fluent-bit/tests/internal/aws_credentials_http.c
@@ -0,0 +1,382 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+#include <fluent-bit/flb_sds.h>
+#include <fluent-bit/flb_aws_credentials.h>
+#include <fluent-bit/flb_mem.h>
+#include <fluent-bit/flb_info.h>
+#include <fluent-bit/flb_http_client.h>
+
+#include <monkey/mk_core.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "flb_tests_internal.h"
+
+#define ACCESS_KEY_HTTP "http_akid"
+#define SECRET_KEY_HTTP "http_skid"
+#define TOKEN_HTTP "http_token"
+
+#define HTTP_CREDENTIALS_RESPONSE "{\n\
+ \"AccessKeyId\": \"http_akid\",\n\
+ \"Expiration\": \"2025-10-24T23:00:23Z\",\n\
+ \"RoleArn\": \"TASK_ROLE_ARN\",\n\
+ \"SecretAccessKey\": \"http_skid\",\n\
+ \"Token\": \"http_token\"\n\
+}"
+
+/*
+ * Unexpected/invalid HTTP response. The goal of this is not to test anything
+ * that might happen in production, but rather to test the error handling
+ * code for the providers. This helps ensure all code paths are tested and
+ * the error handling code does not introduce memory leaks.
+ */
+#define HTTP_RESPONSE_MALFORMED "{\n\
+ \"AccessKeyId\": \"http_akid\",\n\
+ \"partially-correct\": \"json\",\n\
+ \"RoleArn\": \"TASK_ROLE_ARN\",\n\
+ \"but incomplete\": \"and not terminated with a closing brace\",\n\
+ \"Token\": \"http_token\""
+
+
+/*
+ * Global Variable that allows us to check the number of calls
+ * made in each test
+ */
+int g_request_count;
+
+struct flb_http_client *request_happy_case(struct flb_aws_client *aws_client,
+ int method, const char *uri)
+{
+ struct flb_http_client *c = NULL;
+
+ TEST_CHECK(method == FLB_HTTP_GET);
+
+ TEST_CHECK(strstr(uri, "happy-case") != NULL);
+
+ /* create an http client so that we can set the response */
+ c = flb_calloc(1, sizeof(struct flb_http_client));
+ if (!c) {
+ flb_errno();
+ return NULL;
+ }
+ mk_list_init(&c->headers);
+
+ c->resp.status = 200;
+ c->resp.payload = HTTP_CREDENTIALS_RESPONSE;
+ c->resp.payload_size = strlen(HTTP_CREDENTIALS_RESPONSE);
+
+ return c;
+}
+
+/* unexpected output test- see description for HTTP_RESPONSE_MALFORMED */
+struct flb_http_client *request_malformed(struct flb_aws_client *aws_client,
+ int method, const char *uri)
+{
+ struct flb_http_client *c = NULL;
+
+ TEST_CHECK(method == FLB_HTTP_GET);
+
+ TEST_CHECK(strstr(uri, "malformed") != NULL);
+
+ /* create an http client so that we can set the response */
+ c = flb_calloc(1, sizeof(struct flb_http_client));
+ if (!c) {
+ flb_errno();
+ return NULL;
+ }
+ mk_list_init(&c->headers);
+
+ c->resp.status = 200;
+ c->resp.payload = HTTP_RESPONSE_MALFORMED;
+ c->resp.payload_size = strlen(HTTP_RESPONSE_MALFORMED);
+
+ return c;
+}
+
+struct flb_http_client *request_error_case(struct flb_aws_client *aws_client,
+ int method, const char *uri)
+{
+ struct flb_http_client *c = NULL;
+
+ TEST_CHECK(method == FLB_HTTP_GET);
+
+ TEST_CHECK(strstr(uri, "error-case") != NULL);
+
+ /* create an http client so that we can set the response */
+ c = flb_calloc(1, sizeof(struct flb_http_client));
+ if (!c) {
+ flb_errno();
+ return NULL;
+ }
+ mk_list_init(&c->headers);
+
+ c->resp.status = 400;
+ c->resp.payload = NULL;
+ c->resp.payload_size = 0;
+
+ return c;
+}
+
+/* test/mock version of the flb_aws_client request function */
+struct flb_http_client *test_http_client_request(struct flb_aws_client *aws_client,
+ int method, const char *uri,
+ const char *body, size_t body_len,
+ struct flb_aws_header *dynamic_headers,
+ size_t dynamic_headers_len)
+{
+ g_request_count++;
+ /*
+ * route to the correct test case fn using the uri
+ */
+ if (strstr(uri, "happy-case") != NULL) {
+ return request_happy_case(aws_client, method, uri);
+ } else if (strstr(uri, "error-case") != NULL) {
+ return request_error_case(aws_client, method, uri);
+ } else if (strstr(uri, "malformed") != NULL) {
+ return request_malformed(aws_client, method, uri);
+ }
+
+ /* uri should match one of the above conditions */
+ flb_errno();
+ return NULL;
+
+}
+
+/* Test/mock flb_aws_client */
+static struct flb_aws_client_vtable test_vtable = {
+ .request = test_http_client_request,
+};
+
+struct flb_aws_client *test_http_client_create()
+{
+ struct flb_aws_client *client = flb_calloc(1,
+ sizeof(struct flb_aws_client));
+ if (!client) {
+ flb_errno();
+ return NULL;
+ }
+ client->client_vtable = &test_vtable;
+ return client;
+}
+
+/* Generator that returns clients with the test vtable */
+static struct flb_aws_client_generator test_generator = {
+ .create = test_http_client_create,
+};
+
+struct flb_aws_client_generator *generator_in_test()
+{
+ return &test_generator;
+}
+
+/* http and ecs providers */
+static void test_http_provider()
+{
+ struct flb_aws_provider *provider;
+ struct flb_aws_credentials *creds;
+ int ret;
+ struct flb_config *config;
+ flb_sds_t host;
+ flb_sds_t path;
+
+ g_request_count = 0;
+
+ config = flb_config_init();
+
+ if (config == NULL) {
+ return;
+ }
+
+ host = flb_sds_create("127.0.0.1");
+ if (!host) {
+ flb_errno();
+ flb_config_exit(config);
+ return;
+ }
+ path = flb_sds_create("/happy-case");
+ if (!path) {
+ flb_errno();
+ flb_config_exit(config);
+ return;
+ }
+
+ provider = flb_http_provider_create(config, host, path,
+ generator_in_test());
+
+ if (!provider) {
+ flb_errno();
+ flb_config_exit(config);
+ return;
+ }
+
+ /* repeated calls to get credentials should return the same set */
+ creds = provider->provider_vtable->get_credentials(provider);
+ if (!creds) {
+ flb_errno();
+ flb_config_exit(config);
+ return;
+ }
+ TEST_CHECK(strcmp(ACCESS_KEY_HTTP, creds->access_key_id) == 0);
+ TEST_CHECK(strcmp(SECRET_KEY_HTTP, creds->secret_access_key) == 0);
+ TEST_CHECK(strcmp(TOKEN_HTTP, creds->session_token) == 0);
+
+ flb_aws_credentials_destroy(creds);
+
+ creds = provider->provider_vtable->get_credentials(provider);
+ if (!creds) {
+ flb_errno();
+ flb_config_exit(config);
+ return;
+ }
+ TEST_CHECK(strcmp(ACCESS_KEY_HTTP, creds->access_key_id) == 0);
+ TEST_CHECK(strcmp(SECRET_KEY_HTTP, creds->secret_access_key) == 0);
+ TEST_CHECK(strcmp(TOKEN_HTTP, creds->session_token) == 0);
+
+ flb_aws_credentials_destroy(creds);
+
+ /* refresh should return 0 (success) */
+ ret = provider->provider_vtable->refresh(provider);
+ TEST_CHECK(ret == 0);
+
+ /*
+ * Request count should be 2:
+ * - One for the first call to get_credentials (2nd should hit cred cache)
+ * - One for the call to refresh
+ */
+ TEST_CHECK(g_request_count == 2);
+
+ flb_aws_provider_destroy(provider);
+ flb_config_exit(config);
+}
+
+static void test_http_provider_error_case()
+{
+ struct flb_aws_provider *provider;
+ struct flb_aws_credentials *creds;
+ int ret;
+ struct flb_config *config;
+ flb_sds_t host;
+ flb_sds_t path;
+
+ g_request_count = 0;
+
+ config = flb_config_init();
+
+ if (config == NULL) {
+ return;
+ }
+
+ host = flb_sds_create("127.0.0.1");
+ if (!host) {
+ flb_errno();
+ flb_config_exit(config);
+ return;
+ }
+ path = flb_sds_create("/error-case");
+ if (!path) {
+ flb_errno();
+ flb_config_exit(config);
+ return;
+ }
+
+ provider = flb_http_provider_create(config, host, path,
+ generator_in_test());
+
+ if (!provider) {
+ flb_errno();
+ flb_config_exit(config);
+ return;
+ }
+
+ /* get_credentials will fail */
+ creds = provider->provider_vtable->get_credentials(provider);
+ TEST_CHECK(creds == NULL);
+
+ creds = provider->provider_vtable->get_credentials(provider);
+ TEST_CHECK(creds == NULL);
+
+ /* refresh should return -1 (failure) */
+ ret = provider->provider_vtable->refresh(provider);
+ TEST_CHECK(ret < 0);
+
+ /*
+ * Request count should be 3:
+ * - Each call to get_credentials and refresh invokes the client's
+ * request method and returns a request failure.
+ */
+ TEST_CHECK(g_request_count == 3);
+
+ flb_aws_provider_destroy(provider);
+ flb_config_exit(config);
+}
+
+static void test_http_provider_malformed_response()
+{
+ struct flb_aws_provider *provider;
+ struct flb_aws_credentials *creds;
+ int ret;
+ struct flb_config *config;
+ flb_sds_t host;
+ flb_sds_t path;
+
+ g_request_count = 0;
+
+ config = flb_config_init();
+
+ if (config == NULL) {
+ return;
+ }
+
+ mk_list_init(&config->upstreams);
+
+ host = flb_sds_create("127.0.0.1");
+ if (!host) {
+ flb_errno();
+ flb_config_exit(config);
+ return;
+ }
+ path = flb_sds_create("/malformed");
+ if (!path) {
+ flb_errno();
+ flb_config_exit(config);
+ return;
+ }
+
+ provider = flb_http_provider_create(config, host, path,
+ generator_in_test());
+
+ if (!provider) {
+ flb_errno();
+ flb_config_exit(config);
+ return;
+ }
+
+ /* get_credentials will fail */
+ creds = provider->provider_vtable->get_credentials(provider);
+ TEST_CHECK(creds == NULL);
+
+ creds = provider->provider_vtable->get_credentials(provider);
+ TEST_CHECK(creds == NULL);
+
+ /* refresh should return -1 (failure) */
+ ret = provider->provider_vtable->refresh(provider);
+ TEST_CHECK(ret < 0);
+
+ /*
+ * Request count should be 3:
+ * - Each call to get_credentials and refresh invokes the client's
+ * request method and returns a request failure.
+ */
+ TEST_CHECK(g_request_count == 3);
+
+ flb_aws_provider_destroy(provider);
+ flb_config_exit(config);
+}
+
+TEST_LIST = {
+ { "test_http_provider" , test_http_provider},
+ { "test_http_provider_error_case" , test_http_provider_error_case},
+ { "test_http_provider_malformed_response" ,
+ test_http_provider_malformed_response},
+ { 0 }
+};
diff --git a/fluent-bit/tests/internal/aws_credentials_process.c b/fluent-bit/tests/internal/aws_credentials_process.c
new file mode 100644
index 00000000..3add81fe
--- /dev/null
+++ b/fluent-bit/tests/internal/aws_credentials_process.c
@@ -0,0 +1,496 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+#include <fluent-bit/flb_sds.h>
+#include <fluent-bit/flb_aws_credentials.h>
+#include <fluent-bit/flb_mem.h>
+#include <fluent-bit/flb_info.h>
+
+#include "aws_credentials_test_internal.h"
+
+#define _TOSTRING(X) #X
+#define TOSTRING(X) _TOSTRING(X)
+#define TESTCASE_NAME() "aws_credentials_process.c:" TOSTRING(__LINE__)
+
+#define MUST_SETENV(name, value) TEST_ASSERT(setenv(name, value, 1) == 0)
+#define MUST_UNSETENV(name) TEST_ASSERT(unsetenv(name) == 0)
+
+static int unset_process_env()
+{
+ int ret;
+
+ ret = unset_profile_env();
+ if (ret < 0) {
+ return -1;
+ }
+
+ ret = unsetenv("_AWS_SECRET_ACCESS_KEY");
+ if (ret < 0) {
+ flb_errno();
+ return -1;
+ }
+
+ ret = unsetenv("_AWS_SESSION_TOKEN");
+ if (ret < 0) {
+ flb_errno();
+ return -1;
+ }
+
+ ret = unsetenv("_AWS_EXPIRATION");
+ if (ret < 0) {
+ flb_errno();
+ return -1;
+ }
+
+ ret = unsetenv("_AWS_EXIT_CODE");
+ if (ret < 0) {
+ flb_errno();
+ return -1;
+ }
+
+ return 0;
+}
+
+static void test_credential_process_default(void)
+{
+ struct flb_aws_provider *provider;
+ struct flb_aws_credentials* creds;
+ char* original_path = getenv("PATH");
+
+ /* Print a newline so the test output starts on its own line. */
+ fprintf(stderr, "\n");
+
+ TEST_CHECK(unset_process_env() == 0);
+
+ MUST_SETENV("AWS_CONFIG_FILE", AWS_TEST_DATA_PATH("shared_config.ini"));
+ MUST_SETENV("PATH", AWS_TEST_DATA_PATH("credential_process"));
+
+ provider = flb_profile_provider_create(NULL);
+ TEST_ASSERT(provider != NULL);
+
+ /* These environment variables are used by the test credential_process. */
+ MUST_SETENV("_AWS_SECRET_ACCESS_KEY", "aws_secret_access_key");
+ MUST_SETENV("_AWS_SESSION_TOKEN", "aws_session_token");
+ MUST_SETENV("_AWS_EXPIRATION", "+15 minutes");
+
+ creds = provider->provider_vtable->get_credentials(provider);
+ TEST_ASSERT(creds != NULL);
+
+ TEST_CHECK(strcmp("default", creds->access_key_id) == 0);
+ TEST_CHECK(strcmp("aws_secret_access_key", creds->secret_access_key) == 0);
+ TEST_CHECK(strcmp("aws_session_token", creds->session_token) == 0);
+
+ flb_aws_credentials_destroy(creds);
+ creds = NULL;
+
+ /* Repeated calls to get_credentials should return the cached credentials. */
+
+ MUST_SETENV("_AWS_SECRET_ACCESS_KEY", "aws_secret_access_key_2");
+ MUST_SETENV("_AWS_SESSION_TOKEN", "aws_session_token_2");
+
+ creds = provider->provider_vtable->get_credentials(provider);
+ TEST_ASSERT(creds != NULL);
+
+ TEST_CHECK(strcmp("default", creds->access_key_id) == 0);
+ TEST_CHECK(strcmp("aws_secret_access_key", creds->secret_access_key) == 0);
+ TEST_CHECK(strcmp("aws_session_token", creds->session_token) == 0);
+
+ flb_aws_credentials_destroy(creds);
+ creds = NULL;
+
+ /* Calling refresh should fetch the new credentials. */
+
+ MUST_SETENV("_AWS_SECRET_ACCESS_KEY", "aws_secret_access_key_3");
+ MUST_SETENV("_AWS_SESSION_TOKEN", "aws_session_token_3");
+
+ TEST_ASSERT(provider->provider_vtable->refresh(provider) == 0);
+
+ creds = provider->provider_vtable->get_credentials(provider);
+ TEST_ASSERT(creds != NULL);
+
+ TEST_CHECK(strcmp("default", creds->access_key_id) == 0);
+ TEST_CHECK(strcmp("aws_secret_access_key_3", creds->secret_access_key) == 0);
+ TEST_CHECK(strcmp("aws_session_token_3", creds->session_token) == 0);
+
+ flb_aws_credentials_destroy(creds);
+ creds = NULL;
+
+ flb_aws_provider_destroy(provider);
+ provider = NULL;
+
+ if (original_path) {
+ MUST_SETENV("PATH", original_path);
+ } else {
+ MUST_UNSETENV("PATH");
+ }
+}
+
+static void test_credential_process_no_expiration(void)
+{
+ struct flb_aws_provider *provider;
+ struct flb_aws_credentials* creds;
+ char* original_path = getenv("PATH");
+
+ /* Print a newline so the test output starts on its own line. */
+ fprintf(stderr, "\n");
+
+ TEST_CHECK(unset_process_env() == 0);
+
+ MUST_SETENV("AWS_CONFIG_FILE", AWS_TEST_DATA_PATH("shared_config.ini"));
+ MUST_SETENV("AWS_PROFILE", "nondefault");
+ MUST_SETENV("PATH", AWS_TEST_DATA_PATH("credential_process"));
+
+ provider = flb_profile_provider_create(NULL);
+ TEST_ASSERT(provider != NULL);
+
+ /* These environment variables are used by the test credential_process. */
+ MUST_SETENV("_AWS_SECRET_ACCESS_KEY", "aws_secret_access_key");
+ MUST_SETENV("_AWS_SESSION_TOKEN", "aws_session_token");
+
+ creds = provider->provider_vtable->get_credentials(provider);
+ TEST_ASSERT(creds != NULL);
+
+ TEST_CHECK(strcmp("nondefault", creds->access_key_id) == 0);
+ TEST_CHECK(strcmp("aws_secret_access_key", creds->secret_access_key) == 0);
+ TEST_CHECK(strcmp("aws_session_token", creds->session_token) == 0);
+
+ flb_aws_credentials_destroy(creds);
+ creds = NULL;
+
+ /* Repeated calls to get_credentials should return the cached credentials. */
+
+ MUST_SETENV("_AWS_SECRET_ACCESS_KEY", "aws_secret_access_key_2");
+ MUST_SETENV("_AWS_SESSION_TOKEN", "aws_session_token_2");
+
+ creds = provider->provider_vtable->get_credentials(provider);
+ TEST_ASSERT(creds != NULL);
+
+ TEST_CHECK(strcmp("nondefault", creds->access_key_id) == 0);
+ TEST_CHECK(strcmp("aws_secret_access_key", creds->secret_access_key) == 0);
+ TEST_CHECK(strcmp("aws_session_token", creds->session_token) == 0);
+
+ flb_aws_credentials_destroy(creds);
+ creds = NULL;
+
+ /* Calling refresh should fetch the new credentials. */
+
+ MUST_SETENV("_AWS_SECRET_ACCESS_KEY", "aws_secret_access_key_3");
+ MUST_SETENV("_AWS_SESSION_TOKEN", "aws_session_token_3");
+
+ TEST_ASSERT(provider->provider_vtable->refresh(provider) == 0);
+
+ creds = provider->provider_vtable->get_credentials(provider);
+ TEST_ASSERT(creds != NULL);
+
+ TEST_CHECK(strcmp("nondefault", creds->access_key_id) == 0);
+ TEST_CHECK(strcmp("aws_secret_access_key_3", creds->secret_access_key) == 0);
+ TEST_CHECK(strcmp("aws_session_token_3", creds->session_token) == 0);
+
+ flb_aws_credentials_destroy(creds);
+ creds = NULL;
+
+ flb_aws_provider_destroy(provider);
+ provider = NULL;
+
+ if (original_path) {
+ MUST_SETENV("PATH", original_path);
+ } else {
+ MUST_UNSETENV("PATH");
+ }
+}
+
+struct credential_process_expired_testcase {
+ char* name;
+ char* expiration;
+};
+
+struct credential_process_expired_testcase credential_process_expired_testcases[] = {
+ /* Credentials that have already expired will be refreshed. */
+ {
+ .name = "expired",
+ .expiration = "-5 minutes",
+ },
+
+ /* Credentials that expire within the next minute will be refreshed. */
+ {
+ .name = "expiring soon",
+ .expiration = "+30 seconds",
+ },
+};
+
+static void test_credential_process_expired_helper(char* expiration)
+{
+ struct flb_aws_provider *provider;
+ struct flb_aws_credentials* creds;
+ char* original_path = getenv("PATH");
+
+ TEST_CHECK(unset_process_env() == 0);
+
+ MUST_SETENV("AWS_CONFIG_FILE", AWS_TEST_DATA_PATH("shared_config.ini"));
+ MUST_SETENV("AWS_PROFILE", "nondefault");
+ MUST_SETENV("PATH", AWS_TEST_DATA_PATH("credential_process"));
+
+ provider = flb_profile_provider_create(NULL);
+ TEST_ASSERT(provider != NULL);
+
+ /* These environment variable are used by the test credential_process. */
+ MUST_SETENV("_AWS_SECRET_ACCESS_KEY", "aws_secret_access_key");
+ if (expiration) {
+ MUST_SETENV("_AWS_EXPIRATION", expiration);
+ }
+
+ creds = provider->provider_vtable->get_credentials(provider);
+ TEST_ASSERT(creds != NULL);
+
+ TEST_CHECK(strcmp("nondefault", creds->access_key_id) == 0);
+ TEST_CHECK(strcmp("aws_secret_access_key", creds->secret_access_key) == 0);
+ TEST_CHECK(creds->session_token == NULL);
+
+ flb_aws_credentials_destroy(creds);
+ creds = NULL;
+
+ /* Repeated calls to get_credentials should fetch new credentials. */
+
+ MUST_SETENV("_AWS_SECRET_ACCESS_KEY", "aws_secret_access_key_2");
+
+ creds = provider->provider_vtable->get_credentials(provider);
+ TEST_ASSERT(creds != NULL);
+
+ TEST_CHECK(strcmp("nondefault", creds->access_key_id) == 0);
+ TEST_CHECK(strcmp("aws_secret_access_key_2", creds->secret_access_key) == 0);
+ TEST_CHECK(creds->session_token == NULL);
+
+ flb_aws_credentials_destroy(creds);
+ creds = NULL;
+
+ flb_aws_provider_destroy(provider);
+ provider = NULL;
+
+ if (original_path) {
+ MUST_SETENV("PATH", original_path);
+ } else {
+ MUST_UNSETENV("PATH");
+ }
+}
+
+static void test_credential_process_expired(void)
+{
+ int i;
+ int num_testcases = sizeof(credential_process_expired_testcases) /
+ sizeof(credential_process_expired_testcases[0]);
+ struct credential_process_expired_testcase* current_testcase = NULL;
+
+ /* Print a newline so the test output starts on its own line. */
+ fprintf(stderr, "\n");
+
+ for (i = 0; i < num_testcases; i++) {
+ current_testcase = &credential_process_expired_testcases[i];
+ TEST_CASE(current_testcase->name);
+ test_credential_process_expired_helper(current_testcase->expiration);
+ }
+}
+
+static void test_credential_process_failure(void)
+{
+ struct flb_aws_provider *provider;
+ struct flb_aws_credentials* creds;
+ char* original_path = getenv("PATH");
+
+ /* Print a newline so the test output starts on its own line. */
+ fprintf(stderr, "\n");
+
+ TEST_CHECK(unset_process_env() == 0);
+
+ MUST_SETENV("AWS_CONFIG_FILE", AWS_TEST_DATA_PATH("shared_config.ini"));
+ MUST_SETENV("PATH", AWS_TEST_DATA_PATH("credential_process"));
+
+ provider = flb_profile_provider_create(NULL);
+ TEST_ASSERT(provider != NULL);
+
+ /* These environment variables are used by the test credential_process. */
+ MUST_SETENV("_AWS_SECRET_ACCESS_KEY", "aws_secret_access_key");
+ MUST_SETENV("_AWS_EXIT_CODE", "1");
+
+ creds = provider->provider_vtable->get_credentials(provider);
+ TEST_ASSERT(creds == NULL);
+
+ flb_aws_credentials_destroy(creds);
+ creds = NULL;
+
+ /* Repeated calls to get_credentials should try to fetch the credentials again. */
+
+ MUST_SETENV("_AWS_SECRET_ACCESS_KEY", "aws_secret_access_key_2");
+ MUST_UNSETENV("_AWS_EXIT_CODE");
+
+ creds = provider->provider_vtable->get_credentials(provider);
+ TEST_ASSERT(creds != NULL);
+
+ TEST_CHECK(strcmp("default", creds->access_key_id) == 0);
+ TEST_CHECK(strcmp("aws_secret_access_key_2", creds->secret_access_key) == 0);
+
+ flb_aws_credentials_destroy(creds);
+ creds = NULL;
+
+ flb_aws_provider_destroy(provider);
+ provider = NULL;
+
+ if (original_path) {
+ MUST_SETENV("PATH", original_path);
+ } else {
+ MUST_UNSETENV("PATH");
+ }
+}
+
+struct parse_credential_process_testcase {
+ char* name;
+ char* input;
+
+ /*
+ * NULL-terminated array of tokens that should be returned.
+ * The choice of 10 here is completely arbitrary.
+ * Since a char*[] cannot be NULL, we need a separate flag for the failure cases.
+ */
+ char* expected[10];
+ int should_fail;
+};
+
+struct parse_credential_process_testcase parse_credential_process_testcases[] = {
+ {
+ .name = TESTCASE_NAME(),
+ .input = "my-process",
+ .expected = { "my-process", NULL },
+ },
+ {
+ .name = TESTCASE_NAME(),
+ .input = " my-process ",
+ .expected = { "my-process", NULL },
+ },
+ {
+ .name = TESTCASE_NAME(),
+ .input = "my-process arg1 arg2",
+ .expected = { "my-process", "arg1", "arg2", NULL },
+ },
+ {
+ .name = TESTCASE_NAME(),
+ .input = " my-process arg1 arg2 ",
+ .expected = { "my-process", "arg1", "arg2", NULL },
+ },
+ {
+ .name = TESTCASE_NAME(),
+ .input = "my-process \"arg1 \" arg2 \"\"",
+ .expected = { "my-process", "arg1 ", "arg2", "", NULL },
+ },
+ {
+ .name = TESTCASE_NAME(),
+ .input = "\"my process\"",
+ .expected = { "my process", NULL },
+ },
+ {
+ .name = TESTCASE_NAME(),
+ .input = " \"my process\" \" \" ",
+ .expected = { "my process", " ", NULL },
+ },
+ {
+ .name = TESTCASE_NAME(),
+ .input = "",
+ .expected = { NULL },
+ },
+ {
+ .name = TESTCASE_NAME(),
+ .input = "\"unterminated",
+ .should_fail = FLB_TRUE,
+ },
+ {
+ .name = TESTCASE_NAME(),
+ .input = " \"unterminated ",
+ .should_fail = FLB_TRUE,
+ },
+ {
+ .name = TESTCASE_NAME(),
+ .input = "abc\"def\"",
+ .should_fail = FLB_TRUE,
+ },
+ {
+ .name = TESTCASE_NAME(),
+ .input = " abc\"def\" ",
+ .should_fail = FLB_TRUE,
+ },
+ {
+ .name = TESTCASE_NAME(),
+ .input = "\"abc\"def",
+ .should_fail = FLB_TRUE,
+ },
+ {
+ .name = TESTCASE_NAME(),
+ .input = " \"abc\"def ",
+ .should_fail = FLB_TRUE,
+ },
+};
+
+static void test_parse_credential_process_helper(char* input, char** expected)
+{
+ char* cpy = NULL;
+ char** tokens = NULL;
+ int i = 0;
+ int input_len = strlen(input) + 1;
+
+ /*
+ * String literals are immutable, but parse_credential_process modifies its input.
+ * To circumvent this, copy the literal into a mutable string.
+ * Note: Because the return value of parse_credential_process will contain pointers
+ * into this string, we cannot free the copy until we are done with the token array.
+ */
+ cpy = flb_malloc(input_len + 1);
+ TEST_ASSERT(cpy != NULL);
+ memcpy(cpy, input, input_len);
+
+ tokens = parse_credential_process(cpy);
+
+ if (expected) {
+ TEST_ASSERT(tokens != NULL);
+ for (i = 0; expected[i]; i++) {
+ TEST_ASSERT_(tokens[i] != NULL, "expected='%s', got=(null)", expected[i]);
+ TEST_CHECK_(strcmp(expected[i], tokens[i]) == 0, "expected='%s', got='%s'",
+ expected[i], tokens[i]);
+ }
+ TEST_ASSERT_(tokens[i] == NULL, "expected=(null), got='%s'", tokens[i]);
+ }
+ else {
+ TEST_ASSERT(tokens == NULL);
+ }
+
+ flb_free(tokens);
+ flb_free(cpy);
+}
+
+static void test_parse_credential_process(void)
+{
+ int i;
+ int num_testcases = sizeof(parse_credential_process_testcases) /
+ sizeof(parse_credential_process_testcases[0]);
+ struct parse_credential_process_testcase* current_testcase = NULL;
+ char** expected = NULL;
+
+ /* Print a newline so the test output starts on its own line. */
+ fprintf(stderr, "\n");
+
+ for (i = 0; i < num_testcases; i++) {
+ current_testcase = &parse_credential_process_testcases[i];
+ TEST_CASE(current_testcase->name);
+ if (current_testcase->should_fail) {
+ expected = NULL;
+ }
+ else {
+ expected = current_testcase->expected;
+ }
+ test_parse_credential_process_helper(current_testcase->input, expected);
+ }
+}
+
+TEST_LIST = {
+ { "test_credential_process_default", test_credential_process_default },
+ { "test_credential_process_no_expiration", test_credential_process_no_expiration },
+ { "test_credential_process_expired", test_credential_process_expired },
+ { "test_credential_process_failure", test_credential_process_failure },
+ { "test_parse_credential_process", test_parse_credential_process },
+ { 0 }
+};
diff --git a/fluent-bit/tests/internal/aws_credentials_profile.c b/fluent-bit/tests/internal/aws_credentials_profile.c
new file mode 100644
index 00000000..1335bd95
--- /dev/null
+++ b/fluent-bit/tests/internal/aws_credentials_profile.c
@@ -0,0 +1,405 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+#include <fluent-bit/flb_sds.h>
+#include <fluent-bit/flb_aws_credentials.h>
+#include <fluent-bit/flb_mem.h>
+#include <fluent-bit/flb_info.h>
+
+#include "aws_credentials_test_internal.h"
+
+#define TEST_CREDENTIALS_FILE AWS_TEST_DATA_PATH("shared_credentials_file.ini")
+
+#define TEST_CREDENTIALS_NODEFAULT AWS_TEST_DATA_PATH("shared_credentials_file_nodefault.ini")
+
+/* these credentials look real but are not */
+#define AKID_DEFAULT_PROFILE "ASIASDMPIJWXJAXT3O3T"
+#define SKID_DEFAULT_PROFILE "EAUBpd/APPT4Nfi4DWY3gnt5TU/4T49laqS5zh8W"
+#define TOKEN_DEFAULT_PROFILE "IQoJb3JpZ2luX2VjEOD//////////wEaCNVzLWVh\
+c3QtMSJHMEUCIKCn7v/EDowMZvJnciSJbxA7rIV4p1K6pOUvcLHM+9EzNgIgeiYbfA47DGS\
+qoEZS3yrRWGN8Fr4Q/bK7ANRgv09Hth8q1gEIWRABGgwxNDQ3MTg3MTE0NzAiDGSqzyXiic\
+OZp63afiqzAUyWOljOn5HaIxRfpQ5pTf+o4roJ2KPlHn+XHEKJZKien4Ydm7zeVi7SbPLKo\
+cjmjYJd31PrlbJ43C6AyrhmY57qaD7Zz4N3N0V6mekzvlAeARXsa4deflsbemqkp1WVsBLk\
+O6qUuk+N04+MxIVXAxkW9RSPRTVjxeS2m5Yobygto58WLFE8gacRoNd4lCK4JUmEdiaxJEQ\
+QO7leZ3v1XxQr6QBS8P/GmcJYcQTxlA6AFQxIMJKGwfAFOuMB2cEc8cF2Htiqf3LVGMk/6b\
+YKkW7fHUtrnttp28jgWtbbLtFbX/zIdlqwm73Ryp7lI+xkM4XNIT+6ZKa4Xw0/Zw3xLzlk3\
+jic6QWPAcffwR6kOunoTOWJzPskK/RZ4Cd+GyGarxG27Cz6xolAzAsDpdGQwV7kCCUPi6/V\
+HjefwKEk9HjZfejC5WuCS173qFrU9kNb4IrYhnK+wmRzzJfgpWUwerdiJKBz95j1iW9rP1a\
+8p1xLR3EXUMN3LIW0+gP8sFjg5iiqDkaS/tUXWZndM2QdJLcrxwAutFchc0nqJHYTijw="
+
+#define AKID_NONDEFAULT_PROFILE "akid"
+#define SKID_NONDEFAULT_PROFILE "skid"
+
+#define AKID_NOSPACE_PROFILE "akidnospace"
+#define SKID_NOSPACE_PROFILE "skidnospace"
+#define TOKEN_NOSPACE_PROFILE "tokennospace"
+
+#define AKID_WEIRDWHITESPACE_PROFILE "akidweird"
+#define SKID_WEIRDWHITESPACE_PROFILE "skidweird"
+#define TOKEN_WEIRDWHITESPACE_PROFILE "tokenweird///token=="
+
+#define CUSTOM_PROFILE_ACCESS_KEY_ID "custom_access_key_id"
+#define CUSTOM_PROFILE_SECRET_ACCESS_KEY "custom_secret_access_key"
+
+static void test_profile_default()
+{
+ struct flb_aws_provider *provider;
+ struct flb_aws_credentials*creds;
+ struct flb_config *config;
+ int ret;
+
+ config = flb_config_init();
+
+ if (config == NULL) {
+ return;
+ }
+
+ TEST_CHECK(unset_profile_env() == 0);
+
+ ret = setenv("AWS_SHARED_CREDENTIALS_FILE", TEST_CREDENTIALS_FILE, 1);
+ TEST_ASSERT(ret == 0);
+
+ provider = flb_profile_provider_create(NULL);
+ TEST_ASSERT(provider != NULL);
+
+ /* repeated calls to get credentials should return the same set */
+ creds = provider->provider_vtable->get_credentials(provider);
+ TEST_ASSERT(creds != NULL);
+
+ TEST_CHECK(strcmp(AKID_DEFAULT_PROFILE, creds->access_key_id) == 0);
+ TEST_CHECK(strcmp(SKID_DEFAULT_PROFILE, creds->secret_access_key) == 0);
+ TEST_CHECK(strcmp(TOKEN_DEFAULT_PROFILE, creds->session_token) == 0);
+
+ flb_aws_credentials_destroy(creds);
+
+ creds = provider->provider_vtable->get_credentials(provider);
+ TEST_ASSERT(creds != NULL);
+
+ TEST_CHECK(strcmp(AKID_DEFAULT_PROFILE, creds->access_key_id) == 0);
+ TEST_CHECK(strcmp(SKID_DEFAULT_PROFILE, creds->secret_access_key) == 0);
+ TEST_CHECK(strcmp(TOKEN_DEFAULT_PROFILE, creds->session_token) == 0);
+
+ flb_aws_credentials_destroy(creds);
+
+ /* refresh should return 0 (success) */
+ ret = provider->provider_vtable->refresh(provider);
+ TEST_CHECK(ret == 0);
+
+ flb_aws_provider_destroy(provider);
+ flb_config_exit(config);
+
+ TEST_CHECK(unset_profile_env() == 0);
+}
+
+static void test_profile_custom()
+{
+ struct flb_aws_provider *provider;
+ struct flb_aws_credentials*creds;
+ struct flb_config *config;
+ int ret;
+
+ config = flb_config_init();
+
+ if (config == NULL) {
+ return;
+ }
+
+ TEST_CHECK(unset_profile_env() == 0);
+
+ ret = setenv("AWS_SHARED_CREDENTIALS_FILE", TEST_CREDENTIALS_FILE, 1);
+ TEST_ASSERT(ret == 0);
+
+ provider = flb_profile_provider_create("custom");
+ TEST_ASSERT(provider != NULL);
+
+ /* repeated calls to get credentials should return the same set */
+ creds = provider->provider_vtable->get_credentials(provider);
+ TEST_ASSERT(creds != NULL);
+
+ TEST_CHECK(strcmp(CUSTOM_PROFILE_ACCESS_KEY_ID, creds->access_key_id) == 0);
+ TEST_CHECK(strcmp(CUSTOM_PROFILE_SECRET_ACCESS_KEY, creds->secret_access_key) == 0);
+
+ flb_aws_credentials_destroy(creds);
+
+ creds = provider->provider_vtable->get_credentials(provider);
+ TEST_ASSERT(creds != NULL);
+
+ TEST_CHECK(strcmp(CUSTOM_PROFILE_ACCESS_KEY_ID, creds->access_key_id) == 0);
+ TEST_CHECK(strcmp(CUSTOM_PROFILE_SECRET_ACCESS_KEY, creds->secret_access_key) == 0);
+
+ flb_aws_credentials_destroy(creds);
+
+ /* refresh should return 0 (success) */
+ ret = provider->provider_vtable->refresh(provider);
+ TEST_CHECK(ret == 0);
+
+ flb_aws_provider_destroy(provider);
+ flb_config_exit(config);
+
+ TEST_CHECK(unset_profile_env() == 0);
+}
+
+static void test_profile_non_default()
+{
+ struct flb_aws_provider *provider;
+ struct flb_aws_credentials*creds;
+ struct flb_config *config;
+ int ret;
+
+ config = flb_config_init();
+
+ if (config == NULL) {
+ return;
+ }
+
+ TEST_CHECK(unset_profile_env() == 0);
+
+ ret = setenv("AWS_SHARED_CREDENTIALS_FILE", TEST_CREDENTIALS_FILE, 1);
+ TEST_ASSERT(ret == 0);
+
+ ret = setenv("AWS_PROFILE", "nondefault", 1);
+ TEST_ASSERT(ret == 0);
+
+ provider = flb_profile_provider_create(NULL);
+ TEST_ASSERT(provider != NULL);
+
+ /* repeated calls to get credentials should return the same set */
+ creds = provider->provider_vtable->get_credentials(provider);
+ TEST_ASSERT(creds != NULL);
+
+ TEST_CHECK(strcmp(AKID_NONDEFAULT_PROFILE, creds->access_key_id) == 0);
+ TEST_CHECK(strcmp(SKID_NONDEFAULT_PROFILE, creds->secret_access_key) == 0);
+ TEST_CHECK(creds->session_token == NULL);
+
+ flb_aws_credentials_destroy(creds);
+
+ creds = provider->provider_vtable->get_credentials(provider);
+ TEST_ASSERT(creds != NULL);
+
+ TEST_CHECK(strcmp(AKID_NONDEFAULT_PROFILE, creds->access_key_id) == 0);
+ TEST_CHECK(strcmp(SKID_NONDEFAULT_PROFILE, creds->secret_access_key) == 0);
+ TEST_CHECK(creds->session_token == NULL);
+
+ flb_aws_credentials_destroy(creds);
+
+ /* refresh should return 0 (success) */
+ ret = provider->provider_vtable->refresh(provider);
+ TEST_CHECK(ret == 0);
+
+ flb_aws_provider_destroy(provider);
+ flb_config_exit(config);
+
+ TEST_CHECK(unset_profile_env() == 0);
+}
+
+static void test_profile_no_space()
+{
+ struct flb_aws_provider *provider;
+ struct flb_aws_credentials*creds;
+ struct flb_config *config;
+ int ret;
+
+ config = flb_config_init();
+
+ if (config == NULL) {
+ return;
+ }
+
+ TEST_CHECK(unset_profile_env() == 0);
+
+ ret = setenv("AWS_SHARED_CREDENTIALS_FILE", TEST_CREDENTIALS_FILE, 1);
+ TEST_ASSERT(ret == 0);
+
+ ret = setenv("AWS_DEFAULT_PROFILE", "nospace", 1);
+ TEST_ASSERT(ret == 0);
+
+ provider = flb_profile_provider_create(NULL);
+ TEST_ASSERT(provider != NULL);
+
+ /* repeated calls to get credentials should return the same set */
+ creds = provider->provider_vtable->get_credentials(provider);
+ TEST_ASSERT(creds != NULL);
+
+ TEST_CHECK(strcmp(AKID_NOSPACE_PROFILE, creds->access_key_id) == 0);
+ TEST_CHECK(strcmp(SKID_NOSPACE_PROFILE, creds->secret_access_key) == 0);
+ TEST_CHECK(strcmp(TOKEN_NOSPACE_PROFILE, creds->session_token) == 0);
+
+ flb_aws_credentials_destroy(creds);
+
+ creds = provider->provider_vtable->get_credentials(provider);
+ TEST_ASSERT(creds != NULL);
+
+ TEST_CHECK(strcmp(AKID_NOSPACE_PROFILE, creds->access_key_id) == 0);
+ TEST_CHECK(strcmp(SKID_NOSPACE_PROFILE, creds->secret_access_key) == 0);
+ TEST_CHECK(strcmp(TOKEN_NOSPACE_PROFILE, creds->session_token) == 0);
+
+ flb_aws_credentials_destroy(creds);
+
+ /* refresh should return 0 (success) */
+ ret = provider->provider_vtable->refresh(provider);
+ TEST_CHECK(ret == 0);
+
+ flb_aws_provider_destroy(provider);
+ flb_config_exit(config);
+
+ TEST_CHECK(unset_profile_env() == 0);
+}
+
+static void test_profile_weird_whitespace()
+{
+ struct flb_aws_provider *provider;
+ struct flb_aws_credentials*creds;
+ struct flb_config *config;
+ int ret;
+
+ config = flb_config_init();
+
+ if (config == NULL) {
+ return;
+ }
+
+ TEST_CHECK(unset_profile_env() == 0);
+
+ ret = setenv("AWS_SHARED_CREDENTIALS_FILE", TEST_CREDENTIALS_FILE, 1);
+ TEST_ASSERT(ret == 0);
+
+ ret = setenv("AWS_DEFAULT_PROFILE", "weirdwhitespace", 1);
+ TEST_ASSERT(ret == 0);
+
+ provider = flb_profile_provider_create(NULL);
+ TEST_ASSERT(provider != NULL);
+
+ /* repeated calls to get credentials should return the same set */
+ creds = provider->provider_vtable->get_credentials(provider);
+ if (!creds) {
+ flb_errno();
+ flb_config_exit(config);
+ return;
+ }
+ TEST_CHECK(strcmp(AKID_WEIRDWHITESPACE_PROFILE, creds->access_key_id) == 0);
+ TEST_CHECK(strcmp(SKID_WEIRDWHITESPACE_PROFILE,
+ creds->secret_access_key) == 0);
+ TEST_CHECK(strcmp(TOKEN_WEIRDWHITESPACE_PROFILE, creds->session_token) == 0);
+
+ flb_aws_credentials_destroy(creds);
+
+ creds = provider->provider_vtable->get_credentials(provider);
+ if (!creds) {
+ flb_errno();
+ flb_config_exit(config);
+ return;
+ }
+ TEST_CHECK(strcmp(AKID_WEIRDWHITESPACE_PROFILE, creds->access_key_id) == 0);
+ TEST_CHECK(strcmp(SKID_WEIRDWHITESPACE_PROFILE,
+ creds->secret_access_key) == 0);
+ TEST_CHECK(strcmp(TOKEN_WEIRDWHITESPACE_PROFILE, creds->session_token) == 0);
+
+ flb_aws_credentials_destroy(creds);
+
+ /* refresh should return 0 (success) */
+ ret = provider->provider_vtable->refresh(provider);
+ TEST_CHECK(ret == 0);
+
+ flb_aws_provider_destroy(provider);
+ flb_config_exit(config);
+
+ TEST_CHECK(unset_profile_env() == 0);
+}
+
+static void test_profile_missing()
+{
+ struct flb_aws_provider *provider;
+ struct flb_aws_credentials*creds;
+ struct flb_config *config;
+ int ret;
+
+ config = flb_config_init();
+
+ if (config == NULL) {
+ return;
+ }
+
+ TEST_CHECK(unset_profile_env() == 0);
+
+ ret = setenv("AWS_SHARED_CREDENTIALS_FILE", TEST_CREDENTIALS_FILE, 1);
+ TEST_ASSERT(ret == 0);
+
+ ret = setenv("AWS_DEFAULT_PROFILE", "missing", 1);
+ TEST_ASSERT(ret == 0);
+
+ provider = flb_profile_provider_create(NULL);
+ TEST_ASSERT(provider != NULL);
+
+ /* repeated calls to get credentials should return the same set */
+ creds = provider->provider_vtable->get_credentials(provider);
+ TEST_CHECK(creds == NULL);
+
+ flb_aws_credentials_destroy(creds);
+
+ creds = provider->provider_vtable->get_credentials(provider);
+ TEST_CHECK(creds == NULL);
+
+ flb_aws_credentials_destroy(creds);
+
+ /* refresh should return -1 (failure) */
+ ret = provider->provider_vtable->refresh(provider);
+ TEST_CHECK(ret < 0);
+
+ flb_aws_provider_destroy(provider);
+ flb_config_exit(config);
+
+ TEST_CHECK(unset_profile_env() == 0);
+}
+
+static void test_profile_nodefault()
+{
+ struct flb_aws_provider *provider;
+ struct flb_aws_credentials*creds;
+ struct flb_config *config;
+ int ret;
+
+ config = flb_config_init();
+
+ if (config == NULL) {
+ return;
+ }
+
+ TEST_CHECK(unset_profile_env() == 0);
+
+ ret = setenv("AWS_SHARED_CREDENTIALS_FILE", TEST_CREDENTIALS_NODEFAULT, 1);
+ TEST_ASSERT(ret == 0);
+
+ provider = flb_profile_provider_create(NULL);
+ TEST_ASSERT(provider != NULL);
+
+ /* repeated calls to get credentials should return the same set */
+ creds = provider->provider_vtable->get_credentials(provider);
+ TEST_CHECK(creds == NULL);
+
+ flb_aws_credentials_destroy(creds);
+
+ creds = provider->provider_vtable->get_credentials(provider);
+ TEST_CHECK(creds == NULL);
+
+ flb_aws_credentials_destroy(creds);
+
+ /* refresh should return -1 (failure) */
+ ret = provider->provider_vtable->refresh(provider);
+ TEST_CHECK(ret < 0);
+
+ flb_aws_provider_destroy(provider);
+ flb_config_exit(config);
+
+ TEST_CHECK(unset_profile_env() == 0);
+}
+
+
+TEST_LIST = {
+ { "test_profile_default", test_profile_default },
+ { "test_profile_non_default", test_profile_non_default },
+ { "test_profile_no_space", test_profile_no_space },
+ { "test_profile_weird_whitespace", test_profile_weird_whitespace },
+ { "test_profile_missing", test_profile_missing },
+ { "test_profile_nodefault", test_profile_nodefault },
+ { "test_profile_custom", test_profile_custom },
+ { 0 }
+};
diff --git a/fluent-bit/tests/internal/aws_credentials_sts.c b/fluent-bit/tests/internal/aws_credentials_sts.c
new file mode 100644
index 00000000..146d937e
--- /dev/null
+++ b/fluent-bit/tests/internal/aws_credentials_sts.c
@@ -0,0 +1,939 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+#include <fluent-bit/flb_sds.h>
+#include <fluent-bit/flb_aws_credentials.h>
+#include <fluent-bit/flb_mem.h>
+#include <fluent-bit/flb_info.h>
+#include <fluent-bit/flb_http_client.h>
+
+#include <monkey/mk_core.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "flb_tests_internal.h"
+
+#define EKS_ACCESS_KEY "eks_akid"
+#define EKS_SECRET_KEY "eks_skid"
+#define EKS_TOKEN "eks_token"
+
+#define STS_ACCESS_KEY "sts_akid"
+#define STS_SECRET_KEY "sts_skid"
+#define STS_TOKEN "sts_token"
+
+/* standard environment variables */
+#define AWS_ACCESS_KEY_ID "AWS_ACCESS_KEY_ID"
+#define AWS_SECRET_ACCESS_KEY "AWS_SECRET_ACCESS_KEY"
+#define AWS_SESSION_TOKEN "AWS_SESSION_TOKEN"
+
+#define TOKEN_FILE_ENV_VAR "AWS_WEB_IDENTITY_TOKEN_FILE"
+#define ROLE_ARN_ENV_VAR "AWS_ROLE_ARN"
+#define SESSION_NAME_ENV_VAR "AWS_ROLE_SESSION_NAME"
+
+#define WEB_TOKEN_FILE FLB_TESTS_DATA_PATH "/data/aws_credentials/\
+web_identity_token_file.txt"
+
+#define STS_RESPONSE_EKS "<AssumeRoleWithWebIdentityResponse \
+xmlns=\"https://sts.amazonaws.com/doc/2011-06-15/\">\n\
+ <AssumeRoleWithWebIdentityResult>\n\
+ <SubjectFromWebIdentityToken>amzn1.account.AF6RHO7KZU5XRVQJGXK6HB56KR2A\n\
+</SubjectFromWebIdentityToken>\n\
+ <Audience>client.5498841531868486423.1548@apps.example.com</Audience>\n\
+ <AssumedRoleUser>\n\
+ <Arn>arn:aws:sts::123456789012:assumed-role/WebIdentityRole/app1</Arn>\n\
+ <AssumedRoleId>AROACLKWSDQRAOEXAMPLE:app1</AssumedRoleId>\n\
+ </AssumedRoleUser>\n\
+ <Credentials>\n\
+ <SessionToken>eks_token</SessionToken>\n\
+ <SecretAccessKey>eks_skid</SecretAccessKey>\n\
+ <Expiration>2025-10-24T23:00:23Z</Expiration>\n\
+ <AccessKeyId>eks_akid</AccessKeyId>\n\
+ </Credentials>\n\
+ <Provider>www.amazon.com</Provider>\n\
+ </AssumeRoleWithWebIdentityResult>\n\
+ <ResponseMetadata>\n\
+ <RequestId>ad4156e9-bce1-11e2-82e6-6b6efEXAMPLE</RequestId>\n\
+ </ResponseMetadata>\n\
+</AssumeRoleWithWebIdentityResponse>"
+
+#define STS_RESPONSE_ASSUME_ROLE "<AssumeRoleResponse \
+xmlns=\"https://sts.amazonaws.com/doc/\n\
+2011-06-15/\">\n\
+ <AssumeRoleResult>\n\
+ <AssumedRoleUser>\n\
+ <Arn>arn:aws:sts::123456789012:assumed-role/demo/TestAR</Arn>\n\
+ <AssumedRoleId>ARO123EXAMPLE123:TestAR</AssumedRoleId>\n\
+ </AssumedRoleUser>\n\
+ <Credentials>\n\
+ <AccessKeyId>sts_akid</AccessKeyId>\n\
+ <SecretAccessKey>sts_skid</SecretAccessKey>\n\
+ <SessionToken>sts_token</SessionToken>\n\
+ <Expiration>2025-11-09T13:34:41Z</Expiration>\n\
+ </Credentials>\n\
+ <PackedPolicySize>6</PackedPolicySize>\n\
+ </AssumeRoleResult>\n\
+ <ResponseMetadata>\n\
+ <RequestId>c6104cbe-af31-11e0-8154-cbc7ccf896c7</RequestId>\n\
+ </ResponseMetadata>\n\
+</AssumeRoleResponse>"
+
+/*
+ * Unexpected/invalid STS response. The goal of this is not to test anything
+ * that might happen in production, but rather to test the error handling
+ * code for the providers. This helps ensure all code paths are tested and
+ * the error handling code does not introduce memory leaks.
+ */
+
+#define STS_RESPONSE_MALFORMED "{\n\
+ \"__type\": \"some unexpected response\",\n\
+ \"this tests\": the error handling code\",\n\
+\"This looks like JSON but is not valid.\"\n\
+<Credentials><AccessKeyId>It also contains xml tags that a correct\n\
+response would have</SecretAccessKey>"
+
+/*
+ * Global Variable that allows us to check the number of calls
+ * made in each test
+ */
+int g_request_count;
+
+/* Each test case has its own request function */
+
+/* unexpected output test- see description for STS_RESPONSE_MALFORMED */
+struct flb_http_client *request_unexpected_response(struct flb_aws_client
+ *aws_client, int method,
+ const char *uri)
+{
+ struct flb_http_client *c;
+ TEST_CHECK(method == FLB_HTTP_GET);
+
+ /* create an http client so that we can set the response */
+ c = flb_calloc(1, sizeof(struct flb_http_client));
+ if (!c) {
+ flb_errno();
+ return NULL;
+ }
+ mk_list_init(&c->headers);
+
+ c->resp.status = 200;
+ c->resp.payload = STS_RESPONSE_MALFORMED;
+ c->resp.payload_size = strlen(STS_RESPONSE_MALFORMED);
+
+ return c;
+}
+struct flb_http_client *request_eks_test1(struct flb_aws_client *aws_client,
+ int method, const char *uri)
+{
+ struct flb_http_client *c;
+
+ TEST_CHECK(method == FLB_HTTP_GET);
+ TEST_CHECK(strstr(uri, "Action=AssumeRoleWithWebIdentity") != NULL);
+ TEST_CHECK(strstr(uri, "RoleArn=arn:aws:iam::123456789012:role/test")
+ != NULL);
+ TEST_CHECK(strstr(uri, "WebIdentityToken=this-is-a-fake-jwt") != NULL);
+ TEST_CHECK(strstr(uri, "RoleSessionName=session_name") != NULL);
+
+ /* create an http client so that we can set the response */
+ c = flb_calloc(1, sizeof(struct flb_http_client));
+ if (!c) {
+ flb_errno();
+ return NULL;
+ }
+ mk_list_init(&c->headers);
+
+ c->resp.status = 200;
+ c->resp.payload = STS_RESPONSE_EKS;
+ c->resp.payload_size = strlen(STS_RESPONSE_EKS);
+
+ return c;
+}
+
+struct flb_http_client *request_eks_flb_sts_session_name(struct flb_aws_client
+ *aws_client,
+ int method,
+ const char *uri)
+{
+ struct flb_http_client *c;
+
+ TEST_CHECK(method == FLB_HTTP_GET);
+ TEST_CHECK(strstr(uri, "Action=AssumeRoleWithWebIdentity") != NULL);
+ TEST_CHECK(strstr(uri, "RoleArn=arn:aws:iam::123456789012:role/"
+ "randomsession") != NULL);
+ TEST_CHECK(strstr(uri, "WebIdentityToken=this-is-a-fake-jwt") != NULL);
+ /* this test case has a random session name */
+ TEST_CHECK(strstr(uri, "RoleSessionName=") != NULL);
+ /* session name should not be the same as test 1 */
+ TEST_CHECK(strstr(uri, "RoleSessionName=session_name") == NULL);
+
+ /* create an http client so that we can set the response */
+ c = flb_calloc(1, sizeof(struct flb_http_client));
+ if (!c) {
+ flb_errno();
+ return NULL;
+ }
+ mk_list_init(&c->headers);
+
+ c->resp.status = 200;
+ c->resp.payload = STS_RESPONSE_EKS;
+ c->resp.payload_size = strlen(STS_RESPONSE_EKS);
+
+ return c;
+}
+
+struct flb_http_client *request_eks_api_error(struct flb_aws_client *aws_client,
+ int method, const char *uri)
+{
+ struct flb_http_client *c;
+
+ TEST_CHECK(method == FLB_HTTP_GET);
+ TEST_CHECK(strstr(uri, "Action=AssumeRoleWithWebIdentity") != NULL);
+ TEST_CHECK(strstr(uri, "RoleArn=arn:aws:iam::123456789012:role/apierror")
+ != NULL);
+ TEST_CHECK(strstr(uri, "WebIdentityToken=this-is-a-fake-jwt") != NULL);
+ /* this test case has a random session name */
+ TEST_CHECK(strstr(uri, "RoleSessionName=") != NULL);
+ /* session name should not be the same as test 1 */
+ TEST_CHECK(strstr(uri, "RoleSessionName=session_name") == NULL);
+
+ /* create an http client so that we can set the response */
+ c = flb_calloc(1, sizeof(struct flb_http_client));
+ if (!c) {
+ flb_errno();
+ return NULL;
+ }
+ mk_list_init(&c->headers);
+
+ c->resp.status = 500;
+ c->resp.payload = NULL;
+ c->resp.payload_size = 0;
+
+ return c;
+}
+
+struct flb_http_client *request_sts_test1(struct flb_aws_client *aws_client,
+ int method, const char *uri)
+{
+ struct flb_http_client *c;
+
+ TEST_CHECK(method == FLB_HTTP_GET);
+ TEST_CHECK(strstr(uri, "Action=AssumeRole") != NULL);
+ TEST_CHECK(strstr(uri, "RoleArn=arn:aws:iam::123456789012:role/test")
+ != NULL);
+ TEST_CHECK(strstr(uri, "ExternalId=external_id") != NULL);
+ TEST_CHECK(strstr(uri, "RoleSessionName=session_name") != NULL);
+
+ /* create an http client so that we can set the response */
+ c = flb_calloc(1, sizeof(struct flb_http_client));
+ if (!c) {
+ flb_errno();
+ return NULL;
+ }
+ mk_list_init(&c->headers);
+
+ c->resp.status = 200;
+ c->resp.payload = STS_RESPONSE_ASSUME_ROLE;
+ c->resp.payload_size = strlen(STS_RESPONSE_ASSUME_ROLE);
+
+ return c;
+}
+
+struct flb_http_client *request_sts_api_error(struct flb_aws_client *aws_client,
+ int method, const char *uri)
+{
+ struct flb_http_client *c;
+
+ TEST_CHECK(method == FLB_HTTP_GET);
+ TEST_CHECK(strstr(uri, "Action=AssumeRole") != NULL);
+ TEST_CHECK(strstr(uri, "RoleArn=arn:aws:iam::123456789012:role/apierror")
+ != NULL);
+ TEST_CHECK(strstr(uri, "ExternalId=external_id") != NULL);
+ TEST_CHECK(strstr(uri, "RoleSessionName=session_name") != NULL);
+
+ /* create an http client so that we can set the response */
+ c = flb_calloc(1, sizeof(struct flb_http_client));
+ if (!c) {
+ flb_errno();
+ return NULL;
+ }
+ mk_list_init(&c->headers);
+
+ c->resp.status = 400;
+ c->resp.payload = NULL;
+ c->resp.payload_size = 0;
+
+ return c;
+}
+
+/* test/mock version of the flb_aws_client request function */
+struct flb_http_client *test_http_client_request(struct flb_aws_client *aws_client,
+ int method, const char *uri,
+ const char *body, size_t body_len,
+ struct flb_aws_header
+ *dynamic_headers,
+ size_t dynamic_headers_len)
+{
+ g_request_count++;
+ if (strcmp(aws_client->name, "sts_client_eks_provider") == 0) {
+ /*
+ * route to the correct test case fn using the uri - the role
+ * name is different in each test case.
+ */
+ if (strstr(uri, "test1") != NULL) {
+ return request_eks_test1(aws_client, method, uri);
+ } else if (strstr(uri, "randomsession") != NULL) {
+ return request_eks_flb_sts_session_name(aws_client, method, uri);
+ } else if (strstr(uri, "apierror") != NULL) {
+ return request_eks_api_error(aws_client, method, uri);
+ } else if (strstr(uri, "unexpected_api_response") != NULL) {
+ return request_unexpected_response(aws_client, method, uri);
+ }
+
+ /* uri should match one of the above conditions */
+ flb_errno();
+ return NULL;
+ } else if (strcmp(aws_client->name, "sts_client_assume_role_provider") == 0)
+ {
+ if (strstr(uri, "test1") != NULL) {
+ return request_sts_test1(aws_client, method, uri);
+ } else if (strstr(uri, "apierror") != NULL) {
+ return request_sts_api_error(aws_client, method, uri);
+ } else if (strstr(uri, "unexpected_api_response") != NULL) {
+ return request_unexpected_response(aws_client, method, uri);
+ }
+ /* uri should match one of the above conditions */
+ flb_errno();
+ return NULL;
+ }
+
+ /* client name should match one of the above conditions */
+ flb_errno();
+ return NULL;
+
+}
+
+/* Test/mock flb_aws_client */
+static struct flb_aws_client_vtable test_vtable = {
+ .request = test_http_client_request,
+};
+
+struct flb_aws_client *test_http_client_create()
+{
+ struct flb_aws_client *client = flb_calloc(1,
+ sizeof(struct flb_aws_client));
+ if (!client) {
+ flb_errno();
+ return NULL;
+ }
+ client->client_vtable = &test_vtable;
+ return client;
+}
+
+/* Generator that returns clients with the test vtable */
+static struct flb_aws_client_generator test_generator = {
+ .create = test_http_client_create,
+};
+
+struct flb_aws_client_generator *generator_in_test()
+{
+ return &test_generator;
+}
+
+static void unsetenv_eks()
+{
+ int ret;
+
+ ret = unsetenv(TOKEN_FILE_ENV_VAR);
+ if (ret < 0) {
+ flb_errno();
+ return;
+ }
+ ret = unsetenv(ROLE_ARN_ENV_VAR);
+ if (ret < 0) {
+ flb_errno();
+ return;
+ }
+ ret = unsetenv(SESSION_NAME_ENV_VAR);
+ if (ret < 0) {
+ flb_errno();
+ return;
+ }
+}
+
+static void test_flb_sts_session_name()
+{
+ char *session_name = flb_sts_session_name();
+
+ TEST_CHECK(strlen(session_name) == 32);
+
+ flb_free(session_name);
+}
+
+static void test_sts_uri()
+{
+ flb_sds_t uri;
+
+ uri = flb_sts_uri("AssumeRole", "myrole", "mysession",
+ "myexternalid", NULL);
+ TEST_CHECK(strcmp(uri, "/?Version=2011-06-15&Action=AssumeRole"
+ "&RoleSessionName=mysession&RoleArn=myrole"
+ "&ExternalId=myexternalid") == 0);
+ flb_sds_destroy(uri);
+}
+
+static void test_process_sts_response()
+{
+ struct flb_aws_credentials *creds;
+ struct flb_config *config;
+ time_t expiration;
+
+ config = flb_config_init();
+
+ if (config == NULL) {
+ return;
+ }
+ creds = flb_parse_sts_resp(STS_RESPONSE_EKS, &expiration);
+
+ TEST_CHECK(strcmp(EKS_ACCESS_KEY, creds->access_key_id) == 0);
+ TEST_CHECK(strcmp(EKS_SECRET_KEY, creds->secret_access_key) == 0);
+ TEST_CHECK(strcmp(EKS_TOKEN, creds->session_token) == 0);
+
+ flb_aws_credentials_destroy(creds);
+ flb_config_exit(config);
+}
+
+static void test_eks_provider() {
+ struct flb_config *config;
+ struct flb_aws_provider *provider;
+ struct flb_aws_credentials *creds;
+ int ret;
+
+ g_request_count = 0;
+
+ config = flb_config_init();
+
+ if (config == NULL) {
+ return;
+ }
+
+ /* set env vars */
+ ret = setenv(ROLE_ARN_ENV_VAR, "arn:aws:iam::123456789012:role/test1", 1);
+ if (ret < 0) {
+ flb_errno();
+ flb_config_exit(config);
+ return;
+ }
+ ret = setenv(SESSION_NAME_ENV_VAR, "session_name", 1);
+ if (ret < 0) {
+ flb_errno();
+ flb_config_exit(config);
+ return;
+ }
+ ret = setenv(TOKEN_FILE_ENV_VAR, WEB_TOKEN_FILE, 1);
+ if (ret < 0) {
+ flb_errno();
+ flb_config_exit(config);
+ return;
+ }
+
+ provider = flb_eks_provider_create(config, NULL, "us-west-2",
+ "https://sts.us-west-2.amazonaws.com",
+ NULL, generator_in_test());
+
+ /* repeated calls to get credentials should return the same set */
+ creds = provider->provider_vtable->get_credentials(provider);
+ if (!creds) {
+ flb_errno();
+ flb_config_exit(config);
+ return;
+ }
+ TEST_CHECK(strcmp(EKS_ACCESS_KEY, creds->access_key_id) == 0);
+ TEST_CHECK(strcmp(EKS_SECRET_KEY, creds->secret_access_key) == 0);
+ TEST_CHECK(strcmp(EKS_TOKEN, creds->session_token) == 0);
+
+ flb_aws_credentials_destroy(creds);
+
+ creds = provider->provider_vtable->get_credentials(provider);
+ if (!creds) {
+ flb_errno();
+ flb_config_exit(config);
+ return;
+ }
+ TEST_CHECK(strcmp(EKS_ACCESS_KEY, creds->access_key_id) == 0);
+ TEST_CHECK(strcmp(EKS_SECRET_KEY, creds->secret_access_key) == 0);
+ TEST_CHECK(strcmp(EKS_TOKEN, creds->session_token) == 0);
+
+ flb_aws_credentials_destroy(creds);
+
+ /* refresh should return 0 (success) */
+ ret = provider->provider_vtable->refresh(provider);
+ TEST_CHECK(ret == 0);
+
+ /*
+ * Request count should be 2:
+ * - One for the first call to get_credentials (2nd should hit cred cache)
+ * - One for the call to refresh
+ */
+ TEST_CHECK(g_request_count == 2);
+
+ flb_aws_provider_destroy(provider);
+ unsetenv_eks();
+ flb_config_exit(config);
+}
+
+static void test_eks_provider_random_session_name() {
+ struct flb_config *config;
+ struct flb_aws_provider *provider;
+ struct flb_aws_credentials *creds;
+ int ret;
+
+ g_request_count = 0;
+
+ config = flb_config_init();
+
+ if (config == NULL) {
+ return;
+ }
+
+ /* set env vars - session name is not set */
+ unsetenv_eks();
+ ret = setenv(ROLE_ARN_ENV_VAR,
+ "arn:aws:iam::123456789012:role/randomsession", 1);
+ if (ret < 0) {
+ flb_errno();
+ flb_config_exit(config);
+ return;
+ }
+ ret = setenv(TOKEN_FILE_ENV_VAR, WEB_TOKEN_FILE, 1);
+ if (ret < 0) {
+ flb_errno();
+ flb_config_exit(config);
+ return;
+ }
+
+ provider = flb_eks_provider_create(config, NULL, "us-west-2",
+ "https://sts.us-west-2.amazonaws.com",
+ NULL, generator_in_test());
+
+ /* repeated calls to get credentials should return the same set */
+ creds = provider->provider_vtable->get_credentials(provider);
+ if (!creds) {
+ flb_errno();
+ flb_config_exit(config);
+ return;
+ }
+ TEST_CHECK(strcmp(EKS_ACCESS_KEY, creds->access_key_id) == 0);
+ TEST_CHECK(strcmp(EKS_SECRET_KEY, creds->secret_access_key) == 0);
+ TEST_CHECK(strcmp(EKS_TOKEN, creds->session_token) == 0);
+
+ flb_aws_credentials_destroy(creds);
+
+ creds = provider->provider_vtable->get_credentials(provider);
+ if (!creds) {
+ flb_errno();
+ flb_config_exit(config);
+ return;
+ }
+ TEST_CHECK(strcmp(EKS_ACCESS_KEY, creds->access_key_id) == 0);
+ TEST_CHECK(strcmp(EKS_SECRET_KEY, creds->secret_access_key) == 0);
+ TEST_CHECK(strcmp(EKS_TOKEN, creds->session_token) == 0);
+
+ flb_aws_credentials_destroy(creds);
+
+ /* refresh should return 0 (success) */
+ ret = provider->provider_vtable->refresh(provider);
+ TEST_CHECK(ret == 0);
+
+ /*
+ * Request count should be 2:
+ * - One for the first call to get_credentials (2nd should hit cred cache)
+ * - One for the call to refresh
+ */
+ TEST_CHECK(g_request_count == 2);
+
+ flb_aws_provider_destroy(provider);
+ unsetenv_eks();
+ flb_config_exit(config);
+}
+
+/* unexpected output test- see description for STS_RESPONSE_MALFORMED */
+static void test_eks_provider_unexpected_api_response() {
+ struct flb_config *config;
+ struct flb_aws_provider *provider;
+ struct flb_aws_credentials *creds;
+ int ret;
+
+ g_request_count = 0;
+
+ config = flb_config_init();
+
+ if (config == NULL) {
+ return;
+ }
+
+ unsetenv_eks();
+ ret = setenv(ROLE_ARN_ENV_VAR, "arn:aws:iam::123456789012:role/"
+ "unexpected_api_response", 1);
+ if (ret < 0) {
+ flb_errno();
+ flb_config_exit(config);
+ return;
+ }
+ ret = setenv(TOKEN_FILE_ENV_VAR, WEB_TOKEN_FILE, 1);
+ if (ret < 0) {
+ flb_errno();
+ flb_config_exit(config);
+ return;
+ }
+
+ provider = flb_eks_provider_create(config, NULL, "us-west-2",
+ "https://sts.us-west-2.amazonaws.com",
+ NULL, generator_in_test());
+
+ /* API will return an error - creds will be NULL */
+ creds = provider->provider_vtable->get_credentials(provider);
+ TEST_CHECK(creds == NULL);
+
+ creds = provider->provider_vtable->get_credentials(provider);
+ TEST_CHECK(creds == NULL);
+
+ /* refresh should return -1 (failure) */
+ ret = provider->provider_vtable->refresh(provider);
+ TEST_CHECK(ret < 0);
+
+ /*
+ * Request count should be 3:
+ * - Each call to get_credentials and refresh invokes the client's
+ * request method and returns a request failure.
+ */
+ TEST_CHECK(g_request_count == 3);
+
+ flb_aws_provider_destroy(provider);
+ unsetenv_eks();
+ flb_config_exit(config);
+}
+
+static void test_eks_provider_api_error() {
+ struct flb_config *config;
+ struct flb_aws_provider *provider;
+ struct flb_aws_credentials *creds;
+ int ret;
+
+ g_request_count = 0;
+
+ config = flb_config_init();
+
+ if (config == NULL) {
+ return;
+ }
+
+ unsetenv_eks();
+ ret = setenv(ROLE_ARN_ENV_VAR, "arn:aws:iam::123456789012:role/apierror",
+ 1);
+ if (ret < 0) {
+ flb_errno();
+ flb_config_exit(config);
+ return;
+ }
+ ret = setenv(TOKEN_FILE_ENV_VAR, WEB_TOKEN_FILE, 1);
+ if (ret < 0) {
+ flb_errno();
+ flb_config_exit(config);
+ return;
+ }
+
+ provider = flb_eks_provider_create(config, NULL, "us-west-2",
+ "https://sts.us-west-2.amazonaws.com",
+ NULL, generator_in_test());
+
+ /* API will return an error - creds will be NULL */
+ creds = provider->provider_vtable->get_credentials(provider);
+ TEST_CHECK(creds == NULL);
+
+ creds = provider->provider_vtable->get_credentials(provider);
+ TEST_CHECK(creds == NULL);
+
+ /* refresh should return -1 (failure) */
+ ret = provider->provider_vtable->refresh(provider);
+ TEST_CHECK(ret < 0);
+
+ /*
+ * Request count should be 3:
+ * - Each call to get_credentials and refresh invokes the client's
+ * request method and returns a request failure.
+ */
+ TEST_CHECK(g_request_count == 3);
+
+ flb_aws_provider_destroy(provider);
+ unsetenv_eks();
+ flb_config_exit(config);
+}
+
+static void test_sts_provider() {
+ struct flb_config *config;
+ struct flb_aws_provider *provider;
+ struct flb_aws_provider *base_provider;
+ struct flb_aws_credentials *creds;
+ int ret;
+
+ g_request_count = 0;
+
+ config = flb_config_init();
+
+ if (config == NULL) {
+ return;
+ }
+
+ /* use the env provider as the base provider */
+ /* set environment */
+ ret = setenv(AWS_ACCESS_KEY_ID, "base_akid", 1);
+ if (ret < 0) {
+ flb_errno();
+ flb_config_exit(config);
+ return;
+ }
+ ret = setenv(AWS_SECRET_ACCESS_KEY, "base_skid", 1);
+ if (ret < 0) {
+ flb_errno();
+ flb_config_exit(config);
+ return;
+ }
+ ret = setenv(AWS_SESSION_TOKEN, "base_token", 1);
+ if (ret < 0) {
+ flb_errno();
+ flb_config_exit(config);
+ return;
+ }
+
+ base_provider = flb_aws_env_provider_create();
+ if (!base_provider) {
+ flb_errno();
+ flb_config_exit(config);
+ return;
+ }
+
+ provider = flb_sts_provider_create(config, NULL, base_provider, "external_id",
+ "arn:aws:iam::123456789012:role/test1",
+ "session_name", "cn-north-1",
+ "https://sts.us-west-2.amazonaws.com",
+ NULL, generator_in_test());
+ if (!provider) {
+ flb_errno();
+ flb_config_exit(config);
+ return;
+ }
+
+ /* repeated calls to get credentials should return the same set */
+ creds = provider->provider_vtable->get_credentials(provider);
+ if (!creds) {
+ flb_errno();
+ flb_config_exit(config);
+ return;
+ }
+ TEST_CHECK(strcmp(STS_ACCESS_KEY, creds->access_key_id) == 0);
+ TEST_CHECK(strcmp(STS_SECRET_KEY, creds->secret_access_key) == 0);
+ TEST_CHECK(strcmp(STS_TOKEN, creds->session_token) == 0);
+
+ flb_aws_credentials_destroy(creds);
+
+ creds = provider->provider_vtable->get_credentials(provider);
+ if (!creds) {
+ flb_errno();
+ flb_config_exit(config);
+ return;
+ }
+ TEST_CHECK(strcmp(STS_ACCESS_KEY, creds->access_key_id) == 0);
+ TEST_CHECK(strcmp(STS_SECRET_KEY, creds->secret_access_key) == 0);
+ TEST_CHECK(strcmp(STS_TOKEN, creds->session_token) == 0);
+
+ flb_aws_credentials_destroy(creds);
+
+ /* refresh should return 0 (success) */
+ ret = provider->provider_vtable->refresh(provider);
+ TEST_CHECK(ret == 0);
+
+ /*
+ * Request count should be 2:
+ * - One for the first call to get_credentials (2nd should hit cred cache)
+ * - One for the call to refresh
+ */
+ TEST_CHECK(g_request_count == 2);
+
+ flb_aws_provider_destroy(base_provider);
+ flb_aws_provider_destroy(provider);
+ flb_config_exit(config);
+}
+
+static void test_sts_provider_api_error() {
+ struct flb_config *config;
+ struct flb_aws_provider *provider;
+ struct flb_aws_provider *base_provider;
+ struct flb_aws_credentials *creds;
+ int ret;
+
+ g_request_count = 0;
+
+ config = flb_config_init();
+
+ if (config == NULL) {
+ return;
+ }
+
+ /* use the env provider as the base provider */
+ /* set environment */
+ ret = setenv(AWS_ACCESS_KEY_ID, "base_akid", 1);
+ if (ret < 0) {
+ flb_errno();
+ flb_config_exit(config);
+ return;
+ }
+ ret = setenv(AWS_SECRET_ACCESS_KEY, "base_skid", 1);
+ if (ret < 0) {
+ flb_errno();
+ flb_config_exit(config);
+ return;
+ }
+ ret = setenv(AWS_SESSION_TOKEN, "base_token", 1);
+ if (ret < 0) {
+ flb_errno();
+ flb_config_exit(config);
+ return;
+ }
+
+ base_provider = flb_aws_env_provider_create();
+ if (!base_provider) {
+ flb_errno();
+ flb_config_exit(config);
+ return;
+ }
+
+ provider = flb_sts_provider_create(config, NULL, base_provider, "external_id",
+ "arn:aws:iam::123456789012:role/apierror",
+ "session_name", "cn-north-1",
+ "https://sts.us-west-2.amazonaws.com",
+ NULL,
+ generator_in_test());
+ if (!provider) {
+ flb_errno();
+ flb_config_exit(config);
+ return;
+ }
+
+ /* repeated calls to get credentials should return the same set */
+ creds = provider->provider_vtable->get_credentials(provider);
+ TEST_CHECK(creds == NULL);
+
+ creds = provider->provider_vtable->get_credentials(provider);
+ TEST_CHECK(creds == NULL);
+
+ /* refresh should return -1 (failure) */
+ ret = provider->provider_vtable->refresh(provider);
+ TEST_CHECK(ret < 0);
+
+ /*
+ * Request count should be 3:
+ * - Each call to get_credentials and refresh invokes the client's
+ * request method and returns a request failure.
+ */
+ TEST_CHECK(g_request_count == 3);
+
+ flb_aws_provider_destroy(base_provider);
+ flb_aws_provider_destroy(provider);
+ flb_config_exit(config);
+}
+
+/* unexpected output test- see description for STS_RESPONSE_MALFORMED */
+static void test_sts_provider_unexpected_api_response() {
+ struct flb_config *config;
+ struct flb_aws_provider *provider;
+ struct flb_aws_provider *base_provider;
+ struct flb_aws_credentials *creds;
+ int ret;
+
+ g_request_count = 0;
+
+ config = flb_config_init();
+
+ if (config == NULL) {
+ return;
+ }
+
+ /* use the env provider as the base provider */
+ /* set environment */
+ ret = setenv(AWS_ACCESS_KEY_ID, "base_akid", 1);
+ if (ret < 0) {
+ flb_errno();
+ flb_config_exit(config);
+ return;
+ }
+ ret = setenv(AWS_SECRET_ACCESS_KEY, "base_skid", 1);
+ if (ret < 0) {
+ flb_errno();
+ flb_config_exit(config);
+ return;
+ }
+ ret = setenv(AWS_SESSION_TOKEN, "base_token", 1);
+ if (ret < 0) {
+ flb_errno();
+ flb_config_exit(config);
+ return;
+ }
+
+ base_provider = flb_aws_env_provider_create();
+ if (!base_provider) {
+ flb_errno();
+ flb_config_exit(config);
+ return;
+ }
+
+ provider = flb_sts_provider_create(config, NULL, base_provider, "external_id",
+ "arn:aws:iam::123456789012:role/"
+ "unexpected_api_response",
+ "session_name", "cn-north-1",
+ "https://sts.us-west-2.amazonaws.com",
+ NULL,
+ generator_in_test());
+ if (!provider) {
+ flb_errno();
+ flb_config_exit(config);
+ return;
+ }
+
+ /* repeated calls to get credentials should return the same set */
+ creds = provider->provider_vtable->get_credentials(provider);
+ TEST_CHECK(creds == NULL);
+
+ creds = provider->provider_vtable->get_credentials(provider);
+ TEST_CHECK(creds == NULL);
+
+ /* refresh should return -1 (failure) */
+ ret = provider->provider_vtable->refresh(provider);
+ TEST_CHECK(ret < 0);
+
+ /*
+ * Request count should be 3:
+ * - Each call to get_credentials and refresh invokes the client's
+ * request method and returns a request failure.
+ */
+ TEST_CHECK(g_request_count == 3);
+
+ flb_aws_provider_destroy(base_provider);
+ flb_aws_provider_destroy(provider);
+ flb_config_exit(config);
+}
+
+
+TEST_LIST = {
+ { "test_flb_sts_session_name" , test_flb_sts_session_name},
+ { "test_sts_uri" , test_sts_uri},
+ { "process_sts_response" , test_process_sts_response},
+ { "eks_credential_provider" , test_eks_provider},
+ { "eks_credential_provider_random_session_name" ,
+ test_eks_provider_random_session_name},
+ { "test_eks_provider_unexpected_api_response" ,
+ test_eks_provider_unexpected_api_response},
+ { "eks_credential_provider_api_error" , test_eks_provider_api_error},
+ { "sts_credential_provider" , test_sts_provider},
+ { "sts_credential_provider_api_error" , test_sts_provider_api_error},
+ { "sts_credential_provider_unexpected_api_response" ,
+ test_sts_provider_unexpected_api_response},
+ { 0 }
+};
diff --git a/fluent-bit/tests/internal/aws_credentials_test_internal.h b/fluent-bit/tests/internal/aws_credentials_test_internal.h
new file mode 100644
index 00000000..bd335782
--- /dev/null
+++ b/fluent-bit/tests/internal/aws_credentials_test_internal.h
@@ -0,0 +1,42 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+#ifndef AWS_CREDENTIALS_TEST_INTERNAL_H
+
+#define AWS_CREDENTIALS_TEST_INTERNAL_H
+
+#include "flb_tests_internal.h"
+
+#define AWS_TEST_DATA_PATH(FILE_PATH) FLB_TESTS_DATA_PATH "data/aws_credentials/" FILE_PATH
+
+static int unset_profile_env()
+{
+ int ret;
+ ret = unsetenv("HOME");
+ if (ret < 0) {
+ flb_errno();
+ return -1;
+ }
+ ret = unsetenv("AWS_CONFIG_FILE");
+ if (ret < 0) {
+ flb_errno();
+ return -1;
+ }
+ ret = unsetenv("AWS_SHARED_CREDENTIALS_FILE");
+ if (ret < 0) {
+ flb_errno();
+ return -1;
+ }
+ ret = unsetenv("AWS_DEFAULT_PROFILE");
+ if (ret < 0) {
+ flb_errno();
+ return -1;
+ }
+ ret = unsetenv("AWS_PROFILE");
+ if (ret < 0) {
+ flb_errno();
+ return -1;
+ }
+ return 0;
+}
+
+#endif /* AWS_CREDENTIALS_TEST_INTERNAL_H */
diff --git a/fluent-bit/tests/internal/aws_util.c b/fluent-bit/tests/internal/aws_util.c
new file mode 100644
index 00000000..6f6fbdb1
--- /dev/null
+++ b/fluent-bit/tests/internal/aws_util.c
@@ -0,0 +1,395 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+#include <fluent-bit/flb_sds.h>
+#include <fluent-bit/flb_aws_util.h>
+#include <fluent-bit/flb_mem.h>
+#include <fluent-bit/flb_pthread.h>
+
+#include "flb_tests_internal.h"
+
+#define S3_KEY_FORMAT_TAG_PART "logs/$TAG[2]/$TAG[0]/%Y/%m/%d"
+#define S3_OBJECT_KEY_TAG_PART "logs/ccc/aa/2020/08/15"
+
+#define S3_KEY_FORMAT_FULL_TAG "logs/$TAG/%Y/%m/%d"
+#define S3_OBJECT_KEY_FULL_TAG "logs/aa.bb.ccc/2020/08/15"
+
+#define S3_KEY_FORMAT_SPECIAL_CHARCATERS_TAG "logs/my.great_photos-2020:jan/$TAG/%Y/%m/%d"
+#define S3_OBJECT_KEY_SPECIAL_CHARCATERS_TAG "logs/my.great_photos-2020:jan/aa.bb.ccc/2020/08/15"
+
+#define S3_OBJECT_KEY_INVALID_DELIMITER "logs/aa.bb-ccc[2]/aa.bb-ccc/2020/08/15"
+
+#define S3_KEY_FORMAT_INVALID_TAG "logs/$TAG[2]/$TAG[-1]/%Y/%m/%d"
+#define S3_OBJECY_KEY_INVALID_TAG "logs/ccc/aa.bb.ccc[-1]/2020/08/15"
+
+#define S3_KEY_FORMAT_OUT_OF_BOUNDS_TAG "logs/$TAG[2]/$TAG[]/%Y/%m/%d"
+
+#define S3_KEY_FORMAT_STATIC_STRING "logs/fluent-bit"
+
+#define S3_KEY_FORMAT_UUID "logs/$UUID"
+#define S3_OBJECT_KEY_UUID "logs/"
+
+#define S3_KEY_FORMAT_ALL_OPTIONS "logs/$TAG[2]/$TAG[1]/$TAG[0]/%Y/%m/%d/file-$INDEX-$UUID"
+#define S3_OBJECT_KEY_ALL_OPTIONS "logs/ccc/bb/aa/2020/08/15/file-0-"
+
+#define S3_KEY_FORMAT_VALID_INDEX "logs/a-$INDEX-b-c"
+#define S3_OBJECT_KEY_VALID_INDEX "logs/a-12-b-c"
+#define S3_OBJECT_KEY_PRE_OVERFLOW_INDEX "logs/a-18446744073709551615-b-c"
+#define S3_OBJECT_KEY_POST_OVERFLOW_INDEX "logs/a-0-b-c"
+
+#define S3_KEY_FORMAT_MIXED_TIMESTAMP "logs/%Y/m/%m/d/%d/%q"
+#ifdef FLB_SYSTEM_MACOS
+/* macOS's strftime throws away for % character for % and suqsequent invalid format character. */
+#define S3_OBJECT_KEY_MIXED_TIMESTAMP "logs/2020/m/08/d/15/q"
+#else
+#define S3_OBJECT_KEY_MIXED_TIMESTAMP "logs/2020/m/08/d/15/%q"
+#endif
+
+#define NO_TAG ""
+#define TAG "aa.bb.ccc"
+#define MULTI_DELIMITER_TAG "aa.bb-ccc"
+#define TAG_DELIMITER "."
+#define TAG_DELIMITERS ".-"
+#define INVALID_TAG_DELIMITERS ",/"
+#define VALID_SEQ_INDEX 0
+
+static void initialization_crutch()
+{
+ struct flb_config *config;
+
+ config = flb_config_init();
+
+ if (config == NULL) {
+ return;
+ }
+
+ flb_config_exit(config);
+}
+
+
+pthread_mutex_t env_mutex = PTHREAD_MUTEX_INITIALIZER;
+static int mktime_utc(struct tm *day, time_t *tm)
+{
+ int ret;
+ char *tzvar = NULL;
+ char orig_tz[256] = {0};
+ time_t t;
+
+ if (!TEST_CHECK(day != NULL)) {
+ TEST_MSG("struct tm is null");
+ return -1;
+ }
+ if (!TEST_CHECK(tm != NULL)) {
+ TEST_MSG("time_t is null");
+ return -1;
+ }
+
+ pthread_mutex_lock(&env_mutex);
+
+ /* save current TZ var */
+ tzvar = getenv("TZ");
+ if (tzvar != NULL) {
+ if (!TEST_CHECK(strlen(tzvar) <= sizeof(orig_tz))) {
+ TEST_MSG("TZ is large. len=%ld TZ=%s", strlen(tzvar), tzvar);
+ pthread_mutex_unlock(&env_mutex);
+ return -1;
+ }
+ strncpy(&orig_tz[0], tzvar, sizeof(orig_tz));
+ }
+
+ /* setenv is not thread safe */
+ ret = setenv("TZ", "UTC", 1);
+ if (!TEST_CHECK(ret == 0)) {
+ TEST_MSG("setenv failed");
+ pthread_mutex_unlock(&env_mutex);
+ return -1;
+ }
+
+ t = mktime(day);
+ *tm = t;
+
+ /* restore TZ */
+ if (tzvar != NULL) {
+ ret = setenv("TZ", &orig_tz[0], 1);
+ }
+ else {
+ ret = unsetenv("TZ");
+ }
+
+ pthread_mutex_unlock(&env_mutex);
+
+ return ret;
+}
+
+static void test_flb_aws_error()
+{
+ flb_sds_t error_type;
+ char *api_response = "{\"__type\":\"IncompleteSignatureException\","
+ "\"message\": \"Credential must have exactly 5 "
+ "slash-delimited elements, e.g. keyid/date/region/"
+ "service/term, got '<Credential>'\"}";
+ char *garbage = "garbage"; /* something that can't be parsed */
+
+ initialization_crutch();
+
+ error_type = flb_aws_error(api_response, strlen(api_response));
+
+ TEST_CHECK(strcmp("IncompleteSignatureException", error_type) == 0);
+
+ flb_sds_destroy(error_type);
+
+ error_type = flb_aws_error(garbage, strlen(garbage));
+
+ TEST_CHECK(error_type == NULL);
+
+ flb_sds_destroy(error_type);
+}
+
+static void test_flb_aws_endpoint()
+{
+ char *endpoint;
+
+ initialization_crutch();
+
+ endpoint = flb_aws_endpoint("cloudwatch", "ap-south-1");
+
+ TEST_CHECK(strcmp("cloudwatch.ap-south-1.amazonaws.com",
+ endpoint) == 0);
+ flb_free(endpoint);
+
+ /* China regions have a different TLD */
+ endpoint = flb_aws_endpoint("cloudwatch", "cn-north-1");
+
+ TEST_CHECK(strcmp("cloudwatch.cn-north-1.amazonaws.com.cn",
+ endpoint) == 0);
+ flb_free(endpoint);
+
+}
+
+static void test_flb_get_s3_key_multi_tag_exists()
+{
+ flb_sds_t s3_key_format = NULL;
+ struct tm day = { 0, 0, 0, 15, 7, 120};
+ time_t t;
+
+ initialization_crutch();
+
+ mktime_utc(&day, &t);
+ s3_key_format = flb_get_s3_key(S3_KEY_FORMAT_TAG_PART, t, TAG, TAG_DELIMITER, 0);
+ TEST_CHECK(strcmp(s3_key_format, S3_OBJECT_KEY_TAG_PART) == 0);
+
+ flb_sds_destroy(s3_key_format);
+}
+
+static void test_flb_get_s3_key_full_tag()
+{
+ flb_sds_t s3_key_format = NULL;
+ struct tm day = { 0, 0, 0, 15, 7, 120};
+ time_t t;
+
+ initialization_crutch();
+
+ mktime_utc(&day, &t);
+ s3_key_format = flb_get_s3_key(S3_KEY_FORMAT_FULL_TAG, t, TAG, TAG_DELIMITER, 0);
+ TEST_CHECK(strcmp(s3_key_format, S3_OBJECT_KEY_FULL_TAG) == 0);
+
+ flb_sds_destroy(s3_key_format);
+}
+
+static void test_flb_get_s3_key_tag_special_characters()
+{
+ flb_sds_t s3_key_format = NULL;
+ struct tm day = { 0, 0, 0, 15, 7, 120};
+ time_t t;
+
+ initialization_crutch();
+
+ mktime_utc(&day, &t);
+ s3_key_format = flb_get_s3_key(S3_KEY_FORMAT_SPECIAL_CHARCATERS_TAG, t, TAG,
+ TAG_DELIMITER, 0);
+ TEST_CHECK(strcmp(s3_key_format, S3_OBJECT_KEY_SPECIAL_CHARCATERS_TAG) == 0);
+
+ flb_sds_destroy(s3_key_format);
+}
+
+static void test_flb_get_s3_key_multi_tag_delimiter()
+{
+ flb_sds_t s3_key_format = NULL;
+ struct tm day = { 0, 0, 0, 15, 7, 120};
+ time_t t;
+
+ initialization_crutch();
+
+ mktime_utc(&day, &t);
+ s3_key_format = flb_get_s3_key(S3_KEY_FORMAT_TAG_PART, t, MULTI_DELIMITER_TAG,
+ TAG_DELIMITERS, 0);
+ TEST_CHECK(strcmp(s3_key_format, S3_OBJECT_KEY_TAG_PART) == 0);
+
+ flb_sds_destroy(s3_key_format);
+}
+
+static void test_flb_get_s3_key_invalid_tag_delimiter()
+{
+ flb_sds_t s3_key_format = NULL;
+ struct tm day = { 0, 0, 0, 15, 7, 120};
+ time_t t;
+
+ initialization_crutch();
+
+ mktime_utc(&day, &t);
+ s3_key_format = flb_get_s3_key(S3_KEY_FORMAT_TAG_PART, t, MULTI_DELIMITER_TAG,
+ INVALID_TAG_DELIMITERS, 0);
+ TEST_CHECK(strcmp(s3_key_format, S3_OBJECT_KEY_INVALID_DELIMITER) == 0);
+
+ flb_sds_destroy(s3_key_format);
+}
+
+static void test_flb_get_s3_key_invalid_tag_index()
+{
+ flb_sds_t s3_key_format = NULL;
+ struct tm day = { 0, 0, 0, 15, 7, 120};
+ time_t t;
+
+ initialization_crutch();
+
+ mktime_utc(&day, &t);
+ s3_key_format = flb_get_s3_key(S3_KEY_FORMAT_INVALID_TAG, t, TAG, TAG_DELIMITER, 0);
+ TEST_CHECK(strcmp(s3_key_format, S3_OBJECY_KEY_INVALID_TAG) == 0);
+
+ flb_sds_destroy(s3_key_format);
+}
+
+static void test_flb_get_s3_key_invalid_key_length()
+{
+ int i;
+ char buf[1100] = "";
+ char tmp[1024] = "";
+ flb_sds_t s3_key_format = NULL;
+
+ initialization_crutch();
+
+ for (i = 0; i <= 975; i++){
+ tmp[i] = 'a';
+ }
+ snprintf(buf, sizeof(buf), "%s%s", S3_KEY_FORMAT_SPECIAL_CHARCATERS_TAG, tmp);
+ struct tm day = { 0, 0, 0, 15, 7, 120};
+ time_t t;
+
+ mktime_utc(&day, &t);
+ s3_key_format = flb_get_s3_key(buf, t, TAG, TAG_DELIMITER, 0);
+ TEST_CHECK(strlen(s3_key_format) <= 1024);
+
+ flb_sds_destroy(s3_key_format);
+}
+
+static void test_flb_get_s3_key_static_string()
+{
+ flb_sds_t s3_key_format = NULL;
+ struct tm day = { 0, 0, 0, 15, 7, 120};
+ time_t t;
+
+ initialization_crutch();
+
+ mktime_utc(&day, &t);
+ s3_key_format = flb_get_s3_key(S3_KEY_FORMAT_STATIC_STRING, t, NO_TAG,
+ TAG_DELIMITER, 0);
+ TEST_CHECK(strcmp(s3_key_format, S3_KEY_FORMAT_STATIC_STRING) == 0);
+
+ flb_sds_destroy(s3_key_format);
+}
+
+static void test_flb_get_s3_key_valid_index()
+{
+ flb_sds_t s3_key_format = NULL;
+ struct tm day = { 0, 0, 0, 15, 7, 120};
+ time_t t;
+
+ initialization_crutch();
+
+ mktime_utc(&day, &t);
+ s3_key_format = flb_get_s3_key(S3_KEY_FORMAT_VALID_INDEX, t, NO_TAG,
+ TAG_DELIMITER, 12);
+ TEST_CHECK(strcmp(s3_key_format, S3_OBJECT_KEY_VALID_INDEX) == 0);
+
+ flb_sds_destroy(s3_key_format);
+}
+
+static void test_flb_get_s3_key_increment_index()
+{
+ struct tm day = { 0, 0, 0, 15, 7, 120};
+ time_t t;
+ flb_sds_t s3_key_format = NULL;
+
+ initialization_crutch();
+
+ mktime_utc(&day, &t);
+ s3_key_format = flb_get_s3_key(S3_KEY_FORMAT_VALID_INDEX, t, NO_TAG,
+ TAG_DELIMITER, 5);
+
+ TEST_CHECK(strcmp(s3_key_format, "logs/a-5-b-c") == 0);
+
+ flb_sds_destroy(s3_key_format);
+
+ s3_key_format = flb_get_s3_key(S3_KEY_FORMAT_VALID_INDEX, t, NO_TAG,
+ TAG_DELIMITER, 10);
+
+ TEST_CHECK(strcmp(s3_key_format, "logs/a-10-b-c") == 0);
+
+ flb_sds_destroy(s3_key_format);
+}
+
+static void test_flb_get_s3_key_index_overflow()
+{
+ flb_sds_t s3_key_format = NULL;
+ struct tm day = { 0, 0, 0, 15, 7, 120};
+ time_t t;
+ uint64_t index = 18446744073709551615U;
+
+ initialization_crutch();
+
+ mktime_utc(&day, &t);
+ s3_key_format = flb_get_s3_key(S3_KEY_FORMAT_VALID_INDEX, t, NO_TAG,
+ TAG_DELIMITER, index);
+ TEST_CHECK(strcmp(s3_key_format, S3_OBJECT_KEY_PRE_OVERFLOW_INDEX) == 0);
+ flb_sds_destroy(s3_key_format);
+
+ index++;
+ s3_key_format = flb_get_s3_key(S3_KEY_FORMAT_VALID_INDEX, t, NO_TAG,
+ TAG_DELIMITER, index);
+ TEST_CHECK(strcmp(s3_key_format, S3_OBJECT_KEY_POST_OVERFLOW_INDEX) == 0);
+
+ flb_sds_destroy(s3_key_format);
+}
+
+static void test_flb_get_s3_key_mixed_timestamp()
+{
+ flb_sds_t s3_key_format = NULL;
+ struct tm day = { 0, 0, 0, 15, 7, 120};
+ time_t t;
+
+ initialization_crutch();
+
+ mktime_utc(&day, &t);
+ s3_key_format = flb_get_s3_key(S3_KEY_FORMAT_MIXED_TIMESTAMP, t, NO_TAG,
+ TAG_DELIMITER, 12);
+ TEST_CHECK(strcmp(s3_key_format, S3_OBJECT_KEY_MIXED_TIMESTAMP) == 0);
+
+ flb_sds_destroy(s3_key_format);
+}
+
+TEST_LIST = {
+ { "parse_api_error" , test_flb_aws_error},
+ { "flb_aws_endpoint" , test_flb_aws_endpoint},
+ {"flb_get_s3_key_multi_tag_exists", test_flb_get_s3_key_multi_tag_exists},
+ {"flb_get_s3_key_full_tag", test_flb_get_s3_key_full_tag},
+ {"flb_get_s3_key_tag_special_characters", test_flb_get_s3_key_tag_special_characters},
+ {"flb_get_s3_key_multi_tag_delimiter", test_flb_get_s3_key_multi_tag_delimiter},
+ {"flb_get_s3_key_invalid_tag_delimiter", test_flb_get_s3_key_invalid_tag_delimiter},
+ {"flb_get_s3_key_invalid_tag_index", test_flb_get_s3_key_invalid_tag_index},
+ {"flb_get_s3_key_invalid_key_length", test_flb_get_s3_key_invalid_key_length},
+ {"flb_get_s3_key_static_string", test_flb_get_s3_key_static_string},
+ {"flb_get_s3_key_valid_index", test_flb_get_s3_key_valid_index},
+ {"flb_get_s3_key_increment_index", test_flb_get_s3_key_increment_index},
+ {"flb_get_s3_key_index_overflow", test_flb_get_s3_key_index_overflow},
+ {"flb_get_s3_key_mixed_timestamp", test_flb_get_s3_key_mixed_timestamp},
+ { 0 }
+};
diff --git a/fluent-bit/tests/internal/base64.c b/fluent-bit/tests/internal/base64.c
new file mode 100644
index 00000000..4375870f
--- /dev/null
+++ b/fluent-bit/tests/internal/base64.c
@@ -0,0 +1,50 @@
+#include <fluent-bit/flb_sds.h>
+#include <fluent-bit/flb_mem.h>
+#include <fluent-bit/flb_base64.h>
+#include <fluent-bit/flb_time.h>
+
+#include "flb_tests_internal.h"
+
+static void b64_basic_test_encode()
+{
+ char* data = "Hello world";
+ char out[100];
+ char* expect = "SGVsbG8gd29ybGQ=";
+ size_t olen;
+ out[16] = 'X';
+
+ flb_base64_encode((unsigned char *) out, 100, &olen, (unsigned char *)data, 11);
+
+ TEST_CHECK(strlen(out) == 16 && olen == 16);
+ TEST_MSG("Base64 encode failed to output result of expected length");
+
+ TEST_CHECK(strcmp(out, expect) == 0);
+ TEST_MSG("Base64 encode failed to output result of expected value");
+
+ TEST_CHECK(out[16] == 0);
+ TEST_MSG("Base64 not null terminated");
+ return;
+}
+
+static void b64_basic_test_decode()
+{
+ char* data = "SGVsbG8gd29ybGQ=";
+ char out[100] = { 0 };
+ char* expect = "Hello world";
+ size_t olen;
+
+ flb_base64_decode((unsigned char *) out, 100, &olen, (unsigned char *)data, 16);
+
+ TEST_CHECK(strlen(out) == 11 && olen == 11);
+ TEST_MSG("Base64 decode failed to output result of expected length");
+
+ TEST_CHECK(strcmp(out, expect) == 0);
+ TEST_MSG("Base64 decode failed to output result of expected value");
+ return;
+}
+
+TEST_LIST = {
+ { "b64_basic_test_encode" , b64_basic_test_encode },
+ { "b64_basic_test_decode", b64_basic_test_decode },
+ { 0 }
+};
diff --git a/fluent-bit/tests/internal/bucket_queue.c b/fluent-bit/tests/internal/bucket_queue.c
new file mode 100644
index 00000000..ddc93597
--- /dev/null
+++ b/fluent-bit/tests/internal/bucket_queue.c
@@ -0,0 +1,248 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+#include <fluent-bit/flb_compat.h>
+#include <fluent-bit/flb_info.h>
+#include <fluent-bit/flb_mem.h>
+#include <fluent-bit/flb_pack.h>
+#include <fluent-bit/flb_mp.h>
+#include <msgpack.h>
+
+#include "flb_tests_internal.h"
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <fluent-bit/flb_bucket_queue.h>
+#include <monkey/mk_core/mk_list.h>
+
+#define TST_PRIORITY_OP_ADD 1
+#define TST_PRIORITY_OP_DELETE 2
+#define TST_PRIORITY_OP_EXPECT 3
+#define TST_PRIORITY_OP_POP 3
+
+
+struct bucket_queue_entry {
+ char *tag;
+ size_t priority;
+ struct mk_list item;
+ size_t add_idx;
+};
+
+struct bucket_queue_op_add {
+ struct bucket_queue_entry *entries;
+};
+
+struct bucket_queue_op_expect {
+ char *tag;
+};
+
+struct bucket_queue_op_pop {
+ char *tag;
+};
+
+union bucket_queue_op_union {
+ struct bucket_queue_op_pop pop;
+ struct bucket_queue_op_add add;
+ struct bucket_queue_op_expect expect;
+};
+
+struct bucket_queue_op {
+ char op;
+ void *op_description;
+};
+
+void test_create_destroy()
+{
+ struct flb_bucket_queue *bucket_queue;
+ bucket_queue = flb_bucket_queue_create(100);
+ flb_bucket_queue_destroy(bucket_queue);
+}
+
+void test_add_priorities()
+{
+ struct flb_bucket_queue *bucket_queue;
+
+ struct bucket_queue_op operations[] = {
+ {
+ TST_PRIORITY_OP_ADD,
+ &(struct bucket_queue_op_add) {
+ (struct bucket_queue_entry[]) {
+ {
+ "a",
+ 2 /* priority */
+ },
+ {
+ "b",
+ 2 /* priority */
+ },
+ {
+ "c",
+ 0 /* priority */
+ },
+ {
+ "d",
+ 2 /* priority */
+ },
+ {
+ "e",
+ 3 /* priority */
+ },
+ { 0 }
+ }
+ }
+ },
+ {
+ TST_PRIORITY_OP_POP,
+ &(struct bucket_queue_op_pop) {
+ "c"
+ }
+ },
+ {
+ TST_PRIORITY_OP_POP,
+ &(struct bucket_queue_op_pop) {
+ "a"
+ }
+ },
+ {
+ TST_PRIORITY_OP_POP,
+ &(struct bucket_queue_op_pop) {
+ "b"
+ }
+ },
+ {
+ TST_PRIORITY_OP_POP,
+ &(struct bucket_queue_op_pop) {
+ "d"
+ }
+ },
+ {
+ TST_PRIORITY_OP_POP,
+ &(struct bucket_queue_op_pop) {
+ "e"
+ }
+ },
+ {
+ TST_PRIORITY_OP_POP,
+ &(struct bucket_queue_op_pop) {
+ "<EXPECT_NULL>"
+ }
+ },
+ {
+ TST_PRIORITY_OP_ADD,
+ &(struct bucket_queue_op_add) {
+ (struct bucket_queue_entry[]) {
+ {
+ "f",
+ 1 /* priority */
+ },
+ {
+ "g",
+ 1 /* priority */
+ },
+ {
+ "h",
+ 0
+ },
+ { 0 }
+ }
+ }
+ },
+ {
+ TST_PRIORITY_OP_POP,
+ &(struct bucket_queue_op_pop) {
+ "h"
+ }
+ },
+ {
+ TST_PRIORITY_OP_POP,
+ &(struct bucket_queue_op_pop) {
+ "f"
+ }
+ },
+ {
+ TST_PRIORITY_OP_POP,
+ &(struct bucket_queue_op_pop) {
+ "g"
+ }
+ },
+ {
+ TST_PRIORITY_OP_POP,
+ &(struct bucket_queue_op_pop) {
+ "<EXPECT_NULL>"
+ }
+ },
+ {
+ TST_PRIORITY_OP_POP,
+ &(struct bucket_queue_op_pop) {
+ "<EXPECT_NULL>"
+ }
+ },
+ { 0 }
+ };
+
+ bucket_queue = flb_bucket_queue_create(4);
+ struct bucket_queue_op *op;
+ struct bucket_queue_op_add *op_desc_add;
+ struct bucket_queue_op_pop *op_desc_pop;
+ struct bucket_queue_entry *bucket_queue_entry;
+ struct mk_list *list_entry;
+ size_t add_idx = 0;
+ int ret;
+ int retB;
+
+ for (op = operations; op->op != 0; ++op) {
+ if (op->op == TST_PRIORITY_OP_ADD) {
+ op_desc_add = (struct bucket_queue_op_add *) op->op_description;
+ for (bucket_queue_entry = op_desc_add->entries; bucket_queue_entry->tag != 0;
+ ++bucket_queue_entry) {
+ bucket_queue_entry->add_idx = add_idx++;
+ ret = flb_bucket_queue_add(bucket_queue, &bucket_queue_entry->item,
+ bucket_queue_entry->priority);
+ TEST_CHECK(ret == 0);
+ TEST_MSG("[op %zu][entry %zu] Add failed. Returned %d", op - operations,
+ bucket_queue_entry - op_desc_add->entries, ret);
+ }
+ }
+
+ else if (op->op == TST_PRIORITY_OP_POP) {
+ op_desc_pop = (struct bucket_queue_op_pop *) op->op_description;
+ list_entry = flb_bucket_queue_pop_min(bucket_queue);
+ ret = strcmp("<EXPECT_NULL>", op_desc_pop->tag);
+
+ if (ret == 0) {
+ TEST_CHECK(list_entry == NULL);
+ TEST_MSG("[op %zu] Pop failed.", op - operations);
+ TEST_MSG("Expect: null");
+ TEST_MSG("Produced: list_entry %p", list_entry);
+ }
+ else {
+ TEST_CHECK(list_entry != NULL);
+ TEST_MSG("[op %zu] Pop failed.", op - operations);
+ TEST_MSG("Expect: non-null");
+ TEST_MSG("Produced: null");
+
+ bucket_queue_entry = mk_list_entry(list_entry, struct bucket_queue_entry, item);
+ ret = strcmp(bucket_queue_entry->tag, op_desc_pop->tag);
+ TEST_CHECK(ret == 0);
+ TEST_MSG("[op %zu] Pop failed.", op - operations);
+ TEST_MSG("Expect tag: %s", op_desc_pop->tag);
+ TEST_MSG("Produced tag: %s", bucket_queue_entry->tag);
+
+ ret = strlen(bucket_queue_entry->tag);
+ retB = strlen(op_desc_pop->tag);
+ TEST_CHECK(ret == retB);
+ TEST_MSG("[op %zu] Pop failed.", op - operations);
+ TEST_MSG("Expect tag len: %d", retB);
+ TEST_MSG("Produced tag len: %d", ret);
+ }
+ }
+ }
+
+ flb_bucket_queue_destroy(bucket_queue);
+}
+
+TEST_LIST = {
+ {"create_destroy" , test_create_destroy},
+ {"add_priorities" , test_add_priorities},
+ { 0 }
+};
diff --git a/fluent-bit/tests/internal/config_format.c b/fluent-bit/tests/internal/config_format.c
new file mode 100644
index 00000000..7f887a54
--- /dev/null
+++ b/fluent-bit/tests/internal/config_format.c
@@ -0,0 +1,86 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+#include <fluent-bit/flb_info.h>
+#include <fluent-bit/flb_mem.h>
+#include <fluent-bit/flb_kv.h>
+#include <fluent-bit/flb_config_format.h>
+
+#include <cfl/cfl.h>
+#include <cfl/cfl_variant.h>
+#include <cfl/cfl_kvlist.h>
+
+#include "flb_tests_internal.h"
+
+void test_api()
+{
+ struct flb_cf *cf;
+ struct flb_cf_section *s_tmp;
+ struct flb_cf_section *service;
+ struct flb_cf_group *g_tmp;
+ struct cfl_variant *ret;
+ struct flb_kv *meta;
+
+ /* create context */
+ cf = flb_cf_create();
+ TEST_CHECK(cf != NULL);
+
+ /* create service section */
+ service = flb_cf_section_create(cf, "SERVICE", 7);
+ TEST_CHECK(service != NULL);
+
+ /* add a property */
+ ret = flb_cf_section_property_add(cf, service->properties, "key", 3, "val", 3);
+ TEST_CHECK(ret != NULL);
+
+ /* add a property with empty spaces on left/right */
+ ret = flb_cf_section_property_add(cf, service->properties, " key ", 5, " val ", 7);
+ TEST_CHECK(ret != NULL);
+
+ /* add an invalid property */
+ ret = flb_cf_section_property_add(cf, service->properties, " ", 3, "", 0);
+ TEST_CHECK(ret == NULL);
+
+ /* try to add another 'SERVICE' section, it should return the same one */
+ s_tmp = flb_cf_section_create(cf, "SERVICE", 7);
+ TEST_CHECK(s_tmp == service);
+
+ /* add a valid section */
+ s_tmp = flb_cf_section_create(cf, "INPUT", 5);
+ TEST_CHECK(s_tmp != NULL);
+
+ TEST_CHECK(mk_list_size(&cf->inputs) == 1);
+
+ /* add property to the section recently created */
+ ret = flb_cf_section_property_add(cf, s_tmp->properties, "key", 3, "val", 3);
+ TEST_CHECK(ret != NULL);
+
+ /* groups: add groups to the last section created */
+ g_tmp = flb_cf_group_create(cf, s_tmp, "FLUENT GROUP", 12);
+ TEST_CHECK(g_tmp != NULL);
+
+ /* add properties to the group */
+ ret = flb_cf_section_property_add(cf, g_tmp->properties, "key", 3, "val", 3);
+ TEST_CHECK(ret != NULL);
+
+ /* groups: invalid group */
+ g_tmp = flb_cf_group_create(cf, s_tmp, "", 0);
+ TEST_CHECK(g_tmp == NULL);
+
+ /* Meta commands */
+ meta = flb_cf_meta_property_add(cf, "@SET a=1 ", 20);
+ TEST_CHECK(meta != NULL);
+ TEST_CHECK(flb_sds_len(meta->key) == 3 && strcmp(meta->key, "SET") == 0);
+ TEST_CHECK(flb_sds_len(meta->val) == 3 && strcmp(meta->val, "a=1") == 0);
+
+ /* invalid meta */
+ meta = flb_cf_meta_property_add(cf, "@a=1 ", 5);
+ TEST_CHECK(meta == NULL);
+
+ /* destroy context */
+ flb_cf_destroy(cf);
+}
+
+TEST_LIST = {
+ { "api" , test_api},
+ { 0 }
+};
diff --git a/fluent-bit/tests/internal/config_format_fluentbit.c b/fluent-bit/tests/internal/config_format_fluentbit.c
new file mode 100644
index 00000000..3f158573
--- /dev/null
+++ b/fluent-bit/tests/internal/config_format_fluentbit.c
@@ -0,0 +1,353 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+#include <fluent-bit/flb_info.h>
+#include <fluent-bit/flb_mem.h>
+#include <fluent-bit/flb_kv.h>
+#include <fluent-bit/flb_config_format.h>
+#include <fluent-bit/flb_sds.h>
+#include <sys/stat.h>
+
+#include <cfl/cfl.h>
+#include <cfl/cfl_list.h>
+
+#include "flb_tests_internal.h"
+
+#define FLB_000 FLB_TESTS_DATA_PATH "/data/config_format/classic/fluent-bit.conf"
+#define FLB_001 FLB_TESTS_DATA_PATH "/data/config_format/classic/issue_5880.conf"
+#define FLB_002 FLB_TESTS_DATA_PATH "/data/config_format/classic/indent_level_error.conf"
+#define FLB_003 FLB_TESTS_DATA_PATH "/data/config_format/classic/recursion.conf"
+#define FLB_004 FLB_TESTS_DATA_PATH "/data/config_format/classic/issue6281.conf"
+#define FLB_005 FLB_TESTS_DATA_PATH "/data/config_format/classic/nolimitline.conf"
+
+#define ERROR_LOG "fluentbit_conf_error.log"
+
+/* data/config_format/fluent-bit.conf */
+void test_basic()
+{
+ struct mk_list *head;
+ struct flb_cf *cf;
+ struct flb_cf_section *s;
+ struct flb_cf_group *g;
+
+ cf = flb_cf_fluentbit_create(NULL, FLB_000, NULL, 0);
+ TEST_CHECK(cf != NULL);
+
+ /* Total number of sections */
+ TEST_CHECK(mk_list_size(&cf->sections) == 8);
+
+ /* SERVICE check */
+ TEST_CHECK(cf->service != NULL);
+ if (cf->service) {
+ TEST_CHECK(cfl_list_size(&cf->service->properties->list) == 3);
+ }
+
+ /* Meta commands */
+ TEST_CHECK(mk_list_size(&cf->metas) == 2);
+
+ /* Check number sections per list */
+ TEST_CHECK(mk_list_size(&cf->parsers) == 1);
+ TEST_CHECK(mk_list_size(&cf->multiline_parsers) == 1);
+ TEST_CHECK(mk_list_size(&cf->customs) == 1);
+ TEST_CHECK(mk_list_size(&cf->inputs) == 1);
+ TEST_CHECK(mk_list_size(&cf->filters) == 1);
+ TEST_CHECK(mk_list_size(&cf->outputs) == 1);
+ TEST_CHECK(mk_list_size(&cf->others) == 1);
+
+ /* groups */
+ s = flb_cf_section_get_by_name(cf, "input");
+ TEST_CHECK(s != NULL);
+ TEST_CHECK(mk_list_size(&s->groups) == 2);
+
+ mk_list_foreach(head, &s->groups) {
+ g = mk_list_entry(head, struct flb_cf_group, _head);
+ TEST_CHECK(cfl_list_size(&g->properties->list) == 2);
+ }
+
+ printf("\n");
+ flb_cf_dump(cf);
+
+ flb_cf_destroy(cf);
+}
+
+struct str_list {
+ size_t size;
+ char **lists;
+};
+
+static int check_str_list(struct str_list *list, FILE *fp)
+{
+ flb_sds_t error_str = NULL;
+ char *p = NULL;
+ size_t size;
+ int str_size = 4096;
+ int i;
+ int fd = fileno(fp);
+ struct stat st;
+
+ if (!TEST_CHECK(list != NULL)) {
+ TEST_MSG("list is NULL");
+ return -1;
+ }
+ if (!TEST_CHECK(fp != NULL)) {
+ TEST_MSG("fp is NULL");
+ return -1;
+ }
+
+ if (!TEST_CHECK(fstat(fd, &st) == 0)) {
+ TEST_MSG("fstat failed");
+ return -1;
+ }
+ str_size = st.st_size;
+ error_str = flb_sds_create_size(str_size);
+ if (!TEST_CHECK(error_str != NULL)) {
+ TEST_MSG("flb_sds_create_size failed.");
+ return -1;
+ }
+
+ size = fread(error_str, 1, str_size, fp);
+ if (size < str_size) {
+ if (!TEST_CHECK(ferror(fp) == 0)) {
+ TEST_MSG("fread failed.");
+ clearerr(fp);
+ flb_sds_destroy(error_str);
+ return -1;
+ }
+ clearerr(fp);
+ }
+
+ for (i=0; i<list->size; i++) {
+ p = strstr(error_str, list->lists[i]);
+ if (!TEST_CHECK(p != NULL)) {
+ TEST_MSG(" Got :%s\n expect:%s", error_str, list->lists[i]);
+ }
+ }
+
+ flb_sds_destroy(error_str);
+ return 0;
+}
+
+
+/* https://github.com/fluent/fluent-bit/issues/5880 */
+void missing_value()
+{
+ struct flb_cf *cf;
+ FILE *fp = NULL;
+ char *expected_strs[] = {"undefined value", ":9:" /* lieno*/ };
+ struct str_list expected = {
+ .size = sizeof(expected_strs)/sizeof(char*),
+ .lists = &expected_strs[0],
+ };
+
+ unlink(ERROR_LOG);
+
+ fp = freopen(ERROR_LOG, "w+", stderr);
+ if (!TEST_CHECK(fp != NULL)) {
+ TEST_MSG("freopen failed. errno=%d path=%s", errno, ERROR_LOG);
+ exit(EXIT_FAILURE);
+ }
+
+ cf = flb_cf_fluentbit_create(NULL, FLB_001, NULL, 0);
+ TEST_CHECK(cf == NULL);
+ fflush(fp);
+ fclose(fp);
+
+ fp = fopen(ERROR_LOG, "r");
+ if (!TEST_CHECK(fp != NULL)) {
+ TEST_MSG("fopen failed. errno=%d path=%s", errno, ERROR_LOG);
+ unlink(ERROR_LOG);
+ exit(EXIT_FAILURE);
+ }
+
+ check_str_list(&expected, fp);
+
+ fclose(fp);
+ unlink(ERROR_LOG);
+}
+
+void indent_level_error()
+{
+ struct flb_cf *cf;
+ FILE *fp = NULL;
+ char *expected_strs[] = {"invalid", "indent", "level"};
+ struct str_list expected = {
+ .size = sizeof(expected_strs)/sizeof(char*),
+ .lists = &expected_strs[0],
+ };
+
+ unlink(ERROR_LOG);
+
+ fp = freopen(ERROR_LOG, "w+", stderr);
+ if (!TEST_CHECK(fp != NULL)) {
+ TEST_MSG("freopen failed. errno=%d path=%s", errno, ERROR_LOG);
+ exit(EXIT_FAILURE);
+ }
+
+ cf = flb_cf_create();
+ if (!TEST_CHECK(cf != NULL)) {
+ TEST_MSG("flb_cf_create failed");
+ exit(EXIT_FAILURE);
+ }
+
+ cf = flb_cf_fluentbit_create(cf, FLB_002, NULL, 0);
+ TEST_CHECK(cf == NULL);
+ fflush(fp);
+ fclose(fp);
+
+ fp = fopen(ERROR_LOG, "r");
+ if (!TEST_CHECK(fp != NULL)) {
+ TEST_MSG("fopen failed. errno=%d path=%s", errno, ERROR_LOG);
+ if (cf != NULL) {
+ flb_cf_destroy(cf);
+ }
+ unlink(ERROR_LOG);
+ exit(EXIT_FAILURE);
+ }
+
+ check_str_list(&expected, fp);
+ if (cf != NULL) {
+ flb_cf_destroy(cf);
+ }
+ fclose(fp);
+ unlink(ERROR_LOG);
+}
+
+void recursion()
+{
+ struct flb_cf *cf;
+
+ cf = flb_cf_create();
+ if (!TEST_CHECK(cf != NULL)) {
+ TEST_MSG("flb_cf_create failed");
+ exit(EXIT_FAILURE);
+ }
+
+ cf = flb_cf_fluentbit_create(cf, FLB_003, NULL, 0);
+
+ /* No SIGSEGV means success */
+}
+
+
+/*
+ * https://github.com/fluent/fluent-bit/issues/6281
+ *
+ * Fluent-bit loads issue6281.conf that loads issue6281_input/output.conf.
+ *
+ * config dir -+-- issue6281.conf
+ * |
+ * +-- issue6281_input.conf
+ * |
+ * +-- issue6281_output.conf
+ */
+void not_current_dir_files()
+{
+ struct flb_cf *cf;
+
+ cf = flb_cf_create();
+ if (!TEST_CHECK(cf != NULL)) {
+ TEST_MSG("flb_cf_create failed");
+ exit(EXIT_FAILURE);
+ }
+
+ cf = flb_cf_fluentbit_create(cf, FLB_004, NULL, 0);
+ if (!TEST_CHECK(cf != NULL)) {
+ TEST_MSG("flb_cf_fluentbit_create failed");
+ exit(EXIT_FAILURE);
+ }
+
+ if (cf != NULL) {
+ flb_cf_destroy(cf);
+ }
+}
+
+/* data/config_format/nolimitline.conf */
+void test_nolimit_line()
+{
+ struct flb_cf *cf;
+ struct flb_cf_section *s;
+ struct cfl_list *p_head;
+ struct cfl_kvpair *kv;
+
+ cf = flb_cf_fluentbit_create(NULL, FLB_005, NULL, 0);
+ TEST_CHECK(cf != NULL);
+
+ /* Total number of sections */
+ TEST_CHECK(mk_list_size(&cf->sections) == 3);
+
+ /* SERVICE check */
+ TEST_CHECK(cf->service == NULL);
+
+ /* Meta commands */
+ TEST_CHECK(mk_list_size(&cf->metas) == 0);
+
+ /* Check number sections per list */
+ TEST_CHECK(mk_list_size(&cf->inputs) == 1);
+ TEST_CHECK(mk_list_size(&cf->filters) == 1);
+ TEST_CHECK(mk_list_size(&cf->outputs) == 1);
+
+ /* Check the previous line buffer limit */
+ s = flb_cf_section_get_by_name(cf, "filter");
+ TEST_CHECK(s != NULL);
+ TEST_CHECK(mk_list_size(&s->groups) == 0);
+
+ if (cfl_list_size(&s->properties->list) > 0) {
+ TEST_CHECK(cfl_list_size(&s->properties->list) == 4);
+ cfl_list_foreach(p_head, &s->properties->list) {
+ kv = cfl_list_entry(p_head, struct cfl_kvpair, _head);
+ if (strcmp(kv->key, "code") == 0) {
+ TEST_CHECK(cfl_sds_len(kv->val->data.as_string) > FLB_DEFAULT_CF_BUF_SIZE);
+ }
+ }
+ }
+
+ printf("\n");
+ flb_cf_dump(cf);
+
+ flb_cf_destroy(cf);
+}
+
+static inline int check_snake_case(char *input, char *output)
+{
+ int len;
+ int ret;
+ flb_sds_t out;
+ struct flb_cf *cf;
+
+
+ cf = flb_cf_create();
+ flb_cf_set_origin_format(cf, FLB_CF_CLASSIC);
+
+ len = strlen(input);
+ out = flb_cf_key_translate(cf, input, len);
+
+ ret = strcmp(out, output);
+
+ flb_sds_destroy(out);
+
+ flb_cf_destroy(cf);
+
+ if (ret == 0) {
+ return FLB_TRUE;
+ }
+
+ return FLB_FALSE;
+}
+
+static void test_snake_case_key()
+{
+ /* normal conversion */
+ TEST_CHECK(check_snake_case("a", "a") == FLB_TRUE);
+ TEST_CHECK(check_snake_case("aB", "ab") == FLB_TRUE);
+ TEST_CHECK(check_snake_case("aBc", "abc") == FLB_TRUE);
+ TEST_CHECK(check_snake_case("interval_Sec", "interval_sec") == FLB_TRUE);
+}
+
+TEST_LIST = {
+ { "basic" , test_basic},
+ { "missing_value_issue5880" , missing_value},
+ { "indent_level_error" , indent_level_error},
+ { "recursion" , recursion},
+ { "not_current_dir_files", not_current_dir_files},
+ { "no_limit_line", test_nolimit_line},
+ { "snake_case_key", test_snake_case_key},
+ { 0 }
+};
diff --git a/fluent-bit/tests/internal/config_format_yaml.c b/fluent-bit/tests/internal/config_format_yaml.c
new file mode 100644
index 00000000..3135022b
--- /dev/null
+++ b/fluent-bit/tests/internal/config_format_yaml.c
@@ -0,0 +1,324 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+#include <fluent-bit/flb_info.h>
+#include <fluent-bit/flb_mem.h>
+#include <fluent-bit/flb_kv.h>
+#include <fluent-bit/flb_str.h>
+#include <fluent-bit/flb_config_format.h>
+
+#include <cfl/cfl.h>
+#include <cfl/cfl_list.h>
+
+#include "flb_tests_internal.h"
+
+#define FLB_TESTS_CONF_PATH FLB_TESTS_DATA_PATH "/data/config_format/yaml"
+#define FLB_000 FLB_TESTS_CONF_PATH "/fluent-bit.yaml"
+#define FLB_001 FLB_TESTS_CONF_PATH "/issue_7559.yaml"
+
+/*
+ * Configurations to test:
+ * * basic single input to single output
+ * * basic single input to single output with a filter
+ * * includes
+ * * slist
+ * * conf parsers
+ * * yaml parsers
+ * * customs
+ * * service
+ * * env
+ */
+
+/* data/config_format/fluent-bit.yaml */
+static void test_basic()
+{
+ struct mk_list *head;
+ struct flb_cf *cf;
+ struct flb_cf_section *s;
+ struct flb_cf_group *g;
+ struct cfl_variant *v;
+ int idx = 0;
+
+ cf = flb_cf_yaml_create(NULL, FLB_000, NULL, 0);
+ TEST_CHECK(cf != NULL);
+ if (!cf) {
+ exit(EXIT_FAILURE);
+ }
+
+ /* Total number of sections */
+ TEST_CHECK(mk_list_size(&cf->sections) == 9);
+
+ /* SERVICE check */
+ TEST_CHECK(cf->service != NULL);
+ if (cf->service) {
+ TEST_CHECK(cfl_list_size(&cf->service->properties->list) == 3);
+ }
+
+ /* Check number sections per list */
+ TEST_CHECK(mk_list_size(&cf->parsers) == 0);
+ TEST_CHECK(mk_list_size(&cf->multiline_parsers) == 0);
+ TEST_CHECK(mk_list_size(&cf->customs) == 1);
+ TEST_CHECK(mk_list_size(&cf->inputs) == 3);
+ TEST_CHECK(mk_list_size(&cf->filters) == 1);
+ TEST_CHECK(mk_list_size(&cf->outputs) == 2);
+ TEST_CHECK(mk_list_size(&cf->others) == 1);
+
+ /* check inputs */
+ idx = 0;
+ mk_list_foreach(head, &cf->inputs) {
+ s = mk_list_entry(head, struct flb_cf_section, _head_section);
+ switch (idx) {
+ case 0:
+ v = flb_cf_section_property_get(cf, s, "name");
+ TEST_CHECK(v->type == CFL_VARIANT_STRING);
+ TEST_CHECK(strcmp(v->data.as_string, "dummy") == 0);
+ break;
+ case 1:
+ v = flb_cf_section_property_get(cf, s, "name");
+ TEST_CHECK(v->type == CFL_VARIANT_STRING);
+ TEST_CHECK(strcmp(v->data.as_string, "tail") == 0);
+ v = flb_cf_section_property_get(cf, s, "path");
+ TEST_CHECK(v->type == CFL_VARIANT_STRING);
+ TEST_CHECK(strcmp(v->data.as_string, "./test.log") == 0);
+ break;
+ case 2:
+ v = flb_cf_section_property_get(cf, s, "name");
+ TEST_CHECK(v->type == CFL_VARIANT_STRING);
+ TEST_CHECK(strcmp(v->data.as_string, "tail") == 0);
+ v = flb_cf_section_property_get(cf, s, "path");
+ TEST_CHECK(v->type == CFL_VARIANT_STRING);
+ TEST_CHECK(strcmp(v->data.as_string, "./test.log") == 0);
+ break;
+ }
+ idx++;
+ }
+
+ /* check outputs */
+ idx = 0;
+ mk_list_foreach(head, &cf->outputs) {
+ s = mk_list_entry(head, struct flb_cf_section, _head_section);
+ switch (idx) {
+ case 0:
+ v = flb_cf_section_property_get(cf, s, "name");
+ TEST_CHECK(v->type == CFL_VARIANT_STRING);
+ TEST_CHECK(strcmp(v->data.as_string, "stdout") == 0);
+ break;
+ case 1:
+ v = flb_cf_section_property_get(cf, s, "name");
+ TEST_CHECK(v->type == CFL_VARIANT_STRING);
+ TEST_CHECK(strcmp(v->data.as_string, "stdout") == 0);
+ break;
+ }
+ idx++;
+ }
+
+ /* check filters */
+ idx = 0;
+ mk_list_foreach(head, &cf->filters) {
+ s = mk_list_entry(head, struct flb_cf_section, _head_section);
+ switch (idx) {
+ case 0:
+ v = flb_cf_section_property_get(cf, s, "name");
+ TEST_CHECK(v->type == CFL_VARIANT_STRING);
+ TEST_CHECK(strcmp(v->data.as_string, "record_modifier") == 0);
+ break;
+ }
+ idx++;
+ }
+
+ /* groups */
+ s = flb_cf_section_get_by_name(cf, "input");
+ TEST_CHECK(s != NULL);
+ TEST_CHECK(mk_list_size(&s->groups) == 1);
+
+ mk_list_foreach(head, &s->groups) {
+ g = mk_list_entry(head, struct flb_cf_group, _head);
+ TEST_CHECK(cfl_list_size(&g->properties->list) == 2);
+ }
+
+ printf("\n");
+ flb_cf_dump(cf);
+ flb_cf_destroy(cf);
+}
+
+/* https://github.com/fluent/fluent-bit/issues/7559 */
+static void test_customs_section()
+{
+ struct flb_cf *cf;
+ struct flb_cf_section *s;
+
+ cf = flb_cf_yaml_create(NULL, FLB_001, NULL, 0);
+ TEST_CHECK(cf != NULL);
+ if (!cf) {
+ exit(EXIT_FAILURE);
+ }
+
+ /* Total number of sections */
+ if(!TEST_CHECK(mk_list_size(&cf->sections) == 3)) {
+ TEST_MSG("Section number error. Got=%d expect=3", mk_list_size(&cf->sections));
+ }
+
+ s = flb_cf_section_get_by_name(cf, "customs");
+ TEST_CHECK(s != NULL);
+ if (!TEST_CHECK(s->type == FLB_CF_CUSTOM)) {
+ TEST_MSG("Section type error. Got=%d expect=%d", s->type, FLB_CF_CUSTOM);
+ }
+
+ flb_cf_dump(cf);
+ flb_cf_destroy(cf);
+}
+
+static void test_slist_even()
+{
+ struct flb_cf *cf;
+ struct flb_cf_section *s;
+ struct cfl_variant *v;
+ struct mk_list *head;
+
+ cf = flb_cf_yaml_create(NULL, FLB_TESTS_CONF_PATH "/pipelines/slist/even.yaml", NULL, 0);
+ TEST_CHECK(cf != NULL);
+ if (!cf) {
+ exit(EXIT_FAILURE);
+ }
+
+ /* Total number of inputs */
+ if(!TEST_CHECK(mk_list_size(&cf->inputs) == 1)) {
+ TEST_MSG("Section number error. Got=%d expect=1", mk_list_size(&cf->inputs));
+ }
+
+ mk_list_foreach(head, &cf->inputs) {
+ s = mk_list_entry(head, struct flb_cf_section, _head_section);
+ TEST_CHECK(s != NULL);
+
+ v = flb_cf_section_property_get(cf, s, "success_header");
+ TEST_CHECK(v->type == CFL_VARIANT_ARRAY);
+ if (!TEST_CHECK(v->data.as_array->entry_count == 2)) {
+ TEST_MSG("Section number error. Got=%lud expect=2", v->data.as_array->entry_count);
+ }
+ }
+
+ flb_cf_dump(cf);
+ flb_cf_destroy(cf);
+}
+
+static void test_slist_odd()
+{
+ struct flb_cf *cf;
+ struct flb_cf_section *s;
+ struct cfl_variant *v;
+ struct mk_list *head;
+
+ cf = flb_cf_yaml_create(NULL, FLB_TESTS_CONF_PATH "/pipelines/slist/odd.yaml", NULL, 0);
+ TEST_CHECK(cf != NULL);
+ if (!cf) {
+ exit(EXIT_FAILURE);
+ }
+
+ /* Total number of inputs */
+ if(!TEST_CHECK(mk_list_size(&cf->inputs) == 1)) {
+ TEST_MSG("Section number error. Got=%d expect=1", mk_list_size(&cf->inputs));
+ }
+
+ mk_list_foreach(head, &cf->inputs) {
+ s = mk_list_entry(head, struct flb_cf_section, _head_section);
+ TEST_CHECK(s != NULL);
+
+ v = flb_cf_section_property_get(cf, s, "success_header");
+ TEST_CHECK(v->type == CFL_VARIANT_ARRAY);
+ if (!TEST_CHECK(v->data.as_array->entry_count == 3)) {
+ TEST_MSG("Section number error. Got=%lud expect=3", v->data.as_array->entry_count);
+ }
+ }
+
+ flb_cf_dump(cf);
+ flb_cf_destroy(cf);
+}
+
+
+static void test_parser_conf()
+{
+ struct flb_cf *cf;
+ struct flb_config *config;
+ int ret;
+ int cnt;
+
+ cf = flb_cf_yaml_create(NULL, FLB_TESTS_CONF_PATH "/parsers/parsers-conf.yaml", NULL, 0);
+ TEST_CHECK(cf != NULL);
+ if (!cf) {
+ exit(EXIT_FAILURE);
+ }
+
+ config = flb_config_init();
+ TEST_CHECK(config != NULL);
+ config->conf_path = flb_strdup(FLB_TESTS_CONF_PATH "/parsers/");
+
+ // count the parsers registered automatically by fluent-bit
+ cnt = mk_list_size(&config->parsers);
+ // load the parsers from the configuration
+ ret = flb_config_load_config_format(config, cf);
+ if (ret != 0) {
+ exit(EXIT_FAILURE);;
+ }
+
+ /* Total number of inputs */
+ if(!TEST_CHECK(mk_list_size(&config->parsers) == cnt+1)) {
+ TEST_MSG("Section number error. Got=%d expect=%d",
+ mk_list_size(&config->parsers),
+ cnt+1);
+ }
+
+ flb_cf_dump(cf);
+ flb_cf_destroy(cf);
+ flb_config_exit(config);
+}
+
+static inline int check_camel_to_snake(char *input, char *output)
+{
+ int len;
+ int ret = -1;
+ flb_sds_t out;
+ struct flb_cf *cf;
+
+ cf = flb_cf_create();
+ flb_cf_set_origin_format(cf, FLB_CF_YAML);
+
+ len = strlen(input);
+ out = flb_cf_key_translate(cf, input, len);
+
+ ret = strcmp(out, output);
+ flb_sds_destroy(out);
+
+ flb_cf_destroy(cf);
+
+ if (ret == 0) {
+ return FLB_TRUE;
+ }
+
+ return FLB_FALSE;
+}
+
+
+static void test_camel_case_key()
+{
+ /* normal conversion */
+ TEST_CHECK(check_camel_to_snake("a", "a") == FLB_TRUE);
+ TEST_CHECK(check_camel_to_snake("aB", "a_b") == FLB_TRUE);
+ TEST_CHECK(check_camel_to_snake("aBc", "a_bc") == FLB_TRUE);
+ TEST_CHECK(check_camel_to_snake("aBcA", "a_bc_a") == FLB_TRUE);
+ TEST_CHECK(check_camel_to_snake("aBCD", "a_b_c_d") == FLB_TRUE);
+ TEST_CHECK(check_camel_to_snake("intervalSec", "interval_sec") == FLB_TRUE);
+
+ /* unsupported conversion, we force lowercase in Yaml */
+ TEST_CHECK(check_camel_to_snake("AA", "AA") == FLB_TRUE);
+ TEST_CHECK(check_camel_to_snake("Interval_Sec", "Interval_Sec") == FLB_TRUE);
+
+}
+
+TEST_LIST = {
+ { "basic" , test_basic},
+ { "customs section", test_customs_section},
+ { "slist odd", test_slist_odd},
+ { "slist even", test_slist_even},
+ { "parsers file conf", test_parser_conf},
+ { "camel_case_key", test_camel_case_key},
+ { 0 }
+};
diff --git a/fluent-bit/tests/internal/config_map.c b/fluent-bit/tests/internal/config_map.c
new file mode 100644
index 00000000..b709d016
--- /dev/null
+++ b/fluent-bit/tests/internal/config_map.c
@@ -0,0 +1,386 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+#include <fluent-bit/flb_info.h>
+#include <fluent-bit/flb_kv.h>
+#include <fluent-bit/flb_slist.h>
+#include <fluent-bit/flb_mem.h>
+#include <fluent-bit/flb_config.h>
+#include <fluent-bit/flb_config_map.h>
+
+#include "flb_tests_internal.h"
+
+struct context {
+ /* Single values */
+ int num_int;
+ size_t size;
+ time_t time;
+ char boolean;
+ double num_double;
+ flb_sds_t string;
+ struct mk_list *list1;
+ struct mk_list *list2;
+
+ /* Multiple entries */
+ struct mk_list *mult_num_int;
+ struct mk_list *mult_boolean;
+ struct mk_list *mult_num_double;
+ struct mk_list *mult_string;
+ struct mk_list *mult_list1;
+ struct mk_list *mult_list2;
+};
+
+struct flb_config_map config_map[] = {
+ {
+ FLB_CONFIG_MAP_BOOL,
+ "boolean",
+ "true",
+ 0, FLB_TRUE, offsetof(struct context, boolean),
+ NULL
+ },
+ {
+ FLB_CONFIG_MAP_INT,
+ "num_int",
+ "123",
+ 0, FLB_TRUE, offsetof(struct context, num_int),
+ NULL
+ },
+ {
+ FLB_CONFIG_MAP_DOUBLE,
+ "num_double", "0.12345",
+ 0, FLB_TRUE, offsetof(struct context, num_double),
+ NULL
+ },
+ {
+ FLB_CONFIG_MAP_STR,
+ "string",
+ "test",
+ 0, FLB_TRUE, offsetof(struct context, string),
+ NULL
+ },
+
+ /* SIZE */
+ {
+ FLB_CONFIG_MAP_SIZE,
+ "test_size",
+ "2M",
+ 0, FLB_TRUE, offsetof(struct context, size),
+ NULL
+ },
+
+ /* TIME */
+ {
+ FLB_CONFIG_MAP_TIME,
+ "test_time",
+ "2H",
+ 0, FLB_TRUE, offsetof(struct context, time),
+ NULL
+ },
+
+ /* CSLIST */
+ {
+ FLB_CONFIG_MAP_CLIST,
+ "test_clist",
+ "a, b, c ,d,e , f, g,h,i,jk , lm , n o,pqr,, , ,stuv,xyz",
+ 0, FLB_TRUE, offsetof(struct context, list1),
+ NULL
+ },
+
+ /* SLIST */
+ {
+ FLB_CONFIG_MAP_SLIST_4,
+ "test_slist",
+ "a b c de f ghi jk l m n o pqr stuv xyz",
+ 0, FLB_TRUE, offsetof(struct context, list2),
+ NULL
+ },
+
+ /* EOF */
+ {0}
+};
+
+struct flb_config_map config_map_mult[] = {
+ {
+ FLB_CONFIG_MAP_BOOL,
+ "no_mult",
+ "true",
+ 0, FLB_TRUE, 1,
+ NULL
+ },
+ {
+ FLB_CONFIG_MAP_BOOL,
+ "mult_boolean",
+ NULL,
+ FLB_CONFIG_MAP_MULT, FLB_TRUE, offsetof(struct context, mult_boolean),
+ NULL
+ },
+ {
+ FLB_CONFIG_MAP_INT,
+ "mult_num_int",
+ "123",
+ FLB_CONFIG_MAP_MULT, FLB_TRUE, offsetof(struct context, mult_num_int),
+ NULL
+ },
+ {
+ FLB_CONFIG_MAP_DOUBLE,
+ "mult_num_double", "0.12345",
+ FLB_CONFIG_MAP_MULT, FLB_TRUE, offsetof(struct context, mult_num_double),
+ NULL
+ },
+ {
+ FLB_CONFIG_MAP_STR,
+ "mult_string",
+ "test",
+ FLB_CONFIG_MAP_MULT, FLB_TRUE, offsetof(struct context, mult_string),
+ NULL
+ },
+ {
+ FLB_CONFIG_MAP_CLIST,
+ "mult_clist",
+ "a, b, c ,d,e , f, g,h,i,jk , lm , n o,pqr,, , ,stuv,xyz",
+ FLB_CONFIG_MAP_MULT, FLB_TRUE, offsetof(struct context, mult_list1),
+ NULL
+ },
+ {
+ FLB_CONFIG_MAP_SLIST_4,
+ "mult_slist",
+ "a b c de f ghi jk l m n o pqr stuv xyz",
+ FLB_CONFIG_MAP_MULT, FLB_TRUE, offsetof(struct context, mult_list2),
+ NULL
+ },
+
+ /* EOF */
+ {0}
+};
+
+void test_helper()
+{
+ int ret;
+ struct context ctx;
+ struct mk_list *map;
+ struct mk_list prop;
+ struct flb_config *config;
+
+ config = flb_config_init();
+ if (!config) {
+ exit(1);
+ }
+
+ memset(&ctx, '\0', sizeof(struct context));
+
+ /* Create invalid property */
+ flb_kv_init(&prop);
+ flb_kv_item_create(&prop, "bad", "property");
+
+ map = flb_config_map_create(config, config_map);
+ TEST_CHECK(map != NULL);
+
+ ret = flb_config_map_properties_check("test", &prop, map);
+ TEST_CHECK(ret == -1);
+
+ flb_config_map_destroy(map);
+ flb_kv_release(&prop);
+
+ flb_config_exit(config);
+}
+
+void test_create()
+{
+ int ret;
+ struct context ctx;
+ struct mk_list *map;
+ struct mk_list properties;
+ struct flb_slist_entry *e;
+ struct flb_config *config;
+
+ config = flb_config_init();
+ if (!config) {
+ exit(1);
+ }
+
+ memset(&ctx, '\0', sizeof(struct context));
+ mk_list_init(&properties);
+
+ map = flb_config_map_create(config, config_map);
+ TEST_CHECK(map != NULL);
+
+ /* Populate default values only */
+ ret = flb_config_map_set(&properties, map, &ctx);
+ TEST_CHECK(ret == 0);
+
+ TEST_CHECK(ctx.boolean == 1);
+ TEST_CHECK(ctx.num_int == 123);
+ TEST_CHECK(ctx.num_double == 0.12345);
+ TEST_CHECK(ctx.size == 2000000);
+ TEST_CHECK(ctx.time == 7200);
+ TEST_CHECK(strcmp(ctx.string, "test") == 0);
+ TEST_CHECK(flb_sds_len(ctx.string) == 4);
+ TEST_CHECK(mk_list_size(ctx.list1) == 15);
+ TEST_CHECK(mk_list_size(ctx.list2) == 5);
+
+ e = mk_list_entry_last(ctx.list2, struct flb_slist_entry, _head);
+ TEST_CHECK(strcmp(e->str, "f ghi jk l m n o pqr stuv xyz") == 0);
+ flb_config_map_destroy(map);
+
+ flb_config_exit(config);
+}
+
+void test_override_defaults()
+{
+ int ret;
+ struct context ctx;
+ struct mk_list *map;
+ struct mk_list properties;
+ struct flb_slist_entry *e;
+ struct flb_config *config;
+
+ config = flb_config_init();
+ if (!config) {
+ exit(1);
+ }
+
+ memset(&ctx, '\0', sizeof(struct context));
+ mk_list_init(&properties);
+
+ map = flb_config_map_create(config, config_map);
+ TEST_CHECK(map != NULL);
+
+ /* Create a properties list that will override default values */
+ flb_kv_item_create(&properties, "boolean", "false");
+ flb_kv_item_create(&properties, "num_int", "321");
+ flb_kv_item_create(&properties, "num_double", "0.54321");
+ flb_kv_item_create(&properties, "string", "no test");
+ flb_kv_item_create(&properties, "test_time", "1H");
+ flb_kv_item_create(&properties, "test_size", "1M");
+ flb_kv_item_create(&properties, "test_clist", "abc, def, ghi ,,,,jkl ");
+ flb_kv_item_create(&properties, "test_slist", "abc def ghi jkl m n o");
+
+ /* Populate default values only */
+ ret = flb_config_map_set(&properties, map, &ctx);
+ TEST_CHECK(ret == 0);
+
+ TEST_CHECK(ctx.boolean == 0);
+ TEST_CHECK(ctx.num_int == 321);
+ TEST_CHECK(ctx.num_double == 0.54321);
+ TEST_CHECK(ctx.size == 1000000);
+ TEST_CHECK(ctx.time == 3600);
+ TEST_CHECK(strcmp(ctx.string, "no test") == 0);
+ TEST_CHECK(flb_sds_len(ctx.string) == 7);
+ TEST_CHECK(mk_list_size(ctx.list1) == 4);
+ TEST_CHECK(mk_list_size(ctx.list2) == 5);
+
+ e = mk_list_entry_last(ctx.list2, struct flb_slist_entry, _head);
+ TEST_CHECK(strcmp(e->str, "m n o") == 0);
+
+ flb_kv_release(&properties);
+ flb_config_map_destroy(map);
+
+ flb_config_exit(config);
+}
+
+/* Check that single property raise an error if are set multiple times (dups) */
+void test_no_multiple()
+{
+ int ret;
+ struct context ctx;
+ struct mk_list *map;
+ struct mk_list prop;
+ struct flb_config *config;
+
+ config = flb_config_init();
+ if (!config) {
+ exit(1);
+ }
+
+ memset(&ctx, '\0', sizeof(struct context));
+
+ /* Assign the property multiple times */
+ flb_kv_init(&prop);
+ flb_kv_item_create(&prop, "no_mult", "true");
+ flb_kv_item_create(&prop, "no_mult", "false");
+
+ map = flb_config_map_create(config, config_map_mult);
+ TEST_CHECK(map != NULL);
+
+ ret = flb_config_map_properties_check("test", &prop, map);
+ TEST_CHECK(ret == -1);
+
+ flb_config_map_destroy(map);
+ flb_kv_release(&prop);
+
+ flb_config_exit(config);
+}
+
+void test_multiple()
+{
+ int ret;
+ int i;
+ int total;
+ struct context ctx;
+ struct mk_list *map;
+ struct mk_list prop;
+ struct mk_list *head;
+ struct flb_config_map_val *mv;
+ struct flb_config *config;
+
+ config = flb_config_init();
+ if (!config) {
+ exit(1);
+ }
+
+ memset(&ctx, '\0', sizeof(struct context));
+
+ /* Create invalid property */
+ flb_kv_init(&prop);
+ flb_kv_item_create(&prop, "mult_boolean", "true");
+ flb_kv_item_create(&prop, "mult_boolean", "false");
+
+ flb_kv_item_create(&prop, "mult_clist", "a, b, c");
+ flb_kv_item_create(&prop, "mult_clist", "d, e, f");
+ flb_kv_item_create(&prop, "mult_clist", "g, h, i");
+
+ flb_kv_item_create(&prop, "mult_slist", "d e f g");
+
+ map = flb_config_map_create(config, config_map_mult);
+ TEST_CHECK(map != NULL);
+
+ ret = flb_config_map_properties_check("test", &prop, map);
+ TEST_CHECK(ret == 0);
+
+ ret = flb_config_map_set(&prop, map, &ctx);
+ TEST_CHECK(ret == 0);
+
+ i = 0;
+ flb_config_map_foreach(head, mv, ctx.mult_boolean) {
+ if (i == 0) {
+ TEST_CHECK(mv->val.boolean == FLB_TRUE);
+ }
+ else {
+ TEST_CHECK(mv->val.boolean == FLB_FALSE);
+ }
+ i++;
+ }
+
+ total = 0;
+ flb_config_map_foreach(head, mv, ctx.mult_list1) {
+ total++;
+ }
+ TEST_CHECK(total == 4);
+
+ total = 0;
+ flb_config_map_foreach(head, mv, ctx.mult_list2) {
+ total++;
+ }
+
+ flb_config_map_destroy(map);
+ flb_kv_release(&prop);
+ flb_config_exit(config);
+}
+
+TEST_LIST = {
+ { "helper" , test_helper},
+ { "create" , test_create},
+ { "override_defaults", test_override_defaults},
+ { "no_multiple" , test_no_multiple},
+ { "multiple" , test_multiple},
+ { 0 }
+};
diff --git a/fluent-bit/tests/internal/crypto.c b/fluent-bit/tests/internal/crypto.c
new file mode 100644
index 00000000..e8c9bb43
--- /dev/null
+++ b/fluent-bit/tests/internal/crypto.c
@@ -0,0 +1,202 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+#include <fluent-bit/flb_info.h>
+#include <fluent-bit/flb_hash.h>
+#include <fluent-bit/flb_crypto.h>
+
+#include "flb_tests_internal.h"
+
+#define PRIVATE_KEY (unsigned char *) \
+ "-----BEGIN RSA PRIVATE KEY-----\n" \
+ "MIICXgIBAAKBgQDUkO22PyhYHJlXYQomNXEeX7ChfdqyY1ukUMsPQPbmjgsDZ/tG\n" \
+ "rvNtJVtwuOjGi+DGTG+fSFdgoGDbypOXOC5sX2Luwen+Ixvqay1V3pm16cOVOHf8\n" \
+ "XYAMLqTh1Aq3CykQeuTLKQPGG5rL6utFsgzQEgVpSZdLi2W3RfcJGS22EQIDAQAB\n" \
+ "AoGAPMuWsWEu8MR9NviSJotyZvWHVyjfu9WfCEfzS9GQzDAkBj1fKMAw7y6YEI1S\n" \
+ "RjcLequx4SSXmRNFoJc3zzBKVj77vf60vahoaq11My9pMLDSENK/JKW+VpueKYrT\n" \
+ "5Z9C6y9dB7NKXd8YANDApYNc4+a4l01WFNxjBXJveDo6+IECQQD0iT1ne96LIPFO\n" \
+ "j82SGlDkc1w7ZOZKl5kvRwM1VfXmDzdpFhEDndDnFa7Dth3t0QOQVVkbOUnNvQ+Z\n" \
+ "XEVr6l2FAkEA3ogBoIns72p8sRqE2Uavum53M7SxRJTN/Fn5mBN6aNX2if4M2k6r\n" \
+ "Uwhyld6k0PwAB1zVNUlTi0pyR7BcnrAGHQJBAO81NTD+1hLJVeQg/do3Dfx78LRV\n" \
+ "HoXHSF0cHUJWZWX4ap7MrDYachkrd7sRcrOJq+/L3Y+o+c5dbF38ChjnuTUCQQCg\n" \
+ "3ZDPjOVK7Z/WJ2WB7Cd8jf590lGTUj7V/fUAipQi1Qm0F4MTDWusSp8K2DgtGv6q\n" \
+ "U+GM88UBHIAgcs2BqZ3BAkEAjKEOgXNjeYabdkVrQMvJMAJF52vBpSksrV1jqV1F\n" \
+ "AH/sGR3C9faYNzFltPnQcE0USluDQRS/7UHNtn1VEBb34A==\n" \
+ "-----END RSA PRIVATE KEY-----\n"
+
+#define PRIVATE_KEY_LENGTH (strlen((char *) PRIVATE_KEY))
+
+#define PUBLIC_KEY (unsigned char *) \
+ "-----BEGIN PUBLIC KEY-----\n" \
+ "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDUkO22PyhYHJlXYQomNXEeX7Ch\n" \
+ "fdqyY1ukUMsPQPbmjgsDZ/tGrvNtJVtwuOjGi+DGTG+fSFdgoGDbypOXOC5sX2Lu\n" \
+ "wen+Ixvqay1V3pm16cOVOHf8XYAMLqTh1Aq3CykQeuTLKQPGG5rL6utFsgzQEgVp\n" \
+ "SZdLi2W3RfcJGS22EQIDAQAB\n" \
+ "-----END PUBLIC KEY-----\n"
+
+#define PUBLIC_KEY_LENGTH (strlen((char *) PUBLIC_KEY))
+
+#define INPUT_DATA (unsigned char *) "This was encrypted by fluent-bits RSA wrapper!"
+
+#define INPUT_DATA_LENGTH (strlen((char *) INPUT_DATA))
+
+#define SIGNATURE_INPUT INPUT_DATA
+#define SIGNATURE_INPUT_LENGTH INPUT_DATA_LENGTH
+
+#define SIGNATURE_DIGEST_TYPE FLB_HASH_SHA256
+#define SIGNATURE_PADDING_TYPE FLB_CRYPTO_PADDING_PKCS1
+#define SIGNATURE_OUTPUT ((unsigned char []) { \
+ 0x73, 0x03, 0x78, 0x0b, 0x61, 0x2a, 0x3b, 0x94, 0x5e, 0x26, 0x77, 0x65, \
+ 0x96, 0x74, 0x96, 0xbb, 0x6b, 0x1b, 0xdf, 0x8b, 0xfa, 0xa2, 0x57, 0x77, \
+ 0xc3, 0x39, 0xaf, 0x21, 0x56, 0x43, 0x87, 0xfe, 0xfb, 0x90, 0xa1, 0x19, \
+ 0xf4, 0xc3, 0xe1, 0x74, 0xdb, 0x6b, 0x6a, 0x89, 0xeb, 0x01, 0x4e, 0xbc, \
+ 0xf7, 0xe6, 0x0b, 0x7c, 0x1b, 0xa1, 0x6a, 0x7b, 0x55, 0x24, 0x1f, 0xbc, \
+ 0x78, 0x20, 0x11, 0x76, 0xf5, 0x02, 0x16, 0x29, 0xb4, 0x17, 0xff, 0x29, \
+ 0x43, 0x89, 0x4a, 0x7d, 0x23, 0x0d, 0x63, 0x59, 0x76, 0x75, 0xd2, 0x9d, \
+ 0x2c, 0x9f, 0x56, 0x6d, 0x27, 0x53, 0xeb, 0xa3, 0xa7, 0x90, 0x56, 0x4d, \
+ 0x88, 0xe5, 0x4e, 0x55, 0xca, 0x36, 0x58, 0x6f, 0x16, 0x1a, 0xb9, 0x1c, \
+ 0x4a, 0x8b, 0x0c, 0x30, 0x41, 0x19, 0x93, 0x23, 0x47, 0xcc, 0x41, 0x6c, \
+ 0x9a, 0x15, 0x9e, 0xec, 0x22, 0xac, 0x4a, 0xb9 \
+ })
+
+#define SIGNATURE_OUTPUT_LENGTH (sizeof(ENCRYPTED_DATA))
+
+#define ENCRYPTION_PADDING_TYPE FLB_CRYPTO_PADDING_PKCS1
+#define ENCRYPTION_INPUT INPUT_DATA
+#define ENCRYPTED_DATA ((unsigned char []) { \
+ 0x0b, 0xd3, 0x91, 0x15, 0xb4, 0xed, 0xcd, 0x6c, 0xf1, 0x5c, 0x88, 0x15, \
+ 0xde, 0x9d, 0x02, 0x7a, 0x0c, 0x13, 0x93, 0xdc, 0x98, 0x7c, 0x7c, 0xa0, \
+ 0x70, 0x80, 0xff, 0x88, 0x68, 0xb6, 0x17, 0x10, 0xfd, 0x02, 0xaa, 0x96, \
+ 0x54, 0x75, 0x83, 0x51, 0x9e, 0xe3, 0x72, 0x58, 0xd3, 0x01, 0x79, 0x61, \
+ 0xfa, 0x17, 0x18, 0x48, 0xa8, 0xcb, 0xe9, 0x54, 0x5d, 0x87, 0x83, 0x86, \
+ 0x5f, 0x1b, 0xf2, 0x01, 0x7f, 0x98, 0x93, 0xa1, 0x6e, 0x2d, 0x23, 0x2c, \
+ 0x8b, 0xc9, 0x36, 0xad, 0xfc, 0xdb, 0x9d, 0xa0, 0xd2, 0x17, 0xa1, 0x9d, \
+ 0x2e, 0x25, 0xee, 0x54, 0xf5, 0xe3, 0xa6, 0xdb, 0x98, 0x7a, 0x09, 0xef, \
+ 0x43, 0xcc, 0x7e, 0x44, 0x53, 0x7e, 0x4a, 0x8b, 0x14, 0x9b, 0x42, 0x67, \
+ 0xa2, 0x9a, 0x51, 0xdb, 0xf4, 0xfc, 0x93, 0xe7, 0xe1, 0xda, 0x28, 0xad, \
+ 0x97, 0x7e, 0xd4, 0xc0, 0xe2, 0x4e, 0xfa, 0xeb \
+ })
+
+#define ENCRYPTED_DATA_LENGTH (sizeof(ENCRYPTED_DATA))
+
+
+/* This test encrypts and decrypts a buffer with a pre-generated 1024 bit RSA
+ * key and then ensures the results match.
+ */
+
+static void test_rsa_simple_encrypt()
+{
+ unsigned char decrypted_data_buffer[1024 * 10];
+ size_t decrypted_data_buffer_size;
+ unsigned char encrypted_data_buffer[1024 * 10];
+ size_t encrypted_data_buffer_size;
+ int result;
+
+ encrypted_data_buffer_size = sizeof(encrypted_data_buffer);
+ decrypted_data_buffer_size = sizeof(decrypted_data_buffer);
+
+ result = flb_crypto_encrypt_simple(ENCRYPTION_PADDING_TYPE,
+ PUBLIC_KEY, PUBLIC_KEY_LENGTH,
+ INPUT_DATA, INPUT_DATA_LENGTH,
+ encrypted_data_buffer,
+ &encrypted_data_buffer_size);
+
+ TEST_CHECK(result == FLB_CRYPTO_SUCCESS);
+
+ if (result == FLB_CRYPTO_SUCCESS) {
+ result = flb_crypto_decrypt_simple(ENCRYPTION_PADDING_TYPE,
+ PRIVATE_KEY,
+ PRIVATE_KEY_LENGTH,
+ encrypted_data_buffer,
+ encrypted_data_buffer_size,
+ decrypted_data_buffer,
+ &decrypted_data_buffer_size);
+
+ TEST_CHECK(result == FLB_CRYPTO_SUCCESS);
+ TEST_CHECK(decrypted_data_buffer_size == INPUT_DATA_LENGTH);
+
+ if (result == FLB_CRYPTO_SUCCESS) {
+ TEST_CHECK(memcmp(decrypted_data_buffer, INPUT_DATA, INPUT_DATA_LENGTH) == 0);
+ }
+ }
+}
+
+/* This test decrypts a buffer that was encrpyted using openssls rsa utility
+ * with a pre-generated 1024 bit RSA key and then ensures the results match.
+ */
+
+static void test_rsa_simple_decrypt()
+{
+ unsigned char decrypted_data_buffer[1024 * 10];
+ size_t decrypted_data_buffer_size;
+ int result;
+
+ decrypted_data_buffer_size = sizeof(decrypted_data_buffer);
+
+ result = flb_crypto_decrypt_simple(ENCRYPTION_PADDING_TYPE,
+ PRIVATE_KEY,
+ PRIVATE_KEY_LENGTH,
+ ENCRYPTED_DATA,
+ ENCRYPTED_DATA_LENGTH,
+ decrypted_data_buffer,
+ &decrypted_data_buffer_size);
+
+ TEST_CHECK(result == FLB_CRYPTO_SUCCESS);
+ TEST_CHECK(decrypted_data_buffer_size == INPUT_DATA_LENGTH);
+
+ if (result == FLB_CRYPTO_SUCCESS) {
+ TEST_CHECK(memcmp(decrypted_data_buffer, INPUT_DATA, INPUT_DATA_LENGTH) == 0);
+ }
+}
+
+
+/* This test signs a message using a pre-generated 1024 bit RSA key and
+ * compares the signature with one that has been pre-generated using
+ * openssls pkeyutil.
+ */
+
+static void test_rsa_simple_sign()
+{
+ unsigned char signature_buffer[1024];
+ size_t signature_buffer_size;
+ unsigned char digest_buffer[32];
+ int result;
+
+ result = flb_hash_simple(SIGNATURE_DIGEST_TYPE,
+ SIGNATURE_INPUT,
+ SIGNATURE_INPUT_LENGTH,
+ digest_buffer,
+ sizeof(digest_buffer));
+
+ TEST_CHECK(result == FLB_CRYPTO_SUCCESS);
+
+ if (result == FLB_CRYPTO_SUCCESS) {
+ signature_buffer_size = sizeof(signature_buffer);
+
+ result = flb_crypto_sign_simple(FLB_CRYPTO_PRIVATE_KEY,
+ SIGNATURE_PADDING_TYPE,
+ SIGNATURE_DIGEST_TYPE,
+ PRIVATE_KEY,
+ PRIVATE_KEY_LENGTH,
+ digest_buffer,
+ sizeof(digest_buffer),
+ signature_buffer,
+ &signature_buffer_size);
+
+ TEST_CHECK(result == FLB_CRYPTO_SUCCESS);
+ TEST_CHECK(signature_buffer_size == SIGNATURE_OUTPUT_LENGTH);
+
+ if (result == FLB_CRYPTO_SUCCESS) {
+ TEST_CHECK(memcmp(signature_buffer,
+ SIGNATURE_OUTPUT,
+ SIGNATURE_OUTPUT_LENGTH) == 0);
+ }
+ }
+}
+
+TEST_LIST = {
+ { "test_rsa_simple_encrypt", test_rsa_simple_encrypt },
+ { "test_rsa_simple_decrypt", test_rsa_simple_decrypt },
+ { "test_rsa_simple_sign", test_rsa_simple_sign },
+
+ { 0 }
+};
diff --git a/fluent-bit/tests/internal/csv.c b/fluent-bit/tests/internal/csv.c
new file mode 100644
index 00000000..ad4503eb
--- /dev/null
+++ b/fluent-bit/tests/internal/csv.c
@@ -0,0 +1,167 @@
+#include <fluent-bit/flb_csv.h>
+#include <fluent-bit/flb_sds.h>
+#include <string.h>
+
+#include "flb_tests_internal.h"
+
+#define MIN(a,b) (((a)<(b))?(a):(b))
+
+#define row_count 3
+#define col_count 4
+
+char data[] =
+ "r0c1,,,\r"
+ ",r1c2,\"\",\"r1three\nlines\nfield\"\r\n"
+ "r2c1,r2c2,\"\"\"r2c\n3\n\",\"r2three\nlines\nfield\"\"\"\n";
+
+char *parse_expected[row_count][col_count] = {
+ { "r0c1", "", "", "" },
+ { "", "r1c2", "", "r1three\nlines\nfield" },
+ { "r2c1", "r2c2", "\"r2c\n3\n", "r2three\nlines\nfield\"" },
+};
+
+char *split_expected[row_count] = {
+ "r0c1,,,\r",
+ ",r1c2,\"\",\"r1three\nlines\nfield\"\r\n",
+ "r2c1,r2c2,\"\"\"r2c\n3\n\",\"r2three\nlines\nfield\"\"\"\n",
+};
+
+struct parse_state {
+ char **parsed;
+ size_t current_row;
+ size_t current_col;
+};
+
+static void field_parsed(void *data, const char *field, size_t field_len)
+{
+ struct parse_state *state = data;
+ size_t col = state->current_col;
+ size_t row = state->current_row;
+ char *fdup = strndup(field, field_len);
+ state->parsed[row * col_count + col] = fdup;
+ state->current_col++;
+}
+
+static char **parse_data_step(int step)
+{
+ int i;
+ size_t len = sizeof(data);
+ struct parse_state state;
+ char *bufptr;
+ struct flb_csv_state csv_state;
+ size_t field_count;
+ state.parsed = malloc(sizeof *state.parsed * row_count * col_count);
+ state.current_row = 0;
+ state.current_col = 0;
+ flb_csv_init(&csv_state, field_parsed, &state);
+ bufptr = data;
+ for (i = 0; i < sizeof(data);) {
+ int ret;
+ size_t bl = MIN(len - i, step);
+ ret = flb_csv_parse_record(&csv_state, &bufptr, &bl, &field_count);
+ i = bufptr - data;
+ if (ret == FLB_CSV_SUCCESS) {
+ TEST_CHECK(field_count == 4);
+ state.current_row++;
+ state.current_col = 0;
+ }
+ else if (ret == FLB_CSV_EOF) {
+ continue;
+ }
+ else {
+ abort();
+ };
+ }
+ flb_csv_destroy(&csv_state);
+ return state.parsed;
+}
+
+static void check_parse_result(char **result)
+{
+ int row, col;
+
+ for (row = 0; row < row_count; row++) {
+ for (col = 0; col < col_count; col++) {
+ char *res = result[row * col_count + col];
+ TEST_CHECK(strcmp(res, parse_expected[row][col]) == 0);
+ TEST_MSG("Mismatch on row %d, col %d", row, col);
+ TEST_MSG("Expected: %s", parse_expected[row][col]);
+ TEST_MSG("Result: %s", result[row * col_count + col]);
+ free(res);
+ }
+ }
+
+ free(result);
+}
+
+static void check_split_result(char **result)
+{
+ int row;
+
+ for (row = 0; row < row_count; row++) {
+ char *res = result[row];
+ TEST_CHECK(strcmp(res, split_expected[row]) == 0);
+ TEST_MSG("Mismatch on row %d", row);
+ TEST_MSG("Expected: %s", split_expected[row]);
+ TEST_MSG("Result: %s", result[row]);
+ free(res);
+ }
+
+ free(result);
+}
+
+static char **split_rows_step(int step)
+{
+ int i;
+ size_t len = sizeof(data);
+ char *bufptr;
+ char *row_start;
+ size_t current_row = 0;
+ char **result = malloc(sizeof *result * row_count);
+ struct flb_csv_state csv_state;
+ flb_csv_init(&csv_state, NULL, NULL);
+ bufptr = data;
+ row_start = data;
+ size_t field_count;
+ for (i = 0; i < len;) {
+ int ret;
+ size_t bl = MIN(len - i, step);
+ ret = flb_csv_parse_record(&csv_state, &bufptr, &bl, &field_count);
+ i = bufptr - data;
+ if (ret == FLB_CSV_SUCCESS) {
+ TEST_CHECK(field_count == 4);
+ result[current_row] = strndup(row_start, bufptr - row_start);
+ row_start = bufptr;
+ current_row++;
+ }
+ else if (ret == FLB_CSV_EOF) {
+ continue;
+ } else {
+ abort();
+ };
+ }
+ flb_csv_destroy(&csv_state);
+ return result;
+}
+
+static void test_basic()
+{
+ int step;
+ for (step = 1; step <= sizeof(data); step++) {
+ check_parse_result(parse_data_step(step));
+ }
+}
+
+static void test_split_lines()
+{
+ int step;
+ for (step = 1; step <= sizeof(data); step++) {
+ check_split_result(split_rows_step(step));
+ }
+}
+
+TEST_LIST = {
+ { "basic" , test_basic},
+ { "split_lines" , test_split_lines},
+ { 0 }
+};
diff --git a/fluent-bit/tests/internal/data/avro/json_single_map_001.json b/fluent-bit/tests/internal/data/avro/json_single_map_001.json
new file mode 100644
index 00000000..6833b392
--- /dev/null
+++ b/fluent-bit/tests/internal/data/avro/json_single_map_001.json
@@ -0,0 +1,5 @@
+{"key001": 123456789,
+ "key002": 0.999887766,
+ "key003": "abcdefghijk",
+ "key004": [{"a": 1, "b": 2}, {"c": 3, "d": 4}]
+}
diff --git a/fluent-bit/tests/internal/data/avro/live-sample.json b/fluent-bit/tests/internal/data/avro/live-sample.json
new file mode 100644
index 00000000..ba6eb8ac
--- /dev/null
+++ b/fluent-bit/tests/internal/data/avro/live-sample.json
@@ -0,0 +1,25 @@
+{
+ "log": "2020-08-21T15:49:48.154291375ZstderrFhdfsExists:invokeMethod((Lorg/apache/hadoop/fs/Path;)Z)error:2020-08-21T15:49:48.154296328ZstderrForg.apache.hadoop.ipc.RemoteException(org.apache.hadoop.security..SecretManager$Invalid):(HDFS_DELEGATION_273964959forweeb)can'tbefoundincache2020-08-21T15:49:48.154300089ZstderrFatorg.apache.hadoop.ipc.Client.call(Client.java:1475)2020-08-21T15:49:48.154303281ZstderrFatorg.apache.hadoop.ipc.Client.call(Client.java:1412)2020-08-21T15:49:48.154307503ZstderrFatorg.apache.hadoop.ipc.ProtobufRpcEngine$Invoker.invoke(ProtobufRpcEngine.java:229)2020-08-21T15:49:48.154311268ZstderrFatcom.sun.proxy.$Proxy9.getFileInfo(UnknownSource)2020-08-21T15:49:48.154314514ZstderrFatorg.apache.hadoop.hdfs.protocolPB.ClientNamenodeProtocolTranslatorPB.getFileInfo(ClientNamenodeProtocolTranslatorPB.java:771)2020-08-21T15:49:48.154317916ZstderrFatsun.reflect.GeneratedMethodAccessor2.invoke(UnknownSource)2020-08-21T15:49:48.154328852ZstderrFatsun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)2020-08-21T15:49:48.154332363ZstderrFatjava.lang.reflect.Method.invoke(Method.java:498)2020-08-21T15:49:48.154335464ZstderrFatorg.apache.hadoop.io.retry.RetryInvocationHandler.invokeMethod(RetryInvocationHandler.java:191)2020-08-21T15:49:48.154338276ZstderrFatorg.apache.hadoop.io.retry.RetryInvocationHandler.invoke(RetryInvocationHandler.java:102)2020-08-21T15:49:48.15434115ZstderrFatcom.sun.proxy.$Proxy10.getFileInfo(UnknownSource)2020-08-21T15:49:48.154344323ZstderrFatorg.apache.hadoop.hdfs.DFSClient.getFileInfo(DFSClient.java:2108)2020-08-21T15:49:48.154347224ZstderrFatorg.apache.hadoop.hdfs.DistributedFileSystem$22.doCall(DistributedFileSystem.java:1305)2020-08-21T15:49:48.154350059ZstderrFatorg.apache.hadoop.hdfs.DistributedFileSystem$22.doCall(DistributedFileSystem.java:1301)2020-08-21T15:49:48.154352819ZstderrFatorg.apache.hadoop.fs.FileSystemLinkResolver.resolve(FileSystemLinkResolver.java:81)2020-08-21T15:49:48.154355939ZstderrFatorg.apache.hadoop.hdfs.DistributedFileSystem.getFileStatus(DistributedFileSystem.java:1301)2020-08-21T15:49:48.154358773ZstderrFatorg.apache.hadoop.fs.FileSystem.exists(FileSystem.java:1424)",
+ "capture": "2020-08-21T15:49:53.16823268ZstderrF20/08/2115:49:53WARNipc.Client:Exceptionencounteredwhileconnectingtotheserver:org.apache.hadoop.ipc.RemoteException(org.apache.hadoop.security..SecretManager$Invalid):(HDFS_DELEGATION_273964959forweeb)can'tbefoundincache",
+ "kubernetes": {
+ "pod_name": "rrrr-bert-completion-tb1-6786c9c8-wj25m",
+ "namespace_name": "k8s-fgg",
+ "pod_id": "50bfc67d-cd3c-410d-9369-8bda8f33b1c7",
+ "labels": {
+ "app": "customized-tb1",
+ "pod-template-hash": "6786c9c8"
+ },
+ "annotations": {
+ "doAs": "weeb",
+ "iddecorator.dkdk.username": "rrrr",
+ "kubernetes.io/limit-ranger": "LimitRangerpluginset:cpu,memoryrequestforcontainertensorboard;cpu,memorylimitforcontainertensorboard;cpu,memoryrequestforinitcontainerfetcher;cpu,memorylimitforinitcontainerfetcher",
+ "kubernetes.io/psp": "katib-nfs-provisioner",
+ "podpreset.admission.kubernetes.io/podpreset-kube-master": "60792473"
+ },
+ "host": "wedddd.dkdk.qqqq.com",
+ "container_name": "tb1",
+ "docker_id": "50bfc67d-cd3c-410d-9369-8bda8f33b1c7",
+ "container_hash": "qqqq.corp.qqqq.com/ai/centos/tf1.15.0-py3.7-horovod@sha256:68b6885f6d1d3fd87ce425a2b2aa687440b9578740d60996912a816ae67be85e",
+ "container_image": "qqqq.corp.qqqq.com/ai/centos/tf1.15.0-py3.7-horovod:1.2"
+ }
+ }
diff --git a/fluent-bit/tests/internal/data/avro/multiline.json b/fluent-bit/tests/internal/data/avro/multiline.json
new file mode 100644
index 00000000..8d0173e8
--- /dev/null
+++ b/fluent-bit/tests/internal/data/avro/multiline.json
@@ -0,0 +1,27 @@
+[
+ {
+ "date": 1597871185.922652,
+ "log": "2020-08-19T21:06:24.337499838Z stderr F hdfsExists: invokeMethod((Lorg/apache/hadoop/fs/Path;)Z) error:\n2020-08-19T21:06:24.33754077Z stderr F org.apache.hadoop.ipc.RemoteException(org.apache.hadoop.security..SecretManager$Invalid): (HDFS_DELEGATION_ 273964959 for weeb) can't be found in cache\n2020-08-19T21:06:24.337544598Z stderr F \tat org.apache.hadoop.ipc.Client.call(Client.java:1475)\n2020-08-19T21:06:24.337547418Z stderr F \tat org.apache.hadoop.ipc.Client.call(Client.java:1412)\n2020-08-19T21:06:24.337550469Z stderr F \tat org.apache.hadoop.ipc.ProtobufRpcEngine$Invoker.invoke(ProtobufRpcEngine.java:229)\n2020-08-19T21:06:24.337553603Z stderr F \tat com.sun.proxy.$Proxy9.getFileInfo(Unknown Source)\n2020-08-19T21:06:24.337556408Z stderr F \tat org.apache.hadoop.hdfs.protocolPB.ClientNamenodeProtocolTranslatorPB.getFileInfo(ClientNamenodeProtocolTranslatorPB.java:771)\n2020-08-19T21:06:24.337559359Z stderr F \tat sun.reflect.GeneratedMethodAccessor2.invoke(Unknown Source)\n2020-08-19T21:06:24.337562715Z stderr F \tat sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)\n2020-08-19T21:06:24.337565488Z stderr F \tat java.lang.reflect.Method.invoke(Method.java:498)\n2020-08-19T21:06:24.337574315Z stderr F \tat org.apache.hadoop.io.retry.RetryInvocationHandler.invokeMethod(RetryInvocationHandler.java:191)\n2020-08-19T21:06:24.337577049Z stderr F \tat org.apache.hadoop.io.retry.RetryInvocationHandler.invoke(RetryInvocationHandler.java:102)\n2020-08-19T21:06:24.33757971Z stderr F \tat com.sun.proxy.$Proxy10.getFileInfo(Unknown Source)\n2020-08-19T21:06:24.337582469Z stderr F \tat org.apache.hadoop.hdfs.DFSClient.getFileInfo(DFSClient.java:2108)\n2020-08-19T21:06:24.337585132Z stderr F \tat org.apache.hadoop.hdfs.DistributedFileSystem$22.doCall(DistributedFileSystem.java:1305)\n2020-08-19T21:06:24.337587799Z stderr F \tat org.apache.hadoop.hdfs.DistributedFileSystem$22.doCall(DistributedFileSystem.java:1301)\n2020-08-19T21:06:24.337590342Z stderr F \tat org.apache.hadoop.fs.FileSystemLinkResolver.resolve(FileSystemLinkResolver.java:81)\n2020-08-19T21:06:24.337592897Z stderr F \tat org.apache.hadoop.hdfs.DistributedFileSystem.getFileStatus(DistributedFileSystem.java:1301)\n2020-08-19T21:06:24.337595578Z stderr F \tat org.apache.hadoop.fs.FileSystem.exists(FileSystem.java:1424)",
+ "kubernetes": {
+ "pod_name": "rrrr-bert-completion-tb1-6786c9c8-wj25m",
+ "namespace_name": "k8s-fgg",
+ "pod_id": "50bfc67d-cd3c-410d-9369-8bda8f33b1c7",
+ "labels": {
+ "app": "customized-tb1",
+ "pod-template-hash": "6786c9c8"
+ },
+ "annotations": {
+ "doAs": "weeb",
+ "iddecorator.dkdk.username": "rrrr",
+ "kubernetes.io/limit-ranger": "LimitRanger",
+ "kubernetes.io/psp": "katib-nfs-provisioner",
+ "podpreset.admission.kubernetes.io/podpreset-kube-master": "60792473"
+ },
+ "host": "wedddd.dkdk.qqqq.com",
+ "container_name": "tb1",
+ "docker_id": "50bfc67d-cd3c-410d-9369-8bda8f33b1c7",
+ "container_hash": "qqqq.corp.qqqq.com/ai/centos/tf1.15.0-py3.7-horovod@sha256:68b6885f6d1d3fd87ce425a2b2aa687440b9578740d60996912a816ae67be85e",
+ "container_image": "qqqq.corp.qqqq.com/ai/centos/tf1.15.0-py3.7-horovod:1.2"
+ }
+ }
+]
diff --git a/fluent-bit/tests/internal/data/aws_credentials/credential_process/aws-credential-process b/fluent-bit/tests/internal/data/aws_credentials/credential_process/aws-credential-process
new file mode 100755
index 00000000..45d90650
--- /dev/null
+++ b/fluent-bit/tests/internal/data/aws_credentials/credential_process/aws-credential-process
@@ -0,0 +1,14 @@
+#!/bin/sh
+
+printf '{"Version":1,"AccessKeyId":"%s","SecretAccessKey":"%s"' "${1}" "${_AWS_SECRET_ACCESS_KEY}"
+if [ -n "${_AWS_SESSION_TOKEN}" ]; then
+ printf ',"SessionToken":"%s"' "${_AWS_SESSION_TOKEN}"
+fi
+if [ -n "${_AWS_EXPIRATION}" ]; then
+ printf ',"Expiration":"%s"' "$(/bin/date -d "${_AWS_EXPIRATION}" --utc '+%Y-%m-%dT%H:%M:%SZ')"
+fi
+printf '}'
+
+if [ -n "${_AWS_EXIT_CODE}" ]; then
+ exit "${_AWS_EXIT_CODE}"
+fi
diff --git a/fluent-bit/tests/internal/data/aws_credentials/shared_config.ini b/fluent-bit/tests/internal/data/aws_credentials/shared_config.ini
new file mode 100644
index 00000000..451b23ba
--- /dev/null
+++ b/fluent-bit/tests/internal/data/aws_credentials/shared_config.ini
@@ -0,0 +1,9 @@
+[default]
+region = us-east-1
+credential_process = aws-credential-process default
+output = json
+
+[profile nondefault]
+region = us-east-1
+credential_process = aws-credential-process nondefault
+output = json
diff --git a/fluent-bit/tests/internal/data/aws_credentials/shared_credentials_file.ini b/fluent-bit/tests/internal/data/aws_credentials/shared_credentials_file.ini
new file mode 100644
index 00000000..be9520a4
--- /dev/null
+++ b/fluent-bit/tests/internal/data/aws_credentials/shared_credentials_file.ini
@@ -0,0 +1,28 @@
+[default]
+aws_access_key_id = ASIASDMPIJWXJAXT3O3T
+aws_secret_access_key = EAUBpd/APPT4Nfi4DWY3gnt5TU/4T49laqS5zh8W
+aws_session_token = IQoJb3JpZ2luX2VjEOD//////////wEaCNVzLWVhc3QtMSJHMEUCIKCn7v/EDowMZvJnciSJbxA7rIV4p1K6pOUvcLHM+9EzNgIgeiYbfA47DGSqoEZS3yrRWGN8Fr4Q/bK7ANRgv09Hth8q1gEIWRABGgwxNDQ3MTg3MTE0NzAiDGSqzyXiicOZp63afiqzAUyWOljOn5HaIxRfpQ5pTf+o4roJ2KPlHn+XHEKJZKien4Ydm7zeVi7SbPLKocjmjYJd31PrlbJ43C6AyrhmY57qaD7Zz4N3N0V6mekzvlAeARXsa4deflsbemqkp1WVsBLkO6qUuk+N04+MxIVXAxkW9RSPRTVjxeS2m5Yobygto58WLFE8gacRoNd4lCK4JUmEdiaxJEQQO7leZ3v1XxQr6QBS8P/GmcJYcQTxlA6AFQxIMJKGwfAFOuMB2cEc8cF2Htiqf3LVGMk/6bYKkW7fHUtrnttp28jgWtbbLtFbX/zIdlqwm73Ryp7lI+xkM4XNIT+6ZKa4Xw0/Zw3xLzlk3jic6QWPAcffwR6kOunoTOWJzPskK/RZ4Cd+GyGarxG27Cz6xolAzAsDpdGQwV7kCCUPi6/VHjefwKEk9HjZfejC5WuCS173qFrU9kNb4IrYhnK+wmRzzJfgpWUwerdiJKBz95j1iW9rP1a8p1xLR3EXUMN3LIW0+gP8sFjg5iiqDkaS/tUXWZndM2QdJLcrxwAutFchc0nqJHYTijw=
+
+[nondefault]
+aws_access_key_id = akid
+aws_secret_access_key = skid
+
+[custom]
+aws_access_key_id=custom_access_key_id
+aws_secret_access_key=custom_secret_access_key
+
+#some comment line
+sadfjasdlkfajskldfjasd some garbage = not_actually_valid_but=parser_should_handle_it
+
+[headerwithnokeys]
+
+[nospace]
+aws_access_key_id=akidnospace
+aws_secret_access_key=skidnospace
+aws_session_token=tokennospace
+
+[weirdwhitespace]
+aws_access_key_id= akidweird
+aws_secret_access_key= skidweird
+
+aws_session_token=tokenweird///token==
diff --git a/fluent-bit/tests/internal/data/aws_credentials/shared_credentials_file_nodefault.ini b/fluent-bit/tests/internal/data/aws_credentials/shared_credentials_file_nodefault.ini
new file mode 100644
index 00000000..532d17dd
--- /dev/null
+++ b/fluent-bit/tests/internal/data/aws_credentials/shared_credentials_file_nodefault.ini
@@ -0,0 +1,12 @@
+[nondefault]
+aws_access_key_id = akid
+aws_secret_access_key = skid
+
+[nospace]
+aws_access_key_id=akidnospace
+aws_secret_access_key=skidnospace
+aws_session_token=tokennospace
+
+[weirdwhitespace]
+aws_access_key_id= akidweird
+aws_secret_access_key= skidweird
diff --git a/fluent-bit/tests/internal/data/aws_credentials/web_identity_token_file.txt b/fluent-bit/tests/internal/data/aws_credentials/web_identity_token_file.txt
new file mode 100644
index 00000000..cb43252b
--- /dev/null
+++ b/fluent-bit/tests/internal/data/aws_credentials/web_identity_token_file.txt
@@ -0,0 +1 @@
+this-is-a-fake-jwt \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/config_format/classic/fluent-bit.conf b/fluent-bit/tests/internal/data/config_format/classic/fluent-bit.conf
new file mode 100644
index 00000000..9acaa675
--- /dev/null
+++ b/fluent-bit/tests/internal/data/config_format/classic/fluent-bit.conf
@@ -0,0 +1,35 @@
+@SET a=1
+@SET b=2
+@INCLUDE service.conf
+
+[PARSER]
+ name test_api
+
+[MULTILINE_PARSER]
+ name abc
+
+[CUSTOM]
+ name calyptia
+
+[INPUT]
+ name tail
+ path /var/log/containers/*.log
+
+ [GROUP 1]
+ key1 aa
+ key2 bb
+
+ [GROUP 2]
+ key3 cc
+ key4 dd
+
+[FILTER]
+ name stdout
+ match *
+
+[OUTPUT]
+ name null
+ match *
+
+[UNKNOWN]
+ name blah
diff --git a/fluent-bit/tests/internal/data/config_format/classic/indent_level_error.conf b/fluent-bit/tests/internal/data/config_format/classic/indent_level_error.conf
new file mode 100644
index 00000000..6aabb6c8
--- /dev/null
+++ b/fluent-bit/tests/internal/data/config_format/classic/indent_level_error.conf
@@ -0,0 +1,6 @@
+[INPUT]
+ Name dummy
+
+[OUTPUT]
+ Name stdout
+ Match *
diff --git a/fluent-bit/tests/internal/data/config_format/classic/issue6281.conf b/fluent-bit/tests/internal/data/config_format/classic/issue6281.conf
new file mode 100644
index 00000000..372be20d
--- /dev/null
+++ b/fluent-bit/tests/internal/data/config_format/classic/issue6281.conf
@@ -0,0 +1,2 @@
+@INCLUDE issue6281_input.conf
+@INCLUDE issue6281_output.conf \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/config_format/classic/issue6281_input.conf b/fluent-bit/tests/internal/data/config_format/classic/issue6281_input.conf
new file mode 100644
index 00000000..1125e31c
--- /dev/null
+++ b/fluent-bit/tests/internal/data/config_format/classic/issue6281_input.conf
@@ -0,0 +1,2 @@
+[INPUT]
+ Name dummy \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/config_format/classic/issue6281_output.conf b/fluent-bit/tests/internal/data/config_format/classic/issue6281_output.conf
new file mode 100644
index 00000000..6ac53a12
--- /dev/null
+++ b/fluent-bit/tests/internal/data/config_format/classic/issue6281_output.conf
@@ -0,0 +1,2 @@
+[OUTPUT]
+ Name stdout \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/config_format/classic/issue_5880.conf b/fluent-bit/tests/internal/data/config_format/classic/issue_5880.conf
new file mode 100644
index 00000000..57bfa81d
--- /dev/null
+++ b/fluent-bit/tests/internal/data/config_format/classic/issue_5880.conf
@@ -0,0 +1,14 @@
+[INPUT]
+ Name dummy
+ Tag dummy
+
+[FILTER]
+ Name modify
+ Match *
+ ADD foo bar
+ ADD
+
+[OUTPUT]
+ Name stdout
+ Match *
+ Format
diff --git a/fluent-bit/tests/internal/data/config_format/classic/nolimitline.conf b/fluent-bit/tests/internal/data/config_format/classic/nolimitline.conf
new file mode 100644
index 00000000..e7fff842
--- /dev/null
+++ b/fluent-bit/tests/internal/data/config_format/classic/nolimitline.conf
@@ -0,0 +1,11 @@
+[INPUT]
+ name dummy
+
+[FILTER]
+ Name lua
+ Match *
+ code local str = 'abcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcd'; function cb_filter(tag, ts, record) record.str = str; return 1, ts, record end
+ call cb_filter
+
+[OUTPUT]
+ name stdout
diff --git a/fluent-bit/tests/internal/data/config_format/classic/recursion.conf b/fluent-bit/tests/internal/data/config_format/classic/recursion.conf
new file mode 100644
index 00000000..10fb25d8
--- /dev/null
+++ b/fluent-bit/tests/internal/data/config_format/classic/recursion.conf
@@ -0,0 +1 @@
+@INCLUDE rec* \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/config_format/classic/service.conf b/fluent-bit/tests/internal/data/config_format/classic/service.conf
new file mode 100644
index 00000000..15d1730c
--- /dev/null
+++ b/fluent-bit/tests/internal/data/config_format/classic/service.conf
@@ -0,0 +1,4 @@
+[SERVICE]
+ flush 1
+ log_level info
+ http_server on
diff --git a/fluent-bit/tests/internal/data/config_format/yaml/fluent-bit.yaml b/fluent-bit/tests/internal/data/config_format/yaml/fluent-bit.yaml
new file mode 100644
index 00000000..49894552
--- /dev/null
+++ b/fluent-bit/tests/internal/data/config_format/yaml/fluent-bit.yaml
@@ -0,0 +1,29 @@
+env:
+ flush_interval: 1
+
+includes:
+ - service.yaml
+
+customs:
+ - name: ${observability}
+ api_key: zyJUb2tlbklEItoiY2ZlMTcx
+
+pipeline:
+ inputs:
+ - name: tail
+ path: ./test.log
+ parser: json
+ read_from_head: true
+ - name: tail
+ path: ./test.log
+ parser: json
+ read_from_head: true
+
+ filters:
+ - name: record_modifier
+ match: "*"
+ record: powered_by calyptia
+
+ outputs:
+ - name: stdout
+ match: "*"
diff --git a/fluent-bit/tests/internal/data/config_format/yaml/issue_7559.yaml b/fluent-bit/tests/internal/data/config_format/yaml/issue_7559.yaml
new file mode 100644
index 00000000..00927cae
--- /dev/null
+++ b/fluent-bit/tests/internal/data/config_format/yaml/issue_7559.yaml
@@ -0,0 +1,14 @@
+customs:
+ - name: calyptia
+
+pipeline:
+ inputs:
+ - name: fluentbit_metrics
+ scrape_interval: 30
+ scrape_on_start: true
+ tag: _calyptia_cloud
+service:
+ HTTP_Listen: 0.0.0.0
+ HTTP_PORT: 2020
+ HTTP_Server: 'On'
+ Log_Level: debug
diff --git a/fluent-bit/tests/internal/data/config_format/yaml/parsers/parsers-conf.yaml b/fluent-bit/tests/internal/data/config_format/yaml/parsers/parsers-conf.yaml
new file mode 100644
index 00000000..6421a8a1
--- /dev/null
+++ b/fluent-bit/tests/internal/data/config_format/yaml/parsers/parsers-conf.yaml
@@ -0,0 +1,3 @@
+---
+service:
+ parsers_file: parsers.conf
diff --git a/fluent-bit/tests/internal/data/config_format/yaml/parsers/parsers.conf b/fluent-bit/tests/internal/data/config_format/yaml/parsers/parsers.conf
new file mode 100644
index 00000000..9f3b6b33
--- /dev/null
+++ b/fluent-bit/tests/internal/data/config_format/yaml/parsers/parsers.conf
@@ -0,0 +1,6 @@
+[PARSER]
+ Name docker
+ Format json
+ Time_Key time
+ Time_Format %Y-%m-%dT%H:%M:%S.%L
+ Time_Keep On
diff --git a/fluent-bit/tests/internal/data/config_format/yaml/pipelines/slist/even.yaml b/fluent-bit/tests/internal/data/config_format/yaml/pipelines/slist/even.yaml
new file mode 100644
index 00000000..5d5b7c46
--- /dev/null
+++ b/fluent-bit/tests/internal/data/config_format/yaml/pipelines/slist/even.yaml
@@ -0,0 +1,7 @@
+---
+pipeline:
+ inputs:
+ - name: http
+ success_header:
+ - foo bar
+ - bar foo
diff --git a/fluent-bit/tests/internal/data/config_format/yaml/pipelines/slist/odd.yaml b/fluent-bit/tests/internal/data/config_format/yaml/pipelines/slist/odd.yaml
new file mode 100644
index 00000000..a7d2058c
--- /dev/null
+++ b/fluent-bit/tests/internal/data/config_format/yaml/pipelines/slist/odd.yaml
@@ -0,0 +1,8 @@
+---
+pipeline:
+ inputs:
+ - name: http
+ success_header:
+ - foo bar
+ - bar foo
+ - foobar barfoo
diff --git a/fluent-bit/tests/internal/data/config_format/yaml/service.yaml b/fluent-bit/tests/internal/data/config_format/yaml/service.yaml
new file mode 100644
index 00000000..712d455b
--- /dev/null
+++ b/fluent-bit/tests/internal/data/config_format/yaml/service.yaml
@@ -0,0 +1,5 @@
+env:
+ observability: calyptia
+
+includes:
+ - test/nested.yaml
diff --git a/fluent-bit/tests/internal/data/config_format/yaml/test.yaml b/fluent-bit/tests/internal/data/config_format/yaml/test.yaml
new file mode 100644
index 00000000..26a3f9f7
--- /dev/null
+++ b/fluent-bit/tests/internal/data/config_format/yaml/test.yaml
@@ -0,0 +1,31 @@
+env:
+ flush_interval: 1
+ my_api_key: abcdefghijk
+
+#includes:
+ #- dummy_pipeline.yaml
+ #- opensearch_pipeline.yaml
+
+service:
+ flush_interval: ${flush_interval}
+ log_level: info
+
+#customs:
+# calyptia:
+# api_key: ${my_api_key}
+
+pipeline:
+ inputs:
+ tail:
+ path: ./test.log
+ parser: json
+ read_from_head: true
+
+ filters:
+ record_modifier:
+ match: "*"
+ record: powered_by calyptia
+
+ outputs:
+ stdout:
+ match: "*"
diff --git a/fluent-bit/tests/internal/data/config_format/yaml/test/dummy_pipeline.yaml b/fluent-bit/tests/internal/data/config_format/yaml/test/dummy_pipeline.yaml
new file mode 100644
index 00000000..770ab99a
--- /dev/null
+++ b/fluent-bit/tests/internal/data/config_format/yaml/test/dummy_pipeline.yaml
@@ -0,0 +1,13 @@
+pipeline:
+ inputs:
+ - name: dummy
+ tag: success
+ group1:
+ a: b
+ c: d
+ outputs:
+ - name: stdout
+ match: "*"
+
+unknown:
+ a: b
diff --git a/fluent-bit/tests/internal/data/config_format/yaml/test/nested.yaml b/fluent-bit/tests/internal/data/config_format/yaml/test/nested.yaml
new file mode 100644
index 00000000..f4707649
--- /dev/null
+++ b/fluent-bit/tests/internal/data/config_format/yaml/test/nested.yaml
@@ -0,0 +1,7 @@
+service:
+ flush: ${flush_interval}
+ log_level: debug
+ http_server: on
+
+includes:
+ - dummy_pipeline.yaml
diff --git a/fluent-bit/tests/internal/data/file/empty_file.txt b/fluent-bit/tests/internal/data/file/empty_file.txt
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/fluent-bit/tests/internal/data/file/empty_file.txt
diff --git a/fluent-bit/tests/internal/data/file/text_file.txt b/fluent-bit/tests/internal/data/file/text_file.txt
new file mode 100644
index 00000000..8c8e5543
--- /dev/null
+++ b/fluent-bit/tests/internal/data/file/text_file.txt
@@ -0,0 +1,5 @@
+Some text file
+
+line 3
+
+line 5
diff --git a/fluent-bit/tests/internal/data/input_chunk/log/a_thousand_plus_one_bytes.log b/fluent-bit/tests/internal/data/input_chunk/log/a_thousand_plus_one_bytes.log
new file mode 100644
index 00000000..ce6ec220
--- /dev/null
+++ b/fluent-bit/tests/internal/data/input_chunk/log/a_thousand_plus_one_bytes.log
@@ -0,0 +1,2 @@
+{"log":"zmHw8IQgSHIR9f4QkIYYZGVBoFhozS6r0EWwuJhTslZeDvYte8r4HwfwYt0clr3gENB9Tpc0vSdbxn7D3xmUFjXlBFgI01cKTcJh4XUUMkNPpcxidVQpSkvxzLiJXHbOiVc0C6AmiLbTP4XSgUiMWS0p42ZkH373JVYIeqa850MVCJUR0Zkx3Uhwq01zOR4eXTKa8UU5t31RV9ghuT2BnR3Aipt0vZk2eBydBLtDonnSHjVFYMwyq2uPmpIGi9V2MbczoTL7sPiFtiRkPHauosLkMtaN1jQCqeQnJ9db8LuTXSRbtqdFh0p7pdpUG29vFgSsbYhS3Yto1gLiqBZSnuLl9mgMYICRQ42OhGvhQE7SByAp7VMpUaA42H3xWH7daNVgHkHFr16LeMQNcGgZ3pZGpfxMd8Ugs5xNIDzrrArtAqaKxAc3BMGLP4d35ipGGIhDQymWhnkJXauM6fSljSmnuqj6kEavdRQ2HJ9WaH108OfKcaIYV5CgjZWqW2ZGSz4NQ9tutKURAntQ3GjvEdaVa3ShCE1mNEWTArch7NN6K3LC8F5mbKiIP79QZ12VyvVcEr98XQAgORykZh4EMBSNwKxEiHDL6BmcFiBkS4DLUBKmlVebRSrAvwoY8cgL17YQXcZHsMOUDLBw4jPxzy5b2A9IFIj10f6QH0D4H2TsSi7wl70EmxjT6UfYhY0SZvzjCl8209QPHtON5KCknB3CJlpGhr9SCvhC7hbguS1IowuvFeTcMgiuy2MGJtl4wKS2gZxFe9C6kLo0krCLF14xhG1b9kBwGD1sFOuDUJ3hxGRpHLPbHQZ99QltqS4LhUuzbzivd5JjxAVGu1gSqAzd5EYU8XaQjPX4FxUUJjbj4CW06lFjAX0sFhfQVkdJ04kMKDLGjMcYaPJwyFBXrQZWR49jD83Xgtp3PU3nR7qlrIYi0OCA1IGgtZrDTjeJXoO9ZK8NcZJ1F3h9hZqN5ba"}
+
diff --git a/fluent-bit/tests/internal/data/input_chunk/log/test_buffer_drop_chunks.h b/fluent-bit/tests/internal/data/input_chunk/log/test_buffer_drop_chunks.h
new file mode 100644
index 00000000..f1c89786
--- /dev/null
+++ b/fluent-bit/tests/internal/data/input_chunk/log/test_buffer_drop_chunks.h
@@ -0,0 +1,27 @@
+#define TEST_BUFFER_DROP_CHUNKS "[" \
+ "1598279645," \
+ "{" \
+ "\"key_0\": false," \
+ "\"key_1\": false," \
+ "\"key_2\": false," \
+ "\"key_3\": false," \
+ "\"key_4\": false," \
+ "\"key_5\": false," \
+ "\"key_6\": false," \
+ "\"key_7\": false," \
+ "\"key_8\": false," \
+ "\"key_9\": false," \
+ "\"key_10\": false," \
+ "\"key_11\": false," \
+ "\"key_12\": false," \
+ "\"key_13\": false," \
+ "\"key_14\": false," \
+ "\"key_15\": false," \
+ "\"key_16\": false," \
+ "\"key_17\": false," \
+ "\"key_18\": false," \
+ "\"key_19\": false," \
+ "\"key_20\": false," \
+ "\"END_KEY\": \"JSON_END\"" \
+ "}]"
+
diff --git a/fluent-bit/tests/internal/data/input_chunk/log/test_buffer_valid.log b/fluent-bit/tests/internal/data/input_chunk/log/test_buffer_valid.log
new file mode 100644
index 00000000..a5a05ee4
--- /dev/null
+++ b/fluent-bit/tests/internal/data/input_chunk/log/test_buffer_valid.log
@@ -0,0 +1,3 @@
+{"log":"Single log\n"}
+{"log":"Second log\n"}
+{"log":"Third log\n"}
diff --git a/fluent-bit/tests/internal/data/input_chunk/out/a_thousand_plus_one_bytes.out b/fluent-bit/tests/internal/data/input_chunk/out/a_thousand_plus_one_bytes.out
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/fluent-bit/tests/internal/data/input_chunk/out/a_thousand_plus_one_bytes.out
diff --git a/fluent-bit/tests/internal/data/input_chunk/out/test_buffer_valid.out b/fluent-bit/tests/internal/data/input_chunk/out/test_buffer_valid.out
new file mode 100644
index 00000000..a5a05ee4
--- /dev/null
+++ b/fluent-bit/tests/internal/data/input_chunk/out/test_buffer_valid.out
@@ -0,0 +1,3 @@
+{"log":"Single log\n"}
+{"log":"Second log\n"}
+{"log":"Third log\n"}
diff --git a/fluent-bit/tests/internal/data/input_chunk/parser.conf b/fluent-bit/tests/internal/data/input_chunk/parser.conf
new file mode 100644
index 00000000..28bde475
--- /dev/null
+++ b/fluent-bit/tests/internal/data/input_chunk/parser.conf
@@ -0,0 +1,10 @@
+[PARSER]
+ Name docker
+ Format json
+ Time_Key time
+ Time_Format %Y-%m-%dT%H:%M:%S.%L%z
+
+[PARSER]
+ Name docker_multiline
+ Format regex
+ Regex (?<log>^{"log":"\d{4}-\d{2}-\d{2}.*)
diff --git a/fluent-bit/tests/internal/data/mp/apache_10k.mp b/fluent-bit/tests/internal/data/mp/apache_10k.mp
new file mode 100644
index 00000000..d3e55e74
--- /dev/null
+++ b/fluent-bit/tests/internal/data/mp/apache_10k.mp
Binary files differ
diff --git a/fluent-bit/tests/internal/data/pack/README.md b/fluent-bit/tests/internal/data/pack/README.md
new file mode 100644
index 00000000..49b00c1b
--- /dev/null
+++ b/fluent-bit/tests/internal/data/pack/README.md
@@ -0,0 +1,3 @@
+Data samples on this directory are generated by Python scripts taken as source the .txt files.
+
+Note that utf-8 files with Unicode characters > 0xffff generate an invalid JSON representation 'FOR THE TEST' purposes.
diff --git a/fluent-bit/tests/internal/data/pack/bug342.json b/fluent-bit/tests/internal/data/pack/bug342.json
new file mode 100644
index 00000000..1f9e5544
--- /dev/null
+++ b/fluent-bit/tests/internal/data/pack/bug342.json
@@ -0,0 +1,240 @@
+{"num": 1}
+{"num": 2}
+{"num": 3}
+{"num": 4}
+{"num": 5}
+{"num": 6}
+{"num": 7}
+{"num": 8}
+{"num": 9}
+{"num": 10}
+{"num": 11}
+{"num": 12}
+{"num": 13}
+{"num": 14}
+{"num": 15}
+{"num": 16}
+{"num": 17}
+{"num": 18}
+{"num": 19}
+{"num": 20}
+{"num": 21}
+{"num": 22}
+{"num": 23}
+{"num": 24}
+{"num": 25}
+{"num": 26}
+{"num": 27}
+{"num": 28}
+{"num": 29}
+{"num": 30}
+{"num": 31}
+{"num": 32}
+{"num": 33}
+{"num": 34}
+{"num": 35}
+{"num": 36}
+{"num": 37}
+{"num": 38}
+{"num": 39}
+{"num": 40}
+{"num": 41}
+{"num": 42}
+{"num": 43}
+{"num": 44}
+{"num": 45}
+{"num": 46}
+{"num": 47}
+{"num": 48}
+{"num": 49}
+{"num": 50}
+{"num": 51}
+{"num": 52}
+{"num": 53}
+{"num": 54}
+{"num": 55}
+{"num": 56}
+{"num": 57}
+{"num": 58}
+{"num": 59}
+{"num": 60}
+{"num": 61}
+{"num": 62}
+{"num": 63}
+{"num": 64}
+{"num": 65}
+{"num": 66}
+{"num": 67}
+{"num": 68}
+{"num": 69}
+{"num": 70}
+{"num": 71}
+{"num": 72}
+{"num": 73}
+{"num": 74}
+{"num": 75}
+{"num": 76}
+{"num": 77}
+{"num": 78}
+{"num": 79}
+{"num": 80}
+{"num": 81}
+{"num": 82}
+{"num": 83}
+{"num": 84}
+{"num": 85}
+{"num": 86}
+{"num": 87}
+{"num": 88}
+{"num": 89}
+{"num": 90}
+{"num": 91}
+{"num": 92}
+{"num": 93}
+{"num": 94}
+{"num": 95}
+{"num": 96}
+{"num": 97}
+{"num": 98}
+{"num": 99}
+{"num": 100}
+{"num": 101}
+{"num": 102}
+{"num": 103}
+{"num": 104}
+{"num": 105}
+{"num": 106}
+{"num": 107}
+{"num": 108}
+{"num": 109}
+{"num": 110}
+{"num": 111}
+{"num": 112}
+{"num": 113}
+{"num": 114}
+{"num": 115}
+{"num": 116}
+{"num": 117}
+{"num": 118}
+{"num": 119}
+{"num": 120}
+{"num": 121}
+{"num": 122}
+{"num": 123}
+{"num": 124}
+{"num": 125}
+{"num": 126}
+{"num": 127}
+{"num": 128}
+{"num": 129}
+{"num": 130}
+{"num": 131}
+{"num": 132}
+{"num": 133}
+{"num": 134}
+{"num": 135}
+{"num": 136}
+{"num": 137}
+{"num": 138}
+{"num": 139}
+{"num": 140}
+{"num": 141}
+{"num": 142}
+{"num": 143}
+{"num": 144}
+{"num": 145}
+{"num": 146}
+{"num": 147}
+{"num": 148}
+{"num": 149}
+{"num": 150}
+{"num": 151}
+{"num": 152}
+{"num": 153}
+{"num": 154}
+{"num": 155}
+{"num": 156}
+{"num": 157}
+{"num": 158}
+{"num": 159}
+{"num": 160}
+{"num": 161}
+{"num": 162}
+{"num": 163}
+{"num": 164}
+{"num": 165}
+{"num": 166}
+{"num": 167}
+{"num": 168}
+{"num": 169}
+{"num": 170}
+{"num": 171}
+{"num": 172}
+{"num": 173}
+{"num": 174}
+{"num": 175}
+{"num": 176}
+{"num": 177}
+{"num": 178}
+{"num": 179}
+{"num": 180}
+{"num": 181}
+{"num": 182}
+{"num": 183}
+{"num": 184}
+{"num": 185}
+{"num": 186}
+{"num": 187}
+{"num": 188}
+{"num": 189}
+{"num": 190}
+{"num": 191}
+{"num": 192}
+{"num": 193}
+{"num": 194}
+{"num": 195}
+{"num": 196}
+{"num": 197}
+{"num": 198}
+{"num": 199}
+{"num": 200}
+{"num": 201}
+{"num": 202}
+{"num": 203}
+{"num": 204}
+{"num": 205}
+{"num": 206}
+{"num": 207}
+{"num": 208}
+{"num": 209}
+{"num": 210}
+{"num": 211}
+{"num": 212}
+{"num": 213}
+{"num": 214}
+{"num": 215}
+{"num": 216}
+{"num": 217}
+{"num": 218}
+{"num": 219}
+{"num": 220}
+{"num": 221}
+{"num": 222}
+{"num": 223}
+{"num": 224}
+{"num": 225}
+{"num": 226}
+{"num": 227}
+{"num": 228}
+{"num": 229}
+{"num": 230}
+{"num": 231}
+{"num": 232}
+{"num": 233}
+{"num": 234}
+{"num": 235}
+{"num": 236}
+{"num": 237}
+{"num": 238}
+{"num": 239}
+{"num": 240}
diff --git a/fluent-bit/tests/internal/data/pack/dup_keys_in.json b/fluent-bit/tests/internal/data/pack/dup_keys_in.json
new file mode 100644
index 00000000..bdfb9d7c
--- /dev/null
+++ b/fluent-bit/tests/internal/data/pack/dup_keys_in.json
@@ -0,0 +1 @@
+[1601487301, {"date": 872835240, "map": {"sub1": false, "sub2": "aaa", "sub3": "bbb", "sub1": null, "sub1": true}, "key1": 12345, "key2": 444, "date": 1059113640, "key1": 333}]
diff --git a/fluent-bit/tests/internal/data/pack/dup_keys_out.json b/fluent-bit/tests/internal/data/pack/dup_keys_out.json
new file mode 100644
index 00000000..5bb557eb
--- /dev/null
+++ b/fluent-bit/tests/internal/data/pack/dup_keys_out.json
@@ -0,0 +1 @@
+{"map":{"sub2":"aaa","sub3":"bbb","sub1":true},"key2":444,"date":1059113640,"key1":333}
diff --git a/fluent-bit/tests/internal/data/pack/json_single_map_001.json b/fluent-bit/tests/internal/data/pack/json_single_map_001.json
new file mode 100644
index 00000000..6833b392
--- /dev/null
+++ b/fluent-bit/tests/internal/data/pack/json_single_map_001.json
@@ -0,0 +1,5 @@
+{"key001": 123456789,
+ "key002": 0.999887766,
+ "key003": "abcdefghijk",
+ "key004": [{"a": 1, "b": 2}, {"c": 3, "d": 4}]
+}
diff --git a/fluent-bit/tests/internal/data/pack/json_single_map_002.json b/fluent-bit/tests/internal/data/pack/json_single_map_002.json
new file mode 100644
index 00000000..d5a3709e
--- /dev/null
+++ b/fluent-bit/tests/internal/data/pack/json_single_map_002.json
@@ -0,0 +1,4 @@
+{"AAA": 123456789,
+ "BBB": 0.999887766,
+ "CCC": ["aa", "bb", "cc", [1, 2, 3]]
+}
diff --git a/fluent-bit/tests/internal/data/pack/mixed.py b/fluent-bit/tests/internal/data/pack/mixed.py
new file mode 100644
index 00000000..7ad0a614
--- /dev/null
+++ b/fluent-bit/tests/internal/data/pack/mixed.py
@@ -0,0 +1,31 @@
+# Fluent Bit / Pack mixed samples to JSON
+# =======================================
+# This script generate the JSON formatted strings for the mixed_ABC.txt samples.
+
+import os
+import json
+import msgpack
+
+def gen_json(f):
+ raw = open(f, 'r')
+ data = raw.read()
+ raw.close()
+
+ out_mp = f[:-4] + ".mp"
+ out_json = f[:-4] + ".json"
+
+ # Write messagepack
+ fmp = open(out_mp, 'w')
+ fmp.write(msgpack.packb(data))
+ fmp.close()
+
+ fjson = open(out_json, 'w')
+ fjson.write(json.dumps(data))
+ fjson.close()
+
+for fn in os.listdir('.'):
+ if not os.path.isfile(fn):
+ continue
+
+ if fn.startswith('mixed_') and fn.endswith('.txt'):
+ gen_json(fn)
diff --git a/fluent-bit/tests/internal/data/pack/mixed_001.json b/fluent-bit/tests/internal/data/pack/mixed_001.json
new file mode 100644
index 00000000..10f7faad
--- /dev/null
+++ b/fluent-bit/tests/internal/data/pack/mixed_001.json
@@ -0,0 +1 @@
+"mixed_001 => \u001b[1;34mI\u001b[0m test\n" \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/pack/mixed_001.mp b/fluent-bit/tests/internal/data/pack/mixed_001.mp
new file mode 100644
index 00000000..587dc63f
--- /dev/null
+++ b/fluent-bit/tests/internal/data/pack/mixed_001.mp
@@ -0,0 +1 @@
+¿mixed_001 => I test
diff --git a/fluent-bit/tests/internal/data/pack/mixed_001.txt b/fluent-bit/tests/internal/data/pack/mixed_001.txt
new file mode 100644
index 00000000..d217bff8
--- /dev/null
+++ b/fluent-bit/tests/internal/data/pack/mixed_001.txt
@@ -0,0 +1 @@
+mixed_001 => I test
diff --git a/fluent-bit/tests/internal/data/pack/mixed_002.json b/fluent-bit/tests/internal/data/pack/mixed_002.json
new file mode 100644
index 00000000..e32314b0
--- /dev/null
+++ b/fluent-bit/tests/internal/data/pack/mixed_002.json
@@ -0,0 +1 @@
+"mixed_002 =>\n\n áéíóú\n\n\n'\n\\t\n" \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/pack/mixed_002.mp b/fluent-bit/tests/internal/data/pack/mixed_002.mp
new file mode 100644
index 00000000..1bf97553
--- /dev/null
+++ b/fluent-bit/tests/internal/data/pack/mixed_002.mp
Binary files differ
diff --git a/fluent-bit/tests/internal/data/pack/mixed_002.txt b/fluent-bit/tests/internal/data/pack/mixed_002.txt
new file mode 100644
index 00000000..a85912bb
--- /dev/null
+++ b/fluent-bit/tests/internal/data/pack/mixed_002.txt
@@ -0,0 +1,7 @@
+mixed_002 =>
+
+ áéíóú
+
+
+'
+\t
diff --git a/fluent-bit/tests/internal/data/pack/mixed_003.json b/fluent-bit/tests/internal/data/pack/mixed_003.json
new file mode 100644
index 00000000..167c89b8
--- /dev/null
+++ b/fluent-bit/tests/internal/data/pack/mixed_003.json
@@ -0,0 +1 @@
+"á\n" \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/pack/mixed_003.mp b/fluent-bit/tests/internal/data/pack/mixed_003.mp
new file mode 100644
index 00000000..760f5e18
--- /dev/null
+++ b/fluent-bit/tests/internal/data/pack/mixed_003.mp
@@ -0,0 +1 @@
+£Ã¡
diff --git a/fluent-bit/tests/internal/data/pack/mixed_003.txt b/fluent-bit/tests/internal/data/pack/mixed_003.txt
new file mode 100644
index 00000000..072d5937
--- /dev/null
+++ b/fluent-bit/tests/internal/data/pack/mixed_003.txt
@@ -0,0 +1 @@
+á
diff --git a/fluent-bit/tests/internal/data/pack/utf8_bell.json b/fluent-bit/tests/internal/data/pack/utf8_bell.json
new file mode 100644
index 00000000..ced4da0c
--- /dev/null
+++ b/fluent-bit/tests/internal/data/pack/utf8_bell.json
@@ -0,0 +1 @@
+"🔔" \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/pack/utf8_bell.mp b/fluent-bit/tests/internal/data/pack/utf8_bell.mp
new file mode 100644
index 00000000..ea4ce5c0
--- /dev/null
+++ b/fluent-bit/tests/internal/data/pack/utf8_bell.mp
@@ -0,0 +1 @@
+¤ðŸ”” \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/pack/utf8_bell.txt b/fluent-bit/tests/internal/data/pack/utf8_bell.txt
new file mode 100644
index 00000000..89b9de00
--- /dev/null
+++ b/fluent-bit/tests/internal/data/pack/utf8_bell.txt
@@ -0,0 +1 @@
+🔔 \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/pack/utf8_copyright.json b/fluent-bit/tests/internal/data/pack/utf8_copyright.json
new file mode 100644
index 00000000..4d52a66f
--- /dev/null
+++ b/fluent-bit/tests/internal/data/pack/utf8_copyright.json
@@ -0,0 +1 @@
+"©" \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/pack/utf8_copyright.mp b/fluent-bit/tests/internal/data/pack/utf8_copyright.mp
new file mode 100644
index 00000000..6bd6a103
--- /dev/null
+++ b/fluent-bit/tests/internal/data/pack/utf8_copyright.mp
@@ -0,0 +1 @@
+¢Â© \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/pack/utf8_copyright.txt b/fluent-bit/tests/internal/data/pack/utf8_copyright.txt
new file mode 100644
index 00000000..5f8778c5
--- /dev/null
+++ b/fluent-bit/tests/internal/data/pack/utf8_copyright.txt
@@ -0,0 +1 @@
+© \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/pack/utf8_gen.py b/fluent-bit/tests/internal/data/pack/utf8_gen.py
new file mode 100644
index 00000000..606e8cc2
--- /dev/null
+++ b/fluent-bit/tests/internal/data/pack/utf8_gen.py
@@ -0,0 +1,32 @@
+# Fluent Bit / Pack utf-8 samples to JSON
+# =======================================
+
+import os
+import json
+import msgpack
+
+def gen_json(f):
+
+ print f
+
+ with io.open(f, 'rb') as raw:
+ data = raw.read()
+
+ out_mp = f[:-4] + ".mp"
+ out_json = f[:-4] + ".json"
+
+ # Write messagepack
+ fmp = open(out_mp, 'w')
+ fmp.write(msgpack.packb(data))
+ fmp.close()
+
+ fjson = open(out_json, 'w')
+ fjson.write(json.dumps(data).encode('utf8'))
+ fjson.close()
+
+for fn in os.listdir('.'):
+ if not os.path.isfile(fn):
+ continue
+
+ if fn.startswith('utf8_') and fn.endswith('.txt'):
+ gen_json(fn)
diff --git a/fluent-bit/tests/internal/data/pack/utf8_hokke.json b/fluent-bit/tests/internal/data/pack/utf8_hokke.json
new file mode 100644
index 00000000..d93624bf
--- /dev/null
+++ b/fluent-bit/tests/internal/data/pack/utf8_hokke.json
@@ -0,0 +1 @@
+"𩸽" \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/pack/utf8_hokke.mp b/fluent-bit/tests/internal/data/pack/utf8_hokke.mp
new file mode 100644
index 00000000..704885a8
--- /dev/null
+++ b/fluent-bit/tests/internal/data/pack/utf8_hokke.mp
@@ -0,0 +1 @@
+¤ð©¸½ \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/pack/utf8_hokke.txt b/fluent-bit/tests/internal/data/pack/utf8_hokke.txt
new file mode 100644
index 00000000..f1d17d76
--- /dev/null
+++ b/fluent-bit/tests/internal/data/pack/utf8_hokke.txt
@@ -0,0 +1 @@
+𩸽 \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/pack/utf8_relaxed.json b/fluent-bit/tests/internal/data/pack/utf8_relaxed.json
new file mode 100644
index 00000000..4526bf40
--- /dev/null
+++ b/fluent-bit/tests/internal/data/pack/utf8_relaxed.json
@@ -0,0 +1 @@
+"☺" \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/pack/utf8_relaxed.mp b/fluent-bit/tests/internal/data/pack/utf8_relaxed.mp
new file mode 100644
index 00000000..1fcce51e
--- /dev/null
+++ b/fluent-bit/tests/internal/data/pack/utf8_relaxed.mp
@@ -0,0 +1 @@
+£â˜º \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/pack/utf8_relaxed.txt b/fluent-bit/tests/internal/data/pack/utf8_relaxed.txt
new file mode 100644
index 00000000..e2890a9c
--- /dev/null
+++ b/fluent-bit/tests/internal/data/pack/utf8_relaxed.txt
@@ -0,0 +1 @@
+☺ \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/parser/json.conf b/fluent-bit/tests/internal/data/parser/json.conf
new file mode 100644
index 00000000..198bbf3c
--- /dev/null
+++ b/fluent-bit/tests/internal/data/parser/json.conf
@@ -0,0 +1,200 @@
+# Parser: no_year
+# ===============
+# the given format don't contain the Year, this is a common
+# case on old Syslog implementations.
+#
+[PARSER]
+ Name no_year
+ Format json
+ Time_Key time
+ Time_Format %b %d %H:%M:%S
+ Time_Keep On
+
+# Parser: no_year_N
+# =================
+# Just for compatibility, check a string with no year but including Nanoseconds.
+#
+[PARSER]
+ Name no_year_N
+ Format json
+ Time_Key time
+ Time_Format %b %d %H:%M:%S.%L
+ Time_Keep On
+
+# Parser: no_year_NC
+# =================
+# Just for compatibility, check a string with no year but including Nanoseconds with comma as fractional separator.
+#
+[PARSER]
+ Name no_year_NC
+ Format json
+ Time_Key time
+ Time_Format %b %d %H:%M:%S,%L
+ Time_Keep On
+
+# Parser: no_year_TZ
+# =================
+# Time string with no year and including timezone
+#
+[PARSER]
+ Name no_year_TZ
+ Format json
+ Time_Key time
+ Time_Format %b %d %H:%M:%S %z
+ Time_Keep On
+
+# Parser: no_year_N_TZ
+# ====================
+# Time string with no year, nanoseconds and timezone
+#
+[PARSER]
+ Name no_year_N_TZ
+ Format json
+ Time_Key time
+ Time_Format %b %d %H:%M:%S.%L %z
+ Time_Keep On
+
+
+# Parser: no_year_NC_TZ
+# ====================
+# Time string with no year, nanoseconds and timezone with comma as fractional separator.
+#
+[PARSER]
+ Name no_year_NC_TZ
+ Format json
+ Time_Key time
+ Time_Format %b %d %H:%M:%S,%L %z
+ Time_Keep On
+
+
+# Parser: default_UTC
+# ===================
+# Time string with timezone in UTC
+#
+[PARSER]
+ Name default_UTC
+ Format json
+ Time_Key time
+ Time_Format %m/%d/%Y %H:%M:%S
+ Time_Keep On
+
+# Parser: default_UTC_Z
+# =====================
+# Time string with timezone in UTC and ending Z
+#
+[PARSER]
+ Name default_UTC_Z
+ Format json
+ Time_Key time
+ Time_Format %m/%d/%Y %H:%M:%SZ
+ Time_Keep On
+
+# Parser: default_UTC_N_Z
+# =======================
+# Time string with timezone in UTC, nanoseconds and ending Z
+#
+[PARSER]
+ Name default_UTC_N_Z
+ Format json
+ Time_Key time
+ Time_Format %m/%d/%Y %H:%M:%S.%LZ
+ Time_Keep On
+
+# Parser: default_UTC_NC_Z
+# =======================
+# Time string with timezone in UTC, nanoseconds with comma as fractional separator and ending Z
+#
+[PARSER]
+ Name default_UTC_NC_Z
+ Format json
+ Time_Key time
+ Time_Format %m/%d/%Y %H:%M:%S,%LZ
+ Time_Keep On
+
+# Parser: generic_TZ
+# ==================
+# Generic date with timezone
+#
+[PARSER]
+ Name generic_TZ
+ Format json
+ Time_Key time
+ Time_Format %m/%d/%Y %H:%M:%S %z
+ Time_Keep On
+
+# Parser: generic
+# ===============
+# Generic date
+#
+[PARSER]
+ Name generic
+ Format json
+ Time_Key time
+ Time_Format %m/%d/%Y %H:%M:%S
+ Time_Keep On
+
+# Parser: generic_N
+# ===============
+# Generic date with nanoseconds
+#
+[PARSER]
+ Name generic_N
+ Format json
+ Time_Key time
+ Time_Format %m/%d/%Y %H:%M:%S.%L
+ Time_Keep On
+
+# Parser: generic_NC
+# ===============
+# Generic date with nanoseconds with comma as fractional separator
+#
+[PARSER]
+ Name generic_NC
+ Format json
+ Time_Key time
+ Time_Format %m/%d/%Y %H:%M:%S,%L
+ Time_Keep On
+
+# Parser: generic_N_TZ
+# ====================
+# Generic date with nanoseconds and timezone
+#
+[PARSER]
+ Name generic_N_TZ
+ Format json
+ Time_Key time
+ Time_Format %m/%d/%Y %H:%M:%S.%L %z
+ Time_Keep On
+
+# Parser: generic_NC_TZ
+# ====================
+# Generic date with nanoseconds with comma as fractional separator and timezone
+#
+[PARSER]
+ Name generic_NC_TZ
+ Format json
+ Time_Key time
+ Time_Format %m/%d/%Y %H:%M:%S,%L %z
+ Time_Keep On
+
+# Parser: generic_NL_TZ
+# ====================
+# Generic date with nanoseconds with colon as fractional separator and timezone
+#
+[PARSER]
+ Name generic_NL_TZ
+ Format json
+ Time_Key time
+ Time_Format %m/%d/%Y %H:%M:%S:%L %z
+ Time_Keep On
+
+# Parser: apache_error
+# ====================
+# Apache error log time format
+#
+[PARSER]
+ Name apache_error
+ Format json
+ Time_Key time
+ Time_Format %a %b %d %H:%M:%S.%L %Y
+ Time_Keep On
diff --git a/fluent-bit/tests/internal/data/parser/regex.conf b/fluent-bit/tests/internal/data/parser/regex.conf
new file mode 100644
index 00000000..5b340dc3
--- /dev/null
+++ b/fluent-bit/tests/internal/data/parser/regex.conf
@@ -0,0 +1,234 @@
+# Parser: no_year
+# ===============
+# the given format don't contain the Year, this is a common
+# case on old Syslog implementations.
+#
+[PARSER]
+ Name no_year
+ Format regex
+ Regex ^(?<key001>[^ ]*) (?<key002>[^ ]*) (?<time>.+)$
+ Time_Key time
+ Time_Format %b %d %H:%M:%S
+ Time_Keep On
+
+# Parser: no_year_N
+# =================
+# Just for compatibility, check a string with no year but including Nanoseconds.
+#
+[PARSER]
+ Name no_year_N
+ Format regex
+ Regex ^(?<key001>[^ ]*) (?<key002>[^ ]*) (?<time>.+)$
+ Time_Key time
+ Time_Format %b %d %H:%M:%S.%L
+ Time_Keep On
+
+# Parser: no_year_NC
+# =================
+# Just for compatibility, check a string with no year but including Nanoseconds with comma as fractional separator.
+#
+[PARSER]
+ Name no_year_NC
+ Format regex
+ Regex ^(?<key001>[^ ]*) (?<key002>[^ ]*) (?<time>.+)$
+ Time_Key time
+ Time_Format %b %d %H:%M:%S,%L
+ Time_Keep On
+
+# Parser: no_year_TZ
+# =================
+# Time string with no year and including timezone
+#
+[PARSER]
+ Name no_year_TZ
+ Format regex
+ Regex ^(?<key001>[^ ]*) (?<key002>[^ ]*) (?<time>.+)$
+ Time_Key time
+ Time_Format %b %d %H:%M:%S %z
+ Time_Keep On
+
+# Parser: no_year_N_TZ
+# ====================
+# Time string with no year, nanoseconds and timezone
+#
+[PARSER]
+ Name no_year_N_TZ
+ Format regex
+ Regex ^(?<key001>[^ ]*) (?<key002>[^ ]*) (?<time>.+)$
+ Time_Key time
+ Time_Format %b %d %H:%M:%S.%L %z
+ Time_Keep On
+
+
+# Parser: no_year_NC_TZ
+# ====================
+# Time string with no year, nanoseconds and timezone with comma as fractional separator.
+#
+[PARSER]
+ Name no_year_NC_TZ
+ Format regex
+ Regex ^(?<key001>[^ ]*) (?<key002>[^ ]*) (?<time>.+)$
+ Time_Key time
+ Time_Format %b %d %H:%M:%S,%L %z
+ Time_Keep On
+
+
+# Parser: default_UTC
+# ===================
+# Time string with timezone in UTC
+#
+[PARSER]
+ Name default_UTC
+ Format regex
+ Regex ^(?<key001>[^ ]*) (?<key002>[^ ]*) (?<time>.+)$
+ Time_Key time
+ Time_Format %m/%d/%Y %H:%M:%S
+ Time_Keep On
+
+# Parser: default_UTC_Z
+# =====================
+# Time string with timezone in UTC and ending Z
+#
+[PARSER]
+ Name default_UTC_Z
+ Format regex
+ Regex ^(?<key001>[^ ]*) (?<key002>[^ ]*) (?<time>.+)$
+ Time_Key time
+ Time_Format %m/%d/%Y %H:%M:%SZ
+ Time_Keep On
+
+# Parser: default_UTC_N_Z
+# =======================
+# Time string with timezone in UTC, nanoseconds and ending Z
+#
+[PARSER]
+ Name default_UTC_N_Z
+ Format regex
+ Regex ^(?<key001>[^ ]*) (?<key002>[^ ]*) (?<time>.+)$
+ Time_Key time
+ Time_Format %m/%d/%Y %H:%M:%S.%LZ
+ Time_Keep On
+
+# Parser: default_UTC_NC_Z
+# =======================
+# Time string with timezone in UTC, nanoseconds with comma as fractional separator and ending Z
+#
+[PARSER]
+ Name default_UTC_NC_Z
+ Format regex
+ Regex ^(?<key001>[^ ]*) (?<key002>[^ ]*) (?<time>.+)$
+ Time_Key time
+ Time_Format %m/%d/%Y %H:%M:%S,%LZ
+ Time_Keep On
+
+# Parser: generic_TZ
+# ==================
+# Generic date with timezone
+#
+[PARSER]
+ Name generic_TZ
+ Format regex
+ Regex ^(?<key001>[^ ]*) (?<key002>[^ ]*) (?<time>.+)$
+ Time_Key time
+ Time_Format %m/%d/%Y %H:%M:%S %z
+ Time_Keep On
+
+# Parser: generic
+# ===============
+# Generic date
+#
+[PARSER]
+ Name generic
+ Format regex
+ Regex ^(?<key001>[^ ]*) (?<key002>[^ ]*) (?<time>.+)$
+ Time_Key time
+ Time_Format %m/%d/%Y %H:%M:%S
+ Time_Keep On
+
+# Parser: generic_N
+# ===============
+# Generic date with nanoseconds
+#
+[PARSER]
+ Name generic_N
+ Format regex
+ Regex ^(?<key001>[^ ]*) (?<key002>[^ ]*) (?<time>.+)$
+ Time_Key time
+ Time_Format %m/%d/%Y %H:%M:%S.%L
+ Time_Keep On
+
+# Parser: generic_NC
+# ===============
+# Generic date with nanoseconds with comma as fractional separator
+#
+[PARSER]
+ Name generic_NC
+ Format regex
+ Regex ^(?<key001>[^ ]*) (?<key002>[^ ]*) (?<time>.+)$
+ Time_Key time
+ Time_Format %m/%d/%Y %H:%M:%S,%L
+ Time_Keep On
+
+# Parser: generic_N_TZ
+# ====================
+# Generic date with nanoseconds and timezone
+#
+[PARSER]
+ Name generic_N_TZ
+ Format regex
+ Regex ^(?<key001>[^ ]*) (?<key002>[^ ]*) (?<time>.+)$
+ Time_Key time
+ Time_Format %m/%d/%Y %H:%M:%S.%L %z
+ Time_Keep On
+
+# Parser: generic_NC_TZ
+# ====================
+# Generic date with nanoseconds with comma as fractional separator and timezone
+#
+[PARSER]
+ Name generic_NC_TZ
+ Format regex
+ Regex ^(?<key001>[^ ]*) (?<key002>[^ ]*) (?<time>.+)$
+ Time_Key time
+ Time_Format %m/%d/%Y %H:%M:%S,%L %z
+ Time_Keep On
+
+# Parser: generic_NL_TZ
+# ====================
+# Generic date with nanoseconds with colon as fractional separator and timezone
+#
+[PARSER]
+ Name generic_NL_TZ
+ Format regex
+ Regex ^(?<key001>[^ ]*) (?<key002>[^ ]*) (?<time>.+)$
+ Time_Key time
+ Time_Format %m/%d/%Y %H:%M:%S:%L %z
+ Time_Keep On
+
+
+# Parser: apache_error
+# ====================
+# Apache error log time format
+#
+[PARSER]
+ Name apache_error
+ Format regex
+ Regex ^(?<key001>[^ ]*) (?<key002>[^ ]*) (?<time>.+)$
+ Time_Key time
+ Time_Format %a %b %d %H:%M:%S.%L %Y
+ Time_Keep On
+
+
+
+# Parser: mysql_quoted_stuff
+# ====================
+# Apache error log time format
+#
+[PARSER]
+ Name mysql_quoted_stuff
+ Format regex
+ Regex ^(?<time>.*?),(?<key001>.*)$
+ Time_Key time
+ Time_Format %Y-%M-%S %H:%M:%S
+ Time_Keep On
+ Decode_Field_As mysql_quoted key001
diff --git a/fluent-bit/tests/internal/data/reload/fluent-bit.conf b/fluent-bit/tests/internal/data/reload/fluent-bit.conf
new file mode 100644
index 00000000..4db44f32
--- /dev/null
+++ b/fluent-bit/tests/internal/data/reload/fluent-bit.conf
@@ -0,0 +1,15 @@
+[SERVICE]
+ Flush 1
+ Daemon Off
+ Log_Level error
+ HTTP_Server On
+ HTTP_Listen 0.0.0.0
+ HTTP_Port 2020
+
+[INPUT]
+ Name dummy
+ Tag dummy.locals
+
+[OUTPUT]
+ Name stdout
+ Match *
diff --git a/fluent-bit/tests/internal/data/reload/yaml/processor.yaml b/fluent-bit/tests/internal/data/reload/yaml/processor.yaml
new file mode 100644
index 00000000..3021ee7e
--- /dev/null
+++ b/fluent-bit/tests/internal/data/reload/yaml/processor.yaml
@@ -0,0 +1,41 @@
+service:
+ log_level: info
+ http_server: on
+ http_listen: 0.0.0.0
+ http_port: 2021
+
+
+pipeline:
+ inputs:
+ - name: random
+ tag: test-tag
+ interval_sec: 1
+
+ processors:
+ logs:
+ - name: modify
+ add: hostname monox
+
+ - name: lua
+ call: append_tag
+ code: |
+ function append_tag(tag, timestamp, record)
+ new_record = record
+ new_record["tag"] = tag
+ return 1, timestamp, new_record
+ end
+
+ outputs:
+ - name: stdout
+ match: '*'
+
+ processors:
+ logs:
+ - name: lua
+ call: add_field
+ code: |
+ function add_field(tag, timestamp, record)
+ new_record = record
+ new_record["output"] = "new data"
+ return 1, timestamp, new_record
+ end
diff --git a/fluent-bit/tests/internal/data/s3_local_buffer/.gitkeep b/fluent-bit/tests/internal/data/s3_local_buffer/.gitkeep
new file mode 100644
index 00000000..786a4bd9
--- /dev/null
+++ b/fluent-bit/tests/internal/data/s3_local_buffer/.gitkeep
@@ -0,0 +1 @@
+# git won't keep an empty dir, hence this file \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/LICENSE b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/LICENSE
new file mode 100644
index 00000000..d6456956
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/LICENSE
@@ -0,0 +1,202 @@
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/NOTICE b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/NOTICE
new file mode 100644
index 00000000..a0821aef
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/NOTICE
@@ -0,0 +1,2 @@
+AWS Signature Version 4 Test Suite
+Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-header-key-duplicate/get-header-key-duplicate.authz b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-header-key-duplicate/get-header-key-duplicate.authz
new file mode 100644
index 00000000..ade3ec75
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-header-key-duplicate/get-header-key-duplicate.authz
@@ -0,0 +1 @@
+AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;my-header1;x-amz-date, Signature=c9d5ea9f3f72853aea855b47ea873832890dbdd183b4468f858259531a5138ea \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-header-key-duplicate/get-header-key-duplicate.creq b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-header-key-duplicate/get-header-key-duplicate.creq
new file mode 100644
index 00000000..fa8f49a1
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-header-key-duplicate/get-header-key-duplicate.creq
@@ -0,0 +1,9 @@
+GET
+/
+
+host:example.amazonaws.com
+my-header1:value2,value2,value1
+x-amz-date:20150830T123600Z
+
+host;my-header1;x-amz-date
+e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-header-key-duplicate/get-header-key-duplicate.req b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-header-key-duplicate/get-header-key-duplicate.req
new file mode 100644
index 00000000..08a0364c
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-header-key-duplicate/get-header-key-duplicate.req
@@ -0,0 +1,6 @@
+GET / HTTP/1.1
+Host:example.amazonaws.com
+My-Header1:value2
+My-Header1:value2
+My-Header1:value1
+X-Amz-Date:20150830T123600Z \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-header-key-duplicate/get-header-key-duplicate.sreq b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-header-key-duplicate/get-header-key-duplicate.sreq
new file mode 100644
index 00000000..f0166e18
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-header-key-duplicate/get-header-key-duplicate.sreq
@@ -0,0 +1,7 @@
+GET / HTTP/1.1
+Host:example.amazonaws.com
+My-Header1:value2
+My-Header1:value2
+My-Header1:value1
+X-Amz-Date:20150830T123600Z
+Authorization: AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;my-header1;x-amz-date, Signature=c9d5ea9f3f72853aea855b47ea873832890dbdd183b4468f858259531a5138ea \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-header-key-duplicate/get-header-key-duplicate.sts b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-header-key-duplicate/get-header-key-duplicate.sts
new file mode 100644
index 00000000..48a135ec
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-header-key-duplicate/get-header-key-duplicate.sts
@@ -0,0 +1,4 @@
+AWS4-HMAC-SHA256
+20150830T123600Z
+20150830/us-east-1/service/aws4_request
+dc7f04a3abfde8d472b0ab1a418b741b7c67174dad1551b4117b15527fbe966c \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-header-value-multiline/get-header-value-multiline.authz b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-header-value-multiline/get-header-value-multiline.authz
new file mode 100644
index 00000000..e2717bf6
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-header-value-multiline/get-header-value-multiline.authz
@@ -0,0 +1 @@
+AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;my-header1;x-amz-date, Signature=ba17b383a53190154eb5fa66a1b836cc297cc0a3d70a5d00705980573d8ff790 \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-header-value-multiline/get-header-value-multiline.creq b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-header-value-multiline/get-header-value-multiline.creq
new file mode 100644
index 00000000..721a39ff
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-header-value-multiline/get-header-value-multiline.creq
@@ -0,0 +1,9 @@
+GET
+/
+
+host:example.amazonaws.com
+my-header1:value1,value2,value3
+x-amz-date:20150830T123600Z
+
+host;my-header1;x-amz-date
+e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-header-value-multiline/get-header-value-multiline.req b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-header-value-multiline/get-header-value-multiline.req
new file mode 100644
index 00000000..7caa6acc
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-header-value-multiline/get-header-value-multiline.req
@@ -0,0 +1,6 @@
+GET / HTTP/1.1
+Host:example.amazonaws.com
+My-Header1:value1
+ value2
+ value3
+X-Amz-Date:20150830T123600Z \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-header-value-multiline/get-header-value-multiline.sreq b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-header-value-multiline/get-header-value-multiline.sreq
new file mode 100644
index 00000000..56955d9a
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-header-value-multiline/get-header-value-multiline.sreq
@@ -0,0 +1,7 @@
+GET / HTTP/1.1
+Host:example.amazonaws.com
+My-Header1:value1
+ value2
+ value3
+X-Amz-Date:20150830T123600Z
+Authorization: AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;my-header1;x-amz-date, Signature=ba17b383a53190154eb5fa66a1b836cc297cc0a3d70a5d00705980573d8ff790 \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-header-value-multiline/get-header-value-multiline.sts b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-header-value-multiline/get-header-value-multiline.sts
new file mode 100644
index 00000000..0a3350ab
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-header-value-multiline/get-header-value-multiline.sts
@@ -0,0 +1,4 @@
+AWS4-HMAC-SHA256
+20150830T123600Z
+20150830/us-east-1/service/aws4_request
+b7b6cbfd8a0430b78891e986784da2630c8a135a8595cec25b26ea94f926ee55 \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-header-value-order/get-header-value-order.authz b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-header-value-order/get-header-value-order.authz
new file mode 100644
index 00000000..c0409ab2
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-header-value-order/get-header-value-order.authz
@@ -0,0 +1 @@
+AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;my-header1;x-amz-date, Signature=08c7e5a9acfcfeb3ab6b2185e75ce8b1deb5e634ec47601a50643f830c755c01 \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-header-value-order/get-header-value-order.creq b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-header-value-order/get-header-value-order.creq
new file mode 100644
index 00000000..e336bc94
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-header-value-order/get-header-value-order.creq
@@ -0,0 +1,9 @@
+GET
+/
+
+host:example.amazonaws.com
+my-header1:value4,value1,value3,value2
+x-amz-date:20150830T123600Z
+
+host;my-header1;x-amz-date
+e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-header-value-order/get-header-value-order.req b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-header-value-order/get-header-value-order.req
new file mode 100644
index 00000000..f7bd9e66
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-header-value-order/get-header-value-order.req
@@ -0,0 +1,7 @@
+GET / HTTP/1.1
+Host:example.amazonaws.com
+My-Header1:value4
+My-Header1:value1
+My-Header1:value3
+My-Header1:value2
+X-Amz-Date:20150830T123600Z \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-header-value-order/get-header-value-order.sreq b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-header-value-order/get-header-value-order.sreq
new file mode 100644
index 00000000..79e16a95
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-header-value-order/get-header-value-order.sreq
@@ -0,0 +1,8 @@
+GET / HTTP/1.1
+Host:example.amazonaws.com
+My-Header1:value4
+My-Header1:value1
+My-Header1:value3
+My-Header1:value2
+X-Amz-Date:20150830T123600Z
+Authorization: AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;my-header1;x-amz-date, Signature=08c7e5a9acfcfeb3ab6b2185e75ce8b1deb5e634ec47601a50643f830c755c01 \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-header-value-order/get-header-value-order.sts b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-header-value-order/get-header-value-order.sts
new file mode 100644
index 00000000..711a8d4d
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-header-value-order/get-header-value-order.sts
@@ -0,0 +1,4 @@
+AWS4-HMAC-SHA256
+20150830T123600Z
+20150830/us-east-1/service/aws4_request
+31ce73cd3f3d9f66977ad3dd957dc47af14df92fcd8509f59b349e9137c58b86 \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-header-value-trim/get-header-value-trim.authz b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-header-value-trim/get-header-value-trim.authz
new file mode 100644
index 00000000..4874ac0b
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-header-value-trim/get-header-value-trim.authz
@@ -0,0 +1 @@
+AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;my-header1;my-header2;x-amz-date, Signature=acc3ed3afb60bb290fc8d2dd0098b9911fcaa05412b367055dee359757a9c736 \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-header-value-trim/get-header-value-trim.creq b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-header-value-trim/get-header-value-trim.creq
new file mode 100644
index 00000000..a59087c9
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-header-value-trim/get-header-value-trim.creq
@@ -0,0 +1,10 @@
+GET
+/
+
+host:example.amazonaws.com
+my-header1:value1
+my-header2:"a b c"
+x-amz-date:20150830T123600Z
+
+host;my-header1;my-header2;x-amz-date
+e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-header-value-trim/get-header-value-trim.req b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-header-value-trim/get-header-value-trim.req
new file mode 100644
index 00000000..901f36c3
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-header-value-trim/get-header-value-trim.req
@@ -0,0 +1,5 @@
+GET / HTTP/1.1
+Host:example.amazonaws.com
+My-Header1: value1
+My-Header2: "a b c"
+X-Amz-Date:20150830T123600Z \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-header-value-trim/get-header-value-trim.sreq b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-header-value-trim/get-header-value-trim.sreq
new file mode 100644
index 00000000..98224c9b
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-header-value-trim/get-header-value-trim.sreq
@@ -0,0 +1,6 @@
+GET / HTTP/1.1
+Host:example.amazonaws.com
+My-Header1: value1
+My-Header2: "a b c"
+X-Amz-Date:20150830T123600Z
+Authorization: AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;my-header1;my-header2;x-amz-date, Signature=acc3ed3afb60bb290fc8d2dd0098b9911fcaa05412b367055dee359757a9c736 \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-header-value-trim/get-header-value-trim.sts b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-header-value-trim/get-header-value-trim.sts
new file mode 100644
index 00000000..a0b15cc7
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-header-value-trim/get-header-value-trim.sts
@@ -0,0 +1,4 @@
+AWS4-HMAC-SHA256
+20150830T123600Z
+20150830/us-east-1/service/aws4_request
+a726db9b0df21c14f559d0a978e563112acb1b9e05476f0a6a1c7d68f28605c7 \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-unreserved/get-unreserved.authz b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-unreserved/get-unreserved.authz
new file mode 100644
index 00000000..2943ec89
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-unreserved/get-unreserved.authz
@@ -0,0 +1 @@
+AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=07ef7494c76fa4850883e2b006601f940f8a34d404d0cfa977f52a65bbf5f24f \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-unreserved/get-unreserved.creq b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-unreserved/get-unreserved.creq
new file mode 100644
index 00000000..8af54df2
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-unreserved/get-unreserved.creq
@@ -0,0 +1,8 @@
+GET
+/-._~0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz
+
+host:example.amazonaws.com
+x-amz-date:20150830T123600Z
+
+host;x-amz-date
+e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-unreserved/get-unreserved.req b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-unreserved/get-unreserved.req
new file mode 100644
index 00000000..da760cdb
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-unreserved/get-unreserved.req
@@ -0,0 +1,3 @@
+GET /-._~0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz HTTP/1.1
+Host:example.amazonaws.com
+X-Amz-Date:20150830T123600Z \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-unreserved/get-unreserved.sreq b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-unreserved/get-unreserved.sreq
new file mode 100644
index 00000000..8001b3d6
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-unreserved/get-unreserved.sreq
@@ -0,0 +1,4 @@
+GET /-._~0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz HTTP/1.1
+Host:example.amazonaws.com
+X-Amz-Date:20150830T123600Z
+Authorization: AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=07ef7494c76fa4850883e2b006601f940f8a34d404d0cfa977f52a65bbf5f24f \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-unreserved/get-unreserved.sts b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-unreserved/get-unreserved.sts
new file mode 100644
index 00000000..e9dc5414
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-unreserved/get-unreserved.sts
@@ -0,0 +1,4 @@
+AWS4-HMAC-SHA256
+20150830T123600Z
+20150830/us-east-1/service/aws4_request
+6a968768eefaa713e2a6b16b589a8ea192661f098f37349f4e2c0082757446f9 \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-utf8/get-utf8.authz b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-utf8/get-utf8.authz
new file mode 100644
index 00000000..738b3fbd
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-utf8/get-utf8.authz
@@ -0,0 +1 @@
+AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=8318018e0b0f223aa2bbf98705b62bb787dc9c0e678f255a891fd03141be5d85 \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-utf8/get-utf8.creq b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-utf8/get-utf8.creq
new file mode 100644
index 00000000..5d4b9f61
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-utf8/get-utf8.creq
@@ -0,0 +1,8 @@
+GET
+/%E1%88%B4
+
+host:example.amazonaws.com
+x-amz-date:20150830T123600Z
+
+host;x-amz-date
+e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-utf8/get-utf8.req b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-utf8/get-utf8.req
new file mode 100644
index 00000000..da4808d0
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-utf8/get-utf8.req
@@ -0,0 +1,3 @@
+GET /ሴ HTTP/1.1
+Host:example.amazonaws.com
+X-Amz-Date:20150830T123600Z \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-utf8/get-utf8.sreq b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-utf8/get-utf8.sreq
new file mode 100644
index 00000000..94eadb6d
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-utf8/get-utf8.sreq
@@ -0,0 +1,4 @@
+GET /ሴ HTTP/1.1
+Host:example.amazonaws.com
+X-Amz-Date:20150830T123600Z
+Authorization: AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=8318018e0b0f223aa2bbf98705b62bb787dc9c0e678f255a891fd03141be5d85 \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-utf8/get-utf8.sts b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-utf8/get-utf8.sts
new file mode 100644
index 00000000..5edc8f45
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-utf8/get-utf8.sts
@@ -0,0 +1,4 @@
+AWS4-HMAC-SHA256
+20150830T123600Z
+20150830/us-east-1/service/aws4_request
+2a0a97d02205e45ce2e994789806b19270cfbbb0921b278ccf58f5249ac42102 \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-empty-query-key/get-vanilla-empty-query-key.authz b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-empty-query-key/get-vanilla-empty-query-key.authz
new file mode 100644
index 00000000..65b5c7ce
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-empty-query-key/get-vanilla-empty-query-key.authz
@@ -0,0 +1 @@
+AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=a67d582fa61cc504c4bae71f336f98b97f1ea3c7a6bfe1b6e45aec72011b9aeb \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-empty-query-key/get-vanilla-empty-query-key.creq b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-empty-query-key/get-vanilla-empty-query-key.creq
new file mode 100644
index 00000000..c6cdceda
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-empty-query-key/get-vanilla-empty-query-key.creq
@@ -0,0 +1,8 @@
+GET
+/
+Param1=value1
+host:example.amazonaws.com
+x-amz-date:20150830T123600Z
+
+host;x-amz-date
+e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-empty-query-key/get-vanilla-empty-query-key.req b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-empty-query-key/get-vanilla-empty-query-key.req
new file mode 100644
index 00000000..970d0a05
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-empty-query-key/get-vanilla-empty-query-key.req
@@ -0,0 +1,3 @@
+GET /?Param1=value1 HTTP/1.1
+Host:example.amazonaws.com
+X-Amz-Date:20150830T123600Z \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-empty-query-key/get-vanilla-empty-query-key.sreq b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-empty-query-key/get-vanilla-empty-query-key.sreq
new file mode 100644
index 00000000..f0815913
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-empty-query-key/get-vanilla-empty-query-key.sreq
@@ -0,0 +1,4 @@
+GET /?Param1=value1 HTTP/1.1
+Host:example.amazonaws.com
+X-Amz-Date:20150830T123600Z
+Authorization: AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=a67d582fa61cc504c4bae71f336f98b97f1ea3c7a6bfe1b6e45aec72011b9aeb \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-empty-query-key/get-vanilla-empty-query-key.sts b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-empty-query-key/get-vanilla-empty-query-key.sts
new file mode 100644
index 00000000..c4ed216c
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-empty-query-key/get-vanilla-empty-query-key.sts
@@ -0,0 +1,4 @@
+AWS4-HMAC-SHA256
+20150830T123600Z
+20150830/us-east-1/service/aws4_request
+1e24db194ed7d0eec2de28d7369675a243488e08526e8c1c73571282f7c517ab \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-query-order-key-case/get-vanilla-query-order-key-case.authz b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-query-order-key-case/get-vanilla-query-order-key-case.authz
new file mode 100644
index 00000000..c781fe66
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-query-order-key-case/get-vanilla-query-order-key-case.authz
@@ -0,0 +1 @@
+AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=b97d918cfa904a5beff61c982a1b6f458b799221646efd99d3219ec94cdf2500 \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-query-order-key-case/get-vanilla-query-order-key-case.creq b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-query-order-key-case/get-vanilla-query-order-key-case.creq
new file mode 100644
index 00000000..8ae02cd6
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-query-order-key-case/get-vanilla-query-order-key-case.creq
@@ -0,0 +1,8 @@
+GET
+/
+Param1=value1&Param2=value2
+host:example.amazonaws.com
+x-amz-date:20150830T123600Z
+
+host;x-amz-date
+e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-query-order-key-case/get-vanilla-query-order-key-case.req b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-query-order-key-case/get-vanilla-query-order-key-case.req
new file mode 100644
index 00000000..8a56f15f
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-query-order-key-case/get-vanilla-query-order-key-case.req
@@ -0,0 +1,3 @@
+GET /?Param2=value2&Param1=value1 HTTP/1.1
+Host:example.amazonaws.com
+X-Amz-Date:20150830T123600Z \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-query-order-key-case/get-vanilla-query-order-key-case.sreq b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-query-order-key-case/get-vanilla-query-order-key-case.sreq
new file mode 100644
index 00000000..aa3162d8
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-query-order-key-case/get-vanilla-query-order-key-case.sreq
@@ -0,0 +1,4 @@
+GET /?Param2=value2&Param1=value1 HTTP/1.1
+Host:example.amazonaws.com
+X-Amz-Date:20150830T123600Z
+Authorization: AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=b97d918cfa904a5beff61c982a1b6f458b799221646efd99d3219ec94cdf2500 \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-query-order-key-case/get-vanilla-query-order-key-case.sts b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-query-order-key-case/get-vanilla-query-order-key-case.sts
new file mode 100644
index 00000000..f773de59
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-query-order-key-case/get-vanilla-query-order-key-case.sts
@@ -0,0 +1,4 @@
+AWS4-HMAC-SHA256
+20150830T123600Z
+20150830/us-east-1/service/aws4_request
+816cd5b414d056048ba4f7c5386d6e0533120fb1fcfa93762cf0fc39e2cf19e0 \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-query-order-key/get-vanilla-query-order-key.authz b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-query-order-key/get-vanilla-query-order-key.authz
new file mode 100644
index 00000000..812cd3fd
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-query-order-key/get-vanilla-query-order-key.authz
@@ -0,0 +1 @@
+AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=eedbc4e291e521cf13422ffca22be7d2eb8146eecf653089df300a15b2382bd1 \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-query-order-key/get-vanilla-query-order-key.creq b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-query-order-key/get-vanilla-query-order-key.creq
new file mode 100644
index 00000000..36c3cdfa
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-query-order-key/get-vanilla-query-order-key.creq
@@ -0,0 +1,8 @@
+GET
+/
+Param1=Value1&Param1=value2
+host:example.amazonaws.com
+x-amz-date:20150830T123600Z
+
+host;x-amz-date
+e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-query-order-key/get-vanilla-query-order-key.req b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-query-order-key/get-vanilla-query-order-key.req
new file mode 100644
index 00000000..375a4965
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-query-order-key/get-vanilla-query-order-key.req
@@ -0,0 +1,3 @@
+GET /?Param1=value2&Param1=Value1 HTTP/1.1
+Host:example.amazonaws.com
+X-Amz-Date:20150830T123600Z \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-query-order-key/get-vanilla-query-order-key.sreq b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-query-order-key/get-vanilla-query-order-key.sreq
new file mode 100644
index 00000000..bc8e6520
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-query-order-key/get-vanilla-query-order-key.sreq
@@ -0,0 +1,4 @@
+GET /?Param1=value2&Param1=Value1 HTTP/1.1
+Host:example.amazonaws.com
+X-Amz-Date:20150830T123600Z
+Authorization: AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=eedbc4e291e521cf13422ffca22be7d2eb8146eecf653089df300a15b2382bd1 \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-query-order-key/get-vanilla-query-order-key.sts b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-query-order-key/get-vanilla-query-order-key.sts
new file mode 100644
index 00000000..fd43a414
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-query-order-key/get-vanilla-query-order-key.sts
@@ -0,0 +1,4 @@
+AWS4-HMAC-SHA256
+20150830T123600Z
+20150830/us-east-1/service/aws4_request
+704b4cef673542d84cdff252633f065e8daeba5f168b77116f8b1bcaf3d38f89 \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-query-order-value/get-vanilla-query-order-value.authz b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-query-order-value/get-vanilla-query-order-value.authz
new file mode 100644
index 00000000..b8ad91f6
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-query-order-value/get-vanilla-query-order-value.authz
@@ -0,0 +1 @@
+AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=5772eed61e12b33fae39ee5e7012498b51d56abc0abb7c60486157bd471c4694 \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-query-order-value/get-vanilla-query-order-value.creq b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-query-order-value/get-vanilla-query-order-value.creq
new file mode 100644
index 00000000..26898ebe
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-query-order-value/get-vanilla-query-order-value.creq
@@ -0,0 +1,8 @@
+GET
+/
+Param1=value1&Param1=value2
+host:example.amazonaws.com
+x-amz-date:20150830T123600Z
+
+host;x-amz-date
+e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-query-order-value/get-vanilla-query-order-value.req b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-query-order-value/get-vanilla-query-order-value.req
new file mode 100644
index 00000000..9255bee0
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-query-order-value/get-vanilla-query-order-value.req
@@ -0,0 +1,3 @@
+GET /?Param1=value2&Param1=value1 HTTP/1.1
+Host:example.amazonaws.com
+X-Amz-Date:20150830T123600Z \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-query-order-value/get-vanilla-query-order-value.sreq b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-query-order-value/get-vanilla-query-order-value.sreq
new file mode 100644
index 00000000..4793e218
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-query-order-value/get-vanilla-query-order-value.sreq
@@ -0,0 +1,4 @@
+GET /?Param1=value2&Param1=value1 HTTP/1.1
+Host:example.amazonaws.com
+X-Amz-Date:20150830T123600Z
+Authorization: AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=5772eed61e12b33fae39ee5e7012498b51d56abc0abb7c60486157bd471c4694 \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-query-order-value/get-vanilla-query-order-value.sts b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-query-order-value/get-vanilla-query-order-value.sts
new file mode 100644
index 00000000..90e66b8d
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-query-order-value/get-vanilla-query-order-value.sts
@@ -0,0 +1,4 @@
+AWS4-HMAC-SHA256
+20150830T123600Z
+20150830/us-east-1/service/aws4_request
+c968629d70850097a2d8781c9bf7edcb988b04cac14cca9be4acc3595f884606 \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-query-unreserved/get-vanilla-query-unreserved.authz b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-query-unreserved/get-vanilla-query-unreserved.authz
new file mode 100644
index 00000000..a44ca5be
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-query-unreserved/get-vanilla-query-unreserved.authz
@@ -0,0 +1 @@
+AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=9c3e54bfcdf0b19771a7f523ee5669cdf59bc7cc0884027167c21bb143a40197 \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-query-unreserved/get-vanilla-query-unreserved.creq b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-query-unreserved/get-vanilla-query-unreserved.creq
new file mode 100644
index 00000000..5249be3b
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-query-unreserved/get-vanilla-query-unreserved.creq
@@ -0,0 +1,8 @@
+GET
+/
+-._~0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz=-._~0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz
+host:example.amazonaws.com
+x-amz-date:20150830T123600Z
+
+host;x-amz-date
+e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-query-unreserved/get-vanilla-query-unreserved.req b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-query-unreserved/get-vanilla-query-unreserved.req
new file mode 100644
index 00000000..d2833b32
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-query-unreserved/get-vanilla-query-unreserved.req
@@ -0,0 +1,3 @@
+GET /?-._~0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz=-._~0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz HTTP/1.1
+Host:example.amazonaws.com
+X-Amz-Date:20150830T123600Z \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-query-unreserved/get-vanilla-query-unreserved.sreq b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-query-unreserved/get-vanilla-query-unreserved.sreq
new file mode 100644
index 00000000..ba1ef402
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-query-unreserved/get-vanilla-query-unreserved.sreq
@@ -0,0 +1,4 @@
+GET /?-._~0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz=-._~0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz HTTP/1.1
+Host:example.amazonaws.com
+X-Amz-Date:20150830T123600Z
+Authorization: AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=9c3e54bfcdf0b19771a7f523ee5669cdf59bc7cc0884027167c21bb143a40197 \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-query-unreserved/get-vanilla-query-unreserved.sts b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-query-unreserved/get-vanilla-query-unreserved.sts
new file mode 100644
index 00000000..24a97d20
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-query-unreserved/get-vanilla-query-unreserved.sts
@@ -0,0 +1,4 @@
+AWS4-HMAC-SHA256
+20150830T123600Z
+20150830/us-east-1/service/aws4_request
+c30d4703d9f799439be92736156d47ccfb2d879ddf56f5befa6d1d6aab979177 \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-query/get-vanilla-query.authz b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-query/get-vanilla-query.authz
new file mode 100644
index 00000000..551c0271
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-query/get-vanilla-query.authz
@@ -0,0 +1 @@
+AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=5fa00fa31553b73ebf1942676e86291e8372ff2a2260956d9b8aae1d763fbf31 \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-query/get-vanilla-query.creq b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-query/get-vanilla-query.creq
new file mode 100644
index 00000000..ed91561f
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-query/get-vanilla-query.creq
@@ -0,0 +1,8 @@
+GET
+/
+
+host:example.amazonaws.com
+x-amz-date:20150830T123600Z
+
+host;x-amz-date
+e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-query/get-vanilla-query.req b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-query/get-vanilla-query.req
new file mode 100644
index 00000000..0f7a9bfa
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-query/get-vanilla-query.req
@@ -0,0 +1,3 @@
+GET / HTTP/1.1
+Host:example.amazonaws.com
+X-Amz-Date:20150830T123600Z \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-query/get-vanilla-query.sreq b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-query/get-vanilla-query.sreq
new file mode 100644
index 00000000..d739b01f
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-query/get-vanilla-query.sreq
@@ -0,0 +1,4 @@
+GET / HTTP/1.1
+Host:example.amazonaws.com
+X-Amz-Date:20150830T123600Z
+Authorization: AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=5fa00fa31553b73ebf1942676e86291e8372ff2a2260956d9b8aae1d763fbf31 \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-query/get-vanilla-query.sts b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-query/get-vanilla-query.sts
new file mode 100644
index 00000000..b187649c
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-query/get-vanilla-query.sts
@@ -0,0 +1,4 @@
+AWS4-HMAC-SHA256
+20150830T123600Z
+20150830/us-east-1/service/aws4_request
+bb579772317eb040ac9ed261061d46c1f17a8133879d6129b6e1c25292927e63 \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-utf8-query/get-vanilla-utf8-query.authz b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-utf8-query/get-vanilla-utf8-query.authz
new file mode 100644
index 00000000..e016c3da
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-utf8-query/get-vanilla-utf8-query.authz
@@ -0,0 +1 @@
+AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=2cdec8eed098649ff3a119c94853b13c643bcf08f8b0a1d91e12c9027818dd04 \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-utf8-query/get-vanilla-utf8-query.creq b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-utf8-query/get-vanilla-utf8-query.creq
new file mode 100644
index 00000000..a835c9e4
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-utf8-query/get-vanilla-utf8-query.creq
@@ -0,0 +1,8 @@
+GET
+/
+%E1%88%B4=bar
+host:example.amazonaws.com
+x-amz-date:20150830T123600Z
+
+host;x-amz-date
+e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-utf8-query/get-vanilla-utf8-query.req b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-utf8-query/get-vanilla-utf8-query.req
new file mode 100644
index 00000000..cc2757e1
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-utf8-query/get-vanilla-utf8-query.req
@@ -0,0 +1,3 @@
+GET /?ሴ=bar HTTP/1.1
+Host:example.amazonaws.com
+X-Amz-Date:20150830T123600Z \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-utf8-query/get-vanilla-utf8-query.sreq b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-utf8-query/get-vanilla-utf8-query.sreq
new file mode 100644
index 00000000..7baf4c82
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-utf8-query/get-vanilla-utf8-query.sreq
@@ -0,0 +1,4 @@
+GET /?ሴ=bar HTTP/1.1
+Host:example.amazonaws.com
+X-Amz-Date:20150830T123600Z
+Authorization: AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=2cdec8eed098649ff3a119c94853b13c643bcf08f8b0a1d91e12c9027818dd04 \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-utf8-query/get-vanilla-utf8-query.sts b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-utf8-query/get-vanilla-utf8-query.sts
new file mode 100644
index 00000000..51ee71b7
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-utf8-query/get-vanilla-utf8-query.sts
@@ -0,0 +1,4 @@
+AWS4-HMAC-SHA256
+20150830T123600Z
+20150830/us-east-1/service/aws4_request
+eb30c5bed55734080471a834cc727ae56beb50e5f39d1bff6d0d38cb192a7073 \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla/get-vanilla.authz b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla/get-vanilla.authz
new file mode 100644
index 00000000..551c0271
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla/get-vanilla.authz
@@ -0,0 +1 @@
+AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=5fa00fa31553b73ebf1942676e86291e8372ff2a2260956d9b8aae1d763fbf31 \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla/get-vanilla.creq b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla/get-vanilla.creq
new file mode 100644
index 00000000..ed91561f
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla/get-vanilla.creq
@@ -0,0 +1,8 @@
+GET
+/
+
+host:example.amazonaws.com
+x-amz-date:20150830T123600Z
+
+host;x-amz-date
+e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla/get-vanilla.req b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla/get-vanilla.req
new file mode 100644
index 00000000..0f7a9bfa
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla/get-vanilla.req
@@ -0,0 +1,3 @@
+GET / HTTP/1.1
+Host:example.amazonaws.com
+X-Amz-Date:20150830T123600Z \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla/get-vanilla.sreq b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla/get-vanilla.sreq
new file mode 100644
index 00000000..d739b01f
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla/get-vanilla.sreq
@@ -0,0 +1,4 @@
+GET / HTTP/1.1
+Host:example.amazonaws.com
+X-Amz-Date:20150830T123600Z
+Authorization: AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=5fa00fa31553b73ebf1942676e86291e8372ff2a2260956d9b8aae1d763fbf31 \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla/get-vanilla.sts b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla/get-vanilla.sts
new file mode 100644
index 00000000..b187649c
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla/get-vanilla.sts
@@ -0,0 +1,4 @@
+AWS4-HMAC-SHA256
+20150830T123600Z
+20150830/us-east-1/service/aws4_request
+bb579772317eb040ac9ed261061d46c1f17a8133879d6129b6e1c25292927e63 \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-relative-relative/get-relative-relative.authz b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-relative-relative/get-relative-relative.authz
new file mode 100644
index 00000000..551c0271
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-relative-relative/get-relative-relative.authz
@@ -0,0 +1 @@
+AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=5fa00fa31553b73ebf1942676e86291e8372ff2a2260956d9b8aae1d763fbf31 \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-relative-relative/get-relative-relative.creq b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-relative-relative/get-relative-relative.creq
new file mode 100644
index 00000000..ed91561f
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-relative-relative/get-relative-relative.creq
@@ -0,0 +1,8 @@
+GET
+/
+
+host:example.amazonaws.com
+x-amz-date:20150830T123600Z
+
+host;x-amz-date
+e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-relative-relative/get-relative-relative.req b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-relative-relative/get-relative-relative.req
new file mode 100644
index 00000000..cfd4e8b7
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-relative-relative/get-relative-relative.req
@@ -0,0 +1,3 @@
+GET /example1/example2/../.. HTTP/1.1
+Host:example.amazonaws.com
+X-Amz-Date:20150830T123600Z \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-relative-relative/get-relative-relative.sreq b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-relative-relative/get-relative-relative.sreq
new file mode 100644
index 00000000..cbdebe2c
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-relative-relative/get-relative-relative.sreq
@@ -0,0 +1,4 @@
+GET /example1/example2/../.. HTTP/1.1
+Host:example.amazonaws.com
+X-Amz-Date:20150830T123600Z
+Authorization: AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=5fa00fa31553b73ebf1942676e86291e8372ff2a2260956d9b8aae1d763fbf31 \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-relative-relative/get-relative-relative.sts b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-relative-relative/get-relative-relative.sts
new file mode 100644
index 00000000..b187649c
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-relative-relative/get-relative-relative.sts
@@ -0,0 +1,4 @@
+AWS4-HMAC-SHA256
+20150830T123600Z
+20150830/us-east-1/service/aws4_request
+bb579772317eb040ac9ed261061d46c1f17a8133879d6129b6e1c25292927e63 \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-relative/get-relative.authz b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-relative/get-relative.authz
new file mode 100644
index 00000000..551c0271
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-relative/get-relative.authz
@@ -0,0 +1 @@
+AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=5fa00fa31553b73ebf1942676e86291e8372ff2a2260956d9b8aae1d763fbf31 \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-relative/get-relative.creq b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-relative/get-relative.creq
new file mode 100644
index 00000000..ed91561f
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-relative/get-relative.creq
@@ -0,0 +1,8 @@
+GET
+/
+
+host:example.amazonaws.com
+x-amz-date:20150830T123600Z
+
+host;x-amz-date
+e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-relative/get-relative.req b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-relative/get-relative.req
new file mode 100644
index 00000000..9d6d7ca2
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-relative/get-relative.req
@@ -0,0 +1,3 @@
+GET /example/.. HTTP/1.1
+Host:example.amazonaws.com
+X-Amz-Date:20150830T123600Z \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-relative/get-relative.sreq b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-relative/get-relative.sreq
new file mode 100644
index 00000000..4f59e7d2
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-relative/get-relative.sreq
@@ -0,0 +1,4 @@
+GET /example/.. HTTP/1.1
+Host:example.amazonaws.com
+X-Amz-Date:20150830T123600Z
+Authorization: AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=5fa00fa31553b73ebf1942676e86291e8372ff2a2260956d9b8aae1d763fbf31 \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-relative/get-relative.sts b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-relative/get-relative.sts
new file mode 100644
index 00000000..b187649c
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-relative/get-relative.sts
@@ -0,0 +1,4 @@
+AWS4-HMAC-SHA256
+20150830T123600Z
+20150830/us-east-1/service/aws4_request
+bb579772317eb040ac9ed261061d46c1f17a8133879d6129b6e1c25292927e63 \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-slash-dot-slash/get-slash-dot-slash.authz b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-slash-dot-slash/get-slash-dot-slash.authz
new file mode 100644
index 00000000..551c0271
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-slash-dot-slash/get-slash-dot-slash.authz
@@ -0,0 +1 @@
+AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=5fa00fa31553b73ebf1942676e86291e8372ff2a2260956d9b8aae1d763fbf31 \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-slash-dot-slash/get-slash-dot-slash.creq b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-slash-dot-slash/get-slash-dot-slash.creq
new file mode 100644
index 00000000..ed91561f
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-slash-dot-slash/get-slash-dot-slash.creq
@@ -0,0 +1,8 @@
+GET
+/
+
+host:example.amazonaws.com
+x-amz-date:20150830T123600Z
+
+host;x-amz-date
+e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-slash-dot-slash/get-slash-dot-slash.req b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-slash-dot-slash/get-slash-dot-slash.req
new file mode 100644
index 00000000..f3537b70
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-slash-dot-slash/get-slash-dot-slash.req
@@ -0,0 +1,3 @@
+GET /./ HTTP/1.1
+Host:example.amazonaws.com
+X-Amz-Date:20150830T123600Z \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-slash-dot-slash/get-slash-dot-slash.sreq b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-slash-dot-slash/get-slash-dot-slash.sreq
new file mode 100644
index 00000000..23a2b41c
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-slash-dot-slash/get-slash-dot-slash.sreq
@@ -0,0 +1,4 @@
+GET /./ HTTP/1.1
+Host:example.amazonaws.com
+X-Amz-Date:20150830T123600Z
+Authorization: AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=5fa00fa31553b73ebf1942676e86291e8372ff2a2260956d9b8aae1d763fbf31 \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-slash-dot-slash/get-slash-dot-slash.sts b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-slash-dot-slash/get-slash-dot-slash.sts
new file mode 100644
index 00000000..b187649c
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-slash-dot-slash/get-slash-dot-slash.sts
@@ -0,0 +1,4 @@
+AWS4-HMAC-SHA256
+20150830T123600Z
+20150830/us-east-1/service/aws4_request
+bb579772317eb040ac9ed261061d46c1f17a8133879d6129b6e1c25292927e63 \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-slash-pointless-dot/get-slash-pointless-dot.authz b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-slash-pointless-dot/get-slash-pointless-dot.authz
new file mode 100644
index 00000000..b76ca1e2
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-slash-pointless-dot/get-slash-pointless-dot.authz
@@ -0,0 +1 @@
+AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=ef75d96142cf21edca26f06005da7988e4f8dc83a165a80865db7089db637ec5 \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-slash-pointless-dot/get-slash-pointless-dot.creq b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-slash-pointless-dot/get-slash-pointless-dot.creq
new file mode 100644
index 00000000..915c57f2
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-slash-pointless-dot/get-slash-pointless-dot.creq
@@ -0,0 +1,8 @@
+GET
+/example
+
+host:example.amazonaws.com
+x-amz-date:20150830T123600Z
+
+host;x-amz-date
+e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-slash-pointless-dot/get-slash-pointless-dot.req b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-slash-pointless-dot/get-slash-pointless-dot.req
new file mode 100644
index 00000000..3c910717
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-slash-pointless-dot/get-slash-pointless-dot.req
@@ -0,0 +1,3 @@
+GET /./example HTTP/1.1
+Host:example.amazonaws.com
+X-Amz-Date:20150830T123600Z \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-slash-pointless-dot/get-slash-pointless-dot.sreq b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-slash-pointless-dot/get-slash-pointless-dot.sreq
new file mode 100644
index 00000000..80966096
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-slash-pointless-dot/get-slash-pointless-dot.sreq
@@ -0,0 +1,4 @@
+GET /./example HTTP/1.1
+Host:example.amazonaws.com
+X-Amz-Date:20150830T123600Z
+Authorization: AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=ef75d96142cf21edca26f06005da7988e4f8dc83a165a80865db7089db637ec5 \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-slash-pointless-dot/get-slash-pointless-dot.sts b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-slash-pointless-dot/get-slash-pointless-dot.sts
new file mode 100644
index 00000000..7429923e
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-slash-pointless-dot/get-slash-pointless-dot.sts
@@ -0,0 +1,4 @@
+AWS4-HMAC-SHA256
+20150830T123600Z
+20150830/us-east-1/service/aws4_request
+214d50c111a8edc4819da6a636336472c916b5240f51e9a51b5c3305180cf702 \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-slash/get-slash.authz b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-slash/get-slash.authz
new file mode 100644
index 00000000..551c0271
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-slash/get-slash.authz
@@ -0,0 +1 @@
+AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=5fa00fa31553b73ebf1942676e86291e8372ff2a2260956d9b8aae1d763fbf31 \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-slash/get-slash.creq b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-slash/get-slash.creq
new file mode 100644
index 00000000..ed91561f
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-slash/get-slash.creq
@@ -0,0 +1,8 @@
+GET
+/
+
+host:example.amazonaws.com
+x-amz-date:20150830T123600Z
+
+host;x-amz-date
+e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-slash/get-slash.req b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-slash/get-slash.req
new file mode 100644
index 00000000..ede8e3c8
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-slash/get-slash.req
@@ -0,0 +1,3 @@
+GET // HTTP/1.1
+Host:example.amazonaws.com
+X-Amz-Date:20150830T123600Z \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-slash/get-slash.sreq b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-slash/get-slash.sreq
new file mode 100644
index 00000000..cde31b43
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-slash/get-slash.sreq
@@ -0,0 +1,4 @@
+GET // HTTP/1.1
+Host:example.amazonaws.com
+X-Amz-Date:20150830T123600Z
+Authorization: AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=5fa00fa31553b73ebf1942676e86291e8372ff2a2260956d9b8aae1d763fbf31 \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-slash/get-slash.sts b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-slash/get-slash.sts
new file mode 100644
index 00000000..b187649c
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-slash/get-slash.sts
@@ -0,0 +1,4 @@
+AWS4-HMAC-SHA256
+20150830T123600Z
+20150830/us-east-1/service/aws4_request
+bb579772317eb040ac9ed261061d46c1f17a8133879d6129b6e1c25292927e63 \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-slashes/get-slashes.authz b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-slashes/get-slashes.authz
new file mode 100644
index 00000000..307c1051
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-slashes/get-slashes.authz
@@ -0,0 +1 @@
+AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=9a624bd73a37c9a373b5312afbebe7a714a789de108f0bdfe846570885f57e84 \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-slashes/get-slashes.creq b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-slashes/get-slashes.creq
new file mode 100644
index 00000000..2bdaf747
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-slashes/get-slashes.creq
@@ -0,0 +1,8 @@
+GET
+/example/
+
+host:example.amazonaws.com
+x-amz-date:20150830T123600Z
+
+host;x-amz-date
+e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-slashes/get-slashes.req b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-slashes/get-slashes.req
new file mode 100644
index 00000000..a4307ce4
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-slashes/get-slashes.req
@@ -0,0 +1,3 @@
+GET //example// HTTP/1.1
+Host:example.amazonaws.com
+X-Amz-Date:20150830T123600Z \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-slashes/get-slashes.sreq b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-slashes/get-slashes.sreq
new file mode 100644
index 00000000..c84a80d5
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-slashes/get-slashes.sreq
@@ -0,0 +1,4 @@
+GET //example// HTTP/1.1
+Host:example.amazonaws.com
+X-Amz-Date:20150830T123600Z
+Authorization: AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=9a624bd73a37c9a373b5312afbebe7a714a789de108f0bdfe846570885f57e84 \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-slashes/get-slashes.sts b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-slashes/get-slashes.sts
new file mode 100644
index 00000000..95d1fc25
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-slashes/get-slashes.sts
@@ -0,0 +1,4 @@
+AWS4-HMAC-SHA256
+20150830T123600Z
+20150830/us-east-1/service/aws4_request
+cb96b4ac96d501f7c5c15bc6d67b3035061cfced4af6585ad927f7e6c985c015 \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-space/get-space.authz b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-space/get-space.authz
new file mode 100644
index 00000000..832d8a50
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-space/get-space.authz
@@ -0,0 +1 @@
+AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=652487583200325589f1fba4c7e578f72c47cb61beeca81406b39ddec1366741 \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-space/get-space.creq b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-space/get-space.creq
new file mode 100644
index 00000000..124a7096
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-space/get-space.creq
@@ -0,0 +1,8 @@
+GET
+/example%20space/
+
+host:example.amazonaws.com
+x-amz-date:20150830T123600Z
+
+host;x-amz-date
+e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-space/get-space.req b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-space/get-space.req
new file mode 100644
index 00000000..b7d5e8bb
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-space/get-space.req
@@ -0,0 +1,3 @@
+GET /example space/ HTTP/1.1
+Host:example.amazonaws.com
+X-Amz-Date:20150830T123600Z \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-space/get-space.sreq b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-space/get-space.sreq
new file mode 100644
index 00000000..eefa20c4
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-space/get-space.sreq
@@ -0,0 +1,4 @@
+GET /example space/ HTTP/1.1
+Host:example.amazonaws.com
+X-Amz-Date:20150830T123600Z
+Authorization: AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=652487583200325589f1fba4c7e578f72c47cb61beeca81406b39ddec1366741 \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-space/get-space.sts b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-space/get-space.sts
new file mode 100644
index 00000000..a633f0c0
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-space/get-space.sts
@@ -0,0 +1,4 @@
+AWS4-HMAC-SHA256
+20150830T123600Z
+20150830/us-east-1/service/aws4_request
+63ee75631ed7234ae61b5f736dfc7754cdccfedbff4b5128a915706ee9390d86 \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/normalize-path.txt b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/normalize-path.txt
new file mode 100644
index 00000000..c2fcb272
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/normalize-path.txt
@@ -0,0 +1,3 @@
+A note about signing requests to Amazon S3:
+
+In exception to this, you do not normalize URI paths for requests to Amazon S3. For example, if you have a bucket with an object named my-object//example//photo.user, use that path. Normalizing the path to my-object/example/photo.user will cause the request to fail. For more information, see Task 1: Create a Canonical Request in the Amazon Simple Storage Service API Reference: http://docs.aws.amazon.com/AmazonS3/latest/API/sig-v4-header-based-auth.html#canonical-request \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-header-key-case/post-header-key-case.authz b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-header-key-case/post-header-key-case.authz
new file mode 100644
index 00000000..89e572e6
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-header-key-case/post-header-key-case.authz
@@ -0,0 +1 @@
+AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=5da7c1a2acd57cee7505fc6676e4e544621c30862966e37dddb68e92efbe5d6b \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-header-key-case/post-header-key-case.creq b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-header-key-case/post-header-key-case.creq
new file mode 100644
index 00000000..5c3a9434
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-header-key-case/post-header-key-case.creq
@@ -0,0 +1,8 @@
+POST
+/
+
+host:example.amazonaws.com
+x-amz-date:20150830T123600Z
+
+host;x-amz-date
+e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-header-key-case/post-header-key-case.req b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-header-key-case/post-header-key-case.req
new file mode 100644
index 00000000..3dc41790
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-header-key-case/post-header-key-case.req
@@ -0,0 +1,3 @@
+POST / HTTP/1.1
+Host:example.amazonaws.com
+X-Amz-Date:20150830T123600Z \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-header-key-case/post-header-key-case.sreq b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-header-key-case/post-header-key-case.sreq
new file mode 100644
index 00000000..a5ada0d9
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-header-key-case/post-header-key-case.sreq
@@ -0,0 +1,4 @@
+POST / HTTP/1.1
+Host:example.amazonaws.com
+X-Amz-Date:20150830T123600Z
+Authorization: AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=5da7c1a2acd57cee7505fc6676e4e544621c30862966e37dddb68e92efbe5d6b \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-header-key-case/post-header-key-case.sts b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-header-key-case/post-header-key-case.sts
new file mode 100644
index 00000000..a6367039
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-header-key-case/post-header-key-case.sts
@@ -0,0 +1,4 @@
+AWS4-HMAC-SHA256
+20150830T123600Z
+20150830/us-east-1/service/aws4_request
+553f88c9e4d10fc9e109e2aeb65f030801b70c2f6468faca261d401ae622fc87 \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-header-key-sort/post-header-key-sort.authz b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-header-key-sort/post-header-key-sort.authz
new file mode 100644
index 00000000..a62589ff
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-header-key-sort/post-header-key-sort.authz
@@ -0,0 +1 @@
+AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;my-header1;x-amz-date, Signature=c5410059b04c1ee005303aed430f6e6645f61f4dc9e1461ec8f8916fdf18852c \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-header-key-sort/post-header-key-sort.creq b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-header-key-sort/post-header-key-sort.creq
new file mode 100644
index 00000000..ebe943e8
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-header-key-sort/post-header-key-sort.creq
@@ -0,0 +1,9 @@
+POST
+/
+
+host:example.amazonaws.com
+my-header1:value1
+x-amz-date:20150830T123600Z
+
+host;my-header1;x-amz-date
+e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-header-key-sort/post-header-key-sort.req b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-header-key-sort/post-header-key-sort.req
new file mode 100644
index 00000000..0253f194
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-header-key-sort/post-header-key-sort.req
@@ -0,0 +1,4 @@
+POST / HTTP/1.1
+Host:example.amazonaws.com
+My-Header1:value1
+X-Amz-Date:20150830T123600Z \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-header-key-sort/post-header-key-sort.sreq b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-header-key-sort/post-header-key-sort.sreq
new file mode 100644
index 00000000..b4b78a16
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-header-key-sort/post-header-key-sort.sreq
@@ -0,0 +1,5 @@
+POST / HTTP/1.1
+Host:example.amazonaws.com
+My-Header1:value1
+X-Amz-Date:20150830T123600Z
+Authorization: AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;my-header1;x-amz-date, Signature=c5410059b04c1ee005303aed430f6e6645f61f4dc9e1461ec8f8916fdf18852c \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-header-key-sort/post-header-key-sort.sts b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-header-key-sort/post-header-key-sort.sts
new file mode 100644
index 00000000..eb663626
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-header-key-sort/post-header-key-sort.sts
@@ -0,0 +1,4 @@
+AWS4-HMAC-SHA256
+20150830T123600Z
+20150830/us-east-1/service/aws4_request
+9368318c2967cf6de74404b30c65a91e8f6253e0a8659d6d5319f1a812f87d65 \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-header-value-case/post-header-value-case.authz b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-header-value-case/post-header-value-case.authz
new file mode 100644
index 00000000..d9e52a37
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-header-value-case/post-header-value-case.authz
@@ -0,0 +1 @@
+AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;my-header1;x-amz-date, Signature=cdbc9802e29d2942e5e10b5bccfdd67c5f22c7c4e8ae67b53629efa58b974b7d \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-header-value-case/post-header-value-case.creq b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-header-value-case/post-header-value-case.creq
new file mode 100644
index 00000000..af824c88
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-header-value-case/post-header-value-case.creq
@@ -0,0 +1,9 @@
+POST
+/
+
+host:example.amazonaws.com
+my-header1:VALUE1
+x-amz-date:20150830T123600Z
+
+host;my-header1;x-amz-date
+e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-header-value-case/post-header-value-case.req b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-header-value-case/post-header-value-case.req
new file mode 100644
index 00000000..3f9987af
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-header-value-case/post-header-value-case.req
@@ -0,0 +1,4 @@
+POST / HTTP/1.1
+Host:example.amazonaws.com
+My-Header1:VALUE1
+X-Amz-Date:20150830T123600Z \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-header-value-case/post-header-value-case.sreq b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-header-value-case/post-header-value-case.sreq
new file mode 100644
index 00000000..99c3210c
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-header-value-case/post-header-value-case.sreq
@@ -0,0 +1,5 @@
+POST / HTTP/1.1
+Host:example.amazonaws.com
+My-Header1:VALUE1
+X-Amz-Date:20150830T123600Z
+Authorization: AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;my-header1;x-amz-date, Signature=cdbc9802e29d2942e5e10b5bccfdd67c5f22c7c4e8ae67b53629efa58b974b7d \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-header-value-case/post-header-value-case.sts b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-header-value-case/post-header-value-case.sts
new file mode 100644
index 00000000..40062c79
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-header-value-case/post-header-value-case.sts
@@ -0,0 +1,4 @@
+AWS4-HMAC-SHA256
+20150830T123600Z
+20150830/us-east-1/service/aws4_request
+d51ced243e649e3de6ef63afbbdcbca03131a21a7103a1583706a64618606a93 \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-sts-token/post-sts-header-after/post-sts-header-after.authz b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-sts-token/post-sts-header-after/post-sts-header-after.authz
new file mode 100644
index 00000000..89e572e6
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-sts-token/post-sts-header-after/post-sts-header-after.authz
@@ -0,0 +1 @@
+AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=5da7c1a2acd57cee7505fc6676e4e544621c30862966e37dddb68e92efbe5d6b \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-sts-token/post-sts-header-after/post-sts-header-after.creq b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-sts-token/post-sts-header-after/post-sts-header-after.creq
new file mode 100644
index 00000000..5c3a9434
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-sts-token/post-sts-header-after/post-sts-header-after.creq
@@ -0,0 +1,8 @@
+POST
+/
+
+host:example.amazonaws.com
+x-amz-date:20150830T123600Z
+
+host;x-amz-date
+e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-sts-token/post-sts-header-after/post-sts-header-after.req b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-sts-token/post-sts-header-after/post-sts-header-after.req
new file mode 100644
index 00000000..3dc41790
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-sts-token/post-sts-header-after/post-sts-header-after.req
@@ -0,0 +1,3 @@
+POST / HTTP/1.1
+Host:example.amazonaws.com
+X-Amz-Date:20150830T123600Z \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-sts-token/post-sts-header-after/post-sts-header-after.sreq b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-sts-token/post-sts-header-after/post-sts-header-after.sreq
new file mode 100644
index 00000000..291ed075
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-sts-token/post-sts-header-after/post-sts-header-after.sreq
@@ -0,0 +1,5 @@
+POST / HTTP/1.1
+Host:example.amazonaws.com
+X-Amz-Date:20150830T123600Z
+X-Amz-Security-Token:AQoDYXdzEPT//////////wEXAMPLEtc764bNrC9SAPBSM22wDOk4x4HIZ8j4FZTwdQWLWsKWHGBuFqwAeMicRXmxfpSPfIeoIYRqTflfKD8YUuwthAx7mSEI/qkPpKPi/kMcGdQrmGdeehM4IC1NtBmUpp2wUE8phUZampKsburEDy0KPkyQDYwT7WZ0wq5VSXDvp75YU9HFvlRd8Tx6q6fE8YQcHNVXAkiY9q6d+xo0rKwT38xVqr7ZD0u0iPPkUL64lIZbqBAz+scqKmlzm8FDrypNC9Yjc8fPOLn9FX9KSYvKTr4rvx3iSIlTJabIQwj2ICCR/oLxBA==
+Authorization: AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=5da7c1a2acd57cee7505fc6676e4e544621c30862966e37dddb68e92efbe5d6b \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-sts-token/post-sts-header-after/post-sts-header-after.sts b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-sts-token/post-sts-header-after/post-sts-header-after.sts
new file mode 100644
index 00000000..a6367039
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-sts-token/post-sts-header-after/post-sts-header-after.sts
@@ -0,0 +1,4 @@
+AWS4-HMAC-SHA256
+20150830T123600Z
+20150830/us-east-1/service/aws4_request
+553f88c9e4d10fc9e109e2aeb65f030801b70c2f6468faca261d401ae622fc87 \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-sts-token/post-sts-header-before/post-sts-header-before.authz b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-sts-token/post-sts-header-before/post-sts-header-before.authz
new file mode 100644
index 00000000..64aa046d
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-sts-token/post-sts-header-before/post-sts-header-before.authz
@@ -0,0 +1 @@
+AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date;x-amz-security-token, Signature=85d96828115b5dc0cfc3bd16ad9e210dd772bbebba041836c64533a82be05ead \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-sts-token/post-sts-header-before/post-sts-header-before.creq b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-sts-token/post-sts-header-before/post-sts-header-before.creq
new file mode 100644
index 00000000..1d5a462e
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-sts-token/post-sts-header-before/post-sts-header-before.creq
@@ -0,0 +1,9 @@
+POST
+/
+
+host:example.amazonaws.com
+x-amz-date:20150830T123600Z
+x-amz-security-token:AQoDYXdzEPT//////////wEXAMPLEtc764bNrC9SAPBSM22wDOk4x4HIZ8j4FZTwdQWLWsKWHGBuFqwAeMicRXmxfpSPfIeoIYRqTflfKD8YUuwthAx7mSEI/qkPpKPi/kMcGdQrmGdeehM4IC1NtBmUpp2wUE8phUZampKsburEDy0KPkyQDYwT7WZ0wq5VSXDvp75YU9HFvlRd8Tx6q6fE8YQcHNVXAkiY9q6d+xo0rKwT38xVqr7ZD0u0iPPkUL64lIZbqBAz+scqKmlzm8FDrypNC9Yjc8fPOLn9FX9KSYvKTr4rvx3iSIlTJabIQwj2ICCR/oLxBA==
+
+host;x-amz-date;x-amz-security-token
+e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-sts-token/post-sts-header-before/post-sts-header-before.req b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-sts-token/post-sts-header-before/post-sts-header-before.req
new file mode 100644
index 00000000..9d917755
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-sts-token/post-sts-header-before/post-sts-header-before.req
@@ -0,0 +1,4 @@
+POST / HTTP/1.1
+Host:example.amazonaws.com
+X-Amz-Date:20150830T123600Z
+X-Amz-Security-Token:AQoDYXdzEPT//////////wEXAMPLEtc764bNrC9SAPBSM22wDOk4x4HIZ8j4FZTwdQWLWsKWHGBuFqwAeMicRXmxfpSPfIeoIYRqTflfKD8YUuwthAx7mSEI/qkPpKPi/kMcGdQrmGdeehM4IC1NtBmUpp2wUE8phUZampKsburEDy0KPkyQDYwT7WZ0wq5VSXDvp75YU9HFvlRd8Tx6q6fE8YQcHNVXAkiY9q6d+xo0rKwT38xVqr7ZD0u0iPPkUL64lIZbqBAz+scqKmlzm8FDrypNC9Yjc8fPOLn9FX9KSYvKTr4rvx3iSIlTJabIQwj2ICCR/oLxBA== \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-sts-token/post-sts-header-before/post-sts-header-before.sreq b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-sts-token/post-sts-header-before/post-sts-header-before.sreq
new file mode 100644
index 00000000..37b2f041
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-sts-token/post-sts-header-before/post-sts-header-before.sreq
@@ -0,0 +1,5 @@
+POST / HTTP/1.1
+Host:example.amazonaws.com
+X-Amz-Date:20150830T123600Z
+X-Amz-Security-Token:AQoDYXdzEPT//////////wEXAMPLEtc764bNrC9SAPBSM22wDOk4x4HIZ8j4FZTwdQWLWsKWHGBuFqwAeMicRXmxfpSPfIeoIYRqTflfKD8YUuwthAx7mSEI/qkPpKPi/kMcGdQrmGdeehM4IC1NtBmUpp2wUE8phUZampKsburEDy0KPkyQDYwT7WZ0wq5VSXDvp75YU9HFvlRd8Tx6q6fE8YQcHNVXAkiY9q6d+xo0rKwT38xVqr7ZD0u0iPPkUL64lIZbqBAz+scqKmlzm8FDrypNC9Yjc8fPOLn9FX9KSYvKTr4rvx3iSIlTJabIQwj2ICCR/oLxBA==
+Authorization: AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date;x-amz-security-token, Signature=85d96828115b5dc0cfc3bd16ad9e210dd772bbebba041836c64533a82be05ead \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-sts-token/post-sts-header-before/post-sts-header-before.sts b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-sts-token/post-sts-header-before/post-sts-header-before.sts
new file mode 100644
index 00000000..bc39ccfc
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-sts-token/post-sts-header-before/post-sts-header-before.sts
@@ -0,0 +1,4 @@
+AWS4-HMAC-SHA256
+20150830T123600Z
+20150830/us-east-1/service/aws4_request
+c237e1b440d4c63c32ca95b5b99481081cb7b13c7e40434868e71567c1a882f6 \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-sts-token/readme.txt b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-sts-token/readme.txt
new file mode 100644
index 00000000..cc342823
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-sts-token/readme.txt
@@ -0,0 +1,15 @@
+A note about using temporary security credentials:
+
+You can use temporary security credentials provided by the AWS Security Token Service (AWS STS) to sign a request. The process is the same as using long-term credentials but requires an additional HTTP header or query string parameter for the security token. The name of the header or query string parameter is X-Amz-Security-Token, and the value is the session token (the string that you received from AWS STS when you obtained temporary security credentials).
+
+When you add X-Amz-Security-Token, some services require that you include this parameter in the canonical (signed) request. For other services, you add this parameter at the end, after you calculate the signature. For details see the API reference documentation for that service.
+
+The test suite has 2 examples:
+
+post-sts-header-before - The X-Amz-Security-Token header is part of the canonical request.
+
+post-sts-header-after - The X-Amz-Security-Token header is added to the request after you calculate the signature.
+
+The test suite uses this example value for X-Amz-Security-Token:
+
+AQoDYXdzEPT//////////wEXAMPLEtc764bNrC9SAPBSM22wDOk4x4HIZ8j4FZTwdQWLWsKWHGBuFqwAeMicRXmxfpSPfIeoIYRqTflfKD8YUuwthAx7mSEI/qkPpKPi/kMcGdQrmGdeehM4IC1NtBmUpp2wUE8phUZampKsburEDy0KPkyQDYwT7WZ0wq5VSXDvp75YU9HFvlRd8Tx6q6fE8YQcHNVXAkiY9q6d+xo0rKwT38xVqr7ZD0u0iPPkUL64lIZbqBAz+scqKmlzm8FDrypNC9Yjc8fPOLn9FX9KSYvKTr4rvx3iSIlTJabIQwj2ICCR/oLxBA== \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-vanilla-empty-query-value/post-vanilla-empty-query-value.authz b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-vanilla-empty-query-value/post-vanilla-empty-query-value.authz
new file mode 100644
index 00000000..44280cd7
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-vanilla-empty-query-value/post-vanilla-empty-query-value.authz
@@ -0,0 +1 @@
+AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=28038455d6de14eafc1f9222cf5aa6f1a96197d7deb8263271d420d138af7f11 \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-vanilla-empty-query-value/post-vanilla-empty-query-value.creq b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-vanilla-empty-query-value/post-vanilla-empty-query-value.creq
new file mode 100644
index 00000000..f5058d43
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-vanilla-empty-query-value/post-vanilla-empty-query-value.creq
@@ -0,0 +1,8 @@
+POST
+/
+Param1=value1
+host:example.amazonaws.com
+x-amz-date:20150830T123600Z
+
+host;x-amz-date
+e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-vanilla-empty-query-value/post-vanilla-empty-query-value.req b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-vanilla-empty-query-value/post-vanilla-empty-query-value.req
new file mode 100644
index 00000000..9157bc74
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-vanilla-empty-query-value/post-vanilla-empty-query-value.req
@@ -0,0 +1,3 @@
+POST /?Param1=value1 HTTP/1.1
+Host:example.amazonaws.com
+X-Amz-Date:20150830T123600Z \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-vanilla-empty-query-value/post-vanilla-empty-query-value.sreq b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-vanilla-empty-query-value/post-vanilla-empty-query-value.sreq
new file mode 100644
index 00000000..82af1505
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-vanilla-empty-query-value/post-vanilla-empty-query-value.sreq
@@ -0,0 +1,4 @@
+POST /?Param1=value1 HTTP/1.1
+Host:example.amazonaws.com
+X-Amz-Date:20150830T123600Z
+Authorization: AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=28038455d6de14eafc1f9222cf5aa6f1a96197d7deb8263271d420d138af7f11 \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-vanilla-empty-query-value/post-vanilla-empty-query-value.sts b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-vanilla-empty-query-value/post-vanilla-empty-query-value.sts
new file mode 100644
index 00000000..ca7cc661
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-vanilla-empty-query-value/post-vanilla-empty-query-value.sts
@@ -0,0 +1,4 @@
+AWS4-HMAC-SHA256
+20150830T123600Z
+20150830/us-east-1/service/aws4_request
+9d659678c1756bb3113e2ce898845a0a79dbbc57b740555917687f1b3340fbbd \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-vanilla-query/post-vanilla-query.authz b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-vanilla-query/post-vanilla-query.authz
new file mode 100644
index 00000000..44280cd7
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-vanilla-query/post-vanilla-query.authz
@@ -0,0 +1 @@
+AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=28038455d6de14eafc1f9222cf5aa6f1a96197d7deb8263271d420d138af7f11 \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-vanilla-query/post-vanilla-query.creq b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-vanilla-query/post-vanilla-query.creq
new file mode 100644
index 00000000..f5058d43
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-vanilla-query/post-vanilla-query.creq
@@ -0,0 +1,8 @@
+POST
+/
+Param1=value1
+host:example.amazonaws.com
+x-amz-date:20150830T123600Z
+
+host;x-amz-date
+e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-vanilla-query/post-vanilla-query.req b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-vanilla-query/post-vanilla-query.req
new file mode 100644
index 00000000..9157bc74
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-vanilla-query/post-vanilla-query.req
@@ -0,0 +1,3 @@
+POST /?Param1=value1 HTTP/1.1
+Host:example.amazonaws.com
+X-Amz-Date:20150830T123600Z \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-vanilla-query/post-vanilla-query.sreq b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-vanilla-query/post-vanilla-query.sreq
new file mode 100644
index 00000000..82af1505
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-vanilla-query/post-vanilla-query.sreq
@@ -0,0 +1,4 @@
+POST /?Param1=value1 HTTP/1.1
+Host:example.amazonaws.com
+X-Amz-Date:20150830T123600Z
+Authorization: AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=28038455d6de14eafc1f9222cf5aa6f1a96197d7deb8263271d420d138af7f11 \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-vanilla-query/post-vanilla-query.sts b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-vanilla-query/post-vanilla-query.sts
new file mode 100644
index 00000000..ca7cc661
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-vanilla-query/post-vanilla-query.sts
@@ -0,0 +1,4 @@
+AWS4-HMAC-SHA256
+20150830T123600Z
+20150830/us-east-1/service/aws4_request
+9d659678c1756bb3113e2ce898845a0a79dbbc57b740555917687f1b3340fbbd \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-vanilla/post-vanilla.authz b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-vanilla/post-vanilla.authz
new file mode 100644
index 00000000..89e572e6
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-vanilla/post-vanilla.authz
@@ -0,0 +1 @@
+AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=5da7c1a2acd57cee7505fc6676e4e544621c30862966e37dddb68e92efbe5d6b \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-vanilla/post-vanilla.creq b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-vanilla/post-vanilla.creq
new file mode 100644
index 00000000..5c3a9434
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-vanilla/post-vanilla.creq
@@ -0,0 +1,8 @@
+POST
+/
+
+host:example.amazonaws.com
+x-amz-date:20150830T123600Z
+
+host;x-amz-date
+e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-vanilla/post-vanilla.req b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-vanilla/post-vanilla.req
new file mode 100644
index 00000000..3dc41790
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-vanilla/post-vanilla.req
@@ -0,0 +1,3 @@
+POST / HTTP/1.1
+Host:example.amazonaws.com
+X-Amz-Date:20150830T123600Z \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-vanilla/post-vanilla.sreq b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-vanilla/post-vanilla.sreq
new file mode 100644
index 00000000..a5ada0d9
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-vanilla/post-vanilla.sreq
@@ -0,0 +1,4 @@
+POST / HTTP/1.1
+Host:example.amazonaws.com
+X-Amz-Date:20150830T123600Z
+Authorization: AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=5da7c1a2acd57cee7505fc6676e4e544621c30862966e37dddb68e92efbe5d6b \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-vanilla/post-vanilla.sts b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-vanilla/post-vanilla.sts
new file mode 100644
index 00000000..a6367039
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-vanilla/post-vanilla.sts
@@ -0,0 +1,4 @@
+AWS4-HMAC-SHA256
+20150830T123600Z
+20150830/us-east-1/service/aws4_request
+553f88c9e4d10fc9e109e2aeb65f030801b70c2f6468faca261d401ae622fc87 \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-x-www-form-urlencoded-parameters/post-x-www-form-urlencoded-parameters.authz b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-x-www-form-urlencoded-parameters/post-x-www-form-urlencoded-parameters.authz
new file mode 100644
index 00000000..df90ca36
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-x-www-form-urlencoded-parameters/post-x-www-form-urlencoded-parameters.authz
@@ -0,0 +1 @@
+AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=content-type;host;x-amz-date, Signature=ad1ac0c5fa1793b9b9d23894903b9ce84044d014533a633091e12c840473b393 \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-x-www-form-urlencoded-parameters/post-x-www-form-urlencoded-parameters.creq b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-x-www-form-urlencoded-parameters/post-x-www-form-urlencoded-parameters.creq
new file mode 100644
index 00000000..457b0d55
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-x-www-form-urlencoded-parameters/post-x-www-form-urlencoded-parameters.creq
@@ -0,0 +1,9 @@
+POST
+/
+Param1=value1
+content-type:application/x-www-form-urlencoded; charset=utf-8
+host:example.amazonaws.com
+x-amz-date:20150830T123600Z
+
+content-type;host;x-amz-date
+e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-x-www-form-urlencoded-parameters/post-x-www-form-urlencoded-parameters.req b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-x-www-form-urlencoded-parameters/post-x-www-form-urlencoded-parameters.req
new file mode 100644
index 00000000..36015f10
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-x-www-form-urlencoded-parameters/post-x-www-form-urlencoded-parameters.req
@@ -0,0 +1,6 @@
+POST / HTTP/1.1
+Content-Type:application/x-www-form-urlencoded; charset=utf-8
+Host:example.amazonaws.com
+X-Amz-Date:20150830T123600Z
+
+Param1=value1 \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-x-www-form-urlencoded-parameters/post-x-www-form-urlencoded-parameters.sreq b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-x-www-form-urlencoded-parameters/post-x-www-form-urlencoded-parameters.sreq
new file mode 100644
index 00000000..b8ecb899
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-x-www-form-urlencoded-parameters/post-x-www-form-urlencoded-parameters.sreq
@@ -0,0 +1,7 @@
+POST / HTTP/1.1
+Content-Type:application/x-www-form-urlencoded; charset=utf-8
+Host:example.amazonaws.com
+X-Amz-Date:20150830T123600Z
+Authorization: AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=content-type;host;x-amz-date, Signature=ad1ac0c5fa1793b9b9d23894903b9ce84044d014533a633091e12c840473b393
+
+Param1=value1 \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-x-www-form-urlencoded-parameters/post-x-www-form-urlencoded-parameters.sts b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-x-www-form-urlencoded-parameters/post-x-www-form-urlencoded-parameters.sts
new file mode 100644
index 00000000..f7e615ee
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-x-www-form-urlencoded-parameters/post-x-www-form-urlencoded-parameters.sts
@@ -0,0 +1,4 @@
+AWS4-HMAC-SHA256
+20150830T123600Z
+20150830/us-east-1/service/aws4_request
+5a4a15e664c6705d500b13808c1c0e2e83f6a4748f1110a980051440f5a0d9f9 \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-x-www-form-urlencoded/post-x-www-form-urlencoded.authz b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-x-www-form-urlencoded/post-x-www-form-urlencoded.authz
new file mode 100644
index 00000000..79077d27
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-x-www-form-urlencoded/post-x-www-form-urlencoded.authz
@@ -0,0 +1 @@
+AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=content-type;host;x-amz-date, Signature=0c3bc22986c53164a4755b4466c2e17db853888c5ddb98567e700a3ecdb262f6 \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-x-www-form-urlencoded/post-x-www-form-urlencoded.creq b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-x-www-form-urlencoded/post-x-www-form-urlencoded.creq
new file mode 100644
index 00000000..71dc9817
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-x-www-form-urlencoded/post-x-www-form-urlencoded.creq
@@ -0,0 +1,9 @@
+POST
+/
+
+content-type:application/x-www-form-urlencoded; charset=utf-8
+host:example.amazonaws.com
+x-amz-date:20150830T123600Z
+
+host;x-amz-date
+e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-x-www-form-urlencoded/post-x-www-form-urlencoded.req b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-x-www-form-urlencoded/post-x-www-form-urlencoded.req
new file mode 100644
index 00000000..78526171
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-x-www-form-urlencoded/post-x-www-form-urlencoded.req
@@ -0,0 +1,4 @@
+POST / HTTP/1.1
+Content-Type:application/x-www-form-urlencoded; charset=utf-8
+Host:example.amazonaws.com
+X-Amz-Date:20150830T123600Z \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-x-www-form-urlencoded/post-x-www-form-urlencoded.sreq b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-x-www-form-urlencoded/post-x-www-form-urlencoded.sreq
new file mode 100644
index 00000000..3e2e5718
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-x-www-form-urlencoded/post-x-www-form-urlencoded.sreq
@@ -0,0 +1,4 @@
+POST / HTTP/1.1
+Host:example.amazonaws.com
+X-Amz-Date:20150830T123600Z
+Authorization: AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=content-type;host;x-amz-date, Signature=0c3bc22986c53164a4755b4466c2e17db853888c5ddb98567e700a3ecdb262f6 \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-x-www-form-urlencoded/post-x-www-form-urlencoded.sts b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-x-www-form-urlencoded/post-x-www-form-urlencoded.sts
new file mode 100644
index 00000000..89ceae8d
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-x-www-form-urlencoded/post-x-www-form-urlencoded.sts
@@ -0,0 +1,4 @@
+AWS4-HMAC-SHA256
+20150830T123600Z
+20150830/us-east-1/service/aws4_request
+6a42edc18395dd67cc71407d4307ff29ab1646b08c1a0107edb319e08e2bc584 \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/stream_processor/gen_msgpack.sh b/fluent-bit/tests/internal/data/stream_processor/gen_msgpack.sh
new file mode 100755
index 00000000..19339318
--- /dev/null
+++ b/fluent-bit/tests/internal/data/stream_processor/gen_msgpack.sh
@@ -0,0 +1,40 @@
+#!/bin/sh
+
+echo "=== Hit ctrl-c if it runs forever!... ==="
+echo
+
+# Delete old samples file
+rm -rf samples.mp samples-subkeys.mp
+
+# Generate new msgpack file using as a source samples.json
+../../../../build/bin/fluent-bit -R ../../../../conf/parsers.conf \
+ -i tail -t samples -p path=samples.json -p parser=json \
+ -i tail -t samples-subkeys -p path=samples-subkeys.json -p parser=json \
+ -o file -m samples -p format=msgpack -p file=samples.mp \
+ -o file -m samples-subkeys -p format=msgpack -p file=samples-subkeys.mp -f 1 &
+ pid=$!
+ sleep 2
+ kill -9 $pid
+
+ # Generate new msgpack files for hopping window
+ files=$(find "samples-hw/" -type f -name "*.mp" | sort)
+
+ for file in $files
+ do
+ rm -f $file
+ done
+
+ files=$(find "samples-hw/" -type f -name "*.json" | sort)
+
+ for file in $files
+ do
+ # Generate new msgpack files for hopping window
+ echo ""
+ echo "=== Generating MessagePack file for $file ... ==="
+ ../../../../build/bin/fluent-bit -R ../../../../conf/parsers.conf \
+ -i tail -p path=$file -p parser=json \
+ -o file -p format=msgpack -p file=$(echo $file | sed s/json/mp/) -f 1 &
+ pid=$!
+ sleep 2
+ kill -9 $pid
+ done
diff --git a/fluent-bit/tests/internal/data/stream_processor/samples-hw/1.json b/fluent-bit/tests/internal/data/stream_processor/samples-hw/1.json
new file mode 100644
index 00000000..45c8aea2
--- /dev/null
+++ b/fluent-bit/tests/internal/data/stream_processor/samples-hw/1.json
@@ -0,0 +1,4 @@
+{"id": 0, "word1": "fluent" , "word2": "logging" , "bytes": 10 , "bool": true, "usage": 10}
+{"id": 1, "word1": "fluentd" , "word2": "rlz" , "bytes": 10.0 , "bool": true, "usage": 20}
+{"id": 2, "word1": "fluent-bit" , "word3": "rlz" , "bytes": 10 , "bool": true, "usage": 30}
+{"id": 3, "word1": "fluent-logger" , "word3": "" , "bytes": 10 , "bool": true, "usage": 40}
diff --git a/fluent-bit/tests/internal/data/stream_processor/samples-hw/1.mp b/fluent-bit/tests/internal/data/stream_processor/samples-hw/1.mp
new file mode 100644
index 00000000..0ea828bd
--- /dev/null
+++ b/fluent-bit/tests/internal/data/stream_processor/samples-hw/1.mp
Binary files differ
diff --git a/fluent-bit/tests/internal/data/stream_processor/samples-hw/2.json b/fluent-bit/tests/internal/data/stream_processor/samples-hw/2.json
new file mode 100644
index 00000000..870a3eb4
--- /dev/null
+++ b/fluent-bit/tests/internal/data/stream_processor/samples-hw/2.json
@@ -0,0 +1,4 @@
+{"id": 4, "word1": "forward" , "word3": "plain" , "bytes": 10 , "bool": true, "usage": 50}
+{"id": 5, "word5": "forward-protocol", "word6": "secure" , "bytes": 10 , "bool": true, "usage": 60}
+{"id": 6, "word1": "stream" , "word3": "processing" , "bytes": 10.20 , "bool": false, "usage": 70}
+{"id": 7, "word1": "edge-rocks" , "word6": "" , "bytes": 10 , "bool": true, "usage": 80}
diff --git a/fluent-bit/tests/internal/data/stream_processor/samples-hw/2.mp b/fluent-bit/tests/internal/data/stream_processor/samples-hw/2.mp
new file mode 100644
index 00000000..a45e1c18
--- /dev/null
+++ b/fluent-bit/tests/internal/data/stream_processor/samples-hw/2.mp
Binary files differ
diff --git a/fluent-bit/tests/internal/data/stream_processor/samples-hw/3.json b/fluent-bit/tests/internal/data/stream_processor/samples-hw/3.json
new file mode 100644
index 00000000..f1376be7
--- /dev/null
+++ b/fluent-bit/tests/internal/data/stream_processor/samples-hw/3.json
@@ -0,0 +1,3 @@
+{"id": 8, "word1": "treasure-data" , "word3": "cncf" , "bytes": 10 , "bool": true, "usage": 90}
+{"id": 9, "word1": "arm" , "word3": "linux foundation", "bytes": "10.30", "bool": false, "usage": 100}
+{"id": 10, "word1": "fluent-bit" , "word3": null , "bytes": 10 , "bool": true, "usage": 110}
diff --git a/fluent-bit/tests/internal/data/stream_processor/samples-hw/3.mp b/fluent-bit/tests/internal/data/stream_processor/samples-hw/3.mp
new file mode 100644
index 00000000..1b6cf92a
--- /dev/null
+++ b/fluent-bit/tests/internal/data/stream_processor/samples-hw/3.mp
Binary files differ
diff --git a/fluent-bit/tests/internal/data/stream_processor/samples-hw/4.json b/fluent-bit/tests/internal/data/stream_processor/samples-hw/4.json
new file mode 100644
index 00000000..72373944
--- /dev/null
+++ b/fluent-bit/tests/internal/data/stream_processor/samples-hw/4.json
@@ -0,0 +1,4 @@
+{"id": 11, "word1": "fluent" , "word2": "logging" , "bytes": 10 , "bool": true, "usage": 120}
+{"id": 12, "word1": "fluentd" , "word2": "rlz" , "bytes": 10.0 , "bool": true, "usage": 130}
+{"id": 13, "word1": "fluent-bit" , "word3": "rlz" , "bytes": 10 , "bool": true, "usage": 140}
+{"id": 14, "word1": "fluent-logger" , "word3": "" , "bytes": 10 , "bool": true, "usage": 150}
diff --git a/fluent-bit/tests/internal/data/stream_processor/samples-hw/4.mp b/fluent-bit/tests/internal/data/stream_processor/samples-hw/4.mp
new file mode 100644
index 00000000..46716103
--- /dev/null
+++ b/fluent-bit/tests/internal/data/stream_processor/samples-hw/4.mp
Binary files differ
diff --git a/fluent-bit/tests/internal/data/stream_processor/samples-hw/5.json b/fluent-bit/tests/internal/data/stream_processor/samples-hw/5.json
new file mode 100644
index 00000000..273820f1
--- /dev/null
+++ b/fluent-bit/tests/internal/data/stream_processor/samples-hw/5.json
@@ -0,0 +1,4 @@
+{"id": 15, "word1": "forward" , "word3": "plain" , "bytes": 10 , "bool": true, "usage": 160}
+{"id": 16, "word5": "forward-protocol", "word6": "secure" , "bytes": 10 , "bool": true, "usage": 170}
+{"id": 17, "word1": "stream" , "word3": "processing" , "bytes": 10.20 , "bool": false, "usage": 180}
+{"id": 18, "word1": "edge-rocks" , "word6": "" , "bytes": 10 , "bool": true, "usage": 190}
diff --git a/fluent-bit/tests/internal/data/stream_processor/samples-hw/5.mp b/fluent-bit/tests/internal/data/stream_processor/samples-hw/5.mp
new file mode 100644
index 00000000..de531bd2
--- /dev/null
+++ b/fluent-bit/tests/internal/data/stream_processor/samples-hw/5.mp
Binary files differ
diff --git a/fluent-bit/tests/internal/data/stream_processor/samples-hw/6.json b/fluent-bit/tests/internal/data/stream_processor/samples-hw/6.json
new file mode 100644
index 00000000..14de259b
--- /dev/null
+++ b/fluent-bit/tests/internal/data/stream_processor/samples-hw/6.json
@@ -0,0 +1,3 @@
+{"id": 19, "word1": "treasure-data" , "word3": "cncf" , "bytes": 10 , "bool": true, "usage": 200}
+{"id": 20, "word1": "arm" , "word3": "linux foundation", "bytes": "10.30", "bool": false, "usage": 210}
+{"id": 21, "word1": "fluent-bit" , "word3": null , "bytes": 10 , "bool": true, "usage": 220}
diff --git a/fluent-bit/tests/internal/data/stream_processor/samples-hw/6.mp b/fluent-bit/tests/internal/data/stream_processor/samples-hw/6.mp
new file mode 100644
index 00000000..cfe85fc7
--- /dev/null
+++ b/fluent-bit/tests/internal/data/stream_processor/samples-hw/6.mp
Binary files differ
diff --git a/fluent-bit/tests/internal/data/stream_processor/samples-hw/7.json b/fluent-bit/tests/internal/data/stream_processor/samples-hw/7.json
new file mode 100644
index 00000000..c30653ee
--- /dev/null
+++ b/fluent-bit/tests/internal/data/stream_processor/samples-hw/7.json
@@ -0,0 +1,4 @@
+{"id": 22, "word1": "fluent" , "word2": "logging" , "bytes": 10 , "bool": true, "usage": 230}
+{"id": 23, "word1": "fluentd" , "word2": "rlz" , "bytes": 10.0 , "bool": true, "usage": 240}
+{"id": 24, "word1": "fluent-bit" , "word3": "rlz" , "bytes": 10 , "bool": true, "usage": 250}
+{"id": 25, "word1": "fluent-logger" , "word3": "" , "bytes": 10 , "bool": true, "usage": 260}
diff --git a/fluent-bit/tests/internal/data/stream_processor/samples-hw/7.mp b/fluent-bit/tests/internal/data/stream_processor/samples-hw/7.mp
new file mode 100644
index 00000000..19f5ccf5
--- /dev/null
+++ b/fluent-bit/tests/internal/data/stream_processor/samples-hw/7.mp
Binary files differ
diff --git a/fluent-bit/tests/internal/data/stream_processor/samples-subkeys.json b/fluent-bit/tests/internal/data/stream_processor/samples-subkeys.json
new file mode 100644
index 00000000..ef4c2ba5
--- /dev/null
+++ b/fluent-bit/tests/internal/data/stream_processor/samples-subkeys.json
@@ -0,0 +1,9 @@
+{"id": 0, "map": {"sub1": {"sub2": {"color": "blue"}}}}
+{"id": 1, "map": {"sub1": {"sub2": {"color": "red"}, "sub4": "circle"}}}
+{"id": 2, "map": {"sub1": {"sub2": 123}}}
+{"id": 3, "map": {"sub1": {"sub2": "123", "stype": "a"}, "mtype": 0}}
+{"id": 4, "map": {"color": "blue"}}
+{"id": 5, "map": {"sub1": {"sub3": "100", "stype": "a", "sub4": "circle"}, "mtype": 0}}
+{"id": 6, "map": {"sub1": {"sub3": "0.50", "stype": "b", "sub4": "rectangle"}, "mtype": 1}}
+{"id": 7, "map": {"sub1": {"sub3": "5.50", "stype": "a", "sub4": "triangle"}, "mtype": 0}}
+{"id": 8, "map": {"sub1": {"sub3": "10.50", "stype": "b", "sub4": "rectangle"}, "mtype": 2}}
diff --git a/fluent-bit/tests/internal/data/stream_processor/samples-subkeys.mp b/fluent-bit/tests/internal/data/stream_processor/samples-subkeys.mp
new file mode 100644
index 00000000..374e16cd
--- /dev/null
+++ b/fluent-bit/tests/internal/data/stream_processor/samples-subkeys.mp
Binary files differ
diff --git a/fluent-bit/tests/internal/data/stream_processor/samples.json b/fluent-bit/tests/internal/data/stream_processor/samples.json
new file mode 100644
index 00000000..2cfdef6e
--- /dev/null
+++ b/fluent-bit/tests/internal/data/stream_processor/samples.json
@@ -0,0 +1,11 @@
+{"id": 0, "word1": "fluent" , "word2": "logging" , "bytes": 10 , "bool": true, "usage": 10}
+{"id": 1, "word1": "fluentd" , "word2": "rlz" , "bytes": 10.0 , "bool": true, "usage": 20}
+{"id": 2, "word1": "fluent-bit" , "word3": "rlz" , "bytes": 10 , "bool": true, "usage": 30}
+{"id": 3, "word1": "fluent-logger" , "word3": "" , "bytes": 10 , "bool": true, "usage": 40}
+{"id": 4, "word1": "forward" , "word3": "plain" , "bytes": 10 , "bool": true, "usage": 50}
+{"id": 5, "word5": "forward-protocol", "word6": "secure" , "bytes": 10 , "bool": true, "usage": 60}
+{"id": 6, "word1": "stream" , "word3": "processing" , "bytes": 10.20 , "bool": false, "usage": 70}
+{"id": 7, "word1": "edge-rocks" , "word6": "" , "bytes": 10 , "bool": true, "usage": 80}
+{"id": 8, "word1": "treasure-data" , "word3": "cncf" , "bytes": 10 , "bool": true, "usage": 90}
+{"id": 9, "word1": "arm" , "word3": "linux foundation", "bytes": "10.30", "bool": false, "usage": 100}
+{"id": 10, "word1": "fluent-bit" , "word3": null , "bytes": 10 , "bool": true, "usage": 110}
diff --git a/fluent-bit/tests/internal/data/stream_processor/samples.mp b/fluent-bit/tests/internal/data/stream_processor/samples.mp
new file mode 100644
index 00000000..8304065d
--- /dev/null
+++ b/fluent-bit/tests/internal/data/stream_processor/samples.mp
Binary files differ
diff --git a/fluent-bit/tests/internal/data/tls/certificate.pem b/fluent-bit/tests/internal/data/tls/certificate.pem
new file mode 100644
index 00000000..b7971987
--- /dev/null
+++ b/fluent-bit/tests/internal/data/tls/certificate.pem
@@ -0,0 +1,22 @@
+-----BEGIN CERTIFICATE-----
+MIIDlzCCAn+gAwIBAgIUCl24aoQh3HuyObboivsozKKOb3cwDQYJKoZIhvcNAQEL
+BQAwWzELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoM
+GEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDEUMBIGA1UEAwwLbGVvLnZjYXAubWUw
+HhcNMjIwODE3MTM0OTAyWhcNMjMwODE3MTM0OTAyWjBbMQswCQYDVQQGEwJBVTET
+MBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0cyBQ
+dHkgTHRkMRQwEgYDVQQDDAtsZW8udmNhcC5tZTCCASIwDQYJKoZIhvcNAQEBBQAD
+ggEPADCCAQoCggEBAJ2cbLtGhSeGPAXKitw+PN7U8CkaSGL7BssPFSuqtuVkJ1AH
+puluHq62FHm4UgTyJB/Iundf1kBxiOehGFr9E1fGp6kUety6iBZK3mmmX0NdXY2A
+ObqfvxTgPNVPEj+5Qp0CJW6fEBRhHrFgpf+OdKzoKnjSCbl1EyJ1wFZeSNDTx98I
+h951xEOCobcXj1qXHDd0snRW5QNKTdSnj+3amqrNqV5DrkCyyb5Rq/VQX9LIyGV8
+tOyq50mUKeK+vOu/oNwfkZqgZ5wlY/AUyPMNvd8UKitnWeQMiMmr3ImQRZLkFnoE
+2mi2Xr6ZQhmsweWbTmmUYxr+ej7L5WvBVQEkbd0CAwEAAaNTMFEwHQYDVR0OBBYE
+FGliHqbvEsRN36C2w1cCq5M/gGCRMB8GA1UdIwQYMBaAFGliHqbvEsRN36C2w1cC
+q5M/gGCRMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBACWCNohz
+YbnnDzGXMhGlfV5PywH6+gguqTrtzJlfJB/FGHDMybBvEp9gda5ssPZfPdv6aAvo
+hz2XC+uwJqYn76ZcX+LOixsqhoXJ367pT0ggMSUB/UNvGkzMFWuGerxcB5Q7xvsh
+0Ui37Ojok7Ozj9Ofk/zroJB1WG3kPDtwoo+GwNmJaDtXhOutRPxCqp5Dw+dC0N0c
+TlPCBRB17hx49Zjw+h9sv1VmOUtQcplNZgRL9lguv7eXB6y0sSdpDyDLlTBbRMGl
+uVnQ20uKnqqALRfpXDWFL8g01xPNEIfV3KctpJfu1inLWtPuklerl0yeyUh9M1f0
+zCAi0URsGEHMFA0=
+-----END CERTIFICATE-----
diff --git a/fluent-bit/tests/internal/data/tls/private_key.pem b/fluent-bit/tests/internal/data/tls/private_key.pem
new file mode 100644
index 00000000..b6a70539
--- /dev/null
+++ b/fluent-bit/tests/internal/data/tls/private_key.pem
@@ -0,0 +1,28 @@
+-----BEGIN PRIVATE KEY-----
+MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCdnGy7RoUnhjwF
+yorcPjze1PApGkhi+wbLDxUrqrblZCdQB6bpbh6uthR5uFIE8iQfyLp3X9ZAcYjn
+oRha/RNXxqepFHrcuogWSt5ppl9DXV2NgDm6n78U4DzVTxI/uUKdAiVunxAUYR6x
+YKX/jnSs6Cp40gm5dRMidcBWXkjQ08ffCIfedcRDgqG3F49alxw3dLJ0VuUDSk3U
+p4/t2pqqzaleQ65Assm+Uav1UF/SyMhlfLTsqudJlCnivrzrv6DcH5GaoGecJWPw
+FMjzDb3fFCorZ1nkDIjJq9yJkEWS5BZ6BNpotl6+mUIZrMHlm05plGMa/no+y+Vr
+wVUBJG3dAgMBAAECggEANXQ/AEkLkfsZ0lD+RXIqTNzlUttiH4fJpwbHhFbSzvvn
+xWHC/zpk15ZTXXDhCGJjVBBNBX2QeazH5N8jFoDslYF/jX2vqbrturnLswNFHeDF
+gN8zNRNGyDrBBwtZQhl/+SYoMdtqpa7GrRv9UK4s7hOTjASYXbjSM4bCI8i4Y3JX
+5iKYnejTJ4GNXm1S5g4H8QpgXxrxYU7HY1UlMBGni1BSDPb5SxANnFvgw0oypaDG
+4a0KpnUmziBtZo8wIQ7+UeStS4xikIeaT2SQM/ilplPNx0HgPHbTMeTBHQylG1JU
+ztEE04Y1F9UF7cqZnxqQLlKmIqsOepLeDOkRXjDtNQKBgQDQtWYbewCmGuo1+AR7
+wStOXpfhqdu6NxAwX+S06ZdAV7f4duMFb//6uJ/c997qwRlNvRvpPlJBdcT3qziS
+zjvR8uk2YN5N9D+ZN0Yi11vltUYVPeGpoAk4L7+Ruj5RTVqk5iZEZ1RbAv5cRIm0
+D24H4W0Cj/aldDED2pwTb9ukEwKBgQDBUv/s7gUNM3LCkNLc1EDg2adSzoUopxHU
+jIQCitXt5iFpY2Lv2tpaIz4ux4HQGwhmao2MiUYOMit0++/pFaKur66tgRQRsr7F
+uS1fpjqtS51/5uFuc1xcWjVquidZXH9u9iChrYvim6SBkxzFsbu4kXNOj4I562cm
+E2VWH5GETwKBgQCo5ePX4VbJFYbsXeXi8JRHO63V5Uv4Co+DVlcTQOYyH8q1vCBE
+Sjrxf29/tugjOllr29o2i0StzMy1UU7bHyKx6M5qP0In+71sFJshnv6ziltI3Wc9
+ilFrstho6jt8OAle4RGe0bAmZunJaX22xbXZkshRBognpTv1Tnh4ElHBGQKBgHIW
+nVohjXGg7xTLiuUvjaokSI6hugunrOoWksE9VcqziPw83uJV8Y5IRiYtLvq1OVvX
+ffl1+ZXfHa5ID+kqD3uvyhIynrljFxpwkcpkuzQR77zPcDJSeis2QVfey+H8qGe/
+cLp5RJhS6d5eBxjULshZbgbqwhuURKc/wwn0T1gZAoGAXu2wqwBE8ikhpaDVFYZR
+EZe0G1iq03CtabFL0oPiddytV9jJy4Mg457tkCj58uCVkxw/8qjXEnm5OoJAzSlJ
+Goc4wh1N1rngYRbSGG+LxD0DeI/Hj3T5tWezMBLCE7J+hieUuVjOB9ELKip7aTj3
+KLNVudOATWYGXyvzux6ciu0=
+-----END PRIVATE KEY-----
diff --git a/fluent-bit/tests/internal/env.c b/fluent-bit/tests/internal/env.c
new file mode 100644
index 00000000..c0a72f0a
--- /dev/null
+++ b/fluent-bit/tests/internal/env.c
@@ -0,0 +1,91 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/* Fluent Bit
+ * ==========
+ * Copyright (C) 2019-2022 The Fluent Bit Authors
+ * Copyright (C) 2015-2018 Treasure Data Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <fluent-bit.h>
+#include <fluent-bit/flb_env.h>
+#include <fluent-bit/flb_sds.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include "flb_tests_internal.h"
+
+/* https://github.com/fluent/fluent-bit/issues/6313 */
+void test_translate_long_env()
+{
+ struct flb_env *env;
+ flb_sds_t buf = NULL;
+ char *long_env = "ABC_APPLICATION_TEST_TEST_ABC_FLUENT_BIT_SECRET_FLUENTD_HTTP_HOST";
+ char long_env_ra[4096] = {0};
+ char *env_val = "aaaaa";
+ char putenv_arg[4096] = {0};
+ size_t ret_size;
+ int ret;
+
+ ret_size = snprintf(&long_env_ra[0], sizeof(long_env_ra), "${%s}", long_env);
+ if (!TEST_CHECK(ret_size < sizeof(long_env_ra))) {
+ TEST_MSG("long_env_ra size error");
+ exit(1);
+ }
+ ret_size = snprintf(&putenv_arg[0], sizeof(putenv_arg), "%s=%s", long_env, env_val);
+ if (!TEST_CHECK(ret_size < sizeof(long_env_ra))) {
+ TEST_MSG("putenv_arg size error");
+ exit(1);
+ }
+
+ env = flb_env_create();
+ if (!TEST_CHECK(env != NULL)) {
+ TEST_MSG("flb_env_create failed");
+ exit(1);
+ }
+#ifndef FLB_SYSTEM_WINDOWS
+ ret = putenv(&putenv_arg[0]);
+#else
+ ret = _putenv(&putenv_arg[0]);
+#endif
+ if (!TEST_CHECK(ret == 0)) {
+ TEST_MSG("setenv failed");
+ flb_env_destroy(env);
+ exit(1);
+ }
+
+ buf = flb_env_var_translate(env, &long_env_ra[0]);
+ if (!TEST_CHECK(buf != NULL)) {
+ TEST_MSG("flb_env_var_translate failed");
+#ifndef FLB_SYSTEM_WINDOWS
+ unsetenv(long_env);
+#endif
+ flb_env_destroy(env);
+ exit(1);
+ }
+
+ if (!TEST_CHECK(strlen(buf) == strlen(env_val) && 0 == strcmp(buf, env_val))) {
+ TEST_MSG("mismatch. Got=%s expect=%s", buf, env_val);
+ }
+ flb_sds_destroy(buf);
+#ifndef FLB_SYSTEM_WINDOWS
+ unsetenv(long_env);
+#endif
+ flb_env_destroy(env);
+}
+
+
+TEST_LIST = {
+ { "translate_long_env" , test_translate_long_env},
+ { NULL, NULL }
+};
diff --git a/fluent-bit/tests/internal/error_reporter.c b/fluent-bit/tests/internal/error_reporter.c
new file mode 100644
index 00000000..9ab84475
--- /dev/null
+++ b/fluent-bit/tests/internal/error_reporter.c
@@ -0,0 +1,82 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <time.h>
+#include <monkey/mk_core/mk_list.h>
+
+#include <fluent-bit/flb_mem.h>
+#include <fluent-bit/aws/flb_aws_error_reporter.h>
+
+#include "flb_tests_internal.h"
+
+const char* file_path = "/tmp/error.log";
+const char* error_message_1 = "[engine] scheduler could not start";
+const char* error_message_2 = "[engine] scheduler could not stop";
+
+
+void test_flb_aws_error_reporter_create() {
+
+ setenv(STATUS_MESSAGE_FILE_PATH_ENV, file_path, 1);
+ struct flb_aws_error_reporter *error_reporter = flb_aws_error_reporter_create();
+ TEST_CHECK((void*) error_reporter != NULL);
+ TEST_CHECK((void*)error_reporter->file_path != NULL);
+ TEST_CHECK(flb_sds_cmp(error_reporter->file_path, file_path, strlen(file_path)) == 0);
+ TEST_CHECK(error_reporter->ttl == STATUS_MESSAGE_TTL_DEFAULT);
+ TEST_CHECK(error_reporter->max_size == STATUS_MESSAGE_MAX_BYTE_LENGTH_DEFAULT);
+ flb_aws_error_reporter_destroy(error_reporter);
+}
+
+void test_flb_aws_error_reporter_write() {
+
+ int size;
+ char error_message_3[1041];
+ for (int i = 0; i < 1040; i++) {
+ error_message_3[i] = 'a';
+ }
+ error_message_3[1040] = '\0';
+
+ setenv(STATUS_MESSAGE_FILE_PATH_ENV, file_path, 1);
+
+ struct flb_aws_error_reporter *error_reporter = flb_aws_error_reporter_create();
+ flb_aws_error_reporter_write(error_reporter, error_message_1);
+ size = mk_list_size(&error_reporter->messages);
+ TEST_CHECK(size == 1);
+ /* message same with the latest one will be combined*/
+ flb_aws_error_reporter_write(error_reporter, error_message_1);
+ size = mk_list_size(&error_reporter->messages);
+ TEST_CHECK(size == 1);
+
+ /* message different of the latest one will be combined*/
+ flb_aws_error_reporter_write(error_reporter, error_message_2);
+ size = mk_list_size(&error_reporter->messages);
+ TEST_CHECK(size == 2);
+
+ /* message larger than max size limit*/
+ flb_aws_error_reporter_write(error_reporter, error_message_3);
+ size = mk_list_size(&error_reporter->messages);
+ TEST_CHECK(size == 1);
+
+ flb_aws_error_reporter_destroy(error_reporter);
+}
+
+void test_flb_aws_error_reporter_clean() {
+
+ setenv(STATUS_MESSAGE_FILE_PATH_ENV, file_path, 1);
+ struct flb_aws_error_reporter *error_reporter = flb_aws_error_reporter_create();
+ flb_aws_error_reporter_write(error_reporter, error_message_1);
+ time_t start = time(NULL);
+ while (time(NULL) - start <= error_reporter->ttl) {
+ flb_aws_error_reporter_clean(error_reporter);
+ }
+ TEST_CHECK(mk_list_size(&error_reporter->messages) == 0);
+
+ flb_aws_error_reporter_destroy(error_reporter);
+}
+
+TEST_LIST = {
+ { "test_flb_aws_error_reporter_create", test_flb_aws_error_reporter_create},
+ {"test_flb_aws_error_reporter_write", test_flb_aws_error_reporter_write},
+ {"test_flb_aws_error_reporter_clean", test_flb_aws_error_reporter_clean},
+ { 0 }
+}; \ No newline at end of file
diff --git a/fluent-bit/tests/internal/file.c b/fluent-bit/tests/internal/file.c
new file mode 100644
index 00000000..974c2668
--- /dev/null
+++ b/fluent-bit/tests/internal/file.c
@@ -0,0 +1,48 @@
+#include <fluent-bit/flb_file.h>
+#include <fluent-bit/flb_sds.h>
+#include <string.h>
+
+#include "flb_tests_internal.h"
+#include "fluent-bit/stream_processor/flb_sp.h"
+
+#define TEXT_FILE FLB_TESTS_DATA_PATH "/data/file/text_file.txt"
+#define EMPTY_FILE FLB_TESTS_DATA_PATH "/data/file/empty_file.txt"
+
+static void check_equals(flb_sds_t result, const char *expected)
+{
+ size_t expected_len = strlen(expected);
+ size_t result_len = flb_sds_len(result);
+ TEST_CHECK(expected_len == result_len);
+ TEST_MSG("Expected length: %zu", expected_len);
+ TEST_MSG("Actual length: %zu", result_len);
+ TEST_CHECK(memcmp(result, expected, expected_len) == 0);
+ TEST_MSG("Expected: %s", expected);
+ TEST_MSG("Actual: %s", result);
+}
+
+static void test_file_read_text_file()
+{
+ flb_sds_t result = flb_file_read(TEXT_FILE);
+ check_equals(result, "Some text file\n\nline 3\n\nline 5\n");
+ flb_sds_destroy(result);
+}
+
+static void test_file_read_empty_file()
+{
+ flb_sds_t result = flb_file_read(EMPTY_FILE);
+ check_equals(result, "");
+ flb_sds_destroy(result);
+}
+
+static void test_file_read_missing()
+{
+ flb_sds_t result = flb_file_read(TEXT_FILE ".missing");
+ TEST_CHECK(result == NULL);
+}
+
+TEST_LIST = {
+ { "file_read_text_file" , test_file_read_text_file},
+ { "file_read_empty_file" , test_file_read_empty_file},
+ { "file_read_missing" , test_file_read_missing},
+ { 0 }
+};
diff --git a/fluent-bit/tests/internal/flb_event_loop.c b/fluent-bit/tests/internal/flb_event_loop.c
new file mode 100644
index 00000000..669475d7
--- /dev/null
+++ b/fluent-bit/tests/internal/flb_event_loop.c
@@ -0,0 +1,592 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+#include <fluent-bit/flb_compat.h>
+#include <fluent-bit/flb_info.h>
+#include <fluent-bit/flb_mem.h>
+#include <fluent-bit/flb_pack.h>
+#include <fluent-bit/flb_mp.h>
+#include <msgpack.h>
+
+#include "flb_tests_internal.h"
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <fluent-bit/flb_event_loop.h>
+#include <fluent-bit/flb_bucket_queue.h>
+#include <monkey/mk_core/mk_list.h>
+
+#define EVENT_LOOP_TEST_PRIORITIES 7
+#define EVENT_LOOP_MAX_EVENTS 64
+#ifdef _WIN32
+ #define TIME_EPSILON_MS 30
+#elif FLB_SYSTEM_MACOS
+ #define TIME_EPSILON_MS 200
+#else
+ #define TIME_EPSILON_MS 10
+#endif
+
+#define TIMER_COARSE_EPSION_MS 300
+
+struct test_evl_context {
+ struct mk_event_loop *evl;
+ struct flb_bucket_queue *bktq;
+};
+
+/*
+ * The following implements a uniform custom delayed event
+ * primarily to help Windows. It is needed to simulate
+ * another thread or network call writing to fd
+ * activating an event without disrupting the event (libevent)
+ * loop. LibEvent timeouts disrupt the libevent loop
+ * making the testcases non-homogeneous between platforms
+ */
+struct delay_worker_args {
+ int fd; /* pipefd */
+ int sec; /* seconds */
+};
+
+/* Writes to pipe after set delay. Cleans self up after pipe closure */
+void _delay_worker(void *arg) {
+ int ret;
+ uint64_t val = 1;
+ char tmp[100];
+
+ struct delay_worker_args *args = (struct delay_worker_args *) arg;
+ static int idx = 0;
+
+ sprintf(tmp, "delay-timer-%i", ++idx);
+ mk_utils_worker_rename(tmp);
+
+ /* Sleep for the delay period */
+ sleep(args->sec);
+
+ /* Send delayed event a notification */
+ flb_pipe_set_nonblocking(args->fd);
+ ret = flb_pipe_w(args->fd, &val, sizeof(uint64_t)); /* supposedly blocking */
+ if (ret == -1) {
+ flb_error("Delayed event: unable to trigger event via write to pipe");
+ }
+
+ /* Clean up */
+ flb_pipe_close(args->fd);
+ flb_free(args);
+}
+
+void test_timeout_create(struct mk_event_loop *loop,
+ time_t sec, long nsec, void *data)
+{
+ flb_pipefd_t fd[2];
+ int ret;
+ pthread_t tid;
+
+ ret = flb_pipe_create(fd);
+ if (ret == -1) {
+ flb_error("pipe creation failure");
+ return;
+ }
+
+ /*
+ * Spin up another thread to keep
+ * track of our delay (don't let register in event loop (like libevent))
+ */
+ struct delay_worker_args *args = flb_malloc(sizeof(struct delay_worker_args));
+
+ /* Register write end of data pipe */
+ args->fd = fd[1];
+ args->sec = sec;
+ ret = mk_utils_worker_spawn(_delay_worker, args, &tid); /* worker handles freeing up args. */
+
+ /* Convert read end to monkey event */
+ MK_EVENT_NEW(data);
+ mk_event_add(loop, fd[0], MK_EVENT_NOTIFICATION, MK_EVENT_READ, data);
+}
+
+void test_timeout_destroy(struct mk_event_loop *loop, void *data)
+{
+ struct mk_event *event = (struct mk_event *) data;
+ mk_event_del(loop, event);
+ flb_pipe_close(event->fd);
+}
+
+struct test_evl_context *evl_context_create()
+{
+ struct test_evl_context *ctx = flb_malloc(sizeof(struct test_evl_context));
+ ctx->evl = mk_event_loop_create(EVENT_LOOP_MAX_EVENTS);
+ ctx->bktq = flb_bucket_queue_create(EVENT_LOOP_TEST_PRIORITIES);
+ return ctx;
+}
+
+void evl_context_destroy(struct test_evl_context *ctx)
+{
+ flb_bucket_queue_destroy(ctx->bktq);
+ mk_event_loop_destroy(ctx->evl);
+ flb_free(ctx);
+}
+
+void test_simple_timeout_1000ms()
+{
+ struct test_evl_context *ctx;
+
+ struct flb_time start_time;
+ struct flb_time end_time;
+ struct flb_time diff_time;
+ uint64_t elapsed_time_flb;
+ int target;
+
+#ifdef _WIN32
+ WSADATA wsa_data;
+ WSAStartup(0x0201, &wsa_data);
+#endif
+
+ target = 1000;
+ ctx = evl_context_create();
+
+ flb_time_get(&start_time);
+
+ mk_event_wait_2(ctx->evl, target);
+
+ flb_time_get(&end_time);
+ flb_time_diff(&end_time, &start_time, &diff_time);
+ elapsed_time_flb = flb_time_to_nanosec(&diff_time) / 1000000;
+ TEST_CHECK(elapsed_time_flb < target + TIME_EPSILON_MS
+ && elapsed_time_flb > target - TIME_EPSILON_MS);
+ TEST_MSG("Target time failed for mk_wait_2. Expect %d ms. Waited %d ms\n", target,
+ (int) elapsed_time_flb);
+
+ evl_context_destroy(ctx);
+
+#ifdef _WIN32
+ WSACleanup();
+#endif
+}
+
+/*
+ * Non-block wait: 0ms, no event
+ * Add timer - 1s (very inexact)
+ * Non-block wait: 0ms, no event
+ * Blocking wait with 2.1s timeout: ~1s (very inexact), 1event
+ * Non-blocking wait: 0ms, 1 event
+ * Remove timer event
+ * Blocking wait with 2.1s timeout: 2.1s, no event
+ */
+void test_non_blocking_and_blocking_timeout()
+{
+ struct test_evl_context *ctx;
+
+ struct mk_event event = {0};
+
+ struct flb_time start_time;
+ struct flb_time end_time;
+ struct flb_time diff_time;
+ uint64_t elapsed_time_flb;
+ int n_events;
+
+ int target;
+ int wait_2_timeout = 2100;
+#ifdef _WIN32
+ WSADATA wsa_data;
+ WSAStartup(0x0201, &wsa_data);
+#endif
+
+ ctx = evl_context_create();
+
+ /* Non blocking wait -- no event */
+ target = 0;
+ flb_time_get(&start_time);
+ n_events = mk_event_wait_2(ctx->evl, 0);
+ flb_time_get(&end_time);
+
+ flb_time_diff(&end_time, &start_time, &diff_time);
+ elapsed_time_flb = flb_time_to_nanosec(&diff_time) / 1000000;
+ TEST_CHECK(elapsed_time_flb <= target + TIME_EPSILON_MS);
+ TEST_MSG("Target time failed for mk_wait_2. Expect %d ms. Waited %d ms\n", target,
+ (int) elapsed_time_flb);
+ TEST_CHECK(n_events == 0);
+
+ /* Add somewhat inexact 1 second timer */
+ target = 1000;
+ event.mask = MK_EVENT_EMPTY;
+ event.status = MK_EVENT_NONE;
+ test_timeout_create(ctx->evl, target / 1000, 0, &event);
+
+ /* Non blocking wait -- one event */
+ target = 0;
+ flb_time_get(&start_time);
+ n_events = mk_event_wait_2(ctx->evl, 0);
+ flb_time_get(&end_time);
+
+ flb_time_diff(&end_time, &start_time, &diff_time);
+ elapsed_time_flb = flb_time_to_nanosec(&diff_time) / 1000000;
+ TEST_CHECK(elapsed_time_flb <= target + TIME_EPSILON_MS);
+ TEST_MSG("Target time failed for mk_wait_2. Expect %d ms. Waited %d ms\n", target,
+ (int) elapsed_time_flb);
+ TEST_CHECK(n_events == 0);
+
+ /* Blocking wait with unused timeout */
+ target = 1000;
+ flb_time_get(&start_time);
+ n_events = mk_event_wait_2(ctx->evl, wait_2_timeout);
+ flb_time_get(&end_time);
+
+ flb_time_diff(&end_time, &start_time, &diff_time);
+ elapsed_time_flb = flb_time_to_nanosec(&diff_time) / 1000000;
+ TEST_CHECK(elapsed_time_flb < target + TIMER_COARSE_EPSION_MS
+ && elapsed_time_flb > 100); /* accommodate for timer inaccuracy */
+ TEST_MSG("Target time failed for mk_wait_2. Expect %d ms. Waited %d ms\n", target,
+ (int) elapsed_time_flb);
+ TEST_CHECK(n_events == 1);
+
+ /* Non blocking wait -- one event */
+ target = 0;
+ flb_time_get(&start_time);
+ n_events = mk_event_wait_2(ctx->evl, 0);
+ flb_time_get(&end_time);
+
+ flb_time_diff(&end_time, &start_time, &diff_time);
+ elapsed_time_flb = flb_time_to_nanosec(&diff_time) / 1000000;
+ TEST_CHECK(elapsed_time_flb <= target + TIME_EPSILON_MS);
+ TEST_MSG("Target time failed for mk_wait_2. Expect %d ms. Waited %d ms\n", target,
+ (int) elapsed_time_flb);
+ TEST_CHECK(n_events == 1);
+
+ /* Remove triggered 1s timer event */
+ test_timeout_destroy(ctx->evl, &event);
+
+ /* Blocking wait, used timeout */
+ target = wait_2_timeout;
+ flb_time_get(&start_time);
+ n_events = mk_event_wait_2(ctx->evl, wait_2_timeout);
+ flb_time_get(&end_time);
+ flb_time_diff(&end_time, &start_time, &diff_time);
+ elapsed_time_flb = flb_time_to_nanosec(&diff_time) / 1000000;
+ TEST_CHECK(elapsed_time_flb < target + TIME_EPSILON_MS
+ && elapsed_time_flb > target - TIME_EPSILON_MS);
+ TEST_MSG("Target time failed for mk_wait_2. Expect %d ms. Waited %d ms\n", target,
+ (int) elapsed_time_flb);
+ TEST_CHECK(n_events == 0);
+
+ evl_context_destroy(ctx);
+#ifdef _WIN32
+ WSACleanup();
+#endif
+}
+
+/*
+ * Add 1s timer
+ * Infinite wait: 1 event, < 1s + epsilon
+ * Remove timer
+ */
+void test_infinite_wait()
+{
+ struct test_evl_context *ctx;
+
+ struct mk_event event = {0};
+
+ struct flb_time start_time;
+ struct flb_time end_time;
+ struct flb_time diff_time;
+ uint64_t elapsed_time_flb;
+ int n_events;
+
+ int target;
+#ifdef _WIN32
+ WSADATA wsa_data;
+ WSAStartup(0x0201, &wsa_data);
+#endif
+
+ ctx = evl_context_create();
+
+ /* Add somewhat inexact 1 second timer */
+ target = 1000;
+ test_timeout_create(ctx->evl, target / 1000, 0, &event);
+
+ /* Infinite wait -- 1 event */
+ target = 1000;
+ flb_time_get(&start_time);
+ n_events = mk_event_wait(ctx->evl);
+ flb_time_get(&end_time);
+
+ flb_time_diff(&end_time, &start_time, &diff_time);
+ elapsed_time_flb = flb_time_to_nanosec(&diff_time) / 1000000;
+ TEST_CHECK(elapsed_time_flb < target + TIMER_COARSE_EPSION_MS
+ && elapsed_time_flb > 100); /* expect timer to be inexact */
+ TEST_MSG("Target time failed for mk_wait_2. Expect %d ms. Waited %d ms\n", target,
+ (int) elapsed_time_flb);
+ TEST_CHECK(n_events == 1);
+
+ /* Remove triggered 1s timer event */
+ test_timeout_destroy(ctx->evl, &event);
+
+ evl_context_destroy(ctx);
+#ifdef _WIN32
+ WSACleanup();
+#endif
+}
+
+void synchronize_tests()
+{
+ test_non_blocking_and_blocking_timeout();
+ test_infinite_wait();
+}
+
+/*
+ * Add non-delayed and delayed timers of varying priority to priority event loop, and
+ * verify timers are processed by order of priority and order of activation. Delete also
+ * checked by deleting events in several cases and confirming deleted events are not
+ * processed.
+ *
+ * Method:
+ * Add n_timers / 2 non-delayed timers
+ * delete 1/4th of the non-delayed timers
+ * Wait for non_delayed timers to activate
+ * delete 1/4th of the non-delayed timers
+ * Process events with mk_event_loop deleting each processed event
+ * Add n_timers / 2 non-delayed timers
+ * Add n_timers / 2 2s delayed timers
+ * Wait for non_delayed timers to activate
+ * Start looping though processing the events
+ * on the first iteration, (after initial events are tracked)
+ * Delete 1/2 of the non-delayed timers
+ * Wait for the delayed timers to activate
+ * Check that deleted events are not processed
+ * Check that non-delayed timers which are tracked first are processed before
+ * non-delayed events.
+ *
+ * Summary:
+ * Track priorities and confirm that all added events were processed
+ * Verify non-delayed timers are triggered before delayed timers
+ * Confirm delete properly deletes events before ready, after ready,
+ * and after tracked by event loop.
+ */
+void event_loop_stress_priority_add_delete()
+{
+ struct test_evl_context *ctx;
+
+ const int n_timers = EVENT_LOOP_MAX_EVENTS;
+ struct mk_event events[EVENT_LOOP_MAX_EVENTS] = {0};
+ struct mk_event *event_cronology[EVENT_LOOP_TEST_PRIORITIES] = {0}; /* event loop priority fifo */
+ int priority_cronology = 0;
+
+ struct mk_event *event;
+ int immediate_timers[EVENT_LOOP_TEST_PRIORITIES] = {0}; /* by priority */
+ int delayed_timers[EVENT_LOOP_TEST_PRIORITIES] = {0}; /* by priority */
+
+ int immediate_timers_triggered[EVENT_LOOP_TEST_PRIORITIES] = {0};
+ int delayed_timers_triggered[EVENT_LOOP_TEST_PRIORITIES] = {0};
+
+ int priority;
+ int n_events;
+ int target;
+
+ int i;
+ int j;
+ int ret = 0;
+ int immediate_timer_count;
+#ifdef _WIN32
+ WSADATA wsa_data;
+ WSAStartup(0x0201, &wsa_data);
+#endif
+
+ ctx = evl_context_create();
+ srand(20);
+
+ /* Add timers with no delay */
+ for (i = 0; i < n_timers / 2; ++i) {
+ priority = rand() % EVENT_LOOP_TEST_PRIORITIES;
+ target = 0;
+ memset(&events[i], 0, sizeof(struct mk_event));
+ test_timeout_create(ctx->evl, 0, 0, &events[i]);
+ events[i].priority = priority;
+ ++immediate_timers[priority];
+ }
+
+ usleep(400000); /* sleep 400 milliseconds for the 0delay events to register */
+
+ /* Remove the first n/8 events */
+ for (i = 0; i < n_timers / 8; ++i) {
+ test_timeout_destroy(ctx->evl, &events[i]);
+ --immediate_timers[(int) events[i].priority];
+ }
+
+ /* Wait on the no delay timers */
+ n_events = mk_event_wait(ctx->evl);
+ TEST_CHECK(n_events == n_timers / 2 - n_timers / 8);
+ TEST_MSG("Expected %i ready events from the no delay timers. Recieved %i",
+ n_timers / 2 - n_timers / 8, ret);
+
+ /* Remove the first n/8 events */
+ for (i = n_timers / 8; i < n_timers / 4; ++i) {
+ test_timeout_destroy(ctx->evl, &events[i]);
+ --immediate_timers[(int) events[i].priority];
+ }
+
+ i = 0;
+ do { /* variable closure */
+ flb_event_priority_live_foreach(event, ctx->bktq, ctx->evl, n_timers) {
+ /* check priority cronology */
+ TEST_CHECK(event->priority >= priority_cronology);
+ TEST_MSG("Priority event loop processed events out of order.");
+ priority_cronology = event->priority;
+
+ /* check none of the deleted records appear */
+ TEST_CHECK(event >= &events[n_timers / 4]);
+ TEST_MSG("Deleted event appeared in priority event loop.");
+
+ /* update records */
+
+ /* delete event */
+ test_timeout_destroy(ctx->evl, event);
+
+ /* update records */
+ test_timeout_destroy(ctx->evl, event);
+ if (event < &events[n_timers/2]) {
+ /* immediate timer */
+ --immediate_timers[(int) event->priority];
+ ++immediate_timers_triggered[(int) event->priority];
+ }
+ else {
+ /* delayed timer */
+ --delayed_timers[(int) event->priority];
+ ++delayed_timers_triggered[(int) event->priority];
+ }
+ ++i;
+ }
+ } while (0);
+ TEST_CHECK(i == n_timers / 4);
+ TEST_MSG("Not all no-wait timers activated");
+
+ /* verify number of immediate timers triggered */
+ for (i = 0; i < EVENT_LOOP_TEST_PRIORITIES; ++i) {
+ TEST_CHECK(immediate_timers[i] == 0);
+ TEST_MSG("Priority event register and triggered mismatch for priority %i. "
+ "Remaining: %i out of: %i", i, immediate_timers[i],
+ immediate_timers_triggered[i]);
+ }
+
+ /* Re-add timers with no delay */
+ for (i = 0; i < n_timers / 2; ++i) {
+ priority = rand() % EVENT_LOOP_TEST_PRIORITIES;
+ target = 0;
+ memset(&events[i], 0, sizeof(struct mk_event));
+ test_timeout_create(ctx->evl, target, 0, &events[i]);
+ events[i].priority = priority;
+ ++immediate_timers[priority];
+ }
+
+ usleep(400000); /* sleep 200 milliseconds for the 0delay events to register */
+
+ /* Add timers with delay */
+ for (i = n_timers / 2; i < n_timers; ++i) {
+ priority = rand() % EVENT_LOOP_TEST_PRIORITIES;
+ target = 2; /* 2 second delay */
+ memset(&events[i], 0, sizeof(struct mk_event));
+ test_timeout_create(ctx->evl, target, 0, &events[i]);
+ events[i].priority = priority;
+ ++delayed_timers[priority];
+ }
+
+ /* Wait on the timers */
+ n_events = mk_event_wait(ctx->evl);
+ TEST_CHECK(n_events == n_timers / 2);
+ TEST_MSG("Expected %i ready events from the no delay timers. Recieved %i", n_timers / 2, ret);
+ j = 0;
+ priority_cronology = 0;
+ do { /* variable closure */
+ flb_event_priority_live_foreach(event, ctx->bktq, ctx->evl, n_timers) {
+
+ /* first round, delete half of all 0delay timers */
+ if (j == 0) {
+
+ /* this tests propper removal from bucket queue */
+ for (i = 0; i < n_timers/4; ++i) {
+ if (&events[i] == event) {
+ continue;
+ }
+ test_timeout_destroy(ctx->evl, &events[i]);
+ --immediate_timers[(int) events[i].priority];
+ }
+
+ /* check priority cronology */
+ TEST_CHECK(event->priority >= priority_cronology);
+ priority_cronology = event->priority;
+
+ /* delete actual event */
+ test_timeout_destroy(ctx->evl, event);
+ --immediate_timers[(int) event->priority];
+ TEST_CHECK(event < &events[n_timers/2]);
+ TEST_MSG("Processed delayed timer first. Should process immediate timer first");
+
+ /* delay for the delayed timers to register */
+ usleep(2500000); /* 2.5 seconds */
+ ++j;
+ continue;
+ }
+
+ /* validate fifo nature. inspect cross from no-timeout to timeout event */
+ /* check event fifo cronology */
+ /* (priority A) [immediate timer] -> [delay timer]: check all immediate timers processed */
+ if (event_cronology[(int) event->priority] < &events[n_timers / 2]
+ && event >= &events[n_timers / 2]) {
+ /* verify that all of the immediate_timers in priority have been removed */
+ immediate_timer_count = 0;
+ for (i = 0; i < EVENT_LOOP_TEST_PRIORITIES; ++i) {
+ immediate_timer_count += immediate_timers[i];
+ }
+ TEST_CHECK(immediate_timers[(int) event->priority] == 0);
+ TEST_MSG("immediate timer events are not all processed before delayed timer events for priority %i", event->priority);
+ }
+ /* check for non fifo behavior */
+ /* (priority A) [delay timer] -> [immediate timer]: disallow */
+ if (!TEST_CHECK(!(event_cronology[(int) event->priority] >= &events[n_timers / 2]
+ && event < &events[n_timers / 2]))) {
+ TEST_MSG("Non fifo behavior within priority. Delayed event processed before immediate event.");
+ }
+ event_cronology[(int) event->priority] = event;
+
+ /* check priority cronology */
+ TEST_CHECK(event->priority >= priority_cronology);
+ TEST_MSG("Priority event loop processed events out of order.");
+ priority_cronology = event->priority;
+
+ /* verify none of the deleted timers are processed */
+ TEST_CHECK(event >= &events[n_timers / 4]);
+ TEST_MSG("Processed a deleted timer. Delete performed after event is registered in the event loop bucket queue.");
+
+ /* update records */
+ test_timeout_destroy(ctx->evl, event);
+ if (event < &events[n_timers/2]) {
+ /* immediate timer */
+ --immediate_timers[(int) event->priority];
+ }
+ else {
+ /* delayed timer */
+ --delayed_timers[(int) event->priority];
+ }
+ ++j;
+ }
+ } while (0);
+
+ /* validate all timers processed */
+ for (i = 0; i < EVENT_LOOP_TEST_PRIORITIES; ++i) {
+ TEST_CHECK(immediate_timers[i] == 0);
+ TEST_MSG("Not all immediate timers processed");
+ }
+ for (i = 0; i < EVENT_LOOP_TEST_PRIORITIES; ++i) {
+ TEST_CHECK(delayed_timers[i] == 0);
+ TEST_MSG("Not all delayed timers processed");
+ }
+
+ evl_context_destroy(ctx);
+#ifdef _WIN32
+ WSACleanup();
+#endif
+}
+
+TEST_LIST = {
+ {"test_simple_timeout_1000ms", test_simple_timeout_1000ms},
+ {"test_non_blocking_and_blocking_timeout", test_non_blocking_and_blocking_timeout},
+ {"test_infinite_wait", test_infinite_wait},
+ {"event_loop_stress_priority_add_delete", event_loop_stress_priority_add_delete},
+ { 0 }
+};
diff --git a/fluent-bit/tests/internal/flb_tests_internal.h.in b/fluent-bit/tests/internal/flb_tests_internal.h.in
new file mode 100644
index 00000000..82a8cd8c
--- /dev/null
+++ b/fluent-bit/tests/internal/flb_tests_internal.h.in
@@ -0,0 +1,43 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/* Fluent Bit
+ * ==========
+ * Copyright (C) 2019-2020 The Fluent Bit Authors
+ * Copyright (C) 2015-2018 Treasure Data Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef FLB_TEST_INTERNAL_H
+#define FLB_TEST_INTERNAL_H
+
+#ifdef FLB_TEST_INIT_NOP
+static inline void init_nop() {};
+#define TEST_INIT { init_nop(); }
+#else
+#include "../include/flb_tests_initialize_tls.h"
+#define TEST_INIT { flb_test_env_config_init(); }
+#endif
+
+#ifdef FLB_TEST_FINI_NOP
+static inline void fini_nop() {};
+#define TEST_FINI { fini_nop(); }
+#else
+#include "../include/flb_tests_initialize_tls.h"
+#define TEST_FINI { flb_test_env_config_destroy(); }
+#endif
+
+#include "../lib/acutest/acutest.h"
+#define FLB_TESTS_DATA_PATH "@FLB_TESTS_DATA_PATH@"
+
+#endif
diff --git a/fluent-bit/tests/internal/flb_time.c b/fluent-bit/tests/internal/flb_time.c
new file mode 100644
index 00000000..a4e2980b
--- /dev/null
+++ b/fluent-bit/tests/internal/flb_time.c
@@ -0,0 +1,335 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/* Fluent Bit
+ * ==========
+ * Copyright (C) 2019-2021 The Fluent Bit Authors
+ * Copyright (C) 2015-2018 Treasure Data Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <fluent-bit.h>
+#include <fluent-bit/flb_time.h>
+#include <fluent-bit/flb_pack.h>
+#include <mpack/mpack.h>
+#include <msgpack.h>
+#include <msgpack/timestamp.h>
+#include "flb_tests_internal.h"
+
+#define SEC_32BIT 1647061992 /* 0x622c2be8 */
+#define NSEC_32BIT 123000000 /* 123ms 0x0754d4c0 */
+#define D_SEC 1647061992.123;
+const char eventtime[8] = {0x62, 0x2c, 0x2b, 0xe8, 0x07, 0x54, 0xd4, 0xc0 };
+
+void test_to_nanosec()
+{
+ uint64_t expect = 123000000456;
+ uint64_t ret;
+ struct flb_time tm;
+
+ flb_time_set(&tm, 123, 456);
+
+ ret = flb_time_to_nanosec(&tm);
+ if (!TEST_CHECK(ret == expect)) {
+ TEST_MSG("given =%" PRIu64, ret);
+ TEST_MSG("expect =%" PRIu64, expect);
+ }
+}
+
+/* https://github.com/fluent/fluent-bit/issues/5215 */
+void test_append_to_mpack_v1() {
+ mpack_writer_t writer;
+ char *data;
+ size_t size;
+ struct flb_time tm;
+ int ret;
+
+ msgpack_zone mempool;
+ msgpack_object ret_obj;
+ size_t off = 0;
+
+ flb_time_set(&tm, 123, 456);
+ mpack_writer_init_growable(&writer, &data, &size);
+
+ ret = flb_time_append_to_mpack(&writer, &tm, FLB_TIME_ETFMT_V1_FIXEXT);
+ if (!TEST_CHECK(ret == 0)) {
+ TEST_MSG("flb_time_append_to_mpack failed");
+ mpack_writer_destroy(&writer);
+ flb_free(data);
+ exit(EXIT_FAILURE);
+ }
+ mpack_writer_destroy(&writer);
+
+ msgpack_zone_init(&mempool, 1024);
+ ret = msgpack_unpack(data, size, &off, &mempool, &ret_obj);
+ if (!TEST_CHECK(ret == MSGPACK_UNPACK_SUCCESS)) {
+ TEST_MSG("unpack failed ret = %d", ret);
+ msgpack_zone_destroy(&mempool);
+ flb_free(data);
+ exit(EXIT_FAILURE);
+ }
+
+ if (!TEST_CHECK(ret_obj.type == MSGPACK_OBJECT_EXT)) {
+ TEST_MSG("data type is not ext. type=%d", ret_obj.type);
+ msgpack_zone_destroy(&mempool);
+ flb_free(data);
+ exit(EXIT_FAILURE);
+ }
+ if (!TEST_CHECK(ret_obj.via.ext.type == 0)) {
+ TEST_MSG("ext type is not 0. ext type=%d", ret_obj.via.ext.type);
+ msgpack_zone_destroy(&mempool);
+ flb_free(data);
+ exit(EXIT_FAILURE);
+ }
+ msgpack_zone_destroy(&mempool);
+ flb_free(data);
+}
+
+void test_msgpack_to_time_int()
+{
+ struct flb_time tm;
+ int64_t expect = SEC_32BIT;
+ int ret;
+
+ msgpack_packer mp_pck;
+ msgpack_sbuffer mp_sbuf;
+ msgpack_unpacked result;
+
+ msgpack_object tm_obj;
+
+ /* create int object*/
+ msgpack_sbuffer_init(&mp_sbuf);
+ msgpack_packer_init(&mp_pck, &mp_sbuf, msgpack_sbuffer_write);
+ msgpack_pack_int(&mp_pck, expect);
+
+ msgpack_unpacked_init(&result);
+ msgpack_unpack_next(&result, mp_sbuf.data, mp_sbuf.size, NULL);
+
+ tm_obj = result.data;
+ ret = flb_time_msgpack_to_time(&tm, &tm_obj);
+ if(!TEST_CHECK(ret == 0)) {
+ TEST_MSG("flb_time_msgpack_to_time failed");
+ exit(EXIT_FAILURE);
+ }
+
+ if (!TEST_CHECK(tm.tm.tv_sec == expect && tm.tm.tv_nsec == 0)) {
+ TEST_MSG("got %ld.%ld, expect %ld.%d", tm.tm.tv_sec, tm.tm.tv_nsec, expect, 0);
+ }
+
+ msgpack_sbuffer_destroy(&mp_sbuf);
+ msgpack_unpacked_destroy(&result);
+}
+
+void test_msgpack_to_time_double()
+{
+ struct flb_time tm;
+ double d_time = D_SEC;
+ int64_t expect_sec = SEC_32BIT;
+ int64_t expect_nsec = NSEC_32BIT;
+
+ int ret;
+
+ msgpack_packer mp_pck;
+ msgpack_sbuffer mp_sbuf;
+ msgpack_unpacked result;
+
+ msgpack_object tm_obj;
+
+ /* create int object*/
+ msgpack_sbuffer_init(&mp_sbuf);
+ msgpack_packer_init(&mp_pck, &mp_sbuf, msgpack_sbuffer_write);
+ msgpack_pack_double(&mp_pck, d_time);
+
+ msgpack_unpacked_init(&result);
+ msgpack_unpack_next(&result, mp_sbuf.data, mp_sbuf.size, NULL);
+
+ tm_obj = result.data;
+ ret = flb_time_msgpack_to_time(&tm, &tm_obj);
+ if(!TEST_CHECK(ret == 0)) {
+ TEST_MSG("flb_time_msgpack_to_time failed");
+ exit(EXIT_FAILURE);
+ }
+
+ if (!TEST_CHECK(tm.tm.tv_sec == expect_sec &&
+ llabs(tm.tm.tv_nsec - expect_nsec ) < 10000 /* 10us*/)) {
+ TEST_MSG("got %ld.%ld, expect %ld.%ld", tm.tm.tv_sec, tm.tm.tv_nsec, expect_sec, expect_nsec);
+ }
+
+ msgpack_sbuffer_destroy(&mp_sbuf);
+ msgpack_unpacked_destroy(&result);
+}
+
+void test_msgpack_to_time_eventtime()
+{
+ struct flb_time tm;
+ int64_t expect_sec = SEC_32BIT;
+ int64_t expect_nsec = NSEC_32BIT;
+ char ext_data[8] = {0};
+ int ret;
+
+ msgpack_packer mp_pck;
+ msgpack_sbuffer mp_sbuf;
+ msgpack_unpacked result;
+
+ msgpack_object tm_obj;
+
+ memcpy(&ext_data[0], &eventtime[0], 8);
+
+ /* create int object*/
+ msgpack_sbuffer_init(&mp_sbuf);
+ msgpack_packer_init(&mp_pck, &mp_sbuf, msgpack_sbuffer_write);
+
+ /* https://github.com/fluent/fluentd/wiki/Forward-Protocol-Specification-v1#eventtime-ext-format */
+ msgpack_pack_ext(&mp_pck, 8/*fixext8*/, 0);
+ msgpack_pack_ext_body(&mp_pck, ext_data, sizeof(ext_data));
+
+ msgpack_unpacked_init(&result);
+ msgpack_unpack_next(&result, mp_sbuf.data, mp_sbuf.size, NULL);
+
+ tm_obj = result.data;
+ ret = flb_time_msgpack_to_time(&tm, &tm_obj);
+ if(!TEST_CHECK(ret == 0)) {
+ TEST_MSG("flb_time_msgpack_to_time failed");
+ exit(EXIT_FAILURE);
+ }
+
+ if (!TEST_CHECK(tm.tm.tv_sec == expect_sec &&
+ llabs(tm.tm.tv_nsec - expect_nsec ) < 10000 /* 10us*/)) {
+ TEST_MSG("got %ld.%ld, expect %ld.%ld", tm.tm.tv_sec, tm.tm.tv_nsec, expect_sec, expect_nsec);
+ }
+
+ msgpack_sbuffer_destroy(&mp_sbuf);
+ msgpack_unpacked_destroy(&result);
+}
+
+void test_msgpack_to_time_invalid()
+{
+ struct flb_time tm;
+ char ext_data[8] = {0x00, 0x11, 0x22, 0xaa, 0xbb, 0xcc, 0xdd, 0xee};
+ int ret;
+
+ msgpack_packer mp_pck;
+ msgpack_sbuffer mp_sbuf;
+ msgpack_unpacked result;
+
+
+ msgpack_object tm_obj;
+
+ /* create int object*/
+ msgpack_sbuffer_init(&mp_sbuf);
+ msgpack_packer_init(&mp_pck, &mp_sbuf, msgpack_sbuffer_write);
+
+ msgpack_pack_ext(&mp_pck, 5 /* invalid size */, 0);
+ msgpack_pack_ext_body(&mp_pck, ext_data, 5);
+
+ msgpack_unpacked_init(&result);
+ msgpack_unpack_next(&result, mp_sbuf.data, mp_sbuf.size, NULL);
+
+ tm_obj = result.data;
+
+ /* Check if ext */
+ TEST_CHECK(tm_obj.type == MSGPACK_OBJECT_EXT);
+ TEST_CHECK(tm_obj.via.ext.type == 0);
+ TEST_CHECK(tm_obj.via.ext.size == 5);
+
+ ret = flb_time_msgpack_to_time(&tm, &tm_obj);
+ if(!TEST_CHECK(ret != 0)) {
+ TEST_MSG("flb_time_msgpack_to_time should fail");
+ exit(EXIT_FAILURE);
+ }
+
+ msgpack_sbuffer_destroy(&mp_sbuf);
+ msgpack_unpacked_destroy(&result);
+
+
+ /* create int object*/
+ msgpack_sbuffer_init(&mp_sbuf);
+ msgpack_packer_init(&mp_pck, &mp_sbuf, msgpack_sbuffer_write);
+
+ msgpack_pack_ext(&mp_pck, 8, 10 /* invalid type */);
+ msgpack_pack_ext_body(&mp_pck, ext_data, 8);
+
+ msgpack_unpacked_init(&result);
+ msgpack_unpack_next(&result, mp_sbuf.data, mp_sbuf.size, NULL);
+
+ tm_obj = result.data;
+
+ /* Check if ext */
+ TEST_CHECK(tm_obj.type == MSGPACK_OBJECT_EXT);
+ TEST_CHECK(tm_obj.via.ext.type == 10);
+ TEST_CHECK(tm_obj.via.ext.size == 8);
+
+ ret = flb_time_msgpack_to_time(&tm, &tm_obj);
+ if(!TEST_CHECK(ret != 0)) {
+ TEST_MSG("flb_time_msgpack_to_time should fail");
+ exit(EXIT_FAILURE);
+ }
+
+ msgpack_sbuffer_destroy(&mp_sbuf);
+ msgpack_unpacked_destroy(&result);
+}
+
+void test_append_to_msgpack_eventtime()
+{
+ struct flb_time tm;
+ int ret;
+ char expect_data[8] = {0};
+
+ msgpack_packer mp_pck;
+ msgpack_sbuffer mp_sbuf;
+ msgpack_unpacked result;
+
+ msgpack_object tm_obj;
+
+ memcpy(&expect_data[0], &eventtime[0], 8);
+
+ tm.tm.tv_sec = SEC_32BIT;
+ tm.tm.tv_nsec = NSEC_32BIT;
+
+ /* create int object*/
+ msgpack_sbuffer_init(&mp_sbuf);
+ msgpack_packer_init(&mp_pck, &mp_sbuf, msgpack_sbuffer_write);
+
+ ret = flb_time_append_to_msgpack(&tm, &mp_pck, FLB_TIME_ETFMT_V1_FIXEXT);
+ if(!TEST_CHECK(ret == 0)) {
+ TEST_MSG("flb_time_append_to_msgpack failed");
+ exit(EXIT_FAILURE);
+ }
+ msgpack_unpacked_init(&result);
+ msgpack_unpack_next(&result, mp_sbuf.data, mp_sbuf.size, NULL);
+
+ tm_obj = result.data;
+
+ /* Check if Eventtime */
+ TEST_CHECK(tm_obj.type == MSGPACK_OBJECT_EXT);
+ TEST_CHECK(tm_obj.via.ext.type == 0);
+ TEST_CHECK(tm_obj.via.ext.size == 8);
+
+ if (!TEST_CHECK(memcmp(&expect_data[0], tm_obj.via.ext.ptr, 8) == 0) ) {
+ TEST_MSG("got 0x%x, expect 0x%x", *(uint32_t*)tm_obj.via.ext.ptr, *((uint32_t*)&expect_data[0]));
+ }
+
+ msgpack_sbuffer_destroy(&mp_sbuf);
+ msgpack_unpacked_destroy(&result);
+}
+
+TEST_LIST = {
+ { "flb_time_to_nanosec" , test_to_nanosec},
+ { "flb_time_append_to_mpack_v1" , test_append_to_mpack_v1},
+ { "msgpack_to_time_int" , test_msgpack_to_time_int},
+ { "msgpack_to_time_double" , test_msgpack_to_time_double},
+ { "msgpack_to_time_eventtime" , test_msgpack_to_time_eventtime},
+ { "msgpack_to_time_invalid" , test_msgpack_to_time_invalid},
+ { "append_to_msgpack_eventtime" , test_append_to_msgpack_eventtime},
+ { NULL, NULL }
+};
diff --git a/fluent-bit/tests/internal/fstore.c b/fluent-bit/tests/internal/fstore.c
new file mode 100644
index 00000000..1f4de61b
--- /dev/null
+++ b/fluent-bit/tests/internal/fstore.c
@@ -0,0 +1,84 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/* Fluent Bit
+ * ==========
+ * Copyright (C) 2019-2020 The Fluent Bit Authors
+ * Copyright (C) 2015-2018 Treasure Data Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <fluent-bit/flb_info.h>
+#include <fluent-bit/flb_fstore.h>
+#include <fluent-bit/flb_mem.h>
+#include <fluent-bit/flb_compat.h>
+
+#include <chunkio/chunkio.h>
+#include <chunkio/cio_utils.h>
+
+#include "flb_tests_internal.h"
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#ifdef FLB_SYSTEM_WINDOWS
+/* Not yet implemented! */
+#else
+#define FSF_STORE_PATH "/tmp/flb-fstore"
+#endif
+
+void cb_all()
+{
+ int ret;
+ void *out_buf;
+ size_t out_size;
+ struct stat st_data;
+ struct flb_fstore *fs;
+ struct flb_fstore_stream *st;
+ struct flb_fstore_file *fsf;
+
+ cio_utils_recursive_delete(FSF_STORE_PATH);
+
+ fs = flb_fstore_create(FSF_STORE_PATH, FLB_FSTORE_FS);
+ TEST_CHECK(fs != NULL);
+
+ st = flb_fstore_stream_create(fs, "abc");
+ TEST_CHECK(st != NULL);
+
+ fsf = flb_fstore_file_create(fs, st, "example.txt", 100);
+ TEST_CHECK(fsf != NULL);
+ if (!fsf) {
+ return;
+ }
+
+ ret = stat(FSF_STORE_PATH "/abc/example.txt", &st_data);
+ TEST_CHECK(ret == 0);
+
+ ret = flb_fstore_file_append(fsf, "fluent-bit\n", 11);
+ TEST_CHECK(ret == 0);
+
+ ret = flb_fstore_file_content_copy(fs, fsf, &out_buf, &out_size);
+ TEST_CHECK(ret == 0);
+
+ TEST_CHECK(memcmp(out_buf, "fluent-bit\n", 11) == 0);
+ TEST_CHECK(out_size == 11);
+ flb_free(out_buf);
+
+ flb_fstore_dump(fs);
+ flb_fstore_destroy(fs);
+}
+
+TEST_LIST = {
+ { "all" , cb_all},
+ { NULL }
+};
diff --git a/fluent-bit/tests/internal/fuzzers/CMakeLists.txt b/fluent-bit/tests/internal/fuzzers/CMakeLists.txt
new file mode 100644
index 00000000..e76be548
--- /dev/null
+++ b/fluent-bit/tests/internal/fuzzers/CMakeLists.txt
@@ -0,0 +1,60 @@
+set(UNIT_TESTS_FILES
+ aws_util_fuzzer.c
+ aws_credentials_fuzzer.c
+ base64_fuzzer.c
+ engine_fuzzer.c
+ cmetrics_decode_fuzz.c
+ config_fuzzer.c
+ config_random_fuzzer.c
+ ctrace_fuzzer.c
+ input_fuzzer.c
+ signv4_fuzzer.c
+ flb_json_fuzzer.c
+ flb_mp_fuzzer.c
+ filter_stdout_fuzzer.c
+ fstore_fuzzer.c
+ parser_fuzzer.c
+ parse_json_fuzzer.c
+ parse_logfmt_fuzzer.c
+ parse_ltsv_fuzzer.c
+ msgpack_parse_fuzzer.c
+ msgpack_to_gelf_fuzzer.c
+ multiline_fuzzer.c
+ pack_json_state_fuzzer.c
+ http_fuzzer.c
+ strp_fuzzer.c
+ utils_fuzzer.c
+ config_map_fuzzer.c
+ record_ac_fuzzer.c
+ config_yaml_fuzzer.c
+ )
+
+# Prepare list of unit tests
+foreach(source_file ${UNIT_TESTS_FILES})
+ get_filename_component(source_file_we ${source_file} NAME_WE)
+ set(source_file_we flb-it-fuzz-${source_file_we})
+
+ add_executable(
+ ${source_file_we}
+ ${source_file} local_test.c
+ )
+
+ if(FLB_JEMALLOC)
+ target_link_libraries(${source_file_we} libjemalloc ${CMAKE_THREAD_LIBS_INIT})
+ else()
+ target_link_libraries(${source_file_we} ${CMAKE_THREAD_LIBS_INIT})
+ endif()
+
+ if(FLB_STREAM_PROCESSOR)
+ target_link_libraries(${source_file_we} flb-sp)
+ endif()
+
+ target_link_libraries(${source_file_we} fluent-bit-static)
+
+ if (FLB_TESTS_OSSFUZZ)
+ add_executable(${source_file_we}_OSSFUZZ ${source_file})
+ target_link_libraries(${source_file_we}_OSSFUZZ ${CMAKE_THREAD_LIBS_INIT})
+ set_target_properties(${source_file_we}_OSSFUZZ PROPERTIES LINK_FLAGS $ENV{LIB_FUZZING_ENGINE})
+ target_link_libraries(${source_file_we}_OSSFUZZ fluent-bit-static)
+ endif()
+endforeach()
diff --git a/fluent-bit/tests/internal/fuzzers/aws_credentials_fuzzer.c b/fluent-bit/tests/internal/fuzzers/aws_credentials_fuzzer.c
new file mode 100644
index 00000000..d6ccb22e
--- /dev/null
+++ b/fluent-bit/tests/internal/fuzzers/aws_credentials_fuzzer.c
@@ -0,0 +1,132 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/* Fluent Bit
+ * ==========
+ * Copyright (C) 2023 The Fluent Bit Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdint.h>
+#include <fluent-bit.h>
+#include <fluent-bit/flb_sds.h>
+#include <fluent-bit/flb_aws_credentials.h>
+#include <fluent-bit/flb_mem.h>
+#include "flb_fuzz_header.h"
+
+int initialization_crutch()
+{
+ struct flb_config *config;
+ config = flb_config_init();
+ if (config == NULL) {
+ return -1;
+ }
+ flb_config_exit(config);
+ return 0;
+}
+
+
+void fuzz_sts(const uint8_t *data, size_t size) {
+ char *sks_response = get_null_terminated(150, &data, &size);
+
+ struct flb_aws_credentials *creds;
+ time_t expiration;
+
+ creds = flb_parse_sts_resp(sks_response, &expiration);
+ if (creds != NULL) {
+ flb_aws_credentials_destroy(creds);
+ }
+
+ if (size > 300) {
+ char *action = get_null_terminated(50, &data, &size);
+ char *role_arn = get_null_terminated(50, &data, &size);
+ char *session_name = get_null_terminated(50, &data, &size);
+ char *external_id = get_null_terminated(50, &data, &size);
+ char *identity_token = get_null_terminated(50, &data, &size);
+
+ flb_sds_t s1 = flb_sts_uri(action, role_arn, session_name,
+ external_id, identity_token);
+ if (s1 != NULL) {
+ flb_sds_destroy(s1);
+ }
+
+ flb_free(action);
+ flb_free(role_arn);
+ flb_free(session_name);
+ flb_free(external_id);
+ flb_free(identity_token);
+ }
+
+ if (sks_response != NULL) {
+ flb_free(sks_response);
+ }
+}
+
+
+void fuzz_http(const uint8_t *data, size_t size) {
+ time_t expiration;
+ struct flb_aws_credentials *creds = NULL;
+
+ char *response = get_null_terminated(250, &data, &size);
+ creds = flb_parse_http_credentials(response, 250, &expiration);
+ if (creds != NULL) {
+ flb_aws_credentials_destroy(creds);
+ }
+ flb_free(response);
+}
+
+
+void fuzz_process(const uint8_t *data, size_t size) {
+ char** tokens = NULL;
+ char *input = get_null_terminated(250, &data, &size);
+ tokens = parse_credential_process(input);
+ if (tokens != NULL) {
+ flb_free(tokens);
+ }
+ flb_free(input);
+}
+
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
+{
+ /* Set flb_malloc_mod to be fuzzer-data dependent */
+ if (size < 304) {
+ return 0;
+ }
+ flb_malloc_p = 0;
+ flb_malloc_mod = *(int*)data;
+ data += 4;
+ size -= 4;
+
+ /* Avoid division by zero for modulo operations */
+ if (flb_malloc_mod == 0) {
+ flb_malloc_mod = 1;
+ }
+ if (initialization_crutch() == -1) {
+ return 0;
+ }
+
+ const uint8_t *data_copy = data;
+ size_t size_copy = size;
+ fuzz_sts(data_copy, size_copy);
+
+ data_copy = data;
+ size_copy = size;
+ fuzz_http(data_copy, size_copy);
+
+ data_copy = data;
+ size_copy = size;
+ fuzz_process(data_copy, size_copy);
+
+ return 0;
+}
diff --git a/fluent-bit/tests/internal/fuzzers/aws_util_fuzzer.c b/fluent-bit/tests/internal/fuzzers/aws_util_fuzzer.c
new file mode 100644
index 00000000..dcaa2b9e
--- /dev/null
+++ b/fluent-bit/tests/internal/fuzzers/aws_util_fuzzer.c
@@ -0,0 +1,107 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/* Fluent Bit
+ * ==========
+ * Copyright (C) 2023 The Fluent Bit Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdint.h>
+#include <fluent-bit.h>
+#include <fluent-bit/flb_sds.h>
+#include <fluent-bit/flb_aws_util.h>
+#include <fluent-bit/flb_mem.h>
+#include "flb_fuzz_header.h"
+
+int initialization_crutch()
+{
+ struct flb_config *config;
+ config = flb_config_init();
+ if (config == NULL) {
+ return -1;
+ }
+ flb_config_exit(config);
+ return 0;
+}
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
+{
+ char *format = NULL;
+ char *tag = NULL;
+ char *tag_delimiter = NULL;
+
+ /* Set flb_malloc_mod to be fuzzer-data dependent */
+ if (size < 4) {
+ return 0;
+ }
+ flb_malloc_p = 0;
+ flb_malloc_mod = *(int*)data;
+ data += 4;
+ size -= 4;
+
+ /* Avoid division by zero for modulo operations */
+ if (flb_malloc_mod == 0) {
+ flb_malloc_mod = 1;
+ }
+
+ if (size < 300) {
+ return 0;
+ }
+
+ format = get_null_terminated(50, &data, &size);
+ tag = get_null_terminated(100, &data, &size);
+ tag_delimiter = get_null_terminated(100, &data, &size);
+
+ struct tm day = { 0, 0, 0, 15, 7, 120};
+ time_t t;
+ memset(&t, 0, sizeof(time_t));
+
+ if (format && tag && tag_delimiter) {
+ if (!initialization_crutch()) {
+ flb_sds_t s3_key_format = NULL;
+ s3_key_format = flb_get_s3_key(format, t, tag, tag_delimiter, 0);
+ if (s3_key_format) {
+ flb_sds_destroy(s3_key_format);
+ }
+ if (size > 200) {
+ char *json_val = get_null_terminated(100, &data, &size);
+ if (json_val != NULL) {
+ flb_sds_t s1 = flb_aws_error(json_val, strlen(json_val));
+ if (s1 != NULL) {
+ flb_sds_destroy(s1);
+ }
+ flb_free(json_val);
+ }
+ char *xml_val = get_null_terminated(100, &data, &size);
+ if (xml_val != NULL) {
+ flb_sds_t s2 = flb_aws_xml_error(xml_val, strlen(xml_val));
+ if (s2 != NULL) {
+ flb_sds_destroy(s2);
+ }
+ flb_free(xml_val);
+ }
+ }
+ }
+ }
+ if (format) {
+ flb_free(format);
+ }
+ if (tag) {
+ flb_free(tag);
+ }
+ if (tag_delimiter) {
+ flb_free(tag_delimiter);
+ }
+ return 0;
+}
diff --git a/fluent-bit/tests/internal/fuzzers/base64_fuzzer.c b/fluent-bit/tests/internal/fuzzers/base64_fuzzer.c
new file mode 100644
index 00000000..90c4c08f
--- /dev/null
+++ b/fluent-bit/tests/internal/fuzzers/base64_fuzzer.c
@@ -0,0 +1,20 @@
+#include <unistd.h>
+#include <stdint.h>
+#include <fluent-bit/flb_base64.h>
+#include <fluent-bit/flb_mem.h>
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
+{
+ char out[100];
+ size_t olen;
+
+ /* Set fuzzer-malloc chance of failure */
+ flb_malloc_p = 0;
+ flb_malloc_mod = 25000;
+
+ flb_base64_encode((unsigned char *) out, 100,
+ &olen, (unsigned char *)data, size);
+ flb_base64_decode((unsigned char *) out, 100,
+ &olen, (unsigned char *)data, size);
+ return 0;
+}
diff --git a/fluent-bit/tests/internal/fuzzers/cmetrics_decode_fuzz.c b/fluent-bit/tests/internal/fuzzers/cmetrics_decode_fuzz.c
new file mode 100644
index 00000000..1d1098c0
--- /dev/null
+++ b/fluent-bit/tests/internal/fuzzers/cmetrics_decode_fuzz.c
@@ -0,0 +1,69 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/* Fluent Bit
+ * ==========
+ * Copyright (C) 2015-2023 The Fluent Bit Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <cmetrics/cmt_decode_opentelemetry.h>
+#include <cmetrics/cmt_decode_prometheus.h>
+
+
+int
+LLVMFuzzerTestOneInput(const uint8_t * data, size_t size)
+{
+ struct cfl_list decoded_contexts;
+ struct cmt *cmt = NULL;
+ size_t off = 0;
+ uint8_t decider;
+ int result;
+
+ /* At least one byte is needed for deciding which decoder to use */
+ if (size < 1) {
+ return 0;
+ }
+
+ decider = data[0] % 3;
+
+ /* Adjust data pointer since the first byte is used */
+ data += 1;
+ size -= 1;
+
+ /* Fuzz a given decoder */
+ if (decider == 0) {
+ result = cmt_decode_opentelemetry_create(&decoded_contexts, data, size,
+ &off);
+ if (result == CMT_DECODE_OPENTELEMETRY_SUCCESS) {
+ cmt_decode_opentelemetry_destroy (&decoded_contexts);
+ }
+ }
+ else if (decider == 1) {
+ result = cmt_decode_msgpack_create(&cmt, (char *) data, size, &off);
+ if (result == 0) {
+ cmt_destroy(cmt);
+ }
+ }
+ else if (decider == 2) {
+ if (size == 0) {
+ return 0;
+ }
+ struct cmt_decode_prometheus_parse_opts opts;
+ result = cmt_decode_prometheus_create(&cmt, data, size, &opts);
+ if (result == CMT_DECODE_PROMETHEUS_SUCCESS) {
+ cmt_decode_prometheus_destroy(cmt);
+ }
+ }
+ return 0;
+}
diff --git a/fluent-bit/tests/internal/fuzzers/config_fuzzer.c b/fluent-bit/tests/internal/fuzzers/config_fuzzer.c
new file mode 100644
index 00000000..5512dac2
--- /dev/null
+++ b/fluent-bit/tests/internal/fuzzers/config_fuzzer.c
@@ -0,0 +1,417 @@
+#include <stdint.h>
+#include <string.h>
+#include <stdlib.h>
+#include <fluent-bit/flb_parser.h>
+#include <fluent-bit/flb_slist.h>
+#include <fluent-bit/flb_kv.h>
+#include "flb_fuzz_header.h"
+
+/* A sample of configurations */
+char conf_file[] = "# Parser: no_year\n"
+"# ===============\n"
+"# the given format don't contain the Year, this is a common\n"
+"# case on old Syslog implementations.\n"
+"#\n"
+"[PARSER]\n"
+" Name no_year\n"
+" Format json\n"
+" Time_Key time\n"
+" Time_Format %b %d %H:%M:%S\n"
+" Time_Keep On\n"
+"\n"
+"# Parser: no_year_N\n"
+"# =================\n"
+"# Just for compatibility, check a string with no year but including Nanoseconds.\n"
+"#\n"
+"[PARSER]\n"
+" Name no_year_N\n"
+" Format json\n"
+" Time_Key time\n"
+" Time_Format %b %d %H:%M:%S.%L\n"
+" Time_Keep On\n"
+"\n"
+"# Parser: no_year_NC\n"
+"# =================\n"
+"# Just for compatibility, check a string with no year but including Nanoseconds with comma as fractional separator.\n"
+"#\n"
+"[PARSER]\n"
+" Name no_year_NC\n"
+" Format json\n"
+" Time_Key time\n"
+" Time_Format %b %d %H:%M:%S,%L\n"
+" Time_Keep On\n"
+"\n"
+"# Parser: no_year_TZ\n"
+"# =================\n"
+"# Time string with no year and including timezone\n"
+"#\n"
+"[PARSER]\n"
+" Name no_year_TZ\n"
+" Format json\n"
+" Time_Key time\n"
+" Time_Format %b %d %H:%M:%S %z\n"
+" Time_Keep On\n"
+"\n"
+"# Parser: no_year_N_TZ\n"
+"# ====================\n"
+"# Time string with no year, nanoseconds and timezone\n"
+"#\n"
+"[PARSER]\n"
+" Name no_year_N_TZ\n"
+" Format json\n"
+" Time_Key time\n"
+" Time_Format %b %d %H:%M:%S.%L %z\n"
+" Time_Keep On\n"
+"\n"
+"\n"
+"# Parser: no_year_NC_TZ\n"
+"# ====================\n"
+"# Time string with no year, nanoseconds and timezone with comma as fractional separator.\n"
+"#\n"
+"[PARSER]\n"
+" Name no_year_NC_TZ\n"
+" Format json\n"
+" Time_Key time\n"
+" Time_Format %b %d %H:%M:%S,%L %z\n"
+" Time_Keep On\n"
+"\n"
+"\n"
+"# Parser: default_UTC\n"
+"# ===================\n"
+"# Time string with timezone in UTC\n"
+"#\n"
+"[PARSER]\n"
+" Name default_UTC\n"
+" Format json\n"
+" Time_Key time\n"
+" Time_Format %m/%d/%Y %H:%M:%S\n"
+" Time_Keep On\n"
+"\n"
+"# Parser: default_UTC_Z\n"
+"# =====================\n"
+"# Time string with timezone in UTC and ending Z\n"
+"#\n"
+"[PARSER]\n"
+" Name default_UTC_Z\n"
+" Format json\n"
+" Time_Key time\n"
+" Time_Format %m/%d/%Y %H:%M:%SZ\n"
+" Time_Keep On\n"
+"\n"
+"# Parser: default_UTC_N_Z\n"
+"# =======================\n"
+"# Time string with timezone in UTC, nanoseconds and ending Z\n"
+"#\n"
+"[PARSER]\n"
+" Name default_UTC_N_Z\n"
+" Format json\n"
+" Time_Key time\n"
+" Time_Format %m/%d/%Y %H:%M:%S.%LZ\n"
+" Time_Keep On\n"
+"\n"
+"# Parser: default_UTC_NC_Z\n"
+"# =======================\n"
+"# Time string with timezone in UTC, nanoseconds with comma as fractional separator and ending Z\n"
+"#\n"
+"[PARSER]\n"
+" Name default_UTC_NC_Z\n"
+" Format json\n"
+" Time_Key time\n"
+" Time_Format %m/%d/%Y %H:%M:%S,%LZ\n"
+" Time_Keep On\n"
+"\n"
+"# Parser: generic_TZ\n"
+"# ==================\n"
+"# Generic date with timezone\n"
+"#\n"
+"[PARSER]\n"
+" Name generic_TZ\n"
+" Format json\n"
+" Time_Key time\n"
+" Time_Format %m/%d/%Y %H:%M:%S %z\n"
+" Time_Keep On\n"
+"\n"
+"# Parser: generic\n"
+"# ===============\n"
+"# Generic date\n"
+"#\n"
+"[PARSER]\n"
+" Name generic\n"
+" Format json\n"
+" Time_Key time\n"
+" Time_Format %m/%d/%Y %H:%M:%S\n"
+" Time_Keep On\n"
+"\n"
+"# Parser: generic_N\n"
+"# ===============\n"
+"# Generic date with nanoseconds\n"
+"#\n"
+"[PARSER]\n"
+" Name generic_N\n"
+" Format json\n"
+" Time_Key time\n"
+" Time_Format %m/%d/%Y %H:%M:%S.%L\n"
+" Time_Keep On\n"
+"\n"
+"# Parser: generic_NC\n"
+"# ===============\n"
+"# Generic date with nanoseconds with comma as fractional separator\n"
+"#\n"
+"[PARSER]\n"
+" Name generic_NC\n"
+" Format json\n"
+" Time_Key time\n"
+" Time_Format %m/%d/%Y %H:%M:%S,%L\n"
+" Time_Keep On\n"
+"\n"
+"# Parser: generic_N_TZ\n"
+"# ====================\n"
+"# Generic date with nanoseconds and timezone\n"
+"#\n"
+"[PARSER]\n"
+" Name generic_N_TZ\n"
+" Format json\n"
+" Time_Key time\n"
+" Time_Format %m/%d/%Y %H:%M:%S.%L %z\n"
+" Time_Keep On\n"
+"\n"
+"# Parser: generic_NC_TZ\n"
+"# ====================\n"
+"# Generic date with nanoseconds with comma as fractional separator and timezone\n"
+"#\n"
+"[PARSER]\n"
+" Name generic_NC_TZ\n"
+" Format json\n"
+" Time_Key time\n"
+" Time_Format %m/%d/%Y %H:%M:%S,%L %z\n"
+" Time_Keep On\n"
+"\n"
+"# Parser: apache_error\n"
+"# ====================\n"
+"# Apache error log time format\n"
+"#\n"
+"[PARSER]\n"
+" Name apache_error\n"
+" Format json\n"
+" Time_Key time\n"
+" Time_Format %a %b %d %H:%M:%S.%L %Y\n"
+" Time_Keep On\n"
+"# Parser: mysql_quoted_stuff\n"
+"# ====================\n"
+"# Apache error log time format\n"
+"#\n"
+"[PARSER]\n"
+" Name mysql_quoted_stuff\n"
+" Format regex\n"
+" Regex ^(?<time>.*?),(?<key001>.*)$\n"
+" Time_Key time\n"
+" Time_Format %Y-%M-%S %H:%M:%S\n"
+" Time_Keep On\n"
+" Decode_Field_As mysql_quoted key001\n"
+"# Parser: REGEX_generic_NC_TZ\n"
+"# ====================\n"
+"# Generic date with nanoseconds with comma as fractional separator and timezone\n"
+"#\n"
+"[PARSER]\n"
+" Name REGEX_generic_NC_TZ\n"
+" Format regex\n"
+" Regex ^(?<key001>[^ ]*) (?<key002>[^ ]*) (?<time>.+)$\n"
+" Time_Key time\n"
+" Time_Format %m/%d/%Y %H:%M:%S,%L %z\n"
+" Time_Keep On\n"
+"\n"
+"# Parser: REGEX_apache_error\n"
+"# ====================\n"
+"# Apache error log time format\n"
+"#\n"
+"[PARSER]\n"
+" Name REGEX_apache_error\n"
+" Format regex\n"
+" Regex ^(?<key001>[^ ]*) (?<key002>[^ ]*) (?<time>.+)$\n"
+" Time_Key time\n"
+" Time_Format %a %b %d %H:%M:%S.%L %Y\n"
+" Time_Keep On\n"
+"\n"
+"\n"
+"\n"
+"# Parser: REGEX_mysql_quoted_stuff\n"
+"# ====================\n"
+"# Apache error log time format\n"
+"#\n"
+"[PARSER]\n"
+" Name REGEX_mysql_quoted_stuff\n"
+" Format regex\n"
+" Regex ^(?<time>.*?),(?<key001>.*)$\n"
+" Time_Key time\n"
+" Time_Format %Y-%M-%S %H:%M:%S\n"
+" Time_Keep On\n"
+" Decode_Field_As mysql_quoted key001\n"
+"\n"
+"\n"
+"\n"
+"# Parser: REGEX2_mysql_quoted_stuff\n"
+"# ====================\n"
+"# Apache error log time format\n"
+"#\n"
+"[PARSER]\n"
+" Name REGEX2_mysql_quoted_stuff\n"
+" Format logfmt\n"
+" Regex ^(?<time>.*?),(?<key001>.*)$\n"
+" Time_Key time\n"
+" Time_Format %Y-%M-%S %H:%M:%S\n"
+" Time_Keep On\n"
+" Decode_Field_As mysql_quoted key001\n"
+" Types A1:integer A2:string A3:bool A4:float A5:hex\n"
+"\n"
+"\n"
+"\n"
+"# Parser: REGEX3_mysql_quoted_stuff\n"
+"# ====================\n"
+"# Apache error log time format\n"
+"#\n"
+"[PARSER]\n"
+" Name REGEX3_mysql_quoted_stuff\n"
+" Format json\n"
+" Regex ^(?<time>.*?),(?<key001>.*)$\n"
+" Time_Key time\n"
+" Time_Format %Y-%M-%S %H:%M:%S\n"
+" Time_Keep On\n"
+" Decode_Field_As escaped_utf8 key001\n"
+" Types A1:integer A2:string A3:bool A4:float A5:hex\n"
+"\n"
+"\n"
+"\n"
+"# Parser: REGEX33_mysql_quoted_stuff\n"
+"# ====================\n"
+"# Apache error log time format\n"
+"#\n"
+"[PARSER]\n"
+" Name REGEX33_mysql_quoted_stuff\n"
+" Format json\n"
+" Regex ^(?<time>.*?),(?<key001>.*)$\n"
+" Time_Key time\n"
+" Time_Format %Y-%M-%S %H:%M:%S\n"
+" Time_Keep On\n"
+" Decode_Field_As escaped key001\n"
+" Types A1:integer A2:string A3:bool A4:float A5:hex\n"
+"\n"
+"\n"
+"\n"
+"# Parser: REGEX4_mysql_quoted_stuff\n"
+"# ====================\n"
+"# Apache error log time format\n"
+"#\n"
+"[PARSER]\n"
+" Name REGEX4_mysql_quoted_stuff\n"
+" Format json\n"
+" Regex ^(?<time>.*?),(?<key001>.*)$\n"
+" Time_Key time\n"
+" Time_Format %Y-%M-%S %H:%M:%S\n"
+" Time_Keep On\n"
+" Decode_Field_As json key001\n"
+" Types A1:integer A2:string A3:bool A4:float A5:hex\n"
+"[MULTILINE_PARSER]\n"
+" name exception_test\n"
+" type regex\n"
+" flush_timeout 1000\n"
+" rule \"start_state\" \"/(Dec \\d+ \\d+\\:\\d+\\:\\d+)(.*)/\" \"cont\"\n"
+" rule \"cont\" \"/^\\s+at.*/\" \"cont\"\n";
+
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
+{
+ /* Set fuzzer-malloc chance of failure */
+ flb_malloc_p = 0;
+ flb_malloc_mod = 25000;
+
+ /* Limit the size of the config files to 32KB. */
+ if (size > 32768) {
+ return 0;
+ }
+
+ /* Write the config file to a location we know OSS-Fuzz has */
+ char filename[256];
+ sprintf(filename, "/tmp/libfuzzer.%d", getpid());
+ FILE *fp = fopen(filename, "wb");
+ if (!fp) {
+ return 0;
+ }
+ fwrite(conf_file, strlen(conf_file), 1, fp);
+ fclose(fp);
+
+
+ /* Now parse random data based on the config files */
+ struct flb_config *config = NULL;
+ config = flb_config_init();
+ int ret = flb_parser_conf_file(filename, config);
+ if (ret == 0) {
+ struct mk_list *head = NULL;
+ mk_list_foreach(head, &config->parsers) {
+ size_t out_size;
+ char *out_buf = NULL;
+ struct flb_parser *parser = NULL;
+ struct flb_time out_time;
+ parser = mk_list_entry(head, struct flb_parser, _head);
+ flb_parser_do(parser, (const char*)data, size, (void **)&out_buf,
+ &out_size, &out_time);
+ if (out_buf != NULL) {
+ free(out_buf);
+ }
+ }
+ }
+ flb_parser_exit(config);
+ flb_config_exit(config);
+
+ if (size > 100) {
+ /* Now let's do a second run where we also call flb_config_set_property */
+ config = flb_config_init();
+ ret = flb_parser_conf_file(filename, config);
+ char *key_1 = get_null_terminated(15, &data, &size);
+ char *val_1 = get_null_terminated(15, &data, &size);
+ char *key_2 = get_null_terminated(15, &data, &size);
+ char *val_2 = get_null_terminated(15, &data, &size);
+ char *progname = get_null_terminated(15, &data, &size);
+
+ flb_config_set_property(config, key_1, val_1);
+ flb_config_set_property(config, key_2, val_2);
+ flb_config_set_program_name(config, progname);
+ set_log_level_from_env(config);
+
+ struct mk_list prop;
+ flb_kv_init(&prop);
+ flb_kv_item_create(&prop, key_1, val_1);
+ flb_config_prop_get(progname, &prop);
+ flb_slist_entry_get(&prop, (int)data[0]);
+ flb_slist_dump(&prop);
+
+ if (ret == 0) {
+ struct mk_list *head = NULL;
+ mk_list_foreach(head, &config->parsers) {
+ size_t out_size;
+ char *out_buf = NULL;
+ struct flb_parser *parser = NULL;
+ struct flb_time out_time;
+
+ parser = mk_list_entry(head, struct flb_parser, _head);
+ flb_parser_do(parser, (const char*)data, size, (void **)&out_buf,
+ &out_size, &out_time);
+ if (out_buf != NULL) {
+ free(out_buf);
+ }
+ }
+ }
+ flb_parser_exit(config);
+ flb_config_exit(config);
+ flb_free(key_1);
+ flb_free(val_1);
+ flb_free(key_2);
+ flb_free(val_2);
+ flb_free(progname);
+ flb_kv_release(&prop);
+ }
+
+ /* clean up the file */
+ unlink(filename);
+
+ return 0;
+}
diff --git a/fluent-bit/tests/internal/fuzzers/config_map_fuzzer.c b/fluent-bit/tests/internal/fuzzers/config_map_fuzzer.c
new file mode 100644
index 00000000..79697f65
--- /dev/null
+++ b/fluent-bit/tests/internal/fuzzers/config_map_fuzzer.c
@@ -0,0 +1,212 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+#include <fluent-bit/flb_info.h>
+#include <fluent-bit/flb_kv.h>
+#include <fluent-bit/flb_slist.h>
+#include <fluent-bit/flb_mem.h>
+#include <fluent-bit/flb_config.h>
+#include <fluent-bit/flb_config_map.h>
+
+#include "flb_fuzz_header.h"
+
+struct context {
+ /* Single values */
+ int num_int;
+ size_t size;
+ time_t time;
+ char boolean;
+ double num_double;
+ flb_sds_t string;
+ struct mk_list *list1;
+ struct mk_list *list2;
+
+ /* Multiple entries */
+ struct mk_list *mult_num_int;
+ struct mk_list *mult_boolean;
+ struct mk_list *mult_num_double;
+ struct mk_list *mult_string;
+ struct mk_list *mult_list1;
+ struct mk_list *mult_list2;
+};
+
+struct flb_config_map config_map[] = {
+ {
+ FLB_CONFIG_MAP_BOOL,
+ "boolean",
+ "true",
+ 0, FLB_TRUE, offsetof(struct context, boolean),
+ NULL
+ },
+ {
+ FLB_CONFIG_MAP_INT,
+ "num_int",
+ "123",
+ 0, FLB_TRUE, offsetof(struct context, num_int),
+ NULL
+ },
+ {
+ FLB_CONFIG_MAP_DOUBLE,
+ "num_double", "0.12345",
+ 0, FLB_TRUE, offsetof(struct context, num_double),
+ NULL
+ },
+ {
+ FLB_CONFIG_MAP_STR,
+ "string",
+ "test",
+ 0, FLB_TRUE, offsetof(struct context, string),
+ NULL
+ },
+
+ /* SIZE */
+ {
+ FLB_CONFIG_MAP_SIZE,
+ "test_size",
+ "2M",
+ 0, FLB_TRUE, offsetof(struct context, size),
+ NULL
+ },
+
+ /* TIME */
+ {
+ FLB_CONFIG_MAP_TIME,
+ "test_time",
+ "2H",
+ 0, FLB_TRUE, offsetof(struct context, time),
+ NULL
+ },
+
+ /* CSLIST */
+ {
+ FLB_CONFIG_MAP_CLIST,
+ "test_clist",
+ "a, b, c ,d,e , f, g,h,i,jk , lm , n o,pqr,, , ,stuv,xyz",
+ 0, FLB_TRUE, offsetof(struct context, list1),
+ NULL
+ },
+
+ /* SLIST */
+ {
+ FLB_CONFIG_MAP_SLIST_4,
+ "test_slist",
+ "a b c de f ghi jk l m n o pqr stuv xyz",
+ 0, FLB_TRUE, offsetof(struct context, list2),
+ NULL
+ },
+
+ /* EOF */
+ {0}
+};
+
+struct flb_config_map config_map_mult[] = {
+ {
+ FLB_CONFIG_MAP_BOOL,
+ "no_mult",
+ "true",
+ 0, FLB_TRUE, 1,
+ NULL
+ },
+ {
+ FLB_CONFIG_MAP_BOOL,
+ "mult_boolean",
+ NULL,
+ FLB_CONFIG_MAP_MULT, FLB_TRUE, offsetof(struct context, mult_boolean),
+ NULL
+ },
+ {
+ FLB_CONFIG_MAP_INT,
+ "mult_num_int",
+ "123",
+ FLB_CONFIG_MAP_MULT, FLB_TRUE, offsetof(struct context, mult_num_int),
+ NULL
+ },
+ {
+ FLB_CONFIG_MAP_DOUBLE,
+ "mult_num_double", "0.12345",
+ FLB_CONFIG_MAP_MULT, FLB_TRUE, offsetof(struct context, mult_num_double),
+ NULL
+ },
+ {
+ FLB_CONFIG_MAP_STR,
+ "mult_string",
+ "test",
+ FLB_CONFIG_MAP_MULT, FLB_TRUE, offsetof(struct context, mult_string),
+ NULL
+ },
+ {
+ FLB_CONFIG_MAP_CLIST,
+ "mult_clist",
+ "a, b, c ,d,e , f, g,h,i,jk , lm , n o,pqr,, , ,stuv,xyz",
+ FLB_CONFIG_MAP_MULT, FLB_TRUE, offsetof(struct context, mult_list1),
+ NULL
+ },
+ {
+ FLB_CONFIG_MAP_SLIST_4,
+ "mult_slist",
+ "a b c de f ghi jk l m n o pqr stuv xyz",
+ FLB_CONFIG_MAP_MULT, FLB_TRUE, offsetof(struct context, mult_list2),
+ NULL
+ },
+
+ /* EOF */
+ {0}
+};
+
+struct flb_config_map *configs[] = {config_map_mult, config_map};
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
+{
+ /* Set flb_malloc_mod to be fuzzer-data dependent */
+ if (size < 4) {
+ return 0;
+ }
+ flb_malloc_p = 0;
+ flb_malloc_mod = *(int*)data;
+ data += 4;
+ size -= 4;
+
+ /* Avoid division by zero for modulo operations */
+ if (flb_malloc_mod == 0) {
+ flb_malloc_mod = 1;
+ }
+
+ if (size < 40) {
+ return 0;
+ }
+
+ struct mk_list *map = NULL;
+ struct flb_config *config = NULL;
+ struct context ctx;
+ bzero(&ctx, sizeof(struct context));
+ struct mk_list prop;
+ bzero(&prop, sizeof(struct mk_list));
+
+ char *fuzz_str1 = get_null_terminated(15, &data, &size);
+ char *fuzz_str2 = get_null_terminated(15, &data, &size);
+ char *fuzz_str3 = get_null_terminated(size, &data, &size);
+
+ for (int i = 0; i < 2; i++) {
+ config = flb_config_init();
+ if (config) {
+ memset(&ctx, '\0', sizeof(struct context));
+
+ flb_kv_init(&prop);
+ if (flb_kv_item_create(&prop, fuzz_str1, fuzz_str2) != NULL) {
+ /* Assign one of the config maps */
+ map = flb_config_map_create(config, configs[i]);
+ if (map) {
+ if (flb_config_map_set(&prop, map, &ctx) != -1) {
+ flb_config_map_properties_check(fuzz_str3, &prop, map);
+ }
+ flb_config_map_destroy(map);
+ }
+ }
+ flb_kv_release(&prop);
+ flb_config_exit(config);
+ }
+ }
+
+ flb_free(fuzz_str1);
+ flb_free(fuzz_str2);
+ flb_free(fuzz_str3);
+ return 0;
+}
diff --git a/fluent-bit/tests/internal/fuzzers/config_random_fuzzer.c b/fluent-bit/tests/internal/fuzzers/config_random_fuzzer.c
new file mode 100644
index 00000000..ead06ad0
--- /dev/null
+++ b/fluent-bit/tests/internal/fuzzers/config_random_fuzzer.c
@@ -0,0 +1,57 @@
+/* Fluent Bit
+ * ==========
+ * Copyright (C) 2019-2021 The Fluent Bit Authors
+ * Copyright (C) 2015-2018 Treasure Data Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include <stdint.h>
+#include <string.h>
+#include <stdlib.h>
+#include <fluent-bit/flb_parser.h>
+#include <fluent-bit/flb_slist.h>
+#include "flb_fuzz_header.h"
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
+{
+ /* Set fuzzer-malloc chance of failure */
+ flb_malloc_p = 0;
+ flb_malloc_mod = 25000;
+
+ /* Limit the size of the config files to 32KB. */
+ if (size > 32768) {
+ return 0;
+ }
+
+ /* Write the config file to a location we know OSS-Fuzz has */
+ char filename[256];
+ sprintf(filename, "/tmp/libfuzzer.%d", getpid());
+ FILE *fp = fopen(filename, "wb");
+ if (!fp) {
+ return 0;
+ }
+ fwrite(data, size, 1, fp);
+ fclose(fp);
+
+ /* Now parse a random config file */
+ struct flb_config *config = NULL;
+ config = flb_config_init();
+ flb_parser_conf_file(filename, config);
+ flb_parser_exit(config);
+ flb_config_exit(config);
+
+ /* Cleanup written config file */
+ unlink(filename);
+
+ return 0;
+}
diff --git a/fluent-bit/tests/internal/fuzzers/config_yaml_fuzzer.c b/fluent-bit/tests/internal/fuzzers/config_yaml_fuzzer.c
new file mode 100644
index 00000000..99e17ba6
--- /dev/null
+++ b/fluent-bit/tests/internal/fuzzers/config_yaml_fuzzer.c
@@ -0,0 +1,65 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/* Fluent Bit
+ * ==========
+ * Copyright (C) 2015-2023 The Fluent Bit Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <fluent-bit/flb_info.h>
+#include <fluent-bit/flb_mem.h>
+#include <fluent-bit/flb_kv.h>
+#include <fluent-bit/flb_config_format.h>
+
+#include <cfl/cfl.h>
+#include <cfl/cfl_list.h>
+
+#include "flb_fuzz_header.h"
+
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
+{
+ /* Set fuzzer-malloc chance of failure */
+ flb_malloc_p = 0;
+ flb_malloc_mod = 25000;
+
+ /* Limit the size of the config files to 32KB. */
+ if (size > 32768) {
+ return 0;
+ }
+
+ /* Write the config file to a location we know OSS-Fuzz has */
+ char filename[256];
+ sprintf(filename, "/tmp/libfuzzer.%d.yaml", getpid());
+ FILE *fp = fopen(filename, "wb");
+ if (!fp) {
+ return 0;
+ }
+ fwrite(data, size, 1, fp);
+ fclose(fp);
+
+
+ struct flb_cf *cf;
+ struct flb_cf_section *s;
+
+ cf = flb_cf_yaml_create(NULL, filename, NULL, 0);
+ if (cf != NULL) {
+ flb_cf_destroy(cf);
+ }
+
+ /* clean up the file */
+ unlink(filename);
+
+ return 0;
+}
diff --git a/fluent-bit/tests/internal/fuzzers/ctrace_fuzzer.c b/fluent-bit/tests/internal/fuzzers/ctrace_fuzzer.c
new file mode 100644
index 00000000..bcd2ca58
--- /dev/null
+++ b/fluent-bit/tests/internal/fuzzers/ctrace_fuzzer.c
@@ -0,0 +1,24 @@
+#include <stdint.h>
+#include <fluent-bit/flb_mem.h>
+#include <ctraces/ctraces.h>
+#include <ctraces/ctr_decode_msgpack.h>
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size){
+ size_t off = 0;
+ struct ctrace *ctr = NULL;
+ size_t msgpack_text_size;
+ char *msgpack_text_buffer = NULL;
+
+ /* Set fuzzer-malloc chance of failure */
+ flb_malloc_p = 0;
+ flb_malloc_mod = 25000;
+
+ ctr_decode_msgpack_create(&ctr, data, size, &off);
+ if (ctr != NULL) {
+ ctr_encode_msgpack_create(ctr, &msgpack_text_buffer, &msgpack_text_size);
+ ctr_encode_msgpack_destroy(msgpack_text_buffer);
+
+ ctr_destroy(ctr);
+ }
+ return 0;
+}
diff --git a/fluent-bit/tests/internal/fuzzers/engine_fuzzer.c b/fluent-bit/tests/internal/fuzzers/engine_fuzzer.c
new file mode 100644
index 00000000..498b86a8
--- /dev/null
+++ b/fluent-bit/tests/internal/fuzzers/engine_fuzzer.c
@@ -0,0 +1,171 @@
+#include <fluent-bit.h>
+#include <fluent-bit/flb_parser.h>
+#include <fluent-bit/flb_parser.h>
+#include <fluent-bit/flb_input_chunk.h>
+#include <fluent-bit/flb_metrics.h>
+#include "flb_fuzz_header.h"
+
+#include <stdio.h>
+#include <monkey/mk_core.h>
+
+struct flb_parser *parser;
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+int filter_ffd;
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
+ if (size < 100) {
+ return 0;
+ }
+ /* Set fuzzer-malloc chance of failure */
+ flb_malloc_p = 0;
+ flb_malloc_mod = 25000;
+
+ uint8_t ud = data[0];
+ MOVE_INPUT(1);
+ #define NM_SIZE 50
+ char *null_terminated = get_null_terminated(NM_SIZE, &data, &size);
+
+ char *nm3 = get_null_terminated(10, &data, &size);
+ char *nm4 = get_null_terminated(10, &data, &size);
+ int random_i1 = *(int *)data;
+ MOVE_INPUT(4);
+ int random_i2 = *(int *)data;
+
+ #define FUNC_NUMS 10
+ switch (ud % FUNC_NUMS) {
+ case 0:
+ flb_output(ctx, null_terminated, nm3);
+ break;
+ case 1:
+ flb_filter(ctx, null_terminated, nm3);
+ break;
+ case 2:
+ flb_input(ctx, null_terminated, nm3);
+ break;
+ case 3:
+ flb_output_check(ctx->config);
+ break;
+ case 4: {
+ struct mk_list *head;
+ struct flb_input_instance *entry;
+ mk_list_foreach(head, &ctx->config->inputs) {
+ entry = mk_list_entry(head, struct flb_input_instance, _head);
+ flb_input_name_exists(nm3, ctx->config);
+ flb_input_get_property(nm3, entry);
+ flb_input_name(entry);
+ flb_input_collector_running(0, entry);
+ flb_input_collector_pause(random_i1, entry);
+ flb_input_collector_resume(random_i2, entry);
+ flb_input_net_default_listener(nm4, random_i1, entry);
+ flb_input_collector_start(random_i2, entry);
+ }
+ }
+ break;
+ case 5: {
+ struct mk_list *head;
+ struct flb_input_instance *entry;
+ mk_list_foreach(head, &ctx->config->inputs) {
+ entry = mk_list_entry(head, struct flb_input_instance, _head);
+ if (entry->storage != NULL) {
+ char bufbuf[100];
+ flb_input_chunk_append_raw(entry, FLB_INPUT_LOGS, 0, "A",
+ 1, "\0", 0);
+
+ struct flb_input_chunk *ic = NULL;
+ ic = flb_input_chunk_create(entry, FLB_INPUT_LOGS, nm3, 10);
+ if (ic != NULL) {
+ flb_input_chunk_get_size(ic);
+ flb_input_chunk_set_up_down(ic);
+ flb_input_chunk_down(ic);
+ flb_input_chunk_set_up(ic);
+ flb_input_chunk_get_name(ic);
+ char *tag_buf;
+ int tag_len;
+ flb_input_chunk_get_tag(ic, &tag_buf, &tag_len);
+ size_t flushed;
+ flb_input_chunk_flush(ic, &flushed);
+ }
+ }
+ }
+ }
+ break;
+ case 6:
+ flb_input_check(ctx->config);
+ flb_input_pause_all(ctx->config);
+ break;
+ case 7: {
+ struct mk_list *head;
+ struct flb_output_instance *entry;
+ mk_list_foreach(head, &ctx->config->outputs) {
+ entry = mk_list_entry(head, struct flb_output_instance, _head);
+ flb_output_net_default(nm4, random_i1, entry);
+ flb_output_name(entry);
+ }
+ }
+ break;
+ default:
+ flb_lib_push(ctx, in_ffd, null_terminated, NM_SIZE);
+ break;
+ }
+
+ flb_free(null_terminated);
+ flb_free(nm3);
+ flb_free(nm4);
+ return 0;
+}
+
+int callback_test(void* data, size_t size, void* cb_data)
+{
+ return 0;
+}
+
+struct flb_lib_out_cb cb;
+
+
+int LLVMFuzzerInitialize(int *argc, char ***argv) {
+ /* Set fuzzer-malloc chance of failure */
+ flb_malloc_p = 0;
+ flb_malloc_mod = 25000;
+
+ ctx = flb_create();
+ flb_service_set(ctx, "Flush", "0", "Grace",
+ "0", "Log_Level", "debug", NULL);
+
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ flb_input_set(ctx, in_ffd, (char *) "test", NULL);
+ flb_input_set(ctx, in_ffd, (char *) "BBBB", NULL);
+ flb_input_set(ctx, in_ffd, (char *) "AAAA", NULL);
+ flb_input_set(ctx, in_ffd, (char *) "AAAAA", NULL);
+ flb_input_set(ctx, in_ffd, (char *) "CC", NULL);
+ flb_input_set(ctx, in_ffd, (char *) "A", NULL);
+
+ parser = flb_parser_create("timestamp", "regex", "^(?<time>.*)$", FLB_TRUE,
+ "%s.%L", "time", NULL, MK_FALSE, 0, FLB_FALSE,
+ NULL, 0, NULL, ctx->config);
+ filter_ffd = flb_filter(ctx, (char *) "parser", NULL);
+ int ret;
+ ret = flb_filter_set(ctx, filter_ffd, "Match", "test",
+ "Key_Name", "@timestamp",
+ "Parser", "timestamp",
+ "Reserve_Data", "On",
+ NULL);
+
+ cb.cb = callback_test;
+ cb.data = NULL;
+ out_ffd = flb_output(ctx, (char *) "lib", &cb);
+ flb_output_set(ctx, out_ffd, "Match", "*",
+ "format", "json", NULL);
+
+ flb_output_set(ctx, out_ffd,"match", "test", NULL);
+ flb_output_set(ctx, out_ffd,"region", "us-west-2", NULL);
+ flb_output_set(ctx, out_ffd,"log_group_name", "fluent", NULL);
+ flb_output_set(ctx, out_ffd,"log_stream_prefix", "from-fluent-", NULL);
+ flb_output_set(ctx, out_ffd,"auto_create_group", "On", NULL);
+ flb_output_set(ctx, out_ffd,"net.keepalive", "Off", NULL);
+ flb_output_set(ctx, out_ffd,"Retry_Limit", "1", NULL);
+
+ /* start the engine */
+ flb_start(ctx);
+}
diff --git a/fluent-bit/tests/internal/fuzzers/filter_stdout_fuzzer.c b/fluent-bit/tests/internal/fuzzers/filter_stdout_fuzzer.c
new file mode 100644
index 00000000..ed9a3f49
--- /dev/null
+++ b/fluent-bit/tests/internal/fuzzers/filter_stdout_fuzzer.c
@@ -0,0 +1,62 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/* Fluent Bit
+ * ==========
+ * Copyright (C) 2015-2022 The Fluent Bit Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdint.h>
+#include <fluent-bit.h>
+#include "flb_fuzz_header.h"
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
+{
+ int ret;
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+
+ /* Set fuzzer-malloc chance of failure */
+ flb_malloc_p = 0;
+ flb_malloc_mod = 25000;
+
+
+ ctx = flb_create();
+ flb_service_set(ctx, "Flush", "1", "Grace", "1", "Log_Level", "error", NULL);
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ if (in_ffd >= 0) {
+ flb_input_set(ctx, in_ffd, "tag", "test", NULL);
+
+ out_ffd = flb_output(ctx, (char *) "stdout", NULL);
+ if (out_ffd >= 0) {
+ flb_output_set(ctx, out_ffd, "match", "test", NULL);
+
+ ret = flb_start(ctx);
+ if (ret == 0) {
+ char *p = get_null_terminated(size, &data, &size);
+ for (int i = 0; i < strlen(p); i++) {
+ flb_lib_push(ctx, in_ffd, p+i, 1);
+ }
+ free(p);
+
+ sleep(1); /* waiting flush */
+ }
+ }
+ }
+ flb_stop(ctx);
+ flb_destroy(ctx);
+
+ return 0;
+}
diff --git a/fluent-bit/tests/internal/fuzzers/flb_fuzz_header.h b/fluent-bit/tests/internal/fuzzers/flb_fuzz_header.h
new file mode 100644
index 00000000..cda08920
--- /dev/null
+++ b/fluent-bit/tests/internal/fuzzers/flb_fuzz_header.h
@@ -0,0 +1,42 @@
+/* Fluent Bit
+ * ==========
+ * Copyright (C) 2019-2021 The Fluent Bit Authors
+ * Copyright (C) 2015-2018 Treasure Data Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdint.h>
+#include <string.h>
+
+#define GET_MOD_EQ(max, idx) (data[0] % max) == idx
+#define MOVE_INPUT(offset) data += offset; size -= offset;
+
+#define TIMEOUT_GUARD if (size > 32768) return 0;
+
+char *get_null_terminated(size_t size, const uint8_t **data,
+ size_t *total_data_size)
+{
+ char *tmp = flb_malloc(size+1);
+ if (tmp == NULL) {
+ tmp = malloc(size+1);
+ }
+ memcpy(tmp, *data, size);
+ tmp[size] = '\0';
+
+ /* Modify the fuzz variables */
+ *total_data_size -= size;
+ *data += size;
+
+ return tmp;
+}
diff --git a/fluent-bit/tests/internal/fuzzers/flb_json_fuzzer.c b/fluent-bit/tests/internal/fuzzers/flb_json_fuzzer.c
new file mode 100644
index 00000000..916b1a51
--- /dev/null
+++ b/fluent-bit/tests/internal/fuzzers/flb_json_fuzzer.c
@@ -0,0 +1,82 @@
+/* Fluent Bit
+ * ==========
+ * Copyright (C) 2019-2021 The Fluent Bit Authors
+ * Copyright (C) 2015-2018 Treasure Data Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include <stdlib.h>
+#include <stdint.h>
+#include <fluent-bit/flb_pack.h>
+#include <fluent-bit/flb_str.h>
+#include "flb_fuzz_header.h"
+
+int LLVMFuzzerTestOneInput(unsigned char *data, size_t size)
+{
+ TIMEOUT_GUARD
+ /* Set fuzzer-malloc chance of failure */
+ flb_malloc_p = 0;
+ flb_malloc_mod = 25000;
+
+ if (size < 1) {
+ return 0;
+ }
+ unsigned char decider = *data;
+ data++;
+ size--;
+
+ /* json packer */
+ char *out_buf = NULL;
+ size_t out_size;
+ int root_type;
+ int ret = flb_pack_json((char*)data, size, &out_buf, &out_size, &root_type, NULL);
+
+ if (ret == 0) {
+ size_t off = 0;
+ msgpack_unpacked result;
+ msgpack_unpacked_init(&result);
+ int ret2 = msgpack_unpack_next(&result, out_buf, out_size, &off);
+ if (ret2 == MSGPACK_UNPACK_SUCCESS) {
+ msgpack_object root = result.data;
+ char *tmp = NULL;
+ tmp = flb_msgpack_to_json_str(0, &root);
+ if (tmp != NULL) {
+ flb_free(tmp);
+ }
+ }
+ msgpack_unpacked_destroy(&result);
+ flb_sds_t d;
+ d = flb_sds_create("date");
+ if (decider < 0x30) {
+ flb_sds_t ret_s = flb_pack_msgpack_to_json_format(out_buf, out_size,
+ FLB_PACK_JSON_FORMAT_LINES,
+ (int)decider, d);
+ free(out_buf);
+ if (ret_s != NULL) {
+ flb_sds_destroy(ret_s);
+ }
+ }
+ else {
+ flb_sds_t ret_s = flb_pack_msgpack_to_json_format(out_buf, out_size,
+ FLB_PACK_JSON_FORMAT_LINES,
+ FLB_PACK_JSON_DATE_EPOCH, NULL);
+ free(out_buf);
+ if (ret_s != NULL) {
+ flb_sds_destroy(ret_s);
+ }
+ }
+ flb_sds_destroy(d);
+ }
+
+ return 0;
+}
diff --git a/fluent-bit/tests/internal/fuzzers/flb_mp_fuzzer.c b/fluent-bit/tests/internal/fuzzers/flb_mp_fuzzer.c
new file mode 100644
index 00000000..b33c9040
--- /dev/null
+++ b/fluent-bit/tests/internal/fuzzers/flb_mp_fuzzer.c
@@ -0,0 +1,55 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/* Fluent Bit
+ * ==========
+ * Copyright (C) 2015-2023 The Fluent Bit Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include <stdlib.h>
+#include <stdint.h>
+#include <fluent-bit/flb_mem.h>
+#include <fluent-bit/flb_mp.h>
+
+#include "flb_fuzz_header.h"
+
+int LLVMFuzzerTestOneInput(unsigned char *data, size_t size)
+{
+ /* Set flb_malloc_mod to be fuzzer-data dependent */
+ if (size < 5) {
+ return 0;
+ }
+ flb_malloc_p = 0;
+ flb_malloc_mod = *(int*)data;
+ data += 4;
+ size -= 4;
+
+ /* Avoid division by zero for modulo operations */
+ if (flb_malloc_mod == 0) {
+ flb_malloc_mod = 1;
+ }
+
+ unsigned char decider = *data;
+ data++;
+ size--;
+
+ int out_records;
+ size_t processed_bytes;
+ if (decider % 2 == 0) {
+ flb_mp_validate_log_chunk(data, size, &out_records, &processed_bytes);
+ }
+ else if (decider % 2 == 1) {
+ flb_mp_validate_metric_chunk(data, size, &out_records, &processed_bytes);
+ }
+ return 0;
+}
diff --git a/fluent-bit/tests/internal/fuzzers/fstore_fuzzer.c b/fluent-bit/tests/internal/fuzzers/fstore_fuzzer.c
new file mode 100644
index 00000000..e2a95106
--- /dev/null
+++ b/fluent-bit/tests/internal/fuzzers/fstore_fuzzer.c
@@ -0,0 +1,86 @@
+/* Fluent Bit
+ * ==========
+ * Copyright (C) 2019-2022 The Fluent Bit Authors
+ * Copyright (C) 2015-2018 Treasure Data Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdint.h>
+#include <string.h>
+#include <stdlib.h>
+#include <assert.h>
+
+#include <fluent-bit/flb_info.h>
+#include <fluent-bit/flb_fstore.h>
+#include <fluent-bit/flb_mem.h>
+#include <fluent-bit/flb_compat.h>
+
+#include <chunkio/chunkio.h>
+#include <chunkio/cio_utils.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+
+#define FSF_STORE_PATH "/tmp/flb-fstore"
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
+{
+ int ret;
+ void *out_buf;
+ size_t out_size;
+ struct stat st_data;
+ struct flb_fstore *fs;
+ struct flb_fstore_stream *st;
+ struct flb_fstore_file *fsf;
+
+ /* Set flb_malloc_mod to be fuzzer-data dependent */
+ if (size < 4) {
+ return 0;
+ }
+ flb_malloc_p = 0;
+ flb_malloc_mod = *(int*)data;
+ data += 4;
+ size -= 4;
+
+ /* Avoid division by zero for modulo operations */
+ if (flb_malloc_mod == 0) {
+ flb_malloc_mod = 1;
+ }
+
+ cio_utils_recursive_delete(FSF_STORE_PATH);
+ fs = flb_fstore_create(FSF_STORE_PATH, FLB_FSTORE_FS);
+ if (fs == NULL) {
+ return 0;
+ }
+ st = flb_fstore_stream_create(fs, "abc");
+ if (st != NULL) {
+ fsf = flb_fstore_file_create(fs, st, "example.txt", size);
+
+ if (fsf != NULL) {
+ ret = flb_fstore_file_append(fsf, data, size);
+ if (ret == 0) {
+ ret = flb_fstore_file_content_copy(fs, fsf, &out_buf, &out_size);
+ if (ret == 0) {
+ assert(memcmp(out_buf, data, size) == 0);
+ }
+ flb_free(out_buf);
+ }
+ }
+ }
+
+ flb_fstore_dump(fs);
+ flb_fstore_destroy(fs);
+ return 0;
+}
diff --git a/fluent-bit/tests/internal/fuzzers/http_fuzzer.c b/fluent-bit/tests/internal/fuzzers/http_fuzzer.c
new file mode 100644
index 00000000..28f0c8d2
--- /dev/null
+++ b/fluent-bit/tests/internal/fuzzers/http_fuzzer.c
@@ -0,0 +1,116 @@
+#include <stdlib.h>
+#include <fluent-bit/flb_time.h>
+#include <fluent-bit/flb_parser.h>
+#include <fluent-bit/flb_info.h>
+#include <fluent-bit/flb_mem.h>
+#include <fluent-bit/flb_error.h>
+#include <fluent-bit/flb_socket.h>
+#include <fluent-bit/flb_stream.h>
+#include <fluent-bit/flb_connection.h>
+#include <fluent-bit/flb_http_client.h>
+
+#include "flb_fuzz_header.h"
+
+extern int fuzz_process_data(struct flb_http_client *c);
+extern int fuzz_check_connection(struct flb_http_client *c);
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
+{
+ /* Set fuzzer-malloc chance of failure */
+ flb_malloc_p = 0;
+ flb_malloc_mod = 25000;
+
+ struct flb_upstream *u;
+ struct flb_connection *u_conn = NULL;
+ struct flb_http_client *c;
+ struct flb_config *config;
+ char *uri = NULL;
+
+ if (size < 160) {
+ return 0;
+ }
+
+ config = flb_config_init();
+ if (config == NULL) {
+ return 0;
+ }
+
+ u = flb_upstream_create(config, "127.0.0.1", 8001, 0, NULL);
+
+ u_conn = flb_connection_create(-1,
+ FLB_TRANSPORT_TCP,
+ (void *) u,
+ NULL,
+ NULL);
+
+ if (u_conn == NULL) {
+ return 0;
+ }
+
+ char *proxy = NULL;
+ if (GET_MOD_EQ(2,1)) {
+ proxy = get_null_terminated(50, &data, &size);
+ }
+
+ uri = get_null_terminated(20, &data, &size);
+
+ int method = (int)data[0];
+ c = flb_http_client(u_conn, method, uri, NULL, 0,
+ "127.0.0.1", 8001, proxy, 0);
+ if (c != NULL) {
+ char *null_terminated = get_null_terminated(30, &data, &size);
+
+ /* Perform a set of operations on the http_client */
+ flb_http_basic_auth(c, null_terminated, null_terminated);
+ flb_http_set_content_encoding_gzip(c);
+ flb_http_set_keepalive(c);
+ flb_http_strip_port_from_host(c);
+ flb_http_allow_duplicated_headers(c, 0);
+
+ flb_http_buffer_size(c, (*(size_t *)data) & 0xfff);
+ MOVE_INPUT(4)
+ flb_http_add_header(c, "User-Agent", 10, "Fluent-Bit", 10);
+ flb_http_add_header(c, (char*)data, size, "Fluent-Bit", 10);
+ flb_http_buffer_size(c, (int)data[0]);
+ MOVE_INPUT(1)
+ flb_http_buffer_available(c);
+
+ size_t b_sent;
+ flb_http_do(c, &b_sent);
+
+ size_t out_size = 0;
+ flb_http_buffer_increase(c, (*(size_t *)data) & 0xfff, &out_size);
+ MOVE_INPUT(4)
+
+ /* Now we need to simulate the reading of data */
+ c->resp.status = 200;
+
+ if (c->resp.data != NULL) {
+ flb_free(c->resp.data);
+ }
+
+ char *new_nulltm = get_null_terminated(30, &data, &size);
+ c->resp.data_len = 30;
+ c->resp.data = new_nulltm;
+ fuzz_process_data(c);
+ fuzz_check_connection(c);
+
+ flb_http_client_destroy(c);
+ flb_free(null_terminated);
+ }
+
+ /* Now try the http_client_proxy_connect function. */
+ flb_http_client_proxy_connect(u_conn);
+
+ flb_connection_destroy(u_conn);
+ flb_upstream_destroy(u);
+ flb_config_exit(config);
+ if (uri != NULL) {
+ flb_free(uri);
+ }
+ if (proxy != NULL) {
+ flb_free(proxy);
+ }
+
+ return 0;
+}
diff --git a/fluent-bit/tests/internal/fuzzers/input_fuzzer.c b/fluent-bit/tests/internal/fuzzers/input_fuzzer.c
new file mode 100644
index 00000000..57458d36
--- /dev/null
+++ b/fluent-bit/tests/internal/fuzzers/input_fuzzer.c
@@ -0,0 +1,164 @@
+#include <fluent-bit.h>
+#include <pthread.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <stdint.h>
+#include <sys/stat.h>
+#include <fluent-bit/flb_input_chunk.h>
+#include <fluent-bit/flb_storage.h>
+#include <fluent-bit/flb_router.h>
+#include <fluent-bit/flb_time.h>
+
+#include "chunkio/chunkio.h"
+#include "flb_fuzz_header.h"
+
+
+const char *input_chunk_property_keywords[] = {
+ "log_suppress_interval",
+ "routable",
+ "alias",
+ "mem_buf_limit",
+ "listen",
+ "log_level",
+ "host",
+ "port",
+ "ipv6",
+ "net.",
+ "tls",
+ "tls.verify",
+ "tls.debug",
+ "tls.ca_path",
+ "tls.key_file",
+ "tls.vhost",
+ "tls.ca_file",
+ "tls.crt_file",
+ "tls.key_passwd",
+ "threaded",
+ "storage.type",
+};
+
+int LLVMFuzzerTestOneInput(const uint8_t *data3, size_t size3)
+{
+ int i;
+ int ret;
+ int in_ffd;
+ int out_ffd;
+
+ flb_ctx_t *ctx;
+ size_t total_bytes;
+ struct flb_input_instance *i_ins;
+ struct mk_list *tmp;
+ struct mk_list *head;
+ struct flb_input_chunk *ic;
+ struct flb_task *task;
+
+ if (size3 < 60) {
+ return 0;
+ }
+ /* Set fuzzer-malloc chance of failure */
+ flb_malloc_p = 0;
+ flb_malloc_mod = 25000;
+ char *input_buffer1 = get_null_terminated(30, &data3, &size3);
+ if (input_buffer1 == NULL) {
+ return 0;
+ }
+ size_t input_buffer1_len = strlen(input_buffer1);
+
+ char *input_buffer2 = get_null_terminated(10, &data3, &size3);
+ if (input_buffer2 == NULL) {
+ return 0;
+ }
+ size_t input_buffer_len2 = strlen(input_buffer2);
+
+ char *input_buffer3 = get_null_terminated(10, &data3, &size3);
+ if (input_buffer3 == NULL) {
+ return 0;
+ }
+ size_t input_buffer_len3 = strlen(input_buffer3);
+ /* Create context, flush every second (some checks omitted here) */
+ ctx = flb_create();
+
+ /* create chunks in /tmp folder */
+ ret = flb_service_set(ctx,
+ "flush", "2", "grace", "1",
+ "storage.path", "/tmp/input-chunk-test/",
+ "Log_Level", "error",
+ NULL);
+ if (ret != 0) {
+ flb_free(input_buffer1);
+ flb_free(input_buffer2);
+ flb_free(input_buffer3);
+ return 0;
+ }
+
+ /* Lib input mode */
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ ret = flb_input_set(ctx, in_ffd,
+ "tag", "test",
+ "storage.type", "filesystem",
+ NULL);
+ if (ret != 0) {
+ flb_free(input_buffer1);
+ flb_free(input_buffer2);
+ flb_free(input_buffer3);
+ return 0;
+ }
+
+ /* an invalid output destination */
+ out_ffd = flb_output(ctx, (char *) "http", NULL);
+ flb_output_set(ctx, out_ffd,
+ "match", "test",
+ "Host", "127.0.0.1",
+ "Port", "1",
+ "storage.total_limit_size", "1K",
+ NULL);
+
+ /* Start */
+ ret = flb_start(ctx);
+ if (ret != 0) {
+ flb_free(input_buffer1);
+ flb_free(input_buffer2);
+ flb_free(input_buffer3);
+ return 0;
+ }
+
+ i_ins = mk_list_entry_first(&ctx->config->inputs,
+ struct flb_input_instance,
+ _head);
+
+ /* main fuzzing logic */
+ flb_input_set_property(i_ins, input_buffer2, input_buffer3);
+ for (int i = 0; i < sizeof(input_chunk_property_keywords)/sizeof(char*); i++) {
+ flb_input_set_property(i_ins,
+ input_chunk_property_keywords[i],
+ input_buffer3);
+ }
+
+ /* Ingest fuzz data sample */
+ for (i = 0; i < 2; ++i) {
+ flb_lib_push(ctx, in_ffd, (char *) input_buffer1, input_buffer1_len);
+ sleep(1);
+ total_bytes = flb_input_chunk_total_size(i_ins);
+ ret = total_bytes > 1000 ? -1 : 0;
+ }
+
+ /* FORCE clean up test tasks */
+ mk_list_foreach_safe(head, tmp, &i_ins->tasks) {
+ task = mk_list_entry(head, struct flb_task, _head);
+ flb_info("[task] cleanup test task");
+ flb_task_destroy(task, FLB_TRUE);
+ }
+
+ /* clean up test chunks */
+ mk_list_foreach_safe(head, tmp, &i_ins->chunks) {
+ ic = mk_list_entry(head, struct flb_input_chunk, _head);
+ flb_input_chunk_destroy(ic, FLB_TRUE);
+ }
+ flb_free(input_buffer1);
+ flb_free(input_buffer2);
+ flb_free(input_buffer3);
+
+ flb_time_msleep(200);
+ flb_stop(ctx);
+ flb_destroy(ctx);
+}
diff --git a/fluent-bit/tests/internal/fuzzers/local_test.c b/fluent-bit/tests/internal/fuzzers/local_test.c
new file mode 100644
index 00000000..309f66b7
--- /dev/null
+++ b/fluent-bit/tests/internal/fuzzers/local_test.c
@@ -0,0 +1,85 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/* Fluent Bit
+ * ==========
+ * Copyright (C) 2019-2020 The Fluent Bit Authors
+ * Copyright (C) 2015-2018 Treasure Data Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <fluent-bit/flb_info.h>
+#include <fluent-bit/flb_log.h>
+#include <fluent-bit/flb_mem.h>
+#include <monkey/mk_core.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+
+/* declare external function test */
+int LLVMFuzzerTestOneInput(unsigned char *data, size_t size);
+
+int main(int argc, char **argv)
+{
+ int i;
+ int ret;
+ FILE *fp;
+ char *buffer;
+ long bytes;
+ struct stat st;
+
+ if (argc < 2) {
+ flb_error("usage: %s TESTCASE_FILE", argv[0]);
+ exit(EXIT_FAILURE);
+ }
+
+ /* Validate the file */
+ ret = stat(argv[1], &st);
+ if (ret == -1) {
+ flb_errno();
+ flb_error("cannot stat(2) testcase file '%s'", argv[1]);
+ exit(EXIT_FAILURE);
+ }
+
+ if (!(fp = fopen(argv[1], "rb"))) {
+ flb_errno();
+ flb_error("cannot fopen(2) testcase file '%s'", argv[1]);
+ return -1;
+ }
+
+ buffer = flb_malloc(st.st_size);
+ if (!buffer) {
+ flb_errno();
+ return -1;
+ }
+
+ bytes = fread(buffer, st.st_size, 1, fp);
+ if (bytes < 1) {
+ fclose(fp);
+ flb_free(buffer);
+ return -1;
+ }
+ fclose(fp);
+
+ /* Invoke the fuzzer entry-point function */
+ for (i = 0; i < 100; i++) {
+ ret = LLVMFuzzerTestOneInput((unsigned char *) buffer, st.st_size);
+ }
+ flb_free(buffer);
+ return 0;
+}
diff --git a/fluent-bit/tests/internal/fuzzers/msgpack_parse_fuzzer.c b/fluent-bit/tests/internal/fuzzers/msgpack_parse_fuzzer.c
new file mode 100644
index 00000000..cbf2ecf1
--- /dev/null
+++ b/fluent-bit/tests/internal/fuzzers/msgpack_parse_fuzzer.c
@@ -0,0 +1,30 @@
+#include <stdint.h>
+#include <stdlib.h>
+#include <msgpack.h>
+#include <fluent-bit/flb_pack.h>
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size){
+ /* Set flb_malloc_mod to be fuzzer-data dependent */
+ if (size < 4) {
+ return 0;
+ }
+ flb_malloc_p = 0;
+ flb_malloc_mod = *(int*)data;
+ data += 4;
+ size -= 4;
+
+ /* Avoid division by zero for modulo operations */
+ if (flb_malloc_mod == 0) {
+ flb_malloc_mod = 1;
+ }
+
+ if (size != 512)
+ return 0;
+
+ /* target the conversion of raw msgpack to json */
+ flb_sds_t record;
+ record = flb_msgpack_raw_to_json_sds(data, size);
+ flb_sds_destroy(record);
+
+ return 0;
+}
diff --git a/fluent-bit/tests/internal/fuzzers/msgpack_to_gelf_fuzzer.c b/fluent-bit/tests/internal/fuzzers/msgpack_to_gelf_fuzzer.c
new file mode 100644
index 00000000..cf57437b
--- /dev/null
+++ b/fluent-bit/tests/internal/fuzzers/msgpack_to_gelf_fuzzer.c
@@ -0,0 +1,25 @@
+#include <stdint.h>
+#include <string.h>
+#include <msgpack.h>
+#include <fluent-bit/flb_pack.h>
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size){
+ /* Set fuzzer-malloc chance of failure */
+ flb_malloc_mod = 25000;
+ flb_malloc_p = 0;
+ if (size != 512)
+ return 0;
+
+ /* Target the conversion of raw msgpack to gelf */
+ flb_sds_t record;
+ struct flb_time tm = {0};
+ struct flb_gelf_fields fields = {0};
+ fields.short_message_key = flb_sds_create("AAAAAAAAAA");
+ record = flb_msgpack_raw_to_gelf((char*)data, size, &tm, &fields);
+
+ /* cleanup */
+ flb_sds_destroy(record);
+ flb_sds_destroy(fields.short_message_key);
+
+ return 0;
+}
diff --git a/fluent-bit/tests/internal/fuzzers/multiline_fuzzer.c b/fluent-bit/tests/internal/fuzzers/multiline_fuzzer.c
new file mode 100644
index 00000000..bc03e246
--- /dev/null
+++ b/fluent-bit/tests/internal/fuzzers/multiline_fuzzer.c
@@ -0,0 +1,180 @@
+/* Fluent Bit
+ * ==========
+ * Copyright (C) 2019-2021 The Fluent Bit Authors
+ * Copyright (C) 2015-2018 Treasure Data Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include <stdint.h>
+#include <stdlib.h>
+
+#include <fluent-bit/flb_info.h>
+#include <fluent-bit/flb_mem.h>
+#include <fluent-bit/flb_pack.h>
+#include <fluent-bit/flb_parser.h>
+#include <fluent-bit/multiline/flb_ml.h>
+#include <fluent-bit/multiline/flb_ml_parser.h>
+#include <fluent-bit/multiline/flb_ml_rule.h>
+
+#include "flb_fuzz_header.h"
+
+static int flush_callback(struct flb_ml_parser *parser,
+ struct flb_ml_stream *mst, void *data, char *buf_data,
+ size_t buf_size) {
+ return 0;
+}
+
+struct record_check {
+ char *buf;
+};
+
+struct expected_result {
+ int current_record;
+ char *key;
+ struct record_check *out_records;
+};
+
+char *random_strings[4];
+
+void test_multiline_parser(msgpack_object *root2, int rand_val) {
+ struct expected_result res = {0};
+ struct flb_config *config = NULL;
+
+ config = flb_config_init();
+
+ struct flb_ml *ml = NULL;
+ ml = flb_ml_create(config, "fuzz-test");
+
+ if (ml != NULL) {
+ uint64_t stream_ids[5];
+
+ flb_ml_parser_instance_create(ml, "docker");
+ flb_ml_parser_instance_create(ml, "python");
+ flb_ml_parser_instance_create(ml, "go");
+ flb_ml_parser_instance_create(ml, "cri");
+ struct flb_ml_parser_ins *mlp_i =
+ flb_ml_parser_instance_create(ml, "java");
+ flb_ml_parser_instance_set(mlp_i, "key_content", "log");
+
+ if (rand_val & 0x01) {
+ flb_ml_stream_create(ml, "java", -1, flush_callback, (void *)&res,
+ &(stream_ids[0]));
+ }
+ if (rand_val >> 1 & 0x01) {
+ flb_ml_stream_create(ml, "python", -1, flush_callback, (void *)&res,
+ &(stream_ids[1]));
+ }
+ if (rand_val >> 2 & 0x01) {
+ flb_ml_stream_create(ml, "go", -1, flush_callback, (void *)&res,
+ &(stream_ids[2]));
+ }
+ if (rand_val >> 3 & 0x01) {
+ flb_ml_stream_create(ml, "docker", -1, flush_callback, (void *)&res,
+ &(stream_ids[3]));
+ }
+ if (rand_val >> 4 & 0x01) {
+ flb_ml_stream_create(ml, "cri", -1, flush_callback, (void *)&res,
+ &(stream_ids[4]));
+ }
+
+ /* Target with msgpack object */
+ if (root2 != NULL) {
+ struct flb_time tm;
+ flb_time_get(&tm);
+ for (int i = 0; i < 4; i++) {
+ flb_ml_append_object(ml, stream_ids[i], &tm, NULL, root2);
+ }
+ }
+
+ /* Target with raw text */
+ struct flb_time tm2;
+ flb_time_get(&tm2);
+ for (int i = 0; i < 4; i++) {
+ for (int j = 0; j < 5; j++) {
+ if (random_strings[i] != NULL && stream_ids[j] != NULL) {
+ /* stream_ids index by j, random_strings index by i */
+ flb_ml_append_text(ml, stream_ids[j], &tm2,
+ random_strings[i], strlen(random_strings[i]));
+ flb_ml_append_text(ml, stream_ids[j], &tm2,
+ random_strings[i], strlen(random_strings[i]));
+ flb_ml_append_text(ml, stream_ids[j], &tm2,
+ random_strings[i], strlen(random_strings[i]));
+ flb_ml_append_text(ml, stream_ids[j], &tm2,
+ random_strings[i],strlen(random_strings[i]));
+ flb_ml_append_text(ml, stream_ids[j], &tm2,
+ random_strings[i], strlen(random_strings[i]));
+ }
+ }
+ }
+ }
+
+ flb_ml_flush_pending_now(ml);
+
+ if (ml) {
+ flb_ml_destroy(ml);
+ }
+
+ flb_config_exit(config);
+}
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
+ TIMEOUT_GUARD
+ /* Set fuzzer-malloc chance of failure */
+ flb_malloc_mod = 25000;
+ flb_malloc_p = 0;
+ /* Ensure there's enough data */
+ if (size < 250) {
+ return 0;
+ }
+
+ int rand_val = *(int *)data;
+ data += 4;
+ size -= 4;
+ for (int i = 0; i < 4; i++) {
+ random_strings[i] = NULL;
+ }
+
+ random_strings[0] = get_null_terminated(40, &data, &size);
+ random_strings[1] = get_null_terminated(40, &data, &size);
+ random_strings[2] = get_null_terminated(40, &data, &size);
+ random_strings[3] = get_null_terminated(40, &data, &size);
+
+ char *out_buf = NULL;
+ size_t out_size;
+ int root_type;
+ int ret =
+ flb_pack_json((char *)data, size, &out_buf, &out_size, &root_type, NULL);
+ if (ret == 0) {
+ size_t off = 0;
+ msgpack_unpacked result;
+ msgpack_unpacked_init(&result);
+ int ret2 = msgpack_unpack_next(&result, out_buf, out_size, &off);
+ if (ret2 == MSGPACK_UNPACK_SUCCESS) {
+ msgpack_object root = result.data;
+
+ /* Pass fuzz data into the multiline parser code */
+ test_multiline_parser(&root, rand_val);
+ }
+ msgpack_unpacked_destroy(&result);
+ free(out_buf);
+ } else {
+ test_multiline_parser(NULL, rand_val);
+ }
+
+ for (int i = 0; i < 4; i++) {
+ if (random_strings[i] != NULL) {
+ free(random_strings[i]);
+ }
+ }
+ return 0;
+}
diff --git a/fluent-bit/tests/internal/fuzzers/pack_json_state_fuzzer.c b/fluent-bit/tests/internal/fuzzers/pack_json_state_fuzzer.c
new file mode 100644
index 00000000..4549557a
--- /dev/null
+++ b/fluent-bit/tests/internal/fuzzers/pack_json_state_fuzzer.c
@@ -0,0 +1,25 @@
+#include <stdint.h>
+#include <stdlib.h>
+#include <fluent-bit/flb_pack.h>
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size){
+ int out_size= 0;
+ char *out_buf = NULL;
+ struct flb_pack_state state;
+ /* Set fuzzer-malloc chance of failure */
+ flb_malloc_mod = 25000;
+ flb_malloc_p = 0;
+
+ /* Exit early to avoid timeouts due to excessive size */
+ if (size > 4096)
+ return 0;
+
+ /* Target json packer */
+ flb_pack_state_init(&state);
+ flb_pack_json_state(data, size, &out_buf, &out_size, &state);
+ flb_pack_state_reset(&state);
+ if (out_buf != NULL)
+ flb_free(out_buf);
+
+ return 0;
+}
diff --git a/fluent-bit/tests/internal/fuzzers/parse_json_fuzzer.c b/fluent-bit/tests/internal/fuzzers/parse_json_fuzzer.c
new file mode 100644
index 00000000..dab8880d
--- /dev/null
+++ b/fluent-bit/tests/internal/fuzzers/parse_json_fuzzer.c
@@ -0,0 +1,69 @@
+/* Fluent Bit
+ * ==========
+ * Copyright (C) 2019-2021 The Fluent Bit Authors
+ * Copyright (C) 2015-2018 Treasure Data Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include <stdint.h>
+#include <string.h>
+#include <stdlib.h>
+#include <fluent-bit/flb_time.h>
+#include <fluent-bit/flb_parser.h>
+#include "flb_fuzz_header.h"
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size){
+ TIMEOUT_GUARD
+ void *out_buf = NULL;
+ size_t out_size = 0;
+ struct flb_time out_time;
+ struct flb_config *fuzz_config;
+ struct flb_parser *fuzz_parser;
+
+ /* Set flb_malloc_mod to be fuzzer-data dependent */
+ if (size < 4) {
+ return 0;
+ }
+ flb_malloc_p = 0;
+ flb_malloc_mod = *(int*)data;
+ data += 4;
+ size -= 4;
+
+ /* Avoid division by zero for modulo operations */
+ if (flb_malloc_mod == 0) {
+ flb_malloc_mod = 1;
+ }
+
+ /* json parser */
+ fuzz_config = flb_config_init();
+ if (fuzz_config == NULL) {
+ return 0;
+ }
+
+ fuzz_parser = flb_parser_create("fuzzer", "json", NULL, FLB_TRUE, NULL,
+ NULL, NULL, MK_FALSE, MK_TRUE, FLB_FALSE,
+ NULL, 0, NULL, fuzz_config);
+ if (fuzz_parser) {
+ flb_parser_do(fuzz_parser, (char*)data, size,
+ &out_buf, &out_size, &out_time);
+
+ if (out_buf != NULL) {
+ free(out_buf);
+ }
+
+ flb_parser_destroy(fuzz_parser);
+ }
+ flb_config_exit(fuzz_config);
+
+ return 0;
+}
diff --git a/fluent-bit/tests/internal/fuzzers/parse_logfmt_fuzzer.c b/fluent-bit/tests/internal/fuzzers/parse_logfmt_fuzzer.c
new file mode 100644
index 00000000..c65a2adb
--- /dev/null
+++ b/fluent-bit/tests/internal/fuzzers/parse_logfmt_fuzzer.c
@@ -0,0 +1,50 @@
+#include <stdint.h>
+#include <string.h>
+#include <stdlib.h>
+#include <fluent-bit/flb_time.h>
+#include <fluent-bit/flb_parser.h>
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size){
+ void *out_buf = NULL;
+ size_t out_size = 0;
+ struct flb_time out_time;
+ struct flb_config *fuzz_config;
+ struct flb_parser *fuzz_parser;
+
+ /* Set flb_malloc_mod to be fuzzer-data dependent */
+ if (size < 4) {
+ return 0;
+ }
+ flb_malloc_p = 0;
+ flb_malloc_mod = *(int*)data;
+ data += 4;
+ size -= 4;
+
+ /* Avoid division by zero for modulo operations */
+ if (flb_malloc_mod == 0) {
+ flb_malloc_mod = 1;
+ }
+
+ /* logfmt parser */
+ fuzz_config = flb_config_init();
+ if (fuzz_config == NULL) {
+ return 0;
+ }
+ fuzz_parser = flb_parser_create("fuzzer", "logfmt", NULL, FLB_TRUE,
+ NULL, NULL, NULL, MK_FALSE,
+ MK_TRUE, FLB_FALSE, NULL, 0, NULL,
+ fuzz_config);
+ if (fuzz_parser) {
+ flb_parser_do(fuzz_parser, (char*)data, size,
+ &out_buf, &out_size, &out_time);
+
+ if (out_buf != NULL) {
+ free(out_buf);
+ }
+ flb_parser_destroy(fuzz_parser);
+ }
+
+ flb_config_exit(fuzz_config);
+
+ return 0;
+}
diff --git a/fluent-bit/tests/internal/fuzzers/parse_ltsv_fuzzer.c b/fluent-bit/tests/internal/fuzzers/parse_ltsv_fuzzer.c
new file mode 100644
index 00000000..cb8bf95a
--- /dev/null
+++ b/fluent-bit/tests/internal/fuzzers/parse_ltsv_fuzzer.c
@@ -0,0 +1,35 @@
+#include <stdint.h>
+#include <string.h>
+#include <stdlib.h>
+#include <fluent-bit/flb_time.h>
+#include <fluent-bit/flb_parser.h>
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size){
+ void *out_buf = NULL;
+ size_t out_size = 0;
+ struct flb_time out_time;
+ struct flb_config *fuzz_config;
+ struct flb_parser *fuzz_parser;
+
+ /* Set fuzzer-malloc chance of failure */
+ flb_malloc_mod = 25000;
+ flb_malloc_p = 0;
+
+ /* ltsvc parser */
+ fuzz_config = flb_config_init();
+ fuzz_parser = flb_parser_create("fuzzer", "ltsv", NULL, FLB_TRUE,
+ NULL, NULL, NULL, MK_FALSE,
+ MK_TRUE, FLB_FALSE, NULL, 0, NULL,
+ fuzz_config);
+ flb_parser_do(fuzz_parser, (char*)data, size,
+ &out_buf, &out_size, &out_time);
+
+ if (out_buf != NULL) {
+ free(out_buf);
+ }
+
+ flb_parser_destroy(fuzz_parser);
+ flb_config_exit(fuzz_config);
+
+ return 0;
+}
diff --git a/fluent-bit/tests/internal/fuzzers/parser_fuzzer.c b/fluent-bit/tests/internal/fuzzers/parser_fuzzer.c
new file mode 100644
index 00000000..a0355551
--- /dev/null
+++ b/fluent-bit/tests/internal/fuzzers/parser_fuzzer.c
@@ -0,0 +1,203 @@
+/* Fluent Bit
+ * ==========
+ * Copyright (C) 2019-2021 The Fluent Bit Authors
+ * Copyright (C) 2015-2018 Treasure Data Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include <stdint.h>
+#include <string.h>
+#include <stdlib.h>
+#include <fluent-bit/flb_utils.h>
+#include <fluent-bit/flb_time.h>
+#include <fluent-bit/flb_parser.h>
+#include <fluent-bit/flb_parser_decoder.h>
+
+#include "flb_fuzz_header.h"
+
+#define TYPES_LEN 5
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
+{
+ TIMEOUT_GUARD
+
+ char *format = NULL;
+ char *time_fmt = NULL;
+ char *time_key = NULL;
+ char *time_offset = NULL;
+ char *pregex = NULL;
+ struct flb_parser_types *types = NULL;
+ struct flb_config *fuzz_config = NULL;
+ struct flb_parser *fuzz_parser = NULL;
+ int time_keep = 0;
+ int types_len = 0;
+
+ /* Set fuzzer-malloc chance of failure */
+ flb_malloc_mod = 25000;
+ flb_malloc_p = 0;
+
+ if (size < 100) {
+ return 0;
+ }
+
+ /* json parser */
+ fuzz_config = flb_config_init();
+
+ /* format + pregex */
+ if (GET_MOD_EQ(4,0)) {
+ format = "json";
+ }
+ else if (GET_MOD_EQ(4,1)) {
+ format = "regex";
+#ifdef PREG_FUZZ
+ pregex = malloc(30);
+ pregex[29] = '\0';
+ memcpy(pregex, data, 29);
+ data += 29;
+ size -= 29;
+#else
+ pregex = "^(?<INT>[^ ]+) (?<FLOAT>[^ ]+) (?<BOOL>[^ ]+) (?<STRING>.+)$";
+#endif
+ }
+ else if (GET_MOD_EQ(4,2)) {
+ format = "ltsv";
+ }
+ else {
+ format = "logfmt";
+ }
+ MOVE_INPUT(1);
+
+ /* time_fmt */
+ if (GET_MOD_EQ(2,1)) {
+ time_fmt = get_null_terminated(15, &data, &size);
+ }
+ MOVE_INPUT(1);
+
+ /* time_key */
+ if (GET_MOD_EQ(2,1)) {
+ time_key = get_null_terminated(15, &data, &size);
+ }
+ MOVE_INPUT(1);
+
+ /* time_offset */
+ if (GET_MOD_EQ(2,1)) {
+ time_offset = get_null_terminated(15, &data, &size);
+ }
+ MOVE_INPUT(1);
+
+ /* time_keep */
+ time_keep = (GET_MOD_EQ(2,1)) ? MK_TRUE : MK_FALSE;
+ MOVE_INPUT(1);
+
+ /* types_str */
+ if (GET_MOD_EQ(2,1)) {
+ types = flb_malloc(sizeof(struct flb_parser_types) * TYPES_LEN);
+ char *parser_type_keys[5] = {"AAA", "BBB", "CCC", "DDD", "EEE" };
+ int parser_types[5] = {FLB_PARSER_TYPE_INT, FLB_PARSER_TYPE_FLOAT,
+ FLB_PARSER_TYPE_BOOL, FLB_PARSER_TYPE_STRING,
+ FLB_PARSER_TYPE_HEX};
+ for (int i = 0; i < TYPES_LEN; i++) {
+ types[i].key = strdup(parser_type_keys[i]);
+ types[i].key_len = strlen(parser_type_keys[i]);
+ types[i].type = parser_types[i];
+ }
+ types_len = TYPES_LEN;
+ }
+ MOVE_INPUT(1);
+
+ /* decoders */
+ struct mk_list *list = NULL;
+ if (GET_MOD_EQ(2,1)) {
+ MOVE_INPUT(1);
+ list = flb_malloc(sizeof(struct mk_list));
+ mk_list_init(list);
+
+ struct flb_parser_dec *dec = malloc(sizeof(struct flb_parser_dec));
+ dec->key = flb_sds_create_len("AAA", 3);
+ dec->buffer = flb_sds_create_size(FLB_PARSER_DEC_BUF_SIZE);
+ dec->add_extra_keys = FLB_TRUE;
+ mk_list_init(&dec->rules);
+ mk_list_add(&dec->_head, list);
+
+ struct flb_parser_dec_rule *dec_rule = malloc(sizeof(struct flb_parser_dec_rule));
+ dec_rule->type = (int)(data[0] % 0x02);
+ MOVE_INPUT(1);
+ dec_rule->backend = (int)(data[0] % 0x04);
+ MOVE_INPUT(1);
+ dec_rule->action = (int)data[0] % 0x03;
+ mk_list_add(&dec_rule->_head, &dec->rules);
+
+ if (GET_MOD_EQ(2,1)) {
+ struct flb_parser_dec_rule *dec_rule2 = malloc(sizeof(struct flb_parser_dec_rule));
+ dec_rule2->type = (int)(data[0] % 0x02);
+ MOVE_INPUT(1);
+ dec_rule2->backend = (int)(data[0] % 0x04);
+ MOVE_INPUT(1);
+ dec_rule->action = (int)data[0] % 0x03;
+ mk_list_add(&dec_rule2->_head, &dec->rules);
+ }
+ }
+ MOVE_INPUT(1);
+ /* print our config struct */
+ flb_utils_print_setup(fuzz_config);
+
+ /* now call into the parser */
+ fuzz_parser = flb_parser_create("fuzzer", format, pregex, FLB_TRUE,
+ time_fmt, time_key, time_offset, time_keep, 0, FLB_FALSE,
+ types, types_len, list, fuzz_config);
+
+ /* Second step is to use the random parser to parse random input */
+ if (fuzz_parser != NULL) {
+ void *out_buf = NULL;
+ size_t out_size = 0;
+ struct flb_time out_time;
+ flb_parser_do(fuzz_parser, (char*)data, size,
+ &out_buf, &out_size, &out_time);
+ if (out_buf != NULL) {
+ free(out_buf);
+ }
+ flb_parser_destroy(fuzz_parser);
+ }
+ else {
+ /* Parser creation failed but we still need to clean
+ * up types and decoders */
+ if (types != NULL) {
+ for (int i=0; i< TYPES_LEN; i++){
+ flb_free(types[i].key);
+ }
+ flb_free(types);
+ }
+ if (list != NULL) {
+ flb_parser_decoder_list_destroy(list);
+ }
+ }
+
+ /* Cleanup everything but the parser */
+ flb_config_exit(fuzz_config);
+ if (time_fmt != NULL) {
+ flb_free(time_fmt);
+ }
+ if (time_key != NULL) {
+ flb_free(time_key);
+ }
+ if (time_offset != NULL) {
+ flb_free(time_offset);
+ }
+#ifdef PREG_FUZZ
+ if (pregex != NULL) {
+ flb_free(pregex);
+ }
+#endif
+
+ return 0;
+}
diff --git a/fluent-bit/tests/internal/fuzzers/record_ac_fuzzer.c b/fluent-bit/tests/internal/fuzzers/record_ac_fuzzer.c
new file mode 100644
index 00000000..9f2ae580
--- /dev/null
+++ b/fluent-bit/tests/internal/fuzzers/record_ac_fuzzer.c
@@ -0,0 +1,101 @@
+#include <fluent-bit/flb_info.h>
+#include <fluent-bit/flb_mem.h>
+#include <fluent-bit/flb_error.h>
+#include <fluent-bit/flb_sds.h>
+#include <fluent-bit/flb_pack.h>
+#include <fluent-bit/flb_record_accessor.h>
+#include <fluent-bit/record_accessor/flb_ra_parser.h>
+#include <msgpack.h>
+#include "flb_fuzz_header.h"
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
+{
+ /* Limit size to 32KB */
+ if (size > 32768 || size < 6) {
+ return 0;
+ }
+
+ char *outbuf = NULL;
+ char *ra_str = NULL;
+ size_t outsize;
+ int type;
+ int len;
+ size_t off = 0;
+ msgpack_object map;
+
+ /* Set flb_malloc_mod to be fuzzer-data dependent */
+ flb_malloc_p = 0;
+ flb_malloc_mod = *(int*)data;
+ data += 4;
+ size -= 4;
+
+ /* Avoid division by zero for modulo operations */
+ if (flb_malloc_mod == 0) {
+ flb_malloc_mod = 1;
+ }
+
+ if (size < 100) {
+ return 0;
+ }
+
+ struct flb_record_accessor *ra = NULL;
+
+ /* Sample JSON message */
+ len = 60;
+ char *json_raw = get_null_terminated(len, &data, &size);
+
+ /* Convert to msgpack */
+ int ret = flb_pack_json(json_raw, len, &outbuf, &outsize, &type, NULL);
+ if (ret == -1) {
+ flb_free(json_raw);
+ return 0;
+ }
+ flb_free(json_raw);
+
+ char *null_terminated = get_null_terminated(size, &data, &size);
+
+ ra_str = flb_sds_create(null_terminated);
+ if (ra_str != NULL) {
+ ra = flb_ra_create(ra_str, FLB_FALSE);
+ if (!ra) {
+ flb_sds_destroy(ra_str);
+ flb_free(null_terminated);
+ flb_free(outbuf);
+ return 0;
+ }
+
+ flb_ra_is_static(ra);
+
+ msgpack_unpacked result;
+ msgpack_unpacked_init(&result);
+ msgpack_unpack_next(&result, outbuf, outsize, &off);
+ map = result.data;
+
+ flb_sds_t str = flb_ra_translate(ra, NULL, -1, map, NULL);
+ if (!str) {
+ flb_ra_destroy(ra);
+ flb_sds_destroy(ra_str);
+ msgpack_unpacked_destroy(&result);
+
+ /* General cleanup */
+ flb_free(null_terminated);
+ flb_free(outbuf);
+ return 0;
+ }
+ flb_ra_dump(ra);
+
+
+ flb_sds_destroy(str);
+ flb_ra_destroy(ra);
+ flb_sds_destroy(ra_str);
+ msgpack_unpacked_destroy(&result);
+ }
+
+ if (outbuf != NULL) {
+ flb_free(outbuf);
+ }
+ if (null_terminated != NULL) {
+ flb_free(null_terminated);
+ }
+ return 0;
+}
diff --git a/fluent-bit/tests/internal/fuzzers/signv4_fuzzer.c b/fluent-bit/tests/internal/fuzzers/signv4_fuzzer.c
new file mode 100644
index 00000000..4393d159
--- /dev/null
+++ b/fluent-bit/tests/internal/fuzzers/signv4_fuzzer.c
@@ -0,0 +1,111 @@
+#include <fluent-bit/flb_info.h>
+#include <fluent-bit/flb_mem.h>
+#include <fluent-bit/flb_http_client.h>
+#include <fluent-bit/flb_upstream.h>
+#include <fluent-bit/flb_signv4.h>
+#include <fluent-bit/flb_aws_credentials.h>
+#include <monkey/mk_core.h>
+#include <unistd.h>
+#include <fluent-bit/flb_sds.h>
+#include "flb_fuzz_header.h"
+
+#define AWS_ACCESS_KEY_ID "AWS_ACCESS_KEY_ID"
+#define AWS_SECRET_ACCESS_KEY "AWS_SECRET_ACCESS_KEY"
+#define AWS_SESSION_TOKEN "AWS_SESSION_TOKEN"
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
+{
+ if (size < 59) {
+ return 0;
+ }
+
+ /* Set flb_malloc_mod to be fuzzer-data dependent */
+ flb_malloc_p = 0;
+ flb_malloc_mod = *(int*)data;
+ data += 4;
+ size -= 4;
+
+ /* Avoid division by zero for modulo operations */
+ if (flb_malloc_mod == 0) {
+ flb_malloc_mod = 1;
+ }
+
+ char s3_mode = data[0];
+ MOVE_INPUT(1)
+ int method = (int)data[0];
+
+ /* Prepare a general null-terminated string */
+ char *uri = get_null_terminated(50, &data, &size);
+ char *null_terminated = get_null_terminated(size, &data, &size);
+
+ /* Now begin the core work of the fuzzer */
+ struct flb_config *config;
+ struct mk_list *tests;
+ struct flb_aws_provider *provider;
+ config = flb_calloc(1, sizeof(struct flb_config));
+ if (!config) {
+ flb_free(uri);
+ flb_free(null_terminated);
+ return 0;
+ }
+ mk_list_init(&config->upstreams);
+ provider = flb_aws_env_provider_create();
+
+ /* Create the necessary http context */
+ struct flb_upstream *http_u;
+ struct flb_connection *http_u_conn = NULL;
+ struct flb_http_client *http_c;
+ struct flb_config *http_config;
+
+ http_config = flb_config_init();
+ if (http_config == NULL) {
+ flb_aws_provider_destroy(provider);
+ flb_free(uri);
+ flb_free(null_terminated);
+ flb_free(config);
+ return 0;
+ }
+
+ http_u = flb_upstream_create(http_config, "127.0.0.1", 8001, 0, NULL);
+ if (http_u != NULL) {
+ http_u_conn = flb_calloc(1, sizeof(struct flb_connection));
+ if (http_u_conn != NULL) {
+ http_u_conn->upstream = http_u;
+
+ http_c = flb_http_client(http_u_conn, method, uri,
+ null_terminated, size, "127.0.0.1", 8001, NULL, 0);
+ if (http_c) {
+ /* Call into the main target flb_signv4_do*/
+ time_t t = 1440938160;
+ char *region = "us-east-1";
+ char *access_key = "AKIDEXAMPLE";
+ char *service = "service";
+ char *secret_key = "wJalrXUtnFEMI/K7MDENG+bPxRfiCYEXAMPLEKEY";
+ int ret = setenv(AWS_ACCESS_KEY_ID, access_key, 1);
+ if (ret >= 0) {
+ ret = setenv(AWS_SECRET_ACCESS_KEY, secret_key, 1);
+ if (ret >= 0) {
+ flb_sds_t signature = flb_signv4_do(http_c, FLB_TRUE, FLB_FALSE,
+ t, region, service, s3_mode, NULL, provider);
+ if (signature) {
+ flb_sds_destroy(signature);
+ }
+ }
+ }
+ flb_http_client_destroy(http_c);
+ }
+ }
+ flb_upstream_destroy(http_u);
+ }
+
+ /* Cleanup */
+ flb_config_exit(http_config);
+ flb_aws_provider_destroy(provider);
+ flb_free(config);
+
+ flb_free(null_terminated);
+ flb_free(http_u_conn);
+ flb_free(uri);
+
+ return 0;
+}
diff --git a/fluent-bit/tests/internal/fuzzers/strp_fuzzer.c b/fluent-bit/tests/internal/fuzzers/strp_fuzzer.c
new file mode 100644
index 00000000..3de21c7a
--- /dev/null
+++ b/fluent-bit/tests/internal/fuzzers/strp_fuzzer.c
@@ -0,0 +1,35 @@
+#include <stdint.h>
+#include <string.h>
+#include <stdlib.h>
+#include <fluent-bit/flb_time.h>
+#include <fluent-bit/flb_parser.h>
+#include <msgpack.h>
+#include <fluent-bit/flb_info.h>
+#include <fluent-bit/flb_mem.h>
+#include <fluent-bit/flb_pack.h>
+#include <fluent-bit/flb_utils.h>
+#include <fluent-bit/flb_version.h>
+#include <fluent-bit/flb_strptime.h>
+
+#include "flb_fuzz_header.h"
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
+{
+ if (size < 40) {
+ return 0;
+ }
+
+ /* Set fuzzer-malloc chance of failure */
+ flb_malloc_mod = 25000;
+ flb_malloc_p = 0;
+
+ char *fmt = get_null_terminated(size - 30, &data, &size);
+ char *buf = get_null_terminated(size, &data, &size);
+
+ struct tm tt;
+ flb_strptime(buf, fmt, &tt);
+
+ flb_free(buf);
+ flb_free(fmt);
+ return 0;
+}
diff --git a/fluent-bit/tests/internal/fuzzers/utils_fuzzer.c b/fluent-bit/tests/internal/fuzzers/utils_fuzzer.c
new file mode 100644
index 00000000..2352adf3
--- /dev/null
+++ b/fluent-bit/tests/internal/fuzzers/utils_fuzzer.c
@@ -0,0 +1,242 @@
+/* Fluent Bit
+ * ==========
+ * Copyright (C) 2019-2021 The Fluent Bit Authors
+ * Copyright (C) 2015-2018 Treasure Data Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdint.h>
+#include <string.h>
+#include <stdlib.h>
+#include <msgpack.h>
+#include <fluent-bit/flb_mem.h>
+#include <fluent-bit/flb_utils.h>
+#include <fluent-bit/flb_slist.h>
+#include <fluent-bit/flb_gzip.h>
+#include <fluent-bit/flb_hash_table.h>
+#include <fluent-bit/flb_uri.h>
+#include <fluent-bit/flb_hash.h>
+#include <fluent-bit/flb_regex.h>
+#include "flb_fuzz_header.h"
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
+{
+ TIMEOUT_GUARD
+
+ if (size < 750) {
+ return 0;
+ }
+
+ /* Set fuzzer-malloc chance of failure */
+ flb_malloc_mod = 25000;
+ flb_malloc_p = 0;
+
+ uint64_t ran_hash = *(uint64_t *)data;
+ char *null_terminated1 = get_null_terminated(25, &data, &size);
+ char *null_terminated2 = get_null_terminated(25, &data, &size);
+ char *null_terminated3 = get_null_terminated(25, &data, &size);
+
+ /* Prepare a general null-terminated string */
+ char *null_terminated = (char*)malloc(size+1);
+ null_terminated[size] = '\0';
+ memcpy(null_terminated, data, size);
+
+ /* Fuzzing of flb_utils.c */
+ int sec;
+ long nsec;
+ size_t new_size;
+ char *prot = NULL;
+ char *host = NULL;
+ char *port = NULL;
+ char *uri = NULL;
+ char *new_dst = NULL;
+
+ if (flb_utils_write_str_buf(null_terminated, size, &new_dst, &new_size) == 0) {
+ flb_free(new_dst);
+ }
+
+ struct mk_list *list = flb_utils_split(null_terminated, 'A', 3);
+ if (list != NULL) {
+ flb_utils_split_free(list);
+ }
+ struct mk_list *list2 = flb_utils_split_quoted(null_terminated, 'A', 3);
+ if (list2 != NULL) {
+ flb_utils_split_free(list2);
+ }
+
+ if (flb_utils_url_split(null_terminated, &prot, &host, &port, &uri) == 0) {
+ flb_free(prot);
+ flb_free(port);
+ flb_free(host);
+ flb_free(uri);
+ }
+
+ char *split_protocol = NULL;
+ char *split_username = NULL;
+ char *split_password = NULL;
+ char *split_host = NULL;
+ char *split_port = NULL;
+ if (flb_utils_proxy_url_split(null_terminated, &split_protocol,
+ &split_username, &split_password, &split_host, &split_port) == 0) {
+ if (split_protocol) {
+ flb_free(split_protocol);
+ }
+ if (split_username) {
+ flb_free(split_username);
+ }
+ if (split_password) {
+ flb_free(split_password);
+ }
+ if (split_host) {
+ flb_free(split_host);
+ }
+ if (split_port) {
+ flb_free(split_port);
+ }
+ }
+
+
+ flb_utils_size_to_bytes(null_terminated);
+ flb_utils_time_split(null_terminated, &sec, &nsec);
+ flb_utils_time_to_seconds(null_terminated);
+ flb_utils_bool(null_terminated);
+ flb_utils_hex2int(null_terminated, size);
+
+ /* Fuzzong of flb_uri.c */
+ struct flb_uri *uri2 = NULL;
+ uri2 = flb_uri_create(null_terminated);
+ if (uri2 != NULL) {
+ flb_uri_get(uri2, (int)data[0]);
+ flb_uri_dump(uri2);
+ flb_uri_destroy(uri2);
+ }
+ flb_sds_t encoded = flb_uri_encode((char*)data, size);
+ if (encoded != NULL) {
+ flb_sds_destroy(encoded);
+ }
+
+ /* Fuzzing of flb_hash.c */
+ struct flb_hash_table *ht = NULL;
+ ht = flb_hash_table_create((int)(data[2] % 0x04),
+ (size_t)data[0],
+ (int)data[1]);
+ if (ht != NULL) {
+ flb_hash_table_add(ht, null_terminated, size, null_terminated, size);
+
+ char *out_buf = NULL;
+ size_t out_size;
+ flb_hash_table_get(ht, null_terminated, size, (void **)&out_buf, &out_size);
+
+ /* now let's create some more instances */
+ char *instances1[128] = { NULL };
+ char *instances2[128] = { NULL };
+ for (int i = 0; i < 128; i++) {
+ char *in1 = malloc(3);
+ char *in2 = malloc(3);
+ memcpy(in1, data+(i*4), 2);
+ memcpy(in2, data+(i*4)+2, 2);
+ in1[2] = '\0';
+ in2[2] = '\0';
+ flb_hash_table_add(ht, in1, 2, in2, 2);
+ instances1[i] = in1;
+ instances2[i] = in2;
+ }
+
+ for(int i = 0; i < 20; i++) {
+ char *hash_out_buf;
+ size_t hash_out_size;
+ flb_hash_table_get_by_id(ht, (int)data[i], null_terminated,
+ (const char **)&hash_out_buf, &hash_out_size);
+ }
+
+ flb_hash_table_del(ht, null_terminated1);
+ flb_hash_table_exists(ht, ran_hash);
+ flb_hash_table_del_ptr(ht, null_terminated2, strlen(null_terminated2), NULL);
+ flb_hash_table_get_ptr(ht, null_terminated3, strlen(null_terminated3));
+
+ flb_hash_table_destroy(ht);
+ for (int i =0; i<128; i++) {
+ flb_free(instances1[i]);
+ flb_free(instances2[i]);
+ }
+ }
+
+ /* sds */
+ flb_sds_t fs = flb_sds_create_len((const char*)data, size);
+ if (fs != NULL) {
+ fs = flb_sds_cat_esc(fs, "AAABBBCCC", 9, "ABC", 3);
+ if (fs != NULL) {
+ flb_sds_destroy(fs);
+ }
+ }
+
+ /* Fuzzing of flb_gzip.c */
+ void *str = NULL;
+ size_t len;
+ void *out_data = NULL;
+ size_t out_len;
+ if (flb_gzip_compress((char*)data, size, &str, &len) != -1) {
+ flb_gzip_uncompress(str, len, &out_data, &out_len);
+ }
+ if (str != NULL) {
+ free(str);
+ }
+ if (out_data != NULL) {
+ free(out_data);
+ }
+ void *out_data2 = NULL;
+ size_t out2_len;
+ int uncompress_ret = flb_gzip_uncompress((char*)data, size, &out_data2, &out2_len);
+ if (uncompress_ret != -1 && out_data2 != NULL) {
+ flb_free(out_data2);
+ }
+
+ /* Fuzzing the sha routines */
+ struct flb_hash sha512;
+ uint8_t buf[64];
+
+ flb_hash_init(&sha512, FLB_HASH_SHA512);
+ flb_hash_update(&sha512, (unsigned char *) null_terminated, 32);
+ flb_hash_update(&sha512, (unsigned char *) null_terminated+32, 32);
+ flb_hash_update(&sha512, (unsigned char *) null_terminated+64, 32);
+ flb_hash_finalize(&sha512, buf, sizeof(buf));
+ flb_hash_cleanup(&sha512);
+
+ /* regex */
+ char *pregex = "^(?<INT>[^ ]+) (?<FLOAT>[^ ]+) (?<BOOL>[^ ]+) (?<STRING>.+)$";
+ flb_regex_init();
+ struct flb_regex *freg = flb_regex_create(pregex);
+ if (freg != NULL) {
+ flb_regex_match(freg, (unsigned char*)null_terminated, size);
+ flb_regex_destroy(freg);
+ }
+ flb_regex_exit();
+
+ /* slist */
+ struct mk_list list3;
+ flb_slist_create(&list3);
+ flb_sds_t slist_str = flb_sds_create_len((const char*)data, size);
+ flb_slist_add_sds(&list3, slist_str);
+ flb_slist_entry_get(&list3, 100);
+ flb_slist_dump(&list3);
+ flb_slist_destroy(&list3);
+
+
+ /* General cleanup */
+ flb_free(null_terminated);
+ flb_free(null_terminated1);
+ flb_free(null_terminated2);
+ flb_free(null_terminated3);
+ return 0;
+}
diff --git a/fluent-bit/tests/internal/gelf.c b/fluent-bit/tests/internal/gelf.c
new file mode 100644
index 00000000..0a3b21e6
--- /dev/null
+++ b/fluent-bit/tests/internal/gelf.c
@@ -0,0 +1,101 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+#include <fluent-bit/flb_info.h>
+#include <fluent-bit/flb_pack.h>
+#include <fluent-bit/flb_sds.h>
+#include <fluent-bit/flb_time.h>
+#include <msgpack.h>
+
+#include "flb_tests_internal.h"
+#include <string.h>
+
+#define EXPECTED_OUT \
+ "{\"version\":\"1.1\", \"short_message\":\"true, 2019, str\", \"_t2\":\"false\", \"timestamp\":337647600.1234}"
+
+void test_gelf_pack()
+{
+ msgpack_sbuffer mp_sbuf;
+ msgpack_packer mp_pck;
+ struct flb_time ts;
+ struct flb_gelf_fields fields = {0};
+ flb_sds_t out;
+
+ /* Pack sample msgpack */
+ msgpack_sbuffer_init(&mp_sbuf);
+ msgpack_packer_init(&mp_pck, &mp_sbuf, msgpack_sbuffer_write);
+
+
+ ts.tm.tv_sec = 337647600;
+ ts.tm.tv_nsec = 1234111111;
+
+ msgpack_pack_map(&mp_pck, 2);
+ msgpack_pack_str(&mp_pck, 2);
+ msgpack_pack_str_body(&mp_pck, "t1", 2);
+ msgpack_pack_array(&mp_pck, 3);
+ msgpack_pack_true(&mp_pck);
+ msgpack_pack_uint64(&mp_pck, 2019);
+ msgpack_pack_str(&mp_pck, 3);
+ msgpack_pack_str_body(&mp_pck, "str", 3);
+ msgpack_pack_str(&mp_pck, 2);
+ msgpack_pack_str_body(&mp_pck, "t2", 2);
+ msgpack_pack_false(&mp_pck);
+
+ fields.short_message_key = flb_sds_create("t1");
+ out = flb_msgpack_raw_to_gelf(mp_sbuf.data, mp_sbuf.size, &ts, &fields);
+ TEST_CHECK(out != NULL);
+
+ TEST_CHECK(strcmp(out, EXPECTED_OUT) == 0);
+ flb_sds_destroy(out);
+ flb_sds_destroy(fields.short_message_key);
+ msgpack_sbuffer_destroy(&mp_sbuf);
+}
+
+#define EXPECTED_OUT_MSEC \
+ "{\"version\":\"1.1\", \"short_message\":\"true, 2019, str\", \"_t2\":\"false\", \"timestamp\":337647600.012}"
+
+/* https://github.com/fluent/fluent-bit/issues/3727 */
+void test_gelf_pack_msec()
+{
+ msgpack_sbuffer mp_sbuf;
+ msgpack_packer mp_pck;
+ struct flb_time ts;
+ struct flb_gelf_fields fields = {0};
+ flb_sds_t out;
+
+ /* Pack sample msgpack */
+ msgpack_sbuffer_init(&mp_sbuf);
+ msgpack_packer_init(&mp_pck, &mp_sbuf, msgpack_sbuffer_write);
+
+
+ ts.tm.tv_sec = 337647600;
+ ts.tm.tv_nsec = 12341111; /* 12.34msec */
+
+ msgpack_pack_map(&mp_pck, 2);
+ msgpack_pack_str(&mp_pck, 2);
+ msgpack_pack_str_body(&mp_pck, "t1", 2);
+ msgpack_pack_array(&mp_pck, 3);
+ msgpack_pack_true(&mp_pck);
+ msgpack_pack_uint64(&mp_pck, 2019);
+ msgpack_pack_str(&mp_pck, 3);
+ msgpack_pack_str_body(&mp_pck, "str", 3);
+ msgpack_pack_str(&mp_pck, 2);
+ msgpack_pack_str_body(&mp_pck, "t2", 2);
+ msgpack_pack_false(&mp_pck);
+
+ fields.short_message_key = flb_sds_create("t1");
+ out = flb_msgpack_raw_to_gelf(mp_sbuf.data, mp_sbuf.size, &ts, &fields);
+ TEST_CHECK(out != NULL);
+
+ if(!TEST_CHECK(strcmp(out, EXPECTED_OUT_MSEC) == 0)) {
+ TEST_MSG("out=%s", out);
+ }
+ flb_sds_destroy(out);
+ flb_sds_destroy(fields.short_message_key);
+ msgpack_sbuffer_destroy(&mp_sbuf);
+}
+
+TEST_LIST = {
+ {"gelf_pack", test_gelf_pack},
+ {"gelf_pack_msec", test_gelf_pack_msec},
+ { 0 }
+};
diff --git a/fluent-bit/tests/internal/gzip.c b/fluent-bit/tests/internal/gzip.c
new file mode 100644
index 00000000..a3f82341
--- /dev/null
+++ b/fluent-bit/tests/internal/gzip.c
@@ -0,0 +1,46 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+#include <fluent-bit/flb_info.h>
+#include <fluent-bit/flb_mem.h>
+#include <fluent-bit/flb_gzip.h>
+
+#include "flb_tests_internal.h"
+
+/* Sample data */
+char *morpheus = "This is your last chance. After this, there is no "
+ "turning back. You take the blue pill - the story ends, you wake up in "
+ "your bed and believe whatever you want to believe. You take the red pill,"
+ "you stay in Wonderland and I show you how deep the rabbit-hole goes.";
+
+void test_compress()
+{
+ int ret;
+ int sample_len;
+ char *in_data = morpheus;
+ size_t in_len;
+ void *str;
+ size_t len;
+
+ sample_len = strlen(morpheus);
+ in_len = sample_len;
+ ret = flb_gzip_compress(in_data, in_len, &str, &len);
+ TEST_CHECK(ret == 0);
+
+ in_data = str;
+ in_len = len;
+
+ ret = flb_gzip_uncompress(in_data, in_len, &str, &len);
+ TEST_CHECK(ret == 0);
+
+ TEST_CHECK(sample_len == len);
+ ret = memcmp(morpheus, str, sample_len);
+ TEST_CHECK(ret == 0);
+
+ flb_free(in_data);
+ flb_free(str);
+}
+
+TEST_LIST = {
+ {"compress", test_compress},
+ { 0 }
+};
diff --git a/fluent-bit/tests/internal/hash.c b/fluent-bit/tests/internal/hash.c
new file mode 100644
index 00000000..3ba90a65
--- /dev/null
+++ b/fluent-bit/tests/internal/hash.c
@@ -0,0 +1,165 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+#include <fluent-bit/flb_info.h>
+#include <fluent-bit/flb_hash.h>
+
+#include "flb_tests_internal.h"
+
+#define SHA512_ABCDEF "e32ef19623e8ed9d267f657a81944b3d07adbb768518068e88435745564e8d4150a0a703be2a7d88b61e3d390c2bb97e2d4c311fdc69d6b1267f05f59aa920e7"
+#define SHA512_OFFBYONE "ca8b236e13383f1f2293c9e286376444e99b7f180ba85713f140b55795fd2f8625d8b84201154d7956b74e2a1e0d5fbff1b61c7288c3f45834ad409e7bdfe536"
+
+static size_t get_sample_digest_data(unsigned char ***buffer_list,
+ size_t **length_list,
+ char **precomputed_digest,
+ int *precomputed_digest_type)
+{
+ static const int digest_type = FLB_HASH_SHA512;
+ static size_t lengths[] = {0, 0, 0, 0};
+ static const char *buffers[] = {
+ "This is one ",
+ "of the four ",
+ "buffers that will be passed ",
+ "to the simple batched digest processor function"
+ };
+ static const char *digest = "65cdb3e49288e227eeacc939010f3e98ef5d4f619dfc629c9cd6d2d6c357e534" \
+ "8f69a87a05ad336d7849e8c2ebd6ba68deeed433aa7b876bbaf1bb173366bf88";
+ size_t index;
+
+ if (lengths[0] == 0) {
+ for (index = 0 ; index < 4 ; index++) {
+ lengths[index] = strlen(buffers[index]);
+ }
+ }
+
+ *buffer_list = (unsigned char **) buffers;
+ *length_list = lengths;
+ *precomputed_digest = (char *) digest;
+ *precomputed_digest_type = digest_type;
+
+ return 4;
+}
+
+static void hexlify(uint8_t *hash, char *out)
+{
+ int i;
+ static const char hex[] = "0123456789abcdef";
+ char *buf = out;
+
+ for (i = 0; i < 64; i++) {
+ *buf++ = hex[hash[i] >> 4];
+ *buf++ = hex[hash[i] & 0xf];
+ }
+}
+
+static void test_digest_abcdef()
+{
+ struct flb_hash digest;
+ char dhex[128];
+ uint8_t buf[64];
+
+ flb_hash_init(&digest, FLB_HASH_SHA512);
+ flb_hash_update(&digest, (unsigned char *) "abc", 3);
+ flb_hash_update(&digest, (unsigned char *) "def", 3);
+ flb_hash_finalize(&digest, buf, sizeof(buf));
+ flb_hash_cleanup(&digest);
+
+ hexlify(buf, dhex);
+
+ TEST_CHECK(memcmp(dhex, SHA512_ABCDEF, 128) == 0);
+}
+
+static void test_digest_offbyone()
+{
+ struct flb_hash digest;
+ uint8_t buf[64];
+ char dhex[128];
+
+ flb_hash_init(&digest, FLB_HASH_SHA512);
+ flb_hash_update(&digest, (unsigned char *) "0123456789abcdef0123456789abcdef", 32);
+ flb_hash_update(&digest, (unsigned char *) "0123456789abcdef0123456789abcdef", 32);
+ flb_hash_update(&digest, (unsigned char *) "0123456789abcdef0123456789abcdef", 32);
+ flb_hash_update(&digest, (unsigned char *) "0123456789abcdef0123456789abcde", 31);
+ flb_hash_finalize(&digest, buf, sizeof(buf));
+ flb_hash_cleanup(&digest);
+
+ hexlify(buf, dhex);
+
+ TEST_CHECK(memcmp(dhex, SHA512_OFFBYONE, 128) == 0);
+
+}
+
+static void test_digest_standard()
+{
+ char hex_digest[128];
+ int ref_digest_type;
+ char *ref_hex_digest;
+ uint8_t raw_digest[64];
+ size_t buffer_count;
+ unsigned char **buffer_list;
+ size_t *length_list;
+ struct flb_hash digest;
+ int result;
+ size_t index;
+
+ buffer_count = get_sample_digest_data(&buffer_list, &length_list,
+ &ref_hex_digest, &ref_digest_type);
+
+ TEST_CHECK(buffer_count > 0);
+ TEST_CHECK(ref_digest_type == FLB_HASH_SHA512);
+
+ result = flb_hash_init(&digest, ref_digest_type);
+ TEST_CHECK(result == FLB_CRYPTO_SUCCESS);
+
+ for (index = 0 ; index < buffer_count ; index++) {
+ result = flb_hash_update(&digest, buffer_list[index], length_list[index]);
+ TEST_CHECK(result == FLB_CRYPTO_SUCCESS);
+ }
+
+ result = flb_hash_finalize(&digest, raw_digest, sizeof(raw_digest));
+ TEST_CHECK(result == FLB_CRYPTO_SUCCESS);
+
+ hexlify(raw_digest, hex_digest);
+
+ TEST_CHECK(memcmp(hex_digest, ref_hex_digest, 128) == 0);
+
+ flb_hash_cleanup(&digest);
+}
+
+static void test_digest_simple_batch()
+{
+ char hex_digest[128];
+ int ref_digest_type;
+ char *ref_hex_digest;
+ uint8_t raw_digest[64];
+ size_t buffer_count;
+ unsigned char **buffer_list;
+ size_t *length_list;
+ int result;
+
+ buffer_count = get_sample_digest_data(&buffer_list, &length_list,
+ &ref_hex_digest, &ref_digest_type);
+
+ TEST_CHECK(buffer_count > 0);
+ TEST_CHECK(ref_digest_type == FLB_HASH_SHA512);
+
+ result = flb_hash_simple_batch(ref_digest_type,
+ buffer_count,
+ buffer_list,
+ length_list,
+ raw_digest,
+ sizeof(raw_digest));
+
+ TEST_CHECK(result == FLB_CRYPTO_SUCCESS);
+
+ hexlify(raw_digest, hex_digest);
+
+ TEST_CHECK(memcmp(hex_digest, ref_hex_digest, 128) == 0);
+}
+
+TEST_LIST = {
+ { "test_digest_simple_batch", test_digest_simple_batch },
+ { "test_digest_standard", test_digest_standard },
+ { "test_digest_abcdef", test_digest_abcdef },
+ { "test_digest_offbyone", test_digest_offbyone },
+ { 0 }
+};
diff --git a/fluent-bit/tests/internal/hashtable.c b/fluent-bit/tests/internal/hashtable.c
new file mode 100644
index 00000000..a7066aeb
--- /dev/null
+++ b/fluent-bit/tests/internal/hashtable.c
@@ -0,0 +1,413 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+#include <fluent-bit/flb_info.h>
+#include <fluent-bit/flb_macros.h>
+#include <fluent-bit/flb_hash_table.h>
+
+#include "flb_tests_internal.h"
+
+struct map {
+ char *key;
+ char *val;
+};
+
+struct map entries[] = {
+ {"key_0", "val_0"},
+ {"key_1", "val_1"}, {"key_2", "val_2"}, {"key_3", "val_3"},
+ {"key_4", "val_4"}, {"key_5", "val_5"}, {"key_6", "val_6"},
+ {"key_7", "val_7"}, {"key_8", "val_8"}, {"key_9", "val_9"},
+ {"key_10", "val_10"}, {"key_11", "val_11"}, {"key_12", "val_12"},
+ {"key_13", "val_13"}, {"key_14", "val_14"}, {"key_15", "val_15"},
+ {"key_16", "val_16"}, {"key_17", "val_17"}, {"key_18", "val_18"},
+ {"key_19", "val_19"}, {"key_20", "val_20"}, {"key_21", "val_21"},
+ {"key_22", "val_22"}, {"key_23", "val_23"}, {"key_24", "val_24"},
+ {"key_25", "val_25"}, {"key_26", "val_26"}, {"key_27", "val_27"},
+ {"key_28", "val_28"}, {"key_29", "val_29"}, {"key_30", "val_30"},
+ {"key_31", "val_31"}, {"key_32", "val_32"}, {"key_33", "val_33"},
+ {"key_34", "val_34"}, {"key_35", "val_35"}, {"key_36", "val_36"},
+ {"key_37", "val_37"}, {"key_38", "val_38"}, {"key_39", "val_39"},
+ {"key_40", "val_40"}, {"key_41", "val_41"}, {"key_42", "val_42"},
+ {"key_43", "val_43"}, {"key_44", "val_44"}, {"key_45", "val_45"},
+ {"key_46", "val_46"}, {"key_47", "val_47"}, {"key_48", "val_48"},
+ {"key_49", "val_49"}, {"key_50", "val_50"}, {"key_51", "val_51"},
+ {"key_52", "val_52"}, {"key_53", "val_53"}, {"key_54", "val_54"},
+ {"key_55", "val_55"}, {"key_56", "val_56"}, {"key_57", "val_57"},
+ {"key_58", "val_58"}, {"key_59", "val_59"}, {"key_60", "val_60"},
+ {"key_61", "val_61"}, {"key_62", "val_62"}, {"key_63", "val_63"},
+ {"key_64", "val_64"}, {"key_65", "val_65"}, {"key_66", "val_66"},
+ {"key_67", "val_67"}, {"key_68", "val_68"}, {"key_69", "val_69"},
+ {"key_70", "val_70"}, {"key_71", "val_71"}, {"key_72", "val_72"},
+ {"key_73", "val_73"}, {"key_74", "val_74"}, {"key_75", "val_75"},
+ {"key_76", "val_76"}, {"key_77", "val_77"}, {"key_78", "val_78"},
+ {"key_79", "val_79"}, {"key_80", "val_80"}, {"key_81", "val_81"},
+ {"key_82", "val_82"}, {"key_83", "val_83"}, {"key_84", "val_84"},
+ {"key_85", "val_85"}, {"key_86", "val_86"}, {"key_87", "val_87"},
+ {"key_88", "val_88"}, {"key_89", "val_89"}, {"key_90", "val_90"},
+ {"key_91", "val_91"}, {"key_92", "val_92"}, {"key_93", "val_93"},
+ {"key_94", "val_94"}, {"key_95", "val_95"}, {"key_96", "val_96"},
+ {"key_97", "val_97"}, {"key_98", "val_98"}, {"key_99", "val_99"},
+
+ /* override some values */
+ {"key_67", "val_AA"}, {"key_68", "val_BB"}, {"key_69", "val_CC"},
+
+};
+
+static int ht_add(struct flb_hash_table *ht, char *key, char *val)
+{
+ int id;
+ int idn;
+ int klen;
+ int vlen;
+ char *out_buf;
+ size_t out_size;
+
+ klen = strlen(key);
+ vlen = strlen(val);
+
+ /* Insert the key value */
+ id = flb_hash_table_add(ht, key, klen, val, vlen);
+ TEST_CHECK(id >=0);
+
+ /* Retrieve the value of the recently added key */
+ idn = flb_hash_table_get(ht, key, klen, (void *) &out_buf, &out_size);
+ TEST_CHECK(idn == id);
+ TEST_CHECK(strcmp(out_buf, val) == 0);
+
+ return id;
+}
+
+void test_create_zero()
+{
+ struct flb_hash_table *ht;
+
+ ht = flb_hash_table_create(FLB_HASH_TABLE_EVICT_NONE, 0, -1);
+ TEST_CHECK(ht == NULL);
+}
+
+/* bug 355 */
+void test_single()
+{
+ int ret;
+ const char *out_buf;
+ size_t out_size;
+ struct flb_hash_table *ht;
+
+ ht = flb_hash_table_create(FLB_HASH_TABLE_EVICT_NONE, 1, -1);
+ TEST_CHECK(ht != NULL);
+
+ ret = ht_add(ht, "key", "value");
+ TEST_CHECK(ret != -1);
+
+ ret = flb_hash_table_get(ht, "key", 3, (void *) &out_buf, &out_size);
+ TEST_CHECK(ret >= 0);
+
+ ret = flb_hash_table_get(ht, "NOT", 3, (void *) &out_buf, &out_size);
+ TEST_CHECK(ret == -1);
+
+ flb_hash_table_destroy(ht);
+}
+
+void test_small_table()
+{
+ int i;
+ struct map *m;
+ struct flb_hash_table *ht;
+
+ ht = flb_hash_table_create(FLB_HASH_TABLE_EVICT_NONE, 8, -1);
+ TEST_CHECK(ht != NULL);
+
+ for (i = 0; i < sizeof(entries) / sizeof(struct map); i++) {
+ m = &entries[i];
+ ht_add(ht, m->key, m->val);
+ }
+
+ flb_hash_table_destroy(ht);
+}
+
+void test_medium_table()
+{
+ int i;
+ struct map *m;
+ struct flb_hash_table *ht;
+
+ ht = flb_hash_table_create(FLB_HASH_TABLE_EVICT_NONE, 8, -1);
+ TEST_CHECK(ht != NULL);
+
+ for (i = 0; i < sizeof(entries) / sizeof(struct map); i++) {
+ m = &entries[i];
+ ht_add(ht, m->key, m->val);
+ }
+
+ flb_hash_table_destroy(ht);
+}
+
+void test_chaining()
+{
+ int i;
+ int inserts = 0;
+ int count;
+ int chains = 0;
+ struct map *m;
+ struct mk_list *head;
+ struct flb_hash_table_chain *table;
+ struct flb_hash_table *ht;
+
+ ht = flb_hash_table_create(FLB_HASH_TABLE_EVICT_NONE, 8, -1);
+ TEST_CHECK(ht != NULL);
+
+ for (i = 0; i < 8; i++) {
+ m = &entries[i];
+ ht_add(ht, m->key, m->val);
+ inserts++;
+ }
+
+ for (i = 0; i < ht->size; i++) {
+ table = &ht->table[i];
+ count = 0;
+ mk_list_foreach(head, &table->chains) {
+ count++;
+ }
+ TEST_CHECK(count == table->count);
+
+ if (count > 0) {
+ chains++;
+ }
+ }
+
+ /* Tests diff between total, new minus 3 overrides */
+ TEST_CHECK(chains == inserts - 3);
+ flb_hash_table_destroy(ht);
+}
+
+void test_delete_all()
+{
+ int i;
+ int ret;
+ int count;
+ int not_found = 0;
+ int total = 0;
+ struct map *m;
+ struct flb_hash_table_chain *table;
+ struct flb_hash_table *ht;
+
+ ht = flb_hash_table_create(FLB_HASH_TABLE_EVICT_NONE, 8, -1);
+ TEST_CHECK(ht != NULL);
+
+ total = sizeof(entries) / sizeof(struct map);
+ for (i = 0; i < total; i++) {
+ m = &entries[i];
+ ht_add(ht, m->key, m->val);
+ }
+
+ for (i = total - 1; i >= 0; i--) {
+ m = &entries[i];
+ ret = flb_hash_table_del(ht, m->key);
+ if (ret == -1) {
+ not_found++;
+ }
+ }
+
+ count = 0;
+ for (i = 0; i < ht->size; i++) {
+ table = &ht->table[i];
+ count += table->count;
+ }
+
+ TEST_CHECK(count == 0);
+ flb_hash_table_destroy(ht);
+}
+
+void test_random_eviction()
+{
+ int ret;
+ const char *out_buf;
+ size_t out_size;
+ struct flb_hash_table *ht;
+
+ ht = flb_hash_table_create(FLB_HASH_TABLE_EVICT_RANDOM, 8, 1);
+ TEST_CHECK(ht != NULL);
+
+ ret = ht_add(ht, "key1", "value1");
+ TEST_CHECK(ret != -1);
+
+ ret = ht_add(ht, "key2", "value2");
+ TEST_CHECK(ret != -1);
+
+ ret = flb_hash_table_get(ht, "key1", 4, (void *) &out_buf, &out_size);
+ TEST_CHECK(ret == -1);
+
+ ret = flb_hash_table_get(ht, "key2", 4, (void *) &out_buf, &out_size);
+ TEST_CHECK(ret >= 0);
+
+ flb_hash_table_destroy(ht);
+}
+
+void test_less_used_eviction()
+{
+ int ret;
+ const char *out_buf;
+ size_t out_size;
+ struct flb_hash_table *ht;
+
+ ht = flb_hash_table_create(FLB_HASH_TABLE_EVICT_LESS_USED, 8, 2);
+ TEST_CHECK(ht != NULL);
+
+ ret = ht_add(ht, "key1", "value1");
+ TEST_CHECK(ret != -1);
+
+ ret = ht_add(ht, "key2", "value2");
+ TEST_CHECK(ret != -1);
+
+ ret = flb_hash_table_get(ht, "key1", 4, (void *) &out_buf, &out_size);
+ TEST_CHECK(ret >= 0);
+
+ ret = flb_hash_table_get(ht, "key2", 4, (void *) &out_buf, &out_size);
+ TEST_CHECK(ret >= 0);
+
+ ret = flb_hash_table_get(ht, "key2", 4, (void *) &out_buf, &out_size);
+ TEST_CHECK(ret >= 0);
+
+ ret = ht_add(ht, "key3", "value3");
+ TEST_CHECK(ret != -1);
+
+ ret = flb_hash_table_get(ht, "key3", 4, (void *) &out_buf, &out_size);
+ TEST_CHECK(ret >= 0);
+
+ ret = flb_hash_table_get(ht, "key2", 4, (void *) &out_buf, &out_size);
+ TEST_CHECK(ret >= 0);
+
+ ret = flb_hash_table_get(ht, "key1", 4, (void *) &out_buf, &out_size);
+ TEST_CHECK(ret == -1);
+
+ flb_hash_table_destroy(ht);
+}
+
+void test_older_eviction()
+{
+ int ret;
+ const char *out_buf;
+ size_t out_size;
+ struct flb_hash_table *ht;
+
+ ht = flb_hash_table_create(FLB_HASH_TABLE_EVICT_OLDER, 8, 2);
+ TEST_CHECK(ht != NULL);
+
+ ret = ht_add(ht, "key2", "value2");
+ TEST_CHECK(ret != -1);
+
+ ret = ht_add(ht, "key1", "value1");
+ TEST_CHECK(ret != -1);
+
+ ret = flb_hash_table_get(ht, "key1", 4, (void *) &out_buf, &out_size);
+ TEST_CHECK(ret >= 0);
+
+ ret = flb_hash_table_get(ht, "key2", 4, (void *) &out_buf, &out_size);
+ TEST_CHECK(ret >= 0);
+
+ ret = flb_hash_table_get(ht, "key2", 4, (void *) &out_buf, &out_size);
+ TEST_CHECK(ret >= 0);
+
+ ret = ht_add(ht, "key3", "value3");
+ TEST_CHECK(ret != -1);
+
+ ret = flb_hash_table_get(ht, "key3", 4, (void *) &out_buf, &out_size);
+ TEST_CHECK(ret >= 0);
+
+ ret = flb_hash_table_get(ht, "key2", 4, (void *) &out_buf, &out_size);
+ TEST_CHECK(ret == -1);
+
+ ret = flb_hash_table_get(ht, "key1", 4, (void *) &out_buf, &out_size);
+ TEST_CHECK(ret >= 0);
+
+ flb_hash_table_destroy(ht);
+}
+
+void test_pointer()
+{
+ int ret;
+ const char *out_buf;
+ size_t out_size;
+ struct flb_hash_table *ht;
+
+ char *val1 = "val1";
+ char *val2 = "val2";
+
+ ht = flb_hash_table_create(FLB_HASH_TABLE_EVICT_NONE, 512, 0);
+ TEST_CHECK(ht != NULL);
+
+ ret = flb_hash_table_add(ht, "key1", 4, (void *) val1, 0);
+ TEST_CHECK(ret >= 0);
+
+ ret = flb_hash_table_add(ht, "key2", 4, (void *) val2, 0);
+ TEST_CHECK(ret >= 0);
+
+ ret = flb_hash_table_get(ht, "key2", 4, (void *) &out_buf, &out_size);
+ TEST_CHECK(ret >= 0);
+ TEST_CHECK((void *) out_buf == (void *) val2);
+
+ out_buf = flb_hash_table_get_ptr(ht, "key2", 4);
+ TEST_CHECK((void *) out_buf == (void *) val2);
+
+ ret = flb_hash_table_del_ptr(ht, "key2", 4, (void *) out_buf);
+ TEST_CHECK(ret == 0);
+
+ TEST_CHECK(ht->total_count == 1);
+ flb_hash_table_destroy(ht);
+}
+
+void test_hash_exists()
+{
+ int i;
+ int id;
+ int ret;
+ int len;
+ struct map *m;
+ uint64_t hash;
+ struct flb_hash_table *ht;
+
+ ht = flb_hash_table_create(FLB_HASH_TABLE_EVICT_NONE, 1000, 0);
+ TEST_CHECK(ht != NULL);
+
+ for (i = 0; i < sizeof(entries) / sizeof(struct map); i++) {
+ m = &entries[i];
+ id = ht_add(ht, m->key, m->val);
+ TEST_CHECK(id >= 0);
+
+ len = strlen(m->key);
+ hash = cfl_hash_64bits(m->key, len);
+
+ ret = flb_hash_table_exists(ht, hash);
+ TEST_CHECK(ret == FLB_TRUE);
+ }
+
+ for (i = 0; i < sizeof(entries) / sizeof(struct map); i++) {
+ m = &entries[i];
+
+ /* get hash */
+ len = strlen(m->key);
+ hash = cfl_hash_64bits(m->key, len);
+
+ /* delete */
+ ret = flb_hash_table_del(ht, m->key);
+
+ ret = flb_hash_table_exists(ht, hash);
+ TEST_CHECK(ret == FLB_FALSE);;
+ }
+
+ flb_hash_table_destroy(ht);
+}
+
+TEST_LIST = {
+ { "zero_size", test_create_zero },
+ { "single", test_single },
+ { "small_table", test_small_table },
+ { "medium_table", test_medium_table },
+ { "chaining_count", test_chaining },
+ { "delete_all", test_delete_all },
+ { "random_eviction", test_random_eviction },
+ { "less_used_eviction", test_less_used_eviction },
+ { "older_eviction", test_older_eviction },
+ { "pointer", test_pointer },
+ { "hash_exists", test_hash_exists},
+ { 0 }
+};
diff --git a/fluent-bit/tests/internal/hmac.c b/fluent-bit/tests/internal/hmac.c
new file mode 100644
index 00000000..f7de588f
--- /dev/null
+++ b/fluent-bit/tests/internal/hmac.c
@@ -0,0 +1,136 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+#include <fluent-bit/flb_info.h>
+#include <fluent-bit/flb_hmac.h>
+
+#include "flb_tests_internal.h"
+
+static size_t get_sample_data(unsigned char ***buffer_list,
+ size_t **length_list,
+ char **precomputed_key,
+ char **precomputed_mac,
+ int *precomputed_mac_type)
+{
+ static size_t lengths[] = {0, 0, 0, 0};
+ static const char *buffers[] = {
+ "This is one ",
+ "of the four ",
+ "buffers that will be passed ",
+ "to the simple batched digest processor function"
+ };
+ static const char *signature = "a68b8b3e480e00e0e61b4c097a29e64a261dc2548c608677c2827ea1e5b65d77";
+ static const int mac_type = FLB_HASH_SHA256;
+ size_t index;
+ static const char *key = "Long live the bird thing!";
+
+ if (lengths[0] == 0) {
+ for (index = 0 ; index < 4 ; index++) {
+ lengths[index] = strlen(buffers[index]);
+ }
+ }
+
+ *buffer_list = (unsigned char **) buffers;
+ *length_list = lengths;
+ *precomputed_key = (char *) key;
+ *precomputed_mac = (char *) signature;
+ *precomputed_mac_type = mac_type;
+
+ return 4;
+}
+
+static void hexlify(uint8_t *hash, char *out)
+{
+ int i;
+ static const char hex[] = "0123456789abcdef";
+ char *buf = out;
+
+ for (i = 0; i < 32; i++) {
+ *buf++ = hex[hash[i] >> 4];
+ *buf++ = hex[hash[i] & 0xf];
+ }
+}
+
+static void test_hmac_standard()
+{
+ int ref_signature_type;
+ char hex_signature[64];
+ char *ref_hex_signature;
+ uint8_t raw_signature[32];
+ size_t buffer_count;
+ unsigned char **buffer_list;
+ size_t *length_list;
+ char *ref_key;
+ int result;
+ size_t index;
+ struct flb_hmac hmac;
+
+ buffer_count = get_sample_data(&buffer_list, &length_list,
+ &ref_key,
+ &ref_hex_signature,
+ &ref_signature_type);
+
+ TEST_CHECK(buffer_count > 0);
+ TEST_CHECK(ref_signature_type == FLB_HASH_SHA256);
+
+ result = flb_hmac_init(&hmac,
+ ref_signature_type,
+ (unsigned char *) ref_key,
+ strlen(ref_key));
+ TEST_CHECK(result == FLB_CRYPTO_SUCCESS);
+
+ for (index = 0 ; index < buffer_count ; index++) {
+ result = flb_hmac_update(&hmac, buffer_list[index], length_list[index]);
+ TEST_CHECK(result == FLB_CRYPTO_SUCCESS);
+ }
+
+ result = flb_hmac_finalize(&hmac, raw_signature, sizeof(raw_signature));
+ TEST_CHECK(result == FLB_CRYPTO_SUCCESS);
+
+ hexlify(raw_signature, hex_signature);
+
+ TEST_CHECK(memcmp(hex_signature, ref_hex_signature, 64) == 0);
+
+ flb_hmac_cleanup(&hmac);
+}
+
+
+static void test_hmac_simple_batch()
+{
+ int ref_signature_type;
+ char hex_signature[64];
+ char *ref_hex_signature;
+ uint8_t raw_signature[32];
+ size_t buffer_count;
+ unsigned char **buffer_list;
+ size_t *length_list;
+ char *ref_key;
+ int result;
+
+ buffer_count = get_sample_data(&buffer_list, &length_list,
+ &ref_key,
+ &ref_hex_signature,
+ &ref_signature_type);
+
+ TEST_CHECK(buffer_count > 0);
+ TEST_CHECK(ref_signature_type == FLB_HASH_SHA256);
+
+ result = flb_hmac_simple_batch(ref_signature_type,
+ (unsigned char *) ref_key,
+ strlen(ref_key),
+ buffer_count,
+ buffer_list,
+ length_list,
+ raw_signature,
+ sizeof(raw_signature));
+ TEST_CHECK(result == FLB_CRYPTO_SUCCESS);
+
+ hexlify(raw_signature, hex_signature);
+
+ TEST_CHECK(memcmp(hex_signature, ref_hex_signature, 64) == 0);
+}
+
+TEST_LIST = {
+ { "test_hmac_simple_batch", test_hmac_simple_batch },
+ { "test_hmac_standard", test_hmac_standard },
+ { 0 }
+};
diff --git a/fluent-bit/tests/internal/http_client.c b/fluent-bit/tests/internal/http_client.c
new file mode 100644
index 00000000..a37daa35
--- /dev/null
+++ b/fluent-bit/tests/internal/http_client.c
@@ -0,0 +1,478 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+#include <fluent-bit/flb_info.h>
+#include <fluent-bit/flb_mem.h>
+#include <fluent-bit/flb_error.h>
+#include <fluent-bit/flb_socket.h>
+#include <fluent-bit/flb_http_client.h>
+
+#include "flb_tests_internal.h"
+
+struct test_ctx {
+ struct flb_upstream *u;
+ struct flb_connection *u_conn;
+ struct flb_config *config;
+};
+
+struct test_ctx* test_ctx_create()
+{
+ struct test_ctx *ret_ctx = NULL;
+
+ ret_ctx = flb_malloc(sizeof(struct test_ctx));
+ if (!TEST_CHECK(ret_ctx != NULL)) {
+ flb_errno();
+ TEST_MSG("flb_malloc(test_ctx) failed");
+ return NULL;
+ }
+ ret_ctx->u = NULL;
+ ret_ctx->u_conn = NULL;
+
+ ret_ctx->config = flb_config_init();
+ if(!TEST_CHECK(ret_ctx->config != NULL)) {
+ TEST_MSG("flb_config_init failed");
+ flb_free(ret_ctx);
+ return NULL;
+ }
+
+ ret_ctx->u = flb_upstream_create(ret_ctx->config, "127.0.0.1", 80, 0, NULL);
+ if (!TEST_CHECK(ret_ctx->u != NULL)) {
+ TEST_MSG("flb_upstream_create failed");
+ flb_config_exit(ret_ctx->config);
+ flb_free(ret_ctx);
+ return NULL;
+ }
+
+ ret_ctx->u_conn = flb_calloc(1, sizeof(struct flb_connection));
+ if(!TEST_CHECK(ret_ctx->u_conn != NULL)) {
+ flb_errno();
+ TEST_MSG("flb_malloc(flb_connection) failed");
+ flb_upstream_destroy(ret_ctx->u);
+ flb_config_exit(ret_ctx->config);
+ flb_free(ret_ctx);
+ return NULL;
+ }
+
+ ret_ctx->u_conn->upstream = ret_ctx->u;
+
+ return ret_ctx;
+}
+
+int test_ctx_destroy(struct test_ctx* ctx)
+{
+ if (!TEST_CHECK(ctx != NULL)) {
+ return -1;
+ }
+ if (ctx->u_conn) {
+ flb_free(ctx->u_conn);
+ }
+ if (ctx->u) {
+ flb_upstream_destroy(ctx->u);
+ }
+ if (ctx->config) {
+ flb_config_exit(ctx->config);
+ }
+
+ flb_free(ctx);
+ return 0;
+}
+
+void test_http_buffer_increase()
+{
+ int ret;
+ size_t s;
+ struct test_ctx *ctx;
+ struct flb_http_client *c;
+ struct flb_http_response *resp;
+
+ ctx = test_ctx_create();
+ if (!TEST_CHECK(ctx != NULL)) {
+ exit(EXIT_FAILURE);
+ }
+
+ /* Create HTTP client instance */
+ c = flb_http_client(ctx->u_conn, FLB_HTTP_GET, "/", NULL, 0,
+ "127.0.0.1", 80, NULL, 0);
+ TEST_CHECK(c != NULL);
+
+ /* Do buffer tests */
+ resp = &c->resp;
+ TEST_CHECK(resp->data_len == 0);
+ TEST_CHECK(resp->data_size == FLB_HTTP_DATA_SIZE_MAX);
+ TEST_CHECK(resp->data_size_max == FLB_HTTP_DATA_SIZE_MAX);
+
+ /* Invalid size */
+ ret = flb_http_buffer_size(c, 1);
+ TEST_CHECK(ret == -1);
+
+ /* Increase max size to 8KB */
+ flb_http_buffer_size(c, 8192);
+
+ /* Request to allocate +4KB */
+ ret = flb_http_buffer_increase(c, 4096, &s);
+ TEST_CHECK(ret == 0);
+ TEST_CHECK(s == 4096);
+
+ /* Request to allocate 1 byte, it should fail */
+ ret = flb_http_buffer_increase(c, 1, &s);
+ TEST_CHECK(ret == -1);
+
+ /* Test unlimited */
+ flb_http_buffer_size(c, 0);
+ ret = flb_http_buffer_increase(c, 1, &s);
+ TEST_CHECK(ret == 0 && s == 1);
+
+ /* Payload test */
+ memcpy(c->resp.data, "00abc11def22ghi__PAYLOAD__8yz9900", 33);
+ c->resp.data[33] = '\0';
+ c->resp.data_len = 33;
+ c->resp.payload = c->resp.data + 15;
+ c->resp.payload_size = 11;
+
+ ret = flb_http_buffer_increase(c, 819200, &s);
+ TEST_CHECK(ret == 0);
+
+ ret = strncmp(c->resp.payload, "__PAYLOAD__", 11);
+ TEST_CHECK(ret == 0);
+
+ flb_http_client_destroy(c);
+ test_ctx_destroy(ctx);
+}
+
+void test_http_add_get_header()
+{
+ struct test_ctx *ctx;
+ struct flb_http_client *c;
+ flb_sds_t ret_str;
+ char *ua = "Fluent-Bit";
+ char *host = "127.0.0.1:80";
+ int ret;
+
+ ctx = test_ctx_create();
+ if (!TEST_CHECK(ctx != NULL)) {
+ exit(EXIT_FAILURE);
+ }
+
+ /* Create HTTP client instance */
+ c = flb_http_client(ctx->u_conn, FLB_HTTP_GET, "/", NULL, 0,
+ "127.0.0.1", 80, NULL, 0);
+ if(!TEST_CHECK(c != NULL)) {
+ TEST_MSG("flb_http_client failed");
+ test_ctx_destroy(ctx);
+ exit(EXIT_FAILURE);
+ }
+
+ /* Check User-Agent */
+ ret = flb_http_add_header(c, "User-Agent", 10, ua, strlen(ua));
+ TEST_CHECK(ret == 0);
+
+ ret_str = flb_http_get_header(c, "User-Agent", 10);
+ if (!TEST_CHECK(ret_str != NULL)) {
+ TEST_MSG("flb_http_get_header failed");
+ flb_http_client_destroy(c);
+ test_ctx_destroy(ctx);
+ exit(EXIT_FAILURE);
+ }
+
+ if (!TEST_CHECK(flb_sds_cmp(ret_str, ua, strlen(ua)) == 0)) {
+ TEST_MSG("strcmp failed. got=%s expect=%s", ret_str, ua);
+ }
+ flb_sds_destroy(ret_str);
+
+
+ /* Check Host */
+ ret_str = flb_http_get_header(c, "Host", 4);
+ if (!TEST_CHECK(ret_str != NULL)) {
+ TEST_MSG("flb_http_get_header failed");
+ flb_http_client_destroy(c);
+ test_ctx_destroy(ctx);
+ exit(EXIT_FAILURE);
+ }
+
+ if (!TEST_CHECK(flb_sds_cmp(ret_str, host, strlen(host)) == 0)) {
+ TEST_MSG("strcmp failed. got=%s expect=%s", ret_str, host);
+ }
+
+ flb_sds_destroy(ret_str);
+ flb_http_client_destroy(c);
+ test_ctx_destroy(ctx);
+}
+
+void test_http_set_keepalive()
+{
+ struct test_ctx *ctx;
+ struct flb_http_client *c;
+ flb_sds_t ret_str;
+ int ret;
+
+ ctx = test_ctx_create();
+ if (!TEST_CHECK(ctx != NULL)) {
+ exit(EXIT_FAILURE);
+ }
+
+ /* Create HTTP client instance */
+ c = flb_http_client(ctx->u_conn, FLB_HTTP_GET, "/", NULL, 0,
+ "127.0.0.1", 80, NULL, 0);
+ if(!TEST_CHECK(c != NULL)) {
+ TEST_MSG("flb_http_client failed");
+ test_ctx_destroy(ctx);
+ exit(EXIT_FAILURE);
+ }
+
+ /* Set keepalive header */
+ ret = flb_http_set_keepalive(c);
+ TEST_CHECK(ret == 0);
+
+ ret_str = flb_http_get_header(c, FLB_HTTP_HEADER_CONNECTION,
+ strlen(FLB_HTTP_HEADER_CONNECTION));
+ if (!TEST_CHECK(ret_str != NULL)) {
+ TEST_MSG("flb_get_header failed");
+ flb_http_client_destroy(c);
+ test_ctx_destroy(ctx);
+ exit(EXIT_FAILURE);
+ }
+
+ /* Compare */
+ if (!TEST_CHECK(flb_sds_cmp(ret_str, FLB_HTTP_HEADER_KA, strlen(FLB_HTTP_HEADER_KA)) == 0)) {
+ TEST_MSG("strcmp failed. got=%s expect=%s", ret_str, FLB_HTTP_HEADER_KA);
+ }
+ flb_sds_destroy(ret_str);
+ flb_http_client_destroy(c);
+ test_ctx_destroy(ctx);
+}
+
+void test_http_strip_port_from_host()
+{
+ struct test_ctx *ctx;
+ struct flb_http_client *c;
+ flb_sds_t ret_str;
+ char *host_port = "127.0.0.1:80";
+ char *host = "127.0.0.1";
+ int ret;
+
+ ctx = test_ctx_create();
+ if (!TEST_CHECK(ctx != NULL)) {
+ exit(EXIT_FAILURE);
+ }
+
+ /* Create HTTP client instance */
+ c = flb_http_client(ctx->u_conn, FLB_HTTP_GET, "/", NULL, 0,
+ "127.0.0.1", 80, NULL, 0);
+ if(!TEST_CHECK(c != NULL)) {
+ TEST_MSG("flb_http_client failed");
+ test_ctx_destroy(ctx);
+ exit(EXIT_FAILURE);
+ }
+
+ /* Check Host. It should contain port number */
+ ret_str = flb_http_get_header(c, "Host", 4);
+ if (!TEST_CHECK(ret_str != NULL)) {
+ TEST_MSG("flb_http_get_header failed");
+ flb_http_client_destroy(c);
+ test_ctx_destroy(ctx);
+ exit(EXIT_FAILURE);
+ }
+
+ if (!TEST_CHECK(flb_sds_cmp(ret_str, host_port, strlen(host_port)) == 0)) {
+ TEST_MSG("strcmp failed. got=%s expect=%s", ret_str, host_port);
+ }
+ flb_sds_destroy(ret_str);
+
+
+ ret = flb_http_strip_port_from_host(c);
+ TEST_CHECK(ret == 0);
+
+ /* Check Host. Port number should be removed. */
+ ret_str = flb_http_get_header(c, "Host", 4);
+ if (!TEST_CHECK(ret_str != NULL)) {
+ TEST_MSG("flb_http_get_header failed");
+ flb_http_client_destroy(c);
+ test_ctx_destroy(ctx);
+ exit(EXIT_FAILURE);
+ }
+
+ if (!TEST_CHECK(flb_sds_cmp(ret_str, host, strlen(host)) == 0)) {
+ TEST_MSG("strcmp failed. got=%s expect=%s", ret_str, host);
+ }
+
+ flb_sds_destroy(ret_str);
+ flb_http_client_destroy(c);
+ test_ctx_destroy(ctx);
+}
+
+void test_http_encoding_gzip()
+{
+ struct test_ctx *ctx;
+ struct flb_http_client *c;
+ flb_sds_t ret_str;
+ char *gzip = "gzip";
+ int ret;
+
+ ctx = test_ctx_create();
+ if (!TEST_CHECK(ctx != NULL)) {
+ exit(EXIT_FAILURE);
+ }
+
+ /* Create HTTP client instance */
+ c = flb_http_client(ctx->u_conn, FLB_HTTP_GET, "/", NULL, 0,
+ "127.0.0.1", 80, NULL, 0);
+ if(!TEST_CHECK(c != NULL)) {
+ TEST_MSG("flb_http_client failed");
+ test_ctx_destroy(ctx);
+ exit(EXIT_FAILURE);
+ }
+
+ /* Check encoding. It should be error */
+ ret_str = flb_http_get_header(c, FLB_HTTP_HEADER_CONTENT_ENCODING,
+ strlen(FLB_HTTP_HEADER_CONTENT_ENCODING));
+ if (!TEST_CHECK(ret_str == NULL)) {
+ TEST_MSG("Got encoding? Header:%s", ret_str);
+ flb_sds_destroy(ret_str);
+ flb_http_client_destroy(c);
+ test_ctx_destroy(ctx);
+ exit(EXIT_FAILURE);
+ }
+
+ ret = flb_http_set_content_encoding_gzip(c);
+ TEST_CHECK(ret == 0);
+
+ /* Check Encoding */
+ ret_str = flb_http_get_header(c, FLB_HTTP_HEADER_CONTENT_ENCODING,
+ strlen(FLB_HTTP_HEADER_CONTENT_ENCODING));
+ if (!TEST_CHECK(ret_str != NULL)) {
+ TEST_MSG("flb_http_get_header failed");
+ flb_http_client_destroy(c);
+ test_ctx_destroy(ctx);
+ exit(EXIT_FAILURE);
+ }
+
+ if (!TEST_CHECK(flb_sds_cmp(ret_str, gzip, strlen(gzip)) == 0)) {
+ TEST_MSG("strcmp failed. got=%s expect=%s", ret_str, gzip);
+ }
+
+ flb_sds_destroy(ret_str);
+ flb_http_client_destroy(c);
+ test_ctx_destroy(ctx);
+}
+
+void test_http_add_basic_auth_header()
+{
+ struct test_ctx *ctx;
+ struct flb_http_client *c;
+ flb_sds_t ret_str;
+ char *expect = "Basic dXNlcjpwYXNzd29yZA=="; /* user:password in base64 */
+ char *auth = FLB_HTTP_HEADER_AUTH;
+ const char *user = "user";
+ char *passwd = "password";
+ int ret;
+
+ ctx = test_ctx_create();
+ if (!TEST_CHECK(ctx != NULL)) {
+ exit(EXIT_FAILURE);
+ }
+
+ /* Create HTTP client instance */
+ c = flb_http_client(ctx->u_conn, FLB_HTTP_GET, "/", NULL, 0,
+ "127.0.0.1", 80, NULL, 0);
+ if(!TEST_CHECK(c != NULL)) {
+ TEST_MSG("flb_http_client failed");
+ test_ctx_destroy(ctx);
+ exit(EXIT_FAILURE);
+ }
+
+ /* Check Autholization. It should be error. */
+ ret_str = flb_http_get_header(c, auth, strlen(auth));
+ if (!TEST_CHECK(ret_str == NULL)) {
+ TEST_MSG("Got auth? Header:%s", ret_str);
+ flb_sds_destroy(ret_str);
+ flb_http_client_destroy(c);
+ test_ctx_destroy(ctx);
+ exit(EXIT_FAILURE);
+ }
+
+ ret = flb_http_basic_auth(c, user, passwd);
+ TEST_CHECK(ret == 0);
+
+ /* Check Autholization. */
+ ret_str = flb_http_get_header(c, auth, strlen(auth));
+ if (!TEST_CHECK(ret_str != NULL)) {
+ TEST_MSG("flb_http_get_header failed");
+ flb_http_client_destroy(c);
+ test_ctx_destroy(ctx);
+ exit(EXIT_FAILURE);
+ }
+
+ if (!TEST_CHECK(flb_sds_cmp(ret_str, expect, strlen(expect)) == 0)) {
+ TEST_MSG("strcmp failed. got=%s expect=%s", ret_str, expect);
+ }
+
+ flb_sds_destroy(ret_str);
+ flb_http_client_destroy(c);
+ test_ctx_destroy(ctx);
+}
+
+void test_http_add_proxy_auth_header()
+{
+ struct test_ctx *ctx;
+ struct flb_http_client *c;
+ flb_sds_t ret_str;
+ char *expect = "Basic dXNlcjpwYXNzd29yZA=="; /* user:password in base64 */
+ char *auth = FLB_HTTP_HEADER_PROXY_AUTH;
+ const char *user = "user";
+ char *passwd = "password";
+ int ret;
+
+ ctx = test_ctx_create();
+ if (!TEST_CHECK(ctx != NULL)) {
+ exit(EXIT_FAILURE);
+ }
+
+ /* Create HTTP client instance */
+ c = flb_http_client(ctx->u_conn, FLB_HTTP_GET, "/", NULL, 0,
+ "127.0.0.1", 80, NULL, 0);
+ if(!TEST_CHECK(c != NULL)) {
+ TEST_MSG("flb_http_client failed");
+ test_ctx_destroy(ctx);
+ exit(EXIT_FAILURE);
+ }
+
+ /* Check autholization header. It should be error. */
+ ret_str = flb_http_get_header(c, auth, strlen(auth));
+ if (!TEST_CHECK(ret_str == NULL)) {
+ TEST_MSG("Got auth? Header:%s", ret_str);
+ flb_sds_destroy(ret_str);
+ flb_http_client_destroy(c);
+ test_ctx_destroy(ctx);
+ exit(EXIT_FAILURE);
+ }
+
+ ret = flb_http_proxy_auth(c, user, passwd);
+ TEST_CHECK(ret == 0);
+
+ /* Check autholization header. */
+ ret_str = flb_http_get_header(c, auth, strlen(auth));
+ if (!TEST_CHECK(ret_str != NULL)) {
+ TEST_MSG("flb_http_get_header failed");
+ flb_http_client_destroy(c);
+ test_ctx_destroy(ctx);
+ exit(EXIT_FAILURE);
+ }
+
+ if (!TEST_CHECK(flb_sds_cmp(ret_str, expect, strlen(expect)) == 0)) {
+ TEST_MSG("strcmp failed. got=%s expect=%s", ret_str, expect);
+ }
+
+ flb_sds_destroy(ret_str);
+ flb_http_client_destroy(c);
+ test_ctx_destroy(ctx);
+}
+
+TEST_LIST = {
+ { "http_buffer_increase" , test_http_buffer_increase},
+ { "add_get_header" , test_http_add_get_header},
+ { "set_keepalive" , test_http_set_keepalive},
+ { "strip_port_from_host" , test_http_strip_port_from_host},
+ { "encoding_gzip" , test_http_encoding_gzip},
+ { "add_basic_auth_header" , test_http_add_basic_auth_header},
+ { "add_proxy_auth_header" , test_http_add_proxy_auth_header},
+ { 0 }
+};
diff --git a/fluent-bit/tests/internal/include/sp_cb_functions.h b/fluent-bit/tests/internal/include/sp_cb_functions.h
new file mode 100644
index 00000000..aee3fb39
--- /dev/null
+++ b/fluent-bit/tests/internal/include/sp_cb_functions.h
@@ -0,0 +1,655 @@
+#include "sp_helpers.h"
+
+#ifndef FLB_TEST_CP_FUNCTIONS
+#define FLB_TEST_CP_FUNCTIONS
+
+/* Callback functions to perform checks over results */
+static void cb_select_all(int id, struct task_check *check,
+ char *buf, size_t size)
+{
+ int ret;
+
+ /* Expect all 11 rows */
+ ret = mp_count_rows(buf, size);
+ TEST_CHECK(ret == 11);
+}
+
+/* Callback test: expect one key per record */
+static void cb_select_id(int id, struct task_check *check,
+ char *buf, size_t size)
+{
+ int ret;
+
+ /* Expect all 11 rows */
+ ret = mp_count_rows(buf, size);
+ TEST_CHECK(ret == 11);
+
+ ret = mp_count_keys(buf, size);
+ TEST_CHECK(ret == 13);
+}
+
+static void cb_select_cond_1(int id, struct task_check *check,
+ char *buf, size_t size)
+{
+ int ret;
+
+ /* Expect 1 row */
+ ret = mp_count_rows(buf, size);
+ TEST_CHECK(ret == 1);
+}
+
+static void cb_select_cond_2(int id, struct task_check *check,
+ char *buf, size_t size)
+{
+ int ret;
+
+ /* Expect 2 rows */
+ ret = mp_count_rows(buf, size);
+ TEST_CHECK(ret == 2);
+}
+
+static void cb_select_cond_not_null(int id, struct task_check *check,
+ char *buf, size_t size)
+{
+ int ret;
+
+ /* Expect 1 row */
+ ret = mp_count_rows(buf, size);
+ TEST_CHECK(ret == 1);
+}
+
+static void cb_select_cond_null(int id, struct task_check *check,
+ char *buf, size_t size)
+{
+ int ret;
+
+ /* Expect 1 row */
+ ret = mp_count_rows(buf, size);
+ TEST_CHECK(ret == 1);
+}
+
+static void cb_select_not_equal_1(int id, struct task_check *check,
+ char *buf, size_t size)
+{
+ int ret;
+
+ /* Expect 1 row */
+ ret = mp_count_rows(buf, size);
+ TEST_CHECK(ret == 2);
+}
+
+static void cb_select_not_equal_2(int id, struct task_check *check,
+ char *buf, size_t size)
+{
+ int ret;
+
+ /* Expect 2 row */
+ ret = mp_count_rows(buf, size);
+ TEST_CHECK(ret == 2);
+}
+
+static void cb_select_aggr_count(int id, struct task_check *check,
+ char *buf, size_t size)
+{
+ int ret;
+
+ /* Expect 1 row */
+ ret = mp_count_rows(buf, size);
+ TEST_CHECK(ret == 1);
+
+ /* COUNT(*) is 11 */
+ ret = mp_record_key_cmp(buf, size,
+ 0, "COUNT(*)",
+ MSGPACK_OBJECT_POSITIVE_INTEGER,
+ NULL, 11, 0);
+ TEST_CHECK(ret == FLB_TRUE);
+}
+
+static void cb_func_time_now(int id, struct task_check *check,
+ char *buf, size_t size)
+{
+ int ret;
+ char tmp[32];
+ struct tm *local;
+ time_t now = time(NULL);
+
+ local = localtime(&now);
+ strftime(tmp, sizeof(tmp) - 1, "%Y-%m-%d %H:%M:%S", local);
+
+ /* Expect 2 rows */
+ ret = mp_count_rows(buf, size);
+ TEST_CHECK(ret == 2);
+
+ /* NOW() */
+ ret = mp_record_key_cmp(buf, size,
+ 0, "NOW()",
+ MSGPACK_OBJECT_STR,
+ tmp, 0, 0);
+ TEST_CHECK(ret == FLB_TRUE);
+
+ /* tnow */
+ ret = mp_record_key_cmp(buf, size,
+ 1, "tnow",
+ MSGPACK_OBJECT_STR,
+ tmp, 0, 0);
+ TEST_CHECK(ret == FLB_TRUE);
+}
+
+static void cb_func_time_unix_timestamp(int id, struct task_check *check,
+ char *buf, size_t size)
+{
+ int ret;
+ time_t now = time(NULL);
+
+ /* Expect 2 rows */
+ ret = mp_count_rows(buf, size);
+ TEST_CHECK(ret == 2);
+
+ /* UNIX_TIMESTAMP() */
+ ret = mp_record_key_cmp(buf, size,
+ 0, "UNIX_TIMESTAMP()",
+ MSGPACK_OBJECT_POSITIVE_INTEGER,
+ NULL, now, 0);
+ TEST_CHECK(ret == FLB_TRUE);
+
+ /* tnow */
+ ret = mp_record_key_cmp(buf, size,
+ 1, "ts",
+ MSGPACK_OBJECT_POSITIVE_INTEGER,
+ NULL, now, 0);
+ TEST_CHECK(ret == FLB_TRUE);
+}
+
+/* No records must be selected */
+static void cb_select_tag_error(int id, struct task_check *check,
+ char *buf, size_t size)
+{
+ int ret;
+
+ TEST_CHECK(buf == NULL && size == 0);
+
+ /* no records expected */
+ ret = mp_count_rows(buf, size);
+ TEST_CHECK(ret == 0);
+}
+
+/* No records must be selected */
+static void cb_select_tag_ok(int id, struct task_check *check,
+ char *buf, size_t size)
+{
+ int ret;
+
+ TEST_CHECK(buf != NULL && size > 0);
+
+ /* 2 records expected */
+ ret = mp_count_rows(buf, size);
+ TEST_CHECK(ret == 2);
+}
+
+static void cb_record_contains(int id, struct task_check *check,
+ char *buf, size_t size)
+{
+ int ret;
+
+ /* Expect 2 rows */
+ ret = mp_count_rows(buf, size);
+ TEST_CHECK(ret == 2);
+}
+
+static void cb_record_not_contains(int id, struct task_check *check,
+ char *buf, size_t size)
+{
+ int ret;
+
+ /* Expect 0 rows */
+ ret = mp_count_rows(buf, size);
+ TEST_CHECK(ret == 0);
+}
+
+/* Callback functions to perform checks over results */
+static void cb_select_sub_blue(int id, struct task_check *check,
+ char *buf, size_t size)
+{
+ int ret;
+
+ /* Expect 1 row */
+ ret = mp_count_rows(buf, size);
+ TEST_CHECK(ret == 1);
+}
+
+static void cb_select_sub_num(int id, struct task_check *check,
+ char *buf, size_t size)
+{
+ int ret;
+
+ /* Expect 2 rows */
+ ret = mp_count_rows(buf, size);
+ TEST_CHECK(ret == 2);
+}
+
+static void cb_select_sub_colors(int id, struct task_check *check,
+ char *buf, size_t size)
+{
+ int ret;
+
+ /* Expect 3 rows */
+ ret = mp_count_rows(buf, size);
+ TEST_CHECK(ret == 3);
+}
+
+static void cb_select_sub_record_contains(int id, struct task_check *check,
+ char *buf, size_t size)
+{
+ int ret;
+
+ /* Expect 5 rows */
+ ret = mp_count_rows(buf, size);
+ TEST_CHECK(ret == 5);
+}
+
+static void cb_select_sub_keys(int id, struct task_check *check,
+ char *buf, size_t size)
+{
+ int ret;
+
+ /* Expect 1 row */
+ ret = mp_count_rows(buf, size);
+ TEST_CHECK(ret == 1);
+
+ ret = mp_record_key_cmp(buf, size,
+ 0, "map['sub1']['sub2']['color']",
+ MSGPACK_OBJECT_STR,
+ "blue", 0, 0);
+ TEST_CHECK(ret == FLB_TRUE);
+}
+
+static void cb_select_sum_sub_keys(int id, struct task_check *check,
+ char *buf, size_t size)
+{
+ int ret;
+
+ /* Expect 1 row */
+ ret = mp_count_rows(buf, size);
+ TEST_CHECK(ret == 1);
+
+ ret = mp_record_key_cmp(buf, size,
+ 0, "SUM(map['sub1']['sub2'])",
+ MSGPACK_OBJECT_POSITIVE_INTEGER,
+ NULL, 246, 0);
+ TEST_CHECK(ret == FLB_TRUE);
+}
+
+static void cb_select_avg_sub_keys(int id, struct task_check *check,
+ char *buf, size_t size)
+{
+ int ret;
+
+ /* Expect 1 row */
+ ret = mp_count_rows(buf, size);
+ TEST_CHECK(ret == 1);
+
+ ret = mp_record_key_cmp(buf, size,
+ 0, "AVG(map['sub1']['sub2'])",
+ MSGPACK_OBJECT_FLOAT,
+ NULL, 0, 123.0);
+ TEST_CHECK(ret == FLB_TRUE);
+}
+
+static void cb_select_count_sub_keys(int id, struct task_check *check,
+ char *buf, size_t size)
+{
+ int ret;
+
+ /* Expect 1 row */
+ ret = mp_count_rows(buf, size);
+ TEST_CHECK(ret == 1);
+
+ ret = mp_record_key_cmp(buf, size,
+ 0, "COUNT(map['sub1']['sub2'])",
+ MSGPACK_OBJECT_POSITIVE_INTEGER,
+ NULL, 2, 0);
+ TEST_CHECK(ret == FLB_TRUE);
+}
+
+static void cb_select_min_sub_keys(int id, struct task_check *check,
+ char *buf, size_t size)
+{
+ int ret;
+
+ /* Expect 1 row */
+ ret = mp_count_rows(buf, size);
+ TEST_CHECK(ret == 1);
+
+ ret = mp_record_key_cmp(buf, size,
+ 0, "MIN(map['sub1']['sub2'])",
+ MSGPACK_OBJECT_POSITIVE_INTEGER,
+ NULL, 123, 0);
+ TEST_CHECK(ret == FLB_TRUE);
+}
+
+static void cb_select_max_sub_keys(int id, struct task_check *check,
+ char *buf, size_t size)
+{
+ int ret;
+
+ /* Expect 1 row */
+ ret = mp_count_rows(buf, size);
+ TEST_CHECK(ret == 1);
+
+ ret = mp_record_key_cmp(buf, size,
+ 0, "MAX(map['sub1']['sub3'])",
+ MSGPACK_OBJECT_FLOAT,
+ NULL, 0, 100);
+ TEST_CHECK(ret == FLB_TRUE);
+}
+
+static void cb_select_sum_sub_keys_group_by(int id, struct task_check *check,
+ char *buf, size_t size)
+{
+ int ret;
+
+ /* Expect 3 rows */
+ ret = mp_count_rows(buf, size);
+ TEST_CHECK(ret == 3);
+
+ ret = mp_record_key_cmp(buf, size,
+ 0, "SUM(map['sub1']['sub3'])",
+ MSGPACK_OBJECT_FLOAT,
+ NULL, 0, 105.5);
+ TEST_CHECK(ret == FLB_TRUE);
+}
+
+static void cb_select_sum_sub_keys_group_by_2(int id, struct task_check *check,
+ char *buf, size_t size)
+{
+ int ret;
+
+ /* Expect 3 rows */
+ ret = mp_count_rows(buf, size);
+ TEST_CHECK(ret == 3);
+
+ ret = mp_record_key_cmp(buf, size,
+ 0, "SUM(map['sub1']['sub3'])",
+ MSGPACK_OBJECT_FLOAT,
+ NULL, 0, 105.5);
+ TEST_CHECK(ret == FLB_TRUE);
+}
+
+static void cb_select_sum_sub_keys_group_by_3(int id, struct task_check *check,
+ char *buf, size_t size)
+{
+ int ret;
+
+ /* Expect 3 rows */
+ ret = mp_count_rows(buf, size);
+ TEST_CHECK(ret == 3);
+
+ ret = mp_record_key_cmp(buf, size,
+ 0, "SUM(map['sub1']['sub3'])",
+ MSGPACK_OBJECT_POSITIVE_INTEGER,
+ NULL, 100, 0);
+ TEST_CHECK(ret == FLB_TRUE);
+
+ ret = mp_record_key_cmp(buf, size,
+ 1, "SUM(map['sub1']['sub3'])",
+ MSGPACK_OBJECT_FLOAT,
+ NULL, 0, 11);
+ TEST_CHECK(ret == FLB_TRUE);
+
+ ret = mp_record_key_cmp(buf, size,
+ 2, "SUM(map['sub1']['sub3'])",
+ MSGPACK_OBJECT_FLOAT,
+ NULL, 0, 5.5);
+ TEST_CHECK(ret == FLB_TRUE);
+}
+
+
+static void cb_forecast_tumbling_window(int id, struct task_check *check,
+ char *buf, size_t size)
+{
+ int ret;
+
+ /* Expect one record only */
+ ret = mp_count_rows(buf, size);
+ TEST_CHECK(ret == 1);
+
+ /* Check SUM value result */
+ ret = mp_record_key_cmp(buf, size, 0, "TIMESERIES_FORECAST(usage)",
+ MSGPACK_OBJECT_FLOAT,
+ NULL, 0, 310.0);
+ TEST_CHECK(ret == FLB_TRUE);
+
+ /* Check AVG value result */
+ ret = mp_record_key_cmp(buf, size, 0, "AVG(usage)",
+ MSGPACK_OBJECT_FLOAT,
+ NULL, 0, 60.0);
+
+ TEST_CHECK(ret == FLB_TRUE);
+}
+
+/* Callback functions to perform checks over results */
+static void cb_snapshot_create(int id, struct task_check *check,
+ char *buf, size_t size)
+{
+ int ret;
+
+ ret = mp_count_rows(buf, size);
+ /* Snapshot doesn't return anything */
+ TEST_CHECK(ret == 0);
+};
+
+static void cb_snapshot_purge(int id, struct task_check *check,
+ char *buf, size_t size)
+{
+ int ret;
+
+ /* Expect 5 rows, as set in snapshot query */
+ ret = mp_count_rows(buf, size);
+ TEST_CHECK(ret == 5);
+};
+
+static void cb_snapshot_purge_time(int id, struct task_check *check,
+ char *buf, size_t size)
+{
+ int ret;
+
+ /* Expect 11 rows, as set in snapshot query */
+ ret = mp_count_rows(buf, size);
+ TEST_CHECK(ret == 11);
+};
+
+static void cb_window_5_second(int id, struct task_check *check,
+ char *buf, size_t size)
+{
+ int ret;
+
+ /* Expect one record only */
+ ret = mp_count_rows(buf, size);
+ TEST_CHECK(ret == 1);
+
+ /* Check SUM value result */
+ ret = mp_record_key_cmp(buf, size, 0, "SUM(id)",
+ MSGPACK_OBJECT_POSITIVE_INTEGER,
+ NULL, 225, 0);
+ TEST_CHECK(ret == FLB_TRUE);
+
+ /* Check AVG value result */
+ ret = mp_record_key_cmp(buf, size, 0, "AVG(id)",
+ MSGPACK_OBJECT_FLOAT,
+ NULL, 0, 4.5);
+
+ TEST_CHECK(ret == FLB_TRUE);
+}
+
+static void cb_select_aggr(int id, struct task_check *check,
+ char *buf, size_t size)
+{
+ int ret;
+
+ /* Expect 1 row */
+ ret = mp_count_rows(buf, size);
+ TEST_CHECK(ret == 1);
+
+ /* MIN(id) is 0 */
+ ret = mp_record_key_cmp(buf, size,
+ 0, "MIN(id)",
+ MSGPACK_OBJECT_POSITIVE_INTEGER,
+ NULL, 0, 0);
+ TEST_CHECK(ret == FLB_TRUE);
+
+ /* MAX(id) is 10 */
+ ret = mp_record_key_cmp(buf, size,
+ 0, "MAX(id)",
+ MSGPACK_OBJECT_POSITIVE_INTEGER,
+ NULL, 10, 0);
+ TEST_CHECK(ret == FLB_TRUE);
+
+ /* COUNT(*) is 11 */
+ ret = mp_record_key_cmp(buf, size,
+ 0, "COUNT(*)",
+ MSGPACK_OBJECT_POSITIVE_INTEGER,
+ NULL, 11, 0);
+ TEST_CHECK(ret == FLB_TRUE);
+
+ /* SUM(bytes) is 110.50 */
+ ret = mp_record_key_cmp(buf, size,
+ 0, "SUM(bytes)",
+ MSGPACK_OBJECT_FLOAT,
+ NULL, 0, 110.50);
+ TEST_CHECK(ret == FLB_TRUE);
+
+ /* AVG(bytes) is 10.04545 */
+ ret = mp_record_key_cmp(buf, size,
+ 0, "AVG(bytes)",
+ MSGPACK_OBJECT_FLOAT,
+ NULL, 0, 10.045455);
+ TEST_CHECK(ret == FLB_TRUE);
+}
+
+static void cb_select_groupby(int id, struct task_check *check,
+ char *buf, size_t size)
+{
+ int ret;
+
+ /* Expect 1 row */
+ ret = mp_count_rows(buf, size);
+ TEST_CHECK(ret == 2);
+
+ /* MIN(id) is 0 for record 0 (bool=true) */
+ ret = mp_record_key_cmp(buf, size,
+ 0, "MIN(id)",
+ MSGPACK_OBJECT_POSITIVE_INTEGER,
+ NULL, 0, 0);
+ TEST_CHECK(ret == FLB_TRUE);
+
+ /* MIN(id) is 6 for record 1 (bool=false) */
+ ret = mp_record_key_cmp(buf, size,
+ 1, "MIN(id)",
+ MSGPACK_OBJECT_POSITIVE_INTEGER,
+ NULL, 6, 0);
+ TEST_CHECK(ret == FLB_TRUE);
+
+ /* MAX(id) is 8 for record 0 (bool=true) */
+ ret = mp_record_key_cmp(buf, size,
+ 0, "MAX(id)",
+ MSGPACK_OBJECT_POSITIVE_INTEGER,
+ NULL, 8, 0);
+ TEST_CHECK(ret == FLB_TRUE);
+
+ /* MAX(id) is i9 for record 1 (bool=false) */
+ ret = mp_record_key_cmp(buf, size,
+ 1, "MAX(id)",
+ MSGPACK_OBJECT_POSITIVE_INTEGER,
+ NULL, 9, 0);
+ TEST_CHECK(ret == FLB_TRUE);
+
+ /* COUNT(*) is 8 for record 0 (bool=true) */
+ ret = mp_record_key_cmp(buf, size,
+ 0, "COUNT(*)",
+ MSGPACK_OBJECT_POSITIVE_INTEGER,
+ NULL, 8, 0);
+ TEST_CHECK(ret == FLB_TRUE);
+
+ /* COUNT(*) is 2 for record 1 (bool=false) */
+ ret = mp_record_key_cmp(buf, size,
+ 1, "COUNT(*)",
+ MSGPACK_OBJECT_POSITIVE_INTEGER,
+ NULL, 2, 0);
+ TEST_CHECK(ret == FLB_TRUE);
+
+ /* SUM(bytes) is 80.0 for record 0 (bool=true) */
+ ret = mp_record_key_cmp(buf, size,
+ 0, "SUM(bytes)",
+ MSGPACK_OBJECT_FLOAT,
+ NULL, 0, 80.0);
+ TEST_CHECK(ret == FLB_TRUE);
+
+ /* SUM(bytes) is 20.50 for record 1 (bool=false) */
+ ret = mp_record_key_cmp(buf, size,
+ 1, "SUM(bytes)",
+ MSGPACK_OBJECT_FLOAT,
+ NULL, 0, 20.50);
+ TEST_CHECK(ret == FLB_TRUE);
+
+ /* AVG(bytes) is 10.0 for record 0 (bool=true) */
+ ret = mp_record_key_cmp(buf, size,
+ 0, "AVG(bytes)",
+ MSGPACK_OBJECT_FLOAT,
+ NULL, 0, 10.0);
+ TEST_CHECK(ret == FLB_TRUE);
+
+ /* AVG(bytes) is 10.25 for record 1 (bool=false) */
+ ret = mp_record_key_cmp(buf, size,
+ 1, "AVG(bytes)",
+ MSGPACK_OBJECT_FLOAT,
+ NULL, 0, 10.25);
+ TEST_CHECK(ret == FLB_TRUE);
+}
+
+static void cb_hopping_window_5_second(int id, struct task_check *check,
+ char *buf, size_t size)
+{
+ int ret;
+
+ /* Expect one record only */
+ ret = mp_count_rows(buf, size);
+ TEST_CHECK(ret == 1);
+
+ /* Check SUM value result */
+ ret = mp_record_key_cmp(buf, size, 0, "SUM(id)",
+ MSGPACK_OBJECT_POSITIVE_INTEGER,
+ NULL, 266, 0);
+ TEST_CHECK(ret == FLB_TRUE);
+
+ /* Check AVG value result */
+ ret = mp_record_key_cmp(buf, size, 0, "AVG(id)",
+ MSGPACK_OBJECT_FLOAT,
+ NULL, 0, 16.625);
+
+ TEST_CHECK(ret == FLB_TRUE);
+}
+
+static void cb_forecast_hopping_window(int id, struct task_check *check,
+ char *buf, size_t size)
+{
+ int ret;
+
+ /* Expect one record only */
+ ret = mp_count_rows(buf, size);
+ TEST_CHECK(ret == 1);
+
+ /* Check SUM value result */
+ ret = mp_record_key_cmp(buf, size, 0, "TIMESERIES_FORECAST(usage)",
+ MSGPACK_OBJECT_FLOAT,
+ NULL, 0, 460.0);
+ TEST_CHECK(ret == FLB_TRUE);
+
+ /* Check AVG value result */
+ ret = mp_record_key_cmp(buf, size, 0, "AVG(usage)",
+ MSGPACK_OBJECT_FLOAT,
+ NULL, 0, 175.0);
+
+ TEST_CHECK(ret == FLB_TRUE);
+}
+
+#endif
diff --git a/fluent-bit/tests/internal/include/sp_helpers.h b/fluent-bit/tests/internal/include/sp_helpers.h
new file mode 100644
index 00000000..2efaaeff
--- /dev/null
+++ b/fluent-bit/tests/internal/include/sp_helpers.h
@@ -0,0 +1,158 @@
+#ifndef FLB_TEST_SP_HELPERS
+#define FLB_TEST_SP_HELPERS
+
+#define MP_UOK MSGPACK_UNPACK_SUCCESS
+
+struct task_check {
+ int id; /* Test identifier */
+ int window_type; /* Type of window in window-based queries */
+ int window_size_sec; /* Size of the window in window-based queries */
+ int window_hop_sec; /* Hopping size in hopping window */
+ char *name; /* Name of the test */
+ char *exec; /* Query */
+ void (*cb_check)(int, struct task_check *, char *, size_t);
+ /* Callback function:
+ id, test_check, output buffer, buffer_size */
+};
+
+/* Helper functions */
+static int mp_count_rows(char *buf, size_t size)
+{
+ int total = 0;
+ size_t off = 0;
+ msgpack_unpacked result;
+
+ msgpack_unpacked_init(&result);
+ while (msgpack_unpack_next(&result, buf, size, &off) == MP_UOK) {
+ total++;
+ }
+
+ msgpack_unpacked_destroy(&result);
+ return total;
+}
+
+/* Count total number of keys considering all rows */
+static int mp_count_keys(char *buf, size_t size)
+{
+ int keys = 0;
+ size_t off = 0;
+ msgpack_unpacked result;
+ msgpack_object root;
+ msgpack_object map;
+
+ msgpack_unpacked_init(&result);
+ while (msgpack_unpack_next(&result, buf, size, &off) == MP_UOK) {
+ root = result.data;
+ map = root.via.array.ptr[1];
+ keys += map.via.map.size;
+ }
+ msgpack_unpacked_destroy(&result);
+
+ return keys;
+}
+
+static inline int float_cmp(double f1, double f2)
+{
+ double precision = 0.00001;
+
+ if (((f1 - precision) < f2) &&
+ ((f1 + precision) > f2)) {
+ return 1;
+ }
+ else {
+ return 0;
+ }
+}
+
+/* Lookup record/row number 'id' and check that 'key' matches 'val' */
+static int mp_record_key_cmp(char *buf, size_t size,
+ int record_id, char *key,
+ int val_type, char *val_str, int64_t val_int64,
+ double val_f64)
+{
+ int i;
+ int ret = FLB_FALSE;
+ int id = 0;
+ int k_len;
+ int v_len;
+ int keys = 0;
+ size_t off = 0;
+ msgpack_unpacked result;
+ msgpack_object root;
+ msgpack_object map;
+ msgpack_object k;
+ msgpack_object v;
+
+ k_len = strlen(key);
+
+ msgpack_unpacked_init(&result);
+ while (msgpack_unpack_next(&result, buf, size, &off) == MP_UOK) {
+ if (id != record_id) {
+ id++;
+ continue;
+ }
+
+ root = result.data;
+ map = root.via.array.ptr[1];
+ keys += map.via.map.size;
+
+ for (i = 0; i < keys; i++) {
+ k = map.via.map.ptr[i].key;
+ v = map.via.map.ptr[i].val;
+
+ if (k.type != MSGPACK_OBJECT_STR) {
+ continue;
+ }
+
+ if (k.via.str.size != k_len) {
+ continue;
+ }
+
+ if (strncmp(k.via.str.ptr, key, k_len) != 0) {
+ continue;
+ }
+
+ /* at this point the key matched, now validate the expected value */
+ if (val_type == MSGPACK_OBJECT_FLOAT) {
+ if (v.type != MSGPACK_OBJECT_FLOAT32 &&
+ v.type != MSGPACK_OBJECT_FLOAT) {
+ msgpack_unpacked_destroy(&result);
+ return FLB_FALSE;
+ }
+ }
+ else if (v.type != val_type) {
+ msgpack_unpacked_destroy(&result);
+ return FLB_FALSE;
+ }
+
+ switch (val_type) {
+ case MSGPACK_OBJECT_STR:
+ v_len = strlen(val_str);
+ if (strncmp(v.via.str.ptr, val_str, v_len) == 0) {
+ ret = FLB_TRUE;
+ }
+ goto exit;
+ case MSGPACK_OBJECT_POSITIVE_INTEGER:
+ if (v.via.i64 == val_int64) {
+ ret = FLB_TRUE;
+ }
+ goto exit;
+ case MSGPACK_OBJECT_FLOAT:
+ if (float_cmp(v.via.f64, val_f64)) {
+ ret = FLB_TRUE;
+ }
+ else {
+ printf("double mismatch: %f exp %f\n",
+ v.via.f64, val_f64);
+ }
+ goto exit;
+ };
+ }
+ }
+
+exit:
+ msgpack_unpacked_destroy(&result);
+ return ret;
+}
+
+#endif
diff --git a/fluent-bit/tests/internal/include/sp_invalid_queries.h b/fluent-bit/tests/internal/include/sp_invalid_queries.h
new file mode 100644
index 00000000..e299e0fd
--- /dev/null
+++ b/fluent-bit/tests/internal/include/sp_invalid_queries.h
@@ -0,0 +1,20 @@
+#ifndef FLB_TEST_INVALID_QUERIES
+#define FLB_TEST_INVALID_QUERIES
+
+/* Tests to check if syntactically invalid queries return error */
+char *invalid_query_checks[] = {
+ "SELECT id, MIN(id) FROM STREAM:FLB;",
+ "SELECT *, COUNT(id) FROM STREAM:FLB;",
+ "SELECT * FROM TAG:FLB WHERE bool = NULL ;",
+ "SELECT * FROM TAG:FLB WHERE @record.some_random_func() ;",
+ "SELECT id, MIN(id) FROM STREAM:FLB WINDOW TUMBLING (1 SECOND)" \
+ " GROUP BY bool;",
+ "SELECT *, COUNT(id) FROM STREAM:FLB WINDOW TUMBLING (1 SECOND)" \
+ " GROUP BY bool;",
+ "SELECT *, COUNT(bool) FROM STREAM:FLB WINDOW TUMBLING (1 SECOND)" \
+ " GROUP BY bool;",
+ "SELECT *, bool, COUNT(bool) FROM STREAM:FLB WINDOW TUMBLING (1 SECOND)" \
+ " GROUP BY bool;"
+};
+
+#endif
diff --git a/fluent-bit/tests/internal/include/sp_select_keys.h b/fluent-bit/tests/internal/include/sp_select_keys.h
new file mode 100644
index 00000000..ba3c809f
--- /dev/null
+++ b/fluent-bit/tests/internal/include/sp_select_keys.h
@@ -0,0 +1,131 @@
+#include "sp_cb_functions.h"
+
+#ifndef FLB_TEST_SP_SELECT_KEYS
+#define FLB_TEST_SP_SELECT_KEYS
+
+/* Tests for 'test_select_keys' */
+struct task_check select_keys_checks[] = {
+ {
+ 0, 0, 0, 0,
+ "select_all",
+ "SELECT * FROM STREAM:FLB;",
+ cb_select_all
+ },
+ {
+ 1, 0, 0, 0,
+ "select_id",
+ "SELECT id, word2 FROM STREAM:FLB;",
+ cb_select_id
+ },
+
+ /* Conditionals */
+ {
+ 2, 0, 0, 0,
+ "select_cond_1",
+ "SELECT * FROM STREAM:FLB WHERE bytes > 10.290;",
+ cb_select_cond_1
+ },
+ {
+ 3, 0, 0, 0,
+ "select_cond_2",
+ "SELECT * FROM STREAM:FLB WHERE word2 = 'rlz' or word3 = 'rlz';",
+ cb_select_cond_2
+ },
+ {
+ 4, 0, 0, 0,
+ "select_cond_not_null",
+ "SELECT * FROM STREAM:FLB WHERE word2 = 'rlz' and word3 IS NOT NULL;",
+ cb_select_cond_not_null
+ },
+ {
+ 5, 0, 0, 0,
+ "select_cond_null",
+ "SELECT * FROM STREAM:FLB WHERE word3 IS NULL;",
+ cb_select_cond_null
+ },
+ {
+ 6, 0, 0, 0,
+ "select_not_equal_1",
+ "SELECT * FROM STREAM:FLB WHERE bool != true;",
+ cb_select_not_equal_1
+ },
+ {
+ 7, 0, 0, 0,
+ "select_not_equal_2",
+ "SELECT * FROM STREAM:FLB WHERE bytes <> 10;",
+ cb_select_not_equal_2
+ },
+
+
+ /* Aggregation functions */
+ {
+ 8, 0, 0, 0,
+ "select_aggr",
+ "SELECT MIN(id), MAX(id), COUNT(*), SUM(bytes), AVG(bytes) " \
+ "FROM STREAM:FLB;",
+ cb_select_aggr,
+ },
+ {
+ 9, 0, 0, 0,
+ "select_aggr_coount",
+ "SELECT COUNT(*) " \
+ "FROM STREAM:FLB;",
+ cb_select_aggr_count,
+ },
+ {
+ 10, 0, 0, 0,
+ "select_aggr_window_tumbling",
+ "SELECT MIN(id), MAX(id), COUNT(*), SUM(bytes), AVG(bytes) FROM STREAM:FLB;",
+ cb_select_aggr,
+ },
+ {
+ 11, 0, 0, 0,
+ "select_aggr_window_tumbling_groupby",
+ "SELECT bool, MIN(id), MAX(id), COUNT(*), SUM(bytes), AVG(bytes) " \
+ "FROM STREAM:FLB WHERE word3 IS NOT NULL GROUP BY bool;",
+ cb_select_groupby,
+ },
+
+ /* Time functions */
+ {
+ 12, 0, 0, 0,
+ "func_time_now",
+ "SELECT NOW(), NOW() as tnow FROM STREAM:FLB WHERE bytes > 10;",
+ cb_func_time_now,
+ },
+ {
+ 13, 0, 0, 0,
+ "func_time_unix_timestamp",
+ "SELECT UNIX_TIMESTAMP(), UNIX_TIMESTAMP() as ts " \
+ "FROM STREAM:FLB WHERE bytes > 10;",
+ cb_func_time_unix_timestamp,
+ },
+
+ /* Stream selection using Tag rules */
+ {
+ 14, 0, 0, 0,
+ "select_from_tag_error",
+ "SELECT id FROM TAG:'no-matches' WHERE bytes > 10;",
+ cb_select_tag_error,
+ },
+ {
+ 15, 0, 0, 0,
+ "select_from_tag",
+ "SELECT id FROM TAG:'samples' WHERE bytes > 10;",
+ cb_select_tag_ok,
+ },
+ {
+ 16, 0, 0, 0,
+ "@recond.contains",
+ "SELECT id FROM TAG:'samples' WHERE bytes = 10 AND @record.contains(word2);",
+ cb_record_contains,
+ },
+ {
+ 17, 0, 0, 0,
+ "@recond.contains",
+ "SELECT id FROM TAG:'samples' WHERE @record.contains(x);",
+ cb_record_not_contains,
+ },
+};
+
+#endif
diff --git a/fluent-bit/tests/internal/include/sp_select_subkeys.h b/fluent-bit/tests/internal/include/sp_select_subkeys.h
new file mode 100644
index 00000000..a5feebf0
--- /dev/null
+++ b/fluent-bit/tests/internal/include/sp_select_subkeys.h
@@ -0,0 +1,84 @@
+#include "sp_cb_functions.h"
+
+#ifndef FLB_TEST_SP_SELECT_SUBKEYS
+#define FLB_TEST_SP_SELECT_SUBKEYS
+
+/* Tests for 'test_select_subkeys' */
+struct task_check select_subkeys_checks[] = {
+ {
+ 0, 0, 0, 0,
+ "select_sub_blue",
+ "SELECT * FROM STREAM:FLB WHERE map['sub1']['sub2']['color'] = 'blue';",
+ cb_select_sub_blue
+ },
+ {
+ 1, 0, 0, 0,
+ "select_sub_num",
+ "SELECT * FROM STREAM:FLB WHERE map['sub1']['sub2'] = 123;",
+ cb_select_sub_num
+ },
+ {
+ 2, 0, 0, 0,
+ "select_sub_colors",
+ "SELECT * FROM STREAM:FLB WHERE " \
+ "map['sub1']['sub2']['color'] = 'blue' OR " \
+ "map['sub1']['sub2']['color'] = 'red' OR " \
+ "map['color'] = 'blue'; ",
+ cb_select_sub_colors
+ },
+ {
+ 3, 0, 0, 0,
+ "cb_select_sub_record_contains",
+ "SELECT * FROM STREAM:FLB WHERE " \
+ "@record.contains(map['sub1']['sub3']) OR " \
+ "@record.contains(map['color']); ",
+ cb_select_sub_record_contains
+ },
+ { 4, 0, 0, 0,
+ "cb_select_sub_keys",
+ "SELECT map['sub1']['sub2']['color'] FROM STREAM:FLB WHERE " \
+ "map['sub1']['sub2']['color'] = 'blue';",
+ cb_select_sub_keys},
+ { 5, 0, 0, 0,
+ "cb_select_sum_sub_keys",
+ "SELECT SUM(map['sub1']['sub2']) FROM STREAM:FLB WHERE " \
+ "map['sub1']['sub2'] = 123;",
+ cb_select_sum_sub_keys},
+ { 6, 0, 0, 0,
+ "cb_select_avg_sub_keys",
+ "SELECT AVG(map['sub1']['sub2']) FROM STREAM:FLB WHERE " \
+ "map['sub1']['sub2'] = 123;",
+ cb_select_avg_sub_keys},
+ { 7, 0, 0, 0,
+ "cb_select_count_sub_keys",
+ "SELECT COUNT(map['sub1']['sub2']) FROM STREAM:FLB WHERE " \
+ "map['sub1']['sub2'] = 123;",
+ cb_select_count_sub_keys},
+ { 8, 0, 0, 0,
+ "cb_select_min_sub_keys",
+ "SELECT MIN(map['sub1']['sub2']) FROM STREAM:FLB WHERE " \
+ "map['sub1']['sub2'] > 0;",
+ cb_select_min_sub_keys},
+ { 9, 0, 0, 0,
+ "cb_select_max_sub_keys",
+ "SELECT MAX(map['sub1']['sub3']) FROM STREAM:FLB WHERE " \
+ "map['sub1']['sub3'] > 0;",
+ cb_select_max_sub_keys},
+ { 10, 0, 0, 0,
+ "cb_select_sum_sub_keys_group_by",
+ "SELECT SUM(map['sub1']['sub3']) FROM STREAM:FLB " \
+ "GROUP BY map['mtype'];",
+ cb_select_sum_sub_keys_group_by},
+ { 11, 0, 0, 0,
+ "cb_select_sum_sub_keys_group_by_2",
+ "SELECT map['sub1']['stype'], map['mtype'], SUM(map['sub1']['sub3']) " \
+ "FROM STREAM:FLB GROUP BY map['mtype'], map['sub1']['stype'];",
+ cb_select_sum_sub_keys_group_by_2},
+ { 12, 0, 0, 0,
+ "cb_select_sum_sub_keys_group_by_3",
+ "SELECT map['sub1']['stype'], map['sub1']['sub4'], SUM(map['sub1']['sub3']) " \
+ "FROM STREAM:FLB GROUP BY map['sub1']['stype'], map['sub1']['sub4'];",
+ cb_select_sum_sub_keys_group_by_3}
+};
+
+#endif
diff --git a/fluent-bit/tests/internal/include/sp_snapshot.h b/fluent-bit/tests/internal/include/sp_snapshot.h
new file mode 100644
index 00000000..e8ca6fad
--- /dev/null
+++ b/fluent-bit/tests/internal/include/sp_snapshot.h
@@ -0,0 +1,38 @@
+#include "sp_cb_functions.h"
+
+#ifndef FLB_TEST_SP_SNAPSHOT
+#define FLB_TEST_SP_SNAPSHOT
+
+/* Tests for 'test_snapshot' */
+struct task_check snapshot_checks[][2] = {
+ {
+ { // Snapshot
+ 0, 0, 0, 0,
+ "snapshot_create",
+ "SELECT * FROM STREAM:FLB LIMIT 5;",
+ cb_snapshot_create
+ },
+ { // Flush
+ 1, 0, 0, 0,
+ "snapshot_purge",
+ "SELECT * FROM STREAM:FLB;",
+ cb_snapshot_purge
+ },
+ },
+ {
+ { // Snapshot
+ 2, 0, 5, 0,
+ "snapshot_create",
+ "SELECT * FROM STREAM:FLB;",
+ cb_snapshot_create
+ },
+ { // Flush
+ 3, 0, 0, 0,
+ "snapshot_purge",
+ "SELECT * FROM STREAM:FLB;",
+ cb_snapshot_purge_time
+ },
+ },
+};
+
+#endif
diff --git a/fluent-bit/tests/internal/include/sp_window.h b/fluent-bit/tests/internal/include/sp_window.h
new file mode 100644
index 00000000..117b1014
--- /dev/null
+++ b/fluent-bit/tests/internal/include/sp_window.h
@@ -0,0 +1,53 @@
+#include "sp_cb_functions.h"
+
+#ifndef FLB_TEST_SP_WINDOW
+#define FLB_TEST_SP_WINDOW
+
+/* Tests for test_window */
+struct task_check window_checks[] = {
+ {
+ 0, FLB_SP_WINDOW_TUMBLING, 5, 0,
+ "window_5_seconds",
+ "SELECT SUM(id), AVG(id) FROM STREAM:FLB WINDOW TUMBLING (5 SECOND) " \
+ "WHERE word3 IS NOT NULL;",
+ cb_window_5_second
+ },
+ {
+ 1, FLB_SP_WINDOW_TUMBLING, 1, 0,
+ "select_aggr_window_tumbling",
+ "SELECT MIN(id), MAX(id), COUNT(*), SUM(bytes), AVG(bytes) " \
+ "FROM STREAM:FLB WINDOW TUMBLING (1 SECOND);",
+ cb_select_aggr,
+ },
+ {
+ 2, FLB_SP_WINDOW_TUMBLING, 1, 0,
+ "select_aggr_window_tumbling_groupby",
+ "SELECT bool, MIN(id), MAX(id), COUNT(*), SUM(bytes), AVG(bytes) " \
+ "FROM STREAM:FLB WINDOW TUMBLING (1 SECOND) WHERE word3 IS NOT NULL " \
+ "GROUP BY bool;",
+ cb_select_groupby,
+ },
+ {
+ 3, FLB_SP_WINDOW_HOPPING, 5, 2,
+ "hopping_window_5_seconds",
+ "SELECT SUM(id), AVG(id) FROM STREAM:FLB WINDOW HOPPING (5 SECOND, " \
+ "ADVANCE BY 2 SECOND) WHERE word3 IS NOT NULL;",
+ cb_hopping_window_5_second
+ },
+ { /* FORECAST */
+ 4, FLB_SP_WINDOW_TUMBLING, 1, 0,
+ "timeseries_forecast_window_tumbling",
+ "SELECT AVG(usage), TIMESERIES_FORECAST(usage, 20) FROM " \
+ "STREAM:FLB WINDOW TUMBLING (5 SECOND);",
+ cb_forecast_tumbling_window
+ },
+ {
+ 5, FLB_SP_WINDOW_HOPPING, 5, 2,
+ "timeseries_forecast_window_hopping",
+ "SELECT AVG(usage), TIMESERIES_FORECAST(usage, 20) FROM " \
+ "STREAM:FLB WINDOW HOPPING (5 SECOND, ADVANCE BY 2 SECOND);",
+ cb_forecast_hopping_window
+ },
+};
+
+#endif
diff --git a/fluent-bit/tests/internal/input_chunk.c b/fluent-bit/tests/internal/input_chunk.c
new file mode 100644
index 00000000..7154b324
--- /dev/null
+++ b/fluent-bit/tests/internal/input_chunk.c
@@ -0,0 +1,516 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+#include <fluent-bit.h>
+#include <pthread.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <fluent-bit/flb_input_chunk.h>
+#include <fluent-bit/flb_storage.h>
+#include <fluent-bit/flb_router.h>
+#include <fluent-bit/flb_time.h>
+#include "flb_tests_internal.h"
+#include "chunkio/chunkio.h"
+#include "data/input_chunk/log/test_buffer_drop_chunks.h"
+
+#define DPATH FLB_TESTS_DATA_PATH "data/input_chunk/"
+#define MAX_LINES 32
+
+int64_t result_time;
+struct tail_test_result {
+ const char *target;
+ int nMatched;
+};
+
+struct tail_file_lines {
+ char *lines[MAX_LINES];
+ int lines_c;
+};
+
+static inline int64_t set_result(int64_t v)
+{
+ int64_t old = __sync_lock_test_and_set(&result_time, v);
+ return old;
+}
+
+static int file_to_buf(const char *path, char **out_buf, size_t *out_size)
+{
+ int ret;
+ long bytes;
+ char *buf;
+ FILE *fp;
+ struct stat st;
+
+ ret = stat(path, &st);
+ if (ret == -1) {
+ return -1;
+ }
+
+ fp = fopen(path, "r");
+ if (!fp) {
+ return -1;
+ }
+
+ buf = flb_malloc(st.st_size);
+ if (!buf) {
+ flb_errno();
+ fclose(fp);
+ return -1;
+ }
+
+ bytes = fread(buf, st.st_size, 1, fp);
+ if (bytes != 1) {
+ flb_errno();
+ flb_free(buf);
+ fclose(fp);
+ return -1;
+ }
+
+ fclose(fp);
+ *out_buf = buf;
+ *out_size = st.st_size;
+
+ return 0;
+}
+
+/* Given a target, lookup the .out file and return it content in a tail_file_lines structure */
+static struct tail_file_lines *get_out_file_content(const char *target)
+{
+ int i;
+ int ret;
+ char file[PATH_MAX];
+ char *p;
+ char *out_buf;
+ size_t out_size;
+ struct tail_file_lines *file_lines = flb_malloc(sizeof (struct tail_file_lines));
+ file_lines->lines_c = 0;
+
+ snprintf(file, sizeof(file) - 1, DPATH "out/%s.out", target);
+
+ ret = file_to_buf(file, &out_buf, &out_size);
+ TEST_CHECK_(ret == 0, "getting output file content: %s", file);
+ if (ret != 0) {
+ file_lines->lines_c = 0;
+ return file_lines;
+ }
+
+ file_lines->lines[file_lines->lines_c++] = out_buf;
+
+ for (i = 0; i < out_size; i++) {
+ // Nullify \n and \r characters
+ p = (char *)(out_buf + i);
+ if (*p == '\n' || *p == '\r') {
+ *p = '\0';
+
+ if (i == out_size - 1) {
+ break;
+ }
+
+ if (*++p != '\0' && *p != '\n' && *p != '\r' && file_lines->lines_c < MAX_LINES) {
+ file_lines->lines[file_lines->lines_c++] = p;
+ }
+ }
+ }
+
+ // printf("Just before return: %s\n", file_lines.lines[0]);
+ return file_lines;
+}
+
+static int cb_check_result(void *record, size_t size, void *data)
+{
+ int i;
+ struct tail_test_result *result;
+ struct tail_file_lines *out;
+
+ result = (struct tail_test_result *) data;
+
+ char *check;
+
+ out = get_out_file_content(result->target);
+ // printf("What we got from function: %s\n", out.lines[0]);
+ if (!out->lines_c) {
+ goto exit;
+ }
+ /*
+ * Our validation is: check that the one of the output lines
+ * in the output record.
+ */
+ for (i = 0; i<out->lines_c; i++) {
+ check = strstr(record, out->lines[i]);
+ if (check != NULL) {
+ result->nMatched++;
+ goto exit;
+ }
+ }
+
+exit:
+ if (size > 0) {
+ flb_free(record);
+ }
+ if (out->lines_c) {
+ flb_free(out->lines[0]);
+ flb_free(out);
+ }
+ return 0;
+}
+
+void do_test(char *system, const char *target, ...)
+{
+ int64_t ret;
+ flb_ctx_t *ctx = NULL;
+ int in_ffd;
+ int out_ffd;
+ char path[PATH_MAX];
+ struct tail_test_result result = {0};
+ char storage_path[PATH_MAX];
+
+ result.nMatched = 0;
+ result.target = target;
+
+ struct flb_lib_out_cb cb;
+ cb.cb = cb_check_result;
+ cb.data = &result;
+
+ /* initialize */
+ set_result(0);
+
+ ctx = flb_create();
+
+ snprintf(storage_path, sizeof(storage_path) - 1, "/tmp/input-chunk-test-%s", target);
+
+ /* create chunks in /tmp folder */
+ ret = flb_service_set(ctx,
+ "Parsers_File", DPATH "parser.conf",
+ "storage.path", storage_path,
+ "Log_Level", "error",
+ NULL);
+ TEST_CHECK_(ret == 0, "setting service options");
+
+ in_ffd = flb_input(ctx, (char *) system, NULL);
+ TEST_CHECK(in_ffd >= 0);
+ TEST_CHECK(flb_input_set(ctx, in_ffd, "tag", "test", NULL) == 0);
+
+ /* Compose path based on target */
+ snprintf(path, sizeof(path) - 1, DPATH "log/%s.log", target);
+ TEST_CHECK_(access(path, R_OK) == 0, "accessing log file: %s", path);
+
+ TEST_CHECK(flb_input_set(ctx, in_ffd,
+ "Path", path,
+ "storage.type", "filesystem",
+ "Parser", "docker",
+ NULL) == 0);
+
+ out_ffd = flb_output(ctx, (char *) "lib", &cb);
+ TEST_CHECK(out_ffd >= 0);
+ TEST_CHECK(flb_output_set(ctx, out_ffd,
+ "match", "test",
+ "format", "json",
+ "storage.total_limit_size", "1K",
+ NULL) == 0);
+
+ TEST_CHECK(flb_service_set(ctx, "Flush", "0.5",
+ "Grace", "1",
+ NULL) == 0);
+
+ /* Start the engine */
+ ret = flb_start(ctx);
+ TEST_CHECK_(ret == 0, "starting engine");
+
+ sleep(1);
+
+ ret = flb_stop(ctx);
+ TEST_CHECK_(ret == 0, "stopping engine");
+
+ if (ctx) {
+ flb_destroy(ctx);
+ }
+}
+
+void flb_test_input_chunk_exceed_limit()
+{
+ /*
+ * For this test, the input is a log file with more than 1000 bytes.
+ * However we set the limit of storage.total_limit_size to be 1K, no
+ * data should be flushed to the destination as we don't have enough
+ * space to buffer the data.
+ */
+ do_test("tail", "a_thousand_plus_one_bytes",
+ NULL);
+}
+
+void flb_test_input_chunk_buffer_valid()
+{
+ do_test("tail", "test_buffer_valid",
+ NULL);
+}
+
+void flb_test_input_chunk_dropping_chunks()
+{
+ int i;
+ int ret;
+ int in_ffd;
+ int out_ffd;
+ int size = sizeof(TEST_BUFFER_DROP_CHUNKS) - 1;
+ flb_ctx_t *ctx;
+ size_t total_bytes;
+ struct flb_input_instance *i_ins;
+ struct mk_list *tmp;
+ struct mk_list *head;
+ struct flb_input_chunk *ic;
+ struct flb_task *task;
+
+ /* Create context, flush every second (some checks omitted here) */
+ ctx = flb_create();
+
+ /* create chunks in /tmp folder */
+ ret = flb_service_set(ctx,
+ "flush", "2", "grace", "1",
+ "storage.path", "/tmp/input-chunk-test/",
+ "Log_Level", "error",
+ NULL);
+
+ TEST_CHECK_(ret == 0, "setting service options");
+
+ /* Lib input mode */
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ TEST_CHECK(flb_input_set(ctx, in_ffd,
+ "tag", "test",
+ "storage.type", "filesystem",
+ NULL) == 0);
+
+ /* an invalid output destination */
+ out_ffd = flb_output(ctx, (char *) "http", NULL);
+ flb_output_set(ctx, out_ffd,
+ "match", "test",
+ "Host", "127.0.0.1",
+ "Port", "1",
+ "storage.total_limit_size", "1K",
+ NULL);
+
+ /* Start */
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ i_ins = mk_list_entry_first(&ctx->config->inputs,
+ struct flb_input_instance,
+ _head);
+
+ /* Ingest data sample */
+ for (i = 0; i < 10; ++i) {
+ flb_lib_push(ctx, in_ffd, (char *) TEST_BUFFER_DROP_CHUNKS, size);
+ sleep(1);
+ total_bytes = flb_input_chunk_total_size(i_ins);
+ ret = total_bytes > 1000 ? -1 : 0;
+ TEST_CHECK(ret == 0);
+ }
+
+ /* FORCE clean up test tasks*/
+ mk_list_foreach_safe(head, tmp, &i_ins->tasks) {
+ task = mk_list_entry(head, struct flb_task, _head);
+ flb_info("[task] cleanup test task");
+ flb_task_destroy(task, FLB_TRUE);
+ }
+
+ /* clean up test chunks */
+ mk_list_foreach_safe(head, tmp, &i_ins->chunks) {
+ ic = mk_list_entry(head, struct flb_input_chunk, _head);
+ flb_input_chunk_destroy(ic, FLB_TRUE);
+ }
+
+ flb_time_msleep(2100);
+ flb_stop(ctx);
+ flb_destroy(ctx);
+}
+
+/*
+ * When chunk is set to DOWN from memory, data_size is set to 0 and
+ * cio_chunk_get_content_size(1) returns the data_size. fs_chunks_size
+ * is used to track the size of chunks in filesystem so we need to call
+ * cio_chunk_get_real_size to return the original size in the file system
+ */
+static ssize_t flb_input_chunk_get_real_size(struct flb_input_chunk *ic)
+{
+ ssize_t meta_size;
+ ssize_t size;
+
+ size = cio_chunk_get_real_size(ic->chunk);
+
+ if (size != 0) {
+ return size;
+ }
+
+ // Real size is not synced to chunk yet
+ size = flb_input_chunk_get_size(ic);
+ if (size == 0) {
+ flb_debug("[input chunk] no data in the chunk %s",
+ flb_input_chunk_get_name(ic));
+ return -1;
+ }
+
+ meta_size = cio_meta_size(ic->chunk);
+ size += meta_size
+ /* See https://github.com/edsiper/chunkio#file-layout for more details */
+ + 2 /* HEADER BYTES */
+ + 4 /* CRC32 */
+ + 16 /* PADDING */
+ + 2; /* METADATA LENGTH BYTES */
+
+ return size;
+}
+
+static int gen_buf(msgpack_sbuffer *mp_sbuf, char *buf, size_t buf_size)
+{
+ msgpack_unpacked result;
+ msgpack_packer mp_pck;
+
+ msgpack_unpacked_init(&result);
+
+ /* Initialize local msgpack buffer */
+ msgpack_packer_init(&mp_pck, mp_sbuf, msgpack_sbuffer_write);
+
+ msgpack_pack_str_body(&mp_pck, buf, buf_size);
+ msgpack_unpacked_destroy(&result);
+
+ return 0;
+}
+
+static int log_cb(struct cio_ctx *data, int level, const char *file, int line,
+ char *str)
+{
+ if (level == CIO_LOG_ERROR) {
+ flb_error("[fstore] %s", str);
+ }
+ else if (level == CIO_LOG_WARN) {
+ flb_warn("[fstore] %s", str);
+ }
+ else if (level == CIO_LOG_INFO) {
+ flb_info("[fstore] %s", str);
+ }
+ else if (level == CIO_LOG_DEBUG) {
+ flb_debug("[fstore] %s", str);
+ }
+
+ return 0;
+}
+
+/* This tests uses the subsystems of the engine directly
+ * to avoid threading issues when submitting chunks.
+ */
+void flb_test_input_chunk_fs_chunks_size_real()
+{
+ int records;
+ bool have_size_discrepancy = FLB_FALSE;
+ bool has_checked_size = FLB_FALSE;
+ struct flb_input_instance *i_ins;
+ struct flb_output_instance *o_ins;
+ struct mk_list *tmp;
+ struct mk_list *head;
+ struct flb_input_chunk *ic;
+ struct flb_task *task;
+ size_t chunk_size = 0;
+ struct flb_config *cfg;
+ struct cio_ctx *cio;
+ msgpack_sbuffer mp_sbuf;
+ char buf[262144];
+ struct mk_event_loop *evl;
+ struct cio_options opts = {0};
+
+ flb_init_env();
+ cfg = flb_config_init();
+ evl = mk_event_loop_create(256);
+
+ TEST_CHECK(evl != NULL);
+ cfg->evl = evl;
+
+ flb_log_create(cfg, FLB_LOG_STDERR, FLB_LOG_DEBUG, NULL);
+
+ i_ins = flb_input_new(cfg, "dummy", NULL, FLB_TRUE);
+ i_ins->storage_type = CIO_STORE_FS;
+
+ cio_options_init(&opts);
+
+ opts.root_path = "/tmp/input-chunk-fs_chunks-size_real";
+ opts.log_cb = log_cb;
+ opts.log_level = CIO_LOG_DEBUG;
+ opts.flags = CIO_OPEN;
+
+ cio = cio_create(&opts);
+ flb_storage_input_create(cio, i_ins);
+ flb_input_init_all(cfg);
+
+ o_ins = flb_output_new(cfg, "http", NULL, FLB_TRUE);
+ // not the right way to do this
+ o_ins->id = 1;
+ TEST_CHECK_(o_ins != NULL, "unable to instance output");
+ flb_output_set_property(o_ins, "match", "*");
+ flb_output_set_property(o_ins, "storage.total_limit_size", "1M");
+
+ TEST_CHECK_((flb_router_io_set(cfg) != -1), "unable to router");
+
+ /* fill up the chunk ... */
+ memset((void *)buf, 0x41, sizeof(buf));
+ msgpack_sbuffer_init(&mp_sbuf);
+ gen_buf(&mp_sbuf, buf, sizeof(buf));
+
+ records = flb_mp_count(buf, sizeof(buf));
+ flb_input_chunk_append_raw(i_ins, FLB_INPUT_LOGS, records, "dummy", 4, (void *)buf, sizeof(buf));
+ msgpack_sbuffer_destroy(&mp_sbuf);
+
+ /* then force a realloc? */
+ memset((void *)buf, 0x42, 256);
+ msgpack_sbuffer_init(&mp_sbuf);
+ gen_buf(&mp_sbuf, buf, 256);
+ flb_input_chunk_append_raw(i_ins, FLB_INPUT_LOGS, 256, "dummy", 4, (void *)buf, 256);
+ msgpack_sbuffer_destroy(&mp_sbuf);
+
+ /* clean up test chunks */
+ mk_list_foreach_safe(head, tmp, &i_ins->chunks) {
+ ic = mk_list_entry(head, struct flb_input_chunk, _head);
+ if (cio_chunk_get_real_size(ic->chunk) != cio_chunk_get_content_size(ic->chunk)) {
+ have_size_discrepancy = FLB_TRUE;
+ }
+ chunk_size += flb_input_chunk_get_real_size(ic);
+ }
+
+ TEST_CHECK_(have_size_discrepancy == FLB_TRUE, "need a size discrepancy");
+
+ /* check fs_chunks_size for output plugins against logical and
+ * physical size
+ */
+ mk_list_foreach_safe(head, tmp, &ic->in->config->outputs) {
+ o_ins = mk_list_entry(head, struct flb_output_instance, _head);
+ flb_info("[input chunk test] chunk_size=%zu fs_chunk_size=%zu", chunk_size,
+ o_ins->fs_chunks_size);
+ has_checked_size = FLB_TRUE;
+ TEST_CHECK_(chunk_size == o_ins->fs_chunks_size, "fs_chunks_size must match total real size");
+ }
+ TEST_CHECK_(has_checked_size == FLB_TRUE, "need to check size discrepancy");
+
+ /* FORCE clean up test tasks*/
+ mk_list_foreach_safe(head, tmp, &i_ins->tasks) {
+ task = mk_list_entry(head, struct flb_task, _head);
+ flb_info("[task] cleanup test task");
+ flb_task_destroy(task, FLB_TRUE);
+ }
+
+ /* clean up test chunks */
+ mk_list_foreach_safe(head, tmp, &i_ins->chunks) {
+ ic = mk_list_entry(head, struct flb_input_chunk, _head);
+ flb_input_chunk_destroy(ic, FLB_TRUE);
+ }
+
+ cio_destroy(cio);
+ flb_router_exit(cfg);
+ flb_input_exit_all(cfg);
+ flb_output_exit(cfg);
+ flb_config_exit(cfg);
+}
+
+/* Test list */
+TEST_LIST = {
+ {"input_chunk_exceed_limit", flb_test_input_chunk_exceed_limit},
+ {"input_chunk_buffer_valid", flb_test_input_chunk_buffer_valid},
+ {"input_chunk_dropping_chunks", flb_test_input_chunk_dropping_chunks},
+ {"input_chunk_fs_chunk_size_real", flb_test_input_chunk_fs_chunks_size_real},
+ {NULL, NULL}
+};
diff --git a/fluent-bit/tests/internal/log.c b/fluent-bit/tests/internal/log.c
new file mode 100644
index 00000000..662df98e
--- /dev/null
+++ b/fluent-bit/tests/internal/log.c
@@ -0,0 +1,154 @@
+#include <fluent-bit/flb_sds.h>
+#include <fluent-bit/flb_mem.h>
+#include <fluent-bit/flb_log.h>
+
+#include "flb_tests_internal.h"
+
+#define TIMEOUT 5
+#define TEST_RECORD_01 "this is a test message"
+#define TEST_RECORD_01_SIZE sizeof(TEST_RECORD_01) - 1
+
+#define TEST_RECORD_02 "other type of message"
+#define TEST_RECORD_02_SIZE sizeof(TEST_RECORD_02) - 1
+
+static int check_interval(int timeout, int *interval)
+{
+ if (!TEST_CHECK( (*interval >= timeout - 1) && *interval <= timeout) ) {
+ TEST_MSG("interval error. got=%d expect=%d-%d", *interval, timeout -1 ,timeout);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int update_and_check_interval(int timeout, int ret, int *interval)
+{
+ int ret_val;
+
+ if (ret == FLB_TRUE) {
+ *interval += 1;
+ return 0;
+ }
+
+ /* false means timeout. check interval. */
+ ret_val = check_interval(timeout, interval);
+ *interval = 0; /* reset interval */
+
+ return ret_val;
+}
+
+static void cache_basic_timeout()
+{
+ int i;
+ int ret;
+ int ret_1;
+ int ret_2;
+ int timeout = 5;
+ int interval1 = 0;
+ int interval2 = 0;
+ struct flb_log_cache *cache;
+ struct flb_log_cache_entry *entry;
+
+ printf("\n");
+
+ cache = flb_log_cache_create(10, 0);
+ TEST_CHECK(cache == NULL);
+
+ cache = flb_log_cache_create(5, 4);
+ TEST_CHECK(cache != NULL);
+
+ /* cache must be empty */
+ entry = flb_log_cache_exists(cache, TEST_RECORD_01, TEST_RECORD_01_SIZE);
+ TEST_CHECK(entry == NULL);
+
+ /* upon trying to check for a suppress and if not found, it must be added */
+ ret_1 = flb_log_cache_check_suppress(cache, TEST_RECORD_01, TEST_RECORD_01_SIZE);
+ TEST_CHECK(ret_1 == FLB_FALSE);
+
+ /* double check that it was added */
+ entry = flb_log_cache_exists(cache, TEST_RECORD_01, TEST_RECORD_01_SIZE);
+ TEST_CHECK(entry != NULL);
+
+ printf("------------------------\n");
+
+ /* reset */
+ flb_log_cache_destroy(cache);
+
+ /* create a new cache */
+ cache = flb_log_cache_create(timeout, 4);
+ TEST_CHECK(cache != NULL);
+
+ ret_1 = flb_log_cache_check_suppress(cache, TEST_RECORD_01, TEST_RECORD_01_SIZE);
+ ret_2 = flb_log_cache_check_suppress(cache, TEST_RECORD_02, TEST_RECORD_02_SIZE);
+ TEST_CHECK(ret_1 == FLB_FALSE);
+ TEST_CHECK(ret_2 == FLB_FALSE);
+ sleep(1);
+ interval1++;
+ interval2++;
+
+ for (i = 1; i < 10; i++) {
+ ret_1 = flb_log_cache_check_suppress(cache, TEST_RECORD_01, TEST_RECORD_01_SIZE);
+ ret = update_and_check_interval(timeout, ret_1, &interval1);
+ if (!TEST_CHECK(ret == 0)) {
+ TEST_MSG("update_and_check_interval for TEST_RECORD_01 failed. i=%d", i);
+ }
+
+ ret_2 = flb_log_cache_check_suppress(cache, TEST_RECORD_02, TEST_RECORD_02_SIZE);
+ ret = update_and_check_interval(timeout, ret_2, &interval2);
+ if (!TEST_CHECK(ret == 0)) {
+ TEST_MSG("update_and_check_interval for TEST_RECORD_02 failed. i=%d", i);
+ }
+
+ sleep(1);
+ }
+ ret_1 = flb_log_cache_check_suppress(cache, TEST_RECORD_01, TEST_RECORD_01_SIZE);
+ ret = update_and_check_interval(timeout, ret_1, &interval1);
+ TEST_CHECK(ret == 0);
+
+ ret_2 = flb_log_cache_check_suppress(cache, TEST_RECORD_02, TEST_RECORD_02_SIZE);
+ ret = update_and_check_interval(timeout, ret_2, &interval2);
+ TEST_CHECK(ret == 0);
+
+ flb_log_cache_destroy(cache);
+}
+
+static void cache_one_slot()
+{
+ int i;
+ int ret_1;
+ int ret_2;
+ struct flb_log_cache *cache;
+
+ printf("\n");
+
+ cache = flb_log_cache_create(2, 1);
+ TEST_CHECK(cache != NULL);
+
+ for (i = 0; i < 10; i++) {
+
+ if (i == 0) {
+ ret_1 = flb_log_cache_check_suppress(cache, TEST_RECORD_01, TEST_RECORD_01_SIZE);
+ TEST_CHECK(ret_1 == FLB_FALSE);
+
+ ret_1 = flb_log_cache_check_suppress(cache, TEST_RECORD_01, TEST_RECORD_01_SIZE);
+ TEST_CHECK(ret_1 == FLB_TRUE);
+ }
+ else {
+ ret_2 = flb_log_cache_check_suppress(cache, TEST_RECORD_02, TEST_RECORD_02_SIZE);
+ ret_1 = flb_log_cache_check_suppress(cache, TEST_RECORD_01, TEST_RECORD_01_SIZE);
+
+ TEST_CHECK(ret_1 == FLB_FALSE);
+ TEST_CHECK(ret_2 == FLB_FALSE);
+ }
+
+ sleep(1);
+ }
+
+ flb_log_cache_destroy(cache);
+}
+
+TEST_LIST = {
+ { "cache_basic_timeout" , cache_basic_timeout },
+ { "cache_one_slot" , cache_one_slot },
+ { 0 }
+};
diff --git a/fluent-bit/tests/internal/log_event_decoder.c b/fluent-bit/tests/internal/log_event_decoder.c
new file mode 100644
index 00000000..6f93edcf
--- /dev/null
+++ b/fluent-bit/tests/internal/log_event_decoder.c
@@ -0,0 +1,281 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/* Fluent Bit
+ * ==========
+ * Copyright (C) 2015-2023 The Fluent Bit Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <fluent-bit/flb_info.h>
+#include <fluent-bit/flb_time.h>
+#include <fluent-bit/flb_pack.h>
+#include <fluent-bit/flb_log_event_decoder.h>
+#include <msgpack.h>
+#include <string.h>
+
+#include "flb_tests_internal.h"
+
+static int pack_event_time(msgpack_packer *pck, struct flb_time *tm)
+{
+ char ext_data[8] = {0};
+ uint32_t tmp;
+
+ /* event time */
+ tmp = htonl((uint32_t)tm->tm.tv_sec); /* second from epoch */
+ memcpy(&ext_data, &tmp, 4);
+ tmp = htonl((uint32_t)tm->tm.tv_nsec);/* nanosecond */
+ memcpy(&ext_data[4], &tmp, 4);
+
+ msgpack_pack_ext(pck, 8, 0);
+ msgpack_pack_ext_body(pck, ext_data, sizeof(ext_data));
+
+ return 0;
+}
+
+void create_destroy()
+{
+ struct flb_log_event_decoder *dec = NULL;
+ char buf[256] = {0};
+
+ dec = flb_log_event_decoder_create(&buf[0], sizeof(buf));
+ if (!TEST_CHECK(dec != NULL)) {
+ TEST_MSG("flb_log_event_decoder_create failed");
+ return;
+ }
+
+ flb_log_event_decoder_destroy(dec);
+}
+
+void init_destroy()
+{
+ struct flb_log_event_decoder dec;
+ char buf[256] = {0};
+ int ret;
+
+ ret = flb_log_event_decoder_init(&dec, &buf[0], sizeof(buf));
+ if (!TEST_CHECK(ret == FLB_EVENT_DECODER_SUCCESS)) {
+ TEST_MSG("flb_log_event_decoder_init failed. ret=%s",
+ flb_log_event_decoder_get_error_description(ret));
+ return;
+ }
+
+ flb_log_event_decoder_destroy(&dec);
+}
+
+void decode_timestamp()
+{
+ struct flb_time tm;
+ msgpack_sbuffer sbuf;
+ msgpack_packer pck;
+ msgpack_unpacked result;
+ size_t offset = 0;
+
+ int ret;
+
+ msgpack_sbuffer_init(&sbuf);
+ msgpack_packer_init(&pck, &sbuf, msgpack_sbuffer_write);
+ msgpack_pack_int64(&pck, 123456);
+
+ msgpack_unpacked_init(&result);
+ msgpack_unpack_next(&result, sbuf.data, sbuf.size, &offset);
+
+ ret = flb_log_event_decoder_decode_timestamp(&result.data, &tm);
+ if (!TEST_CHECK(ret == FLB_EVENT_DECODER_SUCCESS)) {
+ TEST_MSG("flb_log_event_decoder_timestamp failed. ret=%s",
+ flb_log_event_decoder_get_error_description(ret));
+ return;
+ }
+ if (!TEST_CHECK(tm.tm.tv_sec == 123456 && tm.tm.tv_nsec == 0)) {
+ TEST_MSG("timestamp error. tv_sec=%ld tv_nsec=%lu", tm.tm.tv_sec, tm.tm.tv_nsec);
+ return;
+ }
+
+ msgpack_unpacked_init(&result);
+ msgpack_sbuffer_clear(&sbuf);
+
+ /* event time */
+ flb_time_set(&tm, 123456, 123456);
+ pack_event_time(&pck, &tm);
+
+ offset = 0;
+ msgpack_unpack_next(&result, sbuf.data, sbuf.size, &offset);
+
+ flb_time_zero(&tm);
+ ret = flb_log_event_decoder_decode_timestamp(&result.data, &tm);
+ if (!TEST_CHECK(ret == FLB_EVENT_DECODER_SUCCESS)) {
+ TEST_MSG("flb_log_event_decoder_timestamp failed. ret=%s",
+ flb_log_event_decoder_get_error_description(ret));
+ return;
+ }
+ if (!TEST_CHECK(tm.tm.tv_sec == 123456 && tm.tm.tv_nsec == 123456)) {
+ TEST_MSG("timestamp error. tv_sec=%ld tv_nsec=%lu", tm.tm.tv_sec, tm.tm.tv_nsec);
+ return;
+ }
+
+ msgpack_unpacked_destroy(&result);
+ msgpack_sbuffer_destroy(&sbuf);
+}
+
+void decode_object()
+{
+ struct flb_log_event_decoder dec;
+ struct flb_log_event event;
+ int ret;
+ struct flb_time tm;
+ msgpack_sbuffer sbuf;
+ msgpack_packer pck;
+ msgpack_unpacked result;
+ size_t offset = 0;
+ char *json = NULL;
+
+ flb_time_set(&tm, 123456, 123456);
+
+ msgpack_sbuffer_init(&sbuf);
+ msgpack_packer_init(&pck, &sbuf, msgpack_sbuffer_write);
+
+ /* [[123456.123456, {}], {"key1":"val1", "key2":"val2"}] */
+ msgpack_pack_array(&pck, 2);
+ msgpack_pack_array(&pck, 2);
+ pack_event_time(&pck, &tm);
+ msgpack_pack_map(&pck, 0);
+ msgpack_pack_map(&pck, 2);
+
+ msgpack_pack_str(&pck, 4);
+ msgpack_pack_str_body(&pck, "key1", 4);
+ msgpack_pack_str(&pck, 4);
+ msgpack_pack_str_body(&pck, "val1", 4);
+
+ msgpack_pack_str(&pck, 4);
+ msgpack_pack_str_body(&pck, "key2", 4);
+ msgpack_pack_str(&pck, 4);
+ msgpack_pack_str_body(&pck, "val2", 4);
+
+ msgpack_unpacked_init(&result);
+ ret = msgpack_unpack_next(&result, sbuf.data, sbuf.size, &offset);
+ if (!TEST_CHECK(ret == MSGPACK_UNPACK_SUCCESS)) {
+ TEST_MSG("msgpack_unpack_next failed");
+ return;
+ }
+
+ ret = flb_event_decoder_decode_object(&dec, &event, &result.data);
+ if (!TEST_CHECK(ret == FLB_EVENT_DECODER_SUCCESS)) {
+ TEST_MSG("flb_log_event_decoder_decode_object failed. ret=%s",
+ flb_log_event_decoder_get_error_description(ret));
+ return;
+ }
+
+ if (!TEST_CHECK(flb_time_equal(&tm, &event.timestamp))) {
+ TEST_MSG("timestamp mismatch");
+ return;
+ }
+
+ json = flb_msgpack_to_json_str(4096, event.body);
+ if (!TEST_CHECK(json != NULL)) {
+ TEST_MSG("flb_msgpack_to_json_str error");
+ return;
+ }
+ if (!TEST_CHECK(strstr(json, "\"key1\":\"val1\"") != NULL)) {
+ TEST_MSG("\"key1\":\"val1\" is missing. json=%s", json);
+ return;
+ }
+ if (!TEST_CHECK(strstr(json, "\"key2\":\"val2\"") != NULL)) {
+ TEST_MSG("\"key2\":\"val2\" is missing. json=%s", json);
+ return;
+ }
+
+ flb_free(json);
+ msgpack_unpacked_destroy(&result);
+ msgpack_sbuffer_destroy(&sbuf);
+}
+
+void decoder_next()
+{
+ struct flb_log_event_decoder dec;
+ struct flb_log_event event;
+ int ret;
+ struct flb_time tm;
+ msgpack_sbuffer sbuf;
+ msgpack_packer pck;
+ char *json = NULL;
+
+ flb_time_set(&tm, 123456, 123456);
+
+ msgpack_sbuffer_init(&sbuf);
+ msgpack_packer_init(&pck, &sbuf, msgpack_sbuffer_write);
+
+ /* [[123456.123456, {}], {"key1":"val1", "key2":"val2"}] */
+ msgpack_pack_array(&pck, 2);
+ msgpack_pack_array(&pck, 2);
+ pack_event_time(&pck, &tm);
+ msgpack_pack_map(&pck, 0);
+ msgpack_pack_map(&pck, 2);
+
+ msgpack_pack_str(&pck, 4);
+ msgpack_pack_str_body(&pck, "key1", 4);
+ msgpack_pack_str(&pck, 4);
+ msgpack_pack_str_body(&pck, "val1", 4);
+
+ msgpack_pack_str(&pck, 4);
+ msgpack_pack_str_body(&pck, "key2", 4);
+ msgpack_pack_str(&pck, 4);
+ msgpack_pack_str_body(&pck, "val2", 4);
+
+
+ ret = flb_log_event_decoder_init(&dec, (char *)sbuf.data, sbuf.size);
+ if (!TEST_CHECK(ret == FLB_EVENT_DECODER_SUCCESS)) {
+ TEST_MSG("flb_log_event_decoder_init failed. ret=%s",
+ flb_log_event_decoder_get_error_description(ret));
+ return;
+ }
+
+ ret = flb_log_event_decoder_next(&dec, &event);
+ if (!TEST_CHECK(ret == FLB_EVENT_DECODER_SUCCESS)) {
+ TEST_MSG("flb_log_event_decoder_next failed. ret=%s",
+ flb_log_event_decoder_get_error_description(ret));
+ return;
+ }
+ if (!TEST_CHECK(flb_time_equal(&tm, &event.timestamp))) {
+ TEST_MSG("timestamp mismatch");
+ return;
+ }
+
+ json = flb_msgpack_to_json_str(4096, event.body);
+ if (!TEST_CHECK(json != NULL)) {
+ TEST_MSG("flb_msgpack_to_json_str error");
+ return;
+ }
+ if (!TEST_CHECK(strstr(json, "\"key1\":\"val1\"") != NULL)) {
+ TEST_MSG("\"key1\":\"val1\" is missing. json=%s", json);
+ return;
+ }
+ if (!TEST_CHECK(strstr(json, "\"key2\":\"val2\"") != NULL)) {
+ TEST_MSG("\"key2\":\"val2\" is missing. json=%s", json);
+ return;
+ }
+
+ flb_free(json);
+ flb_log_event_decoder_destroy(&dec);
+ msgpack_sbuffer_destroy(&sbuf);
+}
+
+
+
+TEST_LIST = {
+ { "create_destroy", create_destroy },
+ { "init_destroy", init_destroy },
+ { "decode_timestamp", decode_timestamp },
+ { "decode_object", decode_object },
+ { "decoder_next", decoder_next },
+ { 0 }
+};
diff --git a/fluent-bit/tests/internal/lua.c b/fluent-bit/tests/internal/lua.c
new file mode 100644
index 00000000..122748fb
--- /dev/null
+++ b/fluent-bit/tests/internal/lua.c
@@ -0,0 +1,325 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+#include <fluent-bit/flb_lua.h>
+#include <lauxlib.h>
+#include <lua.h>
+
+#include "flb_tests_internal.h"
+#include "mpack/mpack.h"
+#include "msgpack/object.h"
+#include "msgpack/pack.h"
+#include "msgpack/sbuffer.h"
+
+/* Helper lua function which returns a string representation of lua objects.
+ * Tables are stringified in key order (lua table iteration order is not deterministic. */
+const char lua_stringify_object_helper[] = (""
+"function stringify(o)\n"
+" if type(o) == 'table' then\n"
+" local keys = {}\n"
+" for k in pairs(o) do table.insert(keys, k) end\n"
+" table.sort(keys)\n"
+" local s = '{ '\n"
+" for _,k in ipairs(keys) do\n"
+" local v = o[k]\n"
+" s = s .. '['..k..'] = ' .. stringify(v) .. ' '\n"
+" end\n"
+" return s .. '}'\n"
+" else\n"
+" return tostring(o)\n"
+" end\n"
+"end\n");
+
+static lua_State *lua_setup(const char *script)
+{
+ lua_State *ret = luaL_newstate();
+ if (!ret) {
+ flb_error("[lua] error creating new context");
+ return NULL;
+ }
+ luaL_openlibs(ret);
+ luaL_loadstring(ret, lua_stringify_object_helper);
+ if (lua_pcall(ret, 0, 0, 0)) {
+ flb_error("[lua] error executing stringify helper script");
+ lua_close(ret);
+ return NULL;
+ }
+ if (script) {
+ luaL_loadstring(ret, script);
+ if (lua_pcall(ret, 0, 0, 0)) {
+ flb_error("[lua] error executing test script");
+ lua_close(ret);
+ return NULL;
+ }
+ }
+ return ret;
+}
+
+static void check_equals(lua_State *l, const char *expected)
+{
+ /* push the stringify function on the stack */
+ lua_getglobal(l, "stringify");
+ /* swap the top two elements of the stack, so that the function is below the arg */
+ lua_insert(l, -2);
+ /* call the function */
+ if (lua_pcall(l, 1, 1, 0)) {
+ flb_error("[lua] error calling stringify helper function");
+ return;
+ }
+ const char *result = lua_tostring(l, -1);
+ TEST_CHECK(strcmp(result, expected) == 0);
+ TEST_MSG("Expected: %s", expected);
+ TEST_MSG("Actual: %s", result);
+ /* remove the result */
+ lua_pop(l, 1);
+}
+
+static void test_is_valid_func()
+{
+ lua_State *l = lua_setup(NULL);
+ TEST_CHECK(flb_lua_is_valid_func(l, "invalid_function") == false);
+ TEST_CHECK(flb_lua_is_valid_func(l, "stringify") == true);
+ lua_close(l);
+}
+
+static void test_pushtimetable()
+{
+ lua_State *l = lua_setup(NULL);
+ struct flb_time t = {{ 5, 6 }};
+ flb_lua_pushtimetable(l, &t);
+ check_equals(l, "{ [nsec] = 6 [sec] = 5 }");
+ t.tm.tv_nsec = 7;
+ t.tm.tv_sec = 8;
+ flb_lua_pushtimetable(l, &t);
+ check_equals(l, "{ [nsec] = 7 [sec] = 8 }");
+ lua_close(l);
+}
+
+static void test_pushmsgpack()
+{
+ msgpack_packer pck;
+ msgpack_sbuffer sbuf;
+ msgpack_unpacked msg;
+ lua_State *l = lua_setup(NULL);
+
+ msgpack_sbuffer_init(&sbuf);
+ msgpack_packer_init(&pck, &sbuf, msgpack_sbuffer_write);
+ msgpack_pack_array(&pck, 3);
+ msgpack_pack_map(&pck, 1);
+ msgpack_pack_str_with_body(&pck, "key", 3);
+ msgpack_pack_str_with_body(&pck, "value", 5);
+ msgpack_pack_str_with_body(&pck, "msgpack-str", 11);
+ msgpack_pack_int(&pck, 4);
+
+ msgpack_unpacked_init(&msg);
+ msgpack_unpack_next(&msg, sbuf.data, sbuf.size, NULL);
+ flb_lua_pushmsgpack(l, &msg.data);
+ check_equals(l, "{ [1] = { [key] = value } [2] = msgpack-str [3] = 4 }");
+
+ msgpack_unpacked_destroy(&msg);
+ msgpack_sbuffer_destroy(&sbuf);
+ lua_close(l);
+}
+
+static void test_pushmpack()
+{
+ msgpack_packer pck;
+ msgpack_sbuffer sbuf;
+ mpack_reader_t reader;
+ lua_State *l = lua_setup(NULL);
+
+ msgpack_sbuffer_init(&sbuf);
+ msgpack_packer_init(&pck, &sbuf, msgpack_sbuffer_write);
+ msgpack_pack_array(&pck, 3);
+ msgpack_pack_map(&pck, 1);
+ msgpack_pack_str_with_body(&pck, "key", 3);
+ msgpack_pack_str_with_body(&pck, "value", 5);
+ msgpack_pack_str_with_body(&pck, "msgpack-str", 11);
+ msgpack_pack_int(&pck, 4);
+
+ mpack_reader_init_data(&reader, sbuf.data, sbuf.size);
+ flb_lua_pushmpack(l, &reader);
+ check_equals(l, "{ [1] = { [key] = value } [2] = msgpack-str [3] = 4 }");
+
+ msgpack_sbuffer_destroy(&sbuf);
+ lua_close(l);
+}
+
+static void test_tomsgpack()
+{
+ const char expected[] = "[{\"key\"=>\"value\"}, \"msgpack-str\", 4]";
+ char buf[256];
+ msgpack_packer pck;
+ msgpack_sbuffer sbuf;
+ msgpack_unpacked msg;
+ struct flb_lua_l2c_config l2cc;
+ lua_State *l = lua_setup("obj = {{['key']='value'},'msgpack-str',4}");
+
+ msgpack_sbuffer_init(&sbuf);
+ msgpack_packer_init(&pck, &sbuf, msgpack_sbuffer_write);
+ mk_list_init(&l2cc.l2c_types);
+ l2cc.l2c_types_num = 0;
+
+ lua_getglobal(l, "obj");
+ flb_lua_tomsgpack(l, &pck, 0, &l2cc);
+
+ msgpack_unpacked_init(&msg);
+ msgpack_unpack_next(&msg, sbuf.data, sbuf.size, NULL);
+ msgpack_object_print_buffer(buf, sizeof(buf), msg.data);
+
+ TEST_CHECK(strcmp(buf, expected) == 0);
+ TEST_MSG("Expected: %s", expected);
+ TEST_MSG("Actual: %s", buf);
+
+ msgpack_unpacked_destroy(&msg);
+ msgpack_sbuffer_destroy(&sbuf);
+ lua_close(l);
+}
+
+static void test_tompack()
+{
+ const char expected[] = "[{\"key\"=>\"value\"}, \"msgpack-str\", 4]";
+ char buf[256];
+ char printbuf[256];
+ mpack_writer_t writer;
+ msgpack_unpacked msg;
+ struct flb_lua_l2c_config l2cc;
+ lua_State *l = lua_setup("obj = {{['key']='value'},'msgpack-str',4}");
+
+ mpack_writer_init(&writer, buf, sizeof(buf));
+ mk_list_init(&l2cc.l2c_types);
+ l2cc.l2c_types_num = 0;
+
+ lua_getglobal(l, "obj");
+ flb_lua_tompack(l, &writer, 0, &l2cc);
+
+ msgpack_unpacked_init(&msg);
+ msgpack_unpack_next(&msg, writer.buffer, writer.position - writer.buffer, NULL);
+ msgpack_object_print_buffer(printbuf, sizeof(printbuf), msg.data);
+
+ TEST_CHECK(strcmp(printbuf, expected) == 0);
+ TEST_MSG("Expected: %s", expected);
+ TEST_MSG("Actual: %s", buf);
+
+ msgpack_unpacked_destroy(&msg);
+ lua_close(l);
+}
+
+
+static void test_lua_arraylength()
+{
+ lua_State *l;
+ int i;
+ int len;
+ int size = 10;
+
+ l = luaL_newstate();
+ if (!TEST_CHECK(l != NULL)) {
+ TEST_MSG("luaL_newstate faild");
+ return;
+ }
+ luaL_openlibs(l);
+
+ /* create array. */
+ lua_createtable(l, size, 0);
+
+ for (i=1; i<=size; i++) {
+ lua_pushinteger(l, i); /* set an index of array */
+ lua_pushinteger(l, 3+i); /* set a value */
+ lua_settable(l, -3); /* points created table */
+ }
+
+ len = flb_lua_arraylength(l, -1);
+ if (!TEST_CHECK(len == size)) {
+ TEST_MSG("size error. got=%d expect=%d", len, size);
+ }
+ lua_pop(l, 1);
+
+ lua_close(l);
+}
+
+static void test_lua_arraylength_with_index()
+{
+ lua_State *l;
+ int i;
+ int len;
+ int size = 10;
+
+ l = luaL_newstate();
+ if (!TEST_CHECK(l != NULL)) {
+ TEST_MSG("luaL_newstate faild");
+ return;
+ }
+ luaL_openlibs(l);
+
+ /* create array. */
+ lua_createtable(l, size, 0);
+
+ for (i=1; i<=size; i++) {
+ lua_pushinteger(l, i); /* set an index of array */
+ lua_pushinteger(l, 3+i); /* set a value */
+ lua_settable(l, -3); /* points created table */
+ }
+
+ /* push 2 values */
+ lua_pushinteger(l, 100);
+ lua_pushinteger(l, 101);
+
+ len = flb_lua_arraylength(l, -3); /* points array. -1 points 101, -2 points 100 */
+ if (!TEST_CHECK(len == size)) {
+ TEST_MSG("size error. got=%d expect=%d", len, size);
+ }
+ lua_pop(l, 1);
+
+ lua_close(l);
+}
+
+static void test_lua_arraylength_for_array_contains_nil()
+{
+ lua_State *l;
+ int i;
+ int len;
+ int size = 10;
+
+ l = luaL_newstate();
+ if (!TEST_CHECK(l != NULL)) {
+ TEST_MSG("luaL_newstate faild");
+ return;
+ }
+ luaL_openlibs(l);
+
+ /* create array. */
+ lua_createtable(l, size, 0);
+
+ for (i=1; i<=size; i++) {
+ lua_pushinteger(l, i); /* set an index of array */
+ if (i == 7) {
+ lua_pushnil(l);
+ }
+ else {
+ lua_pushinteger(l, 3+i); /* set a value */
+ }
+ lua_settable(l, -3); /* points created table */
+ }
+
+ len = flb_lua_arraylength(l, -1);
+ if (!TEST_CHECK(len == size)) {
+ TEST_MSG("size error. got=%d expect=%d", len, size);
+ }
+ lua_pop(l, 1);
+
+ lua_close(l);
+}
+
+
+TEST_LIST = {
+ { "lua_is_valid_func" , test_is_valid_func},
+ { "lua_pushtimetable" , test_pushtimetable},
+ { "lua_pushmsgpack" , test_pushmsgpack },
+ { "lua_pushmpack" , test_pushmpack },
+ { "lua_tomsgpack" , test_tomsgpack },
+ { "lua_tompack" , test_tompack },
+ { "lua_arraylength" , test_lua_arraylength },
+ { "lua_arraylength_with_index" , test_lua_arraylength_with_index },
+ { "lua_arraylength_for_array_contains_nil", test_lua_arraylength_for_array_contains_nil},
+ { 0 }
+};
diff --git a/fluent-bit/tests/internal/metrics.c b/fluent-bit/tests/internal/metrics.c
new file mode 100644
index 00000000..08f8bc70
--- /dev/null
+++ b/fluent-bit/tests/internal/metrics.c
@@ -0,0 +1,83 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+#include <fluent-bit/flb_info.h>
+#include <fluent-bit/flb_mem.h>
+#include <fluent-bit/flb_error.h>
+#include <fluent-bit/flb_metrics.h>
+
+#include "flb_tests_internal.h"
+
+static void test_create_usage()
+{
+ int ret;
+ int id_1;
+ int id_2;
+ int id_3;
+ struct flb_metric *m;
+ struct flb_metrics *ctx;
+
+ /* Create and destroy */
+ ctx = flb_metrics_create("metrics");
+ TEST_CHECK(ctx != NULL);
+ ret = flb_metrics_destroy(ctx);
+ TEST_CHECK(ret == 0);
+
+ /* Register one metric */
+ ctx = flb_metrics_create("metrics");
+ id_1 = flb_metrics_add(-1, "sample", ctx);
+ TEST_CHECK(id_1 == 0);
+ ret = flb_metrics_destroy(ctx);
+ TEST_CHECK(ret == 1);
+
+ /* Duplicate metric ID, it should fail */
+ ctx = flb_metrics_create("metrics");
+ id_1 = flb_metrics_add(-1, "sample 1", ctx);
+ id_2 = flb_metrics_add(0, "sample 2", ctx);
+ TEST_CHECK(id_2 == -1);
+ ret = flb_metrics_destroy(ctx);
+ TEST_CHECK(ret == 1);
+
+ /* Auto ID */
+ ctx = flb_metrics_create("ctx");
+ id_1 = flb_metrics_add(-1, "sample 1", ctx);
+ id_2 = flb_metrics_add(-1, "sample 2", ctx);
+ id_3 = flb_metrics_add(-1, "sample 3", ctx);
+ TEST_CHECK(id_1 == 0);
+ TEST_CHECK(id_2 == 1);
+ TEST_CHECK(id_3 == 2);
+ ret = flb_metrics_destroy(ctx);
+ TEST_CHECK(ret == 3);
+
+ /* Update values */
+ ctx = flb_metrics_create("ctx");
+ id_1 = flb_metrics_add(-1, "sample 1", ctx);
+ id_2 = flb_metrics_add(-1, "sample 2", ctx);
+ id_3 = flb_metrics_add(5, "sample 3", ctx);
+ TEST_CHECK(id_1 == 0);
+ TEST_CHECK(id_2 == 1);
+ TEST_CHECK(id_3 == 5);
+
+ ret = flb_metrics_sum(id_1, 2001, ctx);
+ TEST_CHECK(ret == 0);
+
+ ret = flb_metrics_sum(id_2, 2017, ctx);
+ TEST_CHECK(ret == 0);
+
+ ret = flb_metrics_sum(1234, 0, ctx);
+ TEST_CHECK(ret == -1);
+
+ ret = flb_metrics_sum(id_3, 1, ctx);
+ TEST_CHECK(ret == 0);
+
+ m = flb_metrics_get_id(id_3, ctx);
+ TEST_CHECK(m != NULL);
+ TEST_CHECK(m->val == 1);
+
+ ret = flb_metrics_destroy(ctx);
+ TEST_CHECK(ret == 3);
+}
+
+TEST_LIST = {
+ { "create_usage", test_create_usage},
+ { 0 }
+};
diff --git a/fluent-bit/tests/internal/mp.c b/fluent-bit/tests/internal/mp.c
new file mode 100644
index 00000000..5c1e17c2
--- /dev/null
+++ b/fluent-bit/tests/internal/mp.c
@@ -0,0 +1,353 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+#include <fluent-bit/flb_compat.h>
+#include <fluent-bit/flb_info.h>
+#include <fluent-bit/flb_mem.h>
+#include <fluent-bit/flb_pack.h>
+#include <fluent-bit/flb_mp.h>
+#include <msgpack.h>
+
+#include "flb_tests_internal.h"
+
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#define APACHE_10K FLB_TESTS_DATA_PATH "/data/mp/apache_10k.mp"
+
+void test_count()
+{
+ int ret;
+ int count;
+ char *data;
+ size_t len;
+ struct stat st;
+
+ ret = stat(APACHE_10K, &st);
+ if (ret == -1) {
+ exit(1);
+ }
+ len = st.st_size;
+
+ data = mk_file_to_buffer(APACHE_10K);
+ TEST_CHECK(data != NULL);
+
+ count = flb_mp_count(data, len);
+ TEST_CHECK(count == 10000);
+ flb_free(data);
+}
+
+void test_map_header()
+{
+ int i;
+ int ret;
+ size_t off = 0;
+ msgpack_packer mp_pck;
+ msgpack_object root;
+ msgpack_sbuffer mp_sbuf;
+ msgpack_unpacked result;
+ struct flb_mp_map_header mh;
+
+ msgpack_sbuffer_init(&mp_sbuf);
+ msgpack_packer_init(&mp_pck, &mp_sbuf, msgpack_sbuffer_write);
+
+ /* Init map header */
+ flb_mp_map_header_init(&mh, &mp_pck);
+
+ /* Append 1000 items */
+ for (i = 0; i < 100; i++) {
+ flb_mp_map_header_append(&mh);
+ msgpack_pack_str(&mp_pck, 3);
+ msgpack_pack_str_body(&mp_pck, "key", 3);
+ msgpack_pack_uint64(&mp_pck, i);
+ }
+ flb_mp_map_header_end(&mh);
+
+ /* Unpack and check */
+ msgpack_unpacked_init(&result);
+ ret = msgpack_unpack_next(&result, mp_sbuf.data, mp_sbuf.size, &off);
+ TEST_CHECK(ret == MSGPACK_UNPACK_SUCCESS);
+
+ root = result.data;
+ TEST_CHECK(root.type == MSGPACK_OBJECT_MAP);
+ TEST_CHECK(root.via.array.size == 100);
+
+ msgpack_unpacked_destroy(&result);
+ msgpack_sbuffer_destroy(&mp_sbuf);
+}
+
+void test_accessor_keys_remove()
+{
+ int len;
+ int ret;
+ int type;
+ size_t off = 0;
+ char *buf;
+ size_t size;
+ char *out_buf;
+ size_t out_size;
+ char *json;
+ msgpack_unpacked result;
+ msgpack_object map;
+ struct flb_mp_accessor *mpa;
+ struct mk_list patterns;
+
+ /* Sample JSON message */
+ json =
+ "{\"key1\": \"something\", "
+ "\"kubernetes\": "
+ " [true, "
+ " false, "
+ " {\"a\": false, "
+ " \"annotations\": { "
+ " \"fluentbit.io/tag\": \"thetag\","
+ " \"extra\": false\""
+ "}}]}";
+
+ /* Convert to msgpack */
+ len = strlen(json);
+ ret = flb_pack_json(json, len, &buf, &size, &type, NULL);
+ TEST_CHECK(ret == 0);
+ if (ret == -1) {
+ exit(EXIT_FAILURE);
+ }
+
+ /* Unpack the content */
+ msgpack_unpacked_init(&result);
+ msgpack_unpack_next(&result, buf, size, &off);
+ map = result.data;
+
+ /* Create list of patterns */
+ flb_slist_create(&patterns);
+ flb_slist_add(&patterns, "$kubernetes[2]['annotations']['fluentbit.io/tag']");
+ flb_slist_add(&patterns, "$key1");
+
+ /* Create mp accessor */
+ mpa = flb_mp_accessor_create(&patterns);
+ TEST_CHECK(mpa != NULL);
+
+ /* Remove the entry that matches the pattern(s) */
+ ret = flb_mp_accessor_keys_remove(mpa, &map, (void *) &out_buf, &out_size);
+ TEST_CHECK(ret == FLB_TRUE);
+
+ printf("\n=== ORIGINAL ===\n");
+ flb_pack_print(buf, size);
+ flb_free(buf);
+
+ printf("=== FINAL MAP ===\n");
+ if (ret == FLB_TRUE) {
+ flb_pack_print(out_buf, out_size);
+ flb_free(out_buf);
+ }
+
+ flb_mp_accessor_destroy(mpa);
+ flb_slist_destroy(&patterns);
+ msgpack_unpacked_destroy(&result);
+}
+
+/* https://github.com/fluent/fluent-bit/issues/5546 */
+void test_keys_remove_subkey_key()
+{
+ int len;
+ int ret;
+ int type;
+ size_t off = 0;
+ char *buf;
+ size_t size;
+ char *out_buf;
+ size_t out_size;
+ char *json;
+ char final_json[2048] = {0};
+ msgpack_unpacked result;
+ msgpack_unpacked result_final;
+ msgpack_object map;
+ struct flb_mp_accessor *mpa;
+ struct mk_list patterns;
+
+ /* Sample JSON message */
+ json =
+ "{\"key1\": \"something\", "
+ "\"kubernetes\": "
+ " [true, "
+ " false, "
+ " {\"a\": false, "
+ " \"annotations\": { "
+ " \"fluentbit.io/tag\": \"thetag\","
+ " \"extra\": false\""
+ "}}]}";
+
+ /* Convert to msgpack */
+ len = strlen(json);
+ ret = flb_pack_json(json, len, &buf, &size, &type, NULL);
+ TEST_CHECK(ret == 0);
+ if (ret == -1) {
+ exit(EXIT_FAILURE);
+ }
+
+ /* Unpack the content */
+ msgpack_unpacked_init(&result);
+ msgpack_unpack_next(&result, buf, size, &off);
+ map = result.data;
+
+ /* Create list of patterns */
+ flb_slist_create(&patterns);
+
+ /* sub key -> key */
+ flb_slist_add(&patterns, "$kubernetes[2]['annotations']['fluentbit.io/tag']");
+ flb_slist_add(&patterns, "$kubernetes");
+
+
+ /* Create mp accessor */
+ mpa = flb_mp_accessor_create(&patterns);
+ TEST_CHECK(mpa != NULL);
+
+ /* Remove the entry that matches the pattern(s) */
+ ret = flb_mp_accessor_keys_remove(mpa, &map, (void *) &out_buf, &out_size);
+ TEST_CHECK(ret == FLB_TRUE);
+
+ printf("\n=== ORIGINAL ===\n");
+ flb_pack_print(buf, size);
+ flb_free(buf);
+
+ printf("=== FINAL MAP ===\n");
+ if (ret == FLB_TRUE) {
+ flb_pack_print(out_buf, out_size);
+ }
+ msgpack_unpacked_destroy(&result);
+
+ off = 0;
+ msgpack_unpacked_init(&result_final);
+ msgpack_unpack_next(&result_final, out_buf, out_size, &off);
+ flb_msgpack_to_json(&final_json[0], sizeof(final_json), &result_final.data);
+
+ if (!TEST_CHECK(strstr(&final_json[0] ,"kubernetes") == NULL)) {
+ TEST_MSG("kubernetes field should be removed");
+ }
+
+ msgpack_unpacked_destroy(&result_final);
+
+ flb_free(out_buf);
+ flb_mp_accessor_destroy(mpa);
+ flb_slist_destroy(&patterns);
+
+}
+
+void remove_subkey_keys(char *list[], int list_size, int index_start)
+{
+ int len;
+ int ret;
+ int type;
+ size_t off = 0;
+ char *buf;
+ size_t size;
+ char *out_buf;
+ size_t out_size;
+ char *json;
+ char final_json[2048] = {0};
+ msgpack_unpacked result;
+ msgpack_unpacked result_final;
+ msgpack_object map;
+ struct flb_mp_accessor *mpa;
+ struct mk_list patterns;
+ int i;
+ int count = 0;
+
+ /* Sample JSON message */
+ json =
+ "{\"key1\": \"something\", "
+ "\"kubernetes\": "
+ " [true, "
+ " false, "
+ " {\"a\": false, "
+ " \"annotations\": { "
+ " \"fluentbit.io/tag\": \"thetag\","
+ " \"extra\": false\""
+ "}}]}";
+
+ /* Convert to msgpack */
+ len = strlen(json);
+ ret = flb_pack_json(json, len, &buf, &size, &type, NULL);
+ TEST_CHECK(ret == 0);
+ if (ret == -1) {
+ exit(EXIT_FAILURE);
+ }
+
+ /* Unpack the content */
+ msgpack_unpacked_init(&result);
+ msgpack_unpack_next(&result, buf, size, &off);
+ map = result.data;
+
+ /* Create list of patterns */
+ flb_slist_create(&patterns);
+
+ /* sub keys */
+ for (i=index_start; count<list_size; i++) {
+ if (i>=list_size) {
+ i = 0;
+ }
+ flb_slist_add(&patterns, list[i]);
+ count++;
+ }
+
+ /* Create mp accessor */
+ mpa = flb_mp_accessor_create(&patterns);
+ TEST_CHECK(mpa != NULL);
+
+ /* Remove the entry that matches the pattern(s) */
+ ret = flb_mp_accessor_keys_remove(mpa, &map, (void *) &out_buf, &out_size);
+ TEST_CHECK(ret == FLB_TRUE);
+
+ printf("\n=== ORIGINAL ===\n");
+ flb_pack_print(buf, size);
+ flb_free(buf);
+
+ printf("=== FINAL MAP ===\n");
+ if (ret == FLB_TRUE) {
+ flb_pack_print(out_buf, out_size);
+ }
+ msgpack_unpacked_destroy(&result);
+
+ off = 0;
+ msgpack_unpacked_init(&result_final);
+ msgpack_unpack_next(&result_final, out_buf, out_size, &off);
+ flb_msgpack_to_json(&final_json[0], sizeof(final_json), &result_final.data);
+
+ if (!TEST_CHECK(strstr(&final_json[0] ,"kubernetes") == NULL)) {
+ TEST_MSG("kubernetes field should be removed");
+ }
+
+ msgpack_unpacked_destroy(&result_final);
+
+ flb_free(out_buf);
+ flb_mp_accessor_destroy(mpa);
+ flb_slist_destroy(&patterns);
+}
+
+void test_keys_remove_subkey_keys()
+{
+ char *list[] = {"$kubernetes[2]['annotations']['fluentbit.io/tag']",
+ "$kubernetes[2]['a']",
+ "$kubernetes"};
+ char *list2[] = {"$kubernetes[2]['annotations']['fluentbit.io/tag']",
+ "$kubernetes",
+ "$kubernetes[2]['a']"};
+
+ int size = sizeof(list)/sizeof(char*);
+ int i;
+
+ for (i=0; i<size; i++) {
+ remove_subkey_keys(list, size, i);
+ }
+ for (i=0; i<size; i++) {
+ remove_subkey_keys(list2, size, i);
+ }
+}
+
+TEST_LIST = {
+ {"count" , test_count},
+ {"map_header" , test_map_header},
+ {"accessor_keys_remove" , test_accessor_keys_remove},
+ {"accessor_keys_remove_subkey_key" , test_keys_remove_subkey_key},
+ {"accessor_keys_remove_subkey_keys" , test_keys_remove_subkey_keys},
+ { 0 }
+};
diff --git a/fluent-bit/tests/internal/multiline.c b/fluent-bit/tests/internal/multiline.c
new file mode 100644
index 00000000..e175e117
--- /dev/null
+++ b/fluent-bit/tests/internal/multiline.c
@@ -0,0 +1,1478 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+#include <fluent-bit/flb_info.h>
+#include <fluent-bit/flb_pack.h>
+#include <fluent-bit/flb_parser.h>
+#include <fluent-bit/flb_mem.h>
+#include <fluent-bit/multiline/flb_ml.h>
+#include <fluent-bit/multiline/flb_ml_rule.h>
+#include <fluent-bit/multiline/flb_ml_parser.h>
+
+#include "flb_tests_internal.h"
+
+struct record_check {
+ char *buf;
+};
+
+struct expected_result {
+ int current_record;
+ char *key;
+ struct record_check *out_records;
+};
+
+/* Docker */
+struct record_check docker_input[] = {
+ {"{\"log\": \"aa\\n\", \"stream\": \"stdout\", \"time\": \"2021-02-01T16:45:03.01231z\"}"},
+ {"{\"log\": \"aa\\n\", \"stream\": \"stderr\", \"time\": \"2021-02-01T16:45:03.01231z\"}"},
+ {"{\"log\": \"bb\", \"stream\": \"stdout\", \"time\": \"2021-02-01T16:45:03.01232z\"}"},
+ {"{\"log\": \"cc\n\", \"stream\": \"stdout\", \"time\": \"2021-02-01T16:45:03.01233z\"}"},
+ {"{\"log\": \"dd\", \"stream\": \"stderr\", \"time\": \"2021-02-01T16:45:03.01233z\"}"},
+ {"single line to force pending flush of the previous line"},
+ {"{\"log\": \"ee\\n\", \"stream\": \"stderr\", \"time\": \"2021-02-01T16:45:03.01234z\"}"},
+};
+
+struct record_check docker_output[] = {
+ {"aa\n"},
+ {"aa\n"},
+ {"bbcc\n"},
+ {"dd"},
+ {"single line to force pending flush of the previous line"},
+ {"ee\n"},
+};
+
+/* CRI */
+struct record_check cri_input[] = {
+ {"2019-05-07T18:57:50.904275087+00:00 stdout P 1a. some "},
+ {"2019-05-07T18:57:51.904275088+00:00 stdout P multiline "},
+ {"2019-05-07T18:57:52.904275089+00:00 stdout F log"},
+ {"2019-05-07T18:57:50.904275087+00:00 stderr P 1b. some "},
+ {"2019-05-07T18:57:51.904275088+00:00 stderr P multiline "},
+ {"2019-05-07T18:57:52.904275089+00:00 stderr F log"},
+ {"2019-05-07T18:57:53.904275090+00:00 stdout P 2a. another "},
+ {"2019-05-07T18:57:54.904275091+00:00 stdout P multiline "},
+ {"2019-05-07T18:57:55.904275092+00:00 stdout F log"},
+ {"2019-05-07T18:57:53.904275090+00:00 stderr P 2b. another "},
+ {"2019-05-07T18:57:54.904275091+00:00 stderr P multiline "},
+ {"2019-05-07T18:57:55.904275092+00:00 stderr F log"},
+ {"2019-05-07T18:57:56.904275093+00:00 stdout F 3a. non multiline 1"},
+ {"2019-05-07T18:57:57.904275094+00:00 stdout F 4a. non multiline 2"},
+ {"2019-05-07T18:57:56.904275093+00:00 stderr F 3b. non multiline 1"},
+ {"2019-05-07T18:57:57.904275094+00:00 stderr F 4b. non multiline 2"}
+};
+
+struct record_check cri_output[] = {
+ {"1a. some multiline log"},
+ {"1b. some multiline log"},
+ {"2a. another multiline log"},
+ {"2b. another multiline log"},
+ {"3a. non multiline 1"},
+ {"4a. non multiline 2"},
+ {"3b. non multiline 1"},
+ {"4b. non multiline 2"}
+};
+
+/* ENDSWITH */
+struct record_check endswith_input[] = {
+ {"1a. some multiline log \\"},
+ {"1b. some multiline log"},
+ {"2a. another multiline log\\"},
+ {"2b. another multiline log"},
+ {"3a. non multiline 1"},
+ {"4a. non multiline 2"}
+};
+
+struct record_check endswith_output[] = {
+ {"1a. some multiline log \\\n1b. some multiline log\n"},
+ {"2a. another multiline log\\\n2b. another multiline log\n"},
+ {"3a. non multiline 1\n"},
+ {"4a. non multiline 2\n"}
+};
+
+/* Mixed lines of Docker and CRI logs in different streams (stdout/stderr) */
+struct record_check container_mix_input[] = {
+ {"{\"log\": \"a1\\n\", \"stream\": \"stdout\", \"time\": \"2021-02-01T16:45:03.01231z\"}"},
+ {"{\"log\": \"a2\\n\", \"stream\": \"stderr\", \"time\": \"2021-02-01T16:45:03.01231z\"}"},
+ {"{\"log\": \"bb\", \"stream\": \"stdout\", \"time\": \"2021-02-01T16:45:03.01232z\"}"},
+ {"{\"log\": \"cc\", \"stream\": \"stdout\", \"time\": \"2021-02-01T16:45:03.01233z\"}"},
+ {"{\"log\": \"dd\", \"stream\": \"stderr\", \"time\": \"2021-02-01T16:45:03.01232z\"}"},
+ {"{\"log\": \"ee\n\", \"stream\": \"stderr\", \"time\": \"2021-02-01T16:45:03.01233z\"}"},
+ {"2019-05-07T18:57:52.904275089+00:00 stdout F single full"},
+ {"2019-05-07T18:57:50.904275087+00:00 stdout P 1a. some "},
+ {"2019-05-07T18:57:51.904275088+00:00 stdout P multiline "},
+ {"2019-05-07T18:57:52.904275089+00:00 stdout F log"},
+ {"2019-05-07T18:57:50.904275087+00:00 stderr P 1b. some "},
+ {"2019-05-07T18:57:51.904275088+00:00 stderr P multiline "},
+ {"2019-05-07T18:57:52.904275089+00:00 stderr F log"},
+ {"{\"log\": \"dd-out\\n\", \"stream\": \"stdout\", \"time\": \"2021-02-01T16:45:03.01234z\"}"},
+ {"{\"log\": \"dd-err\\n\", \"stream\": \"stderr\", \"time\": \"2021-02-01T16:45:03.01234z\"}"},
+};
+
+struct record_check container_mix_output[] = {
+ {"a1\n"},
+ {"a2\n"},
+ {"ddee\n"},
+ {"bbcc"},
+ {"single full"},
+ {"1a. some multiline log"},
+ {"1b. some multiline log"},
+ {"dd-out\n"},
+ {"dd-err\n"},
+};
+
+/* Java stacktrace detection */
+struct record_check java_input[] = {
+ {"Exception in thread \"main\" java.lang.IllegalStateException: ..null property\n"},
+ {" at com.example.myproject.Author.getBookIds(xx.java:38)\n"},
+ {" at com.example.myproject.Bootstrap.main(Bootstrap.java:14)\n"},
+ {"Caused by: java.lang.NullPointerException\n"},
+ {" at com.example.myproject.Book.getId(Book.java:22)\n"},
+ {" at com.example.myproject.Author.getBookIds(Author.java:35)\n"},
+ {" ... 1 more\n"},
+ {"single line\n"}
+};
+
+struct record_check java_output[] = {
+ {
+ "Exception in thread \"main\" java.lang.IllegalStateException: ..null property\n"
+ " at com.example.myproject.Author.getBookIds(xx.java:38)\n"
+ " at com.example.myproject.Bootstrap.main(Bootstrap.java:14)\n"
+ "Caused by: java.lang.NullPointerException\n"
+ " at com.example.myproject.Book.getId(Book.java:22)\n"
+ " at com.example.myproject.Author.getBookIds(Author.java:35)\n"
+ " ... 1 more\n"
+ },
+ {
+ "single line\n"
+ }
+};
+
+struct record_check ruby_input[] = {
+ {"/app/config/routes.rb:6:in `/': divided by 0 (ZeroDivisionError)"},
+ {" from /app/config/routes.rb:6:in `block in <main>'"},
+ {" from /var/lib/gems/3.0.0/gems/actionpack-7.0.4/lib/action_dispatch/routing/route_set.rb:428:in `instance_exec'"},
+ {" from /var/lib/gems/3.0.0/gems/actionpack-7.0.4/lib/action_dispatch/routing/route_set.rb:428:in `eval_block'"},
+ {" from /var/lib/gems/3.0.0/gems/actionpack-7.0.4/lib/action_dispatch/routing/route_set.rb:410:in `draw'"},
+ {" from /app/config/routes.rb:1:in `<main>'"},
+ {"hello world, not multiline\n"}
+};
+
+struct record_check ruby_output[] = {
+ {
+ "/app/config/routes.rb:6:in `/': divided by 0 (ZeroDivisionError)\n"
+ " from /app/config/routes.rb:6:in `block in <main>'\n"
+ " from /var/lib/gems/3.0.0/gems/actionpack-7.0.4/lib/action_dispatch/routing/route_set.rb:428:in `instance_exec'\n"
+ " from /var/lib/gems/3.0.0/gems/actionpack-7.0.4/lib/action_dispatch/routing/route_set.rb:428:in `eval_block'\n"
+ " from /var/lib/gems/3.0.0/gems/actionpack-7.0.4/lib/action_dispatch/routing/route_set.rb:410:in `draw'\n"
+ " from /app/config/routes.rb:1:in `<main>'\n"
+ },
+ {"hello world, not multiline\n"}
+};
+
+/* Python stacktrace detection */
+struct record_check python_input[] = {
+ {"Traceback (most recent call last):\n"},
+ {" File \"/base/data/home/runtimes/python27/python27_lib/versions/third_party/webapp2-2.5.2/webapp2.py\", line 1535, in __call__\n"},
+ {" rv = self.handle_exception(request, response, e)\n"},
+ {" File \"/base/data/home/apps/s~nearfieldspy/1.378705245900539993/nearfieldspy.py\", line 17, in start\n"},
+ {" return get()\n"},
+ {" File \"/base/data/home/apps/s~nearfieldspy/1.378705245900539993/nearfieldspy.py\", line 5, in get\n"},
+ {" raise Exception('spam', 'eggs')\n"},
+ {"Exception: ('spam', 'eggs')\n"},
+ {"hello world, not multiline\n"}
+};
+
+struct record_check python_output[] = {
+ {
+ "Traceback (most recent call last):\n"
+ " File \"/base/data/home/runtimes/python27/python27_lib/versions/third_party/webapp2-2.5.2/webapp2.py\", line 1535, in __call__\n"
+ " rv = self.handle_exception(request, response, e)\n"
+ " File \"/base/data/home/apps/s~nearfieldspy/1.378705245900539993/nearfieldspy.py\", line 17, in start\n"
+ " return get()\n"
+ " File \"/base/data/home/apps/s~nearfieldspy/1.378705245900539993/nearfieldspy.py\", line 5, in get\n"
+ " raise Exception('spam', 'eggs')\n"
+ "Exception: ('spam', 'eggs')\n"
+ },
+ {"hello world, not multiline\n"}
+};
+
+/* Custom example for Elasticsearch stacktrace */
+struct record_check elastic_input[] = {
+ {"[some weird test] IndexNotFoundException[no such index]\n"},
+ {" at org.elasticsearch.cluster.metadata.IndexNameExpressionResolver....\n"},
+ {" at org.elasticsearch.cluster.metadata.IndexNameExpressionResolver.java:133)\n"},
+ {" at org.elasticsearch.action.admin.indices.delete.java:75)\n"},
+ {"another separate log line\n"}
+};
+
+struct record_check elastic_output[] = {
+ {
+ "[some weird test] IndexNotFoundException[no such index]\n"
+ " at org.elasticsearch.cluster.metadata.IndexNameExpressionResolver....\n"
+ " at org.elasticsearch.cluster.metadata.IndexNameExpressionResolver.java:133)\n"
+ " at org.elasticsearch.action.admin.indices.delete.java:75)\n"
+ },
+ {
+ "another separate log line\n"
+ }
+};
+
+/* Go */
+struct record_check go_input[] = {
+ {"panic: my panic\n"},
+ {"\n"},
+ {"goroutine 4 [running]:\n"},
+ {"panic(0x45cb40, 0x47ad70)\n"},
+ {" /usr/local/go/src/runtime/panic.go:542 +0x46c fp=0xc42003f7b8 sp=0xc42003f710 pc=0x422f7c\n"},
+ {"main.main.func1(0xc420024120)\n"},
+ {" foo.go:6 +0x39 fp=0xc42003f7d8 sp=0xc42003f7b8 pc=0x451339\n"},
+ {"runtime.goexit()\n"},
+ {" /usr/local/go/src/runtime/asm_amd64.s:2337 +0x1 fp=0xc42003f7e0 sp=0xc42003f7d8 pc=0x44b4d1\n"},
+ {"created by main.main\n"},
+ {" foo.go:5 +0x58\n"},
+ {"\n"},
+ {"goroutine 1 [chan receive]:\n"},
+ {"runtime.gopark(0x4739b8, 0xc420024178, 0x46fcd7, 0xc, 0xc420028e17, 0x3)\n"},
+ {" /usr/local/go/src/runtime/proc.go:280 +0x12c fp=0xc420053e30 sp=0xc420053e00 pc=0x42503c\n"},
+ {"runtime.goparkunlock(0xc420024178, 0x46fcd7, 0xc, 0x1000f010040c217, 0x3)\n"},
+ {" /usr/local/go/src/runtime/proc.go:286 +0x5e fp=0xc420053e70 sp=0xc420053e30 pc=0x42512e\n"},
+ {"runtime.chanrecv(0xc420024120, 0x0, 0xc420053f01, 0x4512d8)\n"},
+ {" /usr/local/go/src/runtime/chan.go:506 +0x304 fp=0xc420053f20 sp=0xc420053e70 pc=0x4046b4\n"},
+ {"runtime.chanrecv1(0xc420024120, 0x0)\n"},
+ {" /usr/local/go/src/runtime/chan.go:388 +0x2b fp=0xc420053f50 sp=0xc420053f20 pc=0x40439b\n"},
+ {"main.main()\n"},
+ {" foo.go:9 +0x6f fp=0xc420053f80 sp=0xc420053f50 pc=0x4512ef\n"},
+ {"runtime.main()\n"},
+ {" /usr/local/go/src/runtime/proc.go:185 +0x20d fp=0xc420053fe0 sp=0xc420053f80 pc=0x424bad\n"},
+ {"runtime.goexit()\n"},
+ {" /usr/local/go/src/runtime/asm_amd64.s:2337 +0x1 fp=0xc420053fe8 sp=0xc420053fe0 pc=0x44b4d1\n"},
+ {"\n"},
+ {"goroutine 2 [force gc (idle)]:\n"},
+ {"runtime.gopark(0x4739b8, 0x4ad720, 0x47001e, 0xf, 0x14, 0x1)\n"},
+ {" /usr/local/go/src/runtime/proc.go:280 +0x12c fp=0xc42003e768 sp=0xc42003e738 pc=0x42503c\n"},
+ {"runtime.goparkunlock(0x4ad720, 0x47001e, 0xf, 0xc420000114, 0x1)\n"},
+ {" /usr/local/go/src/runtime/proc.go:286 +0x5e fp=0xc42003e7a8 sp=0xc42003e768 pc=0x42512e\n"},
+ {"runtime.forcegchelper()\n"},
+ {" /usr/local/go/src/runtime/proc.go:238 +0xcc fp=0xc42003e7e0 sp=0xc42003e7a8 pc=0x424e5c\n"},
+ {"runtime.goexit()\n"},
+ {" /usr/local/go/src/runtime/asm_amd64.s:2337 +0x1 fp=0xc42003e7e8 sp=0xc42003e7e0 pc=0x44b4d1\n"},
+ {"created by runtime.init.4\n"},
+ {" /usr/local/go/src/runtime/proc.go:227 +0x35\n"},
+ {"\n"},
+ {"goroutine 3 [GC sweep wait]:\n"},
+ {"runtime.gopark(0x4739b8, 0x4ad7e0, 0x46fdd2, 0xd, 0x419914, 0x1)\n"},
+ {" /usr/local/go/src/runtime/proc.go:280 +0x12c fp=0xc42003ef60 sp=0xc42003ef30 pc=0x42503c\n"},
+ {"runtime.goparkunlock(0x4ad7e0, 0x46fdd2, 0xd, 0x14, 0x1)\n"},
+ {" /usr/local/go/src/runtime/proc.go:286 +0x5e fp=0xc42003efa0 sp=0xc42003ef60 pc=0x42512e\n"},
+ {"runtime.bgsweep(0xc42001e150)\n"},
+ {" /usr/local/go/src/runtime/mgcsweep.go:52 +0xa3 fp=0xc42003efd8 sp=0xc42003efa0 pc=0x419973\n"},
+ {"runtime.goexit()\n"},
+ {" /usr/local/go/src/runtime/asm_amd64.s:2337 +0x1 fp=0xc42003efe0 sp=0xc42003efd8 pc=0x44b4d1\n"},
+ {"created by runtime.gcenable\n"},
+ {" /usr/local/go/src/runtime/mgc.go:216 +0x58\n"},
+ {"one more line, no multiline"}
+};
+
+struct record_check go_output[] = {
+ {
+ "panic: my panic\n"
+ "\n"
+ "goroutine 4 [running]:\n"
+ "panic(0x45cb40, 0x47ad70)\n"
+ " /usr/local/go/src/runtime/panic.go:542 +0x46c fp=0xc42003f7b8 sp=0xc42003f710 pc=0x422f7c\n"
+ "main.main.func1(0xc420024120)\n"
+ " foo.go:6 +0x39 fp=0xc42003f7d8 sp=0xc42003f7b8 pc=0x451339\n"
+ "runtime.goexit()\n"
+ " /usr/local/go/src/runtime/asm_amd64.s:2337 +0x1 fp=0xc42003f7e0 sp=0xc42003f7d8 pc=0x44b4d1\n"
+ "created by main.main\n"
+ " foo.go:5 +0x58\n"
+ "\n"
+ "goroutine 1 [chan receive]:\n"
+ "runtime.gopark(0x4739b8, 0xc420024178, 0x46fcd7, 0xc, 0xc420028e17, 0x3)\n"
+ " /usr/local/go/src/runtime/proc.go:280 +0x12c fp=0xc420053e30 sp=0xc420053e00 pc=0x42503c\n"
+ "runtime.goparkunlock(0xc420024178, 0x46fcd7, 0xc, 0x1000f010040c217, 0x3)\n"
+ " /usr/local/go/src/runtime/proc.go:286 +0x5e fp=0xc420053e70 sp=0xc420053e30 pc=0x42512e\n"
+ "runtime.chanrecv(0xc420024120, 0x0, 0xc420053f01, 0x4512d8)\n"
+ " /usr/local/go/src/runtime/chan.go:506 +0x304 fp=0xc420053f20 sp=0xc420053e70 pc=0x4046b4\n"
+ "runtime.chanrecv1(0xc420024120, 0x0)\n"
+ " /usr/local/go/src/runtime/chan.go:388 +0x2b fp=0xc420053f50 sp=0xc420053f20 pc=0x40439b\n"
+ "main.main()\n"
+ " foo.go:9 +0x6f fp=0xc420053f80 sp=0xc420053f50 pc=0x4512ef\n"
+ "runtime.main()\n"
+ " /usr/local/go/src/runtime/proc.go:185 +0x20d fp=0xc420053fe0 sp=0xc420053f80 pc=0x424bad\n"
+ "runtime.goexit()\n"
+ " /usr/local/go/src/runtime/asm_amd64.s:2337 +0x1 fp=0xc420053fe8 sp=0xc420053fe0 pc=0x44b4d1\n"
+ "\n"
+ "goroutine 2 [force gc (idle)]:\n"
+ "runtime.gopark(0x4739b8, 0x4ad720, 0x47001e, 0xf, 0x14, 0x1)\n"
+ " /usr/local/go/src/runtime/proc.go:280 +0x12c fp=0xc42003e768 sp=0xc42003e738 pc=0x42503c\n"
+ "runtime.goparkunlock(0x4ad720, 0x47001e, 0xf, 0xc420000114, 0x1)\n"
+ " /usr/local/go/src/runtime/proc.go:286 +0x5e fp=0xc42003e7a8 sp=0xc42003e768 pc=0x42512e\n"
+ "runtime.forcegchelper()\n"
+ " /usr/local/go/src/runtime/proc.go:238 +0xcc fp=0xc42003e7e0 sp=0xc42003e7a8 pc=0x424e5c\n"
+ "runtime.goexit()\n"
+ " /usr/local/go/src/runtime/asm_amd64.s:2337 +0x1 fp=0xc42003e7e8 sp=0xc42003e7e0 pc=0x44b4d1\n"
+ "created by runtime.init.4\n"
+ " /usr/local/go/src/runtime/proc.go:227 +0x35\n"
+ "\n"
+ "goroutine 3 [GC sweep wait]:\n"
+ "runtime.gopark(0x4739b8, 0x4ad7e0, 0x46fdd2, 0xd, 0x419914, 0x1)\n"
+ " /usr/local/go/src/runtime/proc.go:280 +0x12c fp=0xc42003ef60 sp=0xc42003ef30 pc=0x42503c\n"
+ "runtime.goparkunlock(0x4ad7e0, 0x46fdd2, 0xd, 0x14, 0x1)\n"
+ " /usr/local/go/src/runtime/proc.go:286 +0x5e fp=0xc42003efa0 sp=0xc42003ef60 pc=0x42512e\n"
+ "runtime.bgsweep(0xc42001e150)\n"
+ " /usr/local/go/src/runtime/mgcsweep.go:52 +0xa3 fp=0xc42003efd8 sp=0xc42003efa0 pc=0x419973\n"
+ "runtime.goexit()\n"
+ " /usr/local/go/src/runtime/asm_amd64.s:2337 +0x1 fp=0xc42003efe0 sp=0xc42003efd8 pc=0x44b4d1\n"
+ "created by runtime.gcenable\n"
+ " /usr/local/go/src/runtime/mgc.go:216 +0x58\n"
+ },
+ {"one more line, no multiline\n"}
+};
+
+/*
+ * Issue 3817 (case: 1)
+ * --------------------
+ * Source CRI messages (need first CRI multiline parsing) + a custom multiline
+ * parser.
+ *
+ * - https://github.com/fluent/fluent-bit/issues/3817
+ *
+ * The 'case 1' represents the problems of identifying two consecutive multiline
+ * messages within the same stream.
+ */
+struct record_check issue_3817_1_input[] = {
+ {"2021-05-17T17:35:01.184675702Z stdout F [DEBUG] 1 start multiline - "},
+ {"2021-05-17T17:35:01.184747208Z stdout F 1 cont A"},
+ {"2021-05-17T17:35:01.184675702Z stdout F [DEBUG] 2 start multiline - "},
+ {"2021-05-17T17:35:01.184747208Z stdout F 2 cont B"},
+ {"another isolated line"}
+};
+
+struct record_check issue_3817_1_output[] = {
+ {
+ "[DEBUG] 1 start multiline - \n"
+ "1 cont A"
+ },
+
+ {
+ "[DEBUG] 2 start multiline - \n"
+ "2 cont B"
+ },
+
+ {
+ "another isolated line"
+ }
+};
+
+/*
+ * Flush callback is invoked every time a multiline stream has completed a multiline
+ * message or a message is not multiline.
+ */
+static int flush_callback(struct flb_ml_parser *parser,
+ struct flb_ml_stream *mst,
+ void *data, char *buf_data, size_t buf_size)
+{
+ int i;
+ int ret;
+ int len;
+ int found = FLB_FALSE;
+ size_t off = 0;
+ msgpack_unpacked result;
+ msgpack_object *map;
+ msgpack_object key;
+ msgpack_object val;
+ struct flb_time tm;
+ struct expected_result *res = data;
+ struct record_check *exp;
+
+ fprintf(stdout, "\n%s----- MULTILINE FLUSH -----%s\n", ANSI_YELLOW, ANSI_RESET);
+
+ /* Print incoming flush buffer */
+ flb_pack_print(buf_data, buf_size);
+
+ fprintf(stdout, "%s----------- EOF -----------%s\n",
+ ANSI_YELLOW, ANSI_RESET);
+
+ /* Validate content */
+ msgpack_unpacked_init(&result);
+ off = 0;
+ ret = msgpack_unpack_next(&result, buf_data, buf_size, &off);
+ TEST_CHECK(ret == MSGPACK_UNPACK_SUCCESS);
+
+ flb_time_pop_from_msgpack(&tm, &result, &map);
+
+ TEST_CHECK(flb_time_to_nanosec(&tm) != 0L);
+
+ exp = &res->out_records[res->current_record];
+ len = strlen(res->key);
+ for (i = 0; i < map->via.map.size; i++) {
+ key = map->via.map.ptr[i].key;
+ val = map->via.map.ptr[i].val;
+
+ if (key.via.str.size != len) {
+ continue;
+ }
+
+ if (strncmp(key.via.str.ptr, res->key, len) == 0) {
+ found = FLB_TRUE;
+ break;
+ }
+ }
+ TEST_CHECK(found == FLB_TRUE);
+
+ len = strlen(exp->buf);
+ TEST_CHECK(val.via.str.size == len);
+ if (val.via.str.size != len) {
+ printf("expected length: %i, received: %i\n", len, val.via.str.size);
+ printf("== received ==\n");
+ msgpack_object_print(stdout, val);
+ printf("\n\n");
+ printf("== expected ==\n%s\n", exp->buf);
+ exit(1);
+ }
+ TEST_CHECK(memcmp(val.via.str.ptr, exp->buf, len) == 0);
+ res->current_record++;
+
+ msgpack_unpacked_destroy(&result);
+ return 0;
+}
+
+static void test_parser_docker()
+{
+ int i;
+ int len;
+ int ret;
+ int entries;
+ uint64_t stream_id;
+ struct record_check *r;
+ struct flb_config *config;
+ struct flb_time tm;
+ struct flb_ml *ml;
+ struct flb_ml_parser_ins *mlp_i;
+ struct expected_result res = {0};
+
+ /* Expected results context */
+ res.key = "log";
+ res.out_records = docker_output;
+
+ /* Initialize environment */
+ config = flb_config_init();
+
+ /* Create docker multiline mode */
+ ml = flb_ml_create(config, "test-docker");
+ TEST_CHECK(ml != NULL);
+
+ /* Load instances of the parsers for current 'ml' context */
+ mlp_i = flb_ml_parser_instance_create(ml, "cri");
+ TEST_CHECK(mlp_i != NULL);
+
+ /* Generate an instance of multiline docker parser */
+ mlp_i = flb_ml_parser_instance_create(ml, "docker");
+ TEST_CHECK(mlp_i != NULL);
+
+ ret = flb_ml_stream_create(ml, "docker", -1, flush_callback, (void *) &res,
+ &stream_id);
+ TEST_CHECK(ret == 0);
+
+ entries = sizeof(docker_input) / sizeof(struct record_check);
+ for (i = 0; i < entries; i++) {
+ r = &docker_input[i];
+ len = strlen(r->buf);
+
+ flb_time_get(&tm);
+
+ /* Package as msgpack */
+ flb_ml_append_text(ml, stream_id, &tm, r->buf, len);
+ }
+
+ if (ml) {
+ flb_ml_destroy(ml);
+ }
+
+ flb_config_exit(config);
+}
+
+static void test_parser_cri()
+{
+ int i;
+ int len;
+ int ret;
+ int entries;
+ uint64_t stream_id;
+ struct record_check *r;
+ struct flb_config *config;
+ struct flb_time tm;
+ struct flb_ml *ml;
+ struct flb_ml_parser_ins *mlp_i;
+ struct expected_result res = {0};
+
+ /* Expected results context */
+ res.key = "log";
+ res.out_records = cri_output;
+
+ /* Initialize environment */
+ config = flb_config_init();
+
+ /* Create docker multiline mode */
+ ml = flb_ml_create(config, "cri-test");
+ TEST_CHECK(ml != NULL);
+
+ /* Generate an instance of multiline docker parser */
+ mlp_i = flb_ml_parser_instance_create(ml, "docker");
+ TEST_CHECK(mlp_i != NULL);
+
+ /* Load instances of the parsers for current 'ml' context */
+ mlp_i = flb_ml_parser_instance_create(ml, "cri");
+ TEST_CHECK(mlp_i != NULL);
+
+ ret = flb_ml_stream_create(ml, "cri", -1, flush_callback, (void *) &res,
+ &stream_id);
+ TEST_CHECK(ret == 0);
+
+ entries = sizeof(cri_input) / sizeof(struct record_check);
+ for (i = 0; i < entries; i++) {
+ r = &cri_input[i];
+ len = strlen(r->buf);
+ flb_time_get(&tm);
+
+ /* Package as msgpack */
+ flb_ml_append_text(ml, stream_id, &tm, r->buf, len);
+ }
+
+ if (ml) {
+ flb_ml_destroy(ml);
+ }
+
+ flb_config_exit(config);
+}
+
+static void test_container_mix()
+{
+ int i;
+ int len;
+ int ret;
+ int entries;
+ uint64_t stream_id;
+ struct record_check *r;
+ struct flb_config *config;
+ struct flb_time tm;
+ struct flb_ml *ml;
+ struct flb_ml_parser_ins *mlp_i;
+ struct expected_result res = {0};
+
+ /* Expected results context */
+ res.key = "log";
+ res.out_records = container_mix_output;
+
+ /* Initialize environment */
+ config = flb_config_init();
+
+ /* Create docker multiline mode */
+ ml = flb_ml_create(config, "container-mix-test");
+ TEST_CHECK(ml != NULL);
+
+ /* Generate an instance of multiline docker parser */
+ mlp_i = flb_ml_parser_instance_create(ml, "docker");
+ TEST_CHECK(mlp_i != NULL);
+
+ /* Load instances of the parsers for current 'ml' context */
+ mlp_i = flb_ml_parser_instance_create(ml, "cri");
+ TEST_CHECK(mlp_i != NULL);
+
+ ret = flb_ml_stream_create(ml, "container-mix", -1, flush_callback, (void *) &res,
+ &stream_id);
+ TEST_CHECK(ret == 0);
+
+ entries = sizeof(container_mix_input) / sizeof(struct record_check);
+ for (i = 0; i < entries; i++) {
+ r = &container_mix_input[i];
+ len = strlen(r->buf);
+ flb_time_get(&tm);
+
+ /* Package as msgpack */
+ flb_ml_append_text(ml, stream_id, &tm, r->buf, len);
+ }
+
+ if (ml) {
+ flb_ml_destroy(ml);
+ }
+
+ flb_config_exit(config);
+}
+
+static void test_parser_java()
+{
+ int i;
+ int len;
+ int ret;
+ int entries;
+ uint64_t stream_id;
+ struct record_check *r;
+ struct flb_config *config;
+ struct flb_time tm;
+ struct flb_ml *ml;
+ struct flb_ml_parser_ins *mlp_i;
+ struct expected_result res = {0};
+ msgpack_packer mp_pck;
+ msgpack_sbuffer mp_sbuf;
+
+ /* Expected results context */
+ res.key = "log";
+ res.out_records = java_output;
+
+ /* Initialize environment */
+ config = flb_config_init();
+
+ /* Create docker multiline mode */
+ ml = flb_ml_create(config, "java-test");
+ TEST_CHECK(ml != NULL);
+
+ /* Generate an instance of multiline java parser */
+ mlp_i = flb_ml_parser_instance_create(ml, "java");
+ TEST_CHECK(mlp_i != NULL);
+
+ flb_ml_parser_instance_set(mlp_i, "key_content", "log");
+
+ ret = flb_ml_stream_create(ml, "java", -1, flush_callback, (void *) &res,
+ &stream_id);
+ TEST_CHECK(ret == 0);
+
+ /* initialize buffers */
+ msgpack_sbuffer_init(&mp_sbuf);
+ msgpack_packer_init(&mp_pck, &mp_sbuf, msgpack_sbuffer_write);
+
+ size_t off = 0;
+ msgpack_unpacked result;
+ msgpack_object root;
+ msgpack_object *map;
+
+ entries = sizeof(java_input) / sizeof(struct record_check);
+ for (i = 0; i < entries; i++) {
+ r = &java_input[i];
+ len = strlen(r->buf);
+
+ /* Package as msgpack */
+ flb_time_get(&tm);
+
+ /* initialize buffers */
+ msgpack_sbuffer_init(&mp_sbuf);
+ msgpack_packer_init(&mp_pck, &mp_sbuf, msgpack_sbuffer_write);
+
+ msgpack_pack_array(&mp_pck, 2);
+ flb_time_append_to_msgpack(&tm, &mp_pck, 0);
+
+ msgpack_pack_map(&mp_pck, 1);
+ msgpack_pack_str(&mp_pck, 3);
+ msgpack_pack_str_body(&mp_pck, "log", 3);
+ msgpack_pack_str(&mp_pck, len);
+ msgpack_pack_str_body(&mp_pck, r->buf, len);
+
+ /* Unpack and lookup the content map */
+ msgpack_unpacked_init(&result);
+ off = 0;
+ ret = msgpack_unpack_next(&result, mp_sbuf.data, mp_sbuf.size, &off);
+
+ flb_pack_print(mp_sbuf.data, mp_sbuf.size);
+
+ root = result.data;
+ map = &root.via.array.ptr[1];
+
+ /* Package as msgpack */
+ ret = flb_ml_append_object(ml, stream_id, &tm, NULL, map);
+
+ msgpack_unpacked_destroy(&result);
+ msgpack_sbuffer_destroy(&mp_sbuf);
+ }
+
+ if (ml) {
+ flb_ml_destroy(ml);
+ }
+
+ flb_config_exit(config);
+}
+
+static void test_parser_python()
+{
+ int i;
+ int len;
+ int ret;
+ int entries;
+ uint64_t stream_id;
+ msgpack_packer mp_pck;
+ msgpack_sbuffer mp_sbuf;
+ struct record_check *r;
+ struct flb_config *config;
+ struct flb_time tm;
+ struct flb_ml *ml;
+ struct flb_ml_parser_ins *mlp_i;
+ struct expected_result res = {0};
+
+ /* Expected results context */
+ res.key = "log";
+ res.out_records = python_output;
+
+ /* initialize buffers */
+ msgpack_sbuffer_init(&mp_sbuf);
+ msgpack_packer_init(&mp_pck, &mp_sbuf, msgpack_sbuffer_write);
+
+ /* Initialize environment */
+ config = flb_config_init();
+
+ /* Create docker multiline mode */
+ ml = flb_ml_create(config, "python-test");
+ TEST_CHECK(ml != NULL);
+
+ /* Generate an instance of multiline python parser */
+ mlp_i = flb_ml_parser_instance_create(ml, "python");
+ TEST_CHECK(mlp_i != NULL);
+
+ ret = flb_ml_stream_create(ml, "python", -1, flush_callback, (void *) &res,
+ &stream_id);
+ TEST_CHECK(ret == 0);
+
+ flb_time_get(&tm);
+
+ printf("\n");
+ entries = sizeof(python_input) / sizeof(struct record_check);
+ for (i = 0; i < entries; i++) {
+ r = &python_input[i];
+ len = strlen(r->buf);
+
+ /* Package as msgpack */
+ flb_time_get(&tm);
+ flb_ml_append_text(ml, stream_id, &tm, r->buf, len);
+ }
+
+ if (ml) {
+ flb_ml_destroy(ml);
+ }
+
+ flb_config_exit(config);
+}
+
+static void test_parser_ruby()
+{
+ int i;
+ int len;
+ int ret;
+ int entries;
+ uint64_t stream_id;
+ msgpack_packer mp_pck;
+ msgpack_sbuffer mp_sbuf;
+ struct record_check *r;
+ struct flb_config *config;
+ struct flb_time tm;
+ struct flb_ml *ml;
+ struct flb_ml_parser_ins *mlp_i;
+ struct expected_result res = {0};
+
+ /* Expected results context */
+ res.key = "log";
+ res.out_records = ruby_output;
+
+ /* initialize buffers */
+ msgpack_sbuffer_init(&mp_sbuf);
+ msgpack_packer_init(&mp_pck, &mp_sbuf, msgpack_sbuffer_write);
+
+ /* Initialize environment */
+ config = flb_config_init();
+
+ /* Create docker multiline mode */
+ ml = flb_ml_create(config, "ruby-test");
+ TEST_CHECK(ml != NULL);
+
+ /* Generate an instance of multiline ruby parser */
+ mlp_i = flb_ml_parser_instance_create(ml, "ruby");
+ TEST_CHECK(mlp_i != NULL);
+
+ ret = flb_ml_stream_create(ml, "ruby", -1, flush_callback, (void *) &res,
+ &stream_id);
+ TEST_CHECK(ret == 0);
+
+ flb_time_get(&tm);
+
+ printf("\n");
+ entries = sizeof(ruby_input) / sizeof(struct record_check);
+ for (i = 0; i < entries; i++) {
+ r = &ruby_input[i];
+ len = strlen(r->buf);
+
+ /* Package as msgpack */
+ flb_time_get(&tm);
+ flb_ml_append_text(ml, stream_id, &tm, r->buf, len);
+ }
+
+ if (ml) {
+ flb_ml_destroy(ml);
+ }
+
+ flb_config_exit(config);
+}
+
+static void test_issue_4949()
+{
+ int i;
+ int len;
+ int ret;
+ int entries;
+ uint64_t stream_id;
+ msgpack_packer mp_pck;
+ msgpack_sbuffer mp_sbuf;
+ struct record_check *r;
+ struct flb_config *config;
+ struct flb_time tm;
+ struct flb_ml *ml;
+ struct flb_ml_parser_ins *mlp_i;
+ struct expected_result res = {0};
+
+ /* Expected results context */
+ res.key = "log";
+ res.out_records = python_output;
+
+ /* initialize buffers */
+ msgpack_sbuffer_init(&mp_sbuf);
+ msgpack_packer_init(&mp_pck, &mp_sbuf, msgpack_sbuffer_write);
+
+ /* Initialize environment */
+ config = flb_config_init();
+
+ /* Create docker multiline mode */
+ ml = flb_ml_create(config, "python-test");
+ TEST_CHECK(ml != NULL);
+
+ /* Generate an instance of multiline python parser */
+ mlp_i = flb_ml_parser_instance_create(ml, "python");
+ TEST_CHECK(mlp_i != NULL);
+
+ ret = flb_ml_stream_create(ml, "python", -1, flush_callback, (void *) &res,
+ &stream_id);
+ TEST_CHECK(ret == 0);
+
+ /* Generate an instance of multiline java parser */
+ mlp_i = flb_ml_parser_instance_create(ml, "java");
+ TEST_CHECK(mlp_i != NULL);
+
+ ret = flb_ml_stream_create(ml, "java", -1, flush_callback, (void *) &res,
+ &stream_id);
+ TEST_CHECK(ret == 0);
+
+ flb_time_get(&tm);
+
+ printf("\n");
+ entries = sizeof(python_input) / sizeof(struct record_check);
+ for (i = 0; i < entries; i++) {
+ r = &python_input[i];
+ len = strlen(r->buf);
+
+ /* Package as msgpack */
+ flb_time_get(&tm);
+ flb_ml_append_text(ml, stream_id, &tm, r->buf, len);
+ }
+
+ if (ml) {
+ flb_ml_destroy(ml);
+ }
+
+ flb_config_exit(config);
+}
+
+static void test_parser_elastic()
+{
+ int i;
+ int len;
+ int ret;
+ int entries;
+ size_t off = 0;
+ uint64_t stream_id;
+ msgpack_packer mp_pck;
+ msgpack_sbuffer mp_sbuf;
+ msgpack_unpacked result;
+ msgpack_object root;
+ msgpack_object *map;
+ struct record_check *r;
+ struct flb_config *config;
+ struct flb_time tm;
+ struct flb_ml *ml;
+ struct flb_ml_parser *mlp;
+ struct flb_ml_parser_ins *mlp_i;
+ struct expected_result res = {0};
+
+ /* Expected results context */
+ res.key = "log";
+ res.out_records = elastic_output;
+
+ /* Initialize environment */
+ config = flb_config_init();
+
+ ml = flb_ml_create(config, "test-elastic");
+ TEST_CHECK(ml != NULL);
+
+ mlp = flb_ml_parser_create(config,
+ "elastic", /* name */
+ FLB_ML_REGEX, /* type */
+ NULL, /* match_str */
+ FLB_FALSE, /* negate */
+ 1000, /* flush_ms */
+ "log", /* key_content */
+ NULL, /* key_pattern */
+ NULL, /* key_group */
+ NULL, /* parser ctx */
+ NULL); /* parser name */
+ TEST_CHECK(mlp != NULL);
+
+ mlp_i = flb_ml_parser_instance_create(ml, "elastic");
+ TEST_CHECK(mlp_i != NULL);
+
+ ret = flb_ml_rule_create(mlp, "start_state", "/^\\[/", "elastic_cont", NULL);
+ if (ret != 0) {
+ fprintf(stderr, "error creating rule 1");
+ }
+
+ ret = flb_ml_rule_create(mlp, "elastic_cont", "/^\\s+/", "elastic_cont", NULL);
+ if (ret != 0) {
+ fprintf(stderr, "error creating rule 2");
+ }
+
+ ret = flb_ml_stream_create(ml, "elastic", -1, flush_callback, (void *) &res,
+ &stream_id);
+ TEST_CHECK(ret == 0);
+
+ ret = flb_ml_parser_init(mlp);
+ if (ret != 0) {
+ fprintf(stderr, "error initializing multiline\n");
+ flb_ml_destroy(ml);
+ return;
+ }
+
+ printf("\n");
+ entries = sizeof(elastic_input) / sizeof(struct record_check);
+ for (i = 0; i < entries; i++) {
+ r = &elastic_input[i];
+ len = strlen(r->buf);
+
+ /* initialize buffers */
+ msgpack_sbuffer_init(&mp_sbuf);
+ msgpack_packer_init(&mp_pck, &mp_sbuf, msgpack_sbuffer_write);
+
+ /* Package raw text as a msgpack record */
+ msgpack_pack_array(&mp_pck, 2);
+
+ flb_time_get(&tm);
+ flb_time_append_to_msgpack(&tm, &mp_pck, 0);
+
+ msgpack_pack_map(&mp_pck, 1);
+ msgpack_pack_str(&mp_pck, 3);
+ msgpack_pack_str_body(&mp_pck, "log", 3);
+ msgpack_pack_str(&mp_pck, len);
+ msgpack_pack_str_body(&mp_pck, r->buf, len);
+
+ /* Unpack and lookup the content map */
+ msgpack_unpacked_init(&result);
+ off = 0;
+ ret = msgpack_unpack_next(&result, mp_sbuf.data, mp_sbuf.size, &off);
+ TEST_CHECK(ret == MSGPACK_UNPACK_SUCCESS);
+
+ root = result.data;
+ map = &root.via.array.ptr[1];
+
+ /* Package as msgpack */
+ ret = flb_ml_append_object(ml, stream_id, &tm, NULL, map);
+
+ msgpack_unpacked_destroy(&result);
+ msgpack_sbuffer_destroy(&mp_sbuf);
+ }
+
+ if (ml) {
+ flb_ml_destroy(ml);
+ }
+
+ flb_config_exit(config);
+}
+
+static void test_endswith()
+{
+ int i;
+ int len;
+ int ret;
+ int entries;
+ uint64_t stream_id = 0;
+ struct record_check *r;
+ struct flb_config *config;
+ struct flb_time tm;
+ struct flb_ml *ml;
+ struct flb_ml_parser *mlp;
+ struct flb_ml_parser_ins *mlp_i;
+ struct expected_result res = {0};
+
+ /* Expected results context */
+ res.key = "log";
+ res.out_records = endswith_output;
+
+ /* Initialize environment */
+ config = flb_config_init();
+
+ /* Create docker multiline mode */
+ ml = flb_ml_create(config, "raw-endswith");
+ TEST_CHECK(ml != NULL);
+
+ mlp = flb_ml_parser_create(config,
+ "endswith", /* name */
+ FLB_ML_ENDSWITH, /* type */
+ "\\", /* match_str */
+ FLB_TRUE, /* negate */
+ 1000, /* flush_ms */
+ NULL, /* key_content */
+ NULL, /* key_pattern */
+ NULL, /* key_group */
+ NULL, /* parser ctx */
+ NULL); /* parser name */
+ TEST_CHECK(mlp != NULL);
+
+ /* Generate an instance of 'endswith' custom parser parser */
+ mlp_i = flb_ml_parser_instance_create(ml, "endswith");
+ TEST_CHECK(mlp_i != NULL);
+
+ ret = flb_ml_stream_create(ml, "test", -1, flush_callback, (void *) &res, &stream_id);
+ TEST_CHECK(ret == 0);
+
+ entries = sizeof(endswith_input) / sizeof(struct record_check);
+ for (i = 0; i < entries; i++) {
+ r = &endswith_input[i];
+ len = strlen(r->buf);
+
+ /* Package as msgpack */
+ flb_time_get(&tm);
+ flb_ml_append_text(ml, stream_id, &tm, r->buf, len);
+ }
+
+ if (ml) {
+ flb_ml_destroy(ml);
+ }
+
+ flb_config_exit(config);
+}
+
+static void test_parser_go()
+{
+ int i;
+ int len;
+ int ret;
+ int entries;
+ uint64_t stream_id = 0;
+ struct record_check *r;
+ struct flb_config *config;
+ struct flb_time tm;
+ struct flb_ml *ml;
+ struct flb_ml_parser_ins *mlp_i;
+ struct expected_result res = {0};
+
+ /* Expected results context */
+ res.key = "log";
+ res.out_records = go_output;
+
+ /* Initialize environment */
+ config = flb_config_init();
+
+ /* Create docker multiline mode */
+ ml = flb_ml_create(config, "go-test");
+ TEST_CHECK(ml != NULL);
+
+ /* Generate an instance of multiline java parser */
+ mlp_i = flb_ml_parser_instance_create(ml, "go");
+ TEST_CHECK(mlp_i != NULL);
+
+ ret = flb_ml_stream_create(ml, "go", -1, flush_callback, (void *) &res, &stream_id);
+ TEST_CHECK(ret == 0);
+
+ entries = sizeof(go_input) / sizeof(struct record_check);
+ for (i = 0; i < entries; i++) {
+ r = &go_input[i];
+ len = strlen(r->buf);
+
+ /* Package as msgpack */
+ flb_time_get(&tm);
+ flb_ml_append_text(ml, stream_id, &tm, r->buf, len);
+ }
+
+ if (ml) {
+ flb_ml_destroy(ml);
+ }
+
+ flb_config_exit(config);
+}
+
+static int flush_callback_to_buf(struct flb_ml_parser *parser,
+ struct flb_ml_stream *mst,
+ void *data, char *buf_data, size_t buf_size)
+{
+ msgpack_sbuffer *mp_sbuf = data;
+ msgpack_sbuffer_write(mp_sbuf, buf_data, buf_size);
+
+ return 0;
+}
+
+static void run_test(struct flb_config *config, char *test_name,
+ struct record_check *in, int in_len,
+ struct record_check *out, int out_len,
+ char *parser1, char *parser2)
+
+{
+ int i;
+ int ret;
+ int len;
+ size_t off = 0;
+ uint64_t stream1 = 0;
+ uint64_t stream2 = 0;
+ struct flb_ml *ml;
+ struct flb_ml_parser_ins *p1 = NULL;
+ struct record_check *r;
+ msgpack_sbuffer mp_sbuf1;
+ msgpack_packer mp_pck1;
+ msgpack_sbuffer mp_sbuf2;
+ msgpack_packer mp_pck2;
+ msgpack_object *map;
+ struct flb_time tm;
+ struct expected_result res = {0};
+ msgpack_unpacked result;
+
+ /* init buffers */
+ msgpack_sbuffer_init(&mp_sbuf1);
+ msgpack_packer_init(&mp_pck1, &mp_sbuf1, msgpack_sbuffer_write);
+ msgpack_sbuffer_init(&mp_sbuf2);
+ msgpack_packer_init(&mp_pck2, &mp_sbuf2, msgpack_sbuffer_write);
+
+ /* Create docker multiline mode */
+ ml = flb_ml_create(config, test_name);
+ TEST_CHECK(ml != NULL);
+
+ if (!parser1) {
+ fprintf(stderr, "run_test(): parser1 is NULL\n");
+ exit(1);
+ }
+
+ /* Parser 1 */
+ p1 = flb_ml_parser_instance_create(ml, parser1);
+ TEST_CHECK(p1 != NULL);
+
+
+ /* Stream 1: use parser name (test_name) to generate the stream id */
+ ret = flb_ml_stream_create(ml, test_name, -1,
+ flush_callback_to_buf,
+ (void *) &mp_sbuf1, &stream1);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest input records into parser 1 */
+ for (i = 0; i < in_len; i++) {
+ r = &in[i];
+ len = strlen(r->buf);
+
+ flb_time_get(&tm);
+
+ /* Package as msgpack */
+ flb_ml_append_text(ml, stream1, &tm, r->buf, len);
+ }
+
+ flb_ml_destroy(ml);
+ ml = flb_ml_create(config, test_name);
+
+ flb_ml_parser_instance_create(ml, parser2);
+
+ /*
+ * After flb_ml_append above(), mp_sbuf1 has been populated with the
+ * output results as structured messages. Now this data needs to be
+ * passed to the next parser.
+ */
+
+ /* Expected results context */
+ res.key = "log";
+ res.out_records = out;
+
+ /* Stream 2 */
+ ret = flb_ml_stream_create(ml, "filter_multiline", -1,
+ flush_callback,
+ (void *) &res, &stream2);
+
+ /* Ingest input records into parser 2 */
+ off = 0;
+ msgpack_unpacked_init(&result);
+ while (msgpack_unpack_next(&result, mp_sbuf1.data, mp_sbuf1.size, &off)) {
+ flb_time_pop_from_msgpack(&tm, &result, &map);
+
+ /* Package as msgpack */
+ ret = flb_ml_append_object(ml, stream2, &tm, NULL, map);
+ }
+ flb_ml_flush_pending_now(ml);
+
+ msgpack_unpacked_destroy(&result);
+ msgpack_sbuffer_destroy(&mp_sbuf1);
+ flb_ml_destroy(ml);
+}
+
+void test_issue_3817_1()
+{
+ int ret;
+ int in_len = sizeof(issue_3817_1_input) / sizeof(struct record_check);
+ int out_len = sizeof(issue_3817_1_output) / sizeof(struct record_check);
+ struct flb_config *config;
+ struct flb_ml_parser *mlp;
+
+ /*
+ * Parser definition for a file:
+ *
+ * [MULTILINE_PARSER]
+ * name parser_3817
+ * type regex
+ * key_content log
+ * #
+ * # Regex rules for multiline parsing
+ * # ---------------------------------
+ * #
+ * # rules | state name | regex pattern | next state
+ * # ------|---------------|------------------------------------
+ * rule "start_state" "/- $/" "cont"
+ * rule "cont" "/^([1-9].*$/" "cont"
+ *
+ */
+
+ /* Initialize environment */
+ config = flb_config_init();
+
+ /* Register custom parser */
+ mlp = flb_ml_parser_create(config,
+ "parser_3817", /* name */
+ FLB_ML_REGEX, /* type */
+ NULL, /* match_str */
+ FLB_FALSE, /* negate */
+ 1000, /* flush_ms */
+ "log", /* key_content */
+ NULL, /* key_pattern */
+ NULL, /* key_group */
+ NULL, /* parser ctx */
+ NULL); /* parser name */
+ TEST_CHECK(mlp != NULL);
+
+ /* rule: start_state */
+ ret = flb_ml_rule_create(mlp, "start_state", "/- $/", "cont", NULL);
+ if (ret != 0) {
+ fprintf(stderr, "error creating rule 1");
+ }
+
+ /* rule: cont */
+ ret = flb_ml_rule_create(mlp, "cont", "/^([1-9]).*$/", "cont", NULL);
+ if (ret != 0) {
+ fprintf(stderr, "error creating rule 2");
+ }
+
+ /* initiaze the parser configuration */
+ ret = flb_ml_parser_init(mlp);
+ TEST_CHECK(ret == 0);
+
+ /* Run the test */
+ run_test(config, "issue_3817_1",
+ issue_3817_1_input, in_len,
+ issue_3817_1_output, out_len,
+ "cri", "parser_3817");
+
+ flb_config_exit(config);
+}
+
+static void test_issue_4034()
+{
+ int i;
+ int len;
+ int ret;
+ int entries;
+ uint64_t stream_id;
+ struct record_check *r;
+ struct flb_config *config;
+ struct flb_time tm;
+ struct flb_ml *ml;
+ struct flb_ml_parser_ins *mlp_i;
+ struct expected_result res = {0};
+ msgpack_packer mp_pck;
+ msgpack_sbuffer mp_sbuf;
+
+ /* Expected results context */
+ res.key = "log";
+ res.out_records = cri_output;
+
+ /* Initialize environment */
+ config = flb_config_init();
+
+ /* Create cri multiline mode */
+ ml = flb_ml_create(config, "cri-test");
+ TEST_CHECK(ml != NULL);
+
+ /* Generate an instance of multiline cri parser */
+ mlp_i = flb_ml_parser_instance_create(ml, "cri");
+ TEST_CHECK(mlp_i != NULL);
+
+ flb_ml_parser_instance_set(mlp_i, "key_content", "log");
+
+ ret = flb_ml_stream_create(ml, "cri", -1, flush_callback, (void *) &res,
+ &stream_id);
+ TEST_CHECK(ret == 0);
+
+ /* initialize buffers */
+ msgpack_sbuffer_init(&mp_sbuf);
+ msgpack_packer_init(&mp_pck, &mp_sbuf, msgpack_sbuffer_write);
+
+ size_t off = 0;
+ msgpack_unpacked result;
+ msgpack_object root;
+ msgpack_object *map;
+
+ entries = sizeof(cri_input) / sizeof(struct record_check);
+ for (i = 0; i < entries; i++) {
+ r = &cri_input[i];
+ len = strlen(r->buf);
+
+ /* Package as msgpack */
+ flb_time_get(&tm);
+
+ /* initialize buffers */
+ msgpack_sbuffer_init(&mp_sbuf);
+ msgpack_packer_init(&mp_pck, &mp_sbuf, msgpack_sbuffer_write);
+
+ msgpack_pack_array(&mp_pck, 2);
+ flb_time_append_to_msgpack(&tm, &mp_pck, 0);
+
+ msgpack_pack_map(&mp_pck, 1);
+ msgpack_pack_str(&mp_pck, 3);
+ msgpack_pack_str_body(&mp_pck, "log", 3);
+ msgpack_pack_str(&mp_pck, len);
+ msgpack_pack_str_body(&mp_pck, r->buf, len);
+
+ /* Unpack and lookup the content map */
+ msgpack_unpacked_init(&result);
+ off = 0;
+ ret = msgpack_unpack_next(&result, mp_sbuf.data, mp_sbuf.size, &off);
+
+ flb_pack_print(mp_sbuf.data, mp_sbuf.size);
+
+ root = result.data;
+ map = &root.via.array.ptr[1];
+
+ /* Package as msgpack */
+ ret = flb_ml_append_object(ml, stream_id, &tm, NULL, map);
+
+ msgpack_unpacked_destroy(&result);
+ msgpack_sbuffer_destroy(&mp_sbuf);
+ }
+ flb_ml_flush_pending_now(ml);
+
+ if (ml) {
+ flb_ml_destroy(ml);
+ }
+
+ flb_config_exit(config);
+}
+
+static void test_issue_5504()
+{
+ uint64_t last_flush;
+ struct flb_config *config;
+ struct flb_ml *ml;
+ struct flb_ml_parser_ins *mlp_i;
+ struct mk_event_loop *evl;
+ struct flb_sched *sched;
+ struct mk_list *tmp;
+ struct mk_list *head;
+ struct flb_sched_timer *timer;
+ void (*cb)(struct flb_config *, void *);
+ int timeout = 500;
+
+#ifdef _WIN32
+ WSADATA wsa_data;
+ WSAStartup(0x0201, &wsa_data);
+#endif
+
+ /* Initialize environment */
+ config = flb_config_init();
+
+ /* Create the event loop */
+ evl = config->evl;
+ config->evl = mk_event_loop_create(32);
+ TEST_CHECK(config->evl != NULL);
+
+ /* Initialize the scheduler */
+ sched = config->sched;
+ config->sched = flb_sched_create(config, config->evl);
+ TEST_CHECK(config->sched != NULL);
+
+ /* Set the thread local scheduler */
+ flb_sched_ctx_init();
+ flb_sched_ctx_set(config->sched);
+
+ ml = flb_ml_create(config, "5504-test");
+ TEST_CHECK(ml != NULL);
+
+ /* Generate an instance of any multiline parser */
+ mlp_i = flb_ml_parser_instance_create(ml, "cri");
+ TEST_CHECK(mlp_i != NULL);
+
+ flb_ml_parser_instance_set(mlp_i, "key_content", "log");
+
+ /* Set the flush timeout */
+ ml->flush_ms = timeout;
+
+ /* Initialize the auto flush */
+ flb_ml_auto_flush_init(ml);
+
+ /* Store the initial last_flush time */
+ last_flush = ml->last_flush;
+
+ /* Find the cb_ml_flush_timer callback from the timers */
+ mk_list_foreach_safe(head, tmp, &((struct flb_sched *)config->sched)->timers) {
+ timer = mk_list_entry(head, struct flb_sched_timer, _head);
+ if (timer->type == FLB_SCHED_TIMER_CB_PERM) {
+ cb = timer->cb;
+ }
+ }
+ TEST_CHECK(cb != NULL);
+
+ /* Trigger the callback without delay */
+ cb(config, ml);
+ /* This should not update the last_flush since it is before the timeout */
+ TEST_CHECK(ml->last_flush == last_flush);
+
+ /* Sleep just enough time to pass the timeout */
+ flb_time_msleep(timeout + 1);
+
+ /* Retrigger the callback */
+ cb(config, ml);
+ /* Ensure this time the last_flush has been updated */
+ TEST_CHECK(ml->last_flush > last_flush);
+
+ /* Cleanup */
+ flb_sched_destroy(config->sched);
+ config->sched = sched;
+ mk_event_loop_destroy(config->evl);
+ config->evl = evl;
+ flb_ml_destroy(ml);
+ flb_config_exit(config);
+
+#ifdef _WIN32
+ WSACleanup();
+#endif
+}
+
+TEST_LIST = {
+ /* Normal features tests */
+ { "parser_docker", test_parser_docker},
+ { "parser_cri", test_parser_cri},
+ { "parser_java", test_parser_java},
+ { "parser_python", test_parser_python},
+ { "parser_ruby", test_parser_ruby},
+ { "parser_elastic", test_parser_elastic},
+ { "parser_go", test_parser_go},
+ { "container_mix", test_container_mix},
+ { "endswith", test_endswith},
+
+ /* Issues reported on Github */
+ { "issue_3817_1" , test_issue_3817_1},
+ { "issue_4034" , test_issue_4034},
+ { "issue_4949" , test_issue_4949},
+ { "issue_5504" , test_issue_5504},
+ { 0 }
+};
diff --git a/fluent-bit/tests/internal/network.c b/fluent-bit/tests/internal/network.c
new file mode 100644
index 00000000..50683f18
--- /dev/null
+++ b/fluent-bit/tests/internal/network.c
@@ -0,0 +1,164 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+#include <fluent-bit/flb_info.h>
+#include <fluent-bit/flb_mem.h>
+#include <fluent-bit/flb_parser.h>
+#include <fluent-bit/flb_error.h>
+#include <fluent-bit/flb_network.h>
+#include <fluent-bit/flb_socket.h>
+#include <fluent-bit/flb_time.h>
+
+#include <time.h>
+#include "flb_tests_internal.h"
+
+#define TEST_HOSTv4 "127.0.0.1"
+#define TEST_HOSTv6 "::1"
+#define TEST_PORT "41322"
+
+#define TEST_EV_CLIENT MK_EVENT_NOTIFICATION
+#define TEST_EV_SERVER MK_EVENT_CUSTOM
+
+static int socket_check_ok(flb_sockfd_t fd)
+{
+ int ret;
+ int error = 0;
+ socklen_t len = sizeof(error);
+
+ ret = getsockopt(fd, SOL_SOCKET, SO_ERROR, &error, &len);
+ if (ret == -1) {
+ return -1;
+ }
+
+ if (error != 0) {
+ return -1;
+ }
+
+ return 0;
+}
+
+static void test_client_server(int is_ipv6)
+{
+ int ret;
+ int loops = 0;
+ int client_OK = FLB_FALSE;
+ int server_OK = FLB_FALSE;
+ int family;
+ char *host;
+ flb_sockfd_t fd_client;
+ flb_sockfd_t fd_server;
+ flb_sockfd_t fd_remote = -1;
+ struct mk_event e_client = {0};
+ struct mk_event e_server = {0};
+ struct mk_event *e_item;
+ struct mk_event_loop *evl;
+
+ if (is_ipv6 == FLB_TRUE) {
+ family = AF_INET6;
+ host = TEST_HOSTv6;
+ }
+ else {
+ family = AF_INET;
+ host = TEST_HOSTv4;
+ }
+
+ /* Create client and server sockets */
+ fd_client = flb_net_socket_create(family, FLB_TRUE);
+ if (errno == EAFNOSUPPORT) {
+ TEST_MSG("This protocol is not supported in this platform");
+ return;
+ }
+ TEST_CHECK(fd_client != -1);
+
+ fd_server = flb_net_server(TEST_PORT, host);
+ TEST_CHECK(fd_server != -1);
+
+ /* Create Event loop */
+ evl = mk_event_loop_create(8);
+ TEST_CHECK(evl != NULL);
+
+ /* Register client/server sockets into the event loop */
+ MK_EVENT_NEW(&e_client);
+ ret = mk_event_add(evl, fd_client, TEST_EV_CLIENT, MK_EVENT_WRITE, &e_client);
+ TEST_CHECK(ret != -1);
+
+ MK_EVENT_NEW(&e_server);
+ ret = mk_event_add(evl, fd_server, TEST_EV_SERVER, MK_EVENT_READ, &e_server);
+ TEST_CHECK(ret != -1);
+
+ /* Test an async connection, we expect -1 */
+ ret = flb_net_tcp_fd_connect(fd_client, host, atol(TEST_PORT));
+ TEST_CHECK(ret == -1);
+ TEST_CHECK(errno == EINPROGRESS);
+
+#ifdef FLB_SYSTEM_MACOS
+ /* On macOS, its internal timer's is inacccurate without waiting code.
+ * We need to proceed its timer tick for processing events. */
+ flb_time_msleep(50);
+#endif
+
+ /* Event loop */
+ while (1) {
+ /* Break immediately for invalid status */
+ if (fd_client == -1 || fd_server == -1) {
+ break;
+ }
+ mk_event_wait(evl);
+ mk_event_foreach(e_item, evl) {
+ if (e_item->type == TEST_EV_CLIENT) {
+ /* Validate event mask */
+ TEST_CHECK(e_item->mask & MK_EVENT_WRITE);
+
+ /*
+ * Client socket get a notification, we expect to get a
+ * successful connection.
+ */
+ ret = socket_check_ok(fd_client);
+ TEST_CHECK(ret == 0);
+ client_OK = FLB_TRUE;
+ }
+ else if (e_item->type == TEST_EV_SERVER) {
+ /* Validate event mask */
+ TEST_CHECK(e_item->mask & MK_EVENT_READ);
+
+ /* Accept the incoming connection */
+ fd_remote = flb_net_accept(fd_server);
+ TEST_CHECK(fd_remote > 0);
+
+ server_OK = FLB_TRUE;
+ }
+ loops++;
+ }
+
+ if (client_OK == FLB_TRUE && server_OK == FLB_TRUE) {
+ break;
+ }
+
+ TEST_CHECK(loops < 2);
+ if (loops >= 2) {
+ break;
+ }
+ }
+
+ mk_event_loop_destroy(evl);
+ flb_socket_close(fd_client);
+ flb_socket_close(fd_server);
+ if (fd_remote > 0) {
+ flb_socket_close(fd_remote);
+ }
+}
+
+void test_ipv4_client_server()
+{
+ test_client_server(FLB_FALSE);
+}
+
+void test_ipv6_client_server()
+{
+ test_client_server(FLB_TRUE);
+}
+
+TEST_LIST = {
+ { "ipv4_client_server", test_ipv4_client_server},
+ { "ipv6_client_server", test_ipv6_client_server},
+ { 0 }
+};
diff --git a/fluent-bit/tests/internal/pack.c b/fluent-bit/tests/internal/pack.c
new file mode 100644
index 00000000..1bfabfeb
--- /dev/null
+++ b/fluent-bit/tests/internal/pack.c
@@ -0,0 +1,914 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+#include <fluent-bit/flb_info.h>
+#include <fluent-bit/flb_mem.h>
+#include <fluent-bit/flb_pack.h>
+#include <fluent-bit/flb_error.h>
+#include <fluent-bit/flb_str.h>
+#include <monkey/mk_core.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <dirent.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <math.h> /* for NAN */
+
+
+#include "flb_tests_internal.h"
+
+/* JSON iteration tests */
+#define JSON_SINGLE_MAP1 FLB_TESTS_DATA_PATH "/data/pack/json_single_map_001.json"
+#define JSON_SINGLE_MAP2 FLB_TESTS_DATA_PATH "/data/pack/json_single_map_002.json"
+#define JSON_DUP_KEYS_I FLB_TESTS_DATA_PATH "/data/pack/dup_keys_in.json"
+#define JSON_DUP_KEYS_O FLB_TESTS_DATA_PATH "/data/pack/dup_keys_out.json"
+
+#define JSON_BUG342 FLB_TESTS_DATA_PATH "/data/pack/bug342.json"
+
+/* Pack Samples path */
+#define PACK_SAMPLES FLB_TESTS_DATA_PATH "/data/pack/"
+
+struct pack_test {
+ char *msgpack;
+ char *json;
+};
+
+/* If we get more than 256 tests, just update the size */
+struct pack_test pt[256];
+
+static inline void consume_bytes(char *buf, int bytes, int length)
+{
+ memmove(buf, buf + bytes, length - bytes);
+}
+
+/* Pack a simple JSON map */
+void test_json_pack()
+{
+ int ret;
+ int root_type;
+ size_t len;
+ char *data;
+ char *out_buf;
+ size_t out_size;
+
+ data = mk_file_to_buffer(JSON_SINGLE_MAP1);
+ TEST_CHECK(data != NULL);
+
+ len = strlen(data);
+
+ ret = flb_pack_json(data, len, &out_buf, &out_size, &root_type, NULL);
+ TEST_CHECK(ret == 0);
+
+ flb_free(data);
+ flb_free(out_buf);
+}
+
+/* Pack a simple JSON map using a state */
+void test_json_pack_iter()
+{
+ int i;
+ int ret;
+ size_t len;
+ char *data;
+ char *out_buf = NULL;
+ int out_size;
+ struct flb_pack_state state;
+
+ data = mk_file_to_buffer(JSON_SINGLE_MAP1);
+ TEST_CHECK(data != NULL);
+
+ len = strlen(data);
+
+ ret = flb_pack_state_init(&state);
+ TEST_CHECK(ret == 0);
+
+ /* Pass byte by byte */
+ for (i = 1; i < len; i++) {
+ ret = flb_pack_json_state(data, i, &out_buf, &out_size, &state);
+ if (i + 1 != len) {
+ TEST_CHECK(ret == FLB_ERR_JSON_PART);
+ }
+ }
+ TEST_CHECK(ret != FLB_ERR_JSON_INVAL && ret != FLB_ERR_JSON_PART);
+
+ flb_pack_state_reset(&state);
+ flb_free(data);
+ flb_free(out_buf);
+}
+
+/* Pack two concatenated JSON maps using a state */
+void test_json_pack_mult()
+
+{
+ int ret;
+ int maps = 0;
+ size_t off = 0;
+ size_t len1;
+ size_t len2;
+ size_t total;
+ char *buf;
+ char *data1;
+ char *data2;
+ char *out_buf;
+ int out_size;
+ msgpack_unpacked result;
+ msgpack_object root;
+ struct flb_pack_state state;
+
+ data1 = mk_file_to_buffer(JSON_SINGLE_MAP1);
+ TEST_CHECK(data1 != NULL);
+ len1 = strlen(data1);
+
+ data2 = mk_file_to_buffer(JSON_SINGLE_MAP2);
+ TEST_CHECK(data2 != NULL);
+ len2 = strlen(data2);
+
+ buf = flb_malloc(len1 + len2 + 1);
+ TEST_CHECK(buf != NULL);
+
+ /* Merge buffers */
+ memcpy(buf, data1, len1);
+ memcpy(buf + len1, data2, len2);
+ total = len1 + len2;
+ buf[total] = '\0';
+
+ /* Release buffers */
+ flb_free(data1);
+ flb_free(data2);
+
+ ret = flb_pack_state_init(&state);
+ TEST_CHECK(ret == 0);
+
+ /* Enable the 'multiple' flag so the parser will accept concatenated msgs */
+ state.multiple = FLB_TRUE;
+
+ /* It should pack two msgpack-maps in out_buf */
+ ret = flb_pack_json_state(buf, total, &out_buf, &out_size, &state);
+ TEST_CHECK(ret == 0);
+
+ /* Validate output buffer */
+ msgpack_unpacked_init(&result);
+ while (msgpack_unpack_next(&result, out_buf, out_size, &off) == MSGPACK_UNPACK_SUCCESS) {
+ maps++;
+ root = result.data;
+ TEST_CHECK(root.type == MSGPACK_OBJECT_MAP);
+ }
+ msgpack_unpacked_destroy(&result);
+
+ TEST_CHECK(maps == 2);
+
+ flb_pack_state_reset(&state);
+ flb_free(out_buf);
+ flb_free(buf);
+}
+
+/* Pack two concatenated JSON maps byte by byte using a state */
+void test_json_pack_mult_iter()
+
+{
+ int i;
+ int ret;
+ int maps = 0;
+ int total_maps = 0;
+ size_t off = 0;
+ size_t len1;
+ size_t len2;
+ size_t total;
+ char *buf;
+ char *data1;
+ char *data2;
+ char *out_buf;
+ int out_size;
+ msgpack_unpacked result;
+ msgpack_object root;
+ struct flb_pack_state state;
+
+ data1 = mk_file_to_buffer(JSON_SINGLE_MAP1);
+ TEST_CHECK(data1 != NULL);
+ len1 = strlen(data1);
+
+ data2 = mk_file_to_buffer(JSON_SINGLE_MAP2);
+ TEST_CHECK(data2 != NULL);
+ len2 = strlen(data2);
+
+ buf = flb_malloc(len1 + len2 + 1);
+ TEST_CHECK(buf != NULL);
+
+ /* Merge buffers */
+ memcpy(buf, data1, len1);
+ memcpy(buf + len1, data2, len2);
+ total = len1 + len2;
+ buf[total] = '\0';
+
+ /* Release buffers */
+ flb_free(data1);
+ flb_free(data2);
+
+ ret = flb_pack_state_init(&state);
+ TEST_CHECK(ret == 0);
+
+ /* Enable the 'multiple' flag so the parser will accept concatenated msgs */
+ state.multiple = FLB_TRUE;
+
+ /* Pass byte by byte */
+ for (i = 1; i < total; i++) {
+ ret = flb_pack_json_state(buf, i, &out_buf, &out_size, &state);
+ if (ret == 0) {
+ /* Consume processed bytes */
+ consume_bytes(buf, state.last_byte, total);
+ i = 1;
+ total -= state.last_byte;
+ flb_pack_state_reset(&state);
+ flb_pack_state_init(&state);
+
+ /* Validate output buffer */
+ off = 0;
+ maps = 0;
+ msgpack_unpacked_init(&result);
+ while (msgpack_unpack_next(&result, out_buf, out_size, &off) == MSGPACK_UNPACK_SUCCESS) {
+ root = result.data;
+ TEST_CHECK(root.type == MSGPACK_OBJECT_MAP);
+ maps++;
+ total_maps++;
+ }
+ TEST_CHECK(maps == 1);
+ msgpack_unpacked_destroy(&result);
+ flb_free(out_buf);
+ }
+ }
+
+ TEST_CHECK(total_maps == 2);
+ flb_pack_state_reset(&state);
+ flb_free(buf);
+}
+
+/* Validate default values of macros used in flb_msgpack_raw_to_json_sds */
+void test_msgpack_to_json_macros()
+{
+ /* Verify default values */
+ TEST_CHECK(FLB_MSGPACK_TO_JSON_INIT_BUFFER_SIZE == 2.0);
+ TEST_CHECK(FLB_MSGPACK_TO_JSON_REALLOC_BUFFER_SIZE == 0.10);
+}
+
+/* Validate that duplicated keys are removed */
+void test_json_dup_keys()
+{
+ int ret;
+ int type;
+ size_t len_in;
+ char *out_buf;
+ size_t out_size;
+ char *data_in;
+ char *data_out;
+ flb_sds_t out_json;
+ flb_sds_t d;
+
+ /* Read JSON input file */
+ data_in = mk_file_to_buffer(JSON_DUP_KEYS_I);
+ TEST_CHECK(data_in != NULL);
+ len_in = strlen(data_in);
+
+ /* Read JSON output file */
+ data_out = mk_file_to_buffer(JSON_DUP_KEYS_O);
+ TEST_CHECK(data_out != NULL);
+
+ /* Pack raw JSON as msgpack */
+ ret = flb_pack_json(data_in, len_in, &out_buf, &out_size, &type, NULL);
+ TEST_CHECK(ret == 0);
+
+ d = flb_sds_create("date");
+ TEST_CHECK(d != NULL);
+
+ /* Convert back to JSON */
+ out_json = flb_pack_msgpack_to_json_format(out_buf, out_size,
+ FLB_PACK_JSON_FORMAT_LINES,
+ FLB_PACK_JSON_DATE_EPOCH,
+ d);
+ TEST_CHECK(out_json != NULL);
+
+ TEST_CHECK(strncmp(out_json, data_out, flb_sds_len(out_json)) == 0);
+ flb_sds_destroy(d);
+ flb_sds_destroy(out_json);
+ flb_free(out_buf);
+ flb_free(data_in);
+ flb_free(data_out);
+}
+
+void test_json_pack_bug342()
+{
+ int i = 0;
+ int records = 0;
+ int fd;
+ int ret;
+ size_t off = 0;
+ ssize_t r = 0;
+ char *out;
+ char buf[1024*4];
+ int out_size;
+ size_t total = 0;
+ int bytes[] = {1, 3, 3, 5, 5, 35, 17, 23,
+ 46, 37, 49, 51, 68, 70, 86, 268,
+ 120, 178, 315, 754, 753, 125};
+ struct stat st;
+ struct flb_pack_state state;
+ msgpack_unpacked result;
+ ret = stat(JSON_BUG342, &st);
+ if (ret == -1) {
+ perror("stat");
+ exit(EXIT_FAILURE);
+ }
+
+ for (i = 0; i < (sizeof(bytes)/sizeof(int)); i++) {
+ total += bytes[i];
+ }
+
+ TEST_CHECK(total == st.st_size);
+ if (total != st.st_size) {
+ exit(EXIT_FAILURE);
+ }
+
+ fd = open(JSON_BUG342, O_RDONLY);
+ if (fd == -1) {
+ perror("open");
+ exit(EXIT_FAILURE);
+ }
+
+ flb_pack_state_init(&state);
+ state.multiple = FLB_TRUE;
+
+ for (i = 0; i < (sizeof(bytes)/sizeof(int)); i++) {
+ r = read(fd, buf + off, bytes[i]);
+ TEST_CHECK(r == bytes[i]);
+ if (r <= 0) {
+ perror("read");
+ exit(EXIT_FAILURE);
+ }
+ off += r;
+
+ ret = flb_pack_json_state(buf, off, &out, &out_size, &state);
+ TEST_CHECK(ret != FLB_ERR_JSON_INVAL);
+ if (ret == FLB_ERR_JSON_INVAL) {
+ exit(EXIT_FAILURE);
+ }
+ else if (ret == FLB_ERR_JSON_PART) {
+ continue;
+ }
+ else if (ret == 0) {
+ /* remove used bytes */
+ consume_bytes(buf, state.last_byte, off);
+ off -= state.last_byte;
+
+ /* reset the packer state */
+ flb_pack_state_reset(&state);
+ flb_pack_state_init(&state);
+ state.multiple = FLB_TRUE;
+ }
+
+ size_t coff = 0;
+ msgpack_unpacked_init(&result);
+ while (msgpack_unpack_next(&result, out, out_size, &coff) == MSGPACK_UNPACK_SUCCESS) {
+ records++;
+ }
+ msgpack_unpacked_destroy(&result);
+
+ TEST_CHECK(off >= state.last_byte);
+ if (off < state.last_byte) {
+ exit(1);
+ }
+ flb_free(out);
+ }
+ flb_pack_state_reset(&state);
+ close(fd);
+ TEST_CHECK(records == 240);
+}
+
+
+/* Iterate data/pack/ directory and compose an array with files to test */
+static int utf8_tests_create()
+{
+ int i = 0;
+ int len;
+ int ret;
+ char ext_mp[PATH_MAX];
+ char ext_json[PATH_MAX];
+ DIR *dir;
+ struct pack_test *test;
+ struct dirent *entry;
+ struct stat st;
+
+ memset(pt, '\0', sizeof(pt));
+
+ dir = opendir(PACK_SAMPLES);
+ TEST_CHECK(dir != NULL);
+ if (dir == NULL) {
+ exit(EXIT_FAILURE);
+ }
+
+ while ((entry = readdir(dir)) != NULL) {
+ if (entry->d_type != DT_REG) {
+ continue;
+ }
+
+ len = strlen(entry->d_name);
+ if (strcmp(entry->d_name + (len - 3), ".mp") != 0) {
+ continue;
+ }
+
+ snprintf(ext_mp, sizeof(ext_mp) - 1,
+ "%s%s", PACK_SAMPLES, entry->d_name);
+ len = snprintf(ext_json, sizeof(ext_json) - 1, "%s%s",
+ PACK_SAMPLES, entry->d_name);
+ snprintf(ext_json + (len - 3), sizeof(ext_json) - len - 3,
+ "%s", ".json");
+
+
+ /* Validate new paths */
+ ret = stat(ext_mp, &st);
+ if (ret == -1) {
+ printf("Unit test msgpack not found: %s\n", ext_mp);
+ exit(EXIT_FAILURE);
+ }
+
+ ret = stat(ext_json, &st);
+ if (ret == -1) {
+ printf("Unit test result JSON not found: %s\n", ext_json);
+ exit(EXIT_FAILURE);
+ }
+
+ /* Insert into table */
+ test = &pt[i];
+ test->msgpack = flb_strdup(ext_mp);
+ test->json = flb_strdup(ext_json);
+ i++;
+ }
+
+ closedir(dir);
+ return i;
+}
+
+static void utf8_tests_destroy(int s)
+{
+ int i;
+ struct pack_test *test;
+
+ for (i = 0; i < s; i++) {
+ test = &pt[i];
+ flb_free(test->msgpack);
+ flb_free(test->json);
+ }
+}
+
+void test_utf8_to_json()
+{
+ int i;
+ int ret;
+ int n_tests;
+ char *file_msgp;
+ char *file_json;
+ flb_sds_t out_buf;
+ size_t out_size;
+ size_t msgp_size;
+ size_t json_size;
+ struct stat st;
+ struct pack_test *test;
+
+ n_tests = utf8_tests_create();
+
+ /* Iterate unit tests table */
+ for (i = 0; i < n_tests; i++) {
+ test = &pt[i];
+ if (!test->msgpack) {
+ break;
+ }
+
+ file_msgp = mk_file_to_buffer(test->msgpack);
+ TEST_CHECK(file_msgp != NULL);
+ stat(test->msgpack, &st);
+ msgp_size = st.st_size;
+
+ file_json = mk_file_to_buffer(test->json);
+ TEST_CHECK(file_json != NULL);
+ if (!file_json) {
+ printf("Missing JSON file: %s\n", test->json);
+ flb_free(file_msgp);
+ continue;
+ }
+
+ json_size = strlen(file_json);
+
+ out_buf = flb_msgpack_raw_to_json_sds(file_msgp, msgp_size);
+ TEST_CHECK(out_buf != NULL);
+ out_size = flb_sds_len(out_buf);
+
+ ret = strcmp(file_json, out_buf);
+ if (ret != 0) {
+ TEST_CHECK(ret == 0);
+ printf("[test] %s\n", test->json);
+ printf(" EXPECTED => '%s'\n", file_json);
+ printf(" ENCODED => '%s'\n", out_buf);
+ }
+
+ TEST_CHECK(out_size == json_size);
+
+ if (out_buf) {
+ flb_sds_destroy(out_buf);
+ }
+ flb_free(file_msgp);
+ flb_free(file_json);
+ }
+
+ utf8_tests_destroy(n_tests);
+}
+
+void test_json_pack_bug1278()
+{
+ int i;
+ int len;
+ int ret;
+ int items;
+ int type;
+ char *p_in;
+ char *p_out;
+ char *out_buf;
+ size_t out_size;
+ msgpack_sbuffer mp_sbuf;
+ msgpack_packer mp_pck;
+ msgpack_unpacked result;
+ msgpack_object root;
+ msgpack_object val;
+ size_t off = 0;
+ flb_sds_t json;
+ char tmp[32];
+
+ char *in[] = {
+ "one\atwo",
+ "one\btwo",
+ "one\ttwo",
+ "one\ntwo",
+ "one\vtwo",
+ "one\ftwo",
+ "one\rtwo",
+ "\\n",
+ };
+
+ char *out[] = {
+ "\"one\\u0007two\"",
+ "\"one\\btwo\"",
+ "\"one\\ttwo\"",
+ "\"one\\ntwo\"",
+ "\"one\\u000btwo\"",
+ "\"one\\ftwo\"",
+ "\"one\\rtwo\"",
+ "\"\\\\n\"",
+ };
+
+ printf("\n");
+ items = sizeof(in) / sizeof(char *);
+ for (i = 0; i < items; i++) {
+ p_in = in[i];
+ p_out = out[i];
+
+ len = strlen(p_in);
+
+ msgpack_sbuffer_init(&mp_sbuf);
+ msgpack_packer_init(&mp_pck, &mp_sbuf, msgpack_sbuffer_write);
+
+ msgpack_pack_str(&mp_pck, len);
+ msgpack_pack_str_body(&mp_pck, p_in, len);
+
+ /* Pack raw string as JSON */
+ json = flb_msgpack_raw_to_json_sds(mp_sbuf.data, mp_sbuf.size);
+
+ /* Compare expected JSON output */
+ ret = strcmp(p_out, json);
+ TEST_CHECK(ret == 0);
+ if (ret != 0) {
+ printf("== JSON comparisson failed ==\n");
+ printf("expected: %s\n", p_out);
+ printf("output : %s\n", json);
+ }
+ else {
+ printf("test %i out => %s\n", i, json);
+ }
+ /* Put JSON string in a map and convert it to msgpack */
+ snprintf(tmp, sizeof(tmp) -1 , "{\"log\": %s}", json);
+ ret = flb_pack_json(tmp, strlen(tmp), &out_buf, &out_size, &type, NULL);
+ TEST_CHECK(ret == 0);
+ if (ret != 0) {
+ printf("failed packaging to JSON\n");
+ }
+
+ /* Unpack 'log' value and compare it to the original raw */
+ off = 0;
+ msgpack_unpacked_init(&result);
+ ret = msgpack_unpack_next(&result, out_buf, out_size, &off);
+ TEST_CHECK(ret == MSGPACK_UNPACK_SUCCESS);
+
+ /* Check parent type is a map */
+ root = result.data;
+ TEST_CHECK(root.type == MSGPACK_OBJECT_MAP);
+
+ /* Get map value */
+ val = root.via.map.ptr[0].val;
+ TEST_CHECK(val.type == MSGPACK_OBJECT_STR);
+
+ /* Compare bytes length */
+ len = strlen(p_in);
+ TEST_CHECK(len == val.via.str.size);
+ if (len != val.via.str.size) {
+ printf("failed comparing string length\n");
+ }
+
+ /* Compare raw bytes */
+ ret = memcmp(val.via.str.ptr, p_in, len);
+ TEST_CHECK(ret == 0);
+ if (ret != 0) {
+ printf("failed comparing to original value\n");
+ }
+
+ /* Relese resources */
+ flb_free(out_buf);
+ flb_sds_destroy(json);
+ msgpack_unpacked_destroy(&result);
+ msgpack_sbuffer_destroy(&mp_sbuf);
+
+ }
+}
+
+void test_json_pack_nan()
+{
+ int ret;
+ char json_str[128] = {0};
+ char *p = NULL;
+ struct flb_config config;
+ msgpack_sbuffer mp_sbuf;
+ msgpack_packer mp_pck;
+ msgpack_object obj;
+ msgpack_zone mempool;
+
+ config.convert_nan_to_null = FLB_TRUE;
+
+ // initialize msgpack
+ msgpack_sbuffer_init(&mp_sbuf);
+ msgpack_packer_init(&mp_pck, &mp_sbuf, msgpack_sbuffer_write);
+ msgpack_pack_double(&mp_pck, NAN);
+ msgpack_zone_init(&mempool, 2048);
+ msgpack_unpack(mp_sbuf.data, mp_sbuf.size, NULL, &mempool, &obj);
+ msgpack_zone_destroy(&mempool);
+ msgpack_sbuffer_destroy(&mp_sbuf);
+
+ // convert msgpack to json
+ ret = flb_msgpack_to_json(&json_str[0], sizeof(json_str), &obj);
+ TEST_CHECK(ret >= 0);
+
+ p = strstr(&json_str[0], "nan");
+ if (!TEST_CHECK(p != NULL)) {
+ TEST_MSG("json should be nan. json_str=%s", json_str);
+ }
+
+ // convert. nan -> null
+ memset(&json_str[0], 0, sizeof(json_str));
+ flb_pack_init(&config);
+ ret = flb_msgpack_to_json(&json_str[0], sizeof(json_str), &obj);
+ TEST_CHECK(ret >= 0);
+
+ p = strstr(&json_str[0], "null");
+ if (!TEST_CHECK(p != NULL)) {
+ TEST_MSG("json should be null. json_str=%s", json_str);
+ }
+
+ // clear setting
+ config.convert_nan_to_null = FLB_FALSE;
+ flb_pack_init(&config);
+}
+
+static int check_msgpack_val(msgpack_object obj, int expected_type, char *expected_val)
+{
+ int len;
+
+ if (!TEST_CHECK(obj.type == expected_type)) {
+ TEST_MSG("type mismatch\nexpected=%d got=%d", expected_type, obj.type);
+ return -1;
+ }
+ switch(obj.type) {
+ case MSGPACK_OBJECT_MAP:
+ if(!TEST_CHECK(obj.via.map.size == atoi(expected_val))) {
+ TEST_MSG("map size mismatch\nexpected=%s got=%d", expected_val, obj.via.map.size);
+ return -1;
+ }
+ break;
+
+ case MSGPACK_OBJECT_ARRAY:
+ if(!TEST_CHECK(obj.via.array.size == atoi(expected_val))) {
+ TEST_MSG("array size mismatch\nexpected=%s got=%d", expected_val, obj.via.array.size);
+ return -1;
+ }
+ break;
+
+ case MSGPACK_OBJECT_STR:
+ len = strlen(expected_val);
+ if (!TEST_CHECK(obj.via.str.size == strlen(expected_val))) {
+ TEST_MSG("str size mismatch\nexpected=%d got=%d", len, obj.via.str.size);
+ return -1;
+ }
+ else if(!TEST_CHECK(strncmp(expected_val, obj.via.str.ptr ,len) == 0)) {
+ TEST_MSG("str mismatch\nexpected=%.*s got=%.*s", len, expected_val, len, obj.via.str.ptr);
+ return -1;
+ }
+ break;
+
+ case MSGPACK_OBJECT_POSITIVE_INTEGER:
+ if(!TEST_CHECK(obj.via.u64 == (uint64_t)atoi(expected_val))) {
+ TEST_MSG("int mismatch\nexpected=%s got=%"PRIu64, expected_val, obj.via.u64);
+ return -1;
+ }
+ break;
+
+ case MSGPACK_OBJECT_BOOLEAN:
+ if (obj.via.boolean) {
+ if(!TEST_CHECK(strncasecmp(expected_val, "true",4) == 0)) {
+ TEST_MSG("bool mismatch\nexpected=%s got=true", expected_val);
+ return -1;
+ }
+ }
+ else {
+ if(!TEST_CHECK(strncasecmp(expected_val, "false",5) == 0)) {
+ TEST_MSG("bool mismatch\nexpected=%s got=false", expected_val);
+ return -1;
+ }
+ }
+ break;
+
+ default:
+ TEST_MSG("unknown type %d", obj.type);
+ return -1;
+ }
+
+ return 0;
+}
+
+/*
+ * https://github.com/fluent/fluent-bit/issues/5336
+ * Pack "valid JSON + partial JSON"
+ */
+#define JSON_BUG5336 "{\"int\":10, \"string\":\"hello\", \"bool\":true, \"array\":[0,1,2]}"
+void test_json_pack_bug5336()
+{
+ int ret;
+ char *json_valid = JSON_BUG5336;
+ size_t len = strlen(json_valid);
+
+ char *json_incomplete = JSON_BUG5336 JSON_BUG5336;
+ char *out = NULL;
+ int out_size;
+ struct flb_pack_state state;
+ int i;
+
+ msgpack_unpacked result;
+ msgpack_object obj;
+ size_t off = 0;
+
+ int loop_cnt = 0;
+
+ for (i=len; i<len*2; i++) {
+ loop_cnt++;
+
+ flb_pack_state_init(&state);
+
+ /* Pass small string size to create incomplete JSON */
+ ret = flb_pack_json_state(json_incomplete, i, &out, &out_size, &state);
+ if (!TEST_CHECK(ret != FLB_ERR_JSON_INVAL)) {
+ TEST_MSG("%ld: FLB_ERR_JSON_INVAL\njson=%.*s", i-len, i, json_incomplete);
+ exit(EXIT_FAILURE);
+ }
+ else if(!TEST_CHECK(ret != FLB_ERR_JSON_PART)) {
+ TEST_MSG("%ld: FLB_ERR_JSON_PART\njson=%.*s", i-len, i, json_incomplete);
+ exit(EXIT_FAILURE);
+ }
+
+ /* unpack parsed data */
+ msgpack_unpacked_init(&result);
+ off = 0;
+ TEST_CHECK(msgpack_unpack_next(&result, out, out_size, &off) == MSGPACK_UNPACK_SUCCESS);
+
+ TEST_CHECK(check_msgpack_val(result.data, MSGPACK_OBJECT_MAP, "4" /*map size*/) == 0);
+
+ /* "int":10 */
+ obj = result.data.via.map.ptr[0].key;
+ TEST_CHECK(check_msgpack_val(obj, MSGPACK_OBJECT_STR, "int") == 0);
+ obj = result.data.via.map.ptr[0].val;
+ TEST_CHECK(check_msgpack_val(obj, MSGPACK_OBJECT_POSITIVE_INTEGER, "10") == 0);
+
+ /* "string":"hello"*/
+ obj = result.data.via.map.ptr[1].key;
+ TEST_CHECK(check_msgpack_val(obj, MSGPACK_OBJECT_STR, "string") == 0);
+ obj = result.data.via.map.ptr[1].val;
+ TEST_CHECK(check_msgpack_val(obj, MSGPACK_OBJECT_STR, "hello") == 0);
+
+ /* "bool":true */
+ obj = result.data.via.map.ptr[2].key;
+ TEST_CHECK(check_msgpack_val(obj, MSGPACK_OBJECT_STR, "bool") == 0);
+ obj = result.data.via.map.ptr[2].val;
+ TEST_CHECK(check_msgpack_val(obj, MSGPACK_OBJECT_BOOLEAN, "true") == 0);
+
+ /* "array":[0,1,2] */
+ obj = result.data.via.map.ptr[3].key;
+ TEST_CHECK(check_msgpack_val(obj, MSGPACK_OBJECT_STR, "array") == 0);
+ obj = result.data.via.map.ptr[3].val;
+ TEST_CHECK(check_msgpack_val(obj, MSGPACK_OBJECT_ARRAY, "3" /*array size*/) == 0);
+ TEST_CHECK(check_msgpack_val(obj.via.array.ptr[0], MSGPACK_OBJECT_POSITIVE_INTEGER, "0") == 0);
+ TEST_CHECK(check_msgpack_val(obj.via.array.ptr[1], MSGPACK_OBJECT_POSITIVE_INTEGER, "1") == 0);
+ TEST_CHECK(check_msgpack_val(obj.via.array.ptr[2], MSGPACK_OBJECT_POSITIVE_INTEGER, "2") == 0);
+
+ msgpack_unpacked_destroy(&result);
+ flb_free(out);
+ flb_pack_state_reset(&state);
+ }
+
+ if(!TEST_CHECK(loop_cnt == len)) {
+ TEST_MSG("loop_cnt expect=%ld got=%d", len, loop_cnt);
+ }
+}
+
+const char input_msgpack[] = {0x92,/* array 2 */
+ 0xd7, 0x00, /* event time*/
+ 0x07, 0x5b, 0xcd, 0x15, /* second = 123456789 = 1973/11/29 21:33:09 */
+ 0x07, 0x5b, 0xcd, 0x15, /* nanosecond = 123456789 */
+ 0x81, 0xa2, 0x61, 0x61, 0xa2, 0x62, 0x62 /* {"aa":"bb"} */
+};
+
+void test_json_date(char* expect, int date_format)
+{
+ flb_sds_t json_key;
+ flb_sds_t ret;
+
+ json_key = flb_sds_create("date");
+ if (!TEST_CHECK(json_key != NULL)) {
+ TEST_MSG("flb_sds_create failed");
+ exit(1);
+ }
+
+ ret = flb_pack_msgpack_to_json_format((const char*)&input_msgpack[0], sizeof(input_msgpack),
+ FLB_PACK_JSON_FORMAT_JSON, date_format,
+ json_key);
+ if (!TEST_CHECK(ret != NULL)) {
+ TEST_MSG("flb_pack_msgpack_to_json_format failed");
+ flb_sds_destroy(json_key);
+ exit(1);
+ }
+ flb_sds_destroy(json_key);
+
+ if (!TEST_CHECK(strstr(ret, expect) != NULL)) {
+ TEST_MSG("mismatch. Got=%s expect=%s", ret, expect);
+ }
+
+ flb_sds_destroy(ret);
+}
+
+void test_json_date_iso8601()
+{
+ test_json_date("1973-11-29T21:33:09.123456Z", FLB_PACK_JSON_DATE_ISO8601);
+}
+
+void test_json_date_double()
+{
+ test_json_date("123456789.123456", FLB_PACK_JSON_DATE_DOUBLE);
+}
+
+void test_json_date_java_sql()
+{
+ test_json_date("1973-11-29 21:33:09.123456", FLB_PACK_JSON_DATE_JAVA_SQL_TIMESTAMP);
+}
+
+void test_json_date_epoch()
+{
+ test_json_date("123456789", FLB_PACK_JSON_DATE_EPOCH);
+}
+
+void test_json_date_epoch_ms()
+{
+ test_json_date("123456789123", FLB_PACK_JSON_DATE_EPOCH_MS);
+}
+
+TEST_LIST = {
+ /* JSON maps iteration */
+ { "json_pack" , test_json_pack },
+ { "json_pack_iter" , test_json_pack_iter},
+ { "json_pack_mult" , test_json_pack_mult},
+ { "json_pack_mult_iter", test_json_pack_mult_iter},
+ { "json_macros" , test_msgpack_to_json_macros},
+ { "json_dup_keys" , test_json_dup_keys},
+ { "json_pack_bug342" , test_json_pack_bug342},
+ { "json_pack_bug1278" , test_json_pack_bug1278},
+ { "json_pack_nan" , test_json_pack_nan},
+ { "json_pack_bug5336" , test_json_pack_bug5336},
+ { "json_date_iso8601" , test_json_date_iso8601},
+ { "json_date_double" , test_json_date_double},
+ { "json_date_java_sql" , test_json_date_java_sql},
+ { "json_date_epoch" , test_json_date_epoch},
+ { "json_date_epoch_ms" , test_json_date_epoch_ms},
+
+ /* Mixed bytes, check JSON encoding */
+ { "utf8_to_json", test_utf8_to_json},
+ { 0 }
+};
diff --git a/fluent-bit/tests/internal/parser.c b/fluent-bit/tests/internal/parser.c
new file mode 100644
index 00000000..6a560db7
--- /dev/null
+++ b/fluent-bit/tests/internal/parser.c
@@ -0,0 +1,528 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+#include <fluent-bit/flb_info.h>
+#include <fluent-bit/flb_mem.h>
+#include <fluent-bit/flb_parser.h>
+#include <fluent-bit/flb_error.h>
+#include <fluent-bit/flb_str.h>
+#include <fluent-bit/flb_sds.h>
+
+#include <time.h>
+#include <string.h>
+#include "flb_tests_internal.h"
+
+/* Parsers configuration */
+#define JSON_PARSERS FLB_TESTS_DATA_PATH "/data/parser/json.conf"
+#define REGEX_PARSERS FLB_TESTS_DATA_PATH "/data/parser/regex.conf"
+
+/* Templates */
+#define JSON_FMT_01 "{\"key001\": 12345, \"key002\": 0.99, \"time\": \"%s\"}"
+#define REGEX_FMT_01 "12345 0.99 %s"
+
+#define isleap(y) ((y) % 4 == 0 && ((y) % 400 == 0 || (y) % 100 != 0))
+#define year2sec(y) (isleap(y) ? 31622400 : 31536000)
+
+/* Timezone */
+struct tz_check {
+ char *val;
+ int diff;
+};
+
+
+struct tz_check tz_entries_ok[] = {
+ {"+0000", 0},
+ {"+00:00", 0},
+ {"+00:59", 3540},
+ {"-0600", -21000},
+ {"-06:00", -21000},
+};
+
+struct tz_check tz_entries_error[] = {
+ {"0000", 0},
+ {"+00:90", 0},
+ {"--600", 0},
+ {"-06:00", -21000},
+};
+
+/* Time Lookup */
+struct time_check {
+ char *parser_name;
+ char *time_string;
+ time_t epoch;
+ double frac_seconds;
+
+ /*
+ * Some tests requires to set the UTC offset to the given time,
+ * when this flag is enabled the parser is adjusted for each test.
+ */
+ int utc_offset;
+};
+
+struct time_check time_entries[] = {
+ /*
+ * Samples generated by scripts/dates.sh
+ * =====================================
+ * UTC => 07/17/2017 20:17:03 +0000, 1500322623
+ * IST => 07/18/2017 01:47:03 +0530, 1500322623
+ * JP => 07/18/2017 05:17:03 +0900, 1500322623
+ * ZW => 07/17/2017 22:17:03 +0200, 1500322623
+ */
+
+ /*
+ * No year tests (old Syslog)
+ * ==========================
+ */
+
+ /* Fixed UTC Offset = -0600 (-21600) */
+ {"no_year" , "Feb 16 04:06:58" , 1487239618, 0 , -21600},
+ {"no_year_N" , "Feb 16 04:06:58.1234" , 1487239618, 0.1234, -21600},
+ {"no_year_NC" , "Feb 16 04:06:58,1234" , 1487239618, 0.1234, -21600},
+
+ /* No year with imezone specified */
+ {"no_year_TZ" , "Feb 16 04:06:58 -0600" , 1487239618, 0 , 0},
+ {"no_year_N_TZ", "Feb 16 04:06:58.1234 -0600", 1487239618, 0.1234, 0},
+ {"no_year_NC_TZ","Feb 16 04:06:58,1234 -0600", 1487239618, 0.1234, 0},
+
+ /* Same date for different timezones, same timestamp */
+ {"generic_TZ" , "07/17/2017 20:17:03 +0000" , 1500322623, 0, 0},
+ {"generic_TZ" , "07/18/2017 01:47:03 +0530" , 1500322623, 0, 0},
+ {"generic_TZ" , "07/18/2017 05:17:03 +0900" , 1500322623, 0, 0},
+ {"generic_TZ" , "07/17/2017 22:17:03 +0200" , 1500322623, 0, 0},
+ {"generic_N_TZ" , "07/17/2017 22:17:03.1 +0200", 1500322623, 0.1, 0},
+ {"generic_NC_TZ", "07/17/2017 22:17:03,1 +0200", 1500322623, 0.1, 0},
+#ifndef __APPLE__
+ {"generic_TZ" , "07/18/2017 01:47:03 +05:30" , 1500322623, 0, 0},
+ {"generic_N_TZ" , "07/17/2017 22:17:03.1 +02:00", 1500322623, 0.1, 0},
+ {"generic_NC_TZ", "07/17/2017 22:17:03,1 +02:00", 1500322623, 0.1, 0},
+ {"generic_NL_TZ", "07/17/2017 22:17:03:1 +02:00", 1500322623, 0.1, 0},
+#endif
+ /* Same date for different timezones, same timestamp w/ fixed UTC offset */
+ {"generic" , "07/18/2017 01:47:03" , 1500322623, 0, 19800},
+ {"generic" , "07/18/2017 05:17:03" , 1500322623, 0, 32400},
+ {"generic" , "07/17/2017 22:17:03" , 1500322623, 0, 7200},
+ {"generic_N" , "07/17/2017 22:17:03.1" , 1500322623, 0.1, 7200},
+ {"generic_NC", "07/17/2017 22:17:03,1" , 1500322623, 0.1, 7200},
+
+ /* default UTC: the following timings 'are' in UTC already */
+ {"default_UTC" , "07/17/2017 20:17:03" , 1500322623, 0 , 0},
+ {"default_UTC_Z" , "07/17/2017 20:17:03Z" , 1500322623, 0 , 0},
+ {"default_UTC_N_Z", "07/17/2017 20:17:03.1234Z", 1500322623, 0.1234, 0},
+ {"default_UTC_NC_Z","07/17/2017 20:17:03,1234Z", 1500322623, 0.1234, 0},
+
+ {"apache_error", "Fri Jul 17 20:17:03.1234 2017", 1500322623, 0.1234, 0}
+};
+
+
+int flb_parser_json_do(struct flb_parser *parser,
+ char *buf, size_t length,
+ void **out_buf, size_t *out_size,
+ struct flb_time *out_time);
+
+int flb_parser_regex_do(struct flb_parser *parser,
+ char *buf, size_t length,
+ void **out_buf, size_t *out_size,
+ struct flb_time *out_time);
+
+/* Parse timezone string and get the offset */
+void test_parser_tzone_offset()
+{
+ int i;
+ int len;
+ int ret;
+ int diff;
+ struct tz_check *t;
+
+ /* Valid offsets */
+ for (i = 0; i < sizeof(tz_entries_ok) / sizeof(struct tz_check); i++) {
+ t = &tz_entries_ok[0];
+ len = strlen(t->val);
+
+ ret = flb_parser_tzone_offset(t->val, len, &diff);
+ TEST_CHECK(ret == 0 && diff == t->diff);
+ }
+
+ /* Invalid offsets */
+ for (i = 0; i < sizeof(tz_entries_error) / sizeof(struct tz_check); i++) {
+ t = &tz_entries_error[0];
+ len = strlen(t->val);
+
+ ret = flb_parser_tzone_offset(t->val, len, &diff);
+ TEST_CHECK(ret != 0);
+ }
+}
+
+static void load_json_parsers(struct flb_config *config)
+{
+ int ret;
+
+ ret = flb_parser_conf_file(JSON_PARSERS, config);
+ TEST_CHECK(ret == 0);
+}
+
+static void load_regex_parsers(struct flb_config *config)
+{
+ int ret;
+
+ ret = flb_parser_conf_file(REGEX_PARSERS, config);
+ TEST_CHECK(ret == 0);
+}
+
+void test_parser_time_lookup()
+{
+ int i;
+ int j;
+ int len;
+ int ret;
+ int toff;
+ int year_diff = 0;
+ double ns;
+ time_t now;
+ time_t epoch;
+ struct flb_parser *p;
+ struct flb_config *config;
+ struct time_check *t;
+ struct flb_tm tm;
+
+ config = flb_config_init();
+
+ load_json_parsers(config);
+
+ /* Iterate tests */
+ now = time(NULL);
+ for (i = 0; i < sizeof(time_entries) / sizeof(struct time_check); i++) {
+ t = &time_entries[i];
+ p = flb_parser_get(t->parser_name, config);
+ TEST_CHECK(p != NULL);
+
+ if (p == NULL) {
+ continue;
+ }
+
+ /* Alter time offset if set */
+ toff = 0;
+ if (t->utc_offset != 0) {
+ toff = p->time_offset;
+ p->time_offset = t->utc_offset;
+ }
+
+ /* Adjust timestamp for parsers using no-year */
+ if (p->time_with_year == FLB_FALSE) {
+ time_t time_test = t->epoch;
+ struct tm tm_now;
+ struct tm tm_test;
+
+ gmtime_r(&now, &tm_now);
+ gmtime_r(&time_test, &tm_test);
+
+ year_diff = 0;
+ for (j = tm_test.tm_year; j < tm_now.tm_year; j++) {
+ year_diff += year2sec(tm_test.tm_mon < 2 ? j : j + 1);
+ }
+ }
+ else {
+ year_diff = 0;
+ }
+
+ /* Lookup time */
+ len = strlen(t->time_string);
+ ret = flb_parser_time_lookup(t->time_string, len, now, p, &tm, &ns);
+ if(!(TEST_CHECK(ret == 0))) {
+ TEST_MSG("time lookup error: parser:'%s' timestr:'%s'", t->parser_name, t->time_string);
+ continue;
+ }
+
+ epoch = flb_parser_tm2time(&tm);
+ epoch -= year_diff;
+ TEST_CHECK(t->epoch == epoch);
+ TEST_CHECK(t->frac_seconds == ns);
+
+ if (t->utc_offset != 0) {
+ p->time_offset = toff;
+ }
+ }
+
+ flb_parser_exit(config);
+ flb_config_exit(config);
+}
+
+/* Do time lookup using the JSON parser backend*/
+void test_json_parser_time_lookup()
+{
+ int i;
+ int j;
+ int ret;
+ int len;
+ int toff;
+ int year_diff = 0;
+ time_t epoch;
+ long nsec;
+ char buf[512];
+ void *out_buf;
+ size_t out_size;
+ struct flb_time out_time;
+ struct flb_parser *p;
+ struct flb_config *config;
+ struct time_check *t;
+
+ config = flb_config_init();
+
+ /* Load parsers */
+ load_json_parsers(config);
+
+ for (i = 0; i < sizeof(time_entries) / sizeof(struct time_check); i++) {
+ t = &time_entries[i];
+ p = flb_parser_get(t->parser_name, config);
+ TEST_CHECK(p != NULL);
+
+ if (p == NULL) {
+ continue;
+ }
+
+ /* Alter time offset if set */
+ toff = 0;
+ if (t->utc_offset != 0) {
+ toff = p->time_offset;
+ p->time_offset = t->utc_offset;
+ }
+
+ /* Adjust timestamp for parsers using no-year */
+ if (p->time_with_year == FLB_FALSE) {
+ time_t time_now = time(NULL);
+ time_t time_test = t->epoch;
+ struct tm tm_now;
+ struct tm tm_test;
+
+ gmtime_r(&time_now, &tm_now);
+ gmtime_r(&time_test, &tm_test);
+
+ year_diff = 0;
+ for (j = tm_test.tm_year; j < tm_now.tm_year; j++) {
+ year_diff += year2sec(tm_test.tm_mon < 2 ? j : j + 1);
+ }
+ }
+ else {
+ year_diff = 0;
+ }
+
+ /* Compose the string */
+ len = snprintf(buf, sizeof(buf) - 1, JSON_FMT_01, t->time_string);
+
+ /* Invoke the JSON parser backend */
+ ret = flb_parser_json_do(p, buf, len, &out_buf, &out_size, &out_time);
+ TEST_CHECK(ret != -1);
+ TEST_CHECK(out_buf != NULL);
+
+ /* Check time */
+ epoch = t->epoch + year_diff;
+
+ TEST_CHECK(out_time.tm.tv_sec == epoch);
+ nsec = t->frac_seconds * 1000000000;
+ TEST_CHECK(out_time.tm.tv_nsec == nsec);
+
+ if (t->utc_offset != 0) {
+ p->time_offset = toff;
+ }
+
+ flb_free(out_buf);
+ }
+
+ flb_parser_exit(config);
+ flb_config_exit(config);
+}
+
+/* Do time lookup using the Regex parser backend*/
+void test_regex_parser_time_lookup()
+{
+ int i;
+ int j;
+ int ret;
+ int len;
+ int toff;
+ int year_diff = 0;
+ time_t epoch;
+ long nsec;
+ char buf[512];
+ void *out_buf;
+ size_t out_size;
+ struct flb_time out_time;
+ struct flb_parser *p;
+ struct flb_config *config;
+ struct time_check *t;
+
+ config = flb_config_init();
+
+ /* Load parsers */
+ load_regex_parsers(config);
+
+ for (i = 0; i < sizeof(time_entries) / sizeof(struct time_check); i++) {
+ t = &time_entries[i];
+ p = flb_parser_get(t->parser_name, config);
+ TEST_CHECK(p != NULL);
+
+ if (p == NULL) {
+ continue;
+ }
+
+ /* Alter time offset if set */
+ toff = 0;
+ if (t->utc_offset != 0) {
+ toff = p->time_offset;
+ p->time_offset = t->utc_offset;
+ }
+
+ /* Adjust timestamp for parsers using no-year */
+ if (p->time_with_year == FLB_FALSE) {
+ time_t time_now = time(NULL);
+ time_t time_test = t->epoch;
+ struct tm tm_now;
+ struct tm tm_test;
+
+ gmtime_r(&time_now, &tm_now);
+ gmtime_r(&time_test, &tm_test);
+
+ year_diff = 0;
+ for (j = tm_test.tm_year; j < tm_now.tm_year; j++) {
+ year_diff += year2sec(tm_test.tm_mon < 2 ? j : j + 1);
+ }
+ }
+ else {
+ year_diff = 0;
+ }
+
+ /* Compose the string */
+ len = snprintf(buf, sizeof(buf) - 1, REGEX_FMT_01, t->time_string);
+
+ /* Invoke the JSON parser backend */
+ ret = flb_parser_regex_do(p, buf, len, &out_buf, &out_size, &out_time);
+ TEST_CHECK(ret != -1);
+ TEST_CHECK(out_buf != NULL);
+
+ /* Adjust time without year */
+ epoch = t->epoch + year_diff;
+
+ /* Check time */
+ TEST_CHECK(out_time.tm.tv_sec == epoch);
+ nsec = t->frac_seconds * 1000000000;
+ TEST_CHECK(out_time.tm.tv_nsec == nsec);
+
+ if (t->utc_offset != 0) {
+ p->time_offset = toff;
+ }
+
+ flb_free(out_buf);
+ }
+
+ flb_parser_exit(config);
+ flb_config_exit(config);
+}
+
+static char *get_msgpack_map_key(void *buf, size_t buf_size, char *key) {
+ int i;
+ size_t off = 0;
+ int key_size;
+ char *ptr = NULL;
+ msgpack_unpacked result;
+ msgpack_object map;
+ msgpack_object k;
+ msgpack_object v;
+
+ msgpack_unpacked_init(&result);
+ msgpack_unpack_next(&result, buf, buf_size, &off);
+
+ map = result.data;
+
+ if (map.type != MSGPACK_OBJECT_MAP) {
+ msgpack_unpacked_destroy(&result);
+ return NULL;
+ }
+
+ key_size = strlen(key);
+
+
+ /* printf("map_size: %d\n", map.via.map.size); */
+
+ for (i = 0; i < map.via.map.size; i++) {
+ k = map.via.map.ptr[i].key;
+ v = map.via.map.ptr[i].val;
+ if (k.type != MSGPACK_OBJECT_STR) {
+ continue;
+ }
+ /* printf("key(%.*s)(%d) == (%s)(%d)\n", k.via.str.size, k.via.str.ptr, k.via.str.size, key, key_size); */
+ if (k.via.str.size == key_size && strncmp(key, (char *) k.via.str.ptr, k.via.str.size) == 0) {
+ ptr = flb_strndup(v.via.str.ptr, v.via.str.size);
+ break;
+ }
+ }
+
+ msgpack_unpacked_destroy(&result);
+
+ return ptr;
+}
+
+static int a_mysql_unquote_test(struct flb_parser *p, char *source, char *expected) {
+
+ int ret;
+ void *out_buf;
+ size_t out_size;
+ struct flb_time out_time;
+ char *val001;
+
+
+ ret = flb_parser_regex_do(p, source, strlen(source), &out_buf, &out_size, &out_time);
+
+ TEST_CHECK(ret != -1);
+ TEST_CHECK(out_buf != NULL);
+ if(ret < 0 || out_buf == NULL) return -1;
+
+ val001 = get_msgpack_map_key(out_buf, out_size, "key001");
+ if(!TEST_CHECK(val001 != NULL)) {
+ flb_free(out_buf);
+ return -1;
+ }
+
+ TEST_CHECK_(strcmp(val001,expected) == 0, "source(%s) expected(%s) got(%s)", source, expected, val001);
+ flb_free(val001);
+ flb_free(out_buf);
+
+ return 1;
+}
+
+
+void test_mysql_unquoted()
+{
+ struct flb_parser *p;
+ struct flb_config *config;
+
+ config = flb_config_init();
+
+ /* Load parsers */
+ load_regex_parsers(config);
+
+ p = flb_parser_get("mysql_quoted_stuff", config);
+ TEST_CHECK(p != NULL);
+
+ a_mysql_unquote_test(p,"2010-01-01 02:10:22,plain", "plain");
+ a_mysql_unquote_test(p,"2010-01-01 02:10:22,''", "");
+ a_mysql_unquote_test(p,"2010-01-01 02:10:22,'333'", "333");
+ a_mysql_unquote_test(p,"2010-01-01 02:10:22,'\\n'", "\n");
+ a_mysql_unquote_test(p,"2010-01-01 02:10:22,'\\r'", "\r");
+ a_mysql_unquote_test(p,"2010-01-01 02:10:22,'\\''", "'");
+ a_mysql_unquote_test(p,"2010-01-01 02:10:22,'\\\"'", "\"");
+ a_mysql_unquote_test(p,"2010-01-01 02:10:22,'\\\\'", "\\");
+ a_mysql_unquote_test(p,"2010-01-01 02:10:22,'abc\\nE\\\\'", "abc\nE\\");
+
+ flb_parser_exit(config);
+ flb_config_exit(config);
+
+
+}
+
+
+TEST_LIST = {
+ { "tzone_offset", test_parser_tzone_offset},
+ { "time_lookup", test_parser_time_lookup},
+ { "json_time_lookup", test_json_parser_time_lookup},
+ { "regex_time_lookup", test_regex_parser_time_lookup},
+ { "mysql_unquoted" , test_mysql_unquoted },
+ { 0 }
+};
diff --git a/fluent-bit/tests/internal/parser_json.c b/fluent-bit/tests/internal/parser_json.c
new file mode 100644
index 00000000..5a9b6ead
--- /dev/null
+++ b/fluent-bit/tests/internal/parser_json.c
@@ -0,0 +1,547 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/* Fluent Bit
+ * ==========
+ * Copyright (C) 2019-2021 The Fluent Bit Authors
+ * Copyright (C) 2015-2018 Treasure Data Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <fluent-bit/flb_config.h>
+#include <fluent-bit/flb_config_format.h>
+#include <fluent-bit/flb_parser.h>
+#include <fluent-bit/flb_parser_decoder.h>
+#include <msgpack.h>
+#include <float.h>
+#include <math.h>
+#include "flb_tests_internal.h"
+
+static int msgpack_strncmp(char* str, size_t str_len, msgpack_object obj)
+{
+ int ret = -1;
+
+ if (str == NULL) {
+ flb_error("str is NULL");
+ return -1;
+ }
+
+ switch (obj.type) {
+ case MSGPACK_OBJECT_STR:
+ if (obj.via.str.size != str_len) {
+ return -1;
+ }
+ ret = strncmp(str, obj.via.str.ptr, str_len);
+ break;
+ case MSGPACK_OBJECT_POSITIVE_INTEGER:
+ {
+ unsigned long val = strtoul(str, NULL, 10);
+ if (val == (unsigned long)obj.via.u64) {
+ ret = 0;
+ }
+ }
+ break;
+ case MSGPACK_OBJECT_NEGATIVE_INTEGER:
+ {
+ long long val = strtoll(str, NULL, 10);
+ if (val == (unsigned long)obj.via.i64) {
+ ret = 0;
+ }
+ }
+ break;
+ case MSGPACK_OBJECT_FLOAT32:
+ case MSGPACK_OBJECT_FLOAT64:
+ {
+ double val = strtod(str, NULL);
+ if (fabs(val - obj.via.f64) < DBL_EPSILON) {
+ ret = 0;
+ }
+ }
+ break;
+ case MSGPACK_OBJECT_BOOLEAN:
+ if (obj.via.boolean) {
+ if (str_len != 4 /*true*/) {
+ return -1;
+ }
+ ret = strncasecmp(str, "true", 4);
+ }
+ else {
+ if (str_len != 5 /*false*/) {
+ return -1;
+ }
+ ret = strncasecmp(str, "false", 5);
+ }
+ break;
+ default:
+ flb_error("not supported");
+ }
+
+ return ret;
+}
+
+struct str_list {
+ size_t size;
+ char **lists;
+};
+
+static int compare_msgpack(void *msgpack_data, size_t msgpack_size, struct str_list *l)
+{
+ msgpack_unpacked result;
+ msgpack_object obj;
+ size_t off = 0;
+ int map_size;
+ int i_map;
+ int i_list;
+ int num = 0;
+
+ if (!TEST_CHECK(msgpack_data != NULL)) {
+ TEST_MSG("msgpack_data is NULL");
+ return -1;
+ }
+ else if (!TEST_CHECK(msgpack_size > 0)) {
+ TEST_MSG("msgpack_size is 0");
+ return -1;
+ }
+
+ msgpack_unpacked_init(&result);
+ while (msgpack_unpack_next(&result, msgpack_data, msgpack_size, &off) == MSGPACK_UNPACK_SUCCESS) {
+ obj = result.data;
+ /*
+ msgpack_object_print(stdout, obj);
+ */
+ if (!TEST_CHECK(obj.type == MSGPACK_OBJECT_MAP)) {
+ TEST_MSG("map error. type = %d", obj.type);
+ continue;
+ }
+ map_size = obj.via.map.size;
+ for (i_map=0; i_map<map_size; i_map++) {
+ if (!TEST_CHECK(obj.via.map.ptr[i_map].key.type == MSGPACK_OBJECT_STR)) {
+ TEST_MSG("key is not string. type =%d", obj.via.map.ptr[i_map].key.type);
+ continue;
+ }
+ for (i_list=0; i_list< l->size/2; i_list++) {
+ if (msgpack_strncmp(l->lists[i_list*2], strlen(l->lists[i_list*2]),
+ obj.via.map.ptr[i_map].key) == 0 &&
+ msgpack_strncmp(l->lists[i_list*2+1], strlen(l->lists[i_list*2+1]),
+ obj.via.map.ptr[i_map].val) == 0) {
+ num++;
+ }
+ }
+ }
+ }
+ msgpack_unpacked_destroy(&result);
+ if (!TEST_CHECK(num == l->size/2)) {
+ msgpack_object_print(stdout, obj);
+ putchar('\n');
+ TEST_MSG("compare failed. matched_num=%d expect=%lu", num, l->size/2);
+ return -1;
+ }
+ return 0;
+}
+
+void test_basic()
+{
+ struct flb_parser *parser = NULL;
+ struct flb_config *config = NULL;
+ int ret = 0;
+ char *input = "{\"str\":\"text\", \"int\":100, \"double\":1.23, \"bool\":true}";
+ void *out_buf = NULL;
+ size_t out_size = 0;
+ struct flb_time out_time;
+ char *expected_strs[] = {"str", "text", "int", "100", "double","1.23", "bool", "true"};
+ struct str_list expected = {
+ .size = sizeof(expected_strs)/sizeof(char*),
+ .lists = &expected_strs[0],
+ };
+
+ config = flb_config_init();
+ if(!TEST_CHECK(config != NULL)) {
+ TEST_MSG("flb_config_init failed");
+ exit(1);
+ }
+
+ parser = flb_parser_create("json", "json", NULL, FLB_FALSE, NULL, NULL, NULL,
+ FLB_FALSE, FLB_FALSE, FLB_FALSE,
+ NULL, 0, NULL, config);
+ if (!TEST_CHECK(parser != NULL)) {
+ TEST_MSG("flb_parser_create failed");
+ flb_config_exit(config);
+ exit(1);
+ }
+
+ ret = flb_parser_do(parser, input, strlen(input), &out_buf, &out_size, &out_time);
+ if (!TEST_CHECK(ret != -1)) {
+ TEST_MSG("flb_parser_do failed");
+ flb_parser_destroy(parser);
+ flb_config_exit(config);
+ exit(1);
+ }
+
+ ret = compare_msgpack(out_buf, out_size, &expected);
+ if (!TEST_CHECK(ret == 0)) {
+ TEST_MSG("compare failed");
+ }
+
+ flb_free(out_buf);
+ flb_parser_destroy(parser);
+ flb_config_exit(config);
+}
+
+void test_time_key()
+{
+ struct flb_parser *parser = NULL;
+ struct flb_config *config = NULL;
+ int ret = 0;
+ char *input = "{\"str\":\"text\", \"int\":100, \"double\":1.23, \"bool\":true, \"time\":\"2022-10-31T12:00:01.123\"}";
+ void *out_buf = NULL;
+ size_t out_size = 0;
+ struct flb_time out_time;
+ char *expected_strs[] = {"str", "text", "int", "100", "double","1.23", "bool", "true"};
+ struct str_list expected = {
+ .size = sizeof(expected_strs)/sizeof(char*),
+ .lists = &expected_strs[0],
+ };
+
+ out_time.tm.tv_sec = 0;
+ out_time.tm.tv_nsec = 0;
+
+
+ config = flb_config_init();
+ if(!TEST_CHECK(config != NULL)) {
+ TEST_MSG("flb_config_init failed");
+ exit(1);
+ }
+
+ parser = flb_parser_create("json", "json", NULL, FLB_FALSE, "%Y-%m-%dT%H:%M:%S.%L", "time", NULL,
+ FLB_FALSE, FLB_FALSE, FLB_FALSE,
+ NULL, 0, NULL, config);
+ if (!TEST_CHECK(parser != NULL)) {
+ TEST_MSG("flb_parser_create failed");
+ flb_config_exit(config);
+ exit(1);
+ }
+
+ ret = flb_parser_do(parser, input, strlen(input), &out_buf, &out_size, &out_time);
+ if (!TEST_CHECK(ret != -1)) {
+ TEST_MSG("flb_parser_do failed");
+ flb_parser_destroy(parser);
+ flb_config_exit(config);
+ exit(1);
+ }
+
+ ret = compare_msgpack(out_buf, out_size, &expected);
+ if (!TEST_CHECK(ret == 0)) {
+ TEST_MSG("compare failed");
+ flb_free(out_buf);
+ flb_parser_destroy(parser);
+ flb_config_exit(config);
+ exit(1);
+ }
+
+ if (!TEST_CHECK(out_time.tm.tv_sec == 1667217601 && out_time.tm.tv_nsec == 123000000)) {
+ TEST_MSG("timestamp error. sec Got=%ld Expect=1667217601", out_time.tm.tv_sec);
+ TEST_MSG("timestamp error. nsec Got=%ld Expect=123000000", out_time.tm.tv_nsec);
+ }
+
+ flb_free(out_buf);
+ flb_parser_destroy(parser);
+ flb_config_exit(config);
+}
+
+void test_time_keep()
+{
+ struct flb_parser *parser = NULL;
+ struct flb_config *config = NULL;
+ int ret = 0;
+ char *input = "{\"str\":\"text\", \"int\":100, \"double\":1.23, \"bool\":true, \"time\":\"2022-10-31T12:00:01.123\"}";
+ void *out_buf = NULL;
+ size_t out_size = 0;
+ struct flb_time out_time;
+ char *expected_strs[] = {"str", "text", "int", "100", "double","1.23", "bool", "true", "time", "2022-10-31T12:00:01.123"};
+ struct str_list expected = {
+ .size = sizeof(expected_strs)/sizeof(char*),
+ .lists = &expected_strs[0],
+ };
+
+ out_time.tm.tv_sec = 0;
+ out_time.tm.tv_nsec = 0;
+
+
+ config = flb_config_init();
+ if(!TEST_CHECK(config != NULL)) {
+ TEST_MSG("flb_config_init failed");
+ exit(1);
+ }
+
+ parser = flb_parser_create("json", "json", NULL, FLB_FALSE, "%Y-%m-%dT%H:%M:%S.%L", "time", NULL,
+ FLB_TRUE /*time_keep */, FLB_FALSE, FLB_FALSE,
+ NULL, 0, NULL, config);
+ if (!TEST_CHECK(parser != NULL)) {
+ TEST_MSG("flb_parser_create failed");
+ flb_config_exit(config);
+ exit(1);
+ }
+
+ ret = flb_parser_do(parser, input, strlen(input), &out_buf, &out_size, &out_time);
+ if (!TEST_CHECK(ret != -1)) {
+ TEST_MSG("flb_parser_do failed");
+ flb_parser_destroy(parser);
+ flb_config_exit(config);
+ exit(1);
+ }
+
+ ret = compare_msgpack(out_buf, out_size, &expected);
+ if (!TEST_CHECK(ret == 0)) {
+ TEST_MSG("compare failed");
+ flb_free(out_buf);
+ flb_parser_destroy(parser);
+ flb_config_exit(config);
+ exit(1);
+ }
+
+ if (!TEST_CHECK(out_time.tm.tv_sec == 1667217601 && out_time.tm.tv_nsec == 123000000)) {
+ TEST_MSG("timestamp error. sec Got=%ld Expect=1667217601", out_time.tm.tv_sec);
+ TEST_MSG("timestamp error. nsec Got=%ld Expect=123000000", out_time.tm.tv_nsec);
+ }
+
+ flb_free(out_buf);
+ flb_parser_destroy(parser);
+ flb_config_exit(config);
+}
+
+/*
+ * JSON parser doesn't support 'types' option.
+ * This test is to check that 'types' doesn't affect output.
+ */
+void test_types_is_not_supported()
+{
+ struct flb_parser *parser = NULL;
+ struct flb_config *config = NULL;
+ int ret = 0;
+ char *input = "{\"str\":\"text\", \"int\":100, \"double\":1.23, \"bool\":true}";
+ struct flb_parser_types *types = NULL;
+
+ void *out_buf = NULL;
+ size_t out_size = 0;
+ struct flb_time out_time;
+ char *expected_strs[] = {"str", "text", "int", "100", "double","1.23", "bool", "true"};
+ struct str_list expected = {
+ .size = sizeof(expected_strs)/sizeof(char*),
+ .lists = &expected_strs[0],
+ };
+
+ config = flb_config_init();
+ if(!TEST_CHECK(config != NULL)) {
+ TEST_MSG("flb_config_init failed");
+ exit(1);
+ }
+
+ /* Note: types will be released by flb_parser_destroy */
+ types = flb_malloc(sizeof(struct flb_parser_types));
+ if (!TEST_CHECK(types != NULL)) {
+ TEST_MSG("flb_malloc failed");
+ flb_config_exit(config);
+ exit(1);
+ }
+ types->key = flb_malloc(strlen("int")+1);
+ if (!TEST_CHECK(types->key != NULL)) {
+ TEST_MSG("flb_malloc failed");
+ flb_free(types);
+ flb_config_exit(config);
+ exit(1);
+ }
+ strcpy(types->key, "int");
+ types->key_len = 3;
+ types->type = FLB_PARSER_TYPE_HEX;
+
+ parser = flb_parser_create("json", "json", NULL, FLB_FALSE, NULL, NULL, NULL,
+ FLB_FALSE, FLB_FALSE, FLB_FALSE,
+ types, 1, NULL, config);
+ if (!TEST_CHECK(parser != NULL)) {
+ TEST_MSG("flb_parser_create failed");
+ flb_free(types->key);
+ flb_free(types);
+ flb_config_exit(config);
+ exit(1);
+ }
+
+ ret = flb_parser_do(parser, input, strlen(input), &out_buf, &out_size, &out_time);
+ if (!TEST_CHECK(ret != -1)) {
+ TEST_MSG("flb_parser_do failed");
+ flb_parser_destroy(parser);
+ flb_config_exit(config);
+ exit(1);
+ }
+
+ ret = compare_msgpack(out_buf, out_size, &expected);
+ if (!TEST_CHECK(ret == 0)) {
+ TEST_MSG("compare failed");
+ }
+
+ flb_free(out_buf);
+ flb_parser_destroy(parser);
+ flb_config_exit(config);
+}
+
+void test_decode_field_json()
+{
+ struct flb_parser *parser = NULL;
+ struct flb_config *config = NULL;
+ struct cfl_variant *var = NULL;
+ int ret = 0;
+ char *input = "{\"json_str\":\"{\\\"str\\\":\\\"text\\\", \\\"int\\\":100, \\\"double\\\":1.23, \\\"bool\\\":true}\"}";
+ struct flb_cf *cf = NULL;
+ struct flb_cf_section *section = NULL;
+ struct mk_list *decoder = NULL;
+
+ void *out_buf = NULL;
+ size_t out_size = 0;
+ struct flb_time out_time;
+ char *expected_strs[] = {"str", "text", "int", "100", "double","1.23", "bool", "true"};
+ struct str_list expected = {
+ .size = sizeof(expected_strs)/sizeof(char*),
+ .lists = &expected_strs[0],
+ };
+
+ config = flb_config_init();
+ if(!TEST_CHECK(config != NULL)) {
+ TEST_MSG("flb_config_init failed");
+ exit(1);
+ }
+ cf = flb_cf_create();
+ if (!TEST_CHECK(cf != NULL)) {
+ TEST_MSG("flb_cf_create failed");
+ flb_config_exit(config);
+ exit(1);
+ }
+
+ section = flb_cf_section_create(cf, "TEST", 4);
+ if (!TEST_CHECK(section != NULL)) {
+ TEST_MSG("flb_cf_section_create failed");
+ flb_cf_destroy(cf);
+ flb_config_exit(config);
+ exit(1);
+ }
+
+ var = flb_cf_section_property_add(cf, section->properties, "decode_field", 12, "json json_str", 13);
+ if(!TEST_CHECK(var != NULL)) {
+ TEST_MSG("flb_cf_section_property_add failed");
+ flb_cf_destroy(cf);
+ flb_config_exit(config);
+ exit(1);
+ }
+
+ decoder = flb_parser_decoder_list_create(section);
+ if (!TEST_CHECK(decoder != NULL)) {
+ TEST_MSG("flb_parser_decoder_list_create failed");
+ flb_cf_destroy(cf);
+ flb_config_exit(config);
+ exit(1);
+ }
+
+ parser = flb_parser_create("json", "json", NULL, FLB_FALSE, NULL, NULL, NULL,
+ FLB_FALSE, FLB_FALSE, FLB_FALSE,
+ NULL, 0, decoder, config);
+ if (!TEST_CHECK(parser != NULL)) {
+ TEST_MSG("flb_parser_create failed");
+ flb_cf_destroy(cf);
+ flb_config_exit(config);
+ exit(1);
+ }
+
+ ret = flb_parser_do(parser, input, strlen(input), &out_buf, &out_size, &out_time);
+ if (!TEST_CHECK(ret != -1)) {
+ TEST_MSG("flb_parser_do failed");
+ flb_parser_destroy(parser);
+ flb_cf_destroy(cf);
+ flb_config_exit(config);
+ exit(1);
+ }
+
+ ret = compare_msgpack(out_buf, out_size, &expected);
+ if (!TEST_CHECK(ret == 0)) {
+ TEST_MSG("compare failed");
+ }
+
+ flb_free(out_buf);
+ flb_parser_destroy(parser);
+ flb_cf_destroy(cf);
+ flb_config_exit(config);
+}
+
+void test_time_key_kept_if_parse_fails()
+{
+ struct flb_parser *parser = NULL;
+ struct flb_config *config = NULL;
+ int ret = 0;
+ char *input = "{\"str\":\"text\", \"time\":\"nonsense\"}";
+ char *time_format = "%Y-%m-%dT%H:%M:%S.%L";
+ void *out_buf = NULL;
+ size_t out_size = 0;
+ struct flb_time out_time;
+ char *expected_strs[] = {"str", "text", "time", "nonsense"};
+ struct str_list expected = {
+ .size = sizeof(expected_strs)/sizeof(char*),
+ .lists = &expected_strs[0],
+ };
+
+ out_time.tm.tv_sec = 0;
+ out_time.tm.tv_nsec = 0;
+
+ config = flb_config_init();
+ if(!TEST_CHECK(config != NULL)) {
+ TEST_MSG("flb_config_init failed");
+ exit(1);
+ }
+
+ parser = flb_parser_create("json", "json", NULL, FLB_FALSE, time_format, "time", NULL,
+ FLB_FALSE, FLB_TRUE, FLB_FALSE,
+ NULL, 0, NULL, config);
+ if (!TEST_CHECK(parser != NULL)) {
+ TEST_MSG("flb_parser_create failed");
+ flb_config_exit(config);
+ exit(1);
+ }
+
+ ret = flb_parser_do(parser, input, strlen(input), &out_buf, &out_size, &out_time);
+ if (!TEST_CHECK(ret != -1)) {
+ TEST_MSG("flb_parser_do failed");
+ flb_parser_destroy(parser);
+ flb_config_exit(config);
+ exit(1);
+ }
+
+ ret = compare_msgpack(out_buf, out_size, &expected);
+ if (!TEST_CHECK(ret == 0)) {
+ TEST_MSG("compare failed");
+ flb_free(out_buf);
+ flb_parser_destroy(parser);
+ flb_config_exit(config);
+ exit(1);
+ }
+
+ flb_free(out_buf);
+ flb_parser_destroy(parser);
+ flb_config_exit(config);
+}
+
+
+TEST_LIST = {
+ { "basic", test_basic},
+ { "time_key", test_time_key},
+ { "time_keep", test_time_keep},
+ { "types_is_not_supported", test_types_is_not_supported},
+ { "decode_field_json", test_decode_field_json},
+ { "time_key_kept_if_parse_fails", test_time_key_kept_if_parse_fails},
+ { 0 }
+};
diff --git a/fluent-bit/tests/internal/parser_logfmt.c b/fluent-bit/tests/internal/parser_logfmt.c
new file mode 100644
index 00000000..ea363c53
--- /dev/null
+++ b/fluent-bit/tests/internal/parser_logfmt.c
@@ -0,0 +1,398 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/* Fluent Bit
+ * ==========
+ * Copyright (C) 2019-2021 The Fluent Bit Authors
+ * Copyright (C) 2015-2018 Treasure Data Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <fluent-bit/flb_config.h>
+#include <fluent-bit/flb_config_format.h>
+#include <fluent-bit/flb_parser.h>
+#include <fluent-bit/flb_parser_decoder.h>
+#include <msgpack.h>
+#include <float.h>
+#include <math.h>
+#include "flb_tests_internal.h"
+
+static int msgpack_strncmp(char* str, size_t str_len, msgpack_object obj)
+{
+ int ret = -1;
+
+ if (str == NULL) {
+ flb_error("str is NULL");
+ return -1;
+ }
+
+ switch (obj.type) {
+ case MSGPACK_OBJECT_STR:
+ if (obj.via.str.size != str_len) {
+ return -1;
+ }
+ ret = strncmp(str, obj.via.str.ptr, str_len);
+ break;
+ case MSGPACK_OBJECT_POSITIVE_INTEGER:
+ {
+ unsigned long val = strtoul(str, NULL, 10);
+ if (val == (unsigned long)obj.via.u64) {
+ ret = 0;
+ }
+ }
+ break;
+ case MSGPACK_OBJECT_NEGATIVE_INTEGER:
+ {
+ long long val = strtoll(str, NULL, 10);
+ if (val == (unsigned long)obj.via.i64) {
+ ret = 0;
+ }
+ }
+ break;
+ case MSGPACK_OBJECT_FLOAT32:
+ case MSGPACK_OBJECT_FLOAT64:
+ {
+ double val = strtod(str, NULL);
+ if (fabs(val - obj.via.f64) < DBL_EPSILON) {
+ ret = 0;
+ }
+ }
+ break;
+ case MSGPACK_OBJECT_BOOLEAN:
+ if (obj.via.boolean) {
+ if (str_len != 4 /*true*/) {
+ return -1;
+ }
+ ret = strncasecmp(str, "true", 4);
+ }
+ else {
+ if (str_len != 5 /*false*/) {
+ return -1;
+ }
+ ret = strncasecmp(str, "false", 5);
+ }
+ break;
+ default:
+ flb_error("not supported");
+ }
+
+ return ret;
+}
+
+struct str_list {
+ size_t size;
+ char **lists;
+};
+
+static int compare_msgpack(void *msgpack_data, size_t msgpack_size, struct str_list *l)
+{
+ msgpack_unpacked result;
+ msgpack_object obj;
+ size_t off = 0;
+ int map_size;
+ int i_map;
+ int i_list;
+ int num = 0;
+
+ if (!TEST_CHECK(msgpack_data != NULL)) {
+ TEST_MSG("msgpack_data is NULL");
+ return -1;
+ }
+ else if (!TEST_CHECK(msgpack_size > 0)) {
+ TEST_MSG("msgpack_size is 0");
+ return -1;
+ }
+
+ msgpack_unpacked_init(&result);
+ while (msgpack_unpack_next(&result, msgpack_data, msgpack_size, &off) == MSGPACK_UNPACK_SUCCESS) {
+ obj = result.data;
+ /*
+ msgpack_object_print(stdout, obj);
+ */
+ if (!TEST_CHECK(obj.type == MSGPACK_OBJECT_MAP)) {
+ TEST_MSG("map error. type = %d", obj.type);
+ continue;
+ }
+ map_size = obj.via.map.size;
+ for (i_map=0; i_map<map_size; i_map++) {
+ if (!TEST_CHECK(obj.via.map.ptr[i_map].key.type == MSGPACK_OBJECT_STR)) {
+ TEST_MSG("key is not string. type =%d", obj.via.map.ptr[i_map].key.type);
+ continue;
+ }
+ for (i_list=0; i_list< l->size/2; i_list++) {
+ if (msgpack_strncmp(l->lists[i_list*2], strlen(l->lists[i_list*2]),
+ obj.via.map.ptr[i_map].key) == 0 &&
+ msgpack_strncmp(l->lists[i_list*2+1], strlen(l->lists[i_list*2+1]),
+ obj.via.map.ptr[i_map].val) == 0) {
+ num++;
+ }
+ }
+ }
+ }
+ msgpack_unpacked_destroy(&result);
+ if (!TEST_CHECK(num == l->size/2)) {
+ msgpack_object_print(stdout, obj);
+ putchar('\n');
+ TEST_MSG("compare failed. matched_num=%d expect=%lu", num, l->size/2);
+ return -1;
+ }
+ return 0;
+}
+
+void test_basic()
+{
+ struct flb_parser *parser = NULL;
+ struct flb_config *config = NULL;
+ int ret = 0;
+ char *input = "str=\"text\" int=100 double=1.23 bool=true";
+ void *out_buf = NULL;
+ size_t out_size = 0;
+ struct flb_time out_time;
+ char *expected_strs[] = {"str", "text", "int", "100", "double","1.23", "bool", "true"};
+ struct str_list expected = {
+ .size = sizeof(expected_strs)/sizeof(char*),
+ .lists = &expected_strs[0],
+ };
+
+ config = flb_config_init();
+ if(!TEST_CHECK(config != NULL)) {
+ TEST_MSG("flb_config_init failed");
+ exit(1);
+ }
+
+ parser = flb_parser_create("logfmt", "logfmt", NULL, FLB_FALSE, NULL, NULL, NULL,
+ FLB_FALSE, FLB_FALSE, FLB_FALSE,
+ NULL, 0, NULL, config);
+ if (!TEST_CHECK(parser != NULL)) {
+ TEST_MSG("flb_parser_create failed");
+ flb_config_exit(config);
+ exit(1);
+ }
+
+ ret = flb_parser_do(parser, input, strlen(input), &out_buf, &out_size, &out_time);
+ if (!TEST_CHECK(ret != -1)) {
+ TEST_MSG("flb_parser_do failed");
+ flb_parser_destroy(parser);
+ flb_config_exit(config);
+ exit(1);
+ }
+
+ ret = compare_msgpack(out_buf, out_size, &expected);
+ if (!TEST_CHECK(ret == 0)) {
+ TEST_MSG("compare failed");
+ }
+
+ flb_free(out_buf);
+ flb_parser_destroy(parser);
+ flb_config_exit(config);
+}
+
+void test_time_key()
+{
+ struct flb_parser *parser = NULL;
+ struct flb_config *config = NULL;
+ int ret = 0;
+ char *input = "str=\"text\" int=100 double=1.23 bool=true time=2022-10-31T12:00:01.123";
+ void *out_buf = NULL;
+ size_t out_size = 0;
+ struct flb_time out_time;
+ char *expected_strs[] = {"str", "text", "int", "100", "double","1.23", "bool", "true"};
+ struct str_list expected = {
+ .size = sizeof(expected_strs)/sizeof(char*),
+ .lists = &expected_strs[0],
+ };
+
+ out_time.tm.tv_sec = 0;
+ out_time.tm.tv_nsec = 0;
+
+
+ config = flb_config_init();
+ if(!TEST_CHECK(config != NULL)) {
+ TEST_MSG("flb_config_init failed");
+ exit(1);
+ }
+
+ parser = flb_parser_create("logfmt", "logfmt", NULL, FLB_FALSE, "%Y-%m-%dT%H:%M:%S.%L", "time", NULL,
+ FLB_FALSE, FLB_FALSE, FLB_FALSE,
+ NULL, 0, NULL, config);
+ if (!TEST_CHECK(parser != NULL)) {
+ TEST_MSG("flb_parser_create failed");
+ flb_config_exit(config);
+ exit(1);
+ }
+
+ ret = flb_parser_do(parser, input, strlen(input), &out_buf, &out_size, &out_time);
+ if (!TEST_CHECK(ret != -1)) {
+ TEST_MSG("flb_parser_do failed");
+ flb_parser_destroy(parser);
+ flb_config_exit(config);
+ exit(1);
+ }
+
+ ret = compare_msgpack(out_buf, out_size, &expected);
+ if (!TEST_CHECK(ret == 0)) {
+ TEST_MSG("compare failed");
+ flb_free(out_buf);
+ flb_parser_destroy(parser);
+ flb_config_exit(config);
+ exit(1);
+ }
+
+ if (!TEST_CHECK(out_time.tm.tv_sec == 1667217601 && out_time.tm.tv_nsec == 123000000)) {
+ TEST_MSG("timestamp error. sec Got=%ld Expect=1667217601", out_time.tm.tv_sec);
+ TEST_MSG("timestamp error. nsec Got=%ld Expect=123000000", out_time.tm.tv_nsec);
+ }
+
+ flb_free(out_buf);
+ flb_parser_destroy(parser);
+ flb_config_exit(config);
+}
+
+void test_time_keep()
+{
+ struct flb_parser *parser = NULL;
+ struct flb_config *config = NULL;
+ int ret = 0;
+ char *input = "str=\"text\" int=100 double=1.23 bool=true time=2022-10-31T12:00:01.123";
+ void *out_buf = NULL;
+ size_t out_size = 0;
+ struct flb_time out_time;
+ char *expected_strs[] = {"str", "text", "int", "100", "double","1.23", "bool", "true", "time", "2022-10-31T12:00:01.123"};
+ struct str_list expected = {
+ .size = sizeof(expected_strs)/sizeof(char*),
+ .lists = &expected_strs[0],
+ };
+
+ out_time.tm.tv_sec = 0;
+ out_time.tm.tv_nsec = 0;
+
+
+ config = flb_config_init();
+ if(!TEST_CHECK(config != NULL)) {
+ TEST_MSG("flb_config_init failed");
+ exit(1);
+ }
+
+ parser = flb_parser_create("logfmt", "logfmt", NULL, FLB_FALSE, "%Y-%m-%dT%H:%M:%S.%L", "time", NULL,
+ FLB_TRUE /*time_keep */, FLB_FALSE, FLB_FALSE,
+ NULL, 0, NULL, config);
+ if (!TEST_CHECK(parser != NULL)) {
+ TEST_MSG("flb_parser_create failed");
+ flb_config_exit(config);
+ exit(1);
+ }
+
+ ret = flb_parser_do(parser, input, strlen(input), &out_buf, &out_size, &out_time);
+ if (!TEST_CHECK(ret != -1)) {
+ TEST_MSG("flb_parser_do failed");
+ flb_parser_destroy(parser);
+ flb_config_exit(config);
+ exit(1);
+ }
+
+ ret = compare_msgpack(out_buf, out_size, &expected);
+ if (!TEST_CHECK(ret == 0)) {
+ TEST_MSG("compare failed");
+ flb_free(out_buf);
+ flb_parser_destroy(parser);
+ flb_config_exit(config);
+ exit(1);
+ }
+
+ if (!TEST_CHECK(out_time.tm.tv_sec == 1667217601 && out_time.tm.tv_nsec == 123000000)) {
+ TEST_MSG("timestamp error. sec Got=%ld Expect=1667217601", out_time.tm.tv_sec);
+ TEST_MSG("timestamp error. nsec Got=%ld Expect=123000000", out_time.tm.tv_nsec);
+ }
+
+ flb_free(out_buf);
+ flb_parser_destroy(parser);
+ flb_config_exit(config);
+}
+
+void test_types()
+{
+ struct flb_parser *parser = NULL;
+ struct flb_config *config = NULL;
+ int ret = 0;
+ char *input = "str=\"text\" int=100 double=1.23 bool=true";
+ struct flb_parser_types *types = NULL;
+
+ void *out_buf = NULL;
+ size_t out_size = 0;
+ struct flb_time out_time;
+ char *expected_strs[] = {"str", "text", "int", "256" /*= 0x100 */, "double","1.23", "bool", "true"};
+ struct str_list expected = {
+ .size = sizeof(expected_strs)/sizeof(char*),
+ .lists = &expected_strs[0],
+ };
+
+ config = flb_config_init();
+ if(!TEST_CHECK(config != NULL)) {
+ TEST_MSG("flb_config_init failed");
+ exit(1);
+ }
+
+ /* Note: types will be released by flb_parser_destroy */
+ types = flb_malloc(sizeof(struct flb_parser_types));
+ if (!TEST_CHECK(types != NULL)) {
+ TEST_MSG("flb_malloc failed");
+ flb_config_exit(config);
+ exit(1);
+ }
+ types->key = flb_malloc(strlen("int")+1);
+ if (!TEST_CHECK(types->key != NULL)) {
+ TEST_MSG("flb_malloc failed");
+ flb_free(types);
+ flb_config_exit(config);
+ exit(1);
+ }
+ strcpy(types->key, "int");
+ types->key_len = 3;
+ types->type = FLB_PARSER_TYPE_HEX;
+
+ parser = flb_parser_create("logfmt", "logfmt", NULL, FLB_FALSE, NULL, NULL, NULL,
+ FLB_FALSE, FLB_FALSE, FLB_FALSE,
+ types, 1, NULL, config);
+ if (!TEST_CHECK(parser != NULL)) {
+ TEST_MSG("flb_parser_create failed");
+ flb_free(types->key);
+ flb_free(types);
+ flb_config_exit(config);
+ exit(1);
+ }
+
+ ret = flb_parser_do(parser, input, strlen(input), &out_buf, &out_size, &out_time);
+ if (!TEST_CHECK(ret != -1)) {
+ TEST_MSG("flb_parser_do failed");
+ flb_parser_destroy(parser);
+ flb_config_exit(config);
+ exit(1);
+ }
+
+ ret = compare_msgpack(out_buf, out_size, &expected);
+ if (!TEST_CHECK(ret == 0)) {
+ TEST_MSG("compare failed");
+ }
+
+ flb_free(out_buf);
+ flb_parser_destroy(parser);
+ flb_config_exit(config);
+}
+
+TEST_LIST = {
+ { "basic", test_basic},
+ { "time_key", test_time_key},
+ { "time_keep", test_time_keep},
+ { "types", test_types},
+ { 0 }
+};
diff --git a/fluent-bit/tests/internal/parser_ltsv.c b/fluent-bit/tests/internal/parser_ltsv.c
new file mode 100644
index 00000000..3f917051
--- /dev/null
+++ b/fluent-bit/tests/internal/parser_ltsv.c
@@ -0,0 +1,486 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/* Fluent Bit
+ * ==========
+ * Copyright (C) 2019-2021 The Fluent Bit Authors
+ * Copyright (C) 2015-2018 Treasure Data Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <fluent-bit/flb_config.h>
+#include <fluent-bit/flb_config_format.h>
+#include <fluent-bit/flb_parser.h>
+#include <fluent-bit/flb_parser_decoder.h>
+#include <msgpack.h>
+#include <float.h>
+#include <math.h>
+#include "flb_tests_internal.h"
+
+static int msgpack_strncmp(char* str, size_t str_len, msgpack_object obj)
+{
+ int ret = -1;
+
+ if (str == NULL) {
+ flb_error("str is NULL");
+ return -1;
+ }
+
+ switch (obj.type) {
+ case MSGPACK_OBJECT_STR:
+ if (obj.via.str.size != str_len) {
+ return -1;
+ }
+ ret = strncmp(str, obj.via.str.ptr, str_len);
+ break;
+ case MSGPACK_OBJECT_POSITIVE_INTEGER:
+ {
+ unsigned long val = strtoul(str, NULL, 10);
+ if (val == (unsigned long)obj.via.u64) {
+ ret = 0;
+ }
+ }
+ break;
+ case MSGPACK_OBJECT_NEGATIVE_INTEGER:
+ {
+ long long val = strtoll(str, NULL, 10);
+ if (val == (unsigned long)obj.via.i64) {
+ ret = 0;
+ }
+ }
+ break;
+ case MSGPACK_OBJECT_FLOAT32:
+ case MSGPACK_OBJECT_FLOAT64:
+ {
+ double val = strtod(str, NULL);
+ if (fabs(val - obj.via.f64) < DBL_EPSILON) {
+ ret = 0;
+ }
+ }
+ break;
+ case MSGPACK_OBJECT_BOOLEAN:
+ if (obj.via.boolean) {
+ if (str_len != 4 /*true*/) {
+ return -1;
+ }
+ ret = strncasecmp(str, "true", 4);
+ }
+ else {
+ if (str_len != 5 /*false*/) {
+ return -1;
+ }
+ ret = strncasecmp(str, "false", 5);
+ }
+ break;
+ default:
+ flb_error("not supported");
+ }
+
+ return ret;
+}
+
+struct str_list {
+ size_t size;
+ char **lists;
+};
+
+static int compare_msgpack(void *msgpack_data, size_t msgpack_size, struct str_list *l)
+{
+ msgpack_unpacked result;
+ msgpack_object obj;
+ size_t off = 0;
+ int map_size;
+ int i_map;
+ int i_list;
+ int num = 0;
+
+ if (!TEST_CHECK(msgpack_data != NULL)) {
+ TEST_MSG("msgpack_data is NULL");
+ return -1;
+ }
+ else if (!TEST_CHECK(msgpack_size > 0)) {
+ TEST_MSG("msgpack_size is 0");
+ return -1;
+ }
+
+ msgpack_unpacked_init(&result);
+ while (msgpack_unpack_next(&result, msgpack_data, msgpack_size, &off) == MSGPACK_UNPACK_SUCCESS) {
+ obj = result.data;
+ /*
+ msgpack_object_print(stdout, obj);
+ */
+ if (!TEST_CHECK(obj.type == MSGPACK_OBJECT_MAP)) {
+ TEST_MSG("map error. type = %d", obj.type);
+ continue;
+ }
+ map_size = obj.via.map.size;
+ for (i_map=0; i_map<map_size; i_map++) {
+ if (!TEST_CHECK(obj.via.map.ptr[i_map].key.type == MSGPACK_OBJECT_STR)) {
+ TEST_MSG("key is not string. type =%d", obj.via.map.ptr[i_map].key.type);
+ continue;
+ }
+ for (i_list=0; i_list< l->size/2; i_list++) {
+ if (msgpack_strncmp(l->lists[i_list*2], strlen(l->lists[i_list*2]),
+ obj.via.map.ptr[i_map].key) == 0 &&
+ msgpack_strncmp(l->lists[i_list*2+1], strlen(l->lists[i_list*2+1]),
+ obj.via.map.ptr[i_map].val) == 0) {
+ num++;
+ }
+ }
+ }
+ }
+ msgpack_unpacked_destroy(&result);
+ if (!TEST_CHECK(num == l->size/2)) {
+ msgpack_object_print(stdout, obj);
+ putchar('\n');
+ TEST_MSG("compare failed. matched_num=%d expect=%lu", num, l->size/2);
+ return -1;
+ }
+ return 0;
+}
+
+void test_basic()
+{
+ struct flb_parser *parser = NULL;
+ struct flb_config *config = NULL;
+ int ret = 0;
+ char *input = "str:text\tint:100\tdouble:1.23\tbool:true";
+ void *out_buf = NULL;
+ size_t out_size = 0;
+ struct flb_time out_time;
+ char *expected_strs[] = {"str", "text", "int", "100", "double","1.23", "bool", "true"};
+ struct str_list expected = {
+ .size = sizeof(expected_strs)/sizeof(char*),
+ .lists = &expected_strs[0],
+ };
+
+ config = flb_config_init();
+ if(!TEST_CHECK(config != NULL)) {
+ TEST_MSG("flb_config_init failed");
+ exit(1);
+ }
+
+ parser = flb_parser_create("ltsv", "ltsv", NULL, FLB_FALSE, NULL, NULL, NULL,
+ FLB_FALSE, FLB_FALSE, FLB_FALSE,
+ NULL, 0, NULL, config);
+ if (!TEST_CHECK(parser != NULL)) {
+ TEST_MSG("flb_parser_create failed");
+ flb_config_exit(config);
+ exit(1);
+ }
+
+ ret = flb_parser_do(parser, input, strlen(input), &out_buf, &out_size, &out_time);
+ if (!TEST_CHECK(ret != -1)) {
+ TEST_MSG("flb_parser_do failed");
+ flb_parser_destroy(parser);
+ flb_config_exit(config);
+ exit(1);
+ }
+
+ ret = compare_msgpack(out_buf, out_size, &expected);
+ if (!TEST_CHECK(ret == 0)) {
+ TEST_MSG("compare failed");
+ }
+
+ flb_free(out_buf);
+ flb_parser_destroy(parser);
+ flb_config_exit(config);
+}
+
+void test_time_key()
+{
+ struct flb_parser *parser = NULL;
+ struct flb_config *config = NULL;
+ int ret = 0;
+ char *input = "str:text\tint:100\tdouble:1.23\tbool:true\ttime:2022-10-31T12:00:01.123";
+ void *out_buf = NULL;
+ size_t out_size = 0;
+ struct flb_time out_time;
+ char *expected_strs[] = {"str", "text", "int", "100", "double","1.23", "bool", "true"};
+ struct str_list expected = {
+ .size = sizeof(expected_strs)/sizeof(char*),
+ .lists = &expected_strs[0],
+ };
+
+ out_time.tm.tv_sec = 0;
+ out_time.tm.tv_nsec = 0;
+
+
+ config = flb_config_init();
+ if(!TEST_CHECK(config != NULL)) {
+ TEST_MSG("flb_config_init failed");
+ exit(1);
+ }
+
+ parser = flb_parser_create("ltsv", "ltsv", NULL, FLB_FALSE, "%Y-%m-%dT%H:%M:%S.%L", "time", NULL,
+ FLB_FALSE, FLB_FALSE, FLB_FALSE,
+ NULL, 0, NULL, config);
+ if (!TEST_CHECK(parser != NULL)) {
+ TEST_MSG("flb_parser_create failed");
+ flb_config_exit(config);
+ exit(1);
+ }
+
+ ret = flb_parser_do(parser, input, strlen(input), &out_buf, &out_size, &out_time);
+ if (!TEST_CHECK(ret != -1)) {
+ TEST_MSG("flb_parser_do failed");
+ flb_parser_destroy(parser);
+ flb_config_exit(config);
+ exit(1);
+ }
+
+ ret = compare_msgpack(out_buf, out_size, &expected);
+ if (!TEST_CHECK(ret == 0)) {
+ TEST_MSG("compare failed");
+ flb_free(out_buf);
+ flb_parser_destroy(parser);
+ flb_config_exit(config);
+ exit(1);
+ }
+
+ if (!TEST_CHECK(out_time.tm.tv_sec == 1667217601 && out_time.tm.tv_nsec == 123000000)) {
+ TEST_MSG("timestamp error. sec Got=%ld Expect=1667217601", out_time.tm.tv_sec);
+ TEST_MSG("timestamp error. nsec Got=%ld Expect=123000000", out_time.tm.tv_nsec);
+ }
+
+ flb_free(out_buf);
+ flb_parser_destroy(parser);
+ flb_config_exit(config);
+}
+
+void test_time_keep()
+{
+ struct flb_parser *parser = NULL;
+ struct flb_config *config = NULL;
+ int ret = 0;
+ char *input = "str:text\tint:100\tdouble:1.23\tbool:true\ttime:2022-10-31T12:00:01.123";
+ void *out_buf = NULL;
+ size_t out_size = 0;
+ struct flb_time out_time;
+ char *expected_strs[] = {"str", "text", "int", "100", "double","1.23", "bool", "true", "time", "2022-10-31T12:00:01.123"};
+ struct str_list expected = {
+ .size = sizeof(expected_strs)/sizeof(char*),
+ .lists = &expected_strs[0],
+ };
+
+ out_time.tm.tv_sec = 0;
+ out_time.tm.tv_nsec = 0;
+
+
+ config = flb_config_init();
+ if(!TEST_CHECK(config != NULL)) {
+ TEST_MSG("flb_config_init failed");
+ exit(1);
+ }
+
+ parser = flb_parser_create("ltsv", "ltsv", NULL, FLB_FALSE, "%Y-%m-%dT%H:%M:%S.%L", "time", NULL,
+ FLB_TRUE /*time_keep */, FLB_FALSE, FLB_FALSE,
+ NULL, 0, NULL, config);
+ if (!TEST_CHECK(parser != NULL)) {
+ TEST_MSG("flb_parser_create failed");
+ flb_config_exit(config);
+ exit(1);
+ }
+
+ ret = flb_parser_do(parser, input, strlen(input), &out_buf, &out_size, &out_time);
+ if (!TEST_CHECK(ret != -1)) {
+ TEST_MSG("flb_parser_do failed");
+ flb_parser_destroy(parser);
+ flb_config_exit(config);
+ exit(1);
+ }
+
+ ret = compare_msgpack(out_buf, out_size, &expected);
+ if (!TEST_CHECK(ret == 0)) {
+ TEST_MSG("compare failed");
+ flb_free(out_buf);
+ flb_parser_destroy(parser);
+ flb_config_exit(config);
+ exit(1);
+ }
+
+ if (!TEST_CHECK(out_time.tm.tv_sec == 1667217601 && out_time.tm.tv_nsec == 123000000)) {
+ TEST_MSG("timestamp error. sec Got=%ld Expect=1667217601", out_time.tm.tv_sec);
+ TEST_MSG("timestamp error. nsec Got=%ld Expect=123000000", out_time.tm.tv_nsec);
+ }
+
+ flb_free(out_buf);
+ flb_parser_destroy(parser);
+ flb_config_exit(config);
+}
+
+void test_types()
+{
+ struct flb_parser *parser = NULL;
+ struct flb_config *config = NULL;
+ int ret = 0;
+ char *input = "str:text\tint:100\tdouble:1.23\tbool:true";
+ struct flb_parser_types *types = NULL;
+
+ void *out_buf = NULL;
+ size_t out_size = 0;
+ struct flb_time out_time;
+ char *expected_strs[] = {"str", "text", "int", "256" /*= 0x100 */, "double","1.23", "bool", "true"};
+ struct str_list expected = {
+ .size = sizeof(expected_strs)/sizeof(char*),
+ .lists = &expected_strs[0],
+ };
+
+ config = flb_config_init();
+ if(!TEST_CHECK(config != NULL)) {
+ TEST_MSG("flb_config_init failed");
+ exit(1);
+ }
+
+ /* Note: types will be released by flb_parser_destroy */
+ types = flb_malloc(sizeof(struct flb_parser_types));
+ if (!TEST_CHECK(types != NULL)) {
+ TEST_MSG("flb_malloc failed");
+ flb_config_exit(config);
+ exit(1);
+ }
+ types->key = flb_malloc(strlen("int")+1);
+ if (!TEST_CHECK(types->key != NULL)) {
+ TEST_MSG("flb_malloc failed");
+ flb_free(types);
+ flb_config_exit(config);
+ exit(1);
+ }
+ strcpy(types->key, "int");
+ types->key_len = 3;
+ types->type = FLB_PARSER_TYPE_HEX;
+
+ parser = flb_parser_create("ltsv", "ltsv", NULL, FLB_FALSE, NULL, NULL, NULL,
+ FLB_FALSE, FLB_FALSE, FLB_FALSE,
+ types, 1, NULL, config);
+ if (!TEST_CHECK(parser != NULL)) {
+ TEST_MSG("flb_parser_create failed");
+ flb_free(types->key);
+ flb_free(types);
+ flb_config_exit(config);
+ exit(1);
+ }
+
+ ret = flb_parser_do(parser, input, strlen(input), &out_buf, &out_size, &out_time);
+ if (!TEST_CHECK(ret != -1)) {
+ TEST_MSG("flb_parser_do failed");
+ flb_parser_destroy(parser);
+ flb_config_exit(config);
+ exit(1);
+ }
+
+ ret = compare_msgpack(out_buf, out_size, &expected);
+ if (!TEST_CHECK(ret == 0)) {
+ TEST_MSG("compare failed");
+ }
+
+ flb_free(out_buf);
+ flb_parser_destroy(parser);
+ flb_config_exit(config);
+}
+
+void test_decode_field_json()
+{
+ struct flb_parser *parser = NULL;
+ struct flb_config *config = NULL;
+ struct cfl_variant *var = NULL;
+ int ret = 0;
+ char *input = "json_str:{\"str\":\"text\", \"int\":100, \"double\":1.23, \"bool\":true}";
+ struct flb_cf *cf = NULL;
+ struct flb_cf_section *section = NULL;
+ struct mk_list *decoder = NULL;
+
+ void *out_buf = NULL;
+ size_t out_size = 0;
+ struct flb_time out_time;
+ char *expected_strs[] = {"str", "text", "int", "100", "double","1.23", "bool", "true"};
+ struct str_list expected = {
+ .size = sizeof(expected_strs)/sizeof(char*),
+ .lists = &expected_strs[0],
+ };
+
+ config = flb_config_init();
+ if(!TEST_CHECK(config != NULL)) {
+ TEST_MSG("flb_config_init failed");
+ exit(1);
+ }
+ cf = flb_cf_create();
+ if (!TEST_CHECK(cf != NULL)) {
+ TEST_MSG("flb_cf_create failed");
+ flb_config_exit(config);
+ exit(1);
+ }
+
+ section = flb_cf_section_create(cf, "TEST", 4);
+ if (!TEST_CHECK(section != NULL)) {
+ TEST_MSG("flb_cf_section_create failed");
+ flb_cf_destroy(cf);
+ flb_config_exit(config);
+ exit(1);
+ }
+
+ var = flb_cf_section_property_add(cf, section->properties, "decode_field", 12, "json json_str", 13);
+ if(!TEST_CHECK(var != NULL)) {
+ TEST_MSG("flb_cf_section_property_add failed");
+ flb_cf_destroy(cf);
+ flb_config_exit(config);
+ exit(1);
+ }
+
+ decoder = flb_parser_decoder_list_create(section);
+ if (!TEST_CHECK(decoder != NULL)) {
+ TEST_MSG("flb_parser_decoder_list_create failed");
+ flb_cf_destroy(cf);
+ flb_config_exit(config);
+ exit(1);
+ }
+
+ parser = flb_parser_create("ltsv", "ltsv", NULL, FLB_FALSE, NULL, NULL, NULL,
+ FLB_FALSE, FLB_FALSE, FLB_FALSE,
+ NULL, 0, decoder, config);
+ if (!TEST_CHECK(parser != NULL)) {
+ TEST_MSG("flb_parser_create failed");
+ flb_cf_destroy(cf);
+ flb_config_exit(config);
+ exit(1);
+ }
+
+ ret = flb_parser_do(parser, input, strlen(input), &out_buf, &out_size, &out_time);
+ if (!TEST_CHECK(ret != -1)) {
+ TEST_MSG("flb_parser_do failed");
+ flb_parser_destroy(parser);
+ flb_cf_destroy(cf);
+ flb_config_exit(config);
+ exit(1);
+ }
+
+ ret = compare_msgpack(out_buf, out_size, &expected);
+ if (!TEST_CHECK(ret == 0)) {
+ TEST_MSG("compare failed");
+ }
+
+ flb_free(out_buf);
+ flb_parser_destroy(parser);
+ flb_cf_destroy(cf);
+ flb_config_exit(config);
+}
+
+
+TEST_LIST = {
+ { "basic", test_basic},
+ { "time_key", test_time_key},
+ { "time_keep", test_time_keep},
+ { "types", test_types},
+ { "decode_field_json", test_decode_field_json},
+ { 0 }
+};
diff --git a/fluent-bit/tests/internal/parser_regex.c b/fluent-bit/tests/internal/parser_regex.c
new file mode 100644
index 00000000..2283231c
--- /dev/null
+++ b/fluent-bit/tests/internal/parser_regex.c
@@ -0,0 +1,491 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/* Fluent Bit
+ * ==========
+ * Copyright (C) 2019-2021 The Fluent Bit Authors
+ * Copyright (C) 2015-2018 Treasure Data Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <fluent-bit/flb_config.h>
+#include <fluent-bit/flb_config_format.h>
+#include <fluent-bit/flb_parser.h>
+#include <fluent-bit/flb_parser_decoder.h>
+#include <msgpack.h>
+#include <float.h>
+#include <math.h>
+#include "flb_tests_internal.h"
+
+static int msgpack_strncmp(char* str, size_t str_len, msgpack_object obj)
+{
+ int ret = -1;
+
+ if (str == NULL) {
+ flb_error("str is NULL");
+ return -1;
+ }
+
+ switch (obj.type) {
+ case MSGPACK_OBJECT_STR:
+ if (obj.via.str.size != str_len) {
+ return -1;
+ }
+ ret = strncmp(str, obj.via.str.ptr, str_len);
+ break;
+ case MSGPACK_OBJECT_POSITIVE_INTEGER:
+ {
+ unsigned long val = strtoul(str, NULL, 10);
+ if (val == (unsigned long)obj.via.u64) {
+ ret = 0;
+ }
+ }
+ break;
+ case MSGPACK_OBJECT_NEGATIVE_INTEGER:
+ {
+ long long val = strtoll(str, NULL, 10);
+ if (val == (unsigned long)obj.via.i64) {
+ ret = 0;
+ }
+ }
+ break;
+ case MSGPACK_OBJECT_FLOAT32:
+ case MSGPACK_OBJECT_FLOAT64:
+ {
+ double val = strtod(str, NULL);
+ if (fabs(val - obj.via.f64) < DBL_EPSILON) {
+ ret = 0;
+ }
+ }
+ break;
+ case MSGPACK_OBJECT_BOOLEAN:
+ if (obj.via.boolean) {
+ if (str_len != 4 /*true*/) {
+ return -1;
+ }
+ ret = strncasecmp(str, "true", 4);
+ }
+ else {
+ if (str_len != 5 /*false*/) {
+ return -1;
+ }
+ ret = strncasecmp(str, "false", 5);
+ }
+ break;
+ default:
+ flb_error("not supported");
+ }
+
+ return ret;
+}
+
+struct str_list {
+ size_t size;
+ char **lists;
+};
+
+static int compare_msgpack(void *msgpack_data, size_t msgpack_size, struct str_list *l)
+{
+ msgpack_unpacked result;
+ msgpack_object obj;
+ size_t off = 0;
+ int map_size;
+ int i_map;
+ int i_list;
+ int num = 0;
+
+ if (!TEST_CHECK(msgpack_data != NULL)) {
+ TEST_MSG("msgpack_data is NULL");
+ return -1;
+ }
+ else if (!TEST_CHECK(msgpack_size > 0)) {
+ TEST_MSG("msgpack_size is 0");
+ return -1;
+ }
+
+ msgpack_unpacked_init(&result);
+ while (msgpack_unpack_next(&result, msgpack_data, msgpack_size, &off) == MSGPACK_UNPACK_SUCCESS) {
+ obj = result.data;
+ /*
+ msgpack_object_print(stdout, obj);
+ */
+ if (!TEST_CHECK(obj.type == MSGPACK_OBJECT_MAP)) {
+ TEST_MSG("map error. type = %d", obj.type);
+ continue;
+ }
+ map_size = obj.via.map.size;
+ for (i_map=0; i_map<map_size; i_map++) {
+ if (!TEST_CHECK(obj.via.map.ptr[i_map].key.type == MSGPACK_OBJECT_STR)) {
+ TEST_MSG("key is not string. type =%d", obj.via.map.ptr[i_map].key.type);
+ continue;
+ }
+ for (i_list=0; i_list< l->size/2; i_list++) {
+ if (msgpack_strncmp(l->lists[i_list*2], strlen(l->lists[i_list*2]),
+ obj.via.map.ptr[i_map].key) == 0 &&
+ msgpack_strncmp(l->lists[i_list*2+1], strlen(l->lists[i_list*2+1]),
+ obj.via.map.ptr[i_map].val) == 0) {
+ num++;
+ }
+ }
+ }
+ }
+ msgpack_unpacked_destroy(&result);
+ if (!TEST_CHECK(num == l->size/2)) {
+ msgpack_object_print(stdout, obj);
+ putchar('\n');
+ TEST_MSG("compare failed. matched_num=%d expect=%lu", num, l->size/2);
+ return -1;
+ }
+ return 0;
+}
+
+void test_basic()
+{
+ struct flb_parser *parser = NULL;
+ struct flb_config *config = NULL;
+ int ret = 0;
+ char *input = "text 100 1.23 true";
+ char *regex = "(?<str>[a-z]+) (?<int>\\d+) (?<double>\\d+\\.\\d+) (?<bool>true)";
+ void *out_buf = NULL;
+ size_t out_size = 0;
+ struct flb_time out_time;
+ char *expected_strs[] = {"str", "text", "int", "100", "double","1.23", "bool", "true"};
+ struct str_list expected = {
+ .size = sizeof(expected_strs)/sizeof(char*),
+ .lists = &expected_strs[0],
+ };
+
+ config = flb_config_init();
+ if(!TEST_CHECK(config != NULL)) {
+ TEST_MSG("flb_config_init failed");
+ exit(1);
+ }
+
+ parser = flb_parser_create("regex", "regex", regex, FLB_FALSE, NULL, NULL, NULL,
+ FLB_FALSE, FLB_FALSE, FLB_FALSE,
+ NULL, 0, NULL, config);
+ if (!TEST_CHECK(parser != NULL)) {
+ TEST_MSG("flb_parser_create failed");
+ flb_config_exit(config);
+ exit(1);
+ }
+
+ ret = flb_parser_do(parser, input, strlen(input), &out_buf, &out_size, &out_time);
+ if (!TEST_CHECK(ret != -1)) {
+ TEST_MSG("flb_parser_do failed");
+ flb_parser_destroy(parser);
+ flb_config_exit(config);
+ exit(1);
+ }
+
+ ret = compare_msgpack(out_buf, out_size, &expected);
+ if (!TEST_CHECK(ret == 0)) {
+ TEST_MSG("compare failed");
+ }
+
+ flb_free(out_buf);
+ flb_parser_destroy(parser);
+ flb_config_exit(config);
+}
+
+void test_time_key()
+{
+ struct flb_parser *parser = NULL;
+ struct flb_config *config = NULL;
+ int ret = 0;
+ char *input = "text 100 1.23 true 2022-10-31T12:00:01.123";
+ char *regex = "(?<str>[a-z]+) (?<int>\\d+) (?<double>\\d+\\.\\d+) (?<bool>true) (?<time>.+)";
+ void *out_buf = NULL;
+ size_t out_size = 0;
+ struct flb_time out_time;
+ char *expected_strs[] = {"str", "text", "int", "100", "double","1.23", "bool", "true"};
+ struct str_list expected = {
+ .size = sizeof(expected_strs)/sizeof(char*),
+ .lists = &expected_strs[0],
+ };
+
+ out_time.tm.tv_sec = 0;
+ out_time.tm.tv_nsec = 0;
+
+
+ config = flb_config_init();
+ if(!TEST_CHECK(config != NULL)) {
+ TEST_MSG("flb_config_init failed");
+ exit(1);
+ }
+
+ parser = flb_parser_create("regex", "regex", regex, FLB_FALSE, "%Y-%m-%dT%H:%M:%S.%L", "time", NULL,
+ FLB_FALSE, FLB_FALSE, FLB_FALSE,
+ NULL, 0, NULL, config);
+ if (!TEST_CHECK(parser != NULL)) {
+ TEST_MSG("flb_parser_create failed");
+ flb_config_exit(config);
+ exit(1);
+ }
+
+ ret = flb_parser_do(parser, input, strlen(input), &out_buf, &out_size, &out_time);
+ if (!TEST_CHECK(ret != -1)) {
+ TEST_MSG("flb_parser_do failed");
+ flb_parser_destroy(parser);
+ flb_config_exit(config);
+ exit(1);
+ }
+
+ ret = compare_msgpack(out_buf, out_size, &expected);
+ if (!TEST_CHECK(ret == 0)) {
+ TEST_MSG("compare failed");
+ flb_free(out_buf);
+ flb_parser_destroy(parser);
+ flb_config_exit(config);
+ exit(1);
+ }
+
+ if (!TEST_CHECK(out_time.tm.tv_sec == 1667217601 && out_time.tm.tv_nsec == 123000000)) {
+ TEST_MSG("timestamp error. sec Got=%ld Expect=1667217601", out_time.tm.tv_sec);
+ TEST_MSG("timestamp error. nsec Got=%ld Expect=123000000", out_time.tm.tv_nsec);
+ }
+
+ flb_free(out_buf);
+ flb_parser_destroy(parser);
+ flb_config_exit(config);
+}
+
+void test_time_keep()
+{
+ struct flb_parser *parser = NULL;
+ struct flb_config *config = NULL;
+ int ret = 0;
+ char *input = "text 100 1.23 true 2022-10-31T12:00:01.123";
+ char *regex = "(?<str>[a-z]+) (?<int>\\d+) (?<double>\\d+\\.\\d+) (?<bool>true) (?<time>.+)";
+ void *out_buf = NULL;
+ size_t out_size = 0;
+ struct flb_time out_time;
+ char *expected_strs[] = {"str", "text", "int", "100", "double","1.23", "bool", "true", "time", "2022-10-31T12:00:01.123"};
+ struct str_list expected = {
+ .size = sizeof(expected_strs)/sizeof(char*),
+ .lists = &expected_strs[0],
+ };
+
+ out_time.tm.tv_sec = 0;
+ out_time.tm.tv_nsec = 0;
+
+
+ config = flb_config_init();
+ if(!TEST_CHECK(config != NULL)) {
+ TEST_MSG("flb_config_init failed");
+ exit(1);
+ }
+
+ parser = flb_parser_create("regex", "regex", regex, FLB_FALSE, "%Y-%m-%dT%H:%M:%S.%L", "time", NULL,
+ FLB_TRUE /*time_keep */, FLB_FALSE, FLB_FALSE,
+ NULL, 0, NULL, config);
+ if (!TEST_CHECK(parser != NULL)) {
+ TEST_MSG("flb_parser_create failed");
+ flb_config_exit(config);
+ exit(1);
+ }
+
+ ret = flb_parser_do(parser, input, strlen(input), &out_buf, &out_size, &out_time);
+ if (!TEST_CHECK(ret != -1)) {
+ TEST_MSG("flb_parser_do failed");
+ flb_parser_destroy(parser);
+ flb_config_exit(config);
+ exit(1);
+ }
+
+ ret = compare_msgpack(out_buf, out_size, &expected);
+ if (!TEST_CHECK(ret == 0)) {
+ TEST_MSG("compare failed");
+ flb_free(out_buf);
+ flb_parser_destroy(parser);
+ flb_config_exit(config);
+ exit(1);
+ }
+
+ if (!TEST_CHECK(out_time.tm.tv_sec == 1667217601 && out_time.tm.tv_nsec == 123000000)) {
+ TEST_MSG("timestamp error. sec Got=%ld Expect=1667217601", out_time.tm.tv_sec);
+ TEST_MSG("timestamp error. nsec Got=%ld Expect=123000000", out_time.tm.tv_nsec);
+ }
+
+ flb_free(out_buf);
+ flb_parser_destroy(parser);
+ flb_config_exit(config);
+}
+
+void test_types()
+{
+ struct flb_parser *parser = NULL;
+ struct flb_config *config = NULL;
+ int ret = 0;
+ char *input = "text 100 1.23 true";
+ char *regex = "(?<str>[a-z]+) (?<int>\\d+) (?<double>\\d+\\.\\d+) (?<bool>true)";
+ struct flb_parser_types *types = NULL;
+
+ void *out_buf = NULL;
+ size_t out_size = 0;
+ struct flb_time out_time;
+ char *expected_strs[] = {"str", "text", "int", "256" /*= 0x100 */, "double","1.23", "bool", "true"};
+ struct str_list expected = {
+ .size = sizeof(expected_strs)/sizeof(char*),
+ .lists = &expected_strs[0],
+ };
+
+ config = flb_config_init();
+ if(!TEST_CHECK(config != NULL)) {
+ TEST_MSG("flb_config_init failed");
+ exit(1);
+ }
+
+ /* Note: types will be released by flb_parser_destroy */
+ types = flb_malloc(sizeof(struct flb_parser_types));
+ if (!TEST_CHECK(types != NULL)) {
+ TEST_MSG("flb_malloc failed");
+ flb_config_exit(config);
+ exit(1);
+ }
+ types->key = flb_malloc(strlen("int")+1);
+ if (!TEST_CHECK(types->key != NULL)) {
+ TEST_MSG("flb_malloc failed");
+ flb_free(types);
+ flb_config_exit(config);
+ exit(1);
+ }
+ strcpy(types->key, "int");
+ types->key_len = 3;
+ types->type = FLB_PARSER_TYPE_HEX;
+
+ parser = flb_parser_create("regex", "regex", regex, FLB_FALSE, NULL, NULL, NULL,
+ FLB_FALSE, FLB_FALSE, FLB_FALSE,
+ types, 1, NULL, config);
+ if (!TEST_CHECK(parser != NULL)) {
+ TEST_MSG("flb_parser_create failed");
+ flb_free(types->key);
+ flb_free(types);
+ flb_config_exit(config);
+ exit(1);
+ }
+
+ ret = flb_parser_do(parser, input, strlen(input), &out_buf, &out_size, &out_time);
+ if (!TEST_CHECK(ret != -1)) {
+ TEST_MSG("flb_parser_do failed");
+ flb_parser_destroy(parser);
+ flb_config_exit(config);
+ exit(1);
+ }
+
+ ret = compare_msgpack(out_buf, out_size, &expected);
+ if (!TEST_CHECK(ret == 0)) {
+ TEST_MSG("compare failed");
+ }
+
+ flb_free(out_buf);
+ flb_parser_destroy(parser);
+ flb_config_exit(config);
+}
+
+void test_decode_field_json()
+{
+ struct flb_parser *parser = NULL;
+ struct flb_config *config = NULL;
+ struct cfl_variant *var = NULL;
+ int ret = 0;
+ char *input = "{\"str\":\"text\", \"int\":100, \"double\":1.23, \"bool\":true}";
+ char *regex = "(?<json_str>.+)";
+ struct flb_cf *cf = NULL;
+ struct flb_cf_section *section = NULL;
+ struct mk_list *decoder = NULL;
+
+ void *out_buf = NULL;
+ size_t out_size = 0;
+ struct flb_time out_time;
+ char *expected_strs[] = {"str", "text", "int", "100", "double","1.23", "bool", "true"};
+ struct str_list expected = {
+ .size = sizeof(expected_strs)/sizeof(char*),
+ .lists = &expected_strs[0],
+ };
+
+ config = flb_config_init();
+ if(!TEST_CHECK(config != NULL)) {
+ TEST_MSG("flb_config_init failed");
+ exit(1);
+ }
+ cf = flb_cf_create();
+ if (!TEST_CHECK(cf != NULL)) {
+ TEST_MSG("flb_cf_create failed");
+ flb_config_exit(config);
+ exit(1);
+ }
+
+ section = flb_cf_section_create(cf, "TEST", 4);
+ if (!TEST_CHECK(section != NULL)) {
+ TEST_MSG("flb_cf_section_create failed");
+ flb_cf_destroy(cf);
+ flb_config_exit(config);
+ exit(1);
+ }
+
+ var = flb_cf_section_property_add(cf, section->properties, "decode_field", 12, "json json_str", 13);
+ if(!TEST_CHECK(var != NULL)) {
+ TEST_MSG("flb_cf_section_property_add failed");
+ flb_cf_destroy(cf);
+ flb_config_exit(config);
+ exit(1);
+ }
+
+ decoder = flb_parser_decoder_list_create(section);
+ if (!TEST_CHECK(decoder != NULL)) {
+ TEST_MSG("flb_parser_decoder_list_create failed");
+ flb_cf_destroy(cf);
+ flb_config_exit(config);
+ exit(1);
+ }
+
+ parser = flb_parser_create("regex", "regex", regex, FLB_FALSE, NULL, NULL, NULL,
+ FLB_FALSE, FLB_FALSE, FLB_FALSE,
+ NULL, 0, decoder, config);
+ if (!TEST_CHECK(parser != NULL)) {
+ TEST_MSG("flb_parser_create failed");
+ flb_cf_destroy(cf);
+ flb_config_exit(config);
+ exit(1);
+ }
+
+ ret = flb_parser_do(parser, input, strlen(input), &out_buf, &out_size, &out_time);
+ if (!TEST_CHECK(ret != -1)) {
+ TEST_MSG("flb_parser_do failed");
+ flb_parser_destroy(parser);
+ flb_cf_destroy(cf);
+ flb_config_exit(config);
+ exit(1);
+ }
+
+ ret = compare_msgpack(out_buf, out_size, &expected);
+ if (!TEST_CHECK(ret == 0)) {
+ TEST_MSG("compare failed");
+ }
+
+ flb_free(out_buf);
+ flb_parser_destroy(parser);
+ flb_cf_destroy(cf);
+ flb_config_exit(config);
+}
+
+
+TEST_LIST = {
+ { "basic", test_basic},
+ { "time_key", test_time_key},
+ { "time_keep", test_time_keep},
+ { "types", test_types},
+ { "decode_field_json", test_decode_field_json},
+ { 0 }
+};
diff --git a/fluent-bit/tests/internal/pipe.c b/fluent-bit/tests/internal/pipe.c
new file mode 100644
index 00000000..f8a08e0d
--- /dev/null
+++ b/fluent-bit/tests/internal/pipe.c
@@ -0,0 +1,89 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+#include <fluent-bit/flb_info.h>
+#include <fluent-bit/flb_pipe.h>
+#ifdef FLB_SYSTEM_WINDOWS
+#include <fluent-bit/flb_compat.h>
+#endif
+
+#include <inttypes.h>
+#include "flb_tests_internal.h"
+
+struct data {
+ int x;
+ char str[32];
+};
+
+static void test_pipe_usage()
+{
+ int i;
+ int ret;
+ char *b;
+ ssize_t bytes;
+ flb_pipefd_t p[2];
+ struct data data;
+ struct data rec;
+#ifdef FLB_SYSTEM_WINDOWS
+ WSADATA wsa_data;
+#endif
+
+#ifdef FLB_SYSTEM_WINDOWS
+ WSAStartup(0x0201, &wsa_data);
+#endif
+ /* Create pipe */
+ ret = flb_pipe_create(p);
+ TEST_CHECK(ret == 0);
+
+ /* Prepare test */
+ memset(&data, '\0', sizeof(struct data));
+ data.x = 2017;
+ memcpy(data.str, "this is a test\n", 15);
+ data.str[15] = '\0';
+
+ /* Simple write/read */
+ bytes = flb_pipe_w(p[1], &data, sizeof(struct data));
+ TEST_CHECK(bytes > 0);
+
+ bytes = flb_pipe_r(p[0], &rec, sizeof(struct data));
+ TEST_CHECK(bytes > 0);
+ TEST_CHECK(rec.x == 2017);
+ TEST_CHECK(strlen(rec.str) == 15);
+
+ /* Iterate write, all_read */
+ b = (char *) &data;
+ for (i = 0; i < sizeof(struct data); i++, b++) {
+ bytes = flb_pipe_w(p[1], b, 1);
+ TEST_CHECK(bytes == 1);
+ }
+
+ b = (char *) &rec;
+ memset(&rec, '\0', sizeof(struct data));
+ for (i = 0; i < sizeof(struct data); i++, b++) {
+ bytes = flb_pipe_read_all(p[0], b, 1);
+ TEST_CHECK(bytes == 1);
+ }
+ TEST_CHECK(rec.x == 2017);
+ TEST_CHECK(strlen(rec.str) == 15);
+
+ /* All write, all read */
+ bytes = flb_pipe_write_all(p[1], &data, sizeof(struct data));
+ TEST_CHECK(bytes == sizeof(struct data));
+
+ memset(&rec, '\0', sizeof(struct data));
+ bytes = flb_pipe_read_all(p[0], &rec, sizeof(struct data));
+ TEST_CHECK(bytes == sizeof(struct data));
+ TEST_CHECK(rec.x == 2017);
+ TEST_CHECK(strlen(rec.str) == 15);
+
+ /* Close pipe channels */
+ flb_pipe_close(p[0]);
+ flb_pipe_close(p[1]);
+#ifdef FLB_SYSTEM_WINDOWS
+ WSACleanup();
+#endif
+}
+
+TEST_LIST = {
+ { "pipe_usage", test_pipe_usage},
+ { 0 }
+};
diff --git a/fluent-bit/tests/internal/processor.c b/fluent-bit/tests/internal/processor.c
new file mode 100644
index 00000000..679fd131
--- /dev/null
+++ b/fluent-bit/tests/internal/processor.c
@@ -0,0 +1,125 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/* Fluent Bit
+ * ==========
+ * Copyright (C) 2015-2023 The Fluent Bit Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <fluent-bit/flb_info.h>
+#include <fluent-bit/flb_lib.h>
+#include <fluent-bit/flb_mem.h>
+#include <fluent-bit/flb_pack.h>
+#include <fluent-bit/flb_processor.h>
+//#include <msgpack.h>
+
+#include "flb_tests_internal.h"
+
+#define APACHE_10K FLB_TESTS_DATA_PATH "/data/mp/apache_10k.mp"
+
+static int create_msgpack_records(char **out_buf, size_t *out_size)
+{
+ int ret;
+ int root_type;
+ char *json = "{\"key1\": 12345, \"key2\": \"fluent bit\"}";
+ char *mp_tmp;
+ size_t mp_size;
+
+ msgpack_sbuffer mp_sbuf;
+ msgpack_packer mp_pck;
+
+ msgpack_sbuffer_init(&mp_sbuf);
+ msgpack_packer_init(&mp_pck, &mp_sbuf, msgpack_sbuffer_write);
+
+ msgpack_pack_array(&mp_pck, 2);
+ flb_pack_time_now(&mp_pck);
+
+ ret = flb_pack_json(json, strlen(json), &mp_tmp, &mp_size, &root_type, NULL);
+ TEST_CHECK(ret == 0);
+
+ msgpack_sbuffer_write(&mp_sbuf, mp_tmp, mp_size);
+ flb_free(mp_tmp);
+
+ *out_buf = mp_sbuf.data;
+ *out_size = mp_sbuf.size;
+
+ return 0;
+}
+
+static void processor()
+{
+ int ret;
+ struct flb_processor *proc;
+ struct flb_processor_unit *pu;
+ struct flb_config *config;
+ char *mp_buf;
+ size_t mp_size;
+ void *out_buf = NULL;
+ size_t out_size;
+ flb_sds_t hostname_prop_key;
+ struct cfl_variant var = {
+ .type = CFL_VARIANT_STRING,
+ .data.as_string = NULL,
+ };
+
+ printf("\n\n");
+
+ flb_init_env();
+
+ hostname_prop_key = flb_sds_create("hostname monox");
+ TEST_CHECK(hostname_prop_key != NULL);
+ var.data.as_string = hostname_prop_key;
+
+ config = flb_config_init();
+ TEST_CHECK(config != NULL);
+
+ proc = flb_processor_create(config, "unit_test", NULL, 0);
+ TEST_CHECK(proc != NULL);
+
+ pu = flb_processor_unit_create(proc, FLB_PROCESSOR_LOGS, "stdout");
+ TEST_CHECK(pu != NULL);
+
+ pu = flb_processor_unit_create(proc, FLB_PROCESSOR_LOGS, "modify");
+ TEST_CHECK(pu != NULL);
+
+ ret = flb_processor_unit_set_property(pu, "add", &var);
+ TEST_CHECK(ret == 0);
+
+ pu = flb_processor_unit_create(proc, FLB_PROCESSOR_LOGS, "stdout");
+ TEST_CHECK(pu != NULL);
+
+ ret = flb_processor_init(proc);
+ TEST_CHECK(ret == 0);
+
+ /* generate records (simulate an input plugin */
+ ret = create_msgpack_records(&mp_buf, &mp_size);
+ TEST_CHECK(ret == 0);
+
+ ret = flb_processor_run(proc, 0, FLB_PROCESSOR_LOGS, "TEST", 4, mp_buf, mp_size, &out_buf, &out_size);
+
+ if (out_buf != mp_buf) {
+ flb_free(out_buf);
+ }
+ flb_free(mp_buf);
+
+ flb_processor_destroy(proc);
+ flb_config_exit(config);
+
+ flb_sds_destroy(hostname_prop_key);
+}
+
+TEST_LIST = {
+ { "processor", processor },
+ { 0 }
+};
diff --git a/fluent-bit/tests/internal/random.c b/fluent-bit/tests/internal/random.c
new file mode 100644
index 00000000..236eaa57
--- /dev/null
+++ b/fluent-bit/tests/internal/random.c
@@ -0,0 +1,33 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+#include <fluent-bit/flb_random.h>
+
+#include "flb_tests_internal.h"
+
+void test_random_bytes()
+{
+ int ret;
+ unsigned char buf1[64] = {0};
+ unsigned char buf2[64] = {0};
+
+ /* The following tests check whether:
+ *
+ * (1) the random generator fills the buffer with numbers at all.
+ * (2) a successive call generates different numbers.
+ *
+ * These tests are probabilistic by nature; If we assume an ideal random
+ * generator, they are expected to fail once in 2^192 (= 10^57) runs.
+ */
+ ret = flb_random_bytes(buf1, 64);
+ TEST_CHECK(ret == 0);
+ TEST_CHECK(memcmp(buf1, buf2, 64) != 0);
+
+ ret = flb_random_bytes(buf2, 64);
+ TEST_CHECK(ret == 0);
+ TEST_CHECK(memcmp(buf1, buf2, 64) != 0);
+}
+
+TEST_LIST = {
+ {"random_bytes", test_random_bytes},
+ { 0 }
+};
diff --git a/fluent-bit/tests/internal/record_accessor.c b/fluent-bit/tests/internal/record_accessor.c
new file mode 100644
index 00000000..5f62b846
--- /dev/null
+++ b/fluent-bit/tests/internal/record_accessor.c
@@ -0,0 +1,1731 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/* Fluent Bit
+ * ==========
+ * Copyright (C) 2019-2020 The Fluent Bit Authors
+ * Copyright (C) 2015-2018 Treasure Data Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <fluent-bit/flb_info.h>
+#include <fluent-bit/flb_mem.h>
+#include <fluent-bit/flb_error.h>
+#include <fluent-bit/flb_sds.h>
+#include <fluent-bit/flb_pack.h>
+#include <fluent-bit/flb_sds.h>
+#include <fluent-bit/flb_sds_list.h>
+#include <fluent-bit/flb_record_accessor.h>
+#include <fluent-bit/record_accessor/flb_ra_parser.h>
+#include <msgpack.h>
+
+#include "flb_tests_internal.h"
+
+#include <stdlib.h>
+
+static int create_map(char *input_json, msgpack_object *out_map,
+ char **out_buf, msgpack_unpacked *out_result)
+{
+ int len;
+ int ret;
+ size_t out_size;
+ int type;
+ size_t off = 0;
+
+ if (input_json == NULL || out_map == NULL) {
+ return -1;
+ }
+ len = strlen(input_json);
+ ret = flb_pack_json(input_json, len, out_buf, &out_size, &type, NULL);
+ if (!TEST_CHECK(ret == 0)) {
+ TEST_MSG("can't convert. input=%s", input_json);
+ exit(EXIT_FAILURE);
+ }
+ /* Unpack msgpack object */
+ msgpack_unpacked_init(out_result);
+ msgpack_unpack_next(out_result, *out_buf, out_size, &off);
+ *out_map = out_result->data;
+
+ return 0;
+}
+
+static int set_str_to_msgpack_object(char *str, msgpack_object *obj)
+{
+ if (str == NULL || obj == NULL) {
+ return -1;
+ }
+ /* create value object to overwrite */
+ obj->type = MSGPACK_OBJECT_STR;
+ obj->via.str.size = strlen(str);
+ obj->via.str.ptr = str;
+ return 0;
+}
+
+
+void cb_keys()
+{
+ struct flb_record_accessor *ra;
+
+ printf("\n=== test ===");
+ ra = flb_ra_create("$aaa['a'] extra $bbb['b'] final access", FLB_TRUE);
+ TEST_CHECK(ra != NULL);
+ if (!ra) {
+ exit(EXIT_FAILURE);
+ }
+ TEST_CHECK(mk_list_size(&ra->list) == 4);
+ flb_ra_dump(ra);
+ flb_ra_destroy(ra);
+
+ printf("\n=== test ===");
+ ra = flb_ra_create("$b['x']['y']", FLB_TRUE);
+ TEST_CHECK(ra != NULL);
+ if (!ra) {
+ exit(EXIT_FAILURE);
+ }
+ TEST_CHECK(mk_list_size(&ra->list) == 1);
+ flb_ra_dump(ra);
+ flb_ra_destroy(ra);
+
+ printf("\n=== test ===");
+ ra = flb_ra_create("$z", FLB_TRUE);
+ TEST_CHECK(ra != NULL);
+ if (!ra) {
+ exit(EXIT_FAILURE);
+ }
+ TEST_CHECK(mk_list_size(&ra->list) == 1);
+ flb_ra_dump(ra);
+ flb_ra_destroy(ra);
+
+ printf("\n=== test ===");
+ ra = flb_ra_create("abc", FLB_TRUE);
+ TEST_CHECK(ra != NULL);
+ if (!ra) {
+ exit(EXIT_FAILURE);
+ }
+ TEST_CHECK(mk_list_size(&ra->list) == 1);
+ flb_ra_dump(ra);
+ flb_ra_destroy(ra);
+
+ ra = flb_ra_create("$abc['a'", FLB_TRUE);
+ TEST_CHECK(ra == NULL);
+
+ ra = flb_ra_create("", FLB_TRUE);
+ flb_ra_destroy(ra);
+}
+
+void cb_translate()
+{
+ int len;
+ int ret;
+ int type;
+ size_t off = 0;
+ char *out_buf;
+ size_t out_size;
+ char *json;
+ char *fmt;
+ char *fmt_out;
+ flb_sds_t str;
+ msgpack_unpacked result;
+ msgpack_object map;
+ struct flb_record_accessor *ra;
+
+ /* Sample JSON message */
+ json =
+ "{\"k1\": \"string\", \"k2\": true, \"k3\": false," \
+ " \"k4\": 0.123456789, \"k5\": 123456789," \
+ " \"k6\": {\"s1\": {\"s2\": \"nested\"}}}";
+
+ /* Convert to msgpack */
+ len = strlen(json);
+ ret = flb_pack_json(json, len, &out_buf, &out_size, &type, NULL);
+ TEST_CHECK(ret == 0);
+ if (ret == -1) {
+ exit(EXIT_FAILURE);
+ }
+
+ /* Set environment variables */
+ putenv("FLB_ENV=translated");
+
+ /* Formatter */
+ fmt = \
+ "START k1 => \"$k1\", k2 => $k2 (bool), k3 => $k3 (bool), " \
+ "k4 => $k4 (float), k5 => $k5 (int)," \
+ "k6 => $k6['s1']['s2'] (nested), k8 => $k8 (nothing), ${FLB_ENV} END";
+
+ fmt_out = \
+ "START k1 => \"string\", k2 => true (bool), " \
+ "k3 => false (bool), k4 => 0.123457 (float), " \
+ "k5 => 123456789 (int),k6 => nested (nested), " \
+ "k8 => (nothing), translated END";
+
+ ra = flb_ra_create(fmt, FLB_TRUE);
+ TEST_CHECK(ra != NULL);
+ if (!ra) {
+ exit(EXIT_FAILURE);
+ }
+
+ /* Unpack msgpack object */
+ msgpack_unpacked_init(&result);
+ msgpack_unpack_next(&result, out_buf, out_size, &off);
+ map = result.data;
+
+ /* Do translation */
+ str = flb_ra_translate(ra, NULL, -1, map, NULL);
+ TEST_CHECK(str != NULL);
+ if (!str) {
+ exit(EXIT_FAILURE);
+ }
+
+ TEST_CHECK(flb_sds_len(str) == strlen(fmt_out));
+ TEST_CHECK(memcmp(str, fmt_out, strlen(fmt_out)) == 0);
+ printf("== input ==\n%s\n== output ==\n%s\n", str, fmt_out);
+
+ flb_sds_destroy(str);
+ flb_ra_destroy(ra);
+ flb_free(out_buf);
+ msgpack_unpacked_destroy(&result);
+}
+
+void cb_translate_tag()
+{
+ int len;
+ int ret;
+ int type;
+ size_t off = 0;
+ char *out_buf;
+ size_t out_size;
+ char *json;
+ char *fmt;
+ flb_sds_t str;
+ msgpack_unpacked result;
+ msgpack_object map;
+ struct flb_record_accessor *ra;
+
+ /* Sample JSON message */
+ json =
+ "{\"k1\": \"string\", \"k2\": true, \"k3\": false," \
+ " \"k4\": 0.123456789, \"k5\": 123456789," \
+ " \"k6\": {\"s1\": {\"s2\": \"nested\"}}}";
+
+ /* Convert to msgpack */
+ len = strlen(json);
+ ret = flb_pack_json(json, len, &out_buf, &out_size, &type, NULL);
+ TEST_CHECK(ret == 0);
+ if (ret == -1) {
+ exit(EXIT_FAILURE);
+ }
+
+ fmt = "$TAG";
+ ra = flb_ra_create(fmt, FLB_TRUE);
+ TEST_CHECK(ra != NULL);
+ if (!ra) {
+ exit(EXIT_FAILURE);
+ }
+
+ /* Unpack msgpack object */
+ msgpack_unpacked_init(&result);
+ msgpack_unpack_next(&result, out_buf, out_size, &off);
+ map = result.data;
+
+ /* Do translation */
+ str = flb_ra_translate(ra, "testapp", 7, map, NULL);
+ TEST_CHECK(str != NULL);
+ if (!str) {
+ exit(EXIT_FAILURE);
+ }
+ TEST_CHECK(flb_sds_len(str) == 7);
+
+ flb_sds_destroy(str);
+ flb_ra_destroy(ra);
+ flb_free(out_buf);
+ msgpack_unpacked_destroy(&result);
+}
+
+void cb_dots_subkeys()
+{
+ int len;
+ int ret;
+ int type;
+ size_t off = 0;
+ char *out_buf;
+ size_t out_size;
+ char *json;
+ char *fmt;
+ char *fmt_out;
+ flb_sds_t str;
+ msgpack_unpacked result;
+ msgpack_object map;
+ struct flb_record_accessor *ra;
+
+ /* Sample JSON message */
+ json =
+ "{\"key1\": \"something\", \"kubernetes\": {\"annotations\": "
+ "{\"fluentbit.io/tag\": \"thetag\"}}}";
+
+ /* Convert to msgpack */
+ len = strlen(json);
+ ret = flb_pack_json(json, len, &out_buf, &out_size, &type, NULL);
+ TEST_CHECK(ret == 0);
+ if (ret == -1) {
+ exit(EXIT_FAILURE);
+ }
+
+ /* Formatter */
+ fmt = flb_sds_create("$kubernetes['annotations']['fluentbit.io/tag']");
+ fmt_out = "thetag";
+
+ ra = flb_ra_create(fmt, FLB_FALSE);
+ TEST_CHECK(ra != NULL);
+ if (!ra) {
+ exit(EXIT_FAILURE);
+ }
+
+ /* Unpack msgpack object */
+ msgpack_unpacked_init(&result);
+ msgpack_unpack_next(&result, out_buf, out_size, &off);
+ map = result.data;
+
+ /* Do translation */
+ str = flb_ra_translate(ra, NULL, -1, map, NULL);
+ TEST_CHECK(str != NULL);
+ if (!str) {
+ exit(EXIT_FAILURE);
+ }
+
+ TEST_CHECK(flb_sds_len(str) == strlen(fmt_out));
+ TEST_CHECK(memcmp(str, fmt_out, strlen(fmt_out)) == 0);
+ printf("== input ==\n%s\n== output ==\n%s\n", str, fmt_out);
+
+ flb_sds_destroy(str);
+ flb_sds_destroy(fmt);
+ flb_ra_destroy(ra);
+ flb_free(out_buf);
+ msgpack_unpacked_destroy(&result);
+}
+
+void cb_array_id()
+{
+ int len;
+ int ret;
+ int type;
+ size_t off = 0;
+ char *out_buf;
+ size_t out_size;
+ char *json;
+ char *fmt;
+ char *fmt_out;
+ flb_sds_t str;
+ msgpack_unpacked result;
+ msgpack_object map;
+ struct flb_record_accessor *ra;
+
+ /* Sample JSON message */
+ json =
+ "{\"key1\": \"something\", "
+ "\"kubernetes\": "
+ " [true, "
+ " false, "
+ " {\"a\": false, "
+ " \"annotations\": { "
+ " \"fluentbit.io/tag\": \"thetag\""
+ "}}]}";
+
+ /* Convert to msgpack */
+ len = strlen(json);
+ ret = flb_pack_json(json, len, &out_buf, &out_size, &type, NULL);
+ TEST_CHECK(ret == 0);
+ if (ret == -1) {
+ exit(EXIT_FAILURE);
+ }
+
+ /* Formatter */
+ fmt = flb_sds_create("$kubernetes[2]['annotations']['fluentbit.io/tag']");
+ fmt_out = "thetag";
+
+ ra = flb_ra_create(fmt, FLB_FALSE);
+ TEST_CHECK(ra != NULL);
+ if (!ra) {
+ exit(EXIT_FAILURE);
+ }
+
+ /* Unpack msgpack object */
+ msgpack_unpacked_init(&result);
+ msgpack_unpack_next(&result, out_buf, out_size, &off);
+ map = result.data;
+
+ /* Do translation */
+ str = flb_ra_translate(ra, NULL, -1, map, NULL);
+ TEST_CHECK(str != NULL);
+ if (!str) {
+ exit(EXIT_FAILURE);
+ }
+
+ TEST_CHECK(flb_sds_len(str) == strlen(fmt_out));
+ TEST_CHECK(memcmp(str, fmt_out, strlen(fmt_out)) == 0);
+ printf("== input ==\n%s\n== output ==\n%s\n", str, fmt_out);
+
+ flb_sds_destroy(str);
+ flb_sds_destroy(fmt);
+ flb_ra_destroy(ra);
+ flb_free(out_buf);
+ msgpack_unpacked_destroy(&result);
+}
+
+void cb_get_kv_pair()
+{
+ int len;
+ int ret;
+ int type;
+ size_t off = 0;
+ char *out_buf;
+ size_t out_size;
+ char *json;
+ char *fmt;
+ char *fmt_out;
+ msgpack_object *start_key;
+ msgpack_object *out_key;
+ msgpack_object *out_val;
+ msgpack_unpacked result;
+ msgpack_object map;
+ struct flb_record_accessor *ra;
+
+ /* Sample JSON message */
+ json =
+ "{\"key1\": \"something\", "
+ "\"kubernetes\": "
+ " [true, "
+ " false, "
+ " {\"a\": false, "
+ " \"annotations\": { "
+ " \"fluentbit.io/tag\": \"thetag\""
+ "}}]}";
+
+ /* Convert to msgpack */
+ len = strlen(json);
+ ret = flb_pack_json(json, len, &out_buf, &out_size, &type, NULL);
+ TEST_CHECK(ret == 0);
+ if (ret == -1) {
+ exit(EXIT_FAILURE);
+ }
+
+ /* Formatter */
+ fmt = flb_sds_create("$kubernetes[2]['annotations']['fluentbit.io/tag']");
+ fmt_out = "thetag";
+
+ ra = flb_ra_create(fmt, FLB_FALSE);
+ TEST_CHECK(ra != NULL);
+ if (!ra) {
+ exit(EXIT_FAILURE);
+ }
+
+ /* Unpack msgpack object */
+ msgpack_unpacked_init(&result);
+ msgpack_unpack_next(&result, out_buf, out_size, &off);
+ map = result.data;
+
+ /* Do translation */
+ ret = flb_ra_get_kv_pair(ra, map, &start_key, &out_key, &out_val);
+ TEST_CHECK(ret == 0);
+ if (ret != 0) {
+ exit(EXIT_FAILURE);
+ }
+
+ TEST_CHECK(out_val->type == MSGPACK_OBJECT_STR);
+ TEST_CHECK(out_val->via.str.size == strlen(fmt_out));
+ TEST_CHECK(memcmp(out_val->via.str.ptr, fmt_out, strlen(fmt_out)) == 0);
+
+ flb_sds_destroy(fmt);
+ flb_ra_destroy(ra);
+ flb_free(out_buf);
+ msgpack_unpacked_destroy(&result);
+}
+
+void cb_update_key_val()
+{
+ int ret;
+ size_t off = 0;
+ char *json;
+ flb_sds_t fmt;
+ flb_sds_t updated_fmt;
+ char *fmt_out_key = "updated_key";
+ char *fmt_out_val = "updated_val";
+
+ char *out_buf = NULL;
+ size_t out_size = 0;
+
+ msgpack_unpacked result;
+ msgpack_unpacked out_result;
+
+ msgpack_object map;
+ msgpack_object *start_key = NULL;
+ msgpack_object *out_key = NULL;
+ msgpack_object *out_val = NULL;
+ void *updated_map;
+ msgpack_object in_key;
+ msgpack_object in_val;
+
+ struct flb_record_accessor *ra;
+ struct flb_record_accessor *updated_ra;
+
+ /* Sample JSON message */
+ json =
+ "{\"key1\": \"something\", "
+ "\"kubernetes\": "
+ " [true, "
+ " false, "
+ " {\"a\": false, "
+ " \"annotations\": { "
+ " \"fluentbit.io/tag\": \"thetag\""
+ "}}]}";
+ ret = create_map(json, &map, &out_buf, &result);
+ if(!TEST_CHECK(ret == 0)) {
+ TEST_MSG("failed create map");
+ if (out_buf != NULL) {
+ flb_free(out_buf);
+ }
+ exit(EXIT_FAILURE);
+ }
+
+ /* Formatter */
+ fmt = flb_sds_create("$kubernetes[2]['annotations']['fluentbit.io/tag']");
+ ra = flb_ra_create(fmt, FLB_FALSE);
+ if(!TEST_CHECK(ra != NULL)) {
+ exit(EXIT_FAILURE);
+ }
+
+ /* Formatter */
+ updated_fmt = flb_sds_create("$kubernetes[2]['annotations']['updated_key']");
+ updated_ra = flb_ra_create(updated_fmt, FLB_FALSE);
+ if(!TEST_CHECK(updated_ra != NULL)) {
+ exit(EXIT_FAILURE);
+ }
+
+ /* create key object to overwrite */
+ ret = set_str_to_msgpack_object(fmt_out_key, &in_key);
+ if (!TEST_CHECK(ret == 0)) {
+ TEST_MSG("failed to set object");
+ exit(EXIT_FAILURE);
+ }
+ /* create value object to overwrite */
+ ret = set_str_to_msgpack_object(fmt_out_val, &in_val);
+ if (!TEST_CHECK(ret == 0)) {
+ TEST_MSG("failed to set object");
+ exit(EXIT_FAILURE);
+ }
+
+ /* Update only value */
+ ret = flb_ra_update_kv_pair(ra, map, (void**)&updated_map, &out_size, &in_key, &in_val);
+ TEST_CHECK(ret == 0);
+ off = 0;
+ msgpack_unpacked_init(&out_result);
+ if (msgpack_unpack_next(&out_result, updated_map, out_size, &off)
+ != MSGPACK_UNPACK_SUCCESS) {
+ TEST_MSG("failed to unpack");
+ exit(EXIT_FAILURE);
+ }
+ ret = flb_ra_get_kv_pair(updated_ra, out_result.data, &start_key, &out_key, &out_val);
+ if (!TEST_CHECK(ret == 0)) {
+ printf("print out_result\n");
+ msgpack_object_print(stdout, out_result.data);
+ exit(EXIT_FAILURE);
+ }
+
+ /* Check updated key */
+ TEST_CHECK(out_key->type == MSGPACK_OBJECT_STR);
+ TEST_CHECK(out_key->via.str.size == strlen(fmt_out_key));
+ TEST_CHECK(memcmp(out_key->via.str.ptr, fmt_out_key, strlen(fmt_out_key)) == 0);
+
+ /* Check updated val */
+ TEST_CHECK(out_val->type == MSGPACK_OBJECT_STR);
+ TEST_CHECK(out_val->via.str.size == strlen(fmt_out_val));
+ TEST_CHECK(memcmp(out_val->via.str.ptr, fmt_out_val, strlen(fmt_out_val)) == 0);
+
+ msgpack_unpacked_destroy(&out_result);
+ msgpack_unpacked_destroy(&result);
+ flb_free(updated_map);
+ flb_sds_destroy(updated_fmt);
+ flb_sds_destroy(fmt);
+ flb_ra_destroy(updated_ra);
+ flb_ra_destroy(ra);
+ flb_free(out_buf);
+}
+
+void cb_update_val()
+{
+ int ret;
+ size_t off = 0;
+ char *json;
+ flb_sds_t fmt;
+ char *fmt_out = "updated";
+ char *out_buf = NULL;
+ size_t out_size = 0;
+
+ msgpack_unpacked result;
+ msgpack_unpacked out_result;
+
+ msgpack_object map;
+ msgpack_object *start_key = NULL;
+ msgpack_object *out_key = NULL;
+ msgpack_object *out_val = NULL;
+ void *updated_map;
+ msgpack_object in_val;
+
+ struct flb_record_accessor *ra;
+
+ /* Sample JSON message */
+ json =
+ "{\"key1\": \"something\", "
+ "\"kubernetes\": "
+ " [true, "
+ " false, "
+ " {\"a\": false, "
+ " \"annotations\": { "
+ " \"fluentbit.io/tag\": \"thetag\""
+ "}}]}";
+ ret = create_map(json, &map, &out_buf, &result);
+ if(!TEST_CHECK(ret == 0)) {
+ TEST_MSG("failed create map");
+ if (out_buf != NULL) {
+ flb_free(out_buf);
+ }
+ exit(EXIT_FAILURE);
+ }
+
+ /* Formatter */
+ fmt = flb_sds_create("$kubernetes[2]['annotations']['fluentbit.io/tag']");
+ ra = flb_ra_create(fmt, FLB_FALSE);
+ if(!TEST_CHECK(ra != NULL)) {
+ exit(EXIT_FAILURE);
+ }
+
+ /* create value object to overwrite */
+ ret = set_str_to_msgpack_object(fmt_out, &in_val);
+ if (!TEST_CHECK(ret == 0)) {
+ TEST_MSG("failed to set object");
+ exit(EXIT_FAILURE);
+ }
+
+ /* Update only value */
+ ret = flb_ra_update_kv_pair(ra, map, (void**)&updated_map, &out_size, NULL, &in_val);
+ TEST_CHECK(ret == 0);
+ off = 0;
+ msgpack_unpacked_init(&out_result);
+ if (msgpack_unpack_next(&out_result, updated_map, out_size, &off)
+ != MSGPACK_UNPACK_SUCCESS) {
+ TEST_MSG("failed to unpack");
+ exit(EXIT_FAILURE);
+ }
+ ret = flb_ra_get_kv_pair(ra, out_result.data, &start_key, &out_key, &out_val);
+ if (!TEST_CHECK(ret == 0)) {
+ printf("print out_result\n");
+ msgpack_object_print(stdout, out_result.data);
+ exit(EXIT_FAILURE);
+ }
+
+ /* Check updated val */
+ TEST_CHECK(out_val->type == MSGPACK_OBJECT_STR);
+ TEST_CHECK(out_val->via.str.size == strlen(fmt_out));
+ TEST_CHECK(memcmp(out_val->via.str.ptr, fmt_out, strlen(fmt_out)) == 0);
+
+ msgpack_unpacked_destroy(&out_result);
+ msgpack_unpacked_destroy(&result);
+ flb_free(updated_map);
+ flb_sds_destroy(fmt);
+ flb_ra_destroy(ra);
+ flb_free(out_buf);
+}
+
+void cb_update_key()
+{
+ int ret;
+ size_t off = 0;
+ char *json;
+ flb_sds_t fmt;
+ flb_sds_t updated_fmt;
+ char *fmt_out = "updated_key";
+
+
+ char *out_buf = NULL;
+ size_t out_size = 0;
+
+ msgpack_unpacked result;
+ msgpack_unpacked out_result;
+
+ msgpack_object map;
+ msgpack_object *start_key = NULL;
+ msgpack_object *out_key = NULL;
+ msgpack_object *out_val = NULL;
+ void *updated_map;
+ msgpack_object in_key;
+
+ struct flb_record_accessor *ra;
+ struct flb_record_accessor *updated_ra;
+
+ /* Sample JSON message */
+ json =
+ "{\"key1\": \"something\", "
+ "\"kubernetes\": "
+ " [true, "
+ " false, "
+ " {\"a\": false, "
+ " \"annotations\": { "
+ " \"fluentbit.io/tag\": \"thetag\""
+ "}}]}";
+ ret = create_map(json, &map, &out_buf, &result);
+ if(!TEST_CHECK(ret == 0)) {
+ TEST_MSG("failed create map");
+ if (out_buf != NULL) {
+ flb_free(out_buf);
+ }
+ exit(EXIT_FAILURE);
+ }
+
+ /* Formatter */
+ fmt = flb_sds_create("$kubernetes[2]['annotations']['fluentbit.io/tag']");
+ ra = flb_ra_create(fmt, FLB_FALSE);
+ if(!TEST_CHECK(ra != NULL)) {
+ exit(EXIT_FAILURE);
+ }
+
+ /* Formatter */
+ updated_fmt = flb_sds_create("$kubernetes[2]['annotations']['updated_key']");
+ updated_ra = flb_ra_create(updated_fmt, FLB_FALSE);
+ if(!TEST_CHECK(updated_ra != NULL)) {
+ exit(EXIT_FAILURE);
+ }
+
+ /* create key object to overwrite */
+ ret = set_str_to_msgpack_object(fmt_out, &in_key);
+ if (!TEST_CHECK(ret == 0)) {
+ TEST_MSG("failed to set object");
+ exit(EXIT_FAILURE);
+ }
+
+ /* Update only value */
+ ret = flb_ra_update_kv_pair(ra, map, (void**)&updated_map, &out_size, &in_key, NULL);
+ TEST_CHECK(ret == 0);
+ off = 0;
+ msgpack_unpacked_init(&out_result);
+ if (msgpack_unpack_next(&out_result, updated_map, out_size, &off)
+ != MSGPACK_UNPACK_SUCCESS) {
+ TEST_MSG("failed to unpack");
+ exit(EXIT_FAILURE);
+ }
+ ret = flb_ra_get_kv_pair(updated_ra, out_result.data, &start_key, &out_key, &out_val);
+ if (!TEST_CHECK(ret == 0)) {
+ printf("print out_result\n");
+ msgpack_object_print(stdout, out_result.data);
+ exit(EXIT_FAILURE);
+ }
+
+ /* Check updated key */
+ TEST_CHECK(out_key->type == MSGPACK_OBJECT_STR);
+ TEST_CHECK(out_key->via.str.size == strlen(fmt_out));
+ TEST_CHECK(memcmp(out_key->via.str.ptr, fmt_out, strlen(fmt_out)) == 0);
+
+ msgpack_unpacked_destroy(&out_result);
+ msgpack_unpacked_destroy(&result);
+ flb_free(updated_map);
+ flb_sds_destroy(updated_fmt);
+ flb_sds_destroy(fmt);
+ flb_ra_destroy(updated_ra);
+ flb_ra_destroy(ra);
+ flb_free(out_buf);
+}
+
+void cb_dash_key()
+{
+ int len;
+ int ret;
+ int type;
+ size_t off = 0;
+ char *out_buf;
+ size_t out_size;
+ char *json;
+ char *fmt;
+ char *fmt_out;
+ flb_sds_t str;
+ msgpack_unpacked result;
+ msgpack_object map;
+ struct flb_record_accessor *ra;
+
+ /* Sample JSON message */
+ json = "{\"key-dash\": \"something\"}";
+
+ /* Convert to msgpack */
+ len = strlen(json);
+ ret = flb_pack_json(json, len, &out_buf, &out_size, &type, NULL);
+ TEST_CHECK(ret == 0);
+ if (ret == -1) {
+ exit(EXIT_FAILURE);
+ }
+
+ /* Formatter */
+ fmt = flb_sds_create("$key-dash");
+ fmt_out = "something";
+
+ ra = flb_ra_create(fmt, FLB_FALSE);
+ TEST_CHECK(ra != NULL);
+ if (!ra) {
+ exit(EXIT_FAILURE);
+ }
+
+ /* Unpack msgpack object */
+ msgpack_unpacked_init(&result);
+ msgpack_unpack_next(&result, out_buf, out_size, &off);
+ map = result.data;
+
+ /* Do translation */
+ str = flb_ra_translate(ra, NULL, -1, map, NULL);
+ TEST_CHECK(str != NULL);
+ if (!str) {
+ exit(EXIT_FAILURE);
+ }
+
+ TEST_CHECK(flb_sds_len(str) == strlen(fmt_out));
+ TEST_CHECK(memcmp(str, fmt_out, strlen(fmt_out)) == 0);
+ printf("== input ==\n%s\n== output ==\n%s\n", str, fmt_out);
+
+ flb_sds_destroy(str);
+ flb_sds_destroy(fmt);
+ flb_ra_destroy(ra);
+ flb_free(out_buf);
+ msgpack_unpacked_destroy(&result);
+}
+
+void cb_dot_and_slash_key()
+{
+ int len;
+ int ret;
+ int type;
+ size_t off = 0;
+ char *out_buf;
+ size_t out_size;
+ char *json;
+ char *fmt;
+ char *fmt_out;
+ flb_sds_t str;
+ msgpack_unpacked result;
+ msgpack_object map;
+ struct flb_record_accessor *ra;
+
+ /* Sample JSON message */
+ json = "{\"root.with/symbols\": \"something\"}";
+
+ /* Convert to msgpack */
+ len = strlen(json);
+ ret = flb_pack_json(json, len, &out_buf, &out_size, &type, NULL);
+ TEST_CHECK(ret == 0);
+ if (ret == -1) {
+ exit(EXIT_FAILURE);
+ }
+
+ /* Formatter */
+ fmt = flb_sds_create("$root.with/symbols");
+ if (!TEST_CHECK(fmt != NULL)) {
+ exit(EXIT_FAILURE);
+ }
+
+ fmt_out = "something";
+
+ ra = flb_ra_create(fmt, FLB_FALSE);
+ TEST_CHECK(ra != NULL);
+ if (!ra) {
+ exit(EXIT_FAILURE);
+ }
+
+ /* Unpack msgpack object */
+ msgpack_unpacked_init(&result);
+ msgpack_unpack_next(&result, out_buf, out_size, &off);
+ map = result.data;
+
+ /* Do translation */
+ str = flb_ra_translate(ra, NULL, -1, map, NULL);
+ TEST_CHECK(str != NULL);
+ if (!str) {
+ exit(EXIT_FAILURE);
+ }
+
+ TEST_CHECK(flb_sds_len(str) == strlen(fmt_out));
+ TEST_CHECK(memcmp(str, fmt_out, strlen(fmt_out)) == 0);
+ printf("== input ==\n%s\n== output ==\n%s\n", str, fmt_out);
+
+ flb_sds_destroy(str);
+ flb_sds_destroy(fmt);
+ flb_ra_destroy(ra);
+ flb_free(out_buf);
+ msgpack_unpacked_destroy(&result);
+}
+
+static int order_lookup_check(char *buf, size_t size,
+ char *fmt, char *expected_out)
+{
+ size_t off = 0;
+ char *fmt_out;
+ flb_sds_t str;
+ msgpack_unpacked result;
+ msgpack_object map;
+ struct flb_record_accessor *ra;
+
+ /* Check bool is 'true' */
+ fmt = flb_sds_create(fmt);
+ if (!TEST_CHECK(fmt != NULL)) {
+ exit(EXIT_FAILURE);
+ }
+ fmt_out = expected_out;
+
+ ra = flb_ra_create(fmt, FLB_FALSE);
+ TEST_CHECK(ra != NULL);
+ if (!ra) {
+ exit(EXIT_FAILURE);
+ }
+
+ /* Unpack msgpack object */
+ msgpack_unpacked_init(&result);
+ msgpack_unpack_next(&result, buf, size, &off);
+ map = result.data;
+
+ /* Do translation */
+ str = flb_ra_translate(ra, NULL, -1, map, NULL);
+ TEST_CHECK(str != NULL);
+ if (!str) {
+ exit(EXIT_FAILURE);
+ }
+
+ TEST_CHECK(flb_sds_len(str) == strlen(expected_out));
+ if (flb_sds_len(str) != strlen(expected_out)) {
+ printf("received: '%s', expected: '%s'\n", str, fmt_out);
+ }
+
+ TEST_CHECK(memcmp(str, expected_out, strlen(expected_out)) == 0);
+
+ flb_sds_destroy(str);
+ flb_sds_destroy(fmt);
+ flb_ra_destroy(ra);
+ msgpack_unpacked_destroy(&result);
+
+ return 0;
+}
+
+void cb_key_order_lookup()
+{
+ int len;
+ int ret;
+ int type;
+ char *out_buf;
+ size_t out_size;
+ char *json;
+
+ /* Sample JSON message */
+ json = "{\"key\": \"abc\", \"bool\": false, \"bool\": true, "
+ "\"str\": \"bad\", \"str\": \"good\", "
+ "\"num\": 0, \"num\": 1}";
+
+ /* Convert to msgpack */
+ len = strlen(json);
+ ret = flb_pack_json(json, len, &out_buf, &out_size, &type, NULL);
+ TEST_CHECK(ret == 0);
+ if (ret == -1) {
+ exit(EXIT_FAILURE);
+ }
+
+ printf("\n-- record --\n");
+ flb_pack_print(out_buf, out_size);
+
+ /* check expected outputs per record accessor pattern */
+ order_lookup_check(out_buf, out_size, "$bool", "true");
+ order_lookup_check(out_buf, out_size, "$str" , "good");
+ order_lookup_check(out_buf, out_size, "$num" , "1");
+
+ flb_free(out_buf);
+}
+
+void cb_issue_4917()
+{
+ int len;
+ int ret;
+ int type;
+ char *out_buf;
+ size_t out_size;
+ char *json;
+ char *fmt_out;
+ size_t off = 0;
+ flb_sds_t fmt;
+ flb_sds_t str;
+ struct flb_record_accessor *ra;
+ msgpack_unpacked result;
+ msgpack_object map;
+
+ fmt_out = "from.new.fluent.bit.out";
+
+ /* Sample JSON message */
+ json = "{\"tool\": \"fluent\", \"sub\": {\"s1\": {\"s2\": \"bit\"}}}";
+ /* Convert to msgpack */
+ len = strlen(json);
+ ret = flb_pack_json(json, len, &out_buf, &out_size, &type, NULL);
+ TEST_CHECK(ret == 0);
+ if (ret == -1) {
+ exit(EXIT_FAILURE);
+ }
+
+ printf("\n-- record --\n");
+ flb_pack_print(out_buf, out_size);
+
+ /* Formatter */
+ fmt = flb_sds_create("from.new.$tool.$sub['s1']['s2'].out");
+ if (!TEST_CHECK(fmt != NULL)) {
+ flb_free(out_buf);
+ exit(EXIT_FAILURE);
+ }
+
+ /* create ra */
+ ra = flb_ra_create(fmt, FLB_FALSE);
+ TEST_CHECK(ra != NULL);
+ if (!ra) {
+ flb_sds_destroy(fmt);
+ flb_free(out_buf);
+ exit(EXIT_FAILURE);
+ }
+
+ /* Unpack msgpack object */
+ msgpack_unpacked_init(&result);
+ msgpack_unpack_next(&result, out_buf, out_size, &off);
+ map = result.data;
+
+ /* Do translation */
+ str = flb_ra_translate(ra, NULL, -1, map, NULL);
+ TEST_CHECK(str != NULL);
+ if (!str) {
+ flb_ra_destroy(ra);
+ msgpack_unpacked_destroy(&result);
+ flb_sds_destroy(fmt);
+ flb_free(out_buf);
+ exit(EXIT_FAILURE);
+ }
+ TEST_CHECK(flb_sds_len(str) == strlen(fmt_out));
+ TEST_CHECK(memcmp(str, fmt_out, strlen(fmt_out)) == 0);
+ printf("== input ==\n%s\n== output ==\n%s\n", str, fmt_out);
+
+ flb_sds_destroy(fmt);
+ flb_sds_destroy(str);
+ flb_ra_destroy(ra);
+ flb_free(out_buf);
+ msgpack_unpacked_destroy(&result);
+}
+
+void cb_update_root_key()
+{
+ int ret;
+ size_t off = 0;
+ char *json;
+ flb_sds_t fmt;
+ flb_sds_t updated_fmt;
+ char *fmt_out = "updated_key";
+
+
+ char *out_buf = NULL;
+ size_t out_size = 0;
+
+ msgpack_unpacked result;
+ msgpack_unpacked out_result;
+
+ msgpack_object map;
+ msgpack_object *start_key = NULL;
+ msgpack_object *out_key = NULL;
+ msgpack_object *out_val = NULL;
+ void *updated_map;
+ msgpack_object in_key;
+
+ struct flb_record_accessor *ra;
+ struct flb_record_accessor *updated_ra;
+
+ /* Sample JSON message */
+ json =
+ "{\"key1\": \"something\", "
+ "\"kubernetes\": "
+ " [true, "
+ " false, "
+ " {\"a\": false, "
+ " \"annotations\": { "
+ " \"fluentbit.io/tag\": \"thetag\""
+ "}}]}";
+ ret = create_map(json, &map, &out_buf, &result);
+ if(!TEST_CHECK(ret == 0)) {
+ TEST_MSG("failed create map");
+ if (out_buf != NULL) {
+ flb_free(out_buf);
+ }
+ exit(EXIT_FAILURE);
+ }
+
+ /* Formatter */
+ fmt = flb_sds_create("$key1");
+ ra = flb_ra_create(fmt, FLB_FALSE);
+ if(!TEST_CHECK(ra != NULL)) {
+ exit(EXIT_FAILURE);
+ }
+
+ /* Formatter */
+ updated_fmt = flb_sds_create("$updated_key");
+ updated_ra = flb_ra_create(updated_fmt, FLB_FALSE);
+ if(!TEST_CHECK(updated_ra != NULL)) {
+ exit(EXIT_FAILURE);
+ }
+
+ /* create key object to overwrite */
+ ret = set_str_to_msgpack_object(fmt_out, &in_key);
+ if (!TEST_CHECK(ret == 0)) {
+ TEST_MSG("failed to set object");
+ exit(EXIT_FAILURE);
+ }
+
+ /* Update only value */
+ ret = flb_ra_update_kv_pair(ra, map, (void**)&updated_map, &out_size, &in_key, NULL);
+ TEST_CHECK(ret == 0);
+ off = 0;
+ msgpack_unpacked_init(&out_result);
+ if (msgpack_unpack_next(&out_result, updated_map, out_size, &off)
+ != MSGPACK_UNPACK_SUCCESS) {
+ TEST_MSG("failed to unpack");
+ exit(EXIT_FAILURE);
+ }
+ ret = flb_ra_get_kv_pair(updated_ra, out_result.data, &start_key, &out_key, &out_val);
+ if (!TEST_CHECK(ret == 0)) {
+ printf("print out_result\n");
+ msgpack_object_print(stdout, out_result.data);
+ exit(EXIT_FAILURE);
+ }
+
+ /* Check updated key */
+ TEST_CHECK(out_key->type == MSGPACK_OBJECT_STR);
+ TEST_CHECK(out_key->via.str.size == strlen(fmt_out));
+ TEST_CHECK(memcmp(out_key->via.str.ptr, fmt_out, strlen(fmt_out)) == 0);
+
+ msgpack_unpacked_destroy(&out_result);
+ msgpack_unpacked_destroy(&result);
+ flb_free(updated_map);
+ flb_sds_destroy(updated_fmt);
+ flb_sds_destroy(fmt);
+ flb_ra_destroy(updated_ra);
+ flb_ra_destroy(ra);
+ flb_free(out_buf);
+}
+
+void cb_update_root_key_val()
+{
+ int ret;
+ size_t off = 0;
+ char *json;
+ flb_sds_t fmt;
+ flb_sds_t updated_fmt;
+ char *fmt_out_key = "updated_key";
+ char *fmt_out_val = "updated_val";
+
+ char *out_buf = NULL;
+ size_t out_size = 0;
+
+ msgpack_unpacked result;
+ msgpack_unpacked out_result;
+
+ msgpack_object map;
+ msgpack_object *start_key = NULL;
+ msgpack_object *out_key = NULL;
+ msgpack_object *out_val = NULL;
+ void *updated_map;
+ msgpack_object in_key;
+ msgpack_object in_val;
+
+ struct flb_record_accessor *ra;
+ struct flb_record_accessor *updated_ra;
+
+ /* Sample JSON message */
+ json =
+ "{\"key1\": \"something\", "
+ "\"kubernetes\": "
+ " [true, "
+ " false, "
+ " {\"a\": false, "
+ " \"annotations\": { "
+ " \"fluentbit.io/tag\": \"thetag\""
+ "}}]}";
+ ret = create_map(json, &map, &out_buf, &result);
+ if(!TEST_CHECK(ret == 0)) {
+ TEST_MSG("failed create map");
+ if (out_buf != NULL) {
+ flb_free(out_buf);
+ }
+ exit(EXIT_FAILURE);
+ }
+
+ /* Formatter */
+ fmt = flb_sds_create("$key1");
+ ra = flb_ra_create(fmt, FLB_FALSE);
+ if(!TEST_CHECK(ra != NULL)) {
+ exit(EXIT_FAILURE);
+ }
+
+ /* Formatter */
+ updated_fmt = flb_sds_create("$updated_key");
+ updated_ra = flb_ra_create(updated_fmt, FLB_FALSE);
+ if(!TEST_CHECK(updated_ra != NULL)) {
+ exit(EXIT_FAILURE);
+ }
+
+ /* create key object to overwrite */
+ ret = set_str_to_msgpack_object(fmt_out_key, &in_key);
+ if (!TEST_CHECK(ret == 0)) {
+ TEST_MSG("failed to set object");
+ exit(EXIT_FAILURE);
+ }
+ /* create value object to overwrite */
+ ret = set_str_to_msgpack_object(fmt_out_val, &in_val);
+ if (!TEST_CHECK(ret == 0)) {
+ TEST_MSG("failed to set object");
+ exit(EXIT_FAILURE);
+ }
+
+ /* Update only value */
+ ret = flb_ra_update_kv_pair(ra, map, (void**)&updated_map, &out_size, &in_key, &in_val);
+ TEST_CHECK(ret == 0);
+ off = 0;
+ msgpack_unpacked_init(&out_result);
+ if (msgpack_unpack_next(&out_result, updated_map, out_size, &off)
+ != MSGPACK_UNPACK_SUCCESS) {
+ TEST_MSG("failed to unpack");
+ exit(EXIT_FAILURE);
+ }
+ ret = flb_ra_get_kv_pair(updated_ra, out_result.data, &start_key, &out_key, &out_val);
+ if (!TEST_CHECK(ret == 0)) {
+ printf("print out_result\n");
+ msgpack_object_print(stdout, out_result.data);
+ exit(EXIT_FAILURE);
+ }
+
+ /* Check updated key */
+ TEST_CHECK(out_key->type == MSGPACK_OBJECT_STR);
+ TEST_CHECK(out_key->via.str.size == strlen(fmt_out_key));
+ TEST_CHECK(memcmp(out_key->via.str.ptr, fmt_out_key, strlen(fmt_out_key)) == 0);
+
+ /* Check updated val */
+ TEST_CHECK(out_val->type == MSGPACK_OBJECT_STR);
+ TEST_CHECK(out_val->via.str.size == strlen(fmt_out_val));
+ TEST_CHECK(memcmp(out_val->via.str.ptr, fmt_out_val, strlen(fmt_out_val)) == 0);
+
+ msgpack_unpacked_destroy(&out_result);
+ msgpack_unpacked_destroy(&result);
+ flb_free(updated_map);
+ flb_sds_destroy(updated_fmt);
+ flb_sds_destroy(fmt);
+ flb_ra_destroy(updated_ra);
+ flb_ra_destroy(ra);
+ flb_free(out_buf);
+}
+
+void cb_add_key_val()
+{
+ int ret;
+ size_t off = 0;
+ char *json;
+ flb_sds_t fmt;
+ flb_sds_t updated_fmt;
+ char *fmt_out_key = "add_key";
+ char *fmt_out_val = "add_val";
+
+ char *out_buf = NULL;
+ size_t out_size = 0;
+
+ msgpack_unpacked result;
+ msgpack_unpacked out_result;
+
+ msgpack_object map;
+ msgpack_object *start_key = NULL;
+ msgpack_object *out_key = NULL;
+ msgpack_object *out_val = NULL;
+ void *updated_map;
+ msgpack_object in_val;
+
+ struct flb_record_accessor *ra;
+ struct flb_record_accessor *updated_ra;
+
+ /* Sample JSON message */
+ json =
+ "{\"key1\": \"something\", "
+ "\"kubernetes\": "
+ " [true, "
+ " false, "
+ " {\"a\": false, "
+ " \"annotations\": { "
+ " \"fluentbit.io/tag\": \"thetag\""
+ "}}]}";
+ ret = create_map(json, &map, &out_buf, &result);
+ if(!TEST_CHECK(ret == 0)) {
+ TEST_MSG("failed create map");
+ if (out_buf != NULL) {
+ flb_free(out_buf);
+ }
+ exit(EXIT_FAILURE);
+ }
+
+ /* Formatter */
+ fmt = flb_sds_create("$kubernetes[2]['annotations']['add_key']");
+ ra = flb_ra_create(fmt, FLB_FALSE);
+ if(!TEST_CHECK(ra != NULL)) {
+ exit(EXIT_FAILURE);
+ }
+
+ /* Formatter */
+ updated_fmt = flb_sds_create("$kubernetes[2]['annotations']['add_key']");
+ updated_ra = flb_ra_create(updated_fmt, FLB_FALSE);
+ if(!TEST_CHECK(updated_ra != NULL)) {
+ exit(EXIT_FAILURE);
+ }
+
+ /* create value object to overwrite */
+ ret = set_str_to_msgpack_object(fmt_out_val, &in_val);
+ if (!TEST_CHECK(ret == 0)) {
+ TEST_MSG("failed to set object");
+ exit(EXIT_FAILURE);
+ }
+
+ /* Add key/value */
+ ret = flb_ra_append_kv_pair(ra, map, (void**)&updated_map, &out_size, &in_val);
+ TEST_CHECK(ret == 0);
+
+ off = 0;
+ msgpack_unpacked_init(&out_result);
+ ret = msgpack_unpack_next(&out_result, updated_map, out_size, &off);
+ if (!TEST_CHECK(ret == MSGPACK_UNPACK_SUCCESS)) {
+ TEST_MSG("failed to unpack");
+ exit(EXIT_FAILURE);
+ }
+ ret = flb_ra_get_kv_pair(updated_ra, out_result.data, &start_key, &out_key, &out_val);
+ if (!TEST_CHECK(ret == 0)) {
+ TEST_MSG("print out_result\n");
+ msgpack_object_print(stdout, out_result.data);
+ exit(EXIT_FAILURE);
+ }
+
+ /* Check updated key */
+ TEST_CHECK(out_key->type == MSGPACK_OBJECT_STR);
+ TEST_CHECK(out_key->via.str.size == strlen(fmt_out_key));
+ TEST_CHECK(memcmp(out_key->via.str.ptr, fmt_out_key, strlen(fmt_out_key)) == 0);
+
+ /* Check updated val */
+ TEST_CHECK(out_val->type == MSGPACK_OBJECT_STR);
+ TEST_CHECK(out_val->via.str.size == strlen(fmt_out_val));
+ TEST_CHECK(memcmp(out_val->via.str.ptr, fmt_out_val, strlen(fmt_out_val)) == 0);
+
+ msgpack_unpacked_destroy(&out_result);
+ msgpack_unpacked_destroy(&result);
+ flb_free(updated_map);
+ flb_sds_destroy(updated_fmt);
+ flb_sds_destroy(fmt);
+ flb_ra_destroy(updated_ra);
+ flb_ra_destroy(ra);
+ flb_free(out_buf);
+}
+
+void cb_add_root_key_val()
+{
+ int ret;
+ size_t off = 0;
+ char *json;
+ flb_sds_t fmt;
+ flb_sds_t updated_fmt;
+ char *fmt_out_key = "add_key";
+ char *fmt_out_val = "add_val";
+
+ char *out_buf = NULL;
+ size_t out_size = 0;
+
+ msgpack_unpacked result;
+ msgpack_unpacked out_result;
+
+ msgpack_object map;
+ msgpack_object *start_key = NULL;
+ msgpack_object *out_key = NULL;
+ msgpack_object *out_val = NULL;
+ void *updated_map;
+ msgpack_object in_val;
+
+ struct flb_record_accessor *ra;
+ struct flb_record_accessor *updated_ra;
+
+ /* Sample JSON message */
+ json =
+ "{\"key1\": \"something\", "
+ "\"kubernetes\": "
+ " [true, "
+ " false, "
+ " {\"a\": false, "
+ " \"annotations\": { "
+ " \"fluentbit.io/tag\": \"thetag\""
+ "}}]}";
+ ret = create_map(json, &map, &out_buf, &result);
+ if(!TEST_CHECK(ret == 0)) {
+ TEST_MSG("failed create map");
+ if (out_buf != NULL) {
+ flb_free(out_buf);
+ }
+ exit(EXIT_FAILURE);
+ }
+
+ /* Formatter */
+ fmt = flb_sds_create("$add_key");
+ ra = flb_ra_create(fmt, FLB_FALSE);
+ if(!TEST_CHECK(ra != NULL)) {
+ exit(EXIT_FAILURE);
+ }
+
+ /* Formatter */
+ updated_fmt = flb_sds_create("$add_key");
+ updated_ra = flb_ra_create(updated_fmt, FLB_FALSE);
+ if(!TEST_CHECK(updated_ra != NULL)) {
+ exit(EXIT_FAILURE);
+ }
+
+ /* create value object to overwrite */
+ ret = set_str_to_msgpack_object(fmt_out_val, &in_val);
+ if (!TEST_CHECK(ret == 0)) {
+ TEST_MSG("failed to set object");
+ exit(EXIT_FAILURE);
+ }
+
+ /* Add key/value */
+ ret = flb_ra_append_kv_pair(ra, map, (void**)&updated_map, &out_size, &in_val);
+ TEST_CHECK(ret == 0);
+
+ off = 0;
+ msgpack_unpacked_init(&out_result);
+ ret = msgpack_unpack_next(&out_result, updated_map, out_size, &off);
+ if (!TEST_CHECK(ret == MSGPACK_UNPACK_SUCCESS)) {
+ TEST_MSG("failed to unpack");
+ exit(EXIT_FAILURE);
+ }
+ ret = flb_ra_get_kv_pair(updated_ra, out_result.data, &start_key, &out_key, &out_val);
+ if (!TEST_CHECK(ret == 0)) {
+ TEST_MSG("print out_result\n");
+ msgpack_object_print(stdout, out_result.data);
+ exit(EXIT_FAILURE);
+ }
+
+ /* Check updated key */
+ TEST_CHECK(out_key->type == MSGPACK_OBJECT_STR);
+ TEST_CHECK(out_key->via.str.size == strlen(fmt_out_key));
+ TEST_CHECK(memcmp(out_key->via.str.ptr, fmt_out_key, strlen(fmt_out_key)) == 0);
+
+ /* Check updated val */
+ TEST_CHECK(out_val->type == MSGPACK_OBJECT_STR);
+ TEST_CHECK(out_val->via.str.size == strlen(fmt_out_val));
+ TEST_CHECK(memcmp(out_val->via.str.ptr, fmt_out_val, strlen(fmt_out_val)) == 0);
+
+ msgpack_unpacked_destroy(&out_result);
+ msgpack_unpacked_destroy(&result);
+ flb_free(updated_map);
+ flb_sds_destroy(updated_fmt);
+ flb_sds_destroy(fmt);
+ flb_ra_destroy(updated_ra);
+ flb_ra_destroy(ra);
+ flb_free(out_buf);
+}
+
+void cb_ra_translate_check()
+{
+ int len;
+ int ret;
+ int type;
+ size_t off = 0;
+ char *out_buf;
+ size_t out_size;
+ char *json;
+ char *fmt;
+ flb_sds_t str;
+ msgpack_unpacked result;
+ msgpack_object map;
+ struct flb_record_accessor *ra;
+ int check_translation = FLB_TRUE;
+
+ /* Sample JSON message */
+ json = "{\"root.with/symbols\": \"something\"}";
+
+ /* Convert to msgpack */
+ len = strlen(json);
+ ret = flb_pack_json(json, len, &out_buf, &out_size, &type, NULL);
+ TEST_CHECK(ret == 0);
+ if (ret == -1) {
+ exit(EXIT_FAILURE);
+ }
+
+ /* Formatter */
+ fmt = flb_sds_create("$root");
+ if (!TEST_CHECK(fmt != NULL)) {
+ exit(EXIT_FAILURE);
+ }
+
+ ra = flb_ra_create(fmt, FLB_FALSE);
+ TEST_CHECK(ra != NULL);
+ if (!ra) {
+ exit(EXIT_FAILURE);
+ }
+
+ /* Unpack msgpack object */
+ msgpack_unpacked_init(&result);
+ msgpack_unpack_next(&result, out_buf, out_size, &off);
+ map = result.data;
+
+ /* Do translation - with check enabled */
+ str = flb_ra_translate_check(ra, NULL, -1, map, NULL, check_translation);
+ /* since translation fails and check is enabled, it returns NULL */
+ TEST_CHECK(str == NULL);
+ if (str) {
+ exit(EXIT_FAILURE);
+ }
+
+ flb_sds_destroy(str);
+ flb_sds_destroy(fmt);
+ flb_ra_destroy(ra);
+ flb_free(out_buf);
+ msgpack_unpacked_destroy(&result);
+}
+
+/*
+ * https://github.com/fluent/fluent-bit/issues/5936
+ * If the last nested element is an array, record accessor can't get its value.
+ */
+void cb_issue_5936_last_array()
+{
+ int len;
+ int ret;
+ int type;
+ size_t off = 0;
+ char *out_buf;
+ size_t out_size;
+ char *json;
+ char *fmt;
+ char *fmt_out;
+ flb_sds_t str;
+ msgpack_unpacked result;
+ msgpack_object map;
+ struct flb_record_accessor *ra;
+
+ /* Sample JSON message */
+ json ="{ \"key\": {\"nested\":[\"val0\", \"val1\"]}}";
+
+
+ /* Convert to msgpack */
+ len = strlen(json);
+ ret = flb_pack_json(json, len, &out_buf, &out_size, &type, NULL);
+ TEST_CHECK(ret == 0);
+ if (ret == -1) {
+ exit(EXIT_FAILURE);
+ }
+
+ /* Formatter */
+ fmt = flb_sds_create("$key['nested'][1]");
+ fmt_out = "val1";
+
+ ra = flb_ra_create(fmt, FLB_FALSE);
+ TEST_CHECK(ra != NULL);
+ if (!ra) {
+ exit(EXIT_FAILURE);
+ }
+
+ /* Unpack msgpack object */
+ msgpack_unpacked_init(&result);
+ msgpack_unpack_next(&result, out_buf, out_size, &off);
+ map = result.data;
+
+ /* Do translation */
+ str = flb_ra_translate(ra, NULL, -1, map, NULL);
+ TEST_CHECK(str != NULL);
+ if (!str) {
+ exit(EXIT_FAILURE);
+ }
+
+ TEST_CHECK(flb_sds_len(str) == strlen(fmt_out));
+ TEST_CHECK(memcmp(str, fmt_out, strlen(fmt_out)) == 0);
+ printf("== input ==\n%s\n== output ==\n%s\n", str, fmt_out);
+
+ flb_sds_destroy(str);
+ flb_sds_destroy(fmt);
+ flb_ra_destroy(ra);
+ flb_free(out_buf);
+ msgpack_unpacked_destroy(&result);
+}
+
+struct char_list_ra_str{
+ char **strs;
+ char *expect;
+};
+
+void cb_ra_create_str_from_list()
+{
+ char *case1[] = {"a", NULL};
+ char *case2[] = {"aa", "bb", "cc", NULL};
+
+ struct char_list_ra_str testcases[] = {
+ { .strs = &case1[0], .expect = "$a"},
+ { .strs = &case2[0], .expect = "$aa['bb']['cc']"},
+ };
+ size_t case_size = sizeof(testcases)/sizeof(struct char_list_ra_str);
+ int case_i;
+ struct flb_sds_list *list = NULL;
+ flb_sds_t ret_str;
+ char *str;
+ int i;
+ int ret;
+
+ for (case_i = 0; case_i < case_size; case_i++) {
+ list = flb_sds_list_create();
+ if (!TEST_CHECK(list != NULL)) {
+ TEST_MSG("%d: flb_sds_list_create failed", case_i);
+ exit(EXIT_FAILURE);
+ }
+ i = 0;
+ while(testcases[case_i].strs[i] != NULL) {
+ str = testcases[case_i].strs[i];
+ ret = flb_sds_list_add(list, str, strlen(str));
+ if (!TEST_CHECK(ret == 0)) {
+ TEST_MSG("%d: flb_sds_list_add failed", case_i);
+ flb_sds_list_destroy(list);
+ exit(EXIT_FAILURE);
+ }
+ i++;
+ }
+
+ ret_str = flb_ra_create_str_from_list(list);
+ if (!TEST_CHECK(ret_str != NULL)) {
+ TEST_MSG("%d: flb_ra_create_str_from failed", case_i);
+ flb_sds_list_destroy(list);
+ exit(EXIT_FAILURE);
+ }
+ if (!TEST_CHECK(strcmp(testcases[case_i].expect, ret_str) == 0)) {
+ TEST_MSG("%d: strcmp error.got=%s expect=%s", case_i, ret_str, testcases[case_i].expect);
+ }
+
+ flb_sds_destroy(ret_str);
+ flb_sds_list_destroy(list);
+ }
+
+
+ /* Error if we pass empty list */
+ list = flb_sds_list_create();
+ if (!TEST_CHECK(list != NULL)) {
+ TEST_MSG("flb_sds_list_create failed");
+ exit(EXIT_FAILURE);
+ }
+ ret_str = flb_ra_create_str_from_list(list);
+ if (!TEST_CHECK(ret_str == NULL)) {
+ TEST_MSG("flb_ra_create_str_from should be failed");
+ flb_sds_list_destroy(list);
+ exit(EXIT_FAILURE);
+ }
+ flb_sds_list_destroy(list);
+}
+
+/*
+ * https://github.com/fluent/fluent-bit/issues/7330
+ */
+void cb_issue_7330_single_char()
+{
+ int ret;
+ int type;
+ char *json;
+ char *out_buf = NULL;
+ size_t out_size;
+ size_t off = 0;
+ flb_sds_t input = NULL;
+ flb_sds_t out_tag = NULL;
+ struct flb_regex_search regex_result;
+ struct flb_record_accessor *ra_tag = NULL;
+ msgpack_unpacked result;
+ msgpack_object map;
+
+ json = "{\"tool\":\"fluent\"}";
+ ret = flb_pack_json(json, strlen(json), &out_buf, &out_size, &type, NULL);
+ if (!TEST_CHECK(ret == 0)) {
+ TEST_MSG("flb_pack_json failed");
+ exit(EXIT_FAILURE);
+ }
+
+ input = flb_sds_create("b");
+ if (!TEST_CHECK(input != NULL)) {
+ goto issue_7330;
+ }
+
+ /* create flb_record_accessor from single character */
+ ra_tag = flb_ra_create(input, FLB_FALSE);
+ if (!TEST_CHECK(ra_tag != NULL)) {
+ TEST_MSG("flb_ra_create failed");
+ goto issue_7330;
+ }
+
+ /* Unpack msgpack object */
+ msgpack_unpacked_init(&result);
+ msgpack_unpack_next(&result, out_buf, out_size, &off);
+ map = result.data;
+
+ /* Do translation */
+ out_tag = flb_ra_translate(ra_tag, "old", 3, map, &regex_result);
+ msgpack_unpacked_destroy(&result);
+ if (!TEST_CHECK(out_tag != NULL)) {
+ TEST_MSG("flb_ra_translate failed");
+ goto issue_7330;
+ }
+ else if (!TEST_CHECK(flb_sds_len(out_tag) > 0)) {
+ TEST_MSG("out_tag len error. len=%zd", flb_sds_len(out_tag));
+ goto issue_7330;
+ }
+
+ issue_7330:
+ if (input) {
+ flb_sds_destroy(input);
+ }
+ if (out_tag) {
+ flb_sds_destroy(out_tag);
+ }
+ if (out_buf) {
+ flb_free(out_buf);
+ }
+ if (ra_tag) {
+ flb_ra_destroy(ra_tag);
+ }
+}
+
+TEST_LIST = {
+ { "keys" , cb_keys},
+ { "dash_key" , cb_dash_key},
+ /*
+ * If #4370 is fixed, this testcase should be enabled.
+ { "dot_slash_key" , cb_dot_and_slash_key},
+ */
+ { "translate" , cb_translate},
+ { "translate_tag" , cb_translate_tag},
+ { "dots_subkeys" , cb_dots_subkeys},
+ { "array_id" , cb_array_id},
+ { "get_kv_pair" , cb_get_kv_pair},
+ { "key_order_lookup", cb_key_order_lookup},
+ { "update_key_val", cb_update_key_val},
+ { "update_key", cb_update_key},
+ { "update_val", cb_update_val},
+ { "update_root_key", cb_update_root_key},
+ { "update_root_key_val", cb_update_root_key_val},
+ { "add_key_val", cb_add_key_val},
+ { "add_root_key_val", cb_add_root_key_val},
+ { "issue_4917" , cb_issue_4917},
+ { "flb_ra_translate_check" , cb_ra_translate_check},
+ { "issue_5936_last_array" , cb_issue_5936_last_array},
+ { "ra_create_str_from_list", cb_ra_create_str_from_list},
+ { "issue_7330_single_character" , cb_issue_7330_single_char},
+ { NULL }
+};
diff --git a/fluent-bit/tests/internal/regex.c b/fluent-bit/tests/internal/regex.c
new file mode 100644
index 00000000..77045a38
--- /dev/null
+++ b/fluent-bit/tests/internal/regex.c
@@ -0,0 +1,330 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/* Fluent Bit
+ * ==========
+ * Copyright (C) 2019-2022 The Fluent Bit Authors
+ * Copyright (C) 2015-2018 Treasure Data Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <string.h>
+#include <fluent-bit/flb_regex.h>
+#include "flb_tests_internal.h"
+
+struct kv_list {
+ int index;
+ size_t size;
+ char **lists;
+};
+static void cb_kv(const char *name, const char *value,
+ size_t vlen, void *data)
+{
+ size_t len;
+ struct kv_list *l = (struct kv_list*)data;
+
+ if (!TEST_CHECK(name != NULL && value != NULL && data != NULL)) {
+ TEST_MSG("input is NULL");
+ return;
+ }
+ len = strlen(name);
+
+ if (!TEST_CHECK(len == strlen(l->lists[l->index * 2]))) {
+ TEST_MSG("name: lenght error. got:%zu expect:%zu", len, strlen(l->lists[l->index * 2]));
+ TEST_MSG("name: got:%s expect:%s", name, l->lists[l->index * 2]);
+ goto cb_kv_end;
+ }
+ if (!TEST_CHECK(strncmp(name, l->lists[l->index * 2], len) == 0)) {
+ TEST_MSG("name: mismatch. got:%s expect:%s", name, l->lists[l->index * 2]);
+ goto cb_kv_end;
+ }
+
+ if (!TEST_CHECK(vlen == strlen(l->lists[l->index * 2+1]))) {
+ TEST_MSG("value: lenght error. got:%zu expect:%zu", vlen, strlen(l->lists[l->index * 2+1]));
+ TEST_MSG("value: got:%s expect:%s", value, l->lists[l->index * 2+1]);
+ goto cb_kv_end;
+ }
+ if (!TEST_CHECK(strncmp(value, l->lists[l->index * 2+1], vlen) == 0)) {
+ TEST_MSG("value: mismatch. got:%s expect:%s", value, l->lists[l->index * 2+1]);
+ goto cb_kv_end;
+ }
+ cb_kv_end:
+ l->index++;
+}
+
+static void test_basic()
+{
+ struct flb_regex *regex = NULL;
+ struct flb_regex_search result;
+ int ret;
+ ssize_t size;
+ const char *input = "string 1234 2022/10/24";
+
+ char *expected_strs[] = {"str","string", "num","1234", "time","2022/10/24"};
+ struct kv_list expected = {
+ .index = 0,
+ .size = sizeof(expected_strs)/sizeof(char *),
+ .lists = &expected_strs[0],
+ };
+
+ regex = flb_regex_create("/(?<str>[a-z]+) (?<num>\\d+) (?<time>\\d{4}/\\d{2}/\\d{2})/");
+ if (!TEST_CHECK(regex != NULL)) {
+ TEST_MSG("flb_regex_create failed");
+ exit(1);
+ }
+
+ size = flb_regex_do(regex, input, strlen(input), &result);
+ if (!TEST_CHECK(size >= 0)) {
+ TEST_MSG("flb_regex_do failed");
+ flb_regex_destroy(regex);
+ exit(1);
+ }
+
+ ret = flb_regex_parse(regex, &result, cb_kv, &expected);
+ if (!TEST_CHECK(ret != -1)) {
+ TEST_MSG("flb_regex_parse failed");
+ flb_regex_destroy(regex);
+ exit(1);
+ }
+
+ ret = flb_regex_destroy(regex);
+ if (!TEST_CHECK(ret == 0)) {
+ TEST_MSG("flb_regex_destroy failed");
+ exit(1);
+ }
+}
+
+/* test uri to check if misunderstanding "/pattern/option" */
+static void test_uri()
+{
+ struct flb_regex *regex = NULL;
+ struct flb_regex_search result;
+ int ret;
+ ssize_t size;
+ const char *input = "/uri/is/hoge";
+
+ char *expected_strs[] = {"middle","is"};
+ struct kv_list expected = {
+ .index = 0,
+ .size = sizeof(expected_strs)/sizeof(char *),
+ .lists = &expected_strs[0],
+ };
+
+ regex = flb_regex_create("/uri/(?<middle>[a-z]+)/hoge");
+ if (!TEST_CHECK(regex != NULL)) {
+ TEST_MSG("flb_regex_create failed");
+ exit(1);
+ }
+
+ size = flb_regex_do(regex, input, strlen(input), &result);
+ if (!TEST_CHECK(size >= 0)) {
+ TEST_MSG("flb_regex_do failed");
+ flb_regex_destroy(regex);
+ exit(1);
+ }
+
+ ret = flb_regex_parse(regex, &result, cb_kv, &expected);
+ if (!TEST_CHECK(ret != -1)) {
+ TEST_MSG("flb_regex_parse failed");
+ flb_regex_destroy(regex);
+ exit(1);
+ }
+
+ ret = flb_regex_destroy(regex);
+ if (!TEST_CHECK(ret == 0)) {
+ TEST_MSG("flb_regex_destroy failed");
+ exit(1);
+ }
+}
+
+/* test "/pattern/i" */
+static void test_option_ignore_case()
+{
+ struct flb_regex *regex = NULL;
+ struct flb_regex_search result;
+ int ret;
+ ssize_t size;
+ const char *input = "STRING";
+
+ char *expected_strs[] = {"str","STRING"};
+ struct kv_list expected = {
+ .index = 0,
+ .size = sizeof(expected_strs)/sizeof(char *),
+ .lists = &expected_strs[0],
+ };
+
+ regex = flb_regex_create("/(?<str>[a-z]+)/i");
+ if (!TEST_CHECK(regex != NULL)) {
+ TEST_MSG("flb_regex_create failed");
+ exit(1);
+ }
+
+ size = flb_regex_do(regex, input, strlen(input), &result);
+ if (!TEST_CHECK(size >= 0)) {
+ TEST_MSG("flb_regex_do failed");
+ flb_regex_destroy(regex);
+ exit(1);
+ }
+
+ ret = flb_regex_parse(regex, &result, cb_kv, &expected);
+ if (!TEST_CHECK(ret != -1)) {
+ TEST_MSG("flb_regex_parse failed");
+ flb_regex_destroy(regex);
+ exit(1);
+ }
+
+ ret = flb_regex_destroy(regex);
+ if (!TEST_CHECK(ret == 0)) {
+ TEST_MSG("flb_regex_destroy failed");
+ exit(1);
+ }
+}
+
+/* test "/pattern/m" */
+static void test_option_multiline()
+{
+ struct flb_regex *regex = NULL;
+ struct flb_regex_search result;
+ int ret;
+ ssize_t size;
+ const char *input = "string\n1234\nstring";
+
+ char *expected_strs[] = {"full_str","string\n1234\nstring"};
+ struct kv_list expected = {
+ .index = 0,
+ .size = sizeof(expected_strs)/sizeof(char *),
+ .lists = &expected_strs[0],
+ };
+
+ regex = flb_regex_create("/(?<full_str>.+)/m");
+ if (!TEST_CHECK(regex != NULL)) {
+ TEST_MSG("flb_regex_create failed");
+ exit(1);
+ }
+
+ size = flb_regex_do(regex, input, strlen(input), &result);
+ if (!TEST_CHECK(size >= 0)) {
+ TEST_MSG("flb_regex_do failed");
+ flb_regex_destroy(regex);
+ exit(1);
+ }
+
+ ret = flb_regex_parse(regex, &result, cb_kv, &expected);
+ if (!TEST_CHECK(ret != -1)) {
+ TEST_MSG("flb_regex_parse failed");
+ flb_regex_destroy(regex);
+ exit(1);
+ }
+
+ ret = flb_regex_destroy(regex);
+ if (!TEST_CHECK(ret == 0)) {
+ TEST_MSG("flb_regex_destroy failed");
+ exit(1);
+ }
+}
+
+/* test "/pattern/x" */
+static void test_option_extend()
+{
+ struct flb_regex *regex = NULL;
+ struct flb_regex_search result;
+ int ret;
+ ssize_t size;
+ const char *input = "3.14";
+
+ char *expected_strs[] = {"pi","3.14"};
+ struct kv_list expected = {
+ .index = 0,
+ .size = sizeof(expected_strs)/sizeof(char *),
+ .lists = &expected_strs[0],
+ };
+
+ regex = flb_regex_create("/(?<pi>\\d \\. 14)/x");
+ if (!TEST_CHECK(regex != NULL)) {
+ TEST_MSG("flb_regex_create failed");
+ exit(1);
+ }
+
+ size = flb_regex_do(regex, input, strlen(input), &result);
+ if (!TEST_CHECK(size >= 0)) {
+ TEST_MSG("flb_regex_do failed");
+ flb_regex_destroy(regex);
+ exit(1);
+ }
+
+ ret = flb_regex_parse(regex, &result, cb_kv, &expected);
+ if (!TEST_CHECK(ret != -1)) {
+ TEST_MSG("flb_regex_parse failed");
+ flb_regex_destroy(regex);
+ exit(1);
+ }
+
+ ret = flb_regex_destroy(regex);
+ if (!TEST_CHECK(ret == 0)) {
+ TEST_MSG("flb_regex_destroy failed");
+ exit(1);
+ }
+}
+
+/* test "/pattern/ix" */
+static void test_option_i_x()
+{
+ struct flb_regex *regex = NULL;
+ struct flb_regex_search result;
+ int ret;
+ ssize_t size;
+ const char *input = "3.14pi";
+
+ char *expected_strs[] = {"full_str","3.14pi"};
+ struct kv_list expected = {
+ .index = 0,
+ .size = sizeof(expected_strs)/sizeof(char *),
+ .lists = &expected_strs[0],
+ };
+
+ regex = flb_regex_create("/(?<full_str>\\d \\. 14PI)/ix");
+ if (!TEST_CHECK(regex != NULL)) {
+ TEST_MSG("flb_regex_create failed");
+ exit(1);
+ }
+
+ size = flb_regex_do(regex, input, strlen(input), &result);
+ if (!TEST_CHECK(size >= 0)) {
+ TEST_MSG("flb_regex_do failed");
+ flb_regex_destroy(regex);
+ exit(1);
+ }
+
+ ret = flb_regex_parse(regex, &result, cb_kv, &expected);
+ if (!TEST_CHECK(ret != -1)) {
+ TEST_MSG("flb_regex_parse failed");
+ flb_regex_destroy(regex);
+ exit(1);
+ }
+
+ ret = flb_regex_destroy(regex);
+ if (!TEST_CHECK(ret == 0)) {
+ TEST_MSG("flb_regex_destroy failed");
+ exit(1);
+ }
+}
+
+TEST_LIST = {
+ { "basic" , test_basic},
+ { "uri" , test_uri},
+ { "option_ignore_case", test_option_ignore_case},
+ { "option_multiline" , test_option_multiline},
+ { "option_extend" , test_option_extend},
+ { "option_i_x" , test_option_i_x},
+ { 0 }
+};
diff --git a/fluent-bit/tests/internal/reload.c b/fluent-bit/tests/internal/reload.c
new file mode 100644
index 00000000..e6a695d5
--- /dev/null
+++ b/fluent-bit/tests/internal/reload.c
@@ -0,0 +1,249 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+#include <fluent-bit/flb_info.h>
+#include <fluent-bit/flb_mem.h>
+#include <fluent-bit/flb_kv.h>
+#include <fluent-bit/flb_config_format.h>
+#include <fluent-bit/flb_lib.h>
+#include <fluent-bit/flb_reload.h>
+
+#include <cfl/cfl.h>
+#include <cfl/cfl_variant.h>
+#include <cfl/cfl_kvlist.h>
+
+#include "flb_tests_internal.h"
+
+#define FLB_YAML FLB_TESTS_DATA_PATH "/data/reload/yaml/processor.yaml"
+#define FLB_CLASSIC FLB_TESTS_DATA_PATH "/data/reload/fluent-bit.conf"
+
+void test_reconstruct_cf()
+{
+ struct flb_cf *cf;
+ struct flb_cf_section *s_tmp;
+ struct flb_cf_section *service;
+ struct flb_cf_group *g_tmp;
+ struct cfl_variant *ret;
+ struct flb_kv *meta;
+ struct flb_cf *new_cf;
+ int status;
+
+
+ /* create context */
+ cf = flb_cf_create();
+ TEST_CHECK(cf != NULL);
+
+ /* create service section */
+ service = flb_cf_section_create(cf, "SERVICE", 7);
+ TEST_CHECK(service != NULL);
+
+ /* add a property */
+ ret = flb_cf_section_property_add(cf, service->properties, "key", 3, "val", 3);
+ TEST_CHECK(ret != NULL);
+
+ /* add a property with empty spaces on left/right */
+ ret = flb_cf_section_property_add(cf, service->properties, " key ", 5, " val ", 7);
+ TEST_CHECK(ret != NULL);
+
+ /* add an invalid property */
+ ret = flb_cf_section_property_add(cf, service->properties, " ", 3, "", 0);
+ TEST_CHECK(ret == NULL);
+
+ /* try to add another 'SERVICE' section, it should return the same one */
+ s_tmp = flb_cf_section_create(cf, "SERVICE", 7);
+ TEST_CHECK(s_tmp == service);
+
+ /* add a valid section */
+ s_tmp = flb_cf_section_create(cf, "INPUT", 5);
+ TEST_CHECK(s_tmp != NULL);
+
+ TEST_CHECK(mk_list_size(&cf->inputs) == 1);
+
+ /* add property to the section recently created */
+ ret = flb_cf_section_property_add(cf, s_tmp->properties, "key", 3, "val", 3);
+ TEST_CHECK(ret != NULL);
+
+ /* groups: add groups to the last section created */
+ g_tmp = flb_cf_group_create(cf, s_tmp, "FLUENT GROUP", 12);
+ TEST_CHECK(g_tmp != NULL);
+
+ /* add properties to the group */
+ ret = flb_cf_section_property_add(cf, g_tmp->properties, "key", 3, "val", 3);
+ TEST_CHECK(ret != NULL);
+
+ /* groups: invalid group */
+ g_tmp = flb_cf_group_create(cf, s_tmp, "", 0);
+ TEST_CHECK(g_tmp == NULL);
+
+ /* Meta commands */
+ meta = flb_cf_meta_property_add(cf, "@SET a=1 ", 20);
+
+ TEST_CHECK(meta != NULL);
+ TEST_CHECK(flb_sds_len(meta->key) == 3 && strcmp(meta->key, "SET") == 0);
+ TEST_CHECK(flb_sds_len(meta->val) == 3 && strcmp(meta->val, "a=1") == 0);
+
+ /* create new context */
+ new_cf = flb_cf_create();
+ TEST_CHECK(cf != NULL);
+
+ status = flb_reload_reconstruct_cf(cf, new_cf);
+ TEST_CHECK(status == 0);
+ TEST_CHECK(new_cf != NULL);
+
+ TEST_CHECK(mk_list_size(&new_cf->inputs) == 1);
+ TEST_CHECK(mk_list_size(&new_cf->sections) == 2);
+ TEST_CHECK(mk_list_size(&new_cf->metas) == 1);
+
+ printf("\n");
+ flb_cf_dump(new_cf);
+
+ /* destroy context */
+ flb_cf_destroy(cf);
+ flb_cf_destroy(new_cf);
+}
+
+/* data/reload/fluent-bit.conf */
+void test_reload()
+{
+ struct flb_cf *cf = NULL;
+ struct flb_cf *cf_opts;
+ struct flb_cf_section *section;
+ struct cfl_variant *ret;
+ flb_ctx_t *ctx;
+ int status;
+
+ /* create context */
+ cf_opts = flb_cf_create();
+ TEST_CHECK(cf_opts != NULL);
+
+ /* add a valid section (input) */
+ section = flb_cf_section_create(cf_opts, "INPUT", 5);
+ TEST_CHECK(section != NULL);
+
+ /* add property to the section recently created */
+ ret = flb_cf_section_property_add(cf_opts, section->properties, "name", 0, "dummy", 0);
+ TEST_CHECK(ret != NULL);
+
+ TEST_CHECK(mk_list_size(&cf_opts->inputs) == 1);
+
+ ctx = flb_create();
+ if (!TEST_CHECK(ctx != NULL)) {
+ TEST_MSG("flb_create failed");
+ exit(EXIT_FAILURE);
+ }
+
+ cf = ctx->config->cf_main;
+
+ status = flb_reload_reconstruct_cf(cf_opts, cf);
+ TEST_CHECK(status == 0);
+
+ /* Mimic operation like as service_configure() */
+ cf = flb_cf_create_from_file(cf, FLB_CLASSIC);
+ TEST_CHECK(cf != NULL);
+
+ ctx->config->conf_path_file = flb_sds_create(FLB_CLASSIC);
+ ctx->config->enable_hot_reload = FLB_TRUE;
+
+ status = flb_config_load_config_format(ctx->config, cf);
+ TEST_CHECK(status == 0);
+
+ /* Start the engine */
+ status = flb_start(ctx);
+ TEST_CHECK(status == 0);
+ TEST_CHECK(mk_list_size(&ctx->config->inputs) == 2);
+
+ sleep(2);
+
+ status = flb_reload(ctx, cf_opts);
+ TEST_CHECK(status == 0);
+
+ sleep(2);
+
+ /* flb context should be replaced with flb_reload() */
+ ctx = flb_context_get();
+
+ TEST_CHECK(mk_list_size(&ctx->config->cf_opts->inputs) == 1);
+ TEST_CHECK(mk_list_size(&ctx->config->inputs) == 2);
+
+ flb_cf_destroy(cf_opts);
+
+ flb_stop(ctx);
+ flb_destroy(ctx);
+}
+
+/* data/reload/yaml/processor.yaml */
+void test_reload_yaml()
+{
+ struct flb_cf *cf = NULL;
+ struct flb_cf *cf_opts;
+ struct flb_cf_section *section;
+ struct cfl_variant *ret;
+ flb_ctx_t *ctx;
+ int status;
+
+ /* create context */
+ cf_opts = flb_cf_create();
+ TEST_CHECK(cf_opts != NULL);
+
+ /* add a valid section (input) */
+ section = flb_cf_section_create(cf_opts, "INPUT", 5);
+ TEST_CHECK(section != NULL);
+
+ /* add property to the section recently created */
+ ret = flb_cf_section_property_add(cf_opts, section->properties, "name", 0, "dummy", 0);
+ TEST_CHECK(ret != NULL);
+
+ TEST_CHECK(mk_list_size(&cf_opts->inputs) == 1);
+
+ ctx = flb_create();
+ if (!TEST_CHECK(ctx != NULL)) {
+ TEST_MSG("flb_create failed");
+ exit(EXIT_FAILURE);
+ }
+
+ cf = ctx->config->cf_main;
+
+ status = flb_reload_reconstruct_cf(cf_opts, cf);
+ TEST_CHECK(status == 0);
+
+ /* Mimic operation like as service_configure() */
+ cf = flb_cf_create_from_file(cf, FLB_YAML);
+ TEST_CHECK(cf != NULL);
+
+ ctx->config->conf_path_file = flb_sds_create(FLB_YAML);
+ ctx->config->enable_hot_reload = FLB_TRUE;
+
+ status = flb_config_load_config_format(ctx->config, cf);
+ TEST_CHECK(status == 0);
+
+ /* Start the engine */
+ status = flb_start(ctx);
+ TEST_CHECK(status == 0);
+ TEST_CHECK(mk_list_size(&ctx->config->inputs) == 2);
+
+ sleep(2);
+
+ status = flb_reload(ctx, cf_opts);
+ TEST_CHECK(status == 0);
+
+ sleep(2);
+
+ /* flb context should be replaced with flb_reload() */
+ ctx = flb_context_get();
+
+ TEST_CHECK(mk_list_size(&ctx->config->cf_opts->inputs) == 1);
+ TEST_CHECK(mk_list_size(&ctx->config->inputs) == 2);
+ TEST_CHECK(mk_list_size(&ctx->config->filters) == 0);
+ TEST_CHECK(mk_list_size(&ctx->config->outputs) == 1);
+
+ flb_cf_destroy(cf_opts);
+
+ flb_stop(ctx);
+ flb_destroy(ctx);
+}
+
+TEST_LIST = {
+ { "reconstruct_cf" , test_reconstruct_cf},
+ { "reload" , test_reload},
+ { "reload_yaml" , test_reload_yaml},
+ { 0 }
+};
diff --git a/fluent-bit/tests/internal/ring_buffer.c b/fluent-bit/tests/internal/ring_buffer.c
new file mode 100644
index 00000000..0c0e2074
--- /dev/null
+++ b/fluent-bit/tests/internal/ring_buffer.c
@@ -0,0 +1,168 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+#include <fluent-bit/flb_info.h>
+#include <fluent-bit/flb_mem.h>
+#include <fluent-bit/flb_engine.h>
+#include <fluent-bit/flb_ring_buffer.h>
+#include <fluent-bit/flb_event_loop.h>
+#include <fluent-bit/flb_bucket_queue.h>
+
+#include "flb_tests_internal.h"
+
+struct check {
+ char *buf_a;
+ char *buf_b;
+};
+
+struct check checks[] = {
+ {"a1", "a2"},
+ {"b1", "b2"},
+ {"c1", "c2"},
+ {"d1", "d2"},
+ {"e1", "e2"},
+};
+
+static void test_basic()
+{
+ int i;
+ int ret;
+ int elements;
+ struct check *c;
+ struct check *tmp;
+ struct flb_ring_buffer *rb;
+
+ elements = sizeof(checks) / sizeof(struct check);
+
+ rb = flb_ring_buffer_create(sizeof(struct check *) * elements);
+ TEST_CHECK(rb != NULL);
+ if (!rb) {
+ exit(EXIT_FAILURE);
+ }
+
+ for (i = 0; i < elements; i++) {
+ c = &checks[i];
+ ret = flb_ring_buffer_write(rb, (void *) &c, sizeof(c));
+ TEST_CHECK(ret == 0);
+ }
+
+ /* try to write another record, it must fail */
+ tmp = c;
+ ret = flb_ring_buffer_write(rb, (void *) &tmp, sizeof(tmp));
+ TEST_CHECK(ret == -1);
+
+ c = NULL;
+
+ /* consume one entry */
+ ret = flb_ring_buffer_read(rb, (void *) &c, sizeof(c));
+ TEST_CHECK(ret == 0);
+
+ /* the consumed entry must be equal to the first one */
+ c = &checks[0];
+ TEST_CHECK(strcmp(c->buf_a, "a1") == 0 && strcmp(c->buf_b, "a2") ==0);
+
+ /* try 'again' to write 'c2', it should succeed */
+ ret = flb_ring_buffer_write(rb, (void *) &tmp, sizeof(tmp));
+ TEST_CHECK(ret == 0);
+
+ flb_ring_buffer_destroy(rb);
+}
+
+static void test_smart_flush()
+{
+ int i;
+ int ret;
+ int n_events;
+ int elements;
+ size_t slots;
+ uint64_t window;
+ struct check *c;
+ struct check *tmp;
+ int flush_event_detected;
+ char signal_buffer[512];
+ struct mk_event *event;
+ struct mk_event_loop *evl;
+ struct flb_ring_buffer *rb;
+ struct flb_bucket_queue *bktq;
+
+#ifdef _WIN32
+ WSADATA wsa_data;
+ WSAStartup(0x0201, &wsa_data);
+#endif
+
+ evl = mk_event_loop_create(100);
+ TEST_CHECK(evl != NULL);
+ if (!evl) {
+ exit(EXIT_FAILURE);
+ }
+
+ bktq = flb_bucket_queue_create(10);
+ TEST_CHECK(bktq != NULL);
+ if (!bktq) {
+ exit(EXIT_FAILURE);
+ }
+
+ elements = sizeof(checks) / sizeof(struct check);
+ slots = elements * 2;
+ window = (((double) (elements + 1)) / slots) * 100;
+
+ /* The slot count was chosen to trigger the flush request
+ * after writing the predefined elements + 1
+ */
+
+ rb = flb_ring_buffer_create(sizeof(struct check *) * slots);
+ TEST_CHECK(rb != NULL);
+ if (!rb) {
+ exit(EXIT_FAILURE);
+ }
+
+ ret = flb_ring_buffer_add_event_loop(rb, evl, window);
+ TEST_CHECK(ret == 0);
+ if (ret) {
+ exit(EXIT_FAILURE);
+ }
+
+ for (i = 0; i < elements; i++) {
+ c = &checks[i];
+ ret = flb_ring_buffer_write(rb, (void *) &c, sizeof(c));
+ TEST_CHECK(ret == 0);
+
+ n_events = mk_event_wait_2(evl, 0);
+ TEST_CHECK(n_events == 0);
+ }
+
+ /* write another record, a signal must be produced */
+ ret = flb_ring_buffer_write(rb, (void *) &tmp, sizeof(tmp));
+ TEST_CHECK(ret == 0);
+
+ n_events = mk_event_wait_2(evl, 0);
+ TEST_CHECK(n_events == 1);
+
+ flush_event_detected = FLB_FALSE;
+ flb_event_priority_live_foreach(event, bktq, evl, 10) {
+ if(event->type == FLB_ENGINE_EV_THREAD_INPUT) {
+ flb_pipe_r(event->fd, signal_buffer, sizeof(signal_buffer));
+
+ flush_event_detected = FLB_TRUE;
+ }
+ }
+
+ TEST_CHECK(flush_event_detected == FLB_TRUE);
+
+ /* write another record, a signal must not be produced because the previous one
+ * was not acknowledged by setting `flush_pending` to `FLB_FALSE`
+ */
+ ret = flb_ring_buffer_write(rb, (void *) &tmp, sizeof(tmp));
+ TEST_CHECK(ret == 0);
+
+ n_events = mk_event_wait_2(evl, 0);
+ TEST_CHECK(n_events == 0);
+
+ flb_ring_buffer_destroy(rb);
+ flb_bucket_queue_destroy(bktq);
+ mk_event_loop_destroy(evl);
+}
+TEST_LIST = {
+ { "basic", test_basic},
+ { "smart_flush", test_smart_flush},
+ { 0 }
+};
diff --git a/fluent-bit/tests/internal/router.c b/fluent-bit/tests/internal/router.c
new file mode 100644
index 00000000..04960a25
--- /dev/null
+++ b/fluent-bit/tests/internal/router.c
@@ -0,0 +1,55 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+#include <fluent-bit/flb_info.h>
+#include <fluent-bit/flb_router.h>
+
+#include "flb_tests_internal.h"
+
+struct check {
+ char *tag;
+ char *match;
+ int matched;
+};
+
+struct check route_checks[] = {
+
+ {"file.apache.log", "file.*.log" , FLB_TRUE},
+ {"cpu.rpi" , "cpu.rpi" , FLB_TRUE},
+ {"cpu.rpi" , "cpu.*" , FLB_TRUE},
+ {"cpu.rpi" , "*" , FLB_TRUE},
+ {"cpu.rpi" , "*.*" , FLB_TRUE},
+ {"cpu.rpi" , "*.rpi" , FLB_TRUE},
+ {"cpu.rpi" , "mem.*" , FLB_FALSE},
+ {"cpu.rpi" , "*u.r*" , FLB_TRUE},
+ {"hoge" , "hogeeeeeee" , FLB_FALSE},
+ {"test" , "test" , FLB_TRUE}
+};
+
+void test_router_wildcard()
+{
+ int i;
+ int ret;
+ int len;
+ int checks = 0;
+ struct check *c;
+
+ checks = sizeof(route_checks) / sizeof(struct check);
+ for (i = 0; i < checks; i++) {
+ c = &route_checks[i];
+ len = strlen(c->tag);
+ ret = flb_router_match(c->tag, len, c->match, NULL);
+ TEST_CHECK(ret == c->matched);
+ if (ret != c->matched) {
+ fprintf(stderr, "test %i failed: tag=%s match=%s expected_to_match=%s\n",
+ i, c->tag, c->match, c->matched ? "YES": "NO");
+ }
+ }
+
+ ret = flb_router_match("aaaX", 3, "aaa", NULL);
+ TEST_CHECK(ret == FLB_TRUE);
+}
+
+TEST_LIST = {
+ { "wildcard", test_router_wildcard},
+ { 0 }
+};
diff --git a/fluent-bit/tests/internal/sds.c b/fluent-bit/tests/internal/sds.c
new file mode 100644
index 00000000..30dc166d
--- /dev/null
+++ b/fluent-bit/tests/internal/sds.c
@@ -0,0 +1,88 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+#include <fluent-bit/flb_info.h>
+#include <fluent-bit/flb_sds.h>
+
+#include "flb_tests_internal.h"
+
+static void test_sds_usage()
+{
+ flb_sds_t s;
+
+ s = flb_sds_create("test");
+ TEST_CHECK(s != NULL);
+ TEST_CHECK(flb_sds_len(s) == 4);
+ TEST_CHECK(flb_sds_alloc(s) == 4);
+ TEST_CHECK(strcmp("test", s) == 0);
+
+ s = flb_sds_cat(s, ",cat message", 12);
+ TEST_CHECK(strcmp("test,cat message", s) == 0);
+
+ flb_sds_destroy(s);
+}
+
+static void test_sds_printf()
+{
+ int len;
+ flb_sds_t s;
+ flb_sds_t tmp;
+ char *str = "0123456789ABCDEFGHIJQLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvewxyz";
+
+ s = flb_sds_create_size(10);
+ tmp = flb_sds_printf(&s, "%s=%s", str, str);
+
+ len = (strlen(str) * 2) + 1;
+ TEST_CHECK(tmp == s);
+ TEST_CHECK(flb_sds_len(s) == len);
+ flb_sds_destroy(s);
+}
+
+/* https://github.com/fluent/fluent-bit/issues/7143 */
+static void test_sds_printf_7143_off_by_1()
+{
+ flb_sds_t tmp;
+ flb_sds_t test;
+ flb_sds_t test2;
+ int len;
+
+ /* 66 char final string, not impacted by bug */
+ test = flb_sds_create_size(64);
+ TEST_CHECK(test != NULL);
+ tmp = flb_sds_printf(&test, "A0123456789 %s", "this-is-54-chars-1234567890-abcdefghijklmnopqrstuvwxyz");
+ TEST_CHECK(tmp != NULL);
+ len = flb_sds_len(test);
+ TEST_CHECK(len == 66);
+ TEST_CHECK(test[len -1] == 'z');
+ flb_sds_destroy(test);
+
+ /* 65 char final string, impacted by bug */
+ test2 = flb_sds_create_size(64);
+ TEST_CHECK(test2 != NULL);
+ tmp = flb_sds_printf(&test2, "0123456789 %s", "this-is-54-chars-1234567890-abcdefghijklmnopqrstuvwxyz");
+ TEST_CHECK(tmp != NULL);
+ len = flb_sds_len(test2);
+ TEST_CHECK(len == 65);
+ TEST_CHECK(test2[len -1] == 'z');
+ flb_sds_destroy(test2);
+
+}
+
+static void test_sds_cat_utf8()
+{
+ flb_sds_t s;
+ char *utf8_str = "\xe8\x9f\xb9\xf0\x9f\xa6\x80";
+
+ s = flb_sds_create("");
+ flb_sds_cat_utf8(&s, utf8_str, strlen(utf8_str));
+
+ TEST_CHECK(strcmp(s, "\\u87f9\\u1f980") == 0);
+ flb_sds_destroy(s);
+}
+
+TEST_LIST = {
+ { "sds_usage" , test_sds_usage},
+ { "sds_printf", test_sds_printf},
+ { "sds_cat_utf8", test_sds_cat_utf8},
+ { "test_sds_printf_7143_off_by_1", test_sds_printf_7143_off_by_1},
+ { 0 }
+};
diff --git a/fluent-bit/tests/internal/sds_list.c b/fluent-bit/tests/internal/sds_list.c
new file mode 100644
index 00000000..63ac280c
--- /dev/null
+++ b/fluent-bit/tests/internal/sds_list.c
@@ -0,0 +1,238 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+#include <fluent-bit/flb_info.h>
+#include <fluent-bit/flb_sds.h>
+#include <fluent-bit/flb_sds_list.h>
+
+#include "flb_tests_internal.h"
+
+static void test_sds_list_create_destroy()
+{
+ struct flb_sds_list *list = NULL;
+ int ret = 0;
+
+ list = flb_sds_list_create();
+ if(!TEST_CHECK(list != NULL)) {
+ TEST_MSG("failed to allocate flb_sds_list");
+ exit(EXIT_FAILURE);
+ }
+
+ ret = flb_sds_list_destroy(list);
+ if(!TEST_CHECK(ret == 0)) {
+ TEST_MSG("failed to destroy flb_sds_list");
+ exit(EXIT_FAILURE);
+ }
+}
+
+static void test_sds_list_add()
+{
+ struct flb_sds_list *list = NULL;
+ int ret = 0;
+
+ list = flb_sds_list_create();
+ if(!TEST_CHECK(list != NULL)) {
+ TEST_MSG("failed to allocate flb_sds_list");
+ exit(EXIT_FAILURE);
+ }
+
+ ret = flb_sds_list_add(list, "hoge", 4);
+ if(!TEST_CHECK(ret == 0)) {
+ TEST_MSG("failed to flb_sds_list_add");
+ flb_sds_list_destroy(list);
+ exit(EXIT_FAILURE);
+ }
+
+ ret = flb_sds_list_destroy(list);
+ if(!TEST_CHECK(ret == 0)) {
+ TEST_MSG("failed to destroy flb_sds_list");
+ exit(EXIT_FAILURE);
+ }
+}
+
+static void test_sds_list_array()
+{
+ struct flb_sds_list *list = NULL;
+ char *c_array[] = {
+ "hoge", "moge", "aaa", NULL
+ };
+ char **ret_array = NULL;
+ int i;
+ int ret = 0;
+
+ list = flb_sds_list_create();
+ if(!TEST_CHECK(list != NULL)) {
+ TEST_MSG("failed to allocate flb_sds_list");
+ exit(EXIT_FAILURE);
+ }
+
+ for (i=0; c_array[i] != NULL; i++) {
+ ret = flb_sds_list_add(list, c_array[i], strlen(c_array[i]));
+ if(!TEST_CHECK(ret == 0)) {
+ TEST_MSG("flb_sds_list_add failed");
+ flb_sds_list_destroy(list);
+ exit(EXIT_FAILURE);
+ }
+ }
+
+ ret_array = flb_sds_list_create_str_array(list);
+ if (!TEST_CHECK(ret_array != NULL)) {
+ TEST_MSG("flb_sds_list_create_str_array failed");
+ flb_sds_list_destroy(list);
+ exit(EXIT_FAILURE);
+ }
+
+ for (i=0; c_array[i] != NULL; i++) {
+ if (!TEST_CHECK(ret_array[i] != NULL)) {
+ TEST_MSG("ret_array[%d] should not be NULL", i);
+ flb_sds_list_destroy_str_array(ret_array);
+ flb_sds_list_destroy(list);
+ exit(EXIT_FAILURE);
+ }
+ if(!TEST_CHECK(strcmp(c_array[i], ret_array[i]) == 0)) {
+ TEST_MSG("%d:mismatch. c=%s sds=%s",i, c_array[i], ret_array[i]);
+ flb_sds_list_destroy_str_array(ret_array);
+ flb_sds_list_destroy(list);
+ exit(EXIT_FAILURE);
+ }
+ }
+
+ ret = flb_sds_list_destroy_str_array(ret_array);
+ if(!TEST_CHECK(ret == 0)) {
+ TEST_MSG("flb_sds_list_destroy_str_array failed");
+ flb_sds_list_destroy(list);
+ exit(EXIT_FAILURE);
+ }
+
+ ret = flb_sds_list_destroy(list);
+ if(!TEST_CHECK(ret == 0)) {
+ TEST_MSG("failed to destroy flb_sds_list");
+ exit(EXIT_FAILURE);
+ }
+}
+
+static void test_sds_list_size()
+{
+ struct flb_sds_list *list = NULL;
+ char *c_array[] = {
+ "hoge", "moge", "aaa", NULL
+ };
+ int i;
+ int ret = 0;
+ size_t size = 0;
+
+ list = flb_sds_list_create();
+ if(!TEST_CHECK(list != NULL)) {
+ TEST_MSG("failed to allocate flb_sds_list");
+ exit(EXIT_FAILURE);
+ }
+
+ for (i=0; c_array[i] != NULL; i++) {
+ ret = flb_sds_list_add(list, c_array[i], strlen(c_array[i]));
+ size++;
+ if(!TEST_CHECK(ret == 0)) {
+ TEST_MSG("flb_sds_list_add failed");
+ flb_sds_list_destroy(list);
+ exit(EXIT_FAILURE);
+ }
+
+ if (!TEST_CHECK(size == flb_sds_list_size(list))) {
+ TEST_MSG("%d: size mismatch. got=%lu expect=%lu",i, flb_sds_list_size(list), size);
+ }
+ }
+
+ ret = flb_sds_list_destroy(list);
+ if(!TEST_CHECK(ret == 0)) {
+ TEST_MSG("failed to destroy flb_sds_list");
+ exit(EXIT_FAILURE);
+ }
+}
+
+static void test_sds_list_del_last_entry()
+{
+ struct flb_sds_list *list = NULL;
+ char *c_array[] = {
+ "hoge", "moge", "aaa", NULL
+ };
+ char **str_array = NULL;
+ int i;
+ int ret = 0;
+ size_t size = 0;
+
+ list = flb_sds_list_create();
+ if(!TEST_CHECK(list != NULL)) {
+ TEST_MSG("failed to allocate flb_sds_list");
+ exit(EXIT_FAILURE);
+ }
+
+ for (i=0; c_array[i] != NULL; i++) {
+ ret = flb_sds_list_add(list, c_array[i], strlen(c_array[i]));
+ size++;
+ if(!TEST_CHECK(ret == 0)) {
+ TEST_MSG("flb_sds_list_add failed");
+ flb_sds_list_destroy(list);
+ exit(EXIT_FAILURE);
+ }
+
+ if (!TEST_CHECK(size == flb_sds_list_size(list))) {
+ TEST_MSG("%d: size mismatch. got=%zu expect=%zu",i, flb_sds_list_size(list), size);
+ }
+ }
+
+ size = flb_sds_list_size(list);
+
+ while(size > 0) {
+ ret = flb_sds_list_del_last_entry(list);
+ if (!TEST_CHECK(ret==0)) {
+ TEST_MSG("flb_sds_list_del_last_entry failed");
+ flb_sds_list_destroy(list);
+ exit(EXIT_FAILURE);
+ }
+ size--;
+ if (!TEST_CHECK(size == flb_sds_list_size(list))) {
+ TEST_MSG("size mismatch. got=%zu expect=%zu",flb_sds_list_size(list), size);
+ flb_sds_list_destroy(list);
+ exit(EXIT_FAILURE);
+ }
+ if (size == 0) {
+ break;
+ }
+
+ str_array = flb_sds_list_create_str_array(list);
+ if (!TEST_CHECK(str_array != NULL)) {
+ TEST_MSG("flb_sds_list_create_str_array failed. size=%zu", size);
+ flb_sds_list_destroy(list);
+ exit(EXIT_FAILURE);
+ }
+
+ for (i=0; str_array[i] != NULL; i++) {
+ if (!TEST_CHECK(str_array[i] != NULL)) {
+ TEST_MSG("str_array[%d] should not be NULL", i);
+ flb_sds_list_destroy_str_array(str_array);
+ flb_sds_list_destroy(list);
+ exit(EXIT_FAILURE);
+ }
+ if(!TEST_CHECK(strcmp(c_array[i], str_array[i]) == 0)) {
+ TEST_MSG("%d:mismatch. c=%s sds=%s",i, c_array[i], str_array[i]);
+ flb_sds_list_destroy_str_array(str_array);
+ flb_sds_list_destroy(list);
+ exit(EXIT_FAILURE);
+ }
+ }
+ flb_sds_list_destroy_str_array(str_array);
+ }
+
+ ret = flb_sds_list_destroy(list);
+ if(!TEST_CHECK(ret == 0)) {
+ TEST_MSG("failed to destroy flb_sds_list");
+ exit(EXIT_FAILURE);
+ }
+}
+
+TEST_LIST = {
+ { "sds_list_create_destroy" , test_sds_list_create_destroy},
+ { "sds_list_add" , test_sds_list_add},
+ { "sds_list_array" , test_sds_list_array},
+ { "sds_list_size" , test_sds_list_size},
+ { "sds_list_del_last_entry" , test_sds_list_del_last_entry},
+ { 0 }
+};
diff --git a/fluent-bit/tests/internal/signv4.c b/fluent-bit/tests/internal/signv4.c
new file mode 100644
index 00000000..7c691bc3
--- /dev/null
+++ b/fluent-bit/tests/internal/signv4.c
@@ -0,0 +1,666 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/* Fluent Bit
+ * ==========
+ * Copyright (C) 2019-2020 The Fluent Bit Authors
+ * Copyright (C) 2015-2018 Treasure Data Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * AWS Signv4 documentation
+ * ========================
+ *
+ * https://docs.aws.amazon.com/general/latest/gr/sigv4-create-canonical-request.html
+ *
+ * AWS Signv4 Test Suite
+ * =====================
+ *
+ * AWS provides a test suite that can be used to validate certain requests type and
+ * expected signatures. The following unit test file, uses the suite provided and
+ * provides certain wrappers to validate expected results.
+ */
+
+#include <fluent-bit/flb_info.h>
+#include <fluent-bit/flb_mem.h>
+#include <fluent-bit/flb_kv.h>
+#include <fluent-bit/flb_http_client.h>
+#include <fluent-bit/flb_upstream.h>
+#include <fluent-bit/flb_signv4.h>
+#include <fluent-bit/flb_aws_credentials.h>
+#include <monkey/mk_core.h>
+
+#include "flb_tests_internal.h"
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <dirent.h>
+#include <stdlib.h>
+
+/* Test suite entry point */
+#define AWS_SUITE FLB_TESTS_DATA_PATH "data/signv4/aws-sig-v4-test-suite/"
+
+/* Credentials Environment Variables */
+#define AWS_ACCESS_KEY_ID "AWS_ACCESS_KEY_ID"
+#define AWS_SECRET_ACCESS_KEY "AWS_SECRET_ACCESS_KEY"
+#define AWS_SESSION_TOKEN "AWS_SESSION_TOKEN"
+
+struct request {
+ int method_i;
+ flb_sds_t method;
+ flb_sds_t uri;
+ flb_sds_t uri_full;
+ flb_sds_t query_string;
+ flb_sds_t payload;
+ struct mk_list headers;
+};
+
+struct aws_test {
+ flb_sds_t name; /* test name */
+ flb_sds_t authz;
+ flb_sds_t creq;
+ flb_sds_t req;
+ flb_sds_t sreq;
+ flb_sds_t sts;
+ struct request *r;
+ struct flb_http_client *c;
+ struct mk_list _head;
+};
+
+static struct request *http_request_create(char *request)
+{
+ int len;
+ char *sep;
+ char *start;
+ char *end;
+ char *p;
+ char *br = NULL;
+ flb_sds_t tmp;
+ flb_sds_t key;
+ flb_sds_t val;
+ flb_sds_t payload = NULL;
+ struct flb_kv *kv = NULL;
+ struct request *req;
+
+ req = flb_calloc(1, sizeof(struct request));
+ if (!req) {
+ flb_errno();
+ return NULL;
+ }
+ mk_list_init(&req->headers);
+
+ /* method */
+ p = strchr(request, ' ');
+ req->method = flb_sds_create_size(10);
+ if (!req->method) {
+ flb_free(req);
+ return NULL;
+ }
+
+ tmp = flb_sds_copy(req->method, request, p - request);
+ if (!tmp) {
+ flb_sds_destroy(req->method);
+ flb_free(req);
+ return NULL;
+ }
+ req->method = tmp;
+
+ if (strcmp(req->method, "GET") == 0) {
+ req->method_i = FLB_HTTP_GET;
+ }
+ else if (strcmp(req->method, "POST") == 0) {
+ req->method_i = FLB_HTTP_POST;
+ }
+
+ /* URI */
+ start = p + 1;
+ p = strchr(start, '\n');
+ if (!p) {
+ flb_sds_destroy(req->method);
+ flb_free(req);
+ return NULL;
+ }
+ p--;
+ if ((p - 8) <= start) {
+ return NULL;
+ }
+ end = (p - 8);
+
+ len = end - start;
+ req->uri = flb_sds_create_size(len);
+ tmp = flb_sds_copy(req->uri, start, len);
+ if (!tmp) {
+ flb_sds_destroy(req->method);
+ flb_sds_destroy(req->uri);
+ flb_free(req);
+ return NULL;
+ }
+
+ req->uri = tmp;
+ req->uri_full = flb_sds_create(req->uri);
+
+ /* Query string: it might be inside the URI */
+ start = req->uri;
+ p = strchr(start, '?');
+ if (p) {
+ flb_sds_len_set(req->uri, (p - req->uri));
+ len = flb_sds_len(req->uri) - (p - req->uri);
+ *p = '\0'; /* terminate the string */
+ start = p + 1;
+ req->query_string = flb_sds_create_len(start, len);
+ }
+
+ /* Headers, everything after the first LF (\n) */
+ p = strchr(request, '\n');
+ p++;
+
+ len = strlen(request) - (p - request);
+ start = p;
+ do {
+ /* HTTP line folding (multi line header) */
+ if ((*start == ' ' || *start == '\t') && kv) {
+ key = flb_sds_create(kv->key);
+ sep = start + 1;
+ }
+ else {
+ /* key */
+ sep = strchr(start, ':');
+ if (!sep) {
+ break;
+ }
+ key = flb_sds_create_len(start, sep - start);
+ }
+
+ /* value */
+ start = sep + 1;
+ br = strchr(start, '\n');
+ if (!br) {
+ break;
+ }
+
+ val = flb_sds_create_len(start, br - start);
+ kv = flb_kv_item_create_len(&req->headers,
+ key, flb_sds_len(key), val, flb_sds_len(val));
+ flb_sds_destroy(key);
+ flb_sds_destroy(val);
+
+ /* next header */
+ start = br + 1;
+ p = strchr(start, '\n');
+ } while (p && *p == '\n');
+
+ /* Is this a POST request with a payload ? */
+ if (p && *p == '\n') {
+ p++;
+ if (p) {
+ len = strlen(request) - (p - request);
+ payload = flb_sds_create_len(p, len);
+ }
+ }
+ else {
+ /* Append any remaining headers, aws tests do not end files with a \n */
+ br++;
+ if ((br - request) - len) {
+ start = br;
+ sep = strchr(start, ':');
+
+ key = flb_sds_create_len(start, sep - start);
+ val = flb_sds_create(sep + 1);
+ flb_kv_item_create_len(&req->headers,
+ key, flb_sds_len(key), val, flb_sds_len(val));
+ flb_sds_destroy(key);
+ flb_sds_destroy(val);
+ }
+ }
+
+ if (payload) {
+ req->payload = payload;
+ }
+
+ return req;
+}
+
+static void http_request_destroy(struct request *req)
+{
+ if (!req) {
+ return;
+ }
+
+ if (req->method) {
+ flb_sds_destroy(req->method);
+ }
+ if (req->uri) {
+ flb_sds_destroy(req->uri);
+ }
+ if (req->uri_full) {
+ flb_sds_destroy(req->uri_full);
+ }
+ if (req->query_string) {
+ flb_sds_destroy(req->query_string);
+ }
+ if (req->payload) {
+ flb_sds_destroy(req->payload);
+ }
+
+ flb_kv_release(&req->headers);
+ flb_free(req);
+}
+
+/* Convert a TXT HTTP request to a Fluent Bit http_client context */
+static struct flb_http_client *convert_request_file(char *request,
+ struct request **r,
+ struct flb_config *config)
+{
+ struct flb_upstream *u;
+ struct flb_connection *u_conn;
+ struct flb_http_client *c;
+ struct mk_list *head;
+ struct flb_kv *kv;
+ struct request *req;
+
+ /* Fake Upstream context, required by http client */
+ u = flb_upstream_create(config, "127.0.0.1", 80, 0, NULL);
+ if (!u) {
+ fprintf(stderr, "error creating upstream context");
+ flb_free(config);
+ return NULL;
+ }
+
+ /* Fake upstream connection */
+ u_conn = flb_calloc(1, sizeof(struct flb_connection));
+ if (!u_conn) {
+ flb_errno();
+ flb_upstream_destroy(u);
+ flb_free(config);
+ }
+ u_conn->upstream = u;
+
+ /* Convert TXT HTTP request to our local 'request' structure */
+ req = http_request_create(request);
+ if (!req) {
+ fprintf(stderr, "error parsing txt http request");
+ exit(1);
+ }
+
+ /* HTTP Client context */
+ c = flb_http_client(u_conn, req->method_i, req->uri_full,
+ req->payload, req->payload ? flb_sds_len(req->payload): -1,
+ NULL, -1, NULL, 0);
+
+ /*
+ * flb_http_client automatically adds host and content-length
+ * for the tests we remove these since all headers come from
+ * the the test file
+ */
+ mk_list_foreach(head, &c->headers) {
+ kv = mk_list_entry(head, struct flb_kv, _head);
+ if (strncasecmp(kv->key, "Host", 4) == 0) {
+ flb_kv_item_destroy(kv);
+ break;
+ }
+ }
+ mk_list_foreach(head, &c->headers) {
+ kv = mk_list_entry(head, struct flb_kv, _head);
+ if (strncasecmp(kv->key, "Content-Length", 14) == 0) {
+ flb_kv_item_destroy(kv);
+ break;
+ }
+ }
+
+ /* Append registered headers */
+ mk_list_foreach(head, &req->headers) {
+ kv = mk_list_entry(head, struct flb_kv, _head);
+ flb_http_add_header(c,
+ kv->key, flb_sds_len(kv->key),
+ kv->val, flb_sds_len(kv->val));
+ }
+
+ *r = req;
+ return c;
+}
+
+static flb_sds_t file_to_buffer(char *path, char *context, char *ext)
+{
+ char abs_path[2048];
+ char *buf;
+ flb_sds_t data;
+
+ snprintf(abs_path, sizeof(abs_path) - 1, "%s/%s.%s", path, context, ext);
+ buf = mk_file_to_buffer(abs_path);
+ if (!buf) {
+ return NULL;
+ }
+
+ data = flb_sds_create(buf);
+ if (!data) {
+ fprintf(stderr, "error allocating sds buffer to test %s file\n", abs_path);
+ flb_free(buf);
+ return NULL;
+ }
+ flb_free(buf);
+
+ return data;
+}
+
+static void aws_test_destroy(struct aws_test *awt)
+{
+ if (awt->name) {
+ flb_sds_destroy(awt->name);
+ }
+ if (awt->authz) {
+ flb_sds_destroy(awt->authz);
+ }
+ if (awt->creq) {
+ flb_sds_destroy(awt->creq);
+ }
+ if (awt->req) {
+ flb_sds_destroy(awt->req);
+ }
+ if (awt->sreq) {
+ flb_sds_destroy(awt->sreq);
+ }
+ if (awt->sts) {
+ flb_sds_destroy(awt->sts);
+ }
+
+ if (awt->c) {
+ flb_upstream_destroy(awt->c->u_conn->upstream);
+ flb_free(awt->c->u_conn);
+ flb_http_client_destroy(awt->c);
+ }
+
+ http_request_destroy(awt->r);
+ flb_free(awt);
+}
+
+static struct aws_test *aws_test_create(char *path, char *context,
+ struct flb_config *config)
+{
+ struct aws_test *awt;
+
+ awt = flb_calloc(1, sizeof(struct aws_test));
+ if (!awt) {
+ flb_errno();
+ return NULL;
+ }
+
+ awt->name = flb_sds_create(context);
+ if (!awt->name) {
+ fprintf(stderr, "cannot allocate awt name\n");
+ goto error;
+ }
+
+ /* If no 'authz' file is found, return right away */
+ awt->authz = file_to_buffer(path, context, "authz");
+ if (!awt->authz) {
+ aws_test_destroy(awt);
+ return NULL;
+ }
+
+ awt->creq = file_to_buffer(path, context, "creq");
+ if (!awt->creq) {
+ fprintf(stderr, "error reading creq file");
+ goto error;
+ }
+
+ awt->req = file_to_buffer(path, context, "req");
+ if (!awt->req) {
+ fprintf(stderr, "error reading req file");
+ goto error;
+ }
+
+ awt->sreq = file_to_buffer(path, context, "sreq");
+ if (!awt->sreq) {
+ fprintf(stderr, "error reading req file");
+ goto error;
+ }
+
+ awt->sts = file_to_buffer(path, context, "sts");
+ if (!awt->sts) {
+ fprintf(stderr, "error reading req file");
+ goto error;
+ }
+
+ /* Convert TXT HTTP request to http_client context */
+ awt->c = convert_request_file(awt->req, &awt->r, config);
+ if (!awt->c) {
+ fprintf(stderr, "error converting TXT request to a context: %s", awt->name);
+ goto error;
+ }
+
+ return awt;
+
+ error:
+ //aws_test_destroy(awt);
+ return NULL;
+}
+
+static int load_aws_test_directory(struct mk_list *list, char *ut_path,
+ struct flb_config *config)
+{
+ int ret;
+ struct dirent *e;
+ DIR *dir;
+ char path[2048];
+ char ut[4096];
+ struct stat st;
+ struct aws_test *awt;
+
+ dir = opendir(ut_path);
+ if (!dir) {
+ flb_errno();
+ flb_error("signv4: cannot open test suite located at '%s'", AWS_SUITE);
+ return -1;
+ }
+
+ /* Read directory entries */
+ while ((e = readdir(dir)) != NULL) {
+ if (*e->d_name == '.') {
+ continue;
+ }
+
+ snprintf(path, sizeof(path) - 1, "%s%s", ut_path, e->d_name);
+ ret = stat(path, &st);
+ if (ret == -1) {
+ continue;
+ }
+
+ /* only process directories */
+ if (!S_ISDIR(st.st_mode)) {
+ continue;
+ }
+
+ /* check for unit test file */
+ snprintf(ut, sizeof(ut) - 1, "%s%s/%s.req", path, e->d_name, e->d_name);
+ ret = stat(path, &st);
+ if (ret == -1) {
+ continue;
+ }
+
+ awt = aws_test_create(path, e->d_name, config);
+ if (!awt) {
+ continue;
+ }
+ mk_list_add(&awt->_head, list);
+ }
+
+ closedir(dir);
+ return 0;
+}
+
+static void aws_tests_destroy(struct mk_list *list)
+{
+ struct mk_list *tmp;
+ struct mk_list *head;
+ struct aws_test *awt;
+
+ mk_list_foreach_safe(head, tmp, list) {
+ awt = mk_list_entry(head, struct aws_test, _head);
+ mk_list_del(&awt->_head);
+ aws_test_destroy(awt);
+ }
+
+ flb_free(list);
+}
+
+static struct mk_list *aws_tests_create(struct flb_config *config)
+{
+ struct mk_list *list;
+ char path[2048];
+
+ printf("\n");
+
+ list = flb_malloc(sizeof(struct mk_list));
+ if (!list) {
+ flb_errno();
+ return NULL;
+ }
+ mk_list_init(list);
+
+ /* Load base path for AWS test suite, some sub-directories will be skipped */
+ load_aws_test_directory(list, AWS_SUITE, config);
+
+ /* Load pending sub-directories */
+ snprintf(path, sizeof(path) - 1, "%s/normalize-path/", AWS_SUITE);
+ load_aws_test_directory(list, path, config);
+
+ snprintf(path, sizeof(path) - 1, "%s/post-sts-token/", AWS_SUITE);
+ load_aws_test_directory(list, path, config);
+
+ return list;
+}
+
+static void aws_test_suite()
+{
+ int ret;
+ time_t t;
+ char *region = NULL;
+ char *access_key = NULL;
+ char *service = NULL;
+ char *secret_key = NULL;
+ flb_sds_t signature;
+ struct mk_list *head;
+ struct mk_list *tests;
+ struct flb_config *config;
+ struct aws_test *awt;
+ struct flb_aws_provider *provider;
+
+ config = flb_calloc(1, sizeof(struct flb_config));
+ if (!config) {
+ flb_errno();
+ return;
+ }
+ mk_list_init(&config->upstreams);
+
+ /* Get a list of tests */
+ tests = aws_tests_create(config);
+ TEST_CHECK(tests != NULL);
+ if (!tests) {
+ flb_free(config);
+ return;
+ }
+
+ /* Convert static '20150830T123600Z' to unix timestamp */
+ t = 1440938160;
+ region = "us-east-1";
+ access_key = "AKIDEXAMPLE";
+ service = "service";
+ secret_key = "wJalrXUtnFEMI/K7MDENG+bPxRfiCYEXAMPLEKEY";
+
+ /* credentials */
+ ret = setenv(AWS_ACCESS_KEY_ID, access_key, 1);
+ if (ret < 0) {
+ flb_errno();
+ return;
+ }
+ ret = setenv(AWS_SECRET_ACCESS_KEY, secret_key, 1);
+ if (ret < 0) {
+ flb_errno();
+ return;
+ }
+ provider = flb_aws_env_provider_create();
+ if (!provider) {
+ flb_errno();
+ return;
+ }
+
+ /* Iterate tests and sign the requests */
+ mk_list_foreach(head, tests) {
+ awt = mk_list_entry(head, struct aws_test, _head);
+ fprintf(stderr, "[AWS Signv4 Unit Test] %-50s", awt->name);
+ signature = flb_signv4_do(awt->c,
+ FLB_TRUE, /* normalize URI ? */
+ FLB_FALSE, /* add x-amz-date header ? */
+ t, region, service,
+ 0, NULL,
+ provider);
+ TEST_CHECK(signature != NULL);
+ if (signature) {
+ ret = strncmp(awt->authz, signature, flb_sds_len(awt->authz));
+ TEST_CHECK(ret == 0);
+ if (ret != 0) {
+ fprintf(stderr, "\t\tFAIL");
+ fprintf(stderr,
+ ">\n> signature check failed...\n received: %s\n expected: %s\n",
+ signature, awt->authz);
+ }
+ else {
+ fprintf(stderr, "PASS");
+ }
+ flb_sds_destroy(signature);
+ }
+ fprintf(stderr, "\n");
+ }
+
+ aws_tests_destroy(tests);
+ flb_aws_provider_destroy(provider);
+ flb_free(config);
+}
+
+static void check_normalize(char *s, size_t len, char *out)
+{
+ flb_sds_t o;
+
+ o = flb_signv4_uri_normalize_path(s, len);
+ TEST_CHECK(strcmp(o, out) == 0);
+ flb_sds_destroy(o);
+}
+
+void normalize()
+{
+ /* get-relative */
+ check_normalize("/example/..", 11, "/");
+
+ /* get-relative-relative */
+ check_normalize("/example1/example2/../..", 24, "/");
+
+ /* get-slash */
+ check_normalize("//", 2, "/");
+
+ /* get-slash-dot-slash */
+ check_normalize("/./", 3, "/");
+
+ /* get-slashes */
+ check_normalize("//example//", 11, "/example/");
+
+ /* get-slash-pointless-dot */
+ check_normalize("/./example", 10, "/example");
+}
+
+TEST_LIST = {
+ { "aws_test_suite", aws_test_suite},
+ { "normalize", normalize},
+ { 0 }
+};
diff --git a/fluent-bit/tests/internal/slist.c b/fluent-bit/tests/internal/slist.c
new file mode 100644
index 00000000..8c786fc8
--- /dev/null
+++ b/fluent-bit/tests/internal/slist.c
@@ -0,0 +1,205 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+#include <fluent-bit/flb_info.h>
+#include <fluent-bit/flb_mem.h>
+#include <fluent-bit/flb_slist.h>
+
+#include "flb_tests_internal.h"
+
+void token_check(struct mk_list *list, int id, char *str)
+{
+ int i = 0;
+ int len;
+ int ret;
+ struct mk_list *head;
+ struct flb_slist_entry *e = NULL;
+
+ mk_list_foreach(head, list) {
+ if (i == id) {
+ e = mk_list_entry(head, struct flb_slist_entry, _head);
+ break;
+ }
+ e = NULL;
+ i++;
+ }
+ TEST_CHECK(e != NULL);
+
+ len = strlen(str);
+ ret = flb_sds_cmp(e->str, str, len);
+ TEST_CHECK(ret == 0);
+ if (ret != 0) {
+ fprintf(stderr, "[token %i] expected '%s', got '%s'\n\n",
+ i, str, e->str);
+ exit(EXIT_FAILURE);
+ }
+}
+
+void test_slist_add()
+{
+ int ret;
+ struct mk_list list;
+ struct flb_slist_entry *e;
+
+ ret = flb_slist_create(&list);
+ TEST_CHECK(ret == 0);
+
+ ret = flb_slist_add(&list, "");
+ TEST_CHECK(ret == -1);
+
+ ret = flb_slist_add(&list, NULL);
+ TEST_CHECK(ret == -1);
+
+ TEST_CHECK(mk_list_is_empty(&list) == 0);
+
+ ret = flb_slist_add(&list, "test");
+ TEST_CHECK(ret == 0);
+
+ e = mk_list_entry_last(&list, struct flb_slist_entry, _head);
+ TEST_CHECK(flb_sds_len(e->str) == 4);
+
+ ret = flb_slist_add_n(&list, "test", 3);
+ TEST_CHECK(ret == 0);
+
+ e = mk_list_entry_last(&list, struct flb_slist_entry, _head);
+ TEST_CHECK(flb_sds_len(e->str) == 3);
+
+ flb_slist_destroy(&list);
+}
+
+void test_slist_split_string()
+{
+ int ret;
+ struct mk_list list;
+ struct flb_slist_entry *e;
+
+ ret = flb_slist_create(&list);
+ TEST_CHECK(ret == 0);
+
+ /* Simple string without separator */
+ ret = flb_slist_split_string(&list, "abcdefg", ' ', -1);
+ TEST_CHECK(ret == 1);
+ TEST_CHECK(mk_list_size(&list) == 1);
+ token_check(&list, 0, "abcdefg");
+ flb_slist_destroy(&list);
+
+ /* Separated strings */
+ ret = flb_slist_split_string(&list, "a bc defg", ' ', -1);
+ TEST_CHECK(ret == 3);
+ TEST_CHECK(mk_list_size(&list) == 3);
+ token_check(&list, 0, "a");
+ token_check(&list, 1, "bc");
+ token_check(&list, 2, "defg");
+ flb_slist_destroy(&list);
+
+ /* One char with empty spaces */
+ ret = flb_slist_split_string(&list, " a ", ' ', 2);
+ TEST_CHECK(ret == 1);
+ TEST_CHECK(mk_list_size(&list) == 1);
+ token_check(&list, 0, "a");
+ flb_slist_destroy(&list);
+
+ /* Two separated characters */
+ ret = flb_slist_split_string(&list, " a b ", ' ', 2);
+ TEST_CHECK(ret == 2);
+ TEST_CHECK(mk_list_size(&list) == 2);
+ token_check(&list, 0, "a");
+ token_check(&list, 1, "b");
+ flb_slist_destroy(&list);
+
+ /* Comma separated strings */
+ ret = flb_slist_split_string(&list, ",,,a ,, b,c ,d,,e ,f,g, , ,", ',', -1);
+ TEST_CHECK(ret == 7);
+ TEST_CHECK(mk_list_size(&list) == 7);
+ token_check(&list, 0, "a");
+ token_check(&list, 1, "b");
+ token_check(&list, 2, "c");
+ token_check(&list, 3, "d");
+ token_check(&list, 4, "e");
+ token_check(&list, 5, "f");
+ token_check(&list, 6, "g");
+ flb_slist_destroy(&list);
+
+ /* Comma seperated strings for real world NO_PROXY example */
+ ret = flb_slist_split_string(&list, "127.0.0.1, localhost, kubernetes.default.svc.cluster.local", ',', -1);
+ TEST_CHECK(ret == 3);
+ TEST_CHECK(mk_list_size(&list) == 3);
+ token_check(&list, 0, "127.0.0.1");
+ token_check(&list, 1, "localhost");
+ token_check(&list, 2, "kubernetes.default.svc.cluster.local");
+ flb_slist_destroy(&list);
+
+ /* Comma separated strings with limit */
+ ret = flb_slist_split_string(&list, ",,,a ,, b, c ,d,,e ,f,g, , ,", ',', 2);
+ TEST_CHECK(ret == 3);
+ TEST_CHECK(mk_list_size(&list) == 3);
+ e = mk_list_entry_last(&list, struct flb_slist_entry, _head);
+ TEST_CHECK(flb_sds_len(e->str) == 22);
+ flb_slist_destroy(&list);
+
+ /* Nothing */
+ ret = flb_slist_split_string(&list, ",,, ,, , , , ", ',', 2);
+ TEST_CHECK(ret == 0);
+ TEST_CHECK(mk_list_size(&list) == 0);
+}
+
+void test_slist_split_tokens()
+{
+ struct mk_list list;
+ char *txt = \
+ " this \"is a tokens parser\" \" apples \", "
+ "no\"quoted \"this is \\\"quoted\\\"\" "
+ "don't escape insi\\\"de q\\\"uoted strings\\\"";
+
+ mk_list_init(&list);
+ flb_slist_split_tokens(&list, txt, -1);
+
+ token_check(&list, 0, "this");
+ token_check(&list, 1, "is a tokens parser");
+ token_check(&list, 2, " apples ");
+ token_check(&list, 3, ",");
+ token_check(&list, 4, "no\"quoted");
+ token_check(&list, 5, "this is \"quoted\"");
+ token_check(&list, 6, "don't");
+ token_check(&list, 7, "escape");
+ token_check(&list, 8, "insi\\\"de");
+ token_check(&list, 9, "q\\\"uoted");
+ token_check(&list, 10, "strings\\\"");
+
+ flb_slist_destroy(&list);
+
+ mk_list_init(&list);
+ flb_slist_split_string(&list, "aaa bbb ccc ddd eee", ' ', 3);
+ token_check(&list, 3, "ddd eee");
+ flb_slist_destroy(&list);
+
+ mk_list_init(&list);
+ flb_slist_split_tokens(&list, "aaa bbb ccc ddd eee", 3);
+ token_check(&list, 3, "ddd eee");
+ flb_slist_destroy(&list);
+
+}
+
+void test_bugs()
+{
+ int ret;
+ struct mk_list list;
+ struct flb_slist_entry *e;
+
+ ret = flb_slist_create(&list);
+
+ /* Bug found during #293 development */
+ ret = flb_slist_split_string(&list, "$key2 ab final-tag true", ' ', 4);
+ TEST_CHECK(ret == 4);
+ e = flb_slist_entry_get(&list, 2);
+ TEST_CHECK(*e->str == 'f');
+
+ flb_slist_destroy(&list);
+}
+
+TEST_LIST = {
+ { "add" , test_slist_add},
+ { "split_string", test_slist_split_string},
+ { "split_tokens", test_slist_split_tokens},
+ { "bugs" , test_bugs},
+ { 0 }
+};
diff --git a/fluent-bit/tests/internal/stream_processor.c b/fluent-bit/tests/internal/stream_processor.c
new file mode 100644
index 00000000..9afda0bf
--- /dev/null
+++ b/fluent-bit/tests/internal/stream_processor.c
@@ -0,0 +1,934 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/* Fluent Bit
+ * ==========
+ * Copyright (C) 2019-2020 The Fluent Bit Authors
+ * Copyright (C) 2015-2018 Treasure Data Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <fluent-bit.h>
+#include <fluent-bit/flb_info.h>
+#include <fluent-bit/flb_mem.h>
+#include <fluent-bit/flb_pack.h>
+#include <fluent-bit/flb_error.h>
+#include <fluent-bit/flb_router.h>
+#include <fluent-bit/flb_storage.h>
+#include <fluent-bit/stream_processor/flb_sp.h>
+#include <fluent-bit/stream_processor/flb_sp_parser.h>
+#include <fluent-bit/stream_processor/flb_sp_stream.h>
+#include <fluent-bit/stream_processor/flb_sp_window.h>
+#include <msgpack.h>
+
+#include "flb_tests_internal.h"
+#include "include/sp_invalid_queries.h"
+#include "include/sp_select_keys.h"
+#include "include/sp_select_subkeys.h"
+#include "include/sp_window.h"
+#include "include/sp_snapshot.h"
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#ifdef _WIN32
+#include <fluent-bit/flb_compat.h>
+#else
+#include <unistd.h>
+#endif
+
+#define DATA_SAMPLES \
+ FLB_TESTS_DATA_PATH "/data/stream_processor/samples.mp"
+
+#define DATA_SAMPLES_SUBKEYS \
+ FLB_TESTS_DATA_PATH "/data/stream_processor/samples-subkeys.mp"
+
+#define DATA_SAMPLES_HOPPING_WINDOW_PATH \
+ FLB_TESTS_DATA_PATH "/data/stream_processor/samples-hw/"
+
+#define MP_UOK MSGPACK_UNPACK_SUCCESS
+
+int flb_sp_fd_event_test(int fd, struct flb_sp_task *task, struct sp_buffer *out_buf)
+{
+ char *tag = NULL;
+ int tag_len = 0;
+
+ if (task->window.type != FLB_SP_WINDOW_DEFAULT) {
+ if (fd == task->window.fd) {
+ if (task->window.records > 0) {
+ /* find input tag from task source */
+ package_results(tag, tag_len, &out_buf->buffer, &out_buf->size, task);
+ if (task->stream) {
+ flb_sp_stream_append_data(out_buf->buffer, out_buf->size, task->stream);
+ }
+ else {
+ flb_pack_print(out_buf->buffer, out_buf->size);
+ }
+ }
+
+ flb_sp_window_prune(task);
+ }
+ else if (fd == task->window.fd_hop) {
+ sp_process_hopping_slot(tag, tag_len, task);
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * Do data processing for internal unit tests, no engine required, set
+ * results on out_data/out_size variables.
+ */
+int flb_sp_do_test(struct flb_sp *sp, struct flb_sp_task *task,
+ const char *tag, int tag_len,
+ struct sp_buffer *data_buf, struct sp_buffer *out_buf)
+{
+ int ret;
+ int records;
+ struct flb_sp_cmd *cmd;
+
+ cmd = task->cmd;
+ if (cmd->source_type == FLB_SP_TAG) {
+ ret = flb_router_match(tag, tag_len, cmd->source_name, NULL);
+ if (ret == FLB_FALSE) {
+ out_buf->buffer = NULL;
+ out_buf->size = 0;
+ return 0;
+ }
+ }
+
+ if (task->aggregate_keys == FLB_TRUE) {
+ ret = sp_process_data_aggr(data_buf->buffer, data_buf->size,
+ tag, tag_len,
+ task, sp, FLB_TRUE);
+ if (ret == -1) {
+ flb_error("[sp] error error processing records for '%s'",
+ task->name);
+ return -1;
+ }
+
+ if (flb_sp_window_populate(task, data_buf->buffer, data_buf->size) == -1) {
+ flb_error("[sp] error populating window for '%s'",
+ task->name);
+ return -1;
+ }
+ if (task->window.type == FLB_SP_WINDOW_DEFAULT || task->window.type == FLB_SP_WINDOW_TUMBLING) {
+ package_results(tag, tag_len, &out_buf->buffer, &out_buf->size, task);
+ }
+
+ records = task->window.records;
+ }
+ else {
+ ret = sp_process_data(tag, tag_len,
+ data_buf->buffer, data_buf->size,
+ &out_buf->buffer, &out_buf->size,
+ task, sp);
+ if (ret == -1) {
+ flb_error("[sp] error processing records for '%s'",
+ task->name);
+ return -1;
+ }
+ records = ret;
+ }
+
+ if (records == 0) {
+ out_buf->buffer = NULL;
+ out_buf->size = 0;
+ return 0;
+ }
+
+ return 0;
+}
+
+/* this function reads the content of a file containing MessagePack data
+ into an input buffer
+*/
+static int file_to_buf(char *path, struct sp_buffer *out_buf)
+{
+ char *buf;
+ int ret;
+ long bytes;
+ FILE *fp;
+ struct stat st;
+
+ ret = stat(path, &st);
+ if (ret == -1) {
+ return -1;
+ }
+
+ fp = fopen(path, "r");
+ if (!fp) {
+ return -1;
+ }
+
+ buf = flb_malloc(st.st_size);
+ if (!buf) {
+ flb_errno();
+ fclose(fp);
+ return -1;
+ }
+
+ bytes = fread(buf, st.st_size, 1, fp);
+ if (bytes != 1) {
+ flb_errno();
+ flb_free(buf);
+ fclose(fp);
+ return -1;
+ }
+
+ fclose(fp);
+ out_buf->buffer = buf;
+ out_buf->size = st.st_size;
+
+ return 0;
+}
+
+static void invalid_queries()
+{
+ int i;
+ int checks;
+ struct flb_config *config;
+ struct flb_sp *sp;
+ struct flb_sp_task *task;
+
+ flb_init_env();
+
+ /* Total number of checks for invalid queries */
+ checks = sizeof(invalid_query_checks) / sizeof(char *);
+
+ config = flb_calloc(1, sizeof(struct flb_config));
+ if (!config) {
+ flb_errno();
+ return;
+ }
+
+ mk_list_init(&config->inputs);
+ mk_list_init(&config->stream_processor_tasks);
+
+ /* Create a stream processor context */
+ sp = flb_sp_create(config);
+ if (!sp) {
+ flb_error("[sp test] cannot create stream processor context");
+ flb_free(config);
+ return;
+ }
+
+ for (i = 0; i < checks; i++) {
+ task = flb_sp_task_create(sp, "invalid_query", invalid_query_checks[i]);
+ TEST_CHECK(task == NULL);
+ }
+
+ flb_sp_destroy(sp);
+ flb_free(config);
+}
+
+static void test_select_keys()
+{
+ int i;
+ int checks;
+ int ret;
+ struct sp_buffer data_buf;
+ struct sp_buffer out_buf;
+ struct flb_config *config;
+ struct flb_sp *sp;
+ struct task_check *check;
+ struct flb_sp_task *task;
+#ifdef _WIN32
+ WSADATA wsa_data;
+#endif
+
+ flb_init_env();
+
+ config = flb_calloc(1, sizeof(struct flb_config));
+ if (!config) {
+ flb_errno();
+ return;
+ }
+#ifdef _WIN32
+ WSAStartup(0x0201, &wsa_data);
+#endif
+ mk_list_init(&config->inputs);
+ mk_list_init(&config->stream_processor_tasks);
+
+ /* Create event loop */
+ config->evl = mk_event_loop_create(256);
+
+ /* Create a stream processor context */
+ sp = flb_sp_create(config);
+ if (!sp) {
+ flb_error("[sp test] cannot create stream processor context");
+ flb_free(config);
+ return;
+ }
+
+ ret = file_to_buf(DATA_SAMPLES, &data_buf);
+ if (ret == -1) {
+ flb_error("[sp test] cannot open DATA_SAMPLES file %s", DATA_SAMPLES);
+ flb_free(config);
+ return;
+ }
+
+ /* Total number of checks for select_keys */
+ checks = (sizeof(select_keys_checks) / sizeof(struct task_check));
+
+ /* Run every test */
+ for (i = 0; i < checks; i++) {
+ check = (struct task_check *) &select_keys_checks[i];
+
+ task = flb_sp_task_create(sp, check->name, check->exec);
+ if (!task) {
+ flb_error("[sp test] wrong check '%s', fix it!", check->name);
+ continue;
+ }
+
+ out_buf.buffer = NULL;
+
+ ret = flb_sp_do_test(sp, task,
+ "samples", strlen("samples"),
+ &data_buf, &out_buf);
+ if (ret == -1) {
+ flb_error("[sp test] error processing check '%s'", check->name);
+ flb_sp_task_destroy(task);
+ continue;
+ }
+
+ /* */
+ flb_sp_fd_event_test(task->window.fd, task, &out_buf);
+
+ flb_info("[sp test] id=%i, SQL => '%s'", check->id, check->exec);
+ check->cb_check(check->id, check, out_buf.buffer, out_buf.size);
+ flb_pack_print(out_buf.buffer, out_buf.size);
+ flb_free(out_buf.buffer);
+ }
+
+ flb_free(data_buf.buffer);
+ flb_sp_destroy(sp);
+ mk_event_loop_destroy(config->evl);
+ flb_free(config);
+#ifdef _WIN32
+ WSACleanup();
+#endif
+}
+
+static void test_select_subkeys()
+{
+ int i;
+ int checks;
+ int ret;
+ struct sp_buffer out_buf;
+ struct sp_buffer data_buf;
+ struct task_check *check;
+ struct flb_config *config;
+ struct flb_sp *sp;
+ struct flb_sp_task *task;
+#ifdef _WIN32
+ WSADATA wsa_data;
+#endif
+
+ flb_init_env();
+
+ config = flb_calloc(1, sizeof(struct flb_config));
+ if (!config) {
+ flb_errno();
+ return;
+ }
+#ifdef _WIN32
+ WSAStartup(0x0201, &wsa_data);
+#endif
+ mk_list_init(&config->inputs);
+ mk_list_init(&config->stream_processor_tasks);
+
+ config->evl = mk_event_loop_create(256);
+
+ sp = flb_sp_create(config);
+ if (!sp) {
+ flb_error("[sp test] cannot create stream processor context");
+ flb_free(config);
+ return;
+ }
+
+ ret = file_to_buf(DATA_SAMPLES_SUBKEYS, &data_buf);
+ if (ret == -1) {
+ flb_error("[sp test] cannot open DATA_SAMPLES file %s",
+ DATA_SAMPLES_SUBKEYS);
+ flb_free(config);
+ return;
+ }
+
+ /* Total number of checks for select_subkeys */
+ checks = (sizeof(select_subkeys_checks) / sizeof(struct task_check));
+
+ /* Run every test */
+ for (i = 0; i < checks; i++) {
+ check = (struct task_check *) &select_subkeys_checks[i];
+
+ task = flb_sp_task_create(sp, check->name, check->exec);
+ if (!task) {
+ flb_error("[sp test] wrong check '%s', fix it!", check->name);
+ continue;
+ }
+
+ out_buf.buffer = NULL;
+ out_buf.size = 0;
+
+ ret = flb_sp_do_test(sp, task,
+ "samples", strlen("samples"),
+ &data_buf, &out_buf);
+ if (ret == -1) {
+ flb_error("[sp test] error processing check '%s'", check->name);
+ flb_sp_task_destroy(task);
+ continue;
+ }
+
+ flb_sp_fd_event_test(task->window.fd, task, &out_buf);
+
+ flb_info("[sp test] id=%i, SQL => '%s'", check->id, check->exec);
+ check->cb_check(check->id, check, out_buf.buffer, out_buf.size);
+ flb_pack_print(out_buf.buffer, out_buf.size);
+ flb_free(out_buf.buffer);
+ }
+
+ flb_free(data_buf.buffer);
+ flb_sp_destroy(sp);
+ mk_event_loop_destroy(config->evl);
+ flb_free(config);
+#ifdef _WIN32
+ WSACleanup();
+#endif
+}
+
+void set_record_timestamps(struct sp_buffer *data_buf, double *record_timestamp)
+{
+ /* unpacker variables */
+ int ok;
+ size_t off = 0;
+ msgpack_object root;
+ msgpack_object map;
+ msgpack_unpacked result;
+ struct flb_time tm;
+
+ /* packer variables */
+ msgpack_sbuffer mp_sbuf;
+ msgpack_packer mp_pck;
+
+ ok = MSGPACK_UNPACK_SUCCESS;
+ msgpack_unpacked_init(&result);
+
+ msgpack_sbuffer_init(&mp_sbuf);
+ msgpack_packer_init(&mp_pck, &mp_sbuf, msgpack_sbuffer_write);
+
+ /* Iterate incoming records */
+ while (msgpack_unpack_next(&result, data_buf->buffer, data_buf->size, &off) == ok) {
+ root = result.data;
+
+ map = root.via.array.ptr[1];
+
+ msgpack_pack_array(&mp_pck, 2);
+ flb_time_set(&tm, *record_timestamp, 0);
+ flb_time_append_to_msgpack(&tm, &mp_pck, 0);
+ msgpack_pack_object(&mp_pck, map);
+
+ *record_timestamp = *record_timestamp + 1;
+ }
+
+ msgpack_unpacked_destroy(&result);
+ flb_free(data_buf->buffer);
+
+ data_buf->buffer = mp_sbuf.data;
+ data_buf->size = mp_sbuf.size;
+}
+
+static void test_window()
+{
+ int i;
+ int t;
+ int checks;
+ int ret;
+ char datafile[PATH_MAX];
+ struct sp_buffer data_buf;
+ struct sp_buffer out_buf;
+ struct task_check *check;
+ struct flb_config *config;
+ struct flb_sp *sp;
+ struct flb_sp_task *task;
+#ifdef _WIN32
+ WSADATA wsa_data;
+#endif
+
+ flb_init_env();
+
+ config = flb_calloc(1, sizeof(struct flb_config));
+ if (!config) {
+ flb_errno();
+ return;
+ }
+#ifdef _WIN32
+ WSAStartup(0x0201, &wsa_data);
+#endif
+ mk_list_init(&config->inputs);
+ mk_list_init(&config->stream_processor_tasks);
+ config->evl = mk_event_loop_create(256);
+
+ sp = flb_sp_create(config);
+ if (!sp) {
+ flb_error("[sp test] cannot create stream processor context");
+ flb_free(config);
+ return;
+ }
+
+ /* Total number of checks for select_keys */
+ checks = (sizeof(window_checks) / sizeof(struct task_check));
+
+ /* Run every test */
+ for (i = 0; i < checks; i++) {
+ check = (struct task_check *) &window_checks[i];
+
+ task = flb_sp_task_create(sp, check->name, check->exec);
+ TEST_CHECK(task != NULL);
+
+ out_buf.buffer = NULL;
+ out_buf.size = 0;
+
+ double record_timestamp = 1.0;
+ if (check->window_type == FLB_SP_WINDOW_TUMBLING) {
+ ret = file_to_buf(DATA_SAMPLES, &data_buf);
+ if (ret == -1) {
+ flb_error("[sp test] cannot open DATA_SAMPLES file %s", DATA_SAMPLES);
+ flb_free(config);
+ return;
+ }
+
+ set_record_timestamps(&data_buf, &record_timestamp);
+
+ /* We ingest the buffer every second */
+ for (t = 0; t < check->window_size_sec; t++) {
+ if (out_buf.buffer != NULL) {
+ flb_free(out_buf.buffer);
+
+ out_buf.buffer = NULL;
+ out_buf.size = 0;
+ }
+
+ ret = flb_sp_do_test(sp, task,
+ "samples", strlen("samples"),
+ &data_buf, &out_buf);
+ if (ret == -1) {
+ flb_error("[sp test] error processing check '%s'",
+ check->name);
+ flb_sp_task_destroy(task);
+ return;
+ }
+
+ /* Sleep for 0.8 seconds, give some delta to the engine */
+ usleep(800000);
+ }
+
+ if (out_buf.buffer != NULL) {
+ flb_free(out_buf.buffer);
+
+ out_buf.buffer = NULL;
+ out_buf.size = 0;
+ }
+
+ flb_sp_fd_event_test(task->window.fd, task, &out_buf);
+
+ flb_info("[sp test] id=%i, SQL => '%s'", check->id, check->exec);
+ check->cb_check(check->id, check, out_buf.buffer, out_buf.size);
+ flb_pack_print(out_buf.buffer, out_buf.size);
+ flb_free(out_buf.buffer);
+ }
+ else if (check->window_type == FLB_SP_WINDOW_HOPPING) {
+ /* Ingest the buffer every second */
+ task->window.fd = 0;
+ task->window.fd_hop = 1;
+ double record_timestamp = 1.0;
+ for (t = 0; t < check->window_size_sec + check->window_hop_sec; t++) {
+ ret = snprintf(datafile, sizeof(datafile)-1, "%s%d.mp",
+ DATA_SAMPLES_HOPPING_WINDOW_PATH, t + 1);
+ if (!TEST_CHECK(ret <= sizeof(datafile)-1)) {
+ exit(1);
+ }
+ ret = file_to_buf(datafile, &data_buf);
+ if (ret == -1) {
+ flb_error("[sp test] cannot open DATA_SAMPLES file %s", datafile);
+ flb_free(config);
+ return;
+ }
+
+ /* Replace record timestamps with test timestamps */
+ set_record_timestamps(&data_buf, &record_timestamp);
+
+ ret = flb_sp_do_test(sp, task,
+ "samples", strlen("samples"),
+ &data_buf, &out_buf);
+ if (ret == -1) {
+ flb_error("[sp test] error processing check '%s'",
+ check->name);
+ flb_sp_task_destroy(task);
+ return;
+ }
+
+ /* Sleep for 0.8 seconds, give some delta to the engine */
+ usleep(800000);
+
+ /* Hopping event */
+ if ((t + 1) % check->window_hop_sec == 0) {
+ if (out_buf.buffer != NULL) {
+ flb_free(out_buf.buffer);
+
+ out_buf.buffer = NULL;
+ out_buf.size = 0;
+ }
+
+ flb_sp_fd_event_test(task->window.fd_hop, task, &out_buf);
+ }
+
+ /* Window event */
+ if ((t + 1) % check->window_size_sec == 0 ||
+ (t + 1 > check->window_size_sec && (t + 1 - check->window_size_sec) % check->window_hop_sec == 0)) {
+ if (out_buf.buffer != NULL) {
+ flb_free(out_buf.buffer);
+
+ out_buf.buffer = NULL;
+ out_buf.size = 0;
+ }
+
+ flb_sp_fd_event_test(task->window.fd, task, &out_buf);
+ }
+ flb_free(data_buf.buffer);
+ data_buf.buffer = NULL;
+ }
+
+ flb_info("[sp test] id=%i, SQL => '%s'", check->id, check->exec);
+ check->cb_check(check->id, check, out_buf.buffer, out_buf.size);
+ flb_pack_print(out_buf.buffer, out_buf.size);
+ flb_free(out_buf.buffer);
+ }
+
+ flb_free(data_buf.buffer);
+ }
+
+ flb_sp_destroy(sp);
+ mk_event_loop_destroy(config->evl);
+ flb_free(config);
+#ifdef _WIN32
+ WSACleanup();
+#endif
+}
+
+static void test_snapshot()
+{
+ int i;
+ int t;
+ int checks;
+ int ret;
+ char datafile[PATH_MAX];
+ char stream_name[100];
+ char window_val[3];
+ struct sp_buffer data_buf;
+ struct sp_buffer out_buf;
+ struct task_check *check;
+ struct task_check *check_flush;
+ struct flb_config *config;
+ struct flb_sp *sp;
+ struct flb_sp_task *task;
+ struct flb_sp_task *task_flush;
+
+#ifdef _WIN32
+ WSADATA wsa_data;
+#endif
+
+ flb_init_env();
+
+ config = flb_calloc(1, sizeof(struct flb_config));
+ if (!config) {
+ flb_errno();
+ return;
+ }
+#ifdef _WIN32
+ WSAStartup(0x0201, &wsa_data);
+#endif
+ mk_list_init(&config->inputs);
+ mk_list_init(&config->stream_processor_tasks);
+ config->evl = mk_event_loop_create(256);
+
+ sp = flb_sp_create(config);
+ if (!sp) {
+ flb_error("[sp test] cannot create stream processor context");
+ flb_free(config);
+ return;
+ }
+
+ ret = file_to_buf(DATA_SAMPLES, &data_buf);
+ if (ret == -1) {
+ flb_error("[sp test] cannot open DATA_SAMPLES file %s", DATA_SAMPLES);
+ flb_free(config);
+ return;
+ }
+
+ /* Total number of checks for select_keys */
+ checks = (sizeof(snapshot_checks) / (sizeof(struct task_check) * 2));
+
+ /* Run every test */
+ for (i = 0; i < checks; i++) {
+ /* Snapshot Create */
+ check = (struct task_check *) &snapshot_checks[i][0];
+
+ task = flb_sp_task_create(sp, check->name, check->exec);
+ if (!task) {
+ flb_error("[sp test] wrong check '%s', fix it!", check->name);
+ continue;
+ }
+
+ snprintf(stream_name, 100, "%s-%d", "SNAPSHOT", i);
+ task->cmd->stream_name = flb_sds_create(stream_name);
+ task->cmd->type = FLB_SP_CREATE_SNAPSHOT;
+ if (check->window_size_sec > 0) {
+ snprintf(window_val, 3, "%d", check->window_size_sec);
+ flb_sp_cmd_stream_prop_add(task->cmd, "seconds", window_val);
+ }
+
+ if (flb_sp_snapshot_create(task) == -1) {
+ flb_error("[sp test] error initializing snapshot for check '%s'!", check->name);
+ continue;
+ }
+
+ out_buf.buffer = NULL;
+ out_buf.size = 0;
+
+ /* Read 1.mp -> 5.mp message pack buffers created for window tests */
+ for (t = 0; t < 5; t++) {
+
+ ret = snprintf(datafile, sizeof(datafile)-1, "%s%d.mp",
+ DATA_SAMPLES_HOPPING_WINDOW_PATH, t + 1);
+ if (!TEST_CHECK(ret <= sizeof(datafile)-1)) {
+ exit(1);
+ }
+
+ if (data_buf.buffer) {
+ flb_free(data_buf.buffer);
+ data_buf.buffer = NULL;
+ }
+
+ ret = file_to_buf(datafile, &data_buf);
+ if (ret == -1) {
+ flb_error("[sp test] cannot open DATA_SAMPLES file %s", datafile);
+ flb_free(config);
+ return;
+ }
+
+ ret = flb_sp_do_test(sp, task,
+ "samples", strlen("samples"),
+ &data_buf, &out_buf);
+
+ if (ret == -1) {
+ flb_error("[sp test] error processing check '%s'", check->name);
+ flb_sp_task_destroy(task);
+ continue;
+ }
+ }
+
+ flb_sp_fd_event_test(task->window.fd, task, &out_buf);
+
+ flb_info("[sp test] id=%i, SQL => '%s'", check->id, check->exec);
+ check->cb_check(check->id, check, out_buf.buffer, out_buf.size);
+ flb_pack_print(out_buf.buffer, out_buf.size);
+ flb_free(out_buf.buffer);
+
+ /* Snapshot flush */
+ check_flush = (struct task_check *) &snapshot_checks[i][1];
+
+ task_flush = flb_sp_task_create(sp, check_flush->name, check_flush->exec);
+ if (!task_flush) {
+ flb_error("[sp test] wrong check '%s', fix it!", check_flush->name);
+ continue;
+ }
+
+ snprintf(stream_name, 100, "%s-%d", "__flush_SNAPSHOT", i);
+ task_flush->cmd->stream_name = flb_sds_create(stream_name);
+ task_flush->cmd->type = FLB_SP_FLUSH_SNAPSHOT;
+
+ out_buf.buffer = NULL;
+ out_buf.size = 0;
+
+ ret = flb_sp_do_test(sp, task_flush,
+ "samples", strlen("samples"),
+ &data_buf, &out_buf);
+ if (ret == -1) {
+ flb_error("[sp test] error processing check '%s'", check_flush->name);
+ flb_sp_task_destroy(task_flush);
+ continue;
+ }
+
+ flb_sp_fd_event_test(task->window.fd, task_flush, &out_buf);
+
+ flb_info("[sp test] id=%i, SQL => '%s'", check_flush->id, check_flush->exec);
+ check_flush->cb_check(check_flush->id, check_flush, out_buf.buffer, out_buf.size);
+ flb_pack_print(out_buf.buffer, out_buf.size);
+ flb_free(out_buf.buffer);
+
+ flb_free(data_buf.buffer);
+ data_buf.buffer = NULL;
+ }
+
+ flb_free(data_buf.buffer);
+ flb_sp_destroy(sp);
+ mk_event_loop_destroy(config->evl);
+ flb_free(config);
+#ifdef _WIN32
+ WSACleanup();
+#endif
+}
+
+static void test_conv_from_str_to_num()
+{
+ struct flb_config *config = NULL;
+ struct flb_sp *sp = NULL;
+ struct flb_sp_task *task = NULL;
+ struct sp_buffer out_buf;
+ struct sp_buffer data_buf;
+ msgpack_sbuffer sbuf;
+ msgpack_packer pck;
+ msgpack_unpacked result;
+ size_t off = 0;
+ char json[4096] = {0};
+ int ret;
+
+#ifdef _WIN32
+ WSADATA wsa_data;
+
+ WSAStartup(0x0201, &wsa_data);
+#endif
+ out_buf.buffer = NULL;
+
+ flb_init_env();
+
+ config = flb_config_init();
+ config->evl = mk_event_loop_create(256);
+
+ ret = flb_storage_create(config);
+ if (!TEST_CHECK(ret == 0)) {
+ TEST_MSG("flb_storage_create failed");
+ flb_config_exit(config);
+ return;
+ }
+
+ sp = flb_sp_create(config);
+ if (!TEST_CHECK(sp != NULL)) {
+ TEST_MSG("[sp test] cannot create stream processor context");
+ goto test_conv_from_str_to_num_end;
+ }
+
+ task = flb_sp_task_create(sp, "tail.0", "CREATE STREAM test WITH (tag=\'test\') AS SELECT word, num, COUNT(*) FROM STREAM:tail.0 WINDOW TUMBLING (1 SECOND) GROUP BY word, num;");
+ if (!TEST_CHECK(task != NULL)) {
+ TEST_MSG("[sp test] wrong check 'conv', fix it!");
+ goto test_conv_from_str_to_num_end;
+ }
+
+ /* Create input data */
+ msgpack_sbuffer_init(&sbuf);
+ msgpack_packer_init(&pck, &sbuf, msgpack_sbuffer_write);
+ msgpack_pack_array(&pck, 2);
+ flb_pack_time_now(&pck);
+ msgpack_pack_map(&pck, 2);
+
+ msgpack_pack_str(&pck, 4);
+ msgpack_pack_str_body(&pck, "word", 4);
+ msgpack_pack_str(&pck, 4);
+ msgpack_pack_str_body(&pck, "hoge", 4);
+
+ msgpack_pack_str(&pck, 3);
+ msgpack_pack_str_body(&pck, "num", 3);
+ msgpack_pack_str(&pck, 6);
+ msgpack_pack_str_body(&pck, "123456", 6);
+
+ data_buf.buffer = sbuf.data;
+ data_buf.size = sbuf.size;
+
+ out_buf.buffer = NULL;
+ out_buf.size = 0;
+
+ /* Exec stream processor */
+ ret = flb_sp_do_test(sp, task, "tail.0", strlen("tail.0"), &data_buf, &out_buf);
+ if (!TEST_CHECK(ret == 0)) {
+ TEST_MSG("flb_sp_do_test failed");
+ msgpack_sbuffer_destroy(&sbuf);
+ goto test_conv_from_str_to_num_end;
+ }
+
+ if (!TEST_CHECK(out_buf.size > 0)) {
+ TEST_MSG("out_buf size is 0");
+ msgpack_sbuffer_destroy(&sbuf);
+ goto test_conv_from_str_to_num_end;
+ }
+
+
+ /* Check output buffer. It should contain a number 123456 not a string "123456" */
+
+ msgpack_unpacked_init(&result);
+ ret = msgpack_unpack_next(&result, out_buf.buffer, out_buf.size, &off);
+ if (!TEST_CHECK(ret == MSGPACK_UNPACK_SUCCESS)) {
+ TEST_MSG("failed to unpack ret=%d", ret);
+ msgpack_unpacked_destroy(&result);
+ msgpack_sbuffer_destroy(&sbuf);
+ goto test_conv_from_str_to_num_end;
+ }
+
+ ret = flb_msgpack_to_json(&json[0], sizeof(json), &result.data);
+ if (!TEST_CHECK(ret > 0)) {
+ TEST_MSG("flb_msgpack_to_json failed");
+ msgpack_unpacked_destroy(&result);
+ msgpack_sbuffer_destroy(&sbuf);
+ goto test_conv_from_str_to_num_end;
+ }
+
+ if (!TEST_CHECK(strstr(json,"123456") != NULL)) {
+ TEST_MSG("number not found");
+ msgpack_unpacked_destroy(&result);
+ msgpack_sbuffer_destroy(&sbuf);
+ goto test_conv_from_str_to_num_end;
+ }
+ if (!TEST_CHECK(strstr(json,"\"123456\"") == NULL)) {
+ TEST_MSG("output should be number type");
+ msgpack_unpacked_destroy(&result);
+ msgpack_sbuffer_destroy(&sbuf);
+ goto test_conv_from_str_to_num_end;
+ }
+
+ msgpack_unpacked_destroy(&result);
+ msgpack_sbuffer_destroy(&sbuf);
+
+ test_conv_from_str_to_num_end:
+ if (out_buf.buffer != NULL) {
+ flb_free(out_buf.buffer);
+ }
+
+#ifdef _WIN32
+ WSACleanup();
+#endif
+ if (sp != NULL) {
+ flb_sp_destroy(sp);
+ }
+ flb_storage_destroy(config);
+ flb_config_exit(config);
+}
+
+TEST_LIST = {
+ { "invalid_queries", invalid_queries},
+ { "select_keys", test_select_keys},
+ { "select_subkeys", test_select_subkeys},
+ { "window", test_window},
+ { "snapshot", test_snapshot},
+ { "conv_from_str_to_num", test_conv_from_str_to_num},
+ { NULL }
+};
diff --git a/fluent-bit/tests/internal/typecast.c b/fluent-bit/tests/internal/typecast.c
new file mode 100644
index 00000000..511d8cf5
--- /dev/null
+++ b/fluent-bit/tests/internal/typecast.c
@@ -0,0 +1,359 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/* Fluent Bit
+ * ==========
+ * Copyright (C) 2019-2021 The Fluent Bit Authors
+ * Copyright (C) 2015-2018 Treasure Data Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <fluent-bit/flb_typecast.h>
+#include "flb_tests_internal.h"
+#include <msgpack.h>
+#include <string.h>
+
+void str_to_int()
+{
+ char *input = "1234";
+
+ msgpack_sbuffer sbuf;
+ msgpack_packer pck;
+ msgpack_unpacked result;
+ size_t off = 0;
+
+ struct flb_typecast_rule *rule = NULL;
+ struct flb_typecast_value *val = NULL;
+
+ /* create input object */
+ msgpack_sbuffer_init(&sbuf);
+ msgpack_packer_init(&pck, &sbuf, msgpack_sbuffer_write);
+
+ msgpack_pack_str(&pck, strlen(input));
+ msgpack_pack_str_body(&pck, input, strlen(input));
+
+ msgpack_unpacked_init(&result);
+ msgpack_unpack_next(&result, sbuf.data, sbuf.size, &off);
+
+ /* create rule */
+ rule = flb_typecast_rule_create("string", 6, "int", 3);
+ if (!TEST_CHECK(rule != NULL)) {
+ TEST_MSG("failed to create rule");
+ exit(EXIT_FAILURE);
+ }
+
+ val = flb_typecast_value_create(result.data, rule);
+ if(!TEST_CHECK(val != NULL)){
+ TEST_MSG("failed to create value");
+ exit(EXIT_FAILURE);
+ }
+
+ TEST_CHECK(val->type == FLB_TYPECAST_TYPE_INT);
+ if(!TEST_CHECK(val->val.i_num == 1234)) {
+ TEST_MSG("got %ld. expect 1234", val->val.i_num);
+ }
+
+ flb_typecast_rule_destroy(rule);
+ flb_typecast_value_destroy(val);
+
+ msgpack_sbuffer_destroy(&sbuf);
+ msgpack_unpacked_destroy(&result);
+}
+
+void int_to_str()
+{
+ int input = 1234;
+
+ msgpack_sbuffer sbuf;
+ msgpack_packer pck;
+ msgpack_unpacked result;
+ size_t off = 0;
+
+ struct flb_typecast_rule *rule = NULL;
+ struct flb_typecast_value *val = NULL;
+
+ /* create input object */
+ msgpack_sbuffer_init(&sbuf);
+ msgpack_packer_init(&pck, &sbuf, msgpack_sbuffer_write);
+
+ msgpack_pack_int64(&pck, input);
+
+ msgpack_unpacked_init(&result);
+ msgpack_unpack_next(&result, sbuf.data, sbuf.size, &off);
+
+ /* create rule */
+ rule = flb_typecast_rule_create("int", 3, "string", 6);
+ if (!TEST_CHECK(rule != NULL)) {
+ TEST_MSG("failed to create rule");
+ exit(EXIT_FAILURE);
+ }
+
+ val = flb_typecast_value_create(result.data, rule);
+ if(!TEST_CHECK(val != NULL)){
+ TEST_MSG("failed to create value");
+ exit(EXIT_FAILURE);
+ }
+
+ TEST_CHECK(val->type == FLB_TYPECAST_TYPE_STR);
+ if(!TEST_CHECK(!strcmp(val->val.str, "1234"))) {
+ TEST_MSG("got %s. expect \"1234\"", val->val.str);
+ }
+
+ flb_typecast_rule_destroy(rule);
+ flb_typecast_value_destroy(val);
+
+ msgpack_sbuffer_destroy(&sbuf);
+ msgpack_unpacked_destroy(&result);
+}
+
+void bool_to_str()
+{
+ msgpack_sbuffer sbuf;
+ msgpack_packer pck;
+ msgpack_unpacked result;
+ size_t off = 0;
+
+ struct flb_typecast_rule *rule = NULL;
+ struct flb_typecast_value *val = NULL;
+
+ /* create input object */
+ msgpack_sbuffer_init(&sbuf);
+ msgpack_packer_init(&pck, &sbuf, msgpack_sbuffer_write);
+
+ msgpack_pack_true(&pck);
+
+ msgpack_unpacked_init(&result);
+ msgpack_unpack_next(&result, sbuf.data, sbuf.size, &off);
+
+ /* create rule */
+ rule = flb_typecast_rule_create("bool", 4, "string", 6);
+ if (!TEST_CHECK(rule != NULL)) {
+ TEST_MSG("failed to create rule");
+ exit(EXIT_FAILURE);
+ }
+
+ val = flb_typecast_value_create(result.data, rule);
+ if(!TEST_CHECK(val != NULL)){
+ TEST_MSG("failed to create value");
+ exit(EXIT_FAILURE);
+ }
+
+ TEST_CHECK(val->type == FLB_TYPECAST_TYPE_STR);
+ if(!TEST_CHECK(!strcmp(val->val.str, "true"))) {
+ TEST_MSG("got %s. expect \"true\"", val->val.str);
+ }
+
+ flb_typecast_rule_destroy(rule);
+ flb_typecast_value_destroy(val);
+
+ msgpack_sbuffer_destroy(&sbuf);
+ msgpack_unpacked_destroy(&result);
+}
+
+void str_to_bool()
+{
+ char *input = "true";
+
+ msgpack_sbuffer sbuf;
+ msgpack_packer pck;
+ msgpack_unpacked result;
+ size_t off = 0;
+
+ struct flb_typecast_rule *rule = NULL;
+ struct flb_typecast_value *val = NULL;
+
+ /* create input object */
+ msgpack_sbuffer_init(&sbuf);
+ msgpack_packer_init(&pck, &sbuf, msgpack_sbuffer_write);
+
+ msgpack_pack_str(&pck, strlen(input));
+ msgpack_pack_str_body(&pck, input, strlen(input));
+
+ msgpack_unpacked_init(&result);
+ msgpack_unpack_next(&result, sbuf.data, sbuf.size, &off);
+
+ /* create rule */
+ rule = flb_typecast_rule_create("string", 6, "bool", 4);
+ if (!TEST_CHECK(rule != NULL)) {
+ TEST_MSG("failed to create rule");
+ exit(EXIT_FAILURE);
+ }
+
+ val = flb_typecast_value_create(result.data, rule);
+ if(!TEST_CHECK(val != NULL)){
+ TEST_MSG("failed to create value");
+ exit(EXIT_FAILURE);
+ }
+
+ TEST_CHECK(val->type == FLB_TYPECAST_TYPE_BOOL);
+ if(!TEST_CHECK(val->val.boolean == FLB_TRUE)) {
+ TEST_MSG("got %d. expect FLB_TRUE", val->val.boolean);
+ }
+
+ flb_typecast_rule_destroy(rule);
+ flb_typecast_value_destroy(val);
+
+ msgpack_sbuffer_destroy(&sbuf);
+ msgpack_unpacked_destroy(&result);
+}
+
+void str_to_hex()
+{
+ char *input = "0xdeadbeef";
+
+ msgpack_sbuffer sbuf;
+ msgpack_packer pck;
+ msgpack_unpacked result;
+ size_t off = 0;
+
+ struct flb_typecast_rule *rule = NULL;
+ struct flb_typecast_value *val = NULL;
+
+ /* create input object */
+ msgpack_sbuffer_init(&sbuf);
+ msgpack_packer_init(&pck, &sbuf, msgpack_sbuffer_write);
+
+ msgpack_pack_str(&pck, strlen(input));
+ msgpack_pack_str_body(&pck, input, strlen(input));
+
+ msgpack_unpacked_init(&result);
+ msgpack_unpack_next(&result, sbuf.data, sbuf.size, &off);
+
+ /* create rule */
+ rule = flb_typecast_rule_create("string", 6, "hex", 3);
+ if (!TEST_CHECK(rule != NULL)) {
+ TEST_MSG("failed to create rule");
+ exit(EXIT_FAILURE);
+ }
+
+ val = flb_typecast_value_create(result.data, rule);
+ if(!TEST_CHECK(val != NULL)){
+ TEST_MSG("failed to create value");
+ exit(EXIT_FAILURE);
+ }
+
+ TEST_CHECK(val->type == FLB_TYPECAST_TYPE_HEX);
+ if(!TEST_CHECK(val->val.ui_num == 0xdeadbeef)) {
+ TEST_MSG("got 0x%lx. expect 0xdeadbeef", val->val.ui_num);
+ }
+
+ flb_typecast_rule_destroy(rule);
+ flb_typecast_value_destroy(val);
+
+ msgpack_sbuffer_destroy(&sbuf);
+ msgpack_unpacked_destroy(&result);
+}
+
+void str_to_float()
+{
+ char *input = "1234.567";
+
+ msgpack_sbuffer sbuf;
+ msgpack_packer pck;
+ msgpack_unpacked result;
+ size_t off = 0;
+
+ struct flb_typecast_rule *rule = NULL;
+ struct flb_typecast_value *val = NULL;
+
+ /* create input object */
+ msgpack_sbuffer_init(&sbuf);
+ msgpack_packer_init(&pck, &sbuf, msgpack_sbuffer_write);
+
+ msgpack_pack_str(&pck, strlen(input));
+ msgpack_pack_str_body(&pck, input, strlen(input));
+
+ msgpack_unpacked_init(&result);
+ msgpack_unpack_next(&result, sbuf.data, sbuf.size, &off);
+
+ /* create rule */
+ rule = flb_typecast_rule_create("string", 6, "float", 5);
+ if (!TEST_CHECK(rule != NULL)) {
+ TEST_MSG("failed to create rule");
+ exit(EXIT_FAILURE);
+ }
+
+ val = flb_typecast_value_create(result.data, rule);
+ if(!TEST_CHECK(val != NULL)){
+ TEST_MSG("failed to create value");
+ exit(EXIT_FAILURE);
+ }
+
+ TEST_CHECK(val->type == FLB_TYPECAST_TYPE_FLOAT);
+ if(!TEST_CHECK(val->val.d_num == 1234.567)) {
+ TEST_MSG("got %f. expect 1234.567", val->val.d_num);
+ }
+
+ flb_typecast_rule_destroy(rule);
+ flb_typecast_value_destroy(val);
+
+ msgpack_sbuffer_destroy(&sbuf);
+ msgpack_unpacked_destroy(&result);
+}
+
+void float_to_str()
+{
+ double input = 1234.567;
+
+ msgpack_sbuffer sbuf;
+ msgpack_packer pck;
+ msgpack_unpacked result;
+ size_t off = 0;
+
+ struct flb_typecast_rule *rule = NULL;
+ struct flb_typecast_value *val = NULL;
+
+ /* create input object */
+ msgpack_sbuffer_init(&sbuf);
+ msgpack_packer_init(&pck, &sbuf, msgpack_sbuffer_write);
+
+ msgpack_pack_float(&pck, input);
+
+ msgpack_unpacked_init(&result);
+ msgpack_unpack_next(&result, sbuf.data, sbuf.size, &off);
+
+ /* create rule */
+ rule = flb_typecast_rule_create("float", 5, "string", 6);
+ if (!TEST_CHECK(rule != NULL)) {
+ TEST_MSG("failed to create rule");
+ exit(EXIT_FAILURE);
+ }
+
+ val = flb_typecast_value_create(result.data, rule);
+ if(!TEST_CHECK(val != NULL)){
+ TEST_MSG("failed to create value");
+ exit(EXIT_FAILURE);
+ }
+
+ TEST_CHECK(val->type == FLB_TYPECAST_TYPE_STR);
+ if(!TEST_CHECK(strstr(val->val.str, "1234.567") != NULL)) {
+ TEST_MSG("got %s. expect \"1234.567\"", val->val.str);
+ }
+
+ flb_typecast_rule_destroy(rule);
+ flb_typecast_value_destroy(val);
+
+ msgpack_sbuffer_destroy(&sbuf);
+ msgpack_unpacked_destroy(&result);
+}
+
+TEST_LIST = {
+ {"str_to_int", str_to_int},
+ {"int_to_str", int_to_str},
+ {"str_to_float", str_to_float},
+ {"float_to_str", float_to_str},
+ {"bool_to_str", bool_to_str},
+ {"str_to_bool", str_to_bool},
+ {"str_to_hex", str_to_hex},
+ {NULL, NULL}
+};
diff --git a/fluent-bit/tests/internal/unit_sizes.c b/fluent-bit/tests/internal/unit_sizes.c
new file mode 100644
index 00000000..d009bafb
--- /dev/null
+++ b/fluent-bit/tests/internal/unit_sizes.c
@@ -0,0 +1,59 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+#include <fluent-bit/flb_info.h>
+#include <fluent-bit/flb_utils.h>
+
+#include "flb_tests_internal.h"
+
+#define to_bytes flb_utils_size_to_bytes
+
+void test_unit_sizes()
+{
+ int64_t KB = 1000;
+ int64_t MB = 1000 * KB;
+ int64_t GB = 1000 * MB;
+
+ /* Bytes, no prefixes */
+ TEST_CHECK(to_bytes("1") == 1);
+ TEST_CHECK(to_bytes("50") == 50);
+ TEST_CHECK(to_bytes("1000") == 1*KB);
+
+ /* Decimal prefix: KB */
+ TEST_CHECK(to_bytes("1KB") == KB);
+ TEST_CHECK(to_bytes("1K") == KB);
+ TEST_CHECK(to_bytes("1kB") == KB);
+ TEST_CHECK(to_bytes("1kb") == KB);
+ TEST_CHECK(to_bytes("1k") == KB);
+
+ /* Decimal prefix: MB */
+ TEST_CHECK(to_bytes("1MB") == MB);
+ TEST_CHECK(to_bytes("1M") == MB);
+ TEST_CHECK(to_bytes("1mB") == MB);
+ TEST_CHECK(to_bytes("1mb") == MB);
+ TEST_CHECK(to_bytes("1m") == MB);
+ TEST_CHECK(to_bytes("5m") == 5*MB);
+
+ /* Decimal prefix: GB */
+ TEST_CHECK(to_bytes("1GB") == GB);
+ TEST_CHECK(to_bytes("1G") == GB);
+ TEST_CHECK(to_bytes("1gB") == GB);
+ TEST_CHECK(to_bytes("1gb") == GB);
+ TEST_CHECK(to_bytes("1g") == GB);
+ TEST_CHECK(to_bytes("5g") == 5*GB);
+ TEST_CHECK(to_bytes("32g") == 32*GB);
+
+ /* Invalid values */
+ TEST_CHECK(to_bytes("aabb") == -1);
+ TEST_CHECK(to_bytes("") == -1);
+
+ /* Invlid prefixes */
+ TEST_CHECK(to_bytes("1kX") == -1);
+ TEST_CHECK(to_bytes("1kX") == -1);
+ TEST_CHECK(to_bytes("1MX") == -1);
+ TEST_CHECK(to_bytes("1GX") == -1);
+}
+
+TEST_LIST = {
+ { "unit_sizes", test_unit_sizes },
+ { 0 }
+};
diff --git a/fluent-bit/tests/internal/uri.c b/fluent-bit/tests/internal/uri.c
new file mode 100644
index 00000000..2b533b37
--- /dev/null
+++ b/fluent-bit/tests/internal/uri.c
@@ -0,0 +1,87 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/* Fluent Bit
+ * ==========
+ * Copyright (C) 2015-2023 The Fluent Bit Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <fluent-bit/flb_info.h>
+#include <fluent-bit/flb_uri.h>
+#include <string.h>
+
+#include "flb_tests_internal.h"
+
+void uri_create_destroy()
+{
+ struct flb_uri *uri;
+ const char *uri_str = "https://fluentbit.io";
+
+ uri = flb_uri_create(uri_str);
+ if (!TEST_CHECK(uri != NULL)) {
+ TEST_MSG("flb_uri_create failed");
+ return;
+ }
+
+ flb_uri_destroy(uri);
+}
+
+void uri_get()
+{
+ struct flb_uri *uri;
+ struct flb_uri_field *field;
+ const char *uri_str = "https://fluentbit.io";
+
+ uri = flb_uri_create(uri_str);
+ if (!TEST_CHECK(uri != NULL)) {
+ TEST_MSG("flb_uri_create failed");
+ return;
+ }
+
+ field = flb_uri_get(uri, 0);
+ if (!TEST_CHECK(field != NULL)) {
+ TEST_MSG("flb_uri_get failed");
+ return;
+ }
+
+ field = flb_uri_get(uri, -1);
+ if (!TEST_CHECK(field == NULL)) {
+ TEST_MSG("flb_uri_get should fail");
+ return;
+ }
+
+ flb_uri_destroy(uri);
+}
+
+void uri_encode()
+{
+ flb_sds_t encoded_uri;
+ const char *input = "&# ";
+ const char *expect = "%26%23%20";
+
+ encoded_uri = flb_uri_encode(input, strlen(input));
+ if (!TEST_CHECK(encoded_uri != NULL)) {
+ TEST_MSG("flb_uri_encode failed");
+ return;
+ }
+
+ flb_sds_destroy(encoded_uri);
+}
+
+TEST_LIST = {
+ { "uri_create_destroy", uri_create_destroy },
+ { "uri_get", uri_get },
+ { "uri_encode", uri_encode },
+ { 0 }
+};
diff --git a/fluent-bit/tests/internal/utils.c b/fluent-bit/tests/internal/utils.c
new file mode 100644
index 00000000..119094bd
--- /dev/null
+++ b/fluent-bit/tests/internal/utils.c
@@ -0,0 +1,622 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+#include <fluent-bit/flb_info.h>
+#include <fluent-bit/flb_mem.h>
+#include <fluent-bit/flb_utils.h>
+#include <stdarg.h>
+#include "flb_tests_internal.h"
+#include "fluent-bit/flb_macros.h"
+
+
+struct url_check {
+ int ret;
+ char *url; /* full URL */
+ char *prot; /* expected protocol */
+ char *host; /* expected host */
+ char *port; /* expected port */
+ char *uri; /* expected uri */
+};
+
+struct write_str_case {
+ char *input;
+ int input_len;
+ char *output;
+ int ret;
+};
+
+struct url_check url_checks[] = {
+ {0, "https://fluentbit.io/something",
+ "https", "fluentbit.io", "443", "/something"},
+ {0, "http://fluentbit.io/something",
+ "http", "fluentbit.io", "80", "/something"},
+ {0, "https://fluentbit.io", "https", "fluentbit.io", "443", "/"},
+ {0, "https://fluentbit.io:1234/something",
+ "https", "fluentbit.io", "1234", "/something"},
+ {0, "https://fluentbit.io:1234", "https", "fluentbit.io", "1234", "/"},
+ {0, "https://fluentbit.io:1234/", "https", "fluentbit.io", "1234", "/"},
+ {0, "https://fluentbit.io:1234/v", "https", "fluentbit.io", "1234", "/v"},
+ {-1, "://", NULL, NULL, NULL, NULL},
+};
+
+void test_url_split()
+{
+ int i;
+ int ret;
+ int size;
+ char *protocol;
+ char *host;
+ char *port;
+ char *uri;
+ struct url_check *u;
+
+ size = sizeof(url_checks) / sizeof(struct url_check);
+ for (i = 0; i < size; i ++) {
+ u = &url_checks[i];
+
+ protocol = NULL;
+ host = NULL;
+ port = NULL;
+ uri = NULL;
+
+ ret = flb_utils_url_split(u->url, &protocol, &host, &port, &uri);
+ TEST_CHECK(ret == u->ret);
+ if (ret == -1) {
+ continue;
+ }
+
+ /* protocol */
+ if (u->prot) {
+ TEST_CHECK(protocol != NULL);
+
+ ret = strcmp(u->prot, protocol);
+ TEST_CHECK(ret == 0);
+ }
+ else {
+ TEST_CHECK(protocol == NULL);
+ }
+
+ /* host */
+ if (u->host) {
+ TEST_CHECK(host != NULL);
+ ret = strcmp(u->host, host);
+ TEST_CHECK(ret == 0);
+ }
+ else {
+ TEST_CHECK(host == NULL);
+ }
+
+ /* port */
+ if (u->port) {
+ TEST_CHECK(port != NULL);
+ ret = strcmp(u->port, port);
+ TEST_CHECK(ret == 0);
+ }
+ else {
+ TEST_CHECK(port == NULL);
+ }
+
+ /* uri */
+ if (u->uri) {
+ TEST_CHECK(uri != NULL);
+ ret = strcmp(u->uri, uri);
+ TEST_CHECK(ret == 0);
+ }
+ else {
+ TEST_CHECK(uri == NULL);
+ }
+
+ if (protocol) {
+ flb_free(protocol);
+ }
+ if (host) {
+ flb_free(host);
+ }
+ if (port) {
+ flb_free(port);
+ }
+ if (uri) {
+ flb_free(uri);
+ }
+ }
+}
+
+/* test case loop for flb_utils_write_str */
+static void write_str_test_cases_w_buf_size(struct write_str_case *cases, int buf_size);
+static void write_str_test_cases(struct write_str_case *cases) {
+ write_str_test_cases_w_buf_size(cases, 100);
+}
+
+/* test case loop for flb_utils_write_str */
+static void write_str_test_cases_w_buf_size(struct write_str_case *cases, int buf_size) {
+ char *buf = flb_calloc(buf_size + 1, sizeof(char));
+ int size = buf_size + 1;
+ int off;
+ int ret;
+
+ struct write_str_case *tcase = cases;
+ while (!(tcase->input == 0 && tcase->output == 0)) {
+ memset(buf, 0, size);
+ off = 0;
+ ret = flb_utils_write_str(buf, &off, buf_size, tcase->input, tcase->input_len);
+
+ if(!TEST_CHECK(ret == tcase->ret)) {
+ TEST_MSG("Input string: %s", tcase->input);
+ TEST_MSG("| Expected return value: %s", (tcase->ret == FLB_TRUE) ? "FLB_TRUE"
+ : "FLB_FALSE");
+ TEST_MSG("| Produced return value: %s", (ret == FLB_TRUE) ? "FLB_TRUE"
+ : "FLB_FALSE");
+ }
+ if(!TEST_CHECK(memcmp(buf, tcase->output, off) == 0)) {
+ TEST_MSG("Input string: %s", tcase->input);
+ TEST_MSG("| Expected output: %s", tcase->output);
+ TEST_MSG("| Produced output: %s", buf);
+ }
+ if (!TEST_CHECK(strlen(buf) == strlen(tcase->output))) {
+ TEST_MSG("Input string: %s", tcase->input);
+ TEST_MSG("| Expected length: %zu", strlen(tcase->output));
+ TEST_MSG("| Produced length: %zu", strlen(buf));
+ TEST_MSG("| Expected output: %s", tcase->output);
+ TEST_MSG("| Produced output: %s", buf);
+ }
+ if (!TEST_CHECK(buf[size-1] == 0)) {
+ TEST_MSG("Out buffer overwrite detected '%c'", buf[size-1]);
+ }
+
+ ++tcase;
+ }
+
+ flb_free(buf);
+}
+
+void test_write_str()
+{
+ char buf[10];
+ char japanese_a[4] = {0xe3, 0x81, 0x82};
+ int size = sizeof(buf);
+ int off;
+ int ret;
+
+ off = 0;
+ ret = flb_utils_write_str(buf, &off, size, "a", 1);
+ TEST_CHECK(ret == FLB_TRUE);
+ TEST_CHECK(memcmp(buf, "a", off) == 0);
+
+ off = 0;
+ ret = flb_utils_write_str(buf, &off, size, "\n", 1);
+ TEST_CHECK(ret == FLB_TRUE);
+ TEST_CHECK(memcmp(buf, "\\n", off) == 0);
+
+ off = 0;
+ ret = flb_utils_write_str(buf, &off, size, "\xe3\x81\x82", 3);
+ TEST_CHECK(ret == FLB_TRUE);
+ TEST_CHECK(memcmp(buf, japanese_a, off) == 0);
+
+ // Truncated bytes
+ off = 0;
+ ret = flb_utils_write_str(buf, &off, size, "\xe3\x81\x82\xe3", 1);
+ TEST_CHECK(ret == FLB_TRUE);
+ TEST_CHECK(memcmp(buf, japanese_a, off) == 0);
+
+ // Error: buffer too small
+ off = 0;
+ ret = flb_utils_write_str(buf, &off, size, "aaaaaaaaaaa", 11);
+ TEST_CHECK(ret == FLB_FALSE);
+}
+
+void test_write_str_invalid_trailing_bytes()
+{
+ struct write_str_case cases[] = {
+ /* Invalid unicode (one bad trailing bytes) */
+ {
+ "\xe3\x81\x01""abc", 6, /* note that 0x01 is an invalid byte */
+ "\xee\x83\xa3" /* e3 fragment */ /* replace invalid unicode */
+ "\xee\x82\x81" /* 81 fragment */
+ "\\u0001abc",
+ FLB_TRUE
+ },
+ /*
+ * Invalid unicode (two bad trailing bytes)
+ */
+ {
+ "\xe3\x01\x01""abc", 6,
+ "\xee\x83\xa3" /* e3 fragment */
+ "\\u0001\\u0001abc",
+ FLB_TRUE
+ },
+ { 0 }
+ };
+
+ write_str_test_cases(cases);
+}
+
+void test_write_str_invalid_leading_byte()
+{
+
+ struct write_str_case cases[] = {
+ /*
+ * Escaped leading hex (two hex, one valid unicode)
+ */
+ {
+ "\x00\x01\xe3\x81\x82""abc", 8, /* note that 0x01 is an invalid byte */
+ "\\u0000\\u0001""\xe3\x81\x82""abc", /* escape hex */
+ FLB_TRUE
+ },
+ /*
+ * Invalid unicode fragment (two byte fragment)
+ * note that 0xf3 is a leading byte with 3 trailing bytes. note that 0xe3 is also a
+ * leading byte with 2 trailing bytes. This should not be consumed by 0xf3 invalid
+ * unicode character
+ */
+ {
+ "\xf3\x81\x81\xe3\x81\x82""abc", 9, /* note that 0xf3 0x81 0x81 is an invalid fragment */
+ "\xee\x83\xb3" /* f3 fragment */ /* replace invalid unicode */
+ "\xee\x82\x81" /* 81 fragment */
+ "\xee\x82\x81" /* 81 fragment */
+ "\xe3\x81\x82""abc", /* valid unicode */
+ FLB_TRUE
+ },
+ /*
+ * Invalid unicode (one bad leading byte + one bad trailing byte)
+ * note that 0xf3 is a leading byte with 3 trailing bytes. 0x01 is an invalid byte
+ */
+ {
+ "\xf3\x81\x01\xe3\x81\x82""abc", 9, /* note that 0x01 is an invalid byte */
+ "\xee\x83\xb3" /* f3 fragment */ /* replace invalid unicode */
+ "\xee\x82\x81" /* 81 fragment */
+ "\\u0001""\xe3\x81\x82""abc",
+ FLB_TRUE
+ },
+ { 0 }
+ };
+
+ write_str_test_cases(cases);
+}
+
+void test_write_str_invalid_leading_byte_case_2()
+{
+
+ struct write_str_case cases[] = {
+ /* Invalid leading bytes */
+ {
+ "\x81\x82""abc", 5, /* note that 0x81 & 0x82 are invalid leading bytes */
+ "\xee\x82\x81" /* 81 fragment */ /* replace invalid unicode */
+ "\xee\x82\x82" /* 82 fragment */
+ "abc",
+ FLB_TRUE
+ },
+ /*
+ * Invalid unicode (one bad leading byte + one bad trailing byte + one bad leading byte)
+ * note that 0xf3 is a leading byte with 3 trailing bytes. 0x01 is an invalid byte
+ * 0x81 & 0x82 are invalid leading bytes
+ */
+ {
+ "\xf3\x81\x01\x81\x82""abc", 8, /* note that 0x81 & 0x82 are invalid leading bytes */
+ "\xee\x83\xb3" /* f3 fragment */ /* replace invalid unicode */
+ "\xee\x82\x81" /* 81 fragment */
+ "\\u0001" /* 0x01 hex escape */
+ "\xee\x82\x81" /* 81 fragment */
+ "\xee\x82\x82" /* 82 fragment */
+ "abc",
+ FLB_TRUE
+ },
+ { 0 }
+ };
+
+ write_str_test_cases(cases);
+}
+
+void test_write_str_edge_cases()
+{
+ struct write_str_case cases[] = {
+ /* Invalid unicode (one bad leading byte) */
+ {
+ "\xf3", 1, /* will this buffer overrun? */
+ "", /* discard invalid unicode */
+ FLB_TRUE
+ },
+ { 0 }
+ };
+
+ write_str_test_cases(cases);
+}
+
+void test_write_str_buffer_overrun()
+{
+ struct write_str_case cases[] = {
+ {
+ "aa""\x81", 3,
+ "aa"
+ "\xee\x82\x81", /* just enough space for 81 fragment */
+ FLB_TRUE
+ },
+ {
+ "aaa""\x81", 4, /* out buffer size: 5, needed bytes: 2 + 3 + 3 = 8 */
+ "aaa",
+ /* "\xee\x82\x81", */ /* 81 fragment -- would overrun */
+ FLB_FALSE
+ },
+ {
+ "aaa"
+ "\xe3\x81\x82", 6, /* required is already grater than buffer */
+ "",
+ FLB_FALSE
+ },
+ {
+ "\""
+ "\xe3\x81\x82", 4, /* valid unicode */
+ "\\\"""\xe3\x81\x82", /* just enough space for valid unicode */
+ FLB_TRUE
+ },
+ {
+ "\x81"
+ "\xe3\x81\x82", 4, /* valid unicode */
+ "\xee\x82\x81", /* 81 fragment */
+ /* not enough space for valid unicode fragment "\xe3\x81\x82" */
+ FLB_FALSE
+ },
+ { 0 }
+ };
+ write_str_test_cases_w_buf_size(cases, 5);
+}
+
+struct proxy_url_check {
+ int ret;
+ char *url; /* full URL */
+ char *prot; /* expected protocol */
+ char *host; /* expected host */
+ char *port; /* expected port */
+ char *username; /* expected username */
+ char *password; /* expected password */
+};
+
+struct proxy_url_check proxy_url_checks[] = {
+ {0, "http://foo:bar@proxy.com:8080",
+ "http", "proxy.com", "8080", "foo", "bar"},
+ {0, "http://proxy.com",
+ "http", "proxy.com", "80", NULL, NULL},
+ {0, "http://proxy.com:8080",
+ "http", "proxy.com", "8080", NULL, NULL},
+ /* issue #5530. Password contains @ */
+ {0, "http://example_user:example_pass_w_@_char@proxy.com:8080",
+ "http", "proxy.com", "8080", "example_user", "example_pass_w_@_char"},
+ {-1, "https://proxy.com:8080",
+ NULL, NULL, NULL, NULL, NULL}
+
+};
+
+void test_proxy_url_split() {
+ int i;
+ int ret;
+ int size;
+ char *protocol;
+ char *host;
+ char *port;
+ char *username;
+ char *password;
+ struct proxy_url_check *u;
+
+ size = sizeof(proxy_url_checks) / sizeof(struct proxy_url_check);
+ for (i = 0; i < size; i++) {
+ u = &proxy_url_checks[i];
+
+ protocol = NULL;
+ host = NULL;
+ port = NULL;
+ username = NULL;
+ password = NULL;
+
+ ret = flb_utils_proxy_url_split(u->url, &protocol, &username, &password, &host, &port);
+ TEST_CHECK(ret == u->ret);
+ if (ret == -1) {
+ continue;
+ }
+
+ /* Protocol */
+ TEST_CHECK(protocol != NULL);
+ ret = strcmp(u->prot, protocol);
+ TEST_CHECK(ret == 0);
+ TEST_MSG("Expected protocol: %s", u->prot);
+ TEST_MSG("Produced protocol: %s", protocol);
+
+ /* Host */
+ TEST_CHECK(host != NULL);
+ ret = strcmp(u->host, host);
+ TEST_CHECK(ret == 0);
+ TEST_MSG("Expected host: %s", u->host);
+ TEST_MSG("Produced host: %s", host);
+
+ /* Port */
+ TEST_CHECK(port != NULL);
+ ret = strcmp(u->port, port);
+ TEST_CHECK(ret == 0);
+ TEST_MSG("Expected port: %s", u->port);
+ TEST_MSG("Produced port: %s", port);
+
+ /* Username */
+ if (u->username) {
+ TEST_CHECK(port != NULL);
+ ret = strcmp(u->port, port);
+ TEST_CHECK(ret == 0);
+ TEST_MSG("Expected username: %s", u->username);
+ TEST_MSG("Produced username: %s", username);
+
+ }
+ else {
+ TEST_CHECK(username == NULL);
+ }
+
+ /* Password */
+ if (u->password) {
+ TEST_CHECK(port != NULL);
+ ret = strcmp(u->port, port);
+ TEST_CHECK(ret == 0);
+ TEST_MSG("Expected password: %s", u->password);
+ TEST_MSG("Produced password: %s", password);
+ }
+ else {
+ TEST_CHECK(password == NULL);
+ }
+
+ if (protocol) {
+ flb_free(protocol);
+ }
+ if (host) {
+ flb_free(host);
+ }
+ if (port) {
+ flb_free(port);
+ }
+ if (username) {
+ flb_free(username);
+ }
+ if (password) {
+ flb_free(password);
+ }
+ }
+}
+
+static int compare_split_entry(const char* input, int separator, int max_split, int quoted, ...)
+{
+ va_list ap;
+ int count = 1;
+ char *expect;
+ struct mk_list *split = NULL;
+ struct mk_list *tmp_list = NULL;
+ struct mk_list *head = NULL;
+ struct flb_split_entry *entry = NULL;
+
+ if (quoted) {
+ split = flb_utils_split_quoted(input, separator, max_split);
+ }
+ else {
+ split = flb_utils_split(input, separator, max_split);
+ }
+
+ if (!TEST_CHECK(split != NULL)) {
+ TEST_MSG("flb_utils_split failed. input=%s", input);
+ return -1;
+ }
+ if (!TEST_CHECK(mk_list_is_empty(split) != 0)) {
+ TEST_MSG("list is empty. input=%s", input);
+ return -1;
+ }
+
+ va_start(ap, quoted);
+ mk_list_foreach_safe(head, tmp_list, split) {
+ if (max_split > 0 && !TEST_CHECK(count <= max_split) ) {
+ TEST_MSG("count error. got=%d expect=%d input=%s", count, max_split, input);
+ }
+
+ expect = va_arg(ap, char*);
+ entry = mk_list_entry(head, struct flb_split_entry, _head);
+ if (!TEST_CHECK(entry != NULL)) {
+ TEST_MSG("entry is NULL. input=%s", input);
+ goto comp_end;
+ }
+ /*
+ printf("%d:%s\n", count, entry->value);
+ */
+ if (!TEST_CHECK(strcmp(expect, entry->value) == 0)) {
+ TEST_MSG("mismatch. got=%s expect=%s. input=%s", entry->value, expect, input);
+ goto comp_end;
+ }
+ count++;
+ }
+ comp_end:
+ if (split != NULL) {
+ flb_utils_split_free(split);
+ }
+ va_end(ap);
+ return 0;
+}
+
+void test_flb_utils_split()
+{
+ compare_split_entry("aa,bb", ',', 2, FLB_FALSE, "aa","bb" );
+ compare_split_entry("localhost:12345", ':', 2, FLB_FALSE, "localhost","12345" );
+ compare_split_entry("https://fluentbit.io/announcements/", '/', -1, FLB_FALSE, "https:", "fluentbit.io","announcements" );
+
+ /* /proc/net/dev example */
+ compare_split_entry("enp0s3: 1955136 1768 0 0 0 0 0 0 89362 931 0 0 0 0 0 0",
+ ' ', 256, FLB_FALSE,
+ "enp0s3:", "1955136", "1768", "0", "0", "0", "0", "0", "0", "89362", "931", "0", "0", "0", "0", "0", "0", "0");
+
+ /* filter_grep configuration */
+ compare_split_entry("Regex test *a*", ' ', 3, FLB_FALSE, "Regex", "test", "*a*");
+
+ /* filter_modify configuration */
+ compare_split_entry("Condition Key_Value_Does_Not_Equal cpustats KNOWN", ' ', 4,
+ FLB_FALSE, "Condition", "Key_Value_Does_Not_Equal", "cpustats", "KNOWN");
+
+ /* nginx_exporter_metrics example */
+ compare_split_entry("Active connections: 1\nserver accepts handled requests\n 10 10 10\nReading: 0 Writing: 1 Waiting: 0", '\n', 4,
+ FLB_FALSE, "Active connections: 1", "server accepts handled requests", " 10 10 10","Reading: 0 Writing: 1 Waiting: 0");
+
+ /* out_cloudwatch_logs example */
+ compare_split_entry("dimension_1,dimension_2;dimension_3", ';', 256,
+ FLB_FALSE, "dimension_1,dimension_2", "dimension_3");
+ /* separator is not contained */
+ compare_split_entry("aa,bb", '/', 2, FLB_FALSE, "aa,bb");
+
+ /* do not parse quotes when tokenizing */
+ compare_split_entry("aa \"bb cc\" dd", ' ', 256, FLB_FALSE, "aa", "\"bb", "cc\"", "dd");
+}
+
+void test_flb_utils_split_quoted()
+{
+ /* Tokens quoted with "..." */
+ compare_split_entry("aa \"double quote\" bb", ' ', 256, FLB_TRUE, "aa", "double quote", "bb");
+ compare_split_entry("\"begin with double quote\" aa", ' ', 256, FLB_TRUE, "begin with double quote", "aa");
+ compare_split_entry("aa \"end with double quote\"", ' ', 256, FLB_TRUE, "aa", "end with double quote");
+
+ /* Tokens quoted with '...' */
+ compare_split_entry("aa bb 'single quote' cc", ' ', 256, FLB_TRUE, "aa", "bb", "single quote", "cc");
+ compare_split_entry("'begin with single quote' aa", ' ', 256, FLB_TRUE, "begin with single quote", "aa");
+ compare_split_entry("aa 'end with single quote'", ' ', 256, FLB_TRUE, "aa", "end with single quote");
+
+ /* Tokens surrounded by more than one separator character */
+ compare_split_entry(" aa \" spaces bb \" cc ' spaces dd ' ff", ' ', 256, FLB_TRUE,
+ "aa", " spaces bb ", "cc", " spaces dd ", "ff");
+
+ /* Escapes within quoted token */
+ compare_split_entry("aa \"escaped \\\" quote\" bb", ' ', 256, FLB_TRUE, "aa", "escaped \" quote", "bb");
+ compare_split_entry("aa 'escaped \\' quote\' bb", ' ', 256, FLB_TRUE, "aa", "escaped \' quote", "bb");
+ compare_split_entry("aa \"\\\"escaped balanced quotes\\\"\" bb", ' ', 256, FLB_TRUE,
+ "aa", "\"escaped balanced quotes\"", "bb");
+ compare_split_entry("aa '\\'escaped balanced quotes\\'\' bb", ' ', 256, FLB_TRUE,
+ "aa", "'escaped balanced quotes'", "bb");
+ compare_split_entry("aa 'escaped \\\\ escape\' bb", ' ', 256, FLB_TRUE, "aa", "escaped \\ escape", "bb");
+
+ /* Escapes that are not processed */
+ compare_split_entry("\\\"aa bb", ' ', 256, FLB_TRUE, "\\\"aa", "bb");
+ compare_split_entry("\\'aa bb", ' ', 256, FLB_TRUE, "\\'aa", "bb");
+ compare_split_entry("\\\\aa bb", ' ', 256, FLB_TRUE, "\\\\aa", "bb");
+ compare_split_entry("aa\\ bb", ' ', 256, FLB_TRUE, "aa\\", "bb");
+
+}
+
+void test_flb_utils_split_quoted_errors()
+{
+ struct mk_list *split = NULL;
+
+ split = flb_utils_split_quoted("aa \"unbalanced quotes should fail", ' ', 256);
+ TEST_CHECK(split == NULL);
+ split = flb_utils_split_quoted("aa 'unbalanced quotes should fail", ' ', 256);
+ TEST_CHECK(split == NULL);
+}
+
+TEST_LIST = {
+ /* JSON maps iteration */
+ { "url_split", test_url_split },
+ { "write_str", test_write_str },
+ { "test_write_str_invalid_trailing_bytes", test_write_str_invalid_trailing_bytes },
+ { "test_write_str_invalid_leading_byte", test_write_str_invalid_leading_byte },
+ { "test_write_str_edge_cases", test_write_str_edge_cases },
+ { "test_write_str_invalid_leading_byte_case_2", test_write_str_invalid_leading_byte_case_2 },
+ { "test_write_str_buffer_overrun", test_write_str_buffer_overrun },
+ { "proxy_url_split", test_proxy_url_split },
+ { "test_flb_utils_split", test_flb_utils_split },
+ { "test_flb_utils_split_quoted", test_flb_utils_split_quoted},
+ { "test_flb_utils_split_quoted_errors", test_flb_utils_split_quoted_errors},
+ { 0 }
+};
diff --git a/fluent-bit/tests/lib/acutest/README.md b/fluent-bit/tests/lib/acutest/README.md
new file mode 100644
index 00000000..e7f75b65
--- /dev/null
+++ b/fluent-bit/tests/lib/acutest/README.md
@@ -0,0 +1,4 @@
+Taken from https://github.com/mity/acutest
+
+MIT License
+
diff --git a/fluent-bit/tests/lib/acutest/acutest.h b/fluent-bit/tests/lib/acutest/acutest.h
new file mode 100644
index 00000000..20500ae5
--- /dev/null
+++ b/fluent-bit/tests/lib/acutest/acutest.h
@@ -0,0 +1,1839 @@
+/*
+ * Acutest -- Another C/C++ Unit Test facility
+ * <https://github.com/mity/acutest>
+ *
+ * Copyright 2013-2020 Martin Mitas
+ * Copyright 2019 Garrett D'Amore
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#ifndef ACUTEST_H
+#define ACUTEST_H
+
+
+/************************
+ *** Public interface ***
+ ************************/
+
+/* By default, "acutest.h" provides the main program entry point (function
+ * main()). However, if the test suite is composed of multiple source files
+ * which include "acutest.h", then this causes a problem of multiple main()
+ * definitions. To avoid this problem, #define macro TEST_NO_MAIN in all
+ * compilation units but one.
+ */
+
+/* Macro to specify list of unit tests in the suite.
+ * The unit test implementation MUST provide list of unit tests it implements
+ * with this macro:
+ *
+ * TEST_LIST = {
+ * { "test1_name", test1_func_ptr },
+ * { "test2_name", test2_func_ptr },
+ * ...
+ * { NULL, NULL } // zeroed record marking the end of the list
+ * };
+ *
+ * The list specifies names of each test (must be unique) and pointer to
+ * a function implementing it. The function does not take any arguments
+ * and has no return values, i.e. every test function has to be compatible
+ * with this prototype:
+ *
+ * void test_func(void);
+ *
+ * Note the list has to be ended with a zeroed record.
+ */
+#define TEST_LIST const struct acutest_test_ acutest_list_[]
+
+
+/* Macros for testing whether an unit test succeeds or fails. These macros
+ * can be used arbitrarily in functions implementing the unit tests.
+ *
+ * If any condition fails throughout execution of a test, the test fails.
+ *
+ * TEST_CHECK takes only one argument (the condition), TEST_CHECK_ allows
+ * also to specify an error message to print out if the condition fails.
+ * (It expects printf-like format string and its parameters). The macros
+ * return non-zero (condition passes) or 0 (condition fails).
+ *
+ * That can be useful when more conditions should be checked only if some
+ * preceding condition passes, as illustrated in this code snippet:
+ *
+ * SomeStruct* ptr = allocate_some_struct();
+ * if(TEST_CHECK(ptr != NULL)) {
+ * TEST_CHECK(ptr->member1 < 100);
+ * TEST_CHECK(ptr->member2 > 200);
+ * }
+ */
+#define TEST_CHECK_(cond,...) acutest_check_((cond), __FILE__, __LINE__, __VA_ARGS__)
+#define TEST_CHECK(cond) acutest_check_((cond), __FILE__, __LINE__, "%s", #cond)
+
+
+/* These macros are the same as TEST_CHECK_ and TEST_CHECK except that if the
+ * condition fails, the currently executed unit test is immediately aborted.
+ *
+ * That is done either by calling abort() if the unit test is executed as a
+ * child process; or via longjmp() if the unit test is executed within the
+ * main Acutest process.
+ *
+ * As a side effect of such abortion, your unit tests may cause memory leaks,
+ * unflushed file descriptors, and other phenomena caused by the abortion.
+ *
+ * Therefore you should not use these as a general replacement for TEST_CHECK.
+ * Use it with some caution, especially if your test causes some other side
+ * effects to the outside world (e.g. communicating with some server, inserting
+ * into a database etc.).
+ */
+#define TEST_ASSERT_(cond,...) \
+ do { \
+ if(!acutest_check_((cond), __FILE__, __LINE__, __VA_ARGS__)) \
+ acutest_abort_(); \
+ } while(0)
+#define TEST_ASSERT(cond) \
+ do { \
+ if(!acutest_check_((cond), __FILE__, __LINE__, "%s", #cond)) \
+ acutest_abort_(); \
+ } while(0)
+
+
+#ifdef __cplusplus
+/* Macros to verify that the code (the 1st argument) throws exception of given
+ * type (the 2nd argument). (Note these macros are only available in C++.)
+ *
+ * TEST_EXCEPTION_ is like TEST_EXCEPTION but accepts custom printf-like
+ * message.
+ *
+ * For example:
+ *
+ * TEST_EXCEPTION(function_that_throw(), ExpectedExceptionType);
+ *
+ * If the function_that_throw() throws ExpectedExceptionType, the check passes.
+ * If the function throws anything incompatible with ExpectedExceptionType
+ * (or if it does not thrown an exception at all), the check fails.
+ */
+#define TEST_EXCEPTION(code, exctype) \
+ do { \
+ bool exc_ok_ = false; \
+ const char *msg_ = NULL; \
+ try { \
+ code; \
+ msg_ = "No exception thrown."; \
+ } catch(exctype const&) { \
+ exc_ok_= true; \
+ } catch(...) { \
+ msg_ = "Unexpected exception thrown."; \
+ } \
+ acutest_check_(exc_ok_, __FILE__, __LINE__, #code " throws " #exctype);\
+ if(msg_ != NULL) \
+ acutest_message_("%s", msg_); \
+ } while(0)
+#define TEST_EXCEPTION_(code, exctype, ...) \
+ do { \
+ bool exc_ok_ = false; \
+ const char *msg_ = NULL; \
+ try { \
+ code; \
+ msg_ = "No exception thrown."; \
+ } catch(exctype const&) { \
+ exc_ok_= true; \
+ } catch(...) { \
+ msg_ = "Unexpected exception thrown."; \
+ } \
+ acutest_check_(exc_ok_, __FILE__, __LINE__, __VA_ARGS__); \
+ if(msg_ != NULL) \
+ acutest_message_("%s", msg_); \
+ } while(0)
+#endif /* #ifdef __cplusplus */
+
+
+/* Sometimes it is useful to split execution of more complex unit tests to some
+ * smaller parts and associate those parts with some names.
+ *
+ * This is especially handy if the given unit test is implemented as a loop
+ * over some vector of multiple testing inputs. Using these macros allow to use
+ * sort of subtitle for each iteration of the loop (e.g. outputting the input
+ * itself or a name associated to it), so that if any TEST_CHECK condition
+ * fails in the loop, it can be easily seen which iteration triggers the
+ * failure, without the need to manually output the iteration-specific data in
+ * every single TEST_CHECK inside the loop body.
+ *
+ * TEST_CASE allows to specify only single string as the name of the case,
+ * TEST_CASE_ provides all the power of printf-like string formatting.
+ *
+ * Note that the test cases cannot be nested. Starting a new test case ends
+ * implicitly the previous one. To end the test case explicitly (e.g. to end
+ * the last test case after exiting the loop), you may use TEST_CASE(NULL).
+ */
+#define TEST_CASE_(...) acutest_case_(__VA_ARGS__)
+#define TEST_CASE(name) acutest_case_("%s", name)
+
+
+/* Maximal output per TEST_CASE call. Longer messages are cut.
+ * You may define another limit prior including "acutest.h"
+ */
+#ifndef TEST_CASE_MAXSIZE
+ #define TEST_CASE_MAXSIZE 64
+#endif
+
+
+/* printf-like macro for outputting an extra information about a failure.
+ *
+ * Intended use is to output some computed output versus the expected value,
+ * e.g. like this:
+ *
+ * if(!TEST_CHECK(produced == expected)) {
+ * TEST_MSG("Expected: %d", expected);
+ * TEST_MSG("Produced: %d", produced);
+ * }
+ *
+ * Note the message is only written down if the most recent use of any checking
+ * macro (like e.g. TEST_CHECK or TEST_EXCEPTION) in the current test failed.
+ * This means the above is equivalent to just this:
+ *
+ * TEST_CHECK(produced == expected);
+ * TEST_MSG("Expected: %d", expected);
+ * TEST_MSG("Produced: %d", produced);
+ *
+ * The macro can deal with multi-line output fairly well. It also automatically
+ * adds a final new-line if there is none present.
+ */
+#define TEST_MSG(...) acutest_message_(__VA_ARGS__)
+
+
+/* Maximal output per TEST_MSG call. Longer messages are cut.
+ * You may define another limit prior including "acutest.h"
+ */
+#ifndef TEST_MSG_MAXSIZE
+ #define TEST_MSG_MAXSIZE 1024
+#endif
+
+
+/* Macro for dumping a block of memory.
+ *
+ * Its intended use is very similar to what TEST_MSG is for, but instead of
+ * generating any printf-like message, this is for dumping raw block of a
+ * memory in a hexadecimal form:
+ *
+ * TEST_CHECK(size_produced == size_expected &&
+ * memcmp(addr_produced, addr_expected, size_produced) == 0);
+ * TEST_DUMP("Expected:", addr_expected, size_expected);
+ * TEST_DUMP("Produced:", addr_produced, size_produced);
+ */
+#define TEST_DUMP(title, addr, size) acutest_dump_(title, addr, size)
+
+/* Maximal output per TEST_DUMP call (in bytes to dump). Longer blocks are cut.
+ * You may define another limit prior including "acutest.h"
+ */
+#ifndef TEST_DUMP_MAXSIZE
+ #define TEST_DUMP_MAXSIZE 1024
+#endif
+
+
+/* Common test initialiation/clean-up
+ *
+ * In some test suites, it may be needed to perform some sort of the same
+ * initialization and/or clean-up in all the tests.
+ *
+ * Such test suites may use macros TEST_INIT and/or TEST_FINI prior including
+ * this header. The expansion of the macro is then used as a body of helper
+ * function called just before executing every single (TEST_INIT) or just after
+ * it ends (TEST_FINI).
+ *
+ * Examples of various ways how to use the macro TEST_INIT:
+ *
+ * #define TEST_INIT my_init_func();
+ * #define TEST_INIT my_init_func() // Works even without the semicolon
+ * #define TEST_INIT setlocale(LC_ALL, NULL);
+ * #define TEST_INIT { setlocale(LC_ALL, NULL); my_init_func(); }
+ *
+ * TEST_FINI is to be used in the same way.
+ */
+
+
+/**********************
+ *** Implementation ***
+ **********************/
+
+/* The unit test files should not rely on anything below. */
+
+#include <ctype.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <setjmp.h>
+
+#if defined(unix) || defined(__unix__) || defined(__unix) || defined(__APPLE__)
+ #define ACUTEST_UNIX_ 1
+ #include <errno.h>
+ #include <libgen.h>
+ #include <unistd.h>
+ #include <sys/types.h>
+ #include <sys/wait.h>
+ #include <signal.h>
+ #include <time.h>
+
+ #if defined CLOCK_PROCESS_CPUTIME_ID && defined CLOCK_MONOTONIC
+ #define ACUTEST_HAS_POSIX_TIMER_ 1
+ #endif
+#endif
+
+#if defined(_gnu_linux_) || defined(__linux__)
+ #define ACUTEST_LINUX_ 1
+ #include <fcntl.h>
+ #include <sys/stat.h>
+#endif
+
+#if defined(_WIN32) || defined(__WIN32__) || defined(__WINDOWS__)
+ #define ACUTEST_WIN_ 1
+ #include <windows.h>
+ #include <io.h>
+#endif
+
+#if defined(__APPLE__)
+ #define ACUTEST_MACOS_
+ #include <assert.h>
+ #include <stdbool.h>
+ #include <sys/types.h>
+ #include <unistd.h>
+ #include <sys/sysctl.h>
+#endif
+
+#ifdef __cplusplus
+ #include <exception>
+#endif
+
+#ifdef __has_include
+ #if __has_include(<valgrind.h>)
+ #include <valgrind.h>
+ #endif
+#endif
+
+/* Enable the use of the non-standard keyword __attribute__ to silence warnings under some compilers */
+#if defined(__GNUC__) || defined(__clang__)
+ #define ACUTEST_ATTRIBUTE_(attr) __attribute__((attr))
+#else
+ #define ACUTEST_ATTRIBUTE_(attr)
+#endif
+
+/* Note our global private identifiers end with '_' to mitigate risk of clash
+ * with the unit tests implementation. */
+
+#ifdef __cplusplus
+ extern "C" {
+#endif
+
+#ifdef _MSC_VER
+ /* In the multi-platform code like ours, we cannot use the non-standard
+ * "safe" functions from Microsoft C lib like e.g. sprintf_s() instead of
+ * standard sprintf(). Hence, lets disable the warning C4996. */
+ #pragma warning(push)
+ #pragma warning(disable: 4996)
+#endif
+
+
+struct acutest_test_ {
+ const char* name;
+ void (*func)(void);
+};
+
+struct acutest_test_data_ {
+ unsigned char flags;
+ double duration;
+};
+
+enum {
+ ACUTEST_FLAG_RUN_ = 1 << 0,
+ ACUTEST_FLAG_SUCCESS_ = 1 << 1,
+ ACUTEST_FLAG_FAILURE_ = 1 << 2,
+};
+
+extern const struct acutest_test_ acutest_list_[];
+
+int acutest_check_(int cond, const char* file, int line, const char* fmt, ...);
+void acutest_case_(const char* fmt, ...);
+void acutest_message_(const char* fmt, ...);
+void acutest_dump_(const char* title, const void* addr, size_t size);
+void acutest_abort_(void) ACUTEST_ATTRIBUTE_(noreturn);
+
+
+#ifndef TEST_NO_MAIN
+
+static char* acutest_argv0_ = NULL;
+static size_t acutest_list_size_ = 0;
+static struct acutest_test_data_* acutest_test_data_ = NULL;
+static size_t acutest_count_ = 0;
+static int acutest_no_exec_ = -1;
+static int acutest_no_summary_ = 0;
+static int acutest_tap_ = 0;
+static int acutest_skip_mode_ = 0;
+static int acutest_worker_ = 0;
+static int acutest_worker_index_ = 0;
+static int acutest_cond_failed_ = 0;
+static int acutest_was_aborted_ = 0;
+static FILE *acutest_xml_output_ = NULL;
+
+static int acutest_stat_failed_units_ = 0;
+static int acutest_stat_run_units_ = 0;
+
+static const struct acutest_test_* acutest_current_test_ = NULL;
+static int acutest_current_index_ = 0;
+static char acutest_case_name_[TEST_CASE_MAXSIZE] = "";
+static int acutest_test_already_logged_ = 0;
+static int acutest_case_already_logged_ = 0;
+static int acutest_verbose_level_ = 2;
+static int acutest_test_failures_ = 0;
+static int acutest_colorize_ = 0;
+static int acutest_timer_ = 0;
+
+static int acutest_abort_has_jmp_buf_ = 0;
+static jmp_buf acutest_abort_jmp_buf_;
+
+
+static void
+acutest_cleanup_(void)
+{
+ free((void*) acutest_test_data_);
+}
+
+static void ACUTEST_ATTRIBUTE_(noreturn)
+acutest_exit_(int exit_code)
+{
+ acutest_cleanup_();
+ exit(exit_code);
+}
+
+#if defined ACUTEST_WIN_
+ typedef LARGE_INTEGER acutest_timer_type_;
+ static LARGE_INTEGER acutest_timer_freq_;
+ static acutest_timer_type_ acutest_timer_start_;
+ static acutest_timer_type_ acutest_timer_end_;
+
+ static void
+ acutest_timer_init_(void)
+ {
+ QueryPerformanceFrequency(&acutest_timer_freq_);
+ }
+
+ static void
+ acutest_timer_get_time_(LARGE_INTEGER* ts)
+ {
+ QueryPerformanceCounter(ts);
+ }
+
+ static double
+ acutest_timer_diff_(LARGE_INTEGER start, LARGE_INTEGER end)
+ {
+ double duration = (double)(end.QuadPart - start.QuadPart);
+ duration /= (double)acutest_timer_freq_.QuadPart;
+ return duration;
+ }
+
+ static void
+ acutest_timer_print_diff_(void)
+ {
+ printf("%.6lf secs", acutest_timer_diff_(acutest_timer_start_, acutest_timer_end_));
+ }
+#elif defined ACUTEST_HAS_POSIX_TIMER_
+ static clockid_t acutest_timer_id_;
+ typedef struct timespec acutest_timer_type_;
+ static acutest_timer_type_ acutest_timer_start_;
+ static acutest_timer_type_ acutest_timer_end_;
+
+ static void
+ acutest_timer_init_(void)
+ {
+ if(acutest_timer_ == 1)
+ acutest_timer_id_ = CLOCK_MONOTONIC;
+ else if(acutest_timer_ == 2)
+ acutest_timer_id_ = CLOCK_PROCESS_CPUTIME_ID;
+ }
+
+ static void
+ acutest_timer_get_time_(struct timespec* ts)
+ {
+ clock_gettime(acutest_timer_id_, ts);
+ }
+
+ static double
+ acutest_timer_diff_(struct timespec start, struct timespec end)
+ {
+ double endns;
+ double startns;
+
+ endns = end.tv_sec;
+ endns *= 1e9;
+ endns += end.tv_nsec;
+
+ startns = start.tv_sec;
+ startns *= 1e9;
+ startns += start.tv_nsec;
+
+ return ((endns - startns)/ 1e9);
+ }
+
+ static void
+ acutest_timer_print_diff_(void)
+ {
+ printf("%.6lf secs",
+ acutest_timer_diff_(acutest_timer_start_, acutest_timer_end_));
+ }
+#else
+ typedef int acutest_timer_type_;
+ static acutest_timer_type_ acutest_timer_start_;
+ static acutest_timer_type_ acutest_timer_end_;
+
+ void
+ acutest_timer_init_(void)
+ {}
+
+ static void
+ acutest_timer_get_time_(int* ts)
+ {
+ (void) ts;
+ }
+
+ static double
+ acutest_timer_diff_(int start, int end)
+ {
+ (void) start;
+ (void) end;
+ return 0.0;
+ }
+
+ static void
+ acutest_timer_print_diff_(void)
+ {}
+#endif
+
+#define ACUTEST_COLOR_DEFAULT_ 0
+#define ACUTEST_COLOR_GREEN_ 1
+#define ACUTEST_COLOR_RED_ 2
+#define ACUTEST_COLOR_DEFAULT_INTENSIVE_ 3
+#define ACUTEST_COLOR_GREEN_INTENSIVE_ 4
+#define ACUTEST_COLOR_RED_INTENSIVE_ 5
+
+static int ACUTEST_ATTRIBUTE_(format (printf, 2, 3))
+acutest_colored_printf_(int color, const char* fmt, ...)
+{
+ va_list args;
+ char buffer[256];
+ int n;
+
+ va_start(args, fmt);
+ vsnprintf(buffer, sizeof(buffer), fmt, args);
+ va_end(args);
+ buffer[sizeof(buffer)-1] = '\0';
+
+ if(!acutest_colorize_) {
+ return printf("%s", buffer);
+ }
+
+#if defined ACUTEST_UNIX_
+ {
+ const char* col_str;
+ switch(color) {
+ case ACUTEST_COLOR_GREEN_: col_str = "\033[0;32m"; break;
+ case ACUTEST_COLOR_RED_: col_str = "\033[0;31m"; break;
+ case ACUTEST_COLOR_GREEN_INTENSIVE_: col_str = "\033[1;32m"; break;
+ case ACUTEST_COLOR_RED_INTENSIVE_: col_str = "\033[1;31m"; break;
+ case ACUTEST_COLOR_DEFAULT_INTENSIVE_: col_str = "\033[1m"; break;
+ default: col_str = "\033[0m"; break;
+ }
+ printf("%s", col_str);
+ n = printf("%s", buffer);
+ printf("\033[0m");
+ return n;
+ }
+#elif defined ACUTEST_WIN_
+ {
+ HANDLE h;
+ CONSOLE_SCREEN_BUFFER_INFO info;
+ WORD attr;
+
+ h = GetStdHandle(STD_OUTPUT_HANDLE);
+ GetConsoleScreenBufferInfo(h, &info);
+
+ switch(color) {
+ case ACUTEST_COLOR_GREEN_: attr = FOREGROUND_GREEN; break;
+ case ACUTEST_COLOR_RED_: attr = FOREGROUND_RED; break;
+ case ACUTEST_COLOR_GREEN_INTENSIVE_: attr = FOREGROUND_GREEN | FOREGROUND_INTENSITY; break;
+ case ACUTEST_COLOR_RED_INTENSIVE_: attr = FOREGROUND_RED | FOREGROUND_INTENSITY; break;
+ case ACUTEST_COLOR_DEFAULT_INTENSIVE_: attr = FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_INTENSITY; break;
+ default: attr = 0; break;
+ }
+ if(attr != 0)
+ SetConsoleTextAttribute(h, attr);
+ n = printf("%s", buffer);
+ SetConsoleTextAttribute(h, info.wAttributes);
+ return n;
+ }
+#else
+ n = printf("%s", buffer);
+ return n;
+#endif
+}
+
+static void
+acutest_begin_test_line_(const struct acutest_test_* test)
+{
+ if(!acutest_tap_) {
+ if(acutest_verbose_level_ >= 3) {
+ acutest_colored_printf_(ACUTEST_COLOR_DEFAULT_INTENSIVE_, "Test %s:\n", test->name);
+ acutest_test_already_logged_++;
+ } else if(acutest_verbose_level_ >= 1) {
+ int n;
+ char spaces[48];
+
+ n = acutest_colored_printf_(ACUTEST_COLOR_DEFAULT_INTENSIVE_, "Test %s... ", test->name);
+ memset(spaces, ' ', sizeof(spaces));
+ if(n < (int) sizeof(spaces))
+ printf("%.*s", (int) sizeof(spaces) - n, spaces);
+ } else {
+ acutest_test_already_logged_ = 1;
+ }
+ }
+}
+
+static void
+acutest_finish_test_line_(int result)
+{
+ if(acutest_tap_) {
+ const char* str = (result == 0) ? "ok" : "not ok";
+
+ printf("%s %d - %s\n", str, acutest_current_index_ + 1, acutest_current_test_->name);
+
+ if(result == 0 && acutest_timer_) {
+ printf("# Duration: ");
+ acutest_timer_print_diff_();
+ printf("\n");
+ }
+ } else {
+ int color = (result == 0) ? ACUTEST_COLOR_GREEN_INTENSIVE_ : ACUTEST_COLOR_RED_INTENSIVE_;
+ const char* str = (result == 0) ? "OK" : "FAILED";
+ printf("[ ");
+ acutest_colored_printf_(color, "%s", str);
+ printf(" ]");
+
+ if(result == 0 && acutest_timer_) {
+ printf(" ");
+ acutest_timer_print_diff_();
+ }
+
+ printf("\n");
+ }
+}
+
+static void
+acutest_line_indent_(int level)
+{
+ static const char spaces[] = " ";
+ int n = level * 2;
+
+ if(acutest_tap_ && n > 0) {
+ n--;
+ printf("#");
+ }
+
+ while(n > 16) {
+ printf("%s", spaces);
+ n -= 16;
+ }
+ printf("%.*s", n, spaces);
+}
+
+int ACUTEST_ATTRIBUTE_(format (printf, 4, 5))
+acutest_check_(int cond, const char* file, int line, const char* fmt, ...)
+{
+ const char *result_str;
+ int result_color;
+ int verbose_level;
+
+ if(cond) {
+ result_str = "ok";
+ result_color = ACUTEST_COLOR_GREEN_;
+ verbose_level = 3;
+ } else {
+ if(!acutest_test_already_logged_ && acutest_current_test_ != NULL)
+ acutest_finish_test_line_(-1);
+
+ result_str = "failed";
+ result_color = ACUTEST_COLOR_RED_;
+ verbose_level = 2;
+ acutest_test_failures_++;
+ acutest_test_already_logged_++;
+ }
+
+ if(acutest_verbose_level_ >= verbose_level) {
+ va_list args;
+
+ if(!acutest_case_already_logged_ && acutest_case_name_[0]) {
+ acutest_line_indent_(1);
+ acutest_colored_printf_(ACUTEST_COLOR_DEFAULT_INTENSIVE_, "Case %s:\n", acutest_case_name_);
+ acutest_test_already_logged_++;
+ acutest_case_already_logged_++;
+ }
+
+ acutest_line_indent_(acutest_case_name_[0] ? 2 : 1);
+ if(file != NULL) {
+#ifdef ACUTEST_WIN_
+ const char* lastsep1 = strrchr(file, '\\');
+ const char* lastsep2 = strrchr(file, '/');
+ if(lastsep1 == NULL)
+ lastsep1 = file-1;
+ if(lastsep2 == NULL)
+ lastsep2 = file-1;
+ file = (lastsep1 > lastsep2 ? lastsep1 : lastsep2) + 1;
+#else
+ const char* lastsep = strrchr(file, '/');
+ if(lastsep != NULL)
+ file = lastsep+1;
+#endif
+ printf("%s:%d: Check ", file, line);
+ }
+
+ va_start(args, fmt);
+ vprintf(fmt, args);
+ va_end(args);
+
+ printf("... ");
+ acutest_colored_printf_(result_color, "%s", result_str);
+ printf("\n");
+ acutest_test_already_logged_++;
+ }
+
+ acutest_cond_failed_ = (cond == 0);
+ return !acutest_cond_failed_;
+}
+
+void ACUTEST_ATTRIBUTE_(format (printf, 1, 2))
+acutest_case_(const char* fmt, ...)
+{
+ va_list args;
+
+ if(acutest_verbose_level_ < 2)
+ return;
+
+ if(acutest_case_name_[0]) {
+ acutest_case_already_logged_ = 0;
+ acutest_case_name_[0] = '\0';
+ }
+
+ if(fmt == NULL)
+ return;
+
+ va_start(args, fmt);
+ vsnprintf(acutest_case_name_, sizeof(acutest_case_name_) - 1, fmt, args);
+ va_end(args);
+ acutest_case_name_[sizeof(acutest_case_name_) - 1] = '\0';
+
+ if(acutest_verbose_level_ >= 3) {
+ acutest_line_indent_(1);
+ acutest_colored_printf_(ACUTEST_COLOR_DEFAULT_INTENSIVE_, "Case %s:\n", acutest_case_name_);
+ acutest_test_already_logged_++;
+ acutest_case_already_logged_++;
+ }
+}
+
+void ACUTEST_ATTRIBUTE_(format (printf, 1, 2))
+acutest_message_(const char* fmt, ...)
+{
+ char buffer[TEST_MSG_MAXSIZE];
+ char* line_beg;
+ char* line_end;
+ va_list args;
+
+ if(acutest_verbose_level_ < 2)
+ return;
+
+ /* We allow extra message only when something is already wrong in the
+ * current test. */
+ if(acutest_current_test_ == NULL || !acutest_cond_failed_)
+ return;
+
+ va_start(args, fmt);
+ vsnprintf(buffer, TEST_MSG_MAXSIZE, fmt, args);
+ va_end(args);
+ buffer[TEST_MSG_MAXSIZE-1] = '\0';
+
+ line_beg = buffer;
+ while(1) {
+ line_end = strchr(line_beg, '\n');
+ if(line_end == NULL)
+ break;
+ acutest_line_indent_(acutest_case_name_[0] ? 3 : 2);
+ printf("%.*s\n", (int)(line_end - line_beg), line_beg);
+ line_beg = line_end + 1;
+ }
+ if(line_beg[0] != '\0') {
+ acutest_line_indent_(acutest_case_name_[0] ? 3 : 2);
+ printf("%s\n", line_beg);
+ }
+}
+
+void
+acutest_dump_(const char* title, const void* addr, size_t size)
+{
+ static const size_t BYTES_PER_LINE = 16;
+ size_t line_beg;
+ size_t truncate = 0;
+
+ if(acutest_verbose_level_ < 2)
+ return;
+
+ /* We allow extra message only when something is already wrong in the
+ * current test. */
+ if(acutest_current_test_ == NULL || !acutest_cond_failed_)
+ return;
+
+ if(size > TEST_DUMP_MAXSIZE) {
+ truncate = size - TEST_DUMP_MAXSIZE;
+ size = TEST_DUMP_MAXSIZE;
+ }
+
+ acutest_line_indent_(acutest_case_name_[0] ? 3 : 2);
+ printf((title[strlen(title)-1] == ':') ? "%s\n" : "%s:\n", title);
+
+ for(line_beg = 0; line_beg < size; line_beg += BYTES_PER_LINE) {
+ size_t line_end = line_beg + BYTES_PER_LINE;
+ size_t off;
+
+ acutest_line_indent_(acutest_case_name_[0] ? 4 : 3);
+ printf("%08lx: ", (unsigned long)line_beg);
+ for(off = line_beg; off < line_end; off++) {
+ if(off < size)
+ printf(" %02x", ((const unsigned char*)addr)[off]);
+ else
+ printf(" ");
+ }
+
+ printf(" ");
+ for(off = line_beg; off < line_end; off++) {
+ unsigned char byte = ((const unsigned char*)addr)[off];
+ if(off < size)
+ printf("%c", (iscntrl(byte) ? '.' : byte));
+ else
+ break;
+ }
+
+ printf("\n");
+ }
+
+ if(truncate > 0) {
+ acutest_line_indent_(acutest_case_name_[0] ? 4 : 3);
+ printf(" ... (and more %u bytes)\n", (unsigned) truncate);
+ }
+}
+
+/* This is called just before each test */
+static void
+acutest_init_(const char *test_name)
+{
+#ifdef TEST_INIT
+ TEST_INIT
+ ; /* Allow for a single unterminated function call */
+#endif
+
+ /* Suppress any warnings about unused variable. */
+ (void) test_name;
+}
+
+/* This is called after each test */
+static void
+acutest_fini_(const char *test_name)
+{
+#ifdef TEST_FINI
+ TEST_FINI
+ ; /* Allow for a single unterminated function call */
+#endif
+
+ /* Suppress any warnings about unused variable. */
+ (void) test_name;
+}
+
+void
+acutest_abort_(void)
+{
+ if(acutest_abort_has_jmp_buf_) {
+ longjmp(acutest_abort_jmp_buf_, 1);
+ } else {
+ if(acutest_current_test_ != NULL)
+ acutest_fini_(acutest_current_test_->name);
+ abort();
+ }
+}
+
+static void
+acutest_list_names_(void)
+{
+ const struct acutest_test_* test;
+
+ printf("Unit tests:\n");
+ for(test = &acutest_list_[0]; test->func != NULL; test++)
+ printf(" %s\n", test->name);
+}
+
+static void
+acutest_remember_(int i)
+{
+ if(acutest_test_data_[i].flags & ACUTEST_FLAG_RUN_)
+ return;
+
+ acutest_test_data_[i].flags |= ACUTEST_FLAG_RUN_;
+ acutest_count_++;
+}
+
+static void
+acutest_set_success_(int i, int success)
+{
+ acutest_test_data_[i].flags |= success ? ACUTEST_FLAG_SUCCESS_ : ACUTEST_FLAG_FAILURE_;
+}
+
+static void
+acutest_set_duration_(int i, double duration)
+{
+ acutest_test_data_[i].duration = duration;
+}
+
+static int
+acutest_name_contains_word_(const char* name, const char* pattern)
+{
+ static const char word_delim[] = " \t-_/.,:;";
+ const char* substr;
+ size_t pattern_len;
+
+ pattern_len = strlen(pattern);
+
+ substr = strstr(name, pattern);
+ while(substr != NULL) {
+ int starts_on_word_boundary = (substr == name || strchr(word_delim, substr[-1]) != NULL);
+ int ends_on_word_boundary = (substr[pattern_len] == '\0' || strchr(word_delim, substr[pattern_len]) != NULL);
+
+ if(starts_on_word_boundary && ends_on_word_boundary)
+ return 1;
+
+ substr = strstr(substr+1, pattern);
+ }
+
+ return 0;
+}
+
+static int
+acutest_lookup_(const char* pattern)
+{
+ int i;
+ int n = 0;
+
+ /* Try exact match. */
+ for(i = 0; i < (int) acutest_list_size_; i++) {
+ if(strcmp(acutest_list_[i].name, pattern) == 0) {
+ acutest_remember_(i);
+ n++;
+ break;
+ }
+ }
+ if(n > 0)
+ return n;
+
+ /* Try word match. */
+ for(i = 0; i < (int) acutest_list_size_; i++) {
+ if(acutest_name_contains_word_(acutest_list_[i].name, pattern)) {
+ acutest_remember_(i);
+ n++;
+ }
+ }
+ if(n > 0)
+ return n;
+
+ /* Try relaxed match. */
+ for(i = 0; i < (int) acutest_list_size_; i++) {
+ if(strstr(acutest_list_[i].name, pattern) != NULL) {
+ acutest_remember_(i);
+ n++;
+ }
+ }
+
+ return n;
+}
+
+
+/* Called if anything goes bad in Acutest, or if the unit test ends in other
+ * way then by normal returning from its function (e.g. exception or some
+ * abnormal child process termination). */
+static void ACUTEST_ATTRIBUTE_(format (printf, 1, 2))
+acutest_error_(const char* fmt, ...)
+{
+ if(acutest_verbose_level_ == 0)
+ return;
+
+ if(acutest_verbose_level_ >= 2) {
+ va_list args;
+
+ acutest_line_indent_(1);
+ if(acutest_verbose_level_ >= 3)
+ acutest_colored_printf_(ACUTEST_COLOR_RED_INTENSIVE_, "ERROR: ");
+ va_start(args, fmt);
+ vprintf(fmt, args);
+ va_end(args);
+ printf("\n");
+ }
+
+ if(acutest_verbose_level_ >= 3) {
+ printf("\n");
+ }
+}
+
+/* Call directly the given test unit function. */
+static int
+acutest_do_run_(const struct acutest_test_* test, int index)
+{
+ int status = -1;
+
+ acutest_was_aborted_ = 0;
+ acutest_current_test_ = test;
+ acutest_current_index_ = index;
+ acutest_test_failures_ = 0;
+ acutest_test_already_logged_ = 0;
+ acutest_cond_failed_ = 0;
+
+#ifdef __cplusplus
+ try {
+#endif
+ acutest_init_(test->name);
+ acutest_begin_test_line_(test);
+
+ /* This is good to do in case the test unit crashes. */
+ fflush(stdout);
+ fflush(stderr);
+
+ if(!acutest_worker_) {
+ acutest_abort_has_jmp_buf_ = 1;
+ if(setjmp(acutest_abort_jmp_buf_) != 0) {
+ acutest_was_aborted_ = 1;
+ goto aborted;
+ }
+ }
+
+ acutest_timer_get_time_(&acutest_timer_start_);
+ test->func();
+aborted:
+ acutest_abort_has_jmp_buf_ = 0;
+ acutest_timer_get_time_(&acutest_timer_end_);
+
+ if(acutest_verbose_level_ >= 3) {
+ acutest_line_indent_(1);
+ if(acutest_test_failures_ == 0) {
+ acutest_colored_printf_(ACUTEST_COLOR_GREEN_INTENSIVE_, "SUCCESS: ");
+ printf("All conditions have passed.\n");
+
+ if(acutest_timer_) {
+ acutest_line_indent_(1);
+ printf("Duration: ");
+ acutest_timer_print_diff_();
+ printf("\n");
+ }
+ } else {
+ acutest_colored_printf_(ACUTEST_COLOR_RED_INTENSIVE_, "FAILED: ");
+ if(!acutest_was_aborted_) {
+ printf("%d condition%s %s failed.\n",
+ acutest_test_failures_,
+ (acutest_test_failures_ == 1) ? "" : "s",
+ (acutest_test_failures_ == 1) ? "has" : "have");
+ } else {
+ printf("Aborted.\n");
+ }
+ }
+ printf("\n");
+ } else if(acutest_verbose_level_ >= 1 && acutest_test_failures_ == 0) {
+ acutest_finish_test_line_(0);
+ }
+
+ status = (acutest_test_failures_ == 0) ? 0 : -1;
+
+#ifdef __cplusplus
+ } catch(std::exception& e) {
+ const char* what = e.what();
+ acutest_check_(0, NULL, 0, "Threw std::exception");
+ if(what != NULL)
+ acutest_message_("std::exception::what(): %s", what);
+
+ if(acutest_verbose_level_ >= 3) {
+ acutest_line_indent_(1);
+ acutest_colored_printf_(ACUTEST_COLOR_RED_INTENSIVE_, "FAILED: ");
+ printf("C++ exception.\n\n");
+ }
+ } catch(...) {
+ acutest_check_(0, NULL, 0, "Threw an exception");
+
+ if(acutest_verbose_level_ >= 3) {
+ acutest_line_indent_(1);
+ acutest_colored_printf_(ACUTEST_COLOR_RED_INTENSIVE_, "FAILED: ");
+ printf("C++ exception.\n\n");
+ }
+ }
+#endif
+
+ acutest_fini_(test->name);
+ acutest_case_(NULL);
+ acutest_current_test_ = NULL;
+
+ return status;
+}
+
+/* Trigger the unit test. If possible (and not suppressed) it starts a child
+ * process who calls acutest_do_run_(), otherwise it calls acutest_do_run_()
+ * directly. */
+static void
+acutest_run_(const struct acutest_test_* test, int index, int master_index)
+{
+ int failed = 1;
+ acutest_timer_type_ start, end;
+
+ acutest_current_test_ = test;
+ acutest_test_already_logged_ = 0;
+ acutest_timer_get_time_(&start);
+
+ if(!acutest_no_exec_) {
+
+#if defined(ACUTEST_UNIX_)
+
+ pid_t pid;
+ int exit_code;
+
+ /* Make sure the child starts with empty I/O buffers. */
+ fflush(stdout);
+ fflush(stderr);
+
+ pid = fork();
+ if(pid == (pid_t)-1) {
+ acutest_error_("Cannot fork. %s [%d]", strerror(errno), errno);
+ failed = 1;
+ } else if(pid == 0) {
+ /* Child: Do the test. */
+ acutest_worker_ = 1;
+ failed = (acutest_do_run_(test, index) != 0);
+ acutest_exit_(failed ? 1 : 0);
+ } else {
+ /* Parent: Wait until child terminates and analyze its exit code. */
+ waitpid(pid, &exit_code, 0);
+ if(WIFEXITED(exit_code)) {
+ switch(WEXITSTATUS(exit_code)) {
+ case 0: failed = 0; break; /* test has passed. */
+ case 1: /* noop */ break; /* "normal" failure. */
+ default: acutest_error_("Unexpected exit code [%d]", WEXITSTATUS(exit_code));
+ }
+ } else if(WIFSIGNALED(exit_code)) {
+ char tmp[32];
+ const char* signame;
+ switch(WTERMSIG(exit_code)) {
+ case SIGINT: signame = "SIGINT"; break;
+ case SIGHUP: signame = "SIGHUP"; break;
+ case SIGQUIT: signame = "SIGQUIT"; break;
+ case SIGABRT: signame = "SIGABRT"; break;
+ case SIGKILL: signame = "SIGKILL"; break;
+ case SIGSEGV: signame = "SIGSEGV"; break;
+ case SIGILL: signame = "SIGILL"; break;
+ case SIGTERM: signame = "SIGTERM"; break;
+ default: sprintf(tmp, "signal %d", WTERMSIG(exit_code)); signame = tmp; break;
+ }
+ acutest_error_("Test interrupted by %s.", signame);
+ } else {
+ acutest_error_("Test ended in an unexpected way [%d].", exit_code);
+ }
+ }
+
+#elif defined(ACUTEST_WIN_)
+
+ char buffer[512] = {0};
+ STARTUPINFOA startupInfo;
+ PROCESS_INFORMATION processInfo;
+ DWORD exitCode;
+
+ /* Windows has no fork(). So we propagate all info into the child
+ * through a command line arguments. */
+ _snprintf(buffer, sizeof(buffer)-1,
+ "%s --worker=%d %s --no-exec --no-summary %s --verbose=%d --color=%s -- \"%s\"",
+ acutest_argv0_, index, acutest_timer_ ? "--time" : "",
+ acutest_tap_ ? "--tap" : "", acutest_verbose_level_,
+ acutest_colorize_ ? "always" : "never",
+ test->name);
+ memset(&startupInfo, 0, sizeof(startupInfo));
+ startupInfo.cb = sizeof(STARTUPINFO);
+ if(CreateProcessA(NULL, buffer, NULL, NULL, FALSE, 0, NULL, NULL, &startupInfo, &processInfo)) {
+ WaitForSingleObject(processInfo.hProcess, INFINITE);
+ GetExitCodeProcess(processInfo.hProcess, &exitCode);
+ CloseHandle(processInfo.hThread);
+ CloseHandle(processInfo.hProcess);
+ failed = (exitCode != 0);
+ if(exitCode > 1) {
+ switch(exitCode) {
+ case 3: acutest_error_("Aborted."); break;
+ case 0xC0000005: acutest_error_("Access violation."); break;
+ default: acutest_error_("Test ended in an unexpected way [%lu].", exitCode); break;
+ }
+ }
+ } else {
+ acutest_error_("Cannot create unit test subprocess [%ld].", GetLastError());
+ failed = 1;
+ }
+
+#else
+
+ /* A platform where we don't know how to run child process. */
+ failed = (acutest_do_run_(test, index) != 0);
+
+#endif
+
+ } else {
+ /* Child processes suppressed through --no-exec. */
+ failed = (acutest_do_run_(test, index) != 0);
+ }
+ acutest_timer_get_time_(&end);
+
+ acutest_current_test_ = NULL;
+
+ acutest_stat_run_units_++;
+ if(failed)
+ acutest_stat_failed_units_++;
+
+ acutest_set_success_(master_index, !failed);
+ acutest_set_duration_(master_index, acutest_timer_diff_(start, end));
+}
+
+#if defined(ACUTEST_WIN_)
+/* Callback for SEH events. */
+static LONG CALLBACK
+acutest_seh_exception_filter_(EXCEPTION_POINTERS *ptrs)
+{
+ acutest_check_(0, NULL, 0, "Unhandled SEH exception");
+ acutest_message_("Exception code: 0x%08lx", ptrs->ExceptionRecord->ExceptionCode);
+ acutest_message_("Exception address: 0x%p", ptrs->ExceptionRecord->ExceptionAddress);
+
+ fflush(stdout);
+ fflush(stderr);
+
+ return EXCEPTION_EXECUTE_HANDLER;
+}
+#endif
+
+
+#define ACUTEST_CMDLINE_OPTFLAG_OPTIONALARG_ 0x0001
+#define ACUTEST_CMDLINE_OPTFLAG_REQUIREDARG_ 0x0002
+
+#define ACUTEST_CMDLINE_OPTID_NONE_ 0
+#define ACUTEST_CMDLINE_OPTID_UNKNOWN_ (-0x7fffffff + 0)
+#define ACUTEST_CMDLINE_OPTID_MISSINGARG_ (-0x7fffffff + 1)
+#define ACUTEST_CMDLINE_OPTID_BOGUSARG_ (-0x7fffffff + 2)
+
+typedef struct acutest_test_CMDLINE_OPTION_ {
+ char shortname;
+ const char* longname;
+ int id;
+ unsigned flags;
+} ACUTEST_CMDLINE_OPTION_;
+
+static int
+acutest_cmdline_handle_short_opt_group_(const ACUTEST_CMDLINE_OPTION_* options,
+ const char* arggroup,
+ int (*callback)(int /*optval*/, const char* /*arg*/))
+{
+ const ACUTEST_CMDLINE_OPTION_* opt;
+ int i;
+ int ret = 0;
+
+ for(i = 0; arggroup[i] != '\0'; i++) {
+ for(opt = options; opt->id != 0; opt++) {
+ if(arggroup[i] == opt->shortname)
+ break;
+ }
+
+ if(opt->id != 0 && !(opt->flags & ACUTEST_CMDLINE_OPTFLAG_REQUIREDARG_)) {
+ ret = callback(opt->id, NULL);
+ } else {
+ /* Unknown option. */
+ char badoptname[3];
+ badoptname[0] = '-';
+ badoptname[1] = arggroup[i];
+ badoptname[2] = '\0';
+ ret = callback((opt->id != 0 ? ACUTEST_CMDLINE_OPTID_MISSINGARG_ : ACUTEST_CMDLINE_OPTID_UNKNOWN_),
+ badoptname);
+ }
+
+ if(ret != 0)
+ break;
+ }
+
+ return ret;
+}
+
+#define ACUTEST_CMDLINE_AUXBUF_SIZE_ 32
+
+static int
+acutest_cmdline_read_(const ACUTEST_CMDLINE_OPTION_* options, int argc, char** argv,
+ int (*callback)(int /*optval*/, const char* /*arg*/))
+{
+
+ const ACUTEST_CMDLINE_OPTION_* opt;
+ char auxbuf[ACUTEST_CMDLINE_AUXBUF_SIZE_+1];
+ int after_doubledash = 0;
+ int i = 1;
+ int ret = 0;
+
+ auxbuf[ACUTEST_CMDLINE_AUXBUF_SIZE_] = '\0';
+
+ while(i < argc) {
+ if(after_doubledash || strcmp(argv[i], "-") == 0) {
+ /* Non-option argument. */
+ ret = callback(ACUTEST_CMDLINE_OPTID_NONE_, argv[i]);
+ } else if(strcmp(argv[i], "--") == 0) {
+ /* End of options. All the remaining members are non-option arguments. */
+ after_doubledash = 1;
+ } else if(argv[i][0] != '-') {
+ /* Non-option argument. */
+ ret = callback(ACUTEST_CMDLINE_OPTID_NONE_, argv[i]);
+ } else {
+ for(opt = options; opt->id != 0; opt++) {
+ if(opt->longname != NULL && strncmp(argv[i], "--", 2) == 0) {
+ size_t len = strlen(opt->longname);
+ if(strncmp(argv[i]+2, opt->longname, len) == 0) {
+ /* Regular long option. */
+ if(argv[i][2+len] == '\0') {
+ /* with no argument provided. */
+ if(!(opt->flags & ACUTEST_CMDLINE_OPTFLAG_REQUIREDARG_))
+ ret = callback(opt->id, NULL);
+ else
+ ret = callback(ACUTEST_CMDLINE_OPTID_MISSINGARG_, argv[i]);
+ break;
+ } else if(argv[i][2+len] == '=') {
+ /* with an argument provided. */
+ if(opt->flags & (ACUTEST_CMDLINE_OPTFLAG_OPTIONALARG_ | ACUTEST_CMDLINE_OPTFLAG_REQUIREDARG_)) {
+ ret = callback(opt->id, argv[i]+2+len+1);
+ } else {
+ sprintf(auxbuf, "--%s", opt->longname);
+ ret = callback(ACUTEST_CMDLINE_OPTID_BOGUSARG_, auxbuf);
+ }
+ break;
+ } else {
+ continue;
+ }
+ }
+ } else if(opt->shortname != '\0' && argv[i][0] == '-') {
+ if(argv[i][1] == opt->shortname) {
+ /* Regular short option. */
+ if(opt->flags & ACUTEST_CMDLINE_OPTFLAG_REQUIREDARG_) {
+ if(argv[i][2] != '\0')
+ ret = callback(opt->id, argv[i]+2);
+ else if(i+1 < argc)
+ ret = callback(opt->id, argv[++i]);
+ else
+ ret = callback(ACUTEST_CMDLINE_OPTID_MISSINGARG_, argv[i]);
+ break;
+ } else {
+ ret = callback(opt->id, NULL);
+
+ /* There might be more (argument-less) short options
+ * grouped together. */
+ if(ret == 0 && argv[i][2] != '\0')
+ ret = acutest_cmdline_handle_short_opt_group_(options, argv[i]+2, callback);
+ break;
+ }
+ }
+ }
+ }
+
+ if(opt->id == 0) { /* still not handled? */
+ if(argv[i][0] != '-') {
+ /* Non-option argument. */
+ ret = callback(ACUTEST_CMDLINE_OPTID_NONE_, argv[i]);
+ } else {
+ /* Unknown option. */
+ char* badoptname = argv[i];
+
+ if(strncmp(badoptname, "--", 2) == 0) {
+ /* Strip any argument from the long option. */
+ char* assignment = strchr(badoptname, '=');
+ if(assignment != NULL) {
+ size_t len = assignment - badoptname;
+ if(len > ACUTEST_CMDLINE_AUXBUF_SIZE_)
+ len = ACUTEST_CMDLINE_AUXBUF_SIZE_;
+ strncpy(auxbuf, badoptname, len);
+ auxbuf[len] = '\0';
+ badoptname = auxbuf;
+ }
+ }
+
+ ret = callback(ACUTEST_CMDLINE_OPTID_UNKNOWN_, badoptname);
+ }
+ }
+ }
+
+ if(ret != 0)
+ return ret;
+ i++;
+ }
+
+ return ret;
+}
+
+static void
+acutest_help_(void)
+{
+ printf("Usage: %s [options] [test...]\n", acutest_argv0_);
+ printf("\n");
+ printf("Run the specified unit tests; or if the option '--skip' is used, run all\n");
+ printf("tests in the suite but those listed. By default, if no tests are specified\n");
+ printf("on the command line, all unit tests in the suite are run.\n");
+ printf("\n");
+ printf("Options:\n");
+ printf(" -s, --skip Execute all unit tests but the listed ones\n");
+ printf(" --exec[=WHEN] If supported, execute unit tests as child processes\n");
+ printf(" (WHEN is one of 'auto', 'always', 'never')\n");
+ printf(" -E, --no-exec Same as --exec=never\n");
+#if defined ACUTEST_WIN_
+ printf(" -t, --time Measure test duration\n");
+#elif defined ACUTEST_HAS_POSIX_TIMER_
+ printf(" -t, --time Measure test duration (real time)\n");
+ printf(" --time=TIMER Measure test duration, using given timer\n");
+ printf(" (TIMER is one of 'real', 'cpu')\n");
+#endif
+ printf(" --no-summary Suppress printing of test results summary\n");
+ printf(" --tap Produce TAP-compliant output\n");
+ printf(" (See https://testanything.org/)\n");
+ printf(" -x, --xml-output=FILE Enable XUnit output to the given file\n");
+ printf(" -l, --list List unit tests in the suite and exit\n");
+ printf(" -v, --verbose Make output more verbose\n");
+ printf(" --verbose=LEVEL Set verbose level to LEVEL:\n");
+ printf(" 0 ... Be silent\n");
+ printf(" 1 ... Output one line per test (and summary)\n");
+ printf(" 2 ... As 1 and failed conditions (this is default)\n");
+ printf(" 3 ... As 1 and all conditions (and extended summary)\n");
+ printf(" -q, --quiet Same as --verbose=0\n");
+ printf(" --color[=WHEN] Enable colorized output\n");
+ printf(" (WHEN is one of 'auto', 'always', 'never')\n");
+ printf(" --no-color Same as --color=never\n");
+ printf(" -h, --help Display this help and exit\n");
+
+ if(acutest_list_size_ < 16) {
+ printf("\n");
+ acutest_list_names_();
+ }
+}
+
+static const ACUTEST_CMDLINE_OPTION_ acutest_cmdline_options_[] = {
+ { 's', "skip", 's', 0 },
+ { 0, "exec", 'e', ACUTEST_CMDLINE_OPTFLAG_OPTIONALARG_ },
+ { 'E', "no-exec", 'E', 0 },
+#if defined ACUTEST_WIN_
+ { 't', "time", 't', 0 },
+ { 0, "timer", 't', 0 }, /* kept for compatibility */
+#elif defined ACUTEST_HAS_POSIX_TIMER_
+ { 't', "time", 't', ACUTEST_CMDLINE_OPTFLAG_OPTIONALARG_ },
+ { 0, "timer", 't', ACUTEST_CMDLINE_OPTFLAG_OPTIONALARG_ }, /* kept for compatibility */
+#endif
+ { 0, "no-summary", 'S', 0 },
+ { 0, "tap", 'T', 0 },
+ { 'l', "list", 'l', 0 },
+ { 'v', "verbose", 'v', ACUTEST_CMDLINE_OPTFLAG_OPTIONALARG_ },
+ { 'q', "quiet", 'q', 0 },
+ { 0, "color", 'c', ACUTEST_CMDLINE_OPTFLAG_OPTIONALARG_ },
+ { 0, "no-color", 'C', 0 },
+ { 'h', "help", 'h', 0 },
+ { 0, "worker", 'w', ACUTEST_CMDLINE_OPTFLAG_REQUIREDARG_ }, /* internal */
+ { 'x', "xml-output", 'x', ACUTEST_CMDLINE_OPTFLAG_REQUIREDARG_ },
+ { 0, NULL, 0, 0 }
+};
+
+static int
+acutest_cmdline_callback_(int id, const char* arg)
+{
+ switch(id) {
+ case 's':
+ acutest_skip_mode_ = 1;
+ break;
+
+ case 'e':
+ if(arg == NULL || strcmp(arg, "always") == 0) {
+ acutest_no_exec_ = 0;
+ } else if(strcmp(arg, "never") == 0) {
+ acutest_no_exec_ = 1;
+ } else if(strcmp(arg, "auto") == 0) {
+ /*noop*/
+ } else {
+ fprintf(stderr, "%s: Unrecognized argument '%s' for option --exec.\n", acutest_argv0_, arg);
+ fprintf(stderr, "Try '%s --help' for more information.\n", acutest_argv0_);
+ acutest_exit_(2);
+ }
+ break;
+
+ case 'E':
+ acutest_no_exec_ = 1;
+ break;
+
+ case 't':
+#if defined ACUTEST_WIN_ || defined ACUTEST_HAS_POSIX_TIMER_
+ if(arg == NULL || strcmp(arg, "real") == 0) {
+ acutest_timer_ = 1;
+ #ifndef ACUTEST_WIN_
+ } else if(strcmp(arg, "cpu") == 0) {
+ acutest_timer_ = 2;
+ #endif
+ } else {
+ fprintf(stderr, "%s: Unrecognized argument '%s' for option --time.\n", acutest_argv0_, arg);
+ fprintf(stderr, "Try '%s --help' for more information.\n", acutest_argv0_);
+ acutest_exit_(2);
+ }
+#endif
+ break;
+
+ case 'S':
+ acutest_no_summary_ = 1;
+ break;
+
+ case 'T':
+ acutest_tap_ = 1;
+ break;
+
+ case 'l':
+ acutest_list_names_();
+ acutest_exit_(0);
+ break;
+
+ case 'v':
+ acutest_verbose_level_ = (arg != NULL ? atoi(arg) : acutest_verbose_level_+1);
+ break;
+
+ case 'q':
+ acutest_verbose_level_ = 0;
+ break;
+
+ case 'c':
+ if(arg == NULL || strcmp(arg, "always") == 0) {
+ acutest_colorize_ = 1;
+ } else if(strcmp(arg, "never") == 0) {
+ acutest_colorize_ = 0;
+ } else if(strcmp(arg, "auto") == 0) {
+ /*noop*/
+ } else {
+ fprintf(stderr, "%s: Unrecognized argument '%s' for option --color.\n", acutest_argv0_, arg);
+ fprintf(stderr, "Try '%s --help' for more information.\n", acutest_argv0_);
+ acutest_exit_(2);
+ }
+ break;
+
+ case 'C':
+ acutest_colorize_ = 0;
+ break;
+
+ case 'h':
+ acutest_help_();
+ acutest_exit_(0);
+ break;
+
+ case 'w':
+ acutest_worker_ = 1;
+ acutest_worker_index_ = atoi(arg);
+ break;
+ case 'x':
+ acutest_xml_output_ = fopen(arg, "w");
+ if (!acutest_xml_output_) {
+ fprintf(stderr, "Unable to open '%s': %s\n", arg, strerror(errno));
+ acutest_exit_(2);
+ }
+ break;
+
+ case 0:
+ if(acutest_lookup_(arg) == 0) {
+ fprintf(stderr, "%s: Unrecognized unit test '%s'\n", acutest_argv0_, arg);
+ fprintf(stderr, "Try '%s --list' for list of unit tests.\n", acutest_argv0_);
+ acutest_exit_(2);
+ }
+ break;
+
+ case ACUTEST_CMDLINE_OPTID_UNKNOWN_:
+ fprintf(stderr, "Unrecognized command line option '%s'.\n", arg);
+ fprintf(stderr, "Try '%s --help' for more information.\n", acutest_argv0_);
+ acutest_exit_(2);
+ break;
+
+ case ACUTEST_CMDLINE_OPTID_MISSINGARG_:
+ fprintf(stderr, "The command line option '%s' requires an argument.\n", arg);
+ fprintf(stderr, "Try '%s --help' for more information.\n", acutest_argv0_);
+ acutest_exit_(2);
+ break;
+
+ case ACUTEST_CMDLINE_OPTID_BOGUSARG_:
+ fprintf(stderr, "The command line option '%s' does not expect an argument.\n", arg);
+ fprintf(stderr, "Try '%s --help' for more information.\n", acutest_argv0_);
+ acutest_exit_(2);
+ break;
+ }
+
+ return 0;
+}
+
+
+#ifdef ACUTEST_LINUX_
+static int
+acutest_is_tracer_present_(void)
+{
+ /* Must be large enough so the line 'TracerPid: ${PID}' can fit in. */
+ static const int OVERLAP = 32;
+
+ char buf[512];
+ int tracer_present = 0;
+ int fd;
+ size_t n_read = 0;
+
+ fd = open("/proc/self/status", O_RDONLY);
+ if(fd == -1)
+ return 0;
+
+ while(1) {
+ static const char pattern[] = "TracerPid:";
+ const char* field;
+
+ while(n_read < sizeof(buf) - 1) {
+ ssize_t n;
+
+ n = read(fd, buf + n_read, sizeof(buf) - 1 - n_read);
+ if(n <= 0)
+ break;
+ n_read += n;
+ }
+ buf[n_read] = '\0';
+
+ field = strstr(buf, pattern);
+ if(field != NULL && field < buf + sizeof(buf) - OVERLAP) {
+ pid_t tracer_pid = (pid_t) atoi(field + sizeof(pattern) - 1);
+ tracer_present = (tracer_pid != 0);
+ break;
+ }
+
+ if(n_read == sizeof(buf) - 1) {
+ /* Move the tail with the potentially incomplete line we're looking
+ * for to the beginning of the buffer. */
+ memmove(buf, buf + sizeof(buf) - 1 - OVERLAP, OVERLAP);
+ n_read = OVERLAP;
+ } else {
+ break;
+ }
+ }
+
+ close(fd);
+ return tracer_present;
+}
+#endif
+
+#ifdef ACUTEST_MACOS_
+static bool
+acutest_AmIBeingDebugged(void)
+{
+ int junk;
+ int mib[4];
+ struct kinfo_proc info;
+ size_t size;
+
+ // Initialize the flags so that, if sysctl fails for some bizarre
+ // reason, we get a predictable result.
+ info.kp_proc.p_flag = 0;
+
+ // Initialize mib, which tells sysctl the info we want, in this case
+ // we're looking for information about a specific process ID.
+ mib[0] = CTL_KERN;
+ mib[1] = KERN_PROC;
+ mib[2] = KERN_PROC_PID;
+ mib[3] = getpid();
+
+ // Call sysctl.
+ size = sizeof(info);
+ junk = sysctl(mib, sizeof(mib) / sizeof(*mib), &info, &size, NULL, 0);
+ assert(junk == 0);
+
+ // We're being debugged if the P_TRACED flag is set.
+ return ( (info.kp_proc.p_flag & P_TRACED) != 0 );
+}
+#endif
+
+int
+main(int argc, char** argv)
+{
+ int i;
+
+ acutest_argv0_ = argv[0];
+
+#if defined ACUTEST_UNIX_
+ acutest_colorize_ = isatty(STDOUT_FILENO);
+#elif defined ACUTEST_WIN_
+ #if defined _BORLANDC_
+ acutest_colorize_ = isatty(_fileno(stdout));
+ #else
+ acutest_colorize_ = _isatty(_fileno(stdout));
+ #endif
+#else
+ acutest_colorize_ = 0;
+#endif
+
+ /* Count all test units */
+ acutest_list_size_ = 0;
+ for(i = 0; acutest_list_[i].func != NULL; i++)
+ acutest_list_size_++;
+
+ acutest_test_data_ = (struct acutest_test_data_*)calloc(acutest_list_size_, sizeof(struct acutest_test_data_));
+ if(acutest_test_data_ == NULL) {
+ fprintf(stderr, "Out of memory.\n");
+ acutest_exit_(2);
+ }
+
+ /* Parse options */
+ acutest_cmdline_read_(acutest_cmdline_options_, argc, argv, acutest_cmdline_callback_);
+
+ /* Initialize the proper timer. */
+ acutest_timer_init_();
+
+#if defined(ACUTEST_WIN_)
+ SetUnhandledExceptionFilter(acutest_seh_exception_filter_);
+#ifdef _MSC_VER
+ _set_abort_behavior(0, _WRITE_ABORT_MSG);
+#endif
+#endif
+
+ /* By default, we want to run all tests. */
+ if(acutest_count_ == 0) {
+ for(i = 0; acutest_list_[i].func != NULL; i++)
+ acutest_remember_(i);
+ }
+
+ /* Guess whether we want to run unit tests as child processes. */
+ if(acutest_no_exec_ < 0) {
+ acutest_no_exec_ = 0;
+
+ if(acutest_count_ <= 1) {
+ acutest_no_exec_ = 1;
+ } else {
+#ifdef ACUTEST_WIN_
+ if(IsDebuggerPresent())
+ acutest_no_exec_ = 1;
+#endif
+#ifdef ACUTEST_LINUX_
+ if(acutest_is_tracer_present_())
+ acutest_no_exec_ = 1;
+#endif
+#ifdef ACUTEST_MACOS_
+ if(acutest_AmIBeingDebugged())
+ acutest_no_exec_ = 1;
+#endif
+#ifdef RUNNING_ON_VALGRIND
+ /* RUNNING_ON_VALGRIND is provided by optionally included <valgrind.h> */
+ if(RUNNING_ON_VALGRIND)
+ acutest_no_exec_ = 1;
+#endif
+ }
+ }
+
+ if(acutest_tap_) {
+ /* TAP requires we know test result ("ok", "not ok") before we output
+ * anything about the test, and this gets problematic for larger verbose
+ * levels. */
+ if(acutest_verbose_level_ > 2)
+ acutest_verbose_level_ = 2;
+
+ /* TAP harness should provide some summary. */
+ acutest_no_summary_ = 1;
+
+ if(!acutest_worker_)
+ printf("1..%d\n", (int) acutest_count_);
+ }
+
+ int index = acutest_worker_index_;
+ for(i = 0; acutest_list_[i].func != NULL; i++) {
+ int run = (acutest_test_data_[i].flags & ACUTEST_FLAG_RUN_);
+ if (acutest_skip_mode_) /* Run all tests except those listed. */
+ run = !run;
+ if(run)
+ acutest_run_(&acutest_list_[i], index++, i);
+ }
+
+ /* Write a summary */
+ if(!acutest_no_summary_ && acutest_verbose_level_ >= 1) {
+ if(acutest_verbose_level_ >= 3) {
+ acutest_colored_printf_(ACUTEST_COLOR_DEFAULT_INTENSIVE_, "Summary:\n");
+
+ printf(" Count of all unit tests: %4d\n", (int) acutest_list_size_);
+ printf(" Count of run unit tests: %4d\n", acutest_stat_run_units_);
+ printf(" Count of failed unit tests: %4d\n", acutest_stat_failed_units_);
+ printf(" Count of skipped unit tests: %4d\n", (int) acutest_list_size_ - acutest_stat_run_units_);
+ }
+
+ if(acutest_stat_failed_units_ == 0) {
+ acutest_colored_printf_(ACUTEST_COLOR_GREEN_INTENSIVE_, "SUCCESS:");
+ printf(" All unit tests have passed.\n");
+ } else {
+ acutest_colored_printf_(ACUTEST_COLOR_RED_INTENSIVE_, "FAILED:");
+ printf(" %d of %d unit tests %s failed.\n",
+ acutest_stat_failed_units_, acutest_stat_run_units_,
+ (acutest_stat_failed_units_ == 1) ? "has" : "have");
+ }
+
+ if(acutest_verbose_level_ >= 3)
+ printf("\n");
+ }
+
+ if (acutest_xml_output_) {
+#if defined ACUTEST_UNIX_
+ char *suite_name = basename(argv[0]);
+#elif defined ACUTEST_WIN_
+ char suite_name[_MAX_FNAME];
+ _splitpath(argv[0], NULL, NULL, suite_name, NULL);
+#else
+ const char *suite_name = argv[0];
+#endif
+ fprintf(acutest_xml_output_, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
+ fprintf(acutest_xml_output_, "<testsuite name=\"%s\" tests=\"%d\" errors=\"%d\" failures=\"%d\" skip=\"%d\">\n",
+ suite_name, (int)acutest_list_size_, acutest_stat_failed_units_, acutest_stat_failed_units_,
+ (int)acutest_list_size_ - acutest_stat_run_units_);
+ for(i = 0; acutest_list_[i].func != NULL; i++) {
+ struct acutest_test_data_ *details = &acutest_test_data_[i];
+ fprintf(acutest_xml_output_, " <testcase name=\"%s\" time=\"%.2f\">\n", acutest_list_[i].name, details->duration);
+ if (details->flags & ACUTEST_FLAG_FAILURE_)
+ fprintf(acutest_xml_output_, " <failure />\n");
+ if (!(details->flags & ACUTEST_FLAG_FAILURE_) && !(details->flags & ACUTEST_FLAG_SUCCESS_))
+ fprintf(acutest_xml_output_, " <skipped />\n");
+ fprintf(acutest_xml_output_, " </testcase>\n");
+ }
+ fprintf(acutest_xml_output_, "</testsuite>\n");
+ fclose(acutest_xml_output_);
+ }
+
+ acutest_cleanup_();
+
+ return (acutest_stat_failed_units_ == 0) ? 0 : 1;
+}
+
+
+#endif /* #ifndef TEST_NO_MAIN */
+
+#ifdef _MSC_VER
+ #pragma warning(pop)
+#endif
+
+#ifdef __cplusplus
+ } /* extern "C" */
+#endif
+
+#endif /* #ifndef ACUTEST_H */
diff --git a/fluent-bit/tests/lib/shunit2/.gitignore b/fluent-bit/tests/lib/shunit2/.gitignore
new file mode 100644
index 00000000..4832c04f
--- /dev/null
+++ b/fluent-bit/tests/lib/shunit2/.gitignore
@@ -0,0 +1,3 @@
+# Hidden files generated by macOS.
+.DS_Store
+._*
diff --git a/fluent-bit/tests/lib/shunit2/.travis.yml b/fluent-bit/tests/lib/shunit2/.travis.yml
new file mode 100644
index 00000000..55aeda47
--- /dev/null
+++ b/fluent-bit/tests/lib/shunit2/.travis.yml
@@ -0,0 +1,29 @@
+language: bash
+
+env:
+ - SHUNIT_COLOR='always'
+
+script:
+ # Execute the unit tests.
+ - ./test_runner
+
+os:
+ - linux
+ - osx
+
+addons:
+ apt:
+ packages:
+ - ksh
+ - zsh
+
+matrix:
+ include:
+ - os: linux
+ script:
+ # Run the source through ShellCheck (http://www.shellcheck.net).
+ - shellcheck shunit2 *_test.sh
+ - shellcheck -s sh shunit2_test_helpers
+ - os: linux
+ # Support Ubuntu Trusty through Apr 2019.
+ dist: trusty
diff --git a/fluent-bit/tests/lib/shunit2/CODE_OF_CONDUCT.md b/fluent-bit/tests/lib/shunit2/CODE_OF_CONDUCT.md
new file mode 100644
index 00000000..dc906ab0
--- /dev/null
+++ b/fluent-bit/tests/lib/shunit2/CODE_OF_CONDUCT.md
@@ -0,0 +1,46 @@
+# Contributor Covenant Code of Conduct
+
+## Our Pledge
+
+In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation.
+
+## Our Standards
+
+Examples of behavior that contributes to creating a positive environment include:
+
+* Using welcoming and inclusive language
+* Being respectful of differing viewpoints and experiences
+* Gracefully accepting constructive criticism
+* Focusing on what is best for the community
+* Showing empathy towards other community members
+
+Examples of unacceptable behavior by participants include:
+
+* The use of sexualized language or imagery and unwelcome sexual attention or advances
+* Trolling, insulting/derogatory comments, and personal or political attacks
+* Public or private harassment
+* Publishing others' private information, such as a physical or electronic address, without explicit permission
+* Other conduct which could reasonably be considered inappropriate in a professional setting
+
+## Our Responsibilities
+
+Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior.
+
+Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful.
+
+## Scope
+
+This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers.
+
+## Enforcement
+
+Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at kate.ward@forestent.com. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.
+
+Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.
+
+## Attribution
+
+This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version]
+
+[homepage]: http://contributor-covenant.org
+[version]: http://contributor-covenant.org/version/1/4/
diff --git a/fluent-bit/tests/lib/shunit2/LICENSE b/fluent-bit/tests/lib/shunit2/LICENSE
new file mode 100644
index 00000000..8dada3ed
--- /dev/null
+++ b/fluent-bit/tests/lib/shunit2/LICENSE
@@ -0,0 +1,201 @@
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "{}"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright {yyyy} {name of copyright owner}
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
diff --git a/fluent-bit/tests/lib/shunit2/README.md b/fluent-bit/tests/lib/shunit2/README.md
new file mode 100644
index 00000000..dd6bba42
--- /dev/null
+++ b/fluent-bit/tests/lib/shunit2/README.md
@@ -0,0 +1,636 @@
+# shUnit2
+
+shUnit2 is a [xUnit](http://en.wikipedia.org/wiki/XUnit) unit test framework for
+Bourne based shell scripts, and it is designed to work in a similar manner to
+[JUnit](http://www.junit.org), [PyUnit](http://pyunit.sourceforge.net), etc.. If
+you have ever had the desire to write a unit test for a shell script, shUnit2
+can do the job.
+
+[![Travis CI](https://img.shields.io/travis/kward/shunit2.svg)](https://travis-ci.org/kward/shunit2)
+
+## Table of Contents
+
+* [Introduction](#introduction)
+ * [Credits / Contributors](#credits-contributors)
+ * [Feedback](#feedback)
+* [Quickstart](#quickstart)
+* [Function Reference](#function-reference)
+ * [General Info](#general-info)
+ * [Asserts](#asserts)
+ * [Failures](#failures)
+ * [Setup/Teardown](#setup-teardown)
+ * [Skipping](#skipping)
+ * [Suites](#suites)
+* [Advanced Usage](#advanced-usage)
+ * [Some constants you can use](#some-constants-you-can-use)
+ * [Error Handling](#error-handling)
+ * [Including Line Numbers in Asserts (Macros)](#including-line-numbers-in-asserts-macros)
+ * [Test Skipping](#test-skipping)
+ * [Running specific tests from the command line](#cmd-line-args)
+* [Appendix](#appendix)
+ * [Getting help](#getting-help)
+ * [Zsh](#zsh)
+
+---
+
+## <a name="introduction"></a> Introduction
+
+shUnit2 was originally developed to provide a consistent testing solution for
+[log4sh][log4sh], a shell based logging framework similar to
+[log4j](http://logging.apache.org). During the development of that product, a
+repeated problem of having things work just fine under one shell (`/bin/bash` on
+Linux to be specific), and then not working under another shell (`/bin/sh` on
+Solaris) kept coming up. Although several simple tests were run, they were not
+adequate and did not catch some corner cases. The decision was finally made to
+write a proper unit test framework after multiple brown-bag releases were made.
+_Research was done to look for an existing product that met the testing
+requirements, but no adequate product was found._
+
+### Tested software
+
+**Tested Operating Systems** (varies over time)
+
+OS | Support | Verified
+----------------------------------- | --------- | --------
+Ubuntu Linux (14.04.05 LTS) | Travis CI | continuous
+macOS High Sierra (10.13.3) | Travis CI | continuous
+FreeBSD | user | unknown
+Solaris 8, 9, 10 (inc. OpenSolaris) | user | unknown
+Cygwin | user | unknown
+
+**Tested Shells**
+
+* Bourne Shell (__sh__)
+* BASH - GNU Bourne Again SHell (__bash__)
+* DASH (__dash__)
+* Korn Shell (__ksh__)
+* pdksh - Public Domain Korn Shell (__pdksh__)
+* zsh - Zsh (__zsh__) (since 2.1.2) _please see the Zsh shell errata for more information_
+
+See the appropriate Release Notes for this release
+(`doc/RELEASE_NOTES-X.X.X.txt`) for the list of actual versions tested.
+
+### <a name="credits-contributors"></a> Credits / Contributors
+
+A list of contributors to shUnit2 can be found in `doc/contributors.md`. Many
+thanks go out to all those who have contributed to make this a better tool.
+
+shUnit2 is the original product of many hours of work by Kate Ward, the primary
+author of the code. For related software, check out https://github.com/kward.
+
+### <a name="feedback"></a> Feedback
+
+Feedback is most certainly welcome for this document. Send your questions,
+comments, and criticisms via the
+[shunit2-users](https://groups.google.com/a/forestent.com/forum/#!forum/shunit2-users/new)
+forum (created 2018-12-09), or file an issue via
+https://github.com/kward/shunit2/issues.
+
+---
+
+## <a name="quickstart"></a> Quickstart
+
+This section will give a very quick start to running unit tests with shUnit2.
+More information is located in later sections.
+
+Here is a quick sample script to show how easy it is to write a unit test in
+shell. _Note: the script as it stands expects that you are running it from the
+"examples" directory._
+
+```sh
+#! /bin/sh
+# file: examples/equality_test.sh
+
+testEquality() {
+ assertEquals 1 1
+}
+
+# Load shUnit2.
+. ./shunit2
+```
+
+Running the unit test should give results similar to the following.
+
+```console
+$ cd examples
+$ ./equality_test.sh
+testEquality
+
+Ran 1 test.
+
+OK
+```
+
+W00t! You've just run your first successful unit test. So, what just happened?
+Quite a bit really, and it all happened simply by sourcing the `shunit2`
+library. The basic functionality for the script above goes like this:
+
+* When shUnit2 is sourced, it will walk through any functions defined whose name
+ starts with the string `test`, and add those to an internal list of tests to
+ execute. Once a list of test functions to be run has been determined, shunit2
+ will go to work.
+* Before any tests are executed, shUnit2 again looks for a function, this time
+ one named `oneTimeSetUp()`. If it exists, it will be run. This function is
+ normally used to setup the environment for all tests to be run. Things like
+ creating directories for output or setting environment variables are good to
+ place here. Just so you know, you can also declare a corresponding function
+ named `oneTimeTearDown()` function that does the same thing, but once all the
+ tests have been completed. It is good for removing temporary directories, etc.
+* shUnit2 is now ready to run tests. Before doing so though, it again looks for
+ another function that might be declared, one named `setUp()`. If the function
+ exists, it will be run before each test. It is good for resetting the
+ environment so that each test starts with a clean slate. **At this stage, the
+ first test is finally run.** The success of the test is recorded for a report
+ that will be generated later. After the test is run, shUnit2 looks for a final
+ function that might be declared, one named `tearDown()`. If it exists, it will
+ be run after each test. It is a good place for cleaning up after each test,
+ maybe doing things like removing files that were created, or removing
+ directories. This set of steps, `setUp() > test() > tearDown()`, is repeated
+ for all of the available tests.
+* Once all the work is done, shUnit2 will generate the nice report you saw
+ above. A summary of all the successes and failures will be given so that you
+ know how well your code is doing.
+
+We should now try adding a test that fails. Change your unit test to look like
+this.
+
+```sh
+#! /bin/sh
+# file: examples/party_test.sh
+
+testEquality() {
+ assertEquals 1 1
+}
+
+testPartyLikeItIs1999() {
+ year=`date '+%Y'`
+ assertEquals "It's not 1999 :-(" '1999' "${year}"
+}
+
+# Load shUnit2.
+. ./shunit2
+```
+
+So, what did you get? I guess it told you that this isn't 1999. Bummer, eh?
+Hopefully, you noticed a couple of things that were different about the second
+test. First, we added an optional message that the user will see if the assert
+fails. Second, we did comparisons of strings instead of integers as in the first
+test. It doesn't matter whether you are testing for equality of strings or
+integers. Both work equally well with shUnit2.
+
+Hopefully, this is enough to get you started with unit testing. If you want a
+ton more examples, take a look at the tests provided with [log4sh][log4sh] or
+[shFlags][shflags]. Both provide excellent examples of more advanced usage.
+shUnit2 was after all written to meet the unit testing need that
+[log4sh][log4sh] had.
+
+---
+
+## <a name="function-reference"></a> Function Reference
+
+### <a name="general-info"></a> General Info
+
+Any string values passed should be properly quoted -- they should must be
+surrounded by single-quote (`'`) or double-quote (`"`) characters -- so that the
+shell will properly parse them.
+
+### <a name="asserts"></a> Asserts
+
+`assertEquals [message] expected actual`
+
+Asserts that _expected_ and _actual_ are equal to one another. The _expected_
+and _actual_ values can be either strings or integer values as both will be
+treated as strings. The _message_ is optional, and must be quoted.
+
+`assertNotEquals [message] unexpected actual`
+
+Asserts that _unexpected_ and _actual_ are not equal to one another. The
+_unexpected_ and _actual_ values can be either strings or integer values as both
+will be treaded as strings. The _message_ is optional, and must be quoted.
+
+`assertSame [message] expected actual`
+
+This function is functionally equivalent to `assertEquals`.
+
+`assertNotSame [message] unexpected actual`
+
+This function is functionally equivalent to `assertNotEquals`.
+
+`assertContains [message] container content`
+
+Asserts that _container_ contains _content_. The _container_ and _content_
+values can be either strings or integer values as both will be treated as
+strings. The _message_ is optional, and must be quoted.
+
+`assertNotContains [message] container content`
+
+Asserts that _container_ does not contain _content_. The _container_ and
+_content_ values can be either strings or integer values as both will be treaded
+as strings. The _message_ is optional, and must be quoted.
+
+`assertNull [message] value`
+
+Asserts that _value_ is _null_, or in shell terms, a zero-length string. The
+_value_ must be a string as an integer value does not translate into a zero-
+length string. The _message_ is optional, and must be quoted.
+
+`assertNotNull [message] value`
+
+Asserts that _value_ is _not null_, or in shell terms, a non-empty string. The
+_value_ may be a string or an integer as the later will be parsed as a non-empty
+string value. The _message_ is optional, and must be quoted.
+
+`assertTrue [message] condition`
+
+Asserts that a given shell test _condition_ is _true_. The condition can be as
+simple as a shell _true_ value (the value `0` -- equivalent to
+`${SHUNIT_TRUE}`), or a more sophisticated shell conditional expression. The
+_message_ is optional, and must be quoted.
+
+A sophisticated shell conditional expression is equivalent to what the __if__ or
+__while__ shell built-ins would use (more specifically, what the __test__
+command would use). Testing for example whether some value is greater than
+another value can be done this way.
+
+`assertTrue "[ 34 -gt 23 ]"`
+
+Testing for the ability to read a file can also be done. This particular test
+will fail.
+
+`assertTrue 'test failed' "[ -r /some/non-existant/file' ]"`
+
+As the expressions are standard shell __test__ expressions, it is possible to
+string multiple expressions together with `-a` and `-o` in the standard fashion.
+This test will succeed as the entire expression evaluates to _true_.
+
+`assertTrue 'test failed' '[ 1 -eq 1 -a 2 -eq 2 ]'`
+
+<i>One word of warning: be very careful with your quoting as shell is not the
+most forgiving of bad quoting, and things will fail in strange ways.</i>
+
+`assertFalse [message] condition`
+
+Asserts that a given shell test _condition_ is _false_. The condition can be as
+simple as a shell _false_ value (the value `1` -- equivalent to
+`${SHUNIT_FALSE}`), or a more sophisticated shell conditional expression. The
+_message_ is optional, and must be quoted.
+
+_For examples of more sophisticated expressions, see `assertTrue`._
+
+### <a name="failures"></a> Failures
+
+Just to clarify, failures __do not__ test the various arguments against one
+another. Failures simply fail, optionally with a message, and that is all they
+do. If you need to test arguments against one another, use asserts.
+
+If all failures do is fail, why might one use them? There are times when you may
+have some very complicated logic that you need to test, and the simple asserts
+provided are simply not adequate. You can do your own validation of the code,
+use an `assertTrue ${SHUNIT_TRUE}` if your own tests succeeded, and use a
+failure to record a failure.
+
+`fail [message]`
+
+Fails the test immediately. The _message_ is optional, and must be quoted.
+
+`failNotEquals [message] unexpected actual`
+
+Fails the test immediately, reporting that the _unexpected_ and _actual_ values
+are not equal to one another. The _message_ is optional, and must be quoted.
+
+_Note: no actual comparison of unexpected and actual is done._
+
+`failSame [message] expected actual`
+
+Fails the test immediately, reporting that the _expected_ and _actual_ values
+are the same. The _message_ is optional, and must be quoted.
+
+_Note: no actual comparison of expected and actual is done._
+
+`failNotSame [message] expected actual`
+
+Fails the test immediately, reporting that the _expected_ and _actual_ values
+are not the same. The _message_ is optional, and must be quoted.
+
+_Note: no actual comparison of expected and actual is done._
+
+`failFound [message] content`
+
+Fails the test immediately, reporting that the _content_ was found. The
+_message_ is optional, and must be quoted.
+
+_Note: no actual search of content is done._
+
+`failNotFound [message] content`
+
+Fails the test immediately, reporting that the _content_ was not found. The
+_message_ is optional, and must be quoted.
+
+_Note: no actual search of content is done._
+
+### <a name="setup-teardown"></a> Setup/Teardown
+
+`oneTimeSetUp`
+
+This function can be be optionally overridden by the user in their test suite.
+
+If this function exists, it will be called once before any tests are run. It is
+useful to prepare a common environment for all tests.
+
+`oneTimeTearDown`
+
+This function can be be optionally overridden by the user in their test suite.
+
+If this function exists, it will be called once after all tests are completed.
+It is useful to clean up the environment after all tests.
+
+`setUp`
+
+This function can be be optionally overridden by the user in their test suite.
+
+If this function exists, it will be called before each test is run. It is useful
+to reset the environment before each test.
+
+`tearDown`
+
+This function can be be optionally overridden by the user in their test suite.
+
+If this function exists, it will be called after each test completes. It is
+useful to clean up the environment after each test.
+
+### <a name="skipping"></a> Skipping
+
+`startSkipping`
+
+This function forces the remaining _assert_ and _fail_ functions to be
+"skipped", i.e. they will have no effect. Each function skipped will be recorded
+so that the total of asserts and fails will not be altered.
+
+`endSkipping`
+
+This function returns calls to the _assert_ and _fail_ functions to their
+default behavior, i.e. they will be called.
+
+`isSkipping`
+
+This function returns the current state of skipping. It can be compared against
+`${SHUNIT_TRUE}` or `${SHUNIT_FALSE}` if desired.
+
+### <a name="suites"></a> Suites
+
+The default behavior of shUnit2 is that all tests will be found dynamically. If
+you have a specific set of tests you want to run, or you don't want to use the
+standard naming scheme of prefixing your tests with `test`, these functions are
+for you. Most users will never use them though.
+
+`suite`
+
+This function can be optionally overridden by the user in their test suite.
+
+If this function exists, it will be called when `shunit2` is sourced. If it does
+not exist, shUnit2 will search the parent script for all functions beginning
+with the word `test`, and they will be added dynamically to the test suite.
+
+`suite_addTest name`
+
+This function adds a function named _name_ to the list of tests scheduled for
+execution as part of this test suite. This function should only be called from
+within the `suite()` function.
+
+---
+
+## <a name="advanced-usage"></a> Advanced Usage
+
+### <a name="some-constants-you-can-use"></a> Some constants you can use
+
+There are several constants provided by shUnit2 as variables that might be of
+use to you.
+
+*Predefined*
+
+| Constant | Value |
+| --------------- | ----- |
+| SHUNIT\_TRUE | Standard shell `true` value (the integer value 0). |
+| SHUNIT\_FALSE | Standard shell `false` value (the integer value 1). |
+| SHUNIT\_ERROR | The integer value 2. |
+| SHUNIT\_TMPDIR | Path to temporary directory that will be automatically cleaned up upon exit of shUnit2. |
+| SHUNIT\_VERSION | The version of shUnit2 you are running. |
+
+*User defined*
+
+| Constant | Value |
+| ----------------- | ----- |
+| SHUNIT\_CMD\_EXPR | Override which `expr` command is used. By default `expr` is used, except on BSD systems where `gexpr` is used. |
+| SHUNIT\_COLOR | Enable colorized output. Options are 'auto', 'always', or 'none', with 'auto' being the default. |
+| SHUNIT\_PARENT | The filename of the shell script containing the tests. This is needed specifically for Zsh support. |
+| SHUNIT\_TEST\_PREFIX | Define this variable to add a prefix in front of each test name that is output in the test report. |
+
+### <a name="error-handling"></a> Error handling
+
+The constants values `SHUNIT_TRUE`, `SHUNIT_FALSE`, and `SHUNIT_ERROR` are
+returned from nearly every function to indicate the success or failure of the
+function. Additionally the variable `flags_error` is filled with a detailed
+error message if any function returns with a `SHUNIT_ERROR` value.
+
+### <a name="including-line-numbers-in-asserts-macros"></a> Including Line Numbers in Asserts (Macros)
+
+If you include lots of assert statements in an individual test function, it can
+become difficult to determine exactly which assert was thrown unless your
+messages are unique. To help somewhat, line numbers can be included in the
+assert messages. To enable this, a special shell "macro" must be used rather
+than the standard assert calls. _Shell doesn't actually have macros; the name is
+used here as the operation is similar to a standard macro._
+
+For example, to include line numbers for a `assertEquals()` function call,
+replace the `assertEquals()` with `${_ASSERT_EQUALS_}`.
+
+_**Example** -- Asserts with and without line numbers_
+
+```sh
+#! /bin/sh
+# file: examples/lineno_test.sh
+
+testLineNo() {
+ # This assert will have line numbers included (e.g. "ASSERT:[123] ...").
+ echo "ae: ${_ASSERT_EQUALS_}"
+ ${_ASSERT_EQUALS_} 'not equal' 1 2
+
+ # This assert will not have line numbers included (e.g. "ASSERT: ...").
+ assertEquals 'not equal' 1 2
+}
+
+# Load shUnit2.
+. ./shunit2
+```
+
+Notes:
+
+1. Due to how shell parses command-line arguments, all strings used with macros
+ should be quoted twice. Namely, single-quotes must be converted to single-
+ double-quotes, and vice-versa. If the string being passed is absolutely for
+ sure not empty, the extra quoting is not necessary.<br/>
+ <br/>
+ Normal `assertEquals` call.<br/>
+ `assertEquals 'some message' 'x' ''`<br/>
+ <br/>
+ Macro `_ASSERT_EQUALS_` call. Note the extra quoting around the _message_ and
+ the _null_ value.<br/>
+ `_ASSERT_EQUALS_ '"some message"' 'x' '""'`
+
+1. Line numbers are not supported in all shells. If a shell does not support
+ them, no errors will be thrown. Supported shells include: __bash__ (>=3.0),
+ __ksh__, __pdksh__, and __zsh__.
+
+### <a name="test-skipping"></a> Test Skipping
+
+There are times where the test code you have written is just not applicable to
+the system you are running on. This section describes how to skip these tests
+but maintain the total test count.
+
+Probably the easiest example would be shell code that is meant to run under the
+__bash__ shell, but the unit test is running under the Bourne shell. There are
+things that just won't work. The following test code demonstrates two sample
+functions, one that will be run under any shell, and the another that will run
+only under the __bash__ shell.
+
+_**Example** -- math include_
+```sh
+# file: examples/math.inc.
+
+add_generic() {
+ num_a=$1
+ num_b=$2
+
+ expr $1 + $2
+}
+
+add_bash() {
+ num_a=$1
+ num_b=$2
+
+ echo $(($1 + $2))
+}
+```
+
+And here is a corresponding unit test that correctly skips the `add_bash()` function when the unit test is not running under the __bash__ shell.
+
+_**Example** -- math unit test_
+```sh
+#! /bin/sh
+# file: examples/math_test.sh
+
+testAdding() {
+ result=`add_generic 1 2`
+ assertEquals \
+ "the result of '${result}' was wrong" \
+ 3 "${result}"
+
+ # Disable non-generic tests.
+ [ -z "${BASH_VERSION:-}" ] && startSkipping
+
+ result=`add_bash 1 2`
+ assertEquals \
+ "the result of '${result}' was wrong" \
+ 3 "${result}"
+}
+
+oneTimeSetUp() {
+ # Load include to test.
+ . ./math.inc
+}
+
+# Load and run shUnit2.
+. ./shunit2
+```
+
+Running the above test under the __bash__ shell will result in the following
+output.
+
+```console
+$ /bin/bash math_test.sh
+testAdding
+
+Ran 1 test.
+
+OK
+```
+
+But, running the test under any other Unix shell will result in the following
+output.
+
+```console
+$ /bin/ksh math_test.sh
+testAdding
+
+Ran 1 test.
+
+OK (skipped=1)
+```
+
+As you can see, the total number of tests has not changed, but the report
+indicates that some tests were skipped.
+
+Skipping can be controlled with the following functions: `startSkipping()`,
+`endSkipping()`, and `isSkipping()`. Once skipping is enabled, it will remain
+enabled until the end of the current test function call, after which skipping is
+disabled.
+
+### <a name="cmd-line-args"></a> Running specific tests from the command line.
+
+When running a test script, you may override the default set of tests, or the suite-specified set of tests, by providing additional arguments on the command line. Each additional argument after the `--` marker is assumed to be the name of a test function to be run in the order specified. e.g.
+
+```console
+test-script.sh -- testOne testTwo otherFunction
+```
+
+or
+
+```console
+shunit2 test-script.sh testOne testTwo otherFunction
+```
+
+In either case, three functions will be run as tests, `testOne`, `testTwo`, and `otherFunction`. Note that the function `otherFunction` would not normally be run by `shunit2` as part of the implicit collection of tests as it's function name does not match the test function name pattern `test*`.
+
+If a specified test function does not exist, `shunit2` will still attempt to run that function and thereby cause a failure which `shunit2` will catch and mark as a failed test. All other tests will run normally.
+
+The specification of tests does not affect how `shunit2` looks for and executes the setup and tear down functions, which will still run as expected.
+
+---
+
+## <a name="appendix"></a> Appendix
+
+### <a name="getting-help"></a> Getting Help
+
+For help, please send requests to either the shunit2-users@forestent.com mailing
+list (archives available on the web at
+https://groups.google.com/a/forestent.com/forum/#!forum/shunit2-users) or
+directly to Kate Ward <kate dot ward at forestent dot com>.
+
+### <a name="zsh"></a> Zsh
+
+For compatibility with Zsh, there is one requirement that must be met -- the
+`shwordsplit` option must be set. There are three ways to accomplish this.
+
+1. In the unit-test script, add the following shell code snippet before sourcing
+ the `shunit2` library.
+
+ ```sh
+ setopt shwordsplit
+ ```
+
+2. When invoking __zsh__ from either the command-line or as a script with `#!`,
+ add the `-y` parameter.
+
+ ```sh
+ #! /bin/zsh -y
+ ```
+
+3. When invoking __zsh__ from the command-line, add `-o shwordsplit --` as
+ parameters before the script name.
+
+ ```console
+ $ zsh -o shwordsplit -- some_script
+ ```
+
+[log4sh]: https://github.com/kward/log4sh
+[shflags]: https://github.com/kward/shflags
diff --git a/fluent-bit/tests/lib/shunit2/doc/CHANGES-2.1.md b/fluent-bit/tests/lib/shunit2/doc/CHANGES-2.1.md
new file mode 100644
index 00000000..776ff5a9
--- /dev/null
+++ b/fluent-bit/tests/lib/shunit2/doc/CHANGES-2.1.md
@@ -0,0 +1,261 @@
+# shUnit2 2.1.x Changes
+
+## Changes with 2.1.8
+
+### New
+
+Issue #29. Add support for user defined prefix for test names. A prefix can be
+added by defining the `SHUNIT_TEST_PREFIX` variable.
+
+### Improvements
+
+Issue #78. Added an example for using suite tests.
+
+Run continuous integration additionally against Ubuntu Trusty.
+
+### Fixed
+
+Issue #94. Removed the `gen_test_report.sh` script as the Travis CI output can
+be used instead. Reports were used before Travis CI was used.
+
+Issue #84. Treat syntax errors in functions as test failures.
+
+Issue #77. Fail tests when the environment functions (e.g. `setup()` or
+`tearDown()`) fail.
+
+
+## Changes with 2.1.7
+
+### Bug fixes
+
+Issue #69. shUnit2 should not exit with 0 when it has (syntax) errors.
+
+### Enhancements
+
+Issue #54. Shell commands prefixed with '\' so that they can be stubbed in
+tests.
+
+Issue #68. Ran all code through [ShellCheck](http://www.shellcheck.net/).
+
+Issue #60. Continuous integration tests now run with
+[Travis CI](https://travis-ci.org/kward/shunit2).
+
+Issue #56. Added color support. Color is enabled automatically when supported,
+but can be disabled by defining the SHUNIT_COLOR environment variable before
+sourcing shunit2. Accepted values are `always`, `auto` (the default), and
+`none`.
+
+Issue #35. Add colored output.
+
+### Other
+
+Moved code to GitHub (https://github.com/kward/shunit2), and restructured to
+be more GitHub like.
+
+Changed to the Apache 2.0 license.
+
+
+## Changes with 2.1.6
+
+Removed all references to the DocBook documentation.
+
+Simplified the 'src' structure.
+
+Fixed error message in fail() that stated wrong number of required arguments.
+
+Updated lib/versions.
+
+Fixed bug in `_shunit_mktempDir()` where a failure occurred when the 'od'
+command was not present in `/usr/bin`.
+
+Renamed `shunit_tmpDir` variable to `SHUNIT_TMPDIR` to closer match the standard
+`TMPDIR` variable.
+
+Added support for calling shunit2 as an executable, in addition to the existing
+method of sourcing it in as a library. This allows users to keep tests working
+despite the location of the shunit2 executable being different for each OS
+distribution.
+
+Issue #14: Improved handling of some strange chars (e.g. single and double
+quotes) in messages.
+
+Issue# 27: Fixed error message for `assertSame()`.
+
+Issue# 25: Added check and error message to user when phantom functions are
+written to a partition mounted with `noexec`.
+
+Issue# 11: Added support for defining functions like `function someFunction()`.
+
+
+## Changes with 2.1.5
+
+Issue# 1: Fixed bug pointed out by R Bernstein in the trap code where certain
+types of exit conditions did not generate the ending report.
+
+Issue# 2: Added `assertNotEquals()` assert.
+
+Issue# 3: Moved check for unset variables out of shUnit2 into the unit tests.
+Testing poorly written software blows up if this check is in, but it is only
+interesting for shUnit2 itself. Added `shunit_test_output.sh` unit test for
+this. Some shells still do not catch such errors properly (e.g. Bourne shell and
+BASH 2.x).
+
+Added new custom assert in test_helpers to check for output to STDOUT, and none
+to STDERR.
+
+Replaced fatal message in the temp directory creation with a `_shunit_fatal()`
+function call.
+
+Fixed test_output unit test so it works now that the 'set -u' stuff was removed
+for Issue# 3.
+
+Flushed out the coding standards in the `README.txt` a bit more, and brought the
+shunit2 code up to par with the documented standards.
+
+Issue# 4: Completely changed the reporting output to be a closer match for
+JUnit and PyUnit. As a result, tests are counted separately from assertions.
+
+Provide public `shunit_tmpDir` variable that can be used by unit test scripts
+that need automated and guaranteed cleanup.
+
+Issue# 7: Fixed duplicated printing of messages passed to asserts.
+
+Per code review, fixed wording of `failSame()` and `failNotSame()` messages.
+
+Replaced `version_info.sh` with versions library and made appropriate changes in
+other scripts to use it.
+
+Added `gen_test_results.sh` to make releases easier.
+
+Fixed bugs in `shlib_relToAbsPath()` in shlib.
+
+Converted DocBook documentation to reStructuredText for easier maintenance. The
+DocBook documentation is now considered obsolete, and will be removed in a
+future release.
+
+Issue# 5: Fixed the documentation around the usage of failures.
+
+Issue# 9: Added unit tests and updated documentation to demonstrate the
+requirement of quoting values twice when macros are used. This is due to how
+shell parses arguments.
+
+When an invalid number of arguments is passed to a function, the invalid number
+is returned to the user so they are more aware of what the cause might be.
+
+
+## Changes with 2.1.4
+
+Removed the `_shunit_functionExists()` function as it was dead code.
+
+Fixed zsh version number check in version_info.
+
+Fixed bug in last resort temporary directory creation.
+
+Fixed off-by-one in exit value for scripts caught by the trap handler.
+
+Added argument count error checking to all functions.
+
+Added mkdir_test.sh example.
+
+Moved src/test into src/shell to better match structure used with shFlags.
+
+Fixed problem where null values were not handled properly under ksh.
+
+Added support for outputting line numbers as part of assert messages.
+
+Started documenting the coding standards, and changed some variable names as a
+result.
+
+Improved zsh version and option checks.
+
+Renamed the `__SHUNIT_VERSION` variable to `SHUNIT_VERSION`.
+
+
+## Changes with 2.1.3
+
+Added some explicit variable defaults, even though the variables are set, as
+they sometimes behave strange when the script is canceled.
+
+Additional workarounds for zsh compatibility.
+
+shUnit2 now exits with a non-zero exit code if any of the tests failed. This was
+done for automated testing frameworks. Tests that were skipped are not
+considered failures, and do not affect the exit code.
+
+Changed detection of STDERR output in unit tests.
+
+
+## Changes with 2.1.2
+
+Unset additional variables that were missed.
+
+Added checks and workarounds to improve zsh compatibility.
+
+Added some argument count checks `assertEquals()`, `assertNull()`, and
+`assertSame()`.
+
+
+## Changes with 2.1.1
+
+Fixed bug where `fail()` was not honoring skipping.
+
+Fixed problem with `docs-docbook-prep` target that prevented it from working.
+(Thanks to Bryan Larsen for pointing this out.)
+
+Changed the test in `assertFalse()` so that any non-zero value registers as
+false. (Credits to Bryan Larsen)
+
+Major fiddling to bring more in line with [JUnit](http://junit.org/). Asserts
+give better output when no message is given, and failures now just fail.
+
+It was pointed out that the simple 'failed' message for a failed assert was not
+only insufficient, it was nonstandard (when compared to JUnit) and didn't
+provide the user with an expected vs actual result. The code was revised
+somewhat to bring closer into alignment with JUnit (v4.3.1 specifically) so
+that it feels more "normal". (Credits to Richard Jensen)
+
+As part of the JUnit realignment, it was noticed that `fail*()` functions in
+JUnit don't actually do any comparisons themselves. They only generate a
+failure message. Updated the code to match.
+
+Added self-testing unit tests. Kinda horkey, but they did find bugs during the
+JUnit realignment.
+
+Fixed the code for returning from asserts as the return was being called before
+the unsetting of variables occurred. (Credits to Mathias Goldau)
+
+The assert(True|False)() functions now accept an integer value for a
+conditional test. A value of '0' is considered 'true', while any non-zero value
+is considered 'false'.
+
+All public functions now fill use default values to work properly with the '-x'
+shell debugging flag.
+
+Fixed the method of percent calculation for the report to get achieve better
+accuracy.
+
+
+## Changes with 2.1.0 (since 2.0.1)
+
+This release is a branch of the 2.0.1 release.
+
+Moving to [reStructured Text](http://docutils.sourceforge.net/rst.html) for
+the documentation.
+
+Fixed problem with `fail()`. The failure message was not properly printed.
+
+Fixed the `Makefile` so that the DocBook XML and XSLT files would be
+downloaded before parsing can continue.
+
+Renamed the internal `__SHUNIT_TRUE` and `__SHUNIT_FALSE` variables to
+`SHUNIT_TRUE` and `SHUNIT_FALSE` so that unit tests can "use" them.
+
+Added support for test "skipping". If skipping is turned on with the
+`startSkip()` function, `assert` and `fail` functions will return immediately,
+and the skip will be recorded.
+
+The report output format was changed to include the percentage for each test
+result, rather than just those successful.
+
+
+[travis_ci]: https://travis-ci.org/kward/shunit2
diff --git a/fluent-bit/tests/lib/shunit2/doc/RELEASE_NOTES-2.1.0.txt b/fluent-bit/tests/lib/shunit2/doc/RELEASE_NOTES-2.1.0.txt
new file mode 100644
index 00000000..c99f7463
--- /dev/null
+++ b/fluent-bit/tests/lib/shunit2/doc/RELEASE_NOTES-2.1.0.txt
@@ -0,0 +1,104 @@
+Release Notes for shUnit2 2.1.0
+===============================
+
+This release was branched from shUnit2 2.0.1. It mostly adds new functionality,
+but there are couple of bugs fixed from the previous release.
+
+See the ``CHANGES-2.1.rst`` file for a full list of changes.
+
+
+Tested Platforms
+----------------
+
+This list of platforms comes from the latest version of log4sh as shUnit2 is
+used in the testing of log4sh on each of these platforms.
+
+Cygwin
+
+- bash 3.2.9(10)
+- pdksh 5.2.14
+
+Linux
+
+- bash 3.1.17(1), 3.2.10(1)
+- dash 0.5.3
+- ksh 1993-12-28
+- pdksh 5.2.14
+- zsh 4.3.2 (does not work)
+
+Mac OS X 10.4.8 (Darwin 8.8)
+
+- bash 2.05b.0(1)
+- ksh 1993-12-28
+
+Solaris 8 U3 (x86)
+
+- /bin/sh
+- bash 2.03.0(1)
+- ksh M-11/16/88i
+
+Solaris 10 U2 (sparc)
+
+- /bin/sh
+- bash 3.00.16(1)
+- ksh M-11/16/88i
+
+Solaris 10 U2 (x86)
+
+- /bin/sh
+- bash 3.00.16(1)
+- ksh M-11/16/88i
+
+
+New Features
+------------
+
+Test skipping
+
+ Support added for test "skipping". A skip mode can be enabled so that
+ subsequent ``assert`` and ``fail`` functions that are called will be recorded
+ as "skipped" rather than as "passed" or "failed". This functionality can be
+ used such that when a set of tests makes sense on one platform but not on
+ another, they can be effectively disabled without altering the total number
+ of tests.
+
+ One example might be when something is supported under ``bash``, but not
+ under a standard Bourne shell.
+
+ New functions: ``startSkipping()``, ``endSkipping``, ``isSkipping``
+
+
+Changes and Enhancements
+------------------------
+
+Moving to the use of `reStructured Text
+<http://docutils.sourceforge.net/rst.html>`_ for documentation. It is easy to
+read and edit in textual form, but converts nicely to HTML.
+
+The report format has changed. Rather than including a simple "success"
+percentage at the end, a percentage is given for each type of test.
+
+
+Bug Fixes
+---------
+
+The ``fail()`` function did not output the optional failure message.
+
+Fixed the ``Makefile`` so that the DocBook XML and XSLT files would be
+downloaded before documentation parsing will continue.
+
+
+Deprecated Features
+-------------------
+
+None.
+
+
+Known Bugs and Issues
+---------------------
+
+None.
+
+
+.. $Revision$
+.. vim:fileencoding=latin1:spell:syntax=rst:textwidth=80
diff --git a/fluent-bit/tests/lib/shunit2/doc/RELEASE_NOTES-2.1.1.txt b/fluent-bit/tests/lib/shunit2/doc/RELEASE_NOTES-2.1.1.txt
new file mode 100644
index 00000000..4c610051
--- /dev/null
+++ b/fluent-bit/tests/lib/shunit2/doc/RELEASE_NOTES-2.1.1.txt
@@ -0,0 +1,88 @@
+Release Notes for shUnit2 2.1.1
+===============================
+
+This is mainly a bug fix release, but it also incorporates a realignment with
+the JUnit 4 code. Asserts now provide better failure messages, and the failure
+functions no longer perform tests.
+
+See the ``CHANGES-2.1.txt`` file for a full list of changes.
+
+
+Tested Platforms
+----------------
+
+This list of platforms comes from the latest version of log4sh as shUnit2 is
+used in the testing of log4sh on each of these platforms.
+
+Cygwin
+
+- bash 3.2.15(13)
+- pdksh 5.2.14
+
+Linux
+
+- bash 3.1.17(1), 3.2.10(1)
+- dash 0.5.3
+- ksh 1993-12-28
+- pdksh 5.2.14
+- zsh 4.3.2 (does not work)
+
+Mac OS X 10.4.9 (Darwin 8.9.1)
+
+- bash 2.05b.0(1)
+- ksh 1993-12-28
+
+Solaris 8 U3 (x86)
+
+- /bin/sh
+- bash 2.03.0(1)
+- ksh M-11/16/88i
+
+Solaris 10 U2 (sparc, x86)
+
+- /bin/sh
+- bash 3.00.16(1)
+- ksh M-11/16/88i
+
+
+New Features
+------------
+
+None.
+
+
+Changes and Enhancements
+------------------------
+
+The internal test in ``assertFalse()`` now accepts any non-zero value as false.
+
+The ``assertTrue()`` and ``assertFalse()`` functions now accept an integer value
+for a conditional test. A value of '0' is considered 'true', while any non-zero
+value is considered 'false'.
+
+Self-testing unit tests were added.
+
+
+Bug Fixes
+---------
+
+The ``fail()`` assert now honors skipping.
+
+The ``docs-docbook-prep`` target now works properly.
+
+All asserts now properly unset their variables.
+
+
+Deprecated Features
+-------------------
+
+None.
+
+
+Known Bugs and Issues
+---------------------
+
+Functions do not properly test for an invalid number of arguments.
+
+
+.. vim:fileencoding=latin1:ft=rst:spell:textwidth=80
diff --git a/fluent-bit/tests/lib/shunit2/doc/RELEASE_NOTES-2.1.2.txt b/fluent-bit/tests/lib/shunit2/doc/RELEASE_NOTES-2.1.2.txt
new file mode 100644
index 00000000..54929842
--- /dev/null
+++ b/fluent-bit/tests/lib/shunit2/doc/RELEASE_NOTES-2.1.2.txt
@@ -0,0 +1,83 @@
+Release Notes for shUnit2 2.1.2
+===============================
+
+This release adds initial support for the zsh shell. Due to some differences
+with this shell as compared with others, some special checks have been added,
+and there are some extra requirements necessary when this shell is to be used.
+
+To use zsh with shUnit2, the following two requirements must be met:
+* The ``shwordsplit`` option must be set.
+* The ``function_argzero`` option must be unset.
+
+Please read the Shell Errata section of the documentation for guidance on how
+to meet these requirements.
+
+
+See the ``CHANGES-2.1.txt`` file for a full list of changes.
+
+
+Tested Platforms
+----------------
+
+This list of platforms comes from the latest version of log4sh as shUnit2 is
+used in the testing of log4sh on each of these platforms.
+
+Linux
+
+- bash 3.1.17(1), 3.2.25(1)
+- dash 0.5.4
+- ksh 1993-12-28
+- pdksh 5.2.14
+- zsh 4.2.5, 4.3.4
+
+Mac OS X 10.4.11 (Darwin 8.11.1)
+
+- bash 2.05b.0(1)
+- ksh 1993-12-28
+- zsh 4.2.3
+
+Solaris 10 U3 (x86)
+
+- /bin/sh
+- bash 3.00.16(1)
+- ksh M-11/16/88i
+- zsh 4.2.1
+
+
+New Features
+------------
+
+Support for the zsh shell.
+
+
+Changes and Enhancements
+------------------------
+
+Added some argument count checks.
+
+
+Bug Fixes
+---------
+
+None.
+
+
+Deprecated Features
+-------------------
+
+None.
+
+
+Known Bugs and Issues
+---------------------
+
+Functions do not properly test for an invalid number of arguments.
+
+ksh and pdksh do not pass null arguments (i.e. empty strings as '') properly,
+and as such checks do not work properly.
+
+zsh requires the ``shwordsplit`` option to be set, and the ``function_argzero``
+option to be unset for proper operation.
+
+
+.. vim:fileencoding=latin1:ft=rst:spell:textwidth=80
diff --git a/fluent-bit/tests/lib/shunit2/doc/RELEASE_NOTES-2.1.3.txt b/fluent-bit/tests/lib/shunit2/doc/RELEASE_NOTES-2.1.3.txt
new file mode 100644
index 00000000..7d1c9f65
--- /dev/null
+++ b/fluent-bit/tests/lib/shunit2/doc/RELEASE_NOTES-2.1.3.txt
@@ -0,0 +1,84 @@
+Release Notes for shUnit2 2.1.3
+===============================
+
+This release is minor feature release. It improves support for zsh (although it
+still isn't what it could be) and adds automated testing framework support by
+returning a non-zero exit when tests fail.
+
+To use zsh with shUnit2, the following two requirements must be met:
+* The ``shwordsplit`` option must be set.
+* The ``function_argzero`` option must be unset.
+
+Please read the Shell Errata section of the documentation for guidance on how
+to meet these requirements.
+
+See the ``CHANGES-2.1.txt`` file for a full list of changes.
+
+
+Tested Platforms
+----------------
+
+Cygwin
+
+- bash 3.2.33(18)
+- pdksh 5.2.14
+
+Linux
+
+- bash 3.2.33(1)
+- dash 0.5.4
+- ksh 1993-12-28
+- pdksh 5.2.14
+- zsh 4.3.4
+
+Mac OS X 10.5.2 (Darwin 9.2.2)
+
+- bash 3.2.17(1)
+- ksh 1993-12-28
+- zsh 4.3.4
+
+Solaris 11 x86 (Nevada 77)
+
+- /bin/sh
+- bash 3.2.25(1)
+- ksh M-11/16/88i
+- zsh 4.3.4
+
+
+New Features
+------------
+
+None.
+
+
+Changes and Enhancements
+------------------------
+
+Support for automated testing frameworks.
+
+
+Bug Fixes
+---------
+
+Fixed some issues with zsh support.
+
+
+Deprecated Features
+-------------------
+
+None.
+
+
+Known Bugs and Issues
+---------------------
+
+Functions do not properly test for an invalid number of arguments.
+
+ksh and pdksh do not pass null arguments (i.e. empty strings as '') properly,
+and as such checks do not work properly.
+
+zsh requires the ``shwordsplit`` option to be set, and the ``function_argzero``
+option to be unset for proper operation.
+
+
+.. vim:fileencoding=latin1:ft=rst:spell:textwidth=80
diff --git a/fluent-bit/tests/lib/shunit2/doc/RELEASE_NOTES-2.1.4.txt b/fluent-bit/tests/lib/shunit2/doc/RELEASE_NOTES-2.1.4.txt
new file mode 100644
index 00000000..007b5c3a
--- /dev/null
+++ b/fluent-bit/tests/lib/shunit2/doc/RELEASE_NOTES-2.1.4.txt
@@ -0,0 +1,100 @@
+Release Notes for shUnit2 2.1.4
+===============================
+
+This release contains lots of bug fixes and changes. Mostly, it fixes zsh
+support in zsh 3.0, and the handling of null values in ksh.
+
+To use zsh with shUnit2, the following requirement must be met:
+
+- The ``shwordsplit`` option must be set.
+
+Please read the Shell Errata section of the documentation for guidance on how
+to meet these requirements.
+
+See the ``CHANGES-2.1.txt`` file for a full list of changes.
+
+
+Tested Platforms
+----------------
+
+Cygwin
+
+- bash 3.2.39(19)
+- pdksh 5.2.14
+- zsh 4.3.4
+
+Linux (Ubuntu Dapper 6.06)
+
+- bash 3.1.17(1)
+- pdksh 5.2.14
+- zsh 4.2.5
+
+Linux (Ubuntu Hardy 8.04)
+
+- bash 3.2.39(1)
+- dash 0.5.4
+- ksh 1993-12-28
+- pdksh 5.2.14
+- zsh 4.3.4
+
+Mac OS X 10.5.4 (Darwin 9.4.0)
+
+- bash 3.2.17(1)
+- ksh 1993-12-28
+- zsh 4.3.4
+
+Solaris 9 U6 x86
+
+- /bin/sh
+- bash 2.05.0(1)
+- ksh M-11/16/88i
+- zsh 3.0.8
+
+Solaris 11 x86 (Nevada 77)
+
+- /bin/sh
+- bash 3.2.25(1)
+- ksh M-11/16/88i
+- zsh 4.3.4
+
+
+New Features
+------------
+
+Support added to output assert source line number as part of assert messages.
+
+
+Changes and Enhancements
+------------------------
+
+Support for automated testing frameworks.
+
+Added argument count error checking to all functions.
+
+
+Bug Fixes
+---------
+
+Fixed some issues with ksh and zsh support.
+
+Fixed off-by-one of exit value in trap handler.
+
+Fixed handling of null values under ksh.
+
+Fixed bug in last resort temporary directory creation.
+
+
+Deprecated Features
+-------------------
+
+None.
+
+
+Known Bugs and Issues
+---------------------
+
+zsh requires the ``shwordsplit`` option to be set.
+
+Line numbers in assert messages do not work properly with Bash 2.x.
+
+.. vim:fileencoding=latin1:ft=rst:spell:tw=80
diff --git a/fluent-bit/tests/lib/shunit2/doc/RELEASE_NOTES-2.1.5.txt b/fluent-bit/tests/lib/shunit2/doc/RELEASE_NOTES-2.1.5.txt
new file mode 100644
index 00000000..d9f26cec
--- /dev/null
+++ b/fluent-bit/tests/lib/shunit2/doc/RELEASE_NOTES-2.1.5.txt
@@ -0,0 +1,128 @@
+Release Notes for shUnit2 2.1.5
+===============================
+
+This release contains several bug fixes and changes. Additionally, it includes
+a rewrite of the test output to better match JUnit and PyUnit.
+
+This version also includes a slightly expanded set of coding standards by which
+shUnit2 is coded. It should help anyone reading the code to better understand
+it.
+
+
+
+Please read the Shell Errata section of the documentation for guidance on how
+to meet these requirements.
+
+See the ``CHANGES-2.1.txt`` file for a full list of changes.
+
+
+Tested Platforms
+----------------
+
+Cygwin
+
+- bash 3.2.39(20)
+- ksh (sym-link to pdksh)
+- pdksh 5.2.14
+- zsh 4.3.4
+
+Linux (Ubuntu Dapper 6.06)
+
+- bash 3.1.17(1)
+- ksh M-1993-12-28
+- pdksh 5.2.14-99/07/13.2
+- zsh 4.2.5
+
+Linux (Ubuntu Hardy 8.04)
+
+- bash 3.2.39(1)
+- dash 0.5.4
+- ksh M-1993-12-28
+- pdksh 5.2.14-99/07/13.2
+- zsh 4.3.4
+
+Mac OS X 10.5.4 (Darwin 9.4.0)
+
+- bash 3.2.17(1)
+- ksh M-1993-12-28
+- zsh 4.3.4
+
+Solaris 9 U6 x86
+
+- /bin/sh
+- bash 2.05.0(1)
+- ksh M-11/16/88i
+- zsh 3.0.8
+
+Solaris 11 x86 (Nevada 77)
+
+- /bin/sh
+- bash 3.2.25(1)
+- ksh M-11/16/88i
+- zsh 4.3.4
+
+
+New Features
+------------
+
+Support added for output assert source line number as part of assert messages.
+
+Issue #2: Added assertNotEquals() assert.
+
+Provided a public ``shunit_tmpDir`` variable that can be used by unit test
+scripts that need automated and guaranteed cleanup.
+
+
+Changes and Enhancements
+------------------------
+
+Issue #3: Removed the check for unset variables as shUnit2 should not expect
+scripts being tested to be clean.
+
+Issue #4: Rewrote the test summary. It is now greatly simplified and much more
+script friendly.
+
+Issue #5: Fixed the documentation around the usage of failures.
+
+Issue #9: Added unit tests and improved documentation around the use of macros.
+
+Code updated to meet documented coding standards.
+
+Improved code reuse of ``_shunit_exit()`` and ``_shunit_fatal()`` functions.
+
+All output except shUnit2 error messages now goes to STDOUT.
+
+Converted DocBook documentation to reStructuredText for easier maintenance.
+
+
+Bug Fixes
+---------
+
+Issue #1: Fixed bug in rap code where certain types of exit conditions did not
+generate the ending report.
+
+Issue #7: Fixed duplicated printing of messages passed to asserts.
+
+Fixed bugs in ``shlib_relToAbsPath()`` in ``shlib``.
+
+
+Deprecated Features
+-------------------
+
+None.
+
+
+Known Bugs and Issues
+---------------------
+
+Zsh requires the ``shwordsplit`` option to be set. See the documentation for
+examples of how to do this.
+
+Line numbers in assert messages do not work properly with BASH 2.x.
+
+The Bourne shell of Solaris, BASH 2.x, and Zsh 3.0.x do not properly catch the
+SIGTERM signal. As such, shell interpreter failures due to such things as
+unbound variables cannot be caught. (See ``shunit_test_misc.sh``)
+
+
+.. vim:fileencoding=latin1:ft=rst:spell:tw=80
diff --git a/fluent-bit/tests/lib/shunit2/doc/RELEASE_NOTES-2.1.6.txt b/fluent-bit/tests/lib/shunit2/doc/RELEASE_NOTES-2.1.6.txt
new file mode 100644
index 00000000..50087fe4
--- /dev/null
+++ b/fluent-bit/tests/lib/shunit2/doc/RELEASE_NOTES-2.1.6.txt
@@ -0,0 +1,112 @@
+Release Notes for shUnit2 2.1.6
+===============================
+
+This release contains bug fixes and changes. It is also the first release to
+support running shunit2 as a standalone program.
+
+Please read the Shell Errata section of the documentation for guidance on how
+to meet these requirements.
+
+See the ``CHANGES-2.1.txt`` file for a full list of changes.
+
+New Features
+------------
+
+Support for running shUnit2 as a standalone program. This makes it possible for
+users to execute their unit tests in a manner that is not dependent on the
+location an OS distribution maintainer chose to place shUnit2 in the file
+system.
+
+Added support for functions defined like 'function someFunction()'.
+
+Changes and Enhancements
+------------------------
+
+Renamed the public ``shunit_tmpDir`` variable to ``SHUNIT_TMPDIR`` to be more
+consistent with the ``TMPDIR`` variable.
+
+Bug Fixes
+---------
+
+Fixed issue where shunit2 would fail on some distributions when creating a
+temporary directory because the **od** command was not present.
+
+Deprecated Features
+-------------------
+
+None.
+
+Known Bugs and Issues
+---------------------
+
+Zsh requires the ``shwordsplit`` option to be set. See the documentation for
+examples of how to do this.
+
+Line numbers in assert messages do not work properly with BASH 2.x.
+
+The Bourne shell of Solaris, BASH 2.x, and Zsh 3.0.x do not properly catch the
+SIGTERM signal. As such, shell interpreter failures due to such things as
+unbound variables cannot be caught. (See ``shunit_test_misc.sh``)
+
+Tested Platforms
+----------------
+
+Cygwin 1.7.9 (Windows XP SP2)
+
+- bash 4.1.10(4)
+- dash 0.5.6.1
+- ksh (sym-link to pdksh)
+- pdksh 5.2.14
+- zsh 4.3.11
+
+Linux (Ubuntu Dapper 6.06.2 LTS)
+
+- bash 3.1.17(1)
+- dash 0.5.3
+- ksh (sym-link to pdksh)
+- pdksh 5.2.14-99/07/13.2
+- zsh 4.2.5
+
+Linux (Ubuntu Hardy 8.04.4 LTS)
+
+- bash 3.2.39(1)
+- dash 0.5.4
+- ksh M-1993-12-28
+- pdksh 5.2.14-99/07/13.2
+- zsh 4.3.4
+
+Linux (Ubuntu Lucid 10.04.2 LTS)
+
+- bash 4.1.5(1)
+- dash 0.5.5.1
+- ksh JM-93t+-2009-05-01
+- pdksh 5.2.14-99/07/13.2
+- zsh 4.3.10
+
+Mac OS X 10.6.7
+
+- bash 3.2.48(1)
+- ksh M-1993-12-28
+- zsh 4.3.9
+
+Solaris 8 U7 x86
+
+- /bin/sh
+- bash 2.03.0(1)
+- ksh M-11/16/88i
+- zsh 3.0.6
+
+Solaris 9 U6 x86
+
+- /bin/sh
+- bash 2.05.0(1)
+- ksh M-11/16/88i
+- zsh 3.0.8
+
+OpenSolaris 2009.06(snv_111b) x86
+
+- /bin/sh
+- bash 3.2.25(1)
+- ksh 2008-11-04
+
+.. vim:fileencoding=latin1:ft=rst:spell:tw=80
diff --git a/fluent-bit/tests/lib/shunit2/doc/RELEASE_NOTES-2.1.7.md b/fluent-bit/tests/lib/shunit2/doc/RELEASE_NOTES-2.1.7.md
new file mode 100644
index 00000000..044564d8
--- /dev/null
+++ b/fluent-bit/tests/lib/shunit2/doc/RELEASE_NOTES-2.1.7.md
@@ -0,0 +1,66 @@
+# shUnit2 2.1.7 Release Notes
+
+https://github.com/kward/shunit2
+
+This release contains bug fixes and enhancements. It is the first release since moving to GitHub. Users can now clone the latest version at any time.
+
+See the `CHANGES-2.1.md` file for a full list of changes.
+
+
+## New Features
+
+Colorized output, based on popular demand. shUnit2 output is now colorized based on the result of the asserts.
+
+
+## Changes and Enhancements
+
+With the move to GitHub, the shUnit2 unit tests are run on every commit using the [Travis CI][TravisCI] continuous integration framework. Additionally, all code is run through [ShellCheck](http:/www.shellcheck.net/) on every commit.
+
+[TravisCI]: https://travis-ci.org/kward/shunit2
+
+Shell commands in shUnit2 are prefixed with '\' so that they can be stubbed in tests.
+
+
+## Bug Fixes
+
+shUnit2 no longer exits with an 'OK' result if there were syntax errors due to incorrect usage of the assert commands.
+
+
+## Deprecated Features
+
+None.
+
+
+## Known Bugs and Issues
+
+Zsh requires the `shwordsplit` option to be set. See the documentation for examples of how to do this.
+
+Line numbers in assert messages do not work properly with BASH 2.x.
+
+The Bourne shell of Solaris, BASH 2.x, and Zsh 3.0.x do not properly catch the
+SIGTERM signal. As such, shell interpreter failures due to such things as
+unbound variables cannot be caught. (See `shunit_test_misc.sh`)
+
+
+## Tested Platforms
+
+Continuous integration testing is provided by
+[Travis CI](https://travis-ci.org/).
+
+https://travis-ci.org/github/kward/shunit2
+
+Tested OSes:
+
+- Linux
+- macOS
+
+Tested shells:
+
+- /bin/sh
+- ash
+- bash
+- dash
+- ksh
+- pdksh
+- zsh
+
diff --git a/fluent-bit/tests/lib/shunit2/doc/RELEASE_NOTES-2.1.8.md b/fluent-bit/tests/lib/shunit2/doc/RELEASE_NOTES-2.1.8.md
new file mode 100644
index 00000000..d09d16ae
--- /dev/null
+++ b/fluent-bit/tests/lib/shunit2/doc/RELEASE_NOTES-2.1.8.md
@@ -0,0 +1,56 @@
+# shUnit2 2.1.8 Release Notes
+
+https://github.com/kward/shunit2
+
+This release contains bug fixes and enhancements. See the `CHANGES-2.1.md` file
+for a full list of changes.
+
+## New features
+
+Users can now define a custom prefix for test function names. The prefix can be
+configured by defining a `SHUNIT_TEST_PREFIX` variable.
+
+## Bug fixes
+
+Syntax errors in functions are now treated as test failures.
+
+Test now fail when `setup()` or `tearDown()` fail.
+
+## Deprecated features
+
+None.
+
+## Known bugs and issues
+
+Zsh requires the `shwordsplit` option to be set. See the documentation for examples of how to do this.
+
+Line numbers in assert messages do not work properly with BASH 2.x.
+
+The Bourne shell of Solaris, BASH 2.x, and Zsh 3.0.x do not properly catch the
+SIGTERM signal. As such, shell interpreter failures due to such things as
+unbound variables cannot be caught. (See `shunit_test_misc.sh`)
+
+shUnit2 does not work when the `-e` shell option is set (typically done with
+`set -e`).
+
+## Tested platforms
+
+Continuous integration testing is provided by
+[Travis CI](https://travis-ci.org/).
+
+https://travis-ci.org/github/kward/shunit2
+
+Tested OSes:
+
+- Linux
+- macOS
+
+Tested shells:
+
+- /bin/sh
+- ash
+- bash
+- dash
+- ksh
+- pdksh
+- zsh
diff --git a/fluent-bit/tests/lib/shunit2/doc/TODO.txt b/fluent-bit/tests/lib/shunit2/doc/TODO.txt
new file mode 100644
index 00000000..cecc17dd
--- /dev/null
+++ b/fluent-bit/tests/lib/shunit2/doc/TODO.txt
@@ -0,0 +1,13 @@
+Make it possible to execute a single test by passing the name of the test on
+the command line
+
+Add support for '--randomize-order' so that the test order is randomized to
+check for dependencies (which shouldn't be there) between tests.
+
+--debug option to display point in source code (line number and such) where the
+problem showed up.
+
+assertTrue() just gives 'ASSERT:', nothing else :-(. others too?
+upd: assertNull() will give message passed, but nothing else useful :-(
+
+$Revision$
diff --git a/fluent-bit/tests/lib/shunit2/doc/contributors.md b/fluent-bit/tests/lib/shunit2/doc/contributors.md
new file mode 100644
index 00000000..7adae223
--- /dev/null
+++ b/fluent-bit/tests/lib/shunit2/doc/contributors.md
@@ -0,0 +1,15 @@
+The original author of shunit2 is Kate Ward. The following people have
+contributed in some way or another to shunit2.
+
+- [Alex Harvey](https://github.com/alexharv074)
+- Bryan Larsen
+- [David Acacio](https://github.com/dacacioa)
+- Kevin Van Horn
+- [Maciej Bliziński](https://github.com/automatthias)
+- Mario Sparada
+- Mathias Goldau
+- Richard Jensen
+- Rob Holland
+- Rocky Bernstein
+- [rugk](https://github.com/rugk)
+- wood4321 (of code.google.com)
diff --git a/fluent-bit/tests/lib/shunit2/doc/design_doc.txt b/fluent-bit/tests/lib/shunit2/doc/design_doc.txt
new file mode 100644
index 00000000..24d41ff1
--- /dev/null
+++ b/fluent-bit/tests/lib/shunit2/doc/design_doc.txt
@@ -0,0 +1,34 @@
+Design Doc for shUnit
+
+shUnit is based upon JUnit. The initial ideas for the script came from the book
+"Pragmatic Unit Testing - In Java with JUnit" by Andrew Hunt and David Thomas.
+
+The script was written to perform unit testing for log4sh. log4sh had grown
+enough that it was becoming difficult to easily test and and verify that the
+tests passed for the many different operating systems on which it was being
+used.
+
+The functions in shUnit are meant to match those in JUnit as much as possible
+where shell allows. In the initial version, there will be no concept of
+exceptions (as normal POSIX shell has no concept of them) but attempts to trap
+problems will be done.
+
+Programatic Standards:
+
+* SHUNIT_TRUE - public global constant
+* __SHUNIT_SHELL_FLAGS - private global constant
+* __shunit_oldShellFlags - private global variable
+
+* assertEquals - public unit test function
+* shunit_publicFunc - public shUnit function; can be called from parent unit
+ test script
+* _shunit_privateFunc - private shUnit function; should not be called from
+ parent script. meant for internal use by shUnit
+
+* _su_myVar - variable inside a public function. prefixing with '_su_' to
+ reduce the chances that a variable outside of shUnit will be overridden.
+* _su__myVar - variable inside a private function. prefixing with '_su__' to
+ reduce the chances that a variable in a shUnit public function, or a variable
+ outside of shUnit will be overridden.
+
+$Revision$
diff --git a/fluent-bit/tests/lib/shunit2/examples/equality_test.sh b/fluent-bit/tests/lib/shunit2/examples/equality_test.sh
new file mode 100755
index 00000000..7b566403
--- /dev/null
+++ b/fluent-bit/tests/lib/shunit2/examples/equality_test.sh
@@ -0,0 +1,9 @@
+#! /bin/sh
+# file: examples/equality_test.sh
+
+testEquality() {
+ assertEquals 1 1
+}
+
+# Load and run shUnit2.
+. ../shunit2
diff --git a/fluent-bit/tests/lib/shunit2/examples/lineno_test.sh b/fluent-bit/tests/lib/shunit2/examples/lineno_test.sh
new file mode 100755
index 00000000..11ddfc8a
--- /dev/null
+++ b/fluent-bit/tests/lib/shunit2/examples/lineno_test.sh
@@ -0,0 +1,15 @@
+#! /bin/sh
+# file: examples/lineno_test.sh
+
+testLineNo() {
+ # This assert will have line numbers included (e.g. "ASSERT:[123] ...") if
+ # they are supported.
+ echo "_ASSERT_EQUALS_ macro value: ${_ASSERT_EQUALS_}"
+ ${_ASSERT_EQUALS_} '"not equal"' 1 2
+
+ # This assert will not have line numbers included (e.g. "ASSERT: ...").
+ assertEquals 'not equal' 1 2
+}
+
+# Load and run shUnit2.
+. ../shunit2
diff --git a/fluent-bit/tests/lib/shunit2/examples/math.inc b/fluent-bit/tests/lib/shunit2/examples/math.inc
new file mode 100644
index 00000000..40971066
--- /dev/null
+++ b/fluent-bit/tests/lib/shunit2/examples/math.inc
@@ -0,0 +1,17 @@
+# available as examples/math.inc
+
+add_generic()
+{
+ num_a=$1
+ num_b=$2
+
+ expr $1 + $2
+}
+
+add_bash()
+{
+ num_a=$1
+ num_b=$2
+
+ echo $(($1 + $2))
+}
diff --git a/fluent-bit/tests/lib/shunit2/examples/math_test.sh b/fluent-bit/tests/lib/shunit2/examples/math_test.sh
new file mode 100755
index 00000000..c6d00299
--- /dev/null
+++ b/fluent-bit/tests/lib/shunit2/examples/math_test.sh
@@ -0,0 +1,25 @@
+#! /bin/sh
+# file: examples/math_test.sh
+
+testAdding() {
+ result=`add_generic 1 2`
+ assertEquals \
+ "the result of '${result}' was wrong" \
+ 3 "${result}"
+
+ # Disable non-generic tests.
+ [ -z "${BASH_VERSION:-}" ] && startSkipping
+
+ result=`add_bash 1 2`
+ assertEquals \
+ "the result of '${result}' was wrong" \
+ 3 "${result}"
+}
+
+oneTimeSetUp() {
+ # Load include to test.
+ . ./math.inc
+}
+
+# Load and run shUnit2.
+. ../shunit2
diff --git a/fluent-bit/tests/lib/shunit2/examples/mkdir_test.sh b/fluent-bit/tests/lib/shunit2/examples/mkdir_test.sh
new file mode 100755
index 00000000..a608e3aa
--- /dev/null
+++ b/fluent-bit/tests/lib/shunit2/examples/mkdir_test.sh
@@ -0,0 +1,79 @@
+#!/bin/sh
+# vim:et:ft=sh:sts=2:sw=2
+#
+# Copyright 2008-2019 Kate Ward. All Rights Reserved.
+# Released under the Apache 2.0 license.
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# shUnit2 -- Unit testing framework for Unix shell scripts.
+# https://github.com/kward/shunit2
+#
+# Author: kate.ward@forestent.com (Kate Ward)
+#
+# Example unit test for the mkdir command.
+#
+# There are times when an existing shell script needs to be tested. In this
+# example, we will test several aspects of the the mkdir command, but the
+# techniques could be used for any existing shell script.
+
+testMissingDirectoryCreation() {
+ ${mkdirCmd} "${testDir}" >${stdoutF} 2>${stderrF}
+ rtrn=$?
+ th_assertTrueWithNoOutput ${rtrn} "${stdoutF}" "${stderrF}"
+
+ assertTrue 'directory missing' "[ -d '${testDir}' ]"
+}
+
+testExistingDirectoryCreationFails() {
+ # Create a directory to test against.
+ ${mkdirCmd} "${testDir}"
+
+ # Test for expected failure while trying to create directory that exists.
+ ${mkdirCmd} "${testDir}" >${stdoutF} 2>${stderrF}
+ rtrn=$?
+ assertFalse 'expecting return code of 1 (false)' ${rtrn}
+ assertNull 'unexpected output to stdout' "`cat ${stdoutF}`"
+ assertNotNull 'expected error message to stderr' "`cat ${stderrF}`"
+
+ assertTrue 'directory missing' "[ -d '${testDir}' ]"
+}
+
+testRecursiveDirectoryCreation() {
+ testDir2="${testDir}/test2"
+
+ ${mkdirCmd} -p "${testDir2}" >${stdoutF} 2>${stderrF}
+ rtrn=$?
+ th_assertTrueWithNoOutput ${rtrn} "${stdoutF}" "${stderrF}"
+
+ assertTrue 'first directory missing' "[ -d '${testDir}' ]"
+ assertTrue 'second directory missing' "[ -d '${testDir2}' ]"
+}
+
+th_assertTrueWithNoOutput() {
+ th_return_=$1
+ th_stdout_=$2
+ th_stderr_=$3
+
+ assertFalse 'unexpected output to STDOUT' "[ -s '${th_stdout_}' ]"
+ assertFalse 'unexpected output to STDERR' "[ -s '${th_stderr_}' ]"
+
+ unset th_return_ th_stdout_ th_stderr_
+}
+
+oneTimeSetUp() {
+ outputDir="${SHUNIT_TMPDIR}/output"
+ mkdir "${outputDir}"
+ stdoutF="${outputDir}/stdout"
+ stderrF="${outputDir}/stderr"
+
+ mkdirCmd='mkdir' # save command name in variable to make future changes easy
+ testDir="${SHUNIT_TMPDIR}/some_test_dir"
+}
+
+tearDown() {
+ rm -fr "${testDir}"
+}
+
+# Load and run shUnit2.
+[ -n "${ZSH_VERSION:-}" ] && SHUNIT_PARENT=$0
+. ../shunit2
diff --git a/fluent-bit/tests/lib/shunit2/examples/mock_file.sh b/fluent-bit/tests/lib/shunit2/examples/mock_file.sh
new file mode 100755
index 00000000..812e4488
--- /dev/null
+++ b/fluent-bit/tests/lib/shunit2/examples/mock_file.sh
@@ -0,0 +1,80 @@
+#!/bin/sh
+#
+# shUnit2 example for mocking files.
+#
+# This example demonstrates two different mechanisms for mocking files on the
+# system. The first method is preferred for testing specific aspects of a file,
+# and the second method is preferred when multiple tests need access to the
+# same mock data.
+#
+# When mocking files, the key thing of importance is providing the code under
+# test with the correct file to read. The best practice for writing code where
+# files need to be mocked is either:
+# - Pass the filename to be tested into a function and test that function, or
+# - Provide a function that returns the name of the filename to be read.
+#
+# The first case is preferred whenever possible as it allows the unit test to
+# be explicit about what is being tested. The second case is useful when the
+# first case is not achievable.
+#
+# For the second case, there are two common methods to mock the filename
+# returned by the function:
+# - Provide a special value (e.g. a mock variable) that is only available
+# during testing, or
+# - Override something (e.g. the constant) in the test script.
+#
+# The first case is preferred as it doesn't require the unit test to alter code
+# in any way. Yes, it means that the code itself knows that it is under test,
+# and it behaves slightly differently than under normal conditions, but a
+# visual inspection of the code by the developer should be sufficient to
+# validate proper functionality of such a simple function.
+
+# Treat unset variables as an error.
+set -u
+
+PASSWD='/etc/passwd'
+
+# Read the root UID from the passwd filename provided as the first argument.
+root_uid_from_passed_filename() {
+ filename=$1
+ root_uid "${filename}"
+ unset filename
+}
+
+
+# Read the root UID from the passwd filename derived by call to the
+# passwd_filename() function.
+root_uid_from_derived_filename() {
+ root_uid "$(passwd_filename)"
+}
+
+passwd_filename() {
+ if [ -n "${MOCK_PASSWD:-}" ]; then
+ echo "${MOCK_PASSWD}" # Mock file for testing.
+ return
+ fi
+ echo "${PASSWD}"
+}
+
+
+# Extract the root UID.
+root_uid() { awk -F: 'u==$1{print $3}' u=root "$1"; }
+
+
+main() {
+ echo "root_uid_from_passed_filename:"
+ root_uid_from_passed_filename "${PASSWD}"
+
+ echo
+
+ echo "root_uid_from_derived_filename:"
+ root_uid_from_derived_filename
+}
+
+
+# Execute main() if this is run in standalone mode (i.e. not in a unit test).
+ARGV0="$(basename "$0")"
+argv0="$(echo "${ARGV0}" |sed 's/_test$//;s/_test\.sh$//')"
+if [ "${ARGV0}" = "${argv0}" ]; then
+ main "$@"
+fi
diff --git a/fluent-bit/tests/lib/shunit2/examples/mock_file_test.sh b/fluent-bit/tests/lib/shunit2/examples/mock_file_test.sh
new file mode 100755
index 00000000..1da8dd2a
--- /dev/null
+++ b/fluent-bit/tests/lib/shunit2/examples/mock_file_test.sh
@@ -0,0 +1,33 @@
+#!/bin/sh
+#
+# shUnit2 example for mocking files.
+
+MOCK_PASSWD='' # This will be overridden in oneTimeSetUp().
+
+test_root_uid_from_passed_filename() {
+ result="$(root_uid_from_passed_filename "${MOCK_PASSWD}")"
+ assertEquals 'unexpected root uid' '0' "${result}"
+}
+
+test_root_uid_from_derived_filename() {
+ result="$(root_uid_from_derived_filename)"
+ assertEquals 'unexpected root uid' '0' "${result}"
+}
+
+oneTimeSetUp() {
+ # Provide a mock passwd file for testing. This will be cleaned up
+ # automatically by shUnit2.
+ MOCK_PASSWD="${SHUNIT_TMPDIR}/passwd"
+ cat <<EOF >"${MOCK_PASSWD}"
+nobody:*:-2:-2:Unprivileged User:/var/empty:/usr/bin/false
+root:*:0:0:System Administrator:/var/root:/bin/sh
+daemon:*:1:1:System Services:/var/root:/usr/bin/false
+EOF
+
+ # Load script under test.
+ . './mock_file.sh'
+}
+
+# Load and run shUnit2.
+[ -n "${ZSH_VERSION:-}" ] && SHUNIT_PARENT=$0
+. ../shunit2
diff --git a/fluent-bit/tests/lib/shunit2/examples/party_test.sh b/fluent-bit/tests/lib/shunit2/examples/party_test.sh
new file mode 100755
index 00000000..41bd124f
--- /dev/null
+++ b/fluent-bit/tests/lib/shunit2/examples/party_test.sh
@@ -0,0 +1,16 @@
+#! /bin/sh
+# file: examples/party_test.sh
+#
+# This test is mostly for fun. Technically, it is a bad example of a unit test
+# because of the temporal requirement, namely that the year be 1999. A better
+# test would have been to pass in both a known-bad and known-good year into a
+# function, and test for the expected result.
+
+testPartyLikeItIs1999() {
+ year=`date '+%Y'`
+ assertEquals "It's not 1999 :-(" \
+ '1999' "${year}"
+}
+
+# Load and run shUnit2.
+. ../shunit2
diff --git a/fluent-bit/tests/lib/shunit2/examples/suite_test.sh b/fluent-bit/tests/lib/shunit2/examples/suite_test.sh
new file mode 100755
index 00000000..278c3a01
--- /dev/null
+++ b/fluent-bit/tests/lib/shunit2/examples/suite_test.sh
@@ -0,0 +1,32 @@
+#!/bin/sh
+# file: examples/suite_test.sh
+#
+# This test demonstrates the use of suites. Note: the suite functionality is
+# deprecated as of v2.1.0, and will be removed in a future major release.
+
+# suite is a special function called by shUnit2 to setup a suite of tests. It
+# enables a developer to call a set of functions that contain tests without
+# needing to rename the functions to start with "test".
+#
+# Tests that are to be called from within `suite()` are added to the list of
+# executable tests by means of the `suite_addTest()` function.
+suite() {
+ # Add the suite_test_one() function to the list of executable tests.
+ suite_addTest suite_test_one
+
+ # Call the suite_test_two() function, but note that the test results will not
+ # be added to the global stats, and therefore not reported at the end of the
+ # unit test execution.
+ suite_test_two
+}
+
+suite_test_one() {
+ assertEquals 1 1
+}
+
+suite_test_two() {
+ assertNotEquals 1 2
+}
+
+# Load and run shUnit2.
+. ../shunit2
diff --git a/fluent-bit/tests/lib/shunit2/lib/shflags b/fluent-bit/tests/lib/shunit2/lib/shflags
new file mode 100644
index 00000000..70cdea4d
--- /dev/null
+++ b/fluent-bit/tests/lib/shunit2/lib/shflags
@@ -0,0 +1,1222 @@
+# vim:et:ft=sh:sts=2:sw=2
+#
+# Copyright 2008-2017 Kate Ward. All Rights Reserved.
+# Released under the Apache License 2.0 license.
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# shFlags -- Advanced command-line flag library for Unix shell scripts.
+# https://github.com/kward/shflags
+#
+# Author: kate.ward@forestent.com (Kate Ward)
+#
+# This module implements something like the gflags library available
+# from https://github.com/gflags/gflags.
+#
+# FLAG TYPES: This is a list of the DEFINE_*'s that you can do. All flags take
+# a name, default value, help-string, and optional 'short' name (one-letter
+# name). Some flags have other arguments, which are described with the flag.
+#
+# DEFINE_string: takes any input, and interprets it as a string.
+#
+# DEFINE_boolean: does not take any arguments. Say --myflag to set
+# FLAGS_myflag to true, or --nomyflag to set FLAGS_myflag to false. For short
+# flags, passing the flag on the command-line negates the default value, i.e.
+# if the default is true, passing the flag sets the value to false.
+#
+# DEFINE_float: takes an input and interprets it as a floating point number. As
+# shell does not support floats per-se, the input is merely validated as
+# being a valid floating point value.
+#
+# DEFINE_integer: takes an input and interprets it as an integer.
+#
+# SPECIAL FLAGS: There are a few flags that have special meaning:
+# --help (or -?) prints a list of all the flags in a human-readable fashion
+# --flagfile=foo read flags from foo. (not implemented yet)
+# -- as in getopt(), terminates flag-processing
+#
+# EXAMPLE USAGE:
+#
+# -- begin hello.sh --
+# #! /bin/sh
+# . ./shflags
+# DEFINE_string name 'world' "somebody's name" n
+# FLAGS "$@" || exit $?
+# eval set -- "${FLAGS_ARGV}"
+# echo "Hello, ${FLAGS_name}."
+# -- end hello.sh --
+#
+# $ ./hello.sh -n Kate
+# Hello, Kate.
+#
+# CUSTOMIZABLE BEHAVIOR:
+#
+# A script can override the default 'getopt' command by providing the path to
+# an alternate implementation by defining the FLAGS_GETOPT_CMD variable.
+#
+# NOTES:
+#
+# * Not all systems include a getopt version that supports long flags. On these
+# systems, only short flags are recognized.
+
+#==============================================================================
+# shFlags
+#
+# Shared attributes:
+# flags_error: last error message
+# flags_output: last function output (rarely valid)
+# flags_return: last return value
+#
+# __flags_longNames: list of long names for all flags
+# __flags_shortNames: list of short names for all flags
+# __flags_boolNames: list of boolean flag names
+#
+# __flags_opts: options parsed by getopt
+#
+# Per-flag attributes:
+# FLAGS_<flag_name>: contains value of flag named 'flag_name'
+# __flags_<flag_name>_default: the default flag value
+# __flags_<flag_name>_help: the flag help string
+# __flags_<flag_name>_short: the flag short name
+# __flags_<flag_name>_type: the flag type
+#
+# Notes:
+# - lists of strings are space separated, and a null value is the '~' char.
+#
+### ShellCheck (http://www.shellcheck.net/)
+# $() are not fully portable (POSIX != portable).
+# shellcheck disable=SC2006
+# [ p -a q ] are well defined enough (vs [ p ] && [ q ]).
+# shellcheck disable=SC2166
+
+# Return if FLAGS already loaded.
+[ -n "${FLAGS_VERSION:-}" ] && return 0
+FLAGS_VERSION='1.2.3pre'
+
+# Return values that scripts can use.
+FLAGS_TRUE=0
+FLAGS_FALSE=1
+FLAGS_ERROR=2
+
+# Logging levels.
+FLAGS_LEVEL_DEBUG=0
+FLAGS_LEVEL_INFO=1
+FLAGS_LEVEL_WARN=2
+FLAGS_LEVEL_ERROR=3
+FLAGS_LEVEL_FATAL=4
+__FLAGS_LEVEL_DEFAULT=${FLAGS_LEVEL_WARN}
+
+# Determine some reasonable command defaults.
+__FLAGS_EXPR_CMD='expr --'
+__FLAGS_UNAME_S=`uname -s`
+if [ "${__FLAGS_UNAME_S}" = 'BSD' ]; then
+ __FLAGS_EXPR_CMD='gexpr --'
+else
+ _flags_output_=`${__FLAGS_EXPR_CMD} 2>&1`
+ if [ $? -eq ${FLAGS_TRUE} -a "${_flags_output_}" = '--' ]; then
+ # We are likely running inside BusyBox.
+ __FLAGS_EXPR_CMD='expr'
+ fi
+ unset _flags_output_
+fi
+
+# Commands a user can override if desired.
+FLAGS_EXPR_CMD=${FLAGS_EXPR_CMD:-${__FLAGS_EXPR_CMD}}
+FLAGS_GETOPT_CMD=${FLAGS_GETOPT_CMD:-getopt}
+
+# Specific shell checks.
+if [ -n "${ZSH_VERSION:-}" ]; then
+ setopt |grep "^shwordsplit$" >/dev/null
+ if [ $? -ne ${FLAGS_TRUE} ]; then
+ _flags_fatal 'zsh shwordsplit option is required for proper zsh operation'
+ fi
+ if [ -z "${FLAGS_PARENT:-}" ]; then
+ _flags_fatal "zsh does not pass \$0 through properly. please declare' \
+\"FLAGS_PARENT=\$0\" before calling shFlags"
+ fi
+fi
+
+# Can we use built-ins?
+( echo "${FLAGS_TRUE#0}"; ) >/dev/null 2>&1
+if [ $? -eq ${FLAGS_TRUE} ]; then
+ __FLAGS_USE_BUILTIN=${FLAGS_TRUE}
+else
+ __FLAGS_USE_BUILTIN=${FLAGS_FALSE}
+fi
+
+
+#
+# Constants.
+#
+
+# Reserved flag names.
+__FLAGS_RESERVED_LIST=' ARGC ARGV ERROR FALSE GETOPT_CMD HELP PARENT TRUE '
+__FLAGS_RESERVED_LIST="${__FLAGS_RESERVED_LIST} VERSION "
+
+# Determined getopt version (standard or enhanced).
+__FLAGS_GETOPT_VERS_STD=0
+__FLAGS_GETOPT_VERS_ENH=1
+
+# shellcheck disable=SC2120
+_flags_getopt_vers() {
+ _flags_getopt_cmd_=${1:-${FLAGS_GETOPT_CMD}}
+ case "`${_flags_getopt_cmd_} -lfoo '' --foo 2>&1`" in
+ ' -- --foo') echo ${__FLAGS_GETOPT_VERS_STD} ;;
+ ' --foo --') echo ${__FLAGS_GETOPT_VERS_ENH} ;;
+ # Unrecognized output. Assuming standard getopt version.
+ *) echo ${__FLAGS_GETOPT_VERS_STD} ;;
+ esac
+ unset _flags_getopt_cmd_
+}
+# shellcheck disable=SC2119
+__FLAGS_GETOPT_VERS=`_flags_getopt_vers`
+
+# getopt optstring lengths
+__FLAGS_OPTSTR_SHORT=0
+__FLAGS_OPTSTR_LONG=1
+
+__FLAGS_NULL='~'
+
+# Flag info strings.
+__FLAGS_INFO_DEFAULT='default'
+__FLAGS_INFO_HELP='help'
+__FLAGS_INFO_SHORT='short'
+__FLAGS_INFO_TYPE='type'
+
+# Flag lengths.
+__FLAGS_LEN_SHORT=0
+__FLAGS_LEN_LONG=1
+
+# Flag types.
+__FLAGS_TYPE_NONE=0
+__FLAGS_TYPE_BOOLEAN=1
+__FLAGS_TYPE_FLOAT=2
+__FLAGS_TYPE_INTEGER=3
+__FLAGS_TYPE_STRING=4
+
+# Set the constants readonly.
+__flags_constants=`set |awk -F= '/^FLAGS_/ || /^__FLAGS_/ {print $1}'`
+for __flags_const in ${__flags_constants}; do
+ # Skip certain flags.
+ case ${__flags_const} in
+ FLAGS_HELP) continue ;;
+ FLAGS_PARENT) continue ;;
+ esac
+ # Set flag readonly.
+ if [ -z "${ZSH_VERSION:-}" ]; then
+ readonly "${__flags_const}"
+ continue
+ fi
+ case ${ZSH_VERSION} in
+ [123].*) readonly "${__flags_const}" ;;
+ *) readonly -g "${__flags_const}" ;; # Declare readonly constants globally.
+ esac
+done
+unset __flags_const __flags_constants
+
+#
+# Internal variables.
+#
+
+# Space separated lists.
+__flags_boolNames=' ' # Boolean flag names.
+__flags_longNames=' ' # Long flag names.
+__flags_shortNames=' ' # Short flag names.
+__flags_definedNames=' ' # Defined flag names (used for validation).
+
+__flags_columns='' # Screen width in columns.
+__flags_level=0 # Default logging level.
+__flags_opts='' # Temporary storage for parsed getopt flags.
+
+#------------------------------------------------------------------------------
+# Private functions.
+#
+
+# Logging functions.
+_flags_debug() {
+ [ ${__flags_level} -le ${FLAGS_LEVEL_DEBUG} ] || return
+ echo "flags:DEBUG $*" >&2
+}
+_flags_info() {
+ [ ${__flags_level} -le ${FLAGS_LEVEL_INFO} ] || return
+ echo "flags:INFO $*" >&2
+}
+_flags_warn() {
+ [ ${__flags_level} -le ${FLAGS_LEVEL_WARN} ] || return
+ echo "flags:WARN $*" >&2
+}
+_flags_error() {
+ [ ${__flags_level} -le ${FLAGS_LEVEL_ERROR} ] || return
+ echo "flags:ERROR $*" >&2
+}
+_flags_fatal() {
+ [ ${__flags_level} -le ${FLAGS_LEVEL_FATAL} ] || return
+ echo "flags:FATAL $*" >&2
+ exit ${FLAGS_ERROR}
+}
+
+# Get the logging level.
+flags_loggingLevel() { echo ${__flags_level}; }
+
+# Set the logging level.
+#
+# Args:
+# _flags_level_: integer: new logging level
+# Returns:
+# nothing
+flags_setLoggingLevel() {
+ [ $# -ne 1 ] && _flags_fatal "flags_setLevel(): logging level missing"
+ _flags_level_=$1
+ [ "${_flags_level_}" -ge "${FLAGS_LEVEL_DEBUG}" \
+ -a "${_flags_level_}" -le "${FLAGS_LEVEL_FATAL}" ] \
+ || _flags_fatal "Invalid logging level '${_flags_level_}' specified."
+ __flags_level=$1
+ unset _flags_level_
+}
+
+# Define a flag.
+#
+# Calling this function will define the following info variables for the
+# specified flag:
+# FLAGS_flagname - the name for this flag (based upon the long flag name)
+# __flags_<flag_name>_default - the default value
+# __flags_flagname_help - the help string
+# __flags_flagname_short - the single letter alias
+# __flags_flagname_type - the type of flag (one of __FLAGS_TYPE_*)
+#
+# Args:
+# _flags_type_: integer: internal type of flag (__FLAGS_TYPE_*)
+# _flags_name_: string: long flag name
+# _flags_default_: default flag value
+# _flags_help_: string: help string
+# _flags_short_: string: (optional) short flag name
+# Returns:
+# integer: success of operation, or error
+_flags_define() {
+ if [ $# -lt 4 ]; then
+ flags_error='DEFINE error: too few arguments'
+ flags_return=${FLAGS_ERROR}
+ _flags_error "${flags_error}"
+ return ${flags_return}
+ fi
+
+ _flags_type_=$1
+ _flags_name_=$2
+ _flags_default_=$3
+ _flags_help_=${4:-§} # Special value '§' indicates no help string provided.
+ _flags_short_=${5:-${__FLAGS_NULL}}
+
+ _flags_debug "type:${_flags_type_} name:${_flags_name_}" \
+ "default:'${_flags_default_}' help:'${_flags_help_}'" \
+ "short:${_flags_short_}"
+
+ _flags_return_=${FLAGS_TRUE}
+ _flags_usName_="`_flags_underscoreName "${_flags_name_}"`"
+
+ # Check whether the flag name is reserved.
+ _flags_itemInList "${_flags_usName_}" "${__FLAGS_RESERVED_LIST}"
+ if [ $? -eq ${FLAGS_TRUE} ]; then
+ flags_error="flag name (${_flags_name_}) is reserved"
+ _flags_return_=${FLAGS_ERROR}
+ fi
+
+ # Require short option for getopt that don't support long options.
+ if [ ${_flags_return_} -eq ${FLAGS_TRUE} \
+ -a "${__FLAGS_GETOPT_VERS}" -ne "${__FLAGS_GETOPT_VERS_ENH}" \
+ -a "${_flags_short_}" = "${__FLAGS_NULL}" ]
+ then
+ flags_error="short flag required for (${_flags_name_}) on this platform"
+ _flags_return_=${FLAGS_ERROR}
+ fi
+
+ # Check for existing long name definition.
+ if [ ${_flags_return_} -eq ${FLAGS_TRUE} ]; then
+ if _flags_itemInList "${_flags_usName_}" "${__flags_definedNames}"; then
+ flags_error="definition for ([no]${_flags_name_}) already exists"
+ _flags_warn "${flags_error}"
+ _flags_return_=${FLAGS_FALSE}
+ fi
+ fi
+
+ # Check for existing short name definition.
+ if [ ${_flags_return_} -eq ${FLAGS_TRUE} \
+ -a "${_flags_short_}" != "${__FLAGS_NULL}" ]
+ then
+ if _flags_itemInList "${_flags_short_}" "${__flags_shortNames}"; then
+ flags_error="flag short name (${_flags_short_}) already defined"
+ _flags_warn "${flags_error}"
+ _flags_return_=${FLAGS_FALSE}
+ fi
+ fi
+
+ # Handle default value. Note, on several occasions the 'if' portion of an
+ # if/then/else contains just a ':' which does nothing. A binary reversal via
+ # '!' is not done because it does not work on all shells.
+ if [ ${_flags_return_} -eq ${FLAGS_TRUE} ]; then
+ case ${_flags_type_} in
+ ${__FLAGS_TYPE_BOOLEAN})
+ if _flags_validBool "${_flags_default_}"; then
+ case ${_flags_default_} in
+ true|t|0) _flags_default_=${FLAGS_TRUE} ;;
+ false|f|1) _flags_default_=${FLAGS_FALSE} ;;
+ esac
+ else
+ flags_error="invalid default flag value '${_flags_default_}'"
+ _flags_return_=${FLAGS_ERROR}
+ fi
+ ;;
+
+ ${__FLAGS_TYPE_FLOAT})
+ if _flags_validFloat "${_flags_default_}"; then
+ :
+ else
+ flags_error="invalid default flag value '${_flags_default_}'"
+ _flags_return_=${FLAGS_ERROR}
+ fi
+ ;;
+
+ ${__FLAGS_TYPE_INTEGER})
+ if _flags_validInt "${_flags_default_}"; then
+ :
+ else
+ flags_error="invalid default flag value '${_flags_default_}'"
+ _flags_return_=${FLAGS_ERROR}
+ fi
+ ;;
+
+ ${__FLAGS_TYPE_STRING}) ;; # Everything in shell is a valid string.
+
+ *)
+ flags_error="unrecognized flag type '${_flags_type_}'"
+ _flags_return_=${FLAGS_ERROR}
+ ;;
+ esac
+ fi
+
+ if [ ${_flags_return_} -eq ${FLAGS_TRUE} ]; then
+ # Store flag information.
+ eval "FLAGS_${_flags_usName_}='${_flags_default_}'"
+ eval "__flags_${_flags_usName_}_${__FLAGS_INFO_TYPE}=${_flags_type_}"
+ eval "__flags_${_flags_usName_}_${__FLAGS_INFO_DEFAULT}=\
+\"${_flags_default_}\""
+ eval "__flags_${_flags_usName_}_${__FLAGS_INFO_HELP}=\"${_flags_help_}\""
+ eval "__flags_${_flags_usName_}_${__FLAGS_INFO_SHORT}='${_flags_short_}'"
+
+ # append flag names to name lists
+ __flags_shortNames="${__flags_shortNames}${_flags_short_} "
+ __flags_longNames="${__flags_longNames}${_flags_name_} "
+ [ "${_flags_type_}" -eq "${__FLAGS_TYPE_BOOLEAN}" ] && \
+ __flags_boolNames="${__flags_boolNames}no${_flags_name_} "
+
+ # Append flag names to defined names for later validation checks.
+ __flags_definedNames="${__flags_definedNames}${_flags_usName_} "
+ [ "${_flags_type_}" -eq "${__FLAGS_TYPE_BOOLEAN}" ] && \
+ __flags_definedNames="${__flags_definedNames}no${_flags_usName_} "
+ fi
+
+ flags_return=${_flags_return_}
+ unset _flags_default_ _flags_help_ _flags_name_ _flags_return_ \
+ _flags_short_ _flags_type_ _flags_usName_
+ [ ${flags_return} -eq ${FLAGS_ERROR} ] && _flags_error "${flags_error}"
+ return ${flags_return}
+}
+
+# Underscore a flag name by replacing dashes with underscores.
+#
+# Args:
+# unnamed: string: log flag name
+# Output:
+# string: underscored name
+_flags_underscoreName() {
+ echo "$1" |tr '-' '_'
+}
+
+# Return valid getopt options using currently defined list of long options.
+#
+# This function builds a proper getopt option string for short (and long)
+# options, using the current list of long options for reference.
+#
+# Args:
+# _flags_optStr: integer: option string type (__FLAGS_OPTSTR_*)
+# Output:
+# string: generated option string for getopt
+# Returns:
+# boolean: success of operation (always returns True)
+_flags_genOptStr() {
+ _flags_optStrType_=$1
+
+ _flags_opts_=''
+
+ for _flags_name_ in ${__flags_longNames}; do
+ _flags_usName_="`_flags_underscoreName "${_flags_name_}"`"
+ _flags_type_="`_flags_getFlagInfo "${_flags_usName_}" "${__FLAGS_INFO_TYPE}"`"
+ [ $? -eq ${FLAGS_TRUE} ] || _flags_fatal 'call to _flags_type_ failed'
+ case ${_flags_optStrType_} in
+ ${__FLAGS_OPTSTR_SHORT})
+ _flags_shortName_="`_flags_getFlagInfo \
+ "${_flags_usName_}" "${__FLAGS_INFO_SHORT}"`"
+ if [ "${_flags_shortName_}" != "${__FLAGS_NULL}" ]; then
+ _flags_opts_="${_flags_opts_}${_flags_shortName_}"
+ # getopt needs a trailing ':' to indicate a required argument.
+ [ "${_flags_type_}" -ne "${__FLAGS_TYPE_BOOLEAN}" ] && \
+ _flags_opts_="${_flags_opts_}:"
+ fi
+ ;;
+
+ ${__FLAGS_OPTSTR_LONG})
+ _flags_opts_="${_flags_opts_:+${_flags_opts_},}${_flags_name_}"
+ # getopt needs a trailing ':' to indicate a required argument
+ [ "${_flags_type_}" -ne "${__FLAGS_TYPE_BOOLEAN}" ] && \
+ _flags_opts_="${_flags_opts_}:"
+ ;;
+ esac
+ done
+
+ echo "${_flags_opts_}"
+ unset _flags_name_ _flags_opts_ _flags_optStrType_ _flags_shortName_ \
+ _flags_type_ _flags_usName_
+ return ${FLAGS_TRUE}
+}
+
+# Returns flag details based on a flag name and flag info.
+#
+# Args:
+# string: underscored flag name
+# string: flag info (see the _flags_define function for valid info types)
+# Output:
+# string: value of dereferenced flag variable
+# Returns:
+# integer: one of FLAGS_{TRUE|FALSE|ERROR}
+_flags_getFlagInfo() {
+ # Note: adding gFI to variable names to prevent naming conflicts with calling
+ # functions
+ _flags_gFI_usName_=$1
+ _flags_gFI_info_=$2
+
+ # Example: given argument usName (underscored flag name) of 'my_flag', and
+ # argument info of 'help', set the _flags_infoValue_ variable to the value of
+ # ${__flags_my_flag_help}, and see if it is non-empty.
+ _flags_infoVar_="__flags_${_flags_gFI_usName_}_${_flags_gFI_info_}"
+ _flags_strToEval_="_flags_infoValue_=\"\${${_flags_infoVar_}:-}\""
+ eval "${_flags_strToEval_}"
+ if [ -n "${_flags_infoValue_}" ]; then
+ # Special value '§' indicates no help string provided.
+ [ "${_flags_gFI_info_}" = ${__FLAGS_INFO_HELP} \
+ -a "${_flags_infoValue_}" = '§' ] && _flags_infoValue_=''
+ flags_return=${FLAGS_TRUE}
+ else
+ # See if the _flags_gFI_usName_ variable is a string as strings can be
+ # empty...
+ # Note: the DRY principle would say to have this function call itself for
+ # the next three lines, but doing so results in an infinite loop as an
+ # invalid _flags_name_ will also not have the associated _type variable.
+ # Because it doesn't (it will evaluate to an empty string) the logic will
+ # try to find the _type variable of the _type variable, and so on. Not so
+ # good ;-)
+ #
+ # Example cont.: set the _flags_typeValue_ variable to the value of
+ # ${__flags_my_flag_type}, and see if it equals '4'.
+ _flags_typeVar_="__flags_${_flags_gFI_usName_}_${__FLAGS_INFO_TYPE}"
+ _flags_strToEval_="_flags_typeValue_=\"\${${_flags_typeVar_}:-}\""
+ eval "${_flags_strToEval_}"
+ # shellcheck disable=SC2154
+ if [ "${_flags_typeValue_}" = "${__FLAGS_TYPE_STRING}" ]; then
+ flags_return=${FLAGS_TRUE}
+ else
+ flags_return=${FLAGS_ERROR}
+ flags_error="missing flag info variable (${_flags_infoVar_})"
+ fi
+ fi
+
+ echo "${_flags_infoValue_}"
+ unset _flags_gFI_usName_ _flags_gfI_info_ _flags_infoValue_ _flags_infoVar_ \
+ _flags_strToEval_ _flags_typeValue_ _flags_typeVar_
+ [ ${flags_return} -eq ${FLAGS_ERROR} ] && _flags_error "${flags_error}"
+ return ${flags_return}
+}
+
+# Check for presence of item in a list.
+#
+# Passed a string (e.g. 'abc'), this function will determine if the string is
+# present in the list of strings (e.g. ' foo bar abc ').
+#
+# Args:
+# _flags_str_: string: string to search for in a list of strings
+# unnamed: list: list of strings
+# Returns:
+# boolean: true if item is in the list
+_flags_itemInList() {
+ _flags_str_=$1
+ shift
+
+ case " ${*:-} " in
+ *\ ${_flags_str_}\ *) flags_return=${FLAGS_TRUE} ;;
+ *) flags_return=${FLAGS_FALSE} ;;
+ esac
+
+ unset _flags_str_
+ return ${flags_return}
+}
+
+# Returns the width of the current screen.
+#
+# Output:
+# integer: width in columns of the current screen.
+_flags_columns() {
+ if [ -z "${__flags_columns}" ]; then
+ if eval stty size >/dev/null 2>&1; then
+ # stty size worked :-)
+ # shellcheck disable=SC2046
+ set -- `stty size`
+ __flags_columns="${2:-}"
+ fi
+ fi
+ if [ -z "${__flags_columns}" ]; then
+ if eval tput cols >/dev/null 2>&1; then
+ # shellcheck disable=SC2046
+ set -- `tput cols`
+ __flags_columns="${1:-}"
+ fi
+ fi
+ echo "${__flags_columns:-80}"
+}
+
+# Validate a boolean.
+#
+# Args:
+# _flags__bool: boolean: value to validate
+# Returns:
+# bool: true if the value is a valid boolean
+_flags_validBool() {
+ _flags_bool_=$1
+
+ flags_return=${FLAGS_TRUE}
+ case "${_flags_bool_}" in
+ true|t|0) ;;
+ false|f|1) ;;
+ *) flags_return=${FLAGS_FALSE} ;;
+ esac
+
+ unset _flags_bool_
+ return ${flags_return}
+}
+
+# Validate a float.
+#
+# Args:
+# _flags_float_: float: value to validate
+# Returns:
+# bool: true if the value is a valid integer
+_flags_validFloat() {
+ flags_return=${FLAGS_FALSE}
+ [ -n "$1" ] || return ${flags_return}
+ _flags_float_=$1
+
+ if _flags_validInt "${_flags_float_}"; then
+ flags_return=${FLAGS_TRUE}
+ elif _flags_useBuiltin; then
+ _flags_float_whole_=${_flags_float_%.*}
+ _flags_float_fraction_=${_flags_float_#*.}
+ if _flags_validInt "${_flags_float_whole_:-0}" -a \
+ _flags_validInt "${_flags_float_fraction_}"; then
+ flags_return=${FLAGS_TRUE}
+ fi
+ unset _flags_float_whole_ _flags_float_fraction_
+ else
+ flags_return=${FLAGS_TRUE}
+ case ${_flags_float_} in
+ -*) # Negative floats.
+ _flags_test_=`${FLAGS_EXPR_CMD} "${_flags_float_}" :\
+ '\(-[0-9]*\.[0-9]*\)'`
+ ;;
+ *) # Positive floats.
+ _flags_test_=`${FLAGS_EXPR_CMD} "${_flags_float_}" :\
+ '\([0-9]*\.[0-9]*\)'`
+ ;;
+ esac
+ [ "${_flags_test_}" != "${_flags_float_}" ] && flags_return=${FLAGS_FALSE}
+ unset _flags_test_
+ fi
+
+ unset _flags_float_ _flags_float_whole_ _flags_float_fraction_
+ return ${flags_return}
+}
+
+# Validate an integer.
+#
+# Args:
+# _flags_int_: integer: value to validate
+# Returns:
+# bool: true if the value is a valid integer
+_flags_validInt() {
+ flags_return=${FLAGS_FALSE}
+ [ -n "$1" ] || return ${flags_return}
+ _flags_int_=$1
+
+ case ${_flags_int_} in
+ -*.*) ;; # Ignore negative floats (we'll invalidate them later).
+ -*) # Strip possible leading negative sign.
+ if _flags_useBuiltin; then
+ _flags_int_=${_flags_int_#-}
+ else
+ _flags_int_=`${FLAGS_EXPR_CMD} "${_flags_int_}" : '-\([0-9][0-9]*\)'`
+ fi
+ ;;
+ esac
+
+ case ${_flags_int_} in
+ *[!0-9]*) flags_return=${FLAGS_FALSE} ;;
+ *) flags_return=${FLAGS_TRUE} ;;
+ esac
+
+ unset _flags_int_
+ return ${flags_return}
+}
+
+# Parse command-line options using the standard getopt.
+#
+# Note: the flag options are passed around in the global __flags_opts so that
+# the formatting is not lost due to shell parsing and such.
+#
+# Args:
+# @: varies: command-line options to parse
+# Returns:
+# integer: a FLAGS success condition
+_flags_getoptStandard() {
+ flags_return=${FLAGS_TRUE}
+ _flags_shortOpts_=`_flags_genOptStr ${__FLAGS_OPTSTR_SHORT}`
+
+ # Check for spaces in passed options.
+ for _flags_opt_ in "$@"; do
+ # Note: the silliness with the x's is purely for ksh93 on Ubuntu 6.06.
+ _flags_match_=`echo "x${_flags_opt_}x" |sed 's/ //g'`
+ if [ "${_flags_match_}" != "x${_flags_opt_}x" ]; then
+ flags_error='the available getopt does not support spaces in options'
+ flags_return=${FLAGS_ERROR}
+ break
+ fi
+ done
+
+ if [ ${flags_return} -eq ${FLAGS_TRUE} ]; then
+ __flags_opts=`getopt "${_flags_shortOpts_}" "$@" 2>&1`
+ _flags_rtrn_=$?
+ if [ ${_flags_rtrn_} -ne ${FLAGS_TRUE} ]; then
+ _flags_warn "${__flags_opts}"
+ flags_error='unable to parse provided options with getopt.'
+ flags_return=${FLAGS_ERROR}
+ fi
+ fi
+
+ unset _flags_match_ _flags_opt_ _flags_rtrn_ _flags_shortOpts_
+ return ${flags_return}
+}
+
+# Parse command-line options using the enhanced getopt.
+#
+# Note: the flag options are passed around in the global __flags_opts so that
+# the formatting is not lost due to shell parsing and such.
+#
+# Args:
+# @: varies: command-line options to parse
+# Returns:
+# integer: a FLAGS success condition
+_flags_getoptEnhanced() {
+ flags_return=${FLAGS_TRUE}
+ _flags_shortOpts_=`_flags_genOptStr ${__FLAGS_OPTSTR_SHORT}`
+ _flags_boolOpts_=`echo "${__flags_boolNames}" \
+ |sed 's/^ *//;s/ *$//;s/ /,/g'`
+ _flags_longOpts_=`_flags_genOptStr ${__FLAGS_OPTSTR_LONG}`
+
+ __flags_opts=`${FLAGS_GETOPT_CMD} \
+ -o "${_flags_shortOpts_}" \
+ -l "${_flags_longOpts_},${_flags_boolOpts_}" \
+ -- "$@" 2>&1`
+ _flags_rtrn_=$?
+ if [ ${_flags_rtrn_} -ne ${FLAGS_TRUE} ]; then
+ _flags_warn "${__flags_opts}"
+ flags_error='unable to parse provided options with getopt.'
+ flags_return=${FLAGS_ERROR}
+ fi
+
+ unset _flags_boolOpts_ _flags_longOpts_ _flags_rtrn_ _flags_shortOpts_
+ return ${flags_return}
+}
+
+# Dynamically parse a getopt result and set appropriate variables.
+#
+# This function does the actual conversion of getopt output and runs it through
+# the standard case structure for parsing. The case structure is actually quite
+# dynamic to support any number of flags.
+#
+# Args:
+# argc: int: original command-line argument count
+# @: varies: output from getopt parsing
+# Returns:
+# integer: a FLAGS success condition
+_flags_parseGetopt() {
+ _flags_argc_=$1
+ shift
+
+ flags_return=${FLAGS_TRUE}
+
+ if [ "${__FLAGS_GETOPT_VERS}" -ne "${__FLAGS_GETOPT_VERS_ENH}" ]; then
+ # The @$ must be unquoted as it needs to be re-split.
+ # shellcheck disable=SC2068
+ set -- $@
+ else
+ # Note the quotes around the `$@' -- they are essential!
+ eval set -- "$@"
+ fi
+
+ # Provide user with the number of arguments to shift by later.
+ # NOTE: the FLAGS_ARGC variable is obsolete as of 1.0.3 because it does not
+ # properly give user access to non-flag arguments mixed in between flag
+ # arguments. Its usage was replaced by FLAGS_ARGV, and it is being kept only
+ # for backwards compatibility reasons.
+ FLAGS_ARGC=`_flags_math "$# - 1 - ${_flags_argc_}"`
+ export FLAGS_ARGC
+
+ # Handle options. note options with values must do an additional shift.
+ while true; do
+ _flags_opt_=$1
+ _flags_arg_=${2:-}
+ _flags_type_=${__FLAGS_TYPE_NONE}
+ _flags_name_=''
+
+ # Determine long flag name.
+ case "${_flags_opt_}" in
+ --) shift; break ;; # Discontinue option parsing.
+
+ --*) # Long option.
+ if _flags_useBuiltin; then
+ _flags_opt_=${_flags_opt_#*--}
+ else
+ _flags_opt_=`${FLAGS_EXPR_CMD} "${_flags_opt_}" : '--\(.*\)'`
+ fi
+ _flags_len_=${__FLAGS_LEN_LONG}
+ if _flags_itemInList "${_flags_opt_}" "${__flags_longNames}"; then
+ _flags_name_=${_flags_opt_}
+ else
+ # Check for negated long boolean version.
+ if _flags_itemInList "${_flags_opt_}" "${__flags_boolNames}"; then
+ if _flags_useBuiltin; then
+ _flags_name_=${_flags_opt_#*no}
+ else
+ _flags_name_=`${FLAGS_EXPR_CMD} "${_flags_opt_}" : 'no\(.*\)'`
+ fi
+ _flags_type_=${__FLAGS_TYPE_BOOLEAN}
+ _flags_arg_=${__FLAGS_NULL}
+ fi
+ fi
+ ;;
+
+ -*) # Short option.
+ if _flags_useBuiltin; then
+ _flags_opt_=${_flags_opt_#*-}
+ else
+ _flags_opt_=`${FLAGS_EXPR_CMD} "${_flags_opt_}" : '-\(.*\)'`
+ fi
+ _flags_len_=${__FLAGS_LEN_SHORT}
+ if _flags_itemInList "${_flags_opt_}" "${__flags_shortNames}"; then
+ # Yes. Match short name to long name. Note purposeful off-by-one
+ # (too high) with awk calculations.
+ _flags_pos_=`echo "${__flags_shortNames}" \
+ |awk 'BEGIN{RS=" ";rn=0}$0==e{rn=NR}END{print rn}' \
+ e="${_flags_opt_}"`
+ _flags_name_=`echo "${__flags_longNames}" \
+ |awk 'BEGIN{RS=" "}rn==NR{print $0}' rn="${_flags_pos_}"`
+ fi
+ ;;
+ esac
+
+ # Die if the flag was unrecognized.
+ if [ -z "${_flags_name_}" ]; then
+ flags_error="unrecognized option (${_flags_opt_})"
+ flags_return=${FLAGS_ERROR}
+ break
+ fi
+
+ # Set new flag value.
+ _flags_usName_=`_flags_underscoreName "${_flags_name_}"`
+ [ ${_flags_type_} -eq ${__FLAGS_TYPE_NONE} ] && \
+ _flags_type_=`_flags_getFlagInfo \
+ "${_flags_usName_}" ${__FLAGS_INFO_TYPE}`
+ case ${_flags_type_} in
+ ${__FLAGS_TYPE_BOOLEAN})
+ if [ ${_flags_len_} -eq ${__FLAGS_LEN_LONG} ]; then
+ if [ "${_flags_arg_}" != "${__FLAGS_NULL}" ]; then
+ eval "FLAGS_${_flags_usName_}=${FLAGS_TRUE}"
+ else
+ eval "FLAGS_${_flags_usName_}=${FLAGS_FALSE}"
+ fi
+ else
+ _flags_strToEval_="_flags_val_=\
+\${__flags_${_flags_usName_}_${__FLAGS_INFO_DEFAULT}}"
+ eval "${_flags_strToEval_}"
+ # shellcheck disable=SC2154
+ if [ "${_flags_val_}" -eq ${FLAGS_FALSE} ]; then
+ eval "FLAGS_${_flags_usName_}=${FLAGS_TRUE}"
+ else
+ eval "FLAGS_${_flags_usName_}=${FLAGS_FALSE}"
+ fi
+ fi
+ ;;
+
+ ${__FLAGS_TYPE_FLOAT})
+ if _flags_validFloat "${_flags_arg_}"; then
+ eval "FLAGS_${_flags_usName_}='${_flags_arg_}'"
+ else
+ flags_error="invalid float value (${_flags_arg_})"
+ flags_return=${FLAGS_ERROR}
+ break
+ fi
+ ;;
+
+ ${__FLAGS_TYPE_INTEGER})
+ if _flags_validInt "${_flags_arg_}"; then
+ eval "FLAGS_${_flags_usName_}='${_flags_arg_}'"
+ else
+ flags_error="invalid integer value (${_flags_arg_})"
+ flags_return=${FLAGS_ERROR}
+ break
+ fi
+ ;;
+
+ ${__FLAGS_TYPE_STRING})
+ eval "FLAGS_${_flags_usName_}='${_flags_arg_}'"
+ ;;
+ esac
+
+ # Handle special case help flag.
+ if [ "${_flags_usName_}" = 'help' ]; then
+ # shellcheck disable=SC2154
+ if [ "${FLAGS_help}" -eq ${FLAGS_TRUE} ]; then
+ flags_help
+ flags_error='help requested'
+ flags_return=${FLAGS_FALSE}
+ break
+ fi
+ fi
+
+ # Shift the option and non-boolean arguments out.
+ shift
+ [ "${_flags_type_}" != ${__FLAGS_TYPE_BOOLEAN} ] && shift
+ done
+
+ # Give user back non-flag arguments.
+ FLAGS_ARGV=''
+ while [ $# -gt 0 ]; do
+ FLAGS_ARGV="${FLAGS_ARGV:+${FLAGS_ARGV} }'$1'"
+ shift
+ done
+
+ unset _flags_arg_ _flags_len_ _flags_name_ _flags_opt_ _flags_pos_ \
+ _flags_strToEval_ _flags_type_ _flags_usName_ _flags_val_
+ return ${flags_return}
+}
+
+# Perform some path using built-ins.
+#
+# Args:
+# $@: string: math expression to evaluate
+# Output:
+# integer: the result
+# Returns:
+# bool: success of math evaluation
+_flags_math() {
+ if [ $# -eq 0 ]; then
+ flags_return=${FLAGS_FALSE}
+ elif _flags_useBuiltin; then
+ # Variable assignment is needed as workaround for Solaris Bourne shell,
+ # which cannot parse a bare $((expression)).
+ # shellcheck disable=SC2016
+ _flags_expr_='$(($@))'
+ eval echo ${_flags_expr_}
+ flags_return=$?
+ unset _flags_expr_
+ else
+ eval expr "$@"
+ flags_return=$?
+ fi
+
+ return ${flags_return}
+}
+
+# Cross-platform strlen() implementation.
+#
+# Args:
+# _flags_str: string: to determine length of
+# Output:
+# integer: length of string
+# Returns:
+# bool: success of strlen evaluation
+_flags_strlen() {
+ _flags_str_=${1:-}
+
+ if [ -z "${_flags_str_}" ]; then
+ flags_output=0
+ elif _flags_useBuiltin; then
+ flags_output=${#_flags_str_}
+ else
+ flags_output=`${FLAGS_EXPR_CMD} "${_flags_str_}" : '.*'`
+ fi
+ flags_return=$?
+
+ unset _flags_str_
+ echo "${flags_output}"
+ return ${flags_return}
+}
+
+# Use built-in helper function to enable unit testing.
+#
+# Args:
+# None
+# Returns:
+# bool: true if built-ins should be used
+_flags_useBuiltin() { return ${__FLAGS_USE_BUILTIN}; }
+
+#------------------------------------------------------------------------------
+# public functions
+#
+# A basic boolean flag. Boolean flags do not take any arguments, and their
+# value is either 1 (false) or 0 (true). For long flags, the false value is
+# specified on the command line by prepending the word 'no'. With short flags,
+# the presence of the flag toggles the current value between true and false.
+# Specifying a short boolean flag twice on the command results in returning the
+# value back to the default value.
+#
+# A default value is required for boolean flags.
+#
+# For example, lets say a Boolean flag was created whose long name was 'update'
+# and whose short name was 'x', and the default value was 'false'. This flag
+# could be explicitly set to 'true' with '--update' or by '-x', and it could be
+# explicitly set to 'false' with '--noupdate'.
+DEFINE_boolean() { _flags_define ${__FLAGS_TYPE_BOOLEAN} "$@"; }
+
+# Other basic flags.
+DEFINE_float() { _flags_define ${__FLAGS_TYPE_FLOAT} "$@"; }
+DEFINE_integer() { _flags_define ${__FLAGS_TYPE_INTEGER} "$@"; }
+DEFINE_string() { _flags_define ${__FLAGS_TYPE_STRING} "$@"; }
+
+# Parse the flags.
+#
+# Args:
+# unnamed: list: command-line flags to parse
+# Returns:
+# integer: success of operation, or error
+FLAGS() {
+ # Define a standard 'help' flag if one isn't already defined.
+ [ -z "${__flags_help_type:-}" ] && \
+ DEFINE_boolean 'help' false 'show this help' 'h'
+
+ # Parse options.
+ if [ $# -gt 0 ]; then
+ if [ "${__FLAGS_GETOPT_VERS}" -ne "${__FLAGS_GETOPT_VERS_ENH}" ]; then
+ _flags_getoptStandard "$@"
+ else
+ _flags_getoptEnhanced "$@"
+ fi
+ flags_return=$?
+ else
+ # Nothing passed; won't bother running getopt.
+ __flags_opts='--'
+ flags_return=${FLAGS_TRUE}
+ fi
+
+ if [ ${flags_return} -eq ${FLAGS_TRUE} ]; then
+ _flags_parseGetopt $# "${__flags_opts}"
+ flags_return=$?
+ fi
+
+ [ ${flags_return} -eq ${FLAGS_ERROR} ] && _flags_fatal "${flags_error}"
+ return ${flags_return}
+}
+
+# This is a helper function for determining the 'getopt' version for platforms
+# where the detection isn't working. It simply outputs debug information that
+# can be included in a bug report.
+#
+# Args:
+# none
+# Output:
+# debug info that can be included in a bug report
+# Returns:
+# nothing
+flags_getoptInfo() {
+ # Platform info.
+ _flags_debug "uname -a: `uname -a`"
+ _flags_debug "PATH: ${PATH}"
+
+ # Shell info.
+ if [ -n "${BASH_VERSION:-}" ]; then
+ _flags_debug 'shell: bash'
+ _flags_debug "BASH_VERSION: ${BASH_VERSION}"
+ elif [ -n "${ZSH_VERSION:-}" ]; then
+ _flags_debug 'shell: zsh'
+ _flags_debug "ZSH_VERSION: ${ZSH_VERSION}"
+ fi
+
+ # getopt info.
+ ${FLAGS_GETOPT_CMD} >/dev/null
+ _flags_getoptReturn=$?
+ _flags_debug "getopt return: ${_flags_getoptReturn}"
+ _flags_debug "getopt --version: `${FLAGS_GETOPT_CMD} --version 2>&1`"
+
+ unset _flags_getoptReturn
+}
+
+# Returns whether the detected getopt version is the enhanced version.
+#
+# Args:
+# none
+# Output:
+# none
+# Returns:
+# bool: true if getopt is the enhanced version
+flags_getoptIsEnh() {
+ test "${__FLAGS_GETOPT_VERS}" -eq "${__FLAGS_GETOPT_VERS_ENH}"
+}
+
+# Returns whether the detected getopt version is the standard version.
+#
+# Args:
+# none
+# Returns:
+# bool: true if getopt is the standard version
+flags_getoptIsStd() {
+ test "${__FLAGS_GETOPT_VERS}" -eq "${__FLAGS_GETOPT_VERS_STD}"
+}
+
+# This is effectively a 'usage()' function. It prints usage information and
+# exits the program with ${FLAGS_FALSE} if it is ever found in the command line
+# arguments. Note this function can be overridden so other apps can define
+# their own --help flag, replacing this one, if they want.
+#
+# Args:
+# none
+# Returns:
+# integer: success of operation (always returns true)
+flags_help() {
+ if [ -n "${FLAGS_HELP:-}" ]; then
+ echo "${FLAGS_HELP}" >&2
+ else
+ echo "USAGE: ${FLAGS_PARENT:-$0} [flags] args" >&2
+ fi
+ if [ -n "${__flags_longNames}" ]; then
+ echo 'flags:' >&2
+ for flags_name_ in ${__flags_longNames}; do
+ flags_flagStr_=''
+ flags_boolStr_=''
+ flags_usName_=`_flags_underscoreName "${flags_name_}"`
+
+ flags_default_=`_flags_getFlagInfo \
+ "${flags_usName_}" ${__FLAGS_INFO_DEFAULT}`
+ flags_help_=`_flags_getFlagInfo \
+ "${flags_usName_}" ${__FLAGS_INFO_HELP}`
+ flags_short_=`_flags_getFlagInfo \
+ "${flags_usName_}" ${__FLAGS_INFO_SHORT}`
+ flags_type_=`_flags_getFlagInfo \
+ "${flags_usName_}" ${__FLAGS_INFO_TYPE}`
+
+ [ "${flags_short_}" != "${__FLAGS_NULL}" ] && \
+ flags_flagStr_="-${flags_short_}"
+
+ if [ "${__FLAGS_GETOPT_VERS}" -eq "${__FLAGS_GETOPT_VERS_ENH}" ]; then
+ [ "${flags_short_}" != "${__FLAGS_NULL}" ] && \
+ flags_flagStr_="${flags_flagStr_},"
+ # Add [no] to long boolean flag names, except the 'help' flag.
+ [ "${flags_type_}" -eq ${__FLAGS_TYPE_BOOLEAN} \
+ -a "${flags_usName_}" != 'help' ] && \
+ flags_boolStr_='[no]'
+ flags_flagStr_="${flags_flagStr_}--${flags_boolStr_}${flags_name_}:"
+ fi
+
+ case ${flags_type_} in
+ ${__FLAGS_TYPE_BOOLEAN})
+ if [ "${flags_default_}" -eq ${FLAGS_TRUE} ]; then
+ flags_defaultStr_='true'
+ else
+ flags_defaultStr_='false'
+ fi
+ ;;
+ ${__FLAGS_TYPE_FLOAT}|${__FLAGS_TYPE_INTEGER})
+ flags_defaultStr_=${flags_default_} ;;
+ ${__FLAGS_TYPE_STRING}) flags_defaultStr_="'${flags_default_}'" ;;
+ esac
+ flags_defaultStr_="(default: ${flags_defaultStr_})"
+
+ flags_helpStr_=" ${flags_flagStr_} ${flags_help_:+${flags_help_} }${flags_defaultStr_}"
+ _flags_strlen "${flags_helpStr_}" >/dev/null
+ flags_helpStrLen_=${flags_output}
+ flags_columns_=`_flags_columns`
+
+ if [ "${flags_helpStrLen_}" -lt "${flags_columns_}" ]; then
+ echo "${flags_helpStr_}" >&2
+ else
+ echo " ${flags_flagStr_} ${flags_help_}" >&2
+ # Note: the silliness with the x's is purely for ksh93 on Ubuntu 6.06
+ # because it doesn't like empty strings when used in this manner.
+ flags_emptyStr_="`echo \"x${flags_flagStr_}x\" \
+ |awk '{printf "%"length($0)-2"s", ""}'`"
+ flags_helpStr_=" ${flags_emptyStr_} ${flags_defaultStr_}"
+ _flags_strlen "${flags_helpStr_}" >/dev/null
+ flags_helpStrLen_=${flags_output}
+
+ if [ "${__FLAGS_GETOPT_VERS}" -eq "${__FLAGS_GETOPT_VERS_STD}" \
+ -o "${flags_helpStrLen_}" -lt "${flags_columns_}" ]; then
+ # Indented to match help string.
+ echo "${flags_helpStr_}" >&2
+ else
+ # Indented four from left to allow for longer defaults as long flag
+ # names might be used too, making things too long.
+ echo " ${flags_defaultStr_}" >&2
+ fi
+ fi
+ done
+ fi
+
+ unset flags_boolStr_ flags_default_ flags_defaultStr_ flags_emptyStr_ \
+ flags_flagStr_ flags_help_ flags_helpStr flags_helpStrLen flags_name_ \
+ flags_columns_ flags_short_ flags_type_ flags_usName_
+ return ${FLAGS_TRUE}
+}
+
+# Reset shflags back to an uninitialized state.
+#
+# Args:
+# none
+# Returns:
+# nothing
+flags_reset() {
+ for flags_name_ in ${__flags_longNames}; do
+ flags_usName_=`_flags_underscoreName "${flags_name_}"`
+ flags_strToEval_="unset FLAGS_${flags_usName_}"
+ for flags_type_ in \
+ ${__FLAGS_INFO_DEFAULT} \
+ ${__FLAGS_INFO_HELP} \
+ ${__FLAGS_INFO_SHORT} \
+ ${__FLAGS_INFO_TYPE}
+ do
+ flags_strToEval_=\
+"${flags_strToEval_} __flags_${flags_usName_}_${flags_type_}"
+ done
+ eval "${flags_strToEval_}"
+ done
+
+ # Reset internal variables.
+ __flags_boolNames=' '
+ __flags_longNames=' '
+ __flags_shortNames=' '
+ __flags_definedNames=' '
+
+ # Reset logging level back to default.
+ flags_setLoggingLevel ${__FLAGS_LEVEL_DEFAULT}
+
+ unset flags_name_ flags_type_ flags_strToEval_ flags_usName_
+}
+
+#
+# Initialization
+#
+
+# Set the default logging level.
+flags_setLoggingLevel ${__FLAGS_LEVEL_DEFAULT}
diff --git a/fluent-bit/tests/lib/shunit2/lib/versions b/fluent-bit/tests/lib/shunit2/lib/versions
new file mode 100755
index 00000000..3b64ff5b
--- /dev/null
+++ b/fluent-bit/tests/lib/shunit2/lib/versions
@@ -0,0 +1,273 @@
+#! /bin/sh
+# vim:et:ft=sh:sts=2:sw=2
+#
+# Versions determines the versions of all installed shells.
+#
+# Copyright 2008-2018 Kate Ward. All Rights Reserved.
+# Released under the Apache 2.0 License.
+#
+# Author: kate.ward@forestent.com (Kate Ward)
+# https://github.com/kward/shlib
+#
+# This library provides reusable functions that determine actual names and
+# versions of installed shells and the OS. The library can also be run as a
+# script if set executable.
+#
+# Disable checks that aren't fully portable (POSIX != portable).
+# shellcheck disable=SC2006
+
+ARGV0=`basename "$0"`
+LSB_RELEASE='/etc/lsb-release'
+VERSIONS_SHELLS='ash /bin/bash /bin/dash /bin/ksh /bin/pdksh /bin/zsh /bin/sh /usr/xpg4/bin/sh /sbin/sh'
+
+true; TRUE=$?
+false; FALSE=$?
+ERROR=2
+
+UNAME_R=`uname -r`
+UNAME_S=`uname -s`
+
+__versions_haveStrings=${ERROR}
+
+versions_osName() {
+ os_name_='unrecognized'
+ os_system_=${UNAME_S}
+ os_release_=${UNAME_R}
+ case ${os_system_} in
+ CYGWIN_NT-*) os_name_='Cygwin' ;;
+ Darwin)
+ os_name_=`/usr/bin/sw_vers -productName`
+ os_version_=`versions_osVersion`
+ case ${os_version_} in
+ 10.4|10.4.[0-9]*) os_name_='Mac OS X Tiger' ;;
+ 10.5|10.5.[0-9]*) os_name_='Mac OS X Leopard' ;;
+ 10.6|10.6.[0-9]*) os_name_='Mac OS X Snow Leopard' ;;
+ 10.7|10.7.[0-9]*) os_name_='Mac OS X Lion' ;;
+ 10.8|10.8.[0-9]*) os_name_='Mac OS X Mountain Lion' ;;
+ 10.9|10.9.[0-9]*) os_name_='Mac OS X Mavericks' ;;
+ 10.10|10.10.[0-9]*) os_name_='Mac OS X Yosemite' ;;
+ 10.11|10.11.[0-9]*) os_name_='Mac OS X El Capitan' ;;
+ 10.12|10.12.[0-9]*) os_name_='macOS Sierra' ;;
+ 10.13|10.13.[0-9]*) os_name_='macOS High Sierra' ;;
+ 10.14|10.14.[0-9]*) os_name_='macOS Mojave' ;;
+ *) os_name_='macOS' ;;
+ esac
+ ;;
+ FreeBSD) os_name_='FreeBSD' ;;
+ Linux) os_name_='Linux' ;;
+ SunOS)
+ os_name_='SunOS'
+ if [ -r '/etc/release' ]; then
+ if grep 'OpenSolaris' /etc/release >/dev/null; then
+ os_name_='OpenSolaris'
+ else
+ os_name_='Solaris'
+ fi
+ fi
+ ;;
+ esac
+
+ echo ${os_name_}
+ unset os_name_ os_system_ os_release_ os_version_
+}
+
+versions_osVersion() {
+ os_version_='unrecognized'
+ os_system_=${UNAME_S}
+ os_release_=${UNAME_R}
+ case ${os_system_} in
+ CYGWIN_NT-*)
+ os_version_=`expr "${os_release_}" : '\([0-9]*\.[0-9]\.[0-9]*\).*'`
+ ;;
+ Darwin)
+ os_version_=`/usr/bin/sw_vers -productVersion`
+ ;;
+ FreeBSD)
+ os_version_=`expr "${os_release_}" : '\([0-9]*\.[0-9]*\)-.*'`
+ ;;
+ Linux)
+ if [ -r '/etc/os-release' ]; then
+ os_version_=`awk -F= '$1~/PRETTY_NAME/{print $2}' /etc/os-release \
+ |sed 's/"//g'`
+ elif [ -r '/etc/redhat-release' ]; then
+ os_version_=`cat /etc/redhat-release`
+ elif [ -r '/etc/SuSE-release' ]; then
+ os_version_=`head -n 1 /etc/SuSE-release`
+ elif [ -r "${LSB_RELEASE}" ]; then
+ if grep -q 'DISTRIB_ID=Ubuntu' "${LSB_RELEASE}"; then
+ # shellcheck disable=SC2002
+ os_version_=`cat "${LSB_RELEASE}" \
+ |awk -F= '$1~/DISTRIB_DESCRIPTION/{print $2}' \
+ |sed 's/"//g;s/ /-/g'`
+ fi
+ fi
+ ;;
+ SunOS)
+ if [ -r '/etc/release' ]; then
+ if grep 'OpenSolaris' /etc/release >/dev/null; then # OpenSolaris
+ os_version_=`grep 'OpenSolaris' /etc/release |awk '{print $2"("$3")"}'`
+ else # Solaris
+ major_=`echo "${os_release_}" |sed 's/[0-9]*\.\([0-9]*\)/\1/'`
+ minor_=`grep Solaris /etc/release |sed 's/[^u]*\(u[0-9]*\).*/\1/'`
+ os_version_="${major_}${minor_}"
+ fi
+ fi
+ ;;
+ esac
+
+ echo "${os_version_}"
+ unset os_release_ os_system_ os_version_ major_ minor_
+}
+
+versions_shellVersion() {
+ shell_=$1
+
+ shell_present_=${FALSE}
+ case "${shell_}" in
+ ash) [ -x '/bin/busybox' ] && shell_present_=${TRUE} ;;
+ *) [ -x "${shell_}" ] && shell_present_=${TRUE} ;;
+ esac
+ if [ ${shell_present_} -eq ${FALSE} ]; then
+ echo 'not installed'
+ return ${FALSE}
+ fi
+
+ version_=''
+ case ${shell_} in
+ /sbin/sh) ;; # SunOS
+ /usr/xpg4/bin/sh)
+ version_=`versions_shell_xpg4 "${shell_}"`
+ ;; # SunOS
+ */sh)
+ # This could be one of any number of shells. Try until one fits.
+ version_=''
+ [ -z "${version_}" ] && version_=`versions_shell_bash "${shell_}"`
+ # dash cannot be self determined yet
+ [ -z "${version_}" ] && version_=`versions_shell_ksh "${shell_}"`
+ # pdksh is covered in versions_shell_ksh()
+ [ -z "${version_}" ] && version_=`versions_shell_xpg4 "${shell_}"`
+ [ -z "${version_}" ] && version_=`versions_shell_zsh "${shell_}"`
+ ;;
+ ash) version_=`versions_shell_ash "${shell_}"` ;;
+ */bash) version_=`versions_shell_bash "${shell_}"` ;;
+ */dash)
+ # Assuming Ubuntu Linux until somebody comes up with a better test. The
+ # following test will return an empty string if dash is not installed.
+ version_=`versions_shell_dash`
+ ;;
+ */ksh) version_=`versions_shell_ksh "${shell_}"` ;;
+ */pdksh) version_=`versions_shell_pdksh "${shell_}"` ;;
+ */zsh) version_=`versions_shell_zsh "${shell_}"` ;;
+ *) version_='invalid'
+ esac
+
+ echo "${version_:-unknown}"
+ unset shell_ version_
+}
+
+# The ash shell is included in BusyBox.
+versions_shell_ash() {
+ busybox --help |head -1 |sed 's/BusyBox v\([0-9.]*\) .*/\1/'
+}
+
+versions_shell_bash() {
+ $1 --version : 2>&1 |grep 'GNU bash' |sed 's/.*version \([^ ]*\).*/\1/'
+}
+
+versions_shell_dash() {
+ eval dpkg >/dev/null 2>&1
+ [ $? -eq 127 ] && return # Return if dpkg not found.
+
+ dpkg -l |grep ' dash ' |awk '{print $3}'
+}
+
+versions_shell_ksh() {
+ versions_shell_=$1
+ versions_version_=''
+
+ # Try a few different ways to figure out the version.
+ versions_version_=`${versions_shell_} --version : 2>&1`
+ # shellcheck disable=SC2181
+ if [ $? -eq 0 ]; then
+ versions_version_=`echo "${versions_version_}" \
+ |sed 's/.*\([0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]\).*/\1/'`
+ else
+ versions_version_=''
+ fi
+ if [ -z "${versions_version_}" ]; then
+ _versions_have_strings
+ versions_version_=`strings "${versions_shell_}" 2>&1 \
+ |grep Version \
+ |sed 's/^.*Version \(.*\)$/\1/;s/ s+ \$$//;s/ /-/g'`
+ fi
+ if [ -z "${versions_version_}" ]; then
+ versions_version_=`versions_shell_pdksh "${versions_shell_}"`
+ fi
+
+ echo "${versions_version_}"
+ unset versions_shell_ versions_version_
+}
+
+versions_shell_pdksh() {
+ _versions_have_strings
+ strings "$1" 2>&1 \
+ |grep 'PD KSH' \
+ |sed -e 's/.*PD KSH \(.*\)/\1/;s/ /-/g'
+}
+
+versions_shell_xpg4() {
+ _versions_have_strings
+ strings "$1" 2>&1 \
+ |grep 'Version' \
+ |sed -e 's/^@(#)Version //'
+}
+
+versions_shell_zsh() {
+ versions_shell_=$1
+
+ # Try a few different ways to figure out the version.
+ # shellcheck disable=SC2016
+ versions_version_=`echo 'echo ${ZSH_VERSION}' |${versions_shell_}`
+ if [ -z "${versions_version_}" ]; then
+ versions_version_=`${versions_shell_} --version : 2>&1`
+ # shellcheck disable=SC2181
+ if [ $? -eq 0 ]; then
+ versions_version_=`echo "${versions_version_}" |awk '{print $2}'`
+ else
+ versions_version_=''
+ fi
+ fi
+
+ echo "${versions_version_}"
+ unset versions_shell_ versions_version_
+}
+
+# Determine if the 'strings' binary installed.
+_versions_have_strings() {
+ [ ${__versions_haveStrings} -ne ${ERROR} ] && return
+ if eval strings /dev/null >/dev/null 2>&1; then
+ __versions_haveStrings=${TRUE}
+ return
+ fi
+
+ echo 'WARN: strings not installed. try installing binutils?' >&2
+ __versions_haveStrings=${FALSE}
+}
+
+versions_main() {
+ # Treat unset variables as an error.
+ set -u
+
+ os_name=`versions_osName`
+ os_version=`versions_osVersion`
+ echo "os: ${os_name} version: ${os_version}"
+
+ for shell in ${VERSIONS_SHELLS}; do
+ shell_version=`versions_shellVersion "${shell}"`
+ echo "shell: ${shell} version: ${shell_version}"
+ done
+}
+
+if [ "${ARGV0}" = 'versions' ]; then
+ versions_main "$@"
+fi
diff --git a/fluent-bit/tests/lib/shunit2/shunit2 b/fluent-bit/tests/lib/shunit2/shunit2
new file mode 100755
index 00000000..6239683a
--- /dev/null
+++ b/fluent-bit/tests/lib/shunit2/shunit2
@@ -0,0 +1,1343 @@
+#! /bin/sh
+# vim:et:ft=sh:sts=2:sw=2
+#
+# Copyright 2008-2020 Kate Ward. All Rights Reserved.
+# Released under the Apache 2.0 license.
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# shUnit2 -- Unit testing framework for Unix shell scripts.
+# https://github.com/kward/shunit2
+#
+# Author: kate.ward@forestent.com (Kate Ward)
+#
+# shUnit2 is a xUnit based unit test framework for Bourne shell scripts. It is
+# based on the popular JUnit unit testing framework for Java.
+#
+# $() are not fully portable (POSIX != portable).
+# shellcheck disable=SC2006
+# expr may be antiquated, but it is the only solution in some cases.
+# shellcheck disable=SC2003
+
+# Return if shunit2 already loaded.
+command [ -n "${SHUNIT_VERSION:-}" ] && exit 0
+SHUNIT_VERSION='2.1.8'
+
+# Return values that scripts can use.
+SHUNIT_TRUE=0
+SHUNIT_FALSE=1
+SHUNIT_ERROR=2
+
+# Logging functions.
+_shunit_warn() {
+ ${__SHUNIT_CMD_ECHO_ESC} \
+ "${__shunit_ansi_yellow}shunit2:WARN${__shunit_ansi_none} $*" >&2
+}
+_shunit_error() {
+ ${__SHUNIT_CMD_ECHO_ESC} \
+ "${__shunit_ansi_red}shunit2:ERROR${__shunit_ansi_none} $*" >&2
+}
+_shunit_fatal() {
+ ${__SHUNIT_CMD_ECHO_ESC} \
+ "${__shunit_ansi_red}shunit2:FATAL${__shunit_ansi_none} $*" >&2
+ exit ${SHUNIT_ERROR}
+}
+
+# Determine some reasonable command defaults.
+__SHUNIT_CMD_ECHO_ESC='echo -e'
+# shellcheck disable=SC2039
+command [ "`echo -e test`" = '-e test' ] && __SHUNIT_CMD_ECHO_ESC='echo'
+
+__SHUNIT_UNAME_S=`uname -s`
+case "${__SHUNIT_UNAME_S}" in
+ BSD) __SHUNIT_CMD_EXPR='gexpr' ;;
+ *) __SHUNIT_CMD_EXPR='expr' ;;
+esac
+__SHUNIT_CMD_TPUT='tput'
+
+# Commands a user can override if needed.
+SHUNIT_CMD_EXPR=${SHUNIT_CMD_EXPR:-${__SHUNIT_CMD_EXPR}}
+SHUNIT_CMD_TPUT=${SHUNIT_CMD_TPUT:-${__SHUNIT_CMD_TPUT}}
+
+# Enable color output. Options are 'never', 'always', or 'auto'.
+SHUNIT_COLOR=${SHUNIT_COLOR:-auto}
+
+# Specific shell checks.
+if command [ -n "${ZSH_VERSION:-}" ]; then
+ setopt |grep "^shwordsplit$" >/dev/null
+ if command [ $? -ne ${SHUNIT_TRUE} ]; then
+ _shunit_fatal 'zsh shwordsplit option is required for proper operation'
+ fi
+ if command [ -z "${SHUNIT_PARENT:-}" ]; then
+ _shunit_fatal "zsh does not pass \$0 through properly. please declare \
+\"SHUNIT_PARENT=\$0\" before calling shUnit2"
+ fi
+fi
+
+#
+# Constants
+#
+
+__SHUNIT_MODE_SOURCED='sourced'
+__SHUNIT_MODE_STANDALONE='standalone'
+__SHUNIT_PARENT=${SHUNIT_PARENT:-$0}
+
+# User provided test prefix to display in front of the name of the test being
+# executed. Define by setting the SHUNIT_TEST_PREFIX variable.
+__SHUNIT_TEST_PREFIX=${SHUNIT_TEST_PREFIX:-}
+
+# ANSI colors.
+__SHUNIT_ANSI_NONE='\033[0m'
+__SHUNIT_ANSI_RED='\033[1;31m'
+__SHUNIT_ANSI_GREEN='\033[1;32m'
+__SHUNIT_ANSI_YELLOW='\033[1;33m'
+__SHUNIT_ANSI_CYAN='\033[1;36m'
+
+# Set the constants readonly.
+__shunit_constants=`set |grep '^__SHUNIT_' |cut -d= -f1`
+echo "${__shunit_constants}" |grep '^Binary file' >/dev/null && \
+ __shunit_constants=`set |grep -a '^__SHUNIT_' |cut -d= -f1`
+for __shunit_const in ${__shunit_constants}; do
+ if command [ -z "${ZSH_VERSION:-}" ]; then
+ readonly "${__shunit_const}"
+ else
+ case ${ZSH_VERSION} in
+ [123].*) readonly "${__shunit_const}" ;;
+ *) readonly -g "${__shunit_const}" # Declare readonly constants globally.
+ esac
+ fi
+done
+unset __shunit_const __shunit_constants
+
+#
+# Internal variables.
+#
+
+# Variables.
+__shunit_lineno='' # Line number of executed test.
+__shunit_mode=${__SHUNIT_MODE_SOURCED} # Operating mode.
+__shunit_reportGenerated=${SHUNIT_FALSE} # Is report generated.
+__shunit_script='' # Filename of unittest script (standalone mode).
+__shunit_skip=${SHUNIT_FALSE} # Is skipping enabled.
+__shunit_suite='' # Suite of tests to execute.
+__shunit_clean=${SHUNIT_FALSE} # _shunit_cleanup() was already called.
+
+# ANSI colors (populated by _shunit_configureColor()).
+__shunit_ansi_none=''
+__shunit_ansi_red=''
+__shunit_ansi_green=''
+__shunit_ansi_yellow=''
+__shunit_ansi_cyan=''
+
+# Counts of tests.
+__shunit_testSuccess=${SHUNIT_TRUE}
+__shunit_testsTotal=0
+__shunit_testsPassed=0
+__shunit_testsFailed=0
+
+# Counts of asserts.
+__shunit_assertsTotal=0
+__shunit_assertsPassed=0
+__shunit_assertsFailed=0
+__shunit_assertsSkipped=0
+
+#
+# Macros.
+#
+
+# shellcheck disable=SC2016,SC2089
+_SHUNIT_LINENO_='eval __shunit_lineno=""; if command [ "${1:-}" = "--lineno" ]; then command [ -n "$2" ] && __shunit_lineno="[$2] "; shift 2; fi'
+
+#-----------------------------------------------------------------------------
+# Assertion functions.
+#
+
+# Assert that two values are equal to one another.
+#
+# Args:
+# message: string: failure message [optional]
+# expected: string: expected value
+# actual: string: actual value
+# Returns:
+# integer: success (TRUE/FALSE/ERROR constant)
+assertEquals() {
+ # shellcheck disable=SC2090
+ ${_SHUNIT_LINENO_}
+ if command [ $# -lt 2 -o $# -gt 3 ]; then
+ _shunit_error "assertEquals() requires two or three arguments; $# given"
+ _shunit_assertFail
+ return ${SHUNIT_ERROR}
+ fi
+ _shunit_shouldSkip && return ${SHUNIT_TRUE}
+
+ shunit_message_=${__shunit_lineno}
+ if command [ $# -eq 3 ]; then
+ shunit_message_="${shunit_message_}$1"
+ shift
+ fi
+ shunit_expected_=$1
+ shunit_actual_=$2
+
+ shunit_return=${SHUNIT_TRUE}
+ if command [ "${shunit_expected_}" = "${shunit_actual_}" ]; then
+ _shunit_assertPass
+ else
+ failNotEquals "${shunit_message_}" "${shunit_expected_}" "${shunit_actual_}"
+ shunit_return=${SHUNIT_FALSE}
+ fi
+
+ unset shunit_message_ shunit_expected_ shunit_actual_
+ return ${shunit_return}
+}
+# shellcheck disable=SC2016,SC2034
+_ASSERT_EQUALS_='eval assertEquals --lineno "${LINENO:-}"'
+
+# Assert that two values are not equal to one another.
+#
+# Args:
+# message: string: failure message [optional]
+# expected: string: expected value
+# actual: string: actual value
+# Returns:
+# integer: success (TRUE/FALSE/ERROR constant)
+assertNotEquals() {
+ # shellcheck disable=SC2090
+ ${_SHUNIT_LINENO_}
+ if command [ $# -lt 2 -o $# -gt 3 ]; then
+ _shunit_error "assertNotEquals() requires two or three arguments; $# given"
+ _shunit_assertFail
+ return ${SHUNIT_ERROR}
+ fi
+ _shunit_shouldSkip && return ${SHUNIT_TRUE}
+
+ shunit_message_=${__shunit_lineno}
+ if command [ $# -eq 3 ]; then
+ shunit_message_="${shunit_message_}$1"
+ shift
+ fi
+ shunit_expected_=$1
+ shunit_actual_=$2
+
+ shunit_return=${SHUNIT_TRUE}
+ if command [ "${shunit_expected_}" != "${shunit_actual_}" ]; then
+ _shunit_assertPass
+ else
+ failSame "${shunit_message_}" "${shunit_expected_}" "${shunit_actual_}"
+ shunit_return=${SHUNIT_FALSE}
+ fi
+
+ unset shunit_message_ shunit_expected_ shunit_actual_
+ return ${shunit_return}
+}
+# shellcheck disable=SC2016,SC2034
+_ASSERT_NOT_EQUALS_='eval assertNotEquals --lineno "${LINENO:-}"'
+
+# Assert that a container contains a content.
+#
+# Args:
+# message: string: failure message [optional]
+# container: string: container to analyze
+# content: string: content to find
+# Returns:
+# integer: success (TRUE/FALSE/ERROR constant)
+assertContains() {
+ # shellcheck disable=SC2090
+ ${_SHUNIT_LINENO_}
+ if command [ $# -lt 2 -o $# -gt 3 ]; then
+ _shunit_error "assertContains() requires two or three arguments; $# given"
+ _shunit_assertFail
+ return ${SHUNIT_ERROR}
+ fi
+ _shunit_shouldSkip && return ${SHUNIT_TRUE}
+
+ shunit_message_=${__shunit_lineno}
+ if command [ $# -eq 3 ]; then
+ shunit_message_="${shunit_message_}$1"
+ shift
+ fi
+ shunit_container_=$1
+ shunit_content_=$2
+
+ shunit_return=${SHUNIT_TRUE}
+ if echo "$shunit_container_" | grep -F -- "$shunit_content_" > /dev/null; then
+ _shunit_assertPass
+ else
+ failNotFound "${shunit_message_}" "${shunit_content_}"
+ shunit_return=${SHUNIT_FALSE}
+ fi
+
+ unset shunit_message_ shunit_container_ shunit_content_
+ return ${shunit_return}
+}
+# shellcheck disable=SC2016,SC2034
+_ASSERT_CONTAINS_='eval assertContains --lineno "${LINENO:-}"'
+
+# Assert that a container does not contain a content.
+#
+# Args:
+# message: string: failure message [optional]
+# container: string: container to analyze
+# content: string: content to look for
+# Returns:
+# integer: success (TRUE/FALSE/ERROR constant)
+assertNotContains() {
+ # shellcheck disable=SC2090
+ ${_SHUNIT_LINENO_}
+ if command [ $# -lt 2 -o $# -gt 3 ]; then
+ _shunit_error "assertNotContains() requires two or three arguments; $# given"
+ _shunit_assertFail
+ return ${SHUNIT_ERROR}
+ fi
+ _shunit_shouldSkip && return ${SHUNIT_TRUE}
+
+ shunit_message_=${__shunit_lineno}
+ if command [ $# -eq 3 ]; then
+ shunit_message_="${shunit_message_}$1"
+ shift
+ fi
+ shunit_container_=$1
+ shunit_content_=$2
+
+ shunit_return=${SHUNIT_TRUE}
+ if echo "$shunit_container_" | grep -F -- "$shunit_content_" > /dev/null; then
+ failFound "${shunit_message_}" "${shunit_content_}"
+ shunit_return=${SHUNIT_FALSE}
+ else
+ _shunit_assertPass
+ fi
+
+ unset shunit_message_ shunit_container_ shunit_content_
+ return ${shunit_return}
+}
+# shellcheck disable=SC2016,SC2034
+_ASSERT_NOT_CONTAINS_='eval assertNotContains --lineno "${LINENO:-}"'
+
+# Assert that a value is null (i.e. an empty string)
+#
+# Args:
+# message: string: failure message [optional]
+# actual: string: actual value
+# Returns:
+# integer: success (TRUE/FALSE/ERROR constant)
+assertNull() {
+ # shellcheck disable=SC2090
+ ${_SHUNIT_LINENO_}
+ if command [ $# -lt 1 -o $# -gt 2 ]; then
+ _shunit_error "assertNull() requires one or two arguments; $# given"
+ _shunit_assertFail
+ return ${SHUNIT_ERROR}
+ fi
+ _shunit_shouldSkip && return ${SHUNIT_TRUE}
+
+ shunit_message_=${__shunit_lineno}
+ if command [ $# -eq 2 ]; then
+ shunit_message_="${shunit_message_}$1"
+ shift
+ fi
+ assertTrue "${shunit_message_}" "[ -z '$1' ]"
+ shunit_return=$?
+
+ unset shunit_message_
+ return ${shunit_return}
+}
+# shellcheck disable=SC2016,SC2034
+_ASSERT_NULL_='eval assertNull --lineno "${LINENO:-}"'
+
+# Assert that a value is not null (i.e. a non-empty string)
+#
+# Args:
+# message: string: failure message [optional]
+# actual: string: actual value
+# Returns:
+# integer: success (TRUE/FALSE/ERROR constant)
+assertNotNull() {
+ # shellcheck disable=SC2090
+ ${_SHUNIT_LINENO_}
+ if command [ $# -gt 2 ]; then # allowing 0 arguments as $1 might actually be null
+ _shunit_error "assertNotNull() requires one or two arguments; $# given"
+ _shunit_assertFail
+ return ${SHUNIT_ERROR}
+ fi
+ _shunit_shouldSkip && return ${SHUNIT_TRUE}
+
+ shunit_message_=${__shunit_lineno}
+ if command [ $# -eq 2 ]; then
+ shunit_message_="${shunit_message_}$1"
+ shift
+ fi
+ shunit_actual_=`_shunit_escapeCharactersInString "${1:-}"`
+ test -n "${shunit_actual_}"
+ assertTrue "${shunit_message_}" $?
+ shunit_return=$?
+
+ unset shunit_actual_ shunit_message_
+ return ${shunit_return}
+}
+# shellcheck disable=SC2016,SC2034
+_ASSERT_NOT_NULL_='eval assertNotNull --lineno "${LINENO:-}"'
+
+# Assert that two values are the same (i.e. equal to one another).
+#
+# Args:
+# message: string: failure message [optional]
+# expected: string: expected value
+# actual: string: actual value
+# Returns:
+# integer: success (TRUE/FALSE/ERROR constant)
+assertSame() {
+ # shellcheck disable=SC2090
+ ${_SHUNIT_LINENO_}
+ if command [ $# -lt 2 -o $# -gt 3 ]; then
+ _shunit_error "assertSame() requires two or three arguments; $# given"
+ _shunit_assertFail
+ return ${SHUNIT_ERROR}
+ fi
+ _shunit_shouldSkip && return ${SHUNIT_TRUE}
+
+ shunit_message_=${__shunit_lineno}
+ if command [ $# -eq 3 ]; then
+ shunit_message_="${shunit_message_}$1"
+ shift
+ fi
+ assertEquals "${shunit_message_}" "$1" "$2"
+ shunit_return=$?
+
+ unset shunit_message_
+ return ${shunit_return}
+}
+# shellcheck disable=SC2016,SC2034
+_ASSERT_SAME_='eval assertSame --lineno "${LINENO:-}"'
+
+# Assert that two values are not the same (i.e. not equal to one another).
+#
+# Args:
+# message: string: failure message [optional]
+# expected: string: expected value
+# actual: string: actual value
+# Returns:
+# integer: success (TRUE/FALSE/ERROR constant)
+assertNotSame() {
+ # shellcheck disable=SC2090
+ ${_SHUNIT_LINENO_}
+ if command [ $# -lt 2 -o $# -gt 3 ]; then
+ _shunit_error "assertNotSame() requires two or three arguments; $# given"
+ _shunit_assertFail
+ return ${SHUNIT_ERROR}
+ fi
+ _shunit_shouldSkip && return ${SHUNIT_TRUE}
+
+ shunit_message_=${__shunit_lineno}
+ if command [ $# -eq 3 ]; then
+ shunit_message_="${shunit_message_:-}$1"
+ shift
+ fi
+ assertNotEquals "${shunit_message_}" "$1" "$2"
+ shunit_return=$?
+
+ unset shunit_message_
+ return ${shunit_return}
+}
+# shellcheck disable=SC2016,SC2034
+_ASSERT_NOT_SAME_='eval assertNotSame --lineno "${LINENO:-}"'
+
+# Assert that a value or shell test condition is true.
+#
+# In shell, a value of 0 is true and a non-zero value is false. Any integer
+# value passed can thereby be tested.
+#
+# Shell supports much more complicated tests though, and a means to support
+# them was needed. As such, this function tests that conditions are true or
+# false through evaluation rather than just looking for a true or false.
+#
+# The following test will succeed:
+# assertTrue 0
+# assertTrue "[ 34 -gt 23 ]"
+# The following test will fail with a message:
+# assertTrue 123
+# assertTrue "test failed" "[ -r '/non/existent/file' ]"
+#
+# Args:
+# message: string: failure message [optional]
+# condition: string: integer value or shell conditional statement
+# Returns:
+# integer: success (TRUE/FALSE/ERROR constant)
+assertTrue() {
+ # shellcheck disable=SC2090
+ ${_SHUNIT_LINENO_}
+ if command [ $# -lt 1 -o $# -gt 2 ]; then
+ _shunit_error "assertTrue() takes one or two arguments; $# given"
+ _shunit_assertFail
+ return ${SHUNIT_ERROR}
+ fi
+ _shunit_shouldSkip && return ${SHUNIT_TRUE}
+
+ shunit_message_=${__shunit_lineno}
+ if command [ $# -eq 2 ]; then
+ shunit_message_="${shunit_message_}$1"
+ shift
+ fi
+ shunit_condition_=$1
+
+ # See if condition is an integer, i.e. a return value.
+ shunit_match_=`expr "${shunit_condition_}" : '\([0-9]*\)'`
+ shunit_return=${SHUNIT_TRUE}
+ if command [ -z "${shunit_condition_}" ]; then
+ # Null condition.
+ shunit_return=${SHUNIT_FALSE}
+ elif command [ -n "${shunit_match_}" -a "${shunit_condition_}" = "${shunit_match_}" ]
+ then
+ # Possible return value. Treating 0 as true, and non-zero as false.
+ command [ "${shunit_condition_}" -ne 0 ] && shunit_return=${SHUNIT_FALSE}
+ else
+ # Hopefully... a condition.
+ ( eval "${shunit_condition_}" ) >/dev/null 2>&1
+ command [ $? -ne 0 ] && shunit_return=${SHUNIT_FALSE}
+ fi
+
+ # Record the test.
+ if command [ ${shunit_return} -eq ${SHUNIT_TRUE} ]; then
+ _shunit_assertPass
+ else
+ _shunit_assertFail "${shunit_message_}"
+ fi
+
+ unset shunit_message_ shunit_condition_ shunit_match_
+ return ${shunit_return}
+}
+# shellcheck disable=SC2016,SC2034
+_ASSERT_TRUE_='eval assertTrue --lineno "${LINENO:-}"'
+
+# Assert that a value or shell test condition is false.
+#
+# In shell, a value of 0 is true and a non-zero value is false. Any integer
+# value passed can thereby be tested.
+#
+# Shell supports much more complicated tests though, and a means to support
+# them was needed. As such, this function tests that conditions are true or
+# false through evaluation rather than just looking for a true or false.
+#
+# The following test will succeed:
+# assertFalse 1
+# assertFalse "[ 'apples' = 'oranges' ]"
+# The following test will fail with a message:
+# assertFalse 0
+# assertFalse "test failed" "[ 1 -eq 1 -a 2 -eq 2 ]"
+#
+# Args:
+# message: string: failure message [optional]
+# condition: string: integer value or shell conditional statement
+# Returns:
+# integer: success (TRUE/FALSE/ERROR constant)
+assertFalse() {
+ # shellcheck disable=SC2090
+ ${_SHUNIT_LINENO_}
+ if command [ $# -lt 1 -o $# -gt 2 ]; then
+ _shunit_error "assertFalse() requires one or two arguments; $# given"
+ _shunit_assertFail
+ return ${SHUNIT_ERROR}
+ fi
+ _shunit_shouldSkip && return ${SHUNIT_TRUE}
+
+ shunit_message_=${__shunit_lineno}
+ if command [ $# -eq 2 ]; then
+ shunit_message_="${shunit_message_}$1"
+ shift
+ fi
+ shunit_condition_=$1
+
+ # See if condition is an integer, i.e. a return value.
+ shunit_match_=`expr "${shunit_condition_}" : '\([0-9]*\)'`
+ shunit_return=${SHUNIT_TRUE}
+ if command [ -z "${shunit_condition_}" ]; then
+ # Null condition.
+ shunit_return=${SHUNIT_FALSE}
+ elif command [ -n "${shunit_match_}" -a "${shunit_condition_}" = "${shunit_match_}" ]
+ then
+ # Possible return value. Treating 0 as true, and non-zero as false.
+ command [ "${shunit_condition_}" -eq 0 ] && shunit_return=${SHUNIT_FALSE}
+ else
+ # Hopefully... a condition.
+ ( eval "${shunit_condition_}" ) >/dev/null 2>&1
+ command [ $? -eq 0 ] && shunit_return=${SHUNIT_FALSE}
+ fi
+
+ # Record the test.
+ if command [ "${shunit_return}" -eq "${SHUNIT_TRUE}" ]; then
+ _shunit_assertPass
+ else
+ _shunit_assertFail "${shunit_message_}"
+ fi
+
+ unset shunit_message_ shunit_condition_ shunit_match_
+ return "${shunit_return}"
+}
+# shellcheck disable=SC2016,SC2034
+_ASSERT_FALSE_='eval assertFalse --lineno "${LINENO:-}"'
+
+#-----------------------------------------------------------------------------
+# Failure functions.
+#
+
+# Records a test failure.
+#
+# Args:
+# message: string: failure message [optional]
+# Returns:
+# integer: success (TRUE/FALSE/ERROR constant)
+fail() {
+ # shellcheck disable=SC2090
+ ${_SHUNIT_LINENO_}
+ if command [ $# -gt 1 ]; then
+ _shunit_error "fail() requires zero or one arguments; $# given"
+ return ${SHUNIT_ERROR}
+ fi
+ _shunit_shouldSkip && return ${SHUNIT_TRUE}
+
+ shunit_message_=${__shunit_lineno}
+ if command [ $# -eq 1 ]; then
+ shunit_message_="${shunit_message_}$1"
+ shift
+ fi
+
+ _shunit_assertFail "${shunit_message_}"
+
+ unset shunit_message_
+ return ${SHUNIT_FALSE}
+}
+# shellcheck disable=SC2016,SC2034
+_FAIL_='eval fail --lineno "${LINENO:-}"'
+
+# Records a test failure, stating two values were not equal.
+#
+# Args:
+# message: string: failure message [optional]
+# expected: string: expected value
+# actual: string: actual value
+# Returns:
+# integer: success (TRUE/FALSE/ERROR constant)
+failNotEquals() {
+ # shellcheck disable=SC2090
+ ${_SHUNIT_LINENO_}
+ if command [ $# -lt 2 -o $# -gt 3 ]; then
+ _shunit_error "failNotEquals() requires one or two arguments; $# given"
+ return ${SHUNIT_ERROR}
+ fi
+ _shunit_shouldSkip && return ${SHUNIT_TRUE}
+
+ shunit_message_=${__shunit_lineno}
+ if command [ $# -eq 3 ]; then
+ shunit_message_="${shunit_message_}$1"
+ shift
+ fi
+ shunit_expected_=$1
+ shunit_actual_=$2
+
+ shunit_message_=${shunit_message_%% }
+ _shunit_assertFail "${shunit_message_:+${shunit_message_} }expected:<${shunit_expected_}> but was:<${shunit_actual_}>"
+
+ unset shunit_message_ shunit_expected_ shunit_actual_
+ return ${SHUNIT_FALSE}
+}
+# shellcheck disable=SC2016,SC2034
+_FAIL_NOT_EQUALS_='eval failNotEquals --lineno "${LINENO:-}"'
+
+# Records a test failure, stating a value was found.
+#
+# Args:
+# message: string: failure message [optional]
+# content: string: found value
+# Returns:
+# integer: success (TRUE/FALSE/ERROR constant)
+failFound() {
+ # shellcheck disable=SC2090
+ ${_SHUNIT_LINENO_}
+ if command [ $# -lt 1 -o $# -gt 2 ]; then
+ _shunit_error "failFound() requires one or two arguments; $# given"
+ return ${SHUNIT_ERROR}
+ fi
+ _shunit_shouldSkip && return ${SHUNIT_TRUE}
+
+ shunit_message_=${__shunit_lineno}
+ if command [ $# -eq 2 ]; then
+ shunit_message_="${shunit_message_}$1"
+ shift
+ fi
+
+ shunit_message_=${shunit_message_%% }
+ _shunit_assertFail "${shunit_message_:+${shunit_message_} }Found"
+
+ unset shunit_message_
+ return ${SHUNIT_FALSE}
+}
+# shellcheck disable=SC2016,SC2034
+_FAIL_FOUND_='eval failFound --lineno "${LINENO:-}"'
+
+# Records a test failure, stating a content was not found.
+#
+# Args:
+# message: string: failure message [optional]
+# content: string: content not found
+# Returns:
+# integer: success (TRUE/FALSE/ERROR constant)
+failNotFound() {
+ # shellcheck disable=SC2090
+ ${_SHUNIT_LINENO_}
+ if command [ $# -lt 1 -o $# -gt 2 ]; then
+ _shunit_error "failNotFound() requires one or two arguments; $# given"
+ return ${SHUNIT_ERROR}
+ fi
+ _shunit_shouldSkip && return ${SHUNIT_TRUE}
+
+ shunit_message_=${__shunit_lineno}
+ if command [ $# -eq 2 ]; then
+ shunit_message_="${shunit_message_}$1"
+ shift
+ fi
+ shunit_content_=$1
+
+ shunit_message_=${shunit_message_%% }
+ _shunit_assertFail "${shunit_message_:+${shunit_message_} }Not found:<${shunit_content_}>"
+
+ unset shunit_message_ shunit_content_
+ return ${SHUNIT_FALSE}
+}
+# shellcheck disable=SC2016,SC2034
+_FAIL_NOT_FOUND_='eval failNotFound --lineno "${LINENO:-}"'
+
+# Records a test failure, stating two values should have been the same.
+#
+# Args:
+# message: string: failure message [optional]
+# expected: string: expected value
+# actual: string: actual value
+# Returns:
+# integer: success (TRUE/FALSE/ERROR constant)
+failSame()
+{
+ # shellcheck disable=SC2090
+ ${_SHUNIT_LINENO_}
+ if command [ $# -lt 2 -o $# -gt 3 ]; then
+ _shunit_error "failSame() requires two or three arguments; $# given"
+ return ${SHUNIT_ERROR}
+ fi
+ _shunit_shouldSkip && return ${SHUNIT_TRUE}
+
+ shunit_message_=${__shunit_lineno}
+ if command [ $# -eq 3 ]; then
+ shunit_message_="${shunit_message_}$1"
+ shift
+ fi
+
+ shunit_message_=${shunit_message_%% }
+ _shunit_assertFail "${shunit_message_:+${shunit_message_} }expected not same"
+
+ unset shunit_message_
+ return ${SHUNIT_FALSE}
+}
+# shellcheck disable=SC2016,SC2034
+_FAIL_SAME_='eval failSame --lineno "${LINENO:-}"'
+
+# Records a test failure, stating two values were not equal.
+#
+# This is functionally equivalent to calling failNotEquals().
+#
+# Args:
+# message: string: failure message [optional]
+# expected: string: expected value
+# actual: string: actual value
+# Returns:
+# integer: success (TRUE/FALSE/ERROR constant)
+failNotSame() {
+ # shellcheck disable=SC2090
+ ${_SHUNIT_LINENO_}
+ if command [ $# -lt 2 -o $# -gt 3 ]; then
+ _shunit_error "failNotSame() requires one or two arguments; $# given"
+ return ${SHUNIT_ERROR}
+ fi
+ _shunit_shouldSkip && return ${SHUNIT_TRUE}
+
+ shunit_message_=${__shunit_lineno}
+ if command [ $# -eq 3 ]; then
+ shunit_message_="${shunit_message_}$1"
+ shift
+ fi
+ failNotEquals "${shunit_message_}" "$1" "$2"
+ shunit_return=$?
+
+ unset shunit_message_
+ return ${shunit_return}
+}
+# shellcheck disable=SC2016,SC2034
+_FAIL_NOT_SAME_='eval failNotSame --lineno "${LINENO:-}"'
+
+#-----------------------------------------------------------------------------
+# Skipping functions.
+#
+
+# Force remaining assert and fail functions to be "skipped".
+#
+# This function forces the remaining assert and fail functions to be "skipped",
+# i.e. they will have no effect. Each function skipped will be recorded so that
+# the total of asserts and fails will not be altered.
+#
+# Args:
+# None
+startSkipping() { __shunit_skip=${SHUNIT_TRUE}; }
+
+# Resume the normal recording behavior of assert and fail calls.
+#
+# Args:
+# None
+endSkipping() { __shunit_skip=${SHUNIT_FALSE}; }
+
+# Returns the state of assert and fail call skipping.
+#
+# Args:
+# None
+# Returns:
+# boolean: (TRUE/FALSE constant)
+isSkipping() { return ${__shunit_skip}; }
+
+#-----------------------------------------------------------------------------
+# Suite functions.
+#
+
+# Stub. This function should contains all unit test calls to be made.
+#
+# DEPRECATED (as of 2.1.0)
+#
+# This function can be optionally overridden by the user in their test suite.
+#
+# If this function exists, it will be called when shunit2 is sourced. If it
+# does not exist, shunit2 will search the parent script for all functions
+# beginning with the word 'test', and they will be added dynamically to the
+# test suite.
+#
+# This function should be overridden by the user in their unit test suite.
+# Note: see _shunit_mktempFunc() for actual implementation
+#
+# Args:
+# None
+#suite() { :; } # DO NOT UNCOMMENT THIS FUNCTION
+
+# Adds a function name to the list of tests schedule for execution.
+#
+# This function should only be called from within the suite() function.
+#
+# Args:
+# function: string: name of a function to add to current unit test suite
+suite_addTest() {
+ shunit_func_=${1:-}
+
+ __shunit_suite="${__shunit_suite:+${__shunit_suite} }${shunit_func_}"
+ __shunit_testsTotal=`expr ${__shunit_testsTotal} + 1`
+
+ unset shunit_func_
+}
+
+# Stub. This function will be called once before any tests are run.
+#
+# Common one-time environment preparation tasks shared by all tests can be
+# defined here.
+#
+# This function should be overridden by the user in their unit test suite.
+# Note: see _shunit_mktempFunc() for actual implementation
+#
+# Args:
+# None
+#oneTimeSetUp() { :; } # DO NOT UNCOMMENT THIS FUNCTION
+
+# Stub. This function will be called once after all tests are finished.
+#
+# Common one-time environment cleanup tasks shared by all tests can be defined
+# here.
+#
+# This function should be overridden by the user in their unit test suite.
+# Note: see _shunit_mktempFunc() for actual implementation
+#
+# Args:
+# None
+#oneTimeTearDown() { :; } # DO NOT UNCOMMENT THIS FUNCTION
+
+# Stub. This function will be called before each test is run.
+#
+# Common environment preparation tasks shared by all tests can be defined here.
+#
+# This function should be overridden by the user in their unit test suite.
+# Note: see _shunit_mktempFunc() for actual implementation
+#
+# Args:
+# None
+#setUp() { :; } # DO NOT UNCOMMENT THIS FUNCTION
+
+# Note: see _shunit_mktempFunc() for actual implementation
+# Stub. This function will be called after each test is run.
+#
+# Common environment cleanup tasks shared by all tests can be defined here.
+#
+# This function should be overridden by the user in their unit test suite.
+# Note: see _shunit_mktempFunc() for actual implementation
+#
+# Args:
+# None
+#tearDown() { :; } # DO NOT UNCOMMENT THIS FUNCTION
+
+#------------------------------------------------------------------------------
+# Internal shUnit2 functions.
+#
+
+# Create a temporary directory to store various run-time files in.
+#
+# This function is a cross-platform temporary directory creation tool. Not all
+# OSes have the `mktemp` function, so one is included here.
+#
+# Args:
+# None
+# Outputs:
+# string: the temporary directory that was created
+_shunit_mktempDir() {
+ # Try the standard `mktemp` function.
+ ( exec mktemp -dqt shunit.XXXXXX 2>/dev/null ) && return
+
+ # The standard `mktemp` didn't work. Use our own.
+ # shellcheck disable=SC2039
+ if command [ -r '/dev/urandom' -a -x '/usr/bin/od' ]; then
+ _shunit_random_=`/usr/bin/od -vAn -N4 -tx4 </dev/urandom \
+ |command sed 's/^[^0-9a-f]*//'`
+ elif command [ -n "${RANDOM:-}" ]; then
+ # $RANDOM works
+ _shunit_random_=${RANDOM}${RANDOM}${RANDOM}$$
+ else
+ # `$RANDOM` doesn't work.
+ _shunit_date_=`date '+%Y%m%d%H%M%S'`
+ _shunit_random_=`expr "${_shunit_date_}" / $$`
+ fi
+
+ _shunit_tmpDir_="${TMPDIR:-/tmp}/shunit.${_shunit_random_}"
+ ( umask 077 && command mkdir "${_shunit_tmpDir_}" ) || \
+ _shunit_fatal 'could not create temporary directory! exiting'
+
+ echo "${_shunit_tmpDir_}"
+ unset _shunit_date_ _shunit_random_ _shunit_tmpDir_
+}
+
+# This function is here to work around issues in Cygwin.
+#
+# Args:
+# None
+_shunit_mktempFunc() {
+ for _shunit_func_ in oneTimeSetUp oneTimeTearDown setUp tearDown suite noexec
+ do
+ _shunit_file_="${__shunit_tmpDir}/${_shunit_func_}"
+ command cat <<EOF >"${_shunit_file_}"
+#! /bin/sh
+exit ${SHUNIT_TRUE}
+EOF
+ command chmod +x "${_shunit_file_}"
+ done
+
+ unset _shunit_file_
+}
+
+# Final cleanup function to leave things as we found them.
+#
+# Besides removing the temporary directory, this function is in charge of the
+# final exit code of the unit test. The exit code is based on how the script
+# was ended (e.g. normal exit, or via Ctrl-C).
+#
+# Args:
+# name: string: name of the trap called (specified when trap defined)
+_shunit_cleanup() {
+ _shunit_name_=$1
+
+ case "${_shunit_name_}" in
+ EXIT) ;;
+ INT) _shunit_signal_=130 ;; # 2+128
+ TERM) _shunit_signal_=143 ;; # 15+128
+ *)
+ _shunit_error "unrecognized trap value (${_shunit_name_})"
+ _shunit_signal_=0
+ ;;
+ esac
+ if command [ "${_shunit_name_}" != 'EXIT' ]; then
+ _shunit_warn "trapped and now handling the (${_shunit_name_}) signal"
+ fi
+
+ # Do our work.
+ if command [ ${__shunit_clean} -eq ${SHUNIT_FALSE} ]; then
+ # Ensure tear downs are only called once.
+ __shunit_clean=${SHUNIT_TRUE}
+
+ tearDown
+ command [ $? -eq ${SHUNIT_TRUE} ] \
+ || _shunit_warn "tearDown() returned non-zero return code."
+ oneTimeTearDown
+ command [ $? -eq ${SHUNIT_TRUE} ] \
+ || _shunit_warn "oneTimeTearDown() returned non-zero return code."
+
+ command rm -fr "${__shunit_tmpDir}"
+ fi
+
+ if command [ "${_shunit_name_}" != 'EXIT' ]; then
+ # Handle all non-EXIT signals.
+ trap - 0 # Disable EXIT trap.
+ exit ${_shunit_signal_}
+ elif command [ ${__shunit_reportGenerated} -eq ${SHUNIT_FALSE} ]; then
+ _shunit_assertFail 'unknown failure encountered running a test'
+ _shunit_generateReport
+ exit ${SHUNIT_ERROR}
+ fi
+
+ unset _shunit_name_ _shunit_signal_
+}
+
+# configureColor based on user color preference.
+#
+# Args:
+# color: string: color mode (one of `always`, `auto`, or `none`).
+_shunit_configureColor() {
+ _shunit_color_=${SHUNIT_FALSE} # By default, no color.
+ case $1 in
+ 'always') _shunit_color_=${SHUNIT_TRUE} ;;
+ 'auto')
+ command [ "`_shunit_colors`" -ge 8 ] && _shunit_color_=${SHUNIT_TRUE}
+ ;;
+ 'none') ;;
+ *) _shunit_fatal "unrecognized color option '$1'" ;;
+ esac
+
+ case ${_shunit_color_} in
+ ${SHUNIT_TRUE})
+ __shunit_ansi_none=${__SHUNIT_ANSI_NONE}
+ __shunit_ansi_red=${__SHUNIT_ANSI_RED}
+ __shunit_ansi_green=${__SHUNIT_ANSI_GREEN}
+ __shunit_ansi_yellow=${__SHUNIT_ANSI_YELLOW}
+ __shunit_ansi_cyan=${__SHUNIT_ANSI_CYAN}
+ ;;
+ ${SHUNIT_FALSE})
+ __shunit_ansi_none=''
+ __shunit_ansi_red=''
+ __shunit_ansi_green=''
+ __shunit_ansi_yellow=''
+ __shunit_ansi_cyan=''
+ ;;
+ esac
+
+ unset _shunit_color_ _shunit_tput_
+}
+
+# colors returns the number of supported colors for the TERM.
+_shunit_colors() {
+ _shunit_tput_=`${SHUNIT_CMD_TPUT} colors 2>/dev/null`
+ if command [ $? -eq 0 ]; then
+ echo "${_shunit_tput_}"
+ else
+ echo 16
+ fi
+ unset _shunit_tput_
+}
+
+# The actual running of the tests happens here.
+#
+# Args:
+# None
+_shunit_execSuite() {
+ for _shunit_test_ in ${__shunit_suite}; do
+ __shunit_testSuccess=${SHUNIT_TRUE}
+
+ # Disable skipping.
+ endSkipping
+
+ # Execute the per-test setup function.
+ setUp
+ command [ $? -eq ${SHUNIT_TRUE} ] \
+ || _shunit_fatal "setup() returned non-zero return code."
+
+ # Execute the test.
+ echo "${__SHUNIT_TEST_PREFIX}${_shunit_test_}"
+ eval "${_shunit_test_}"
+ if command [ $? -ne ${SHUNIT_TRUE} ]; then
+ _shunit_error "${_shunit_test_}() returned non-zero return code."
+ __shunit_testSuccess=${SHUNIT_ERROR}
+ _shunit_incFailedCount
+ fi
+
+ # Execute the per-test tear-down function.
+ tearDown
+ command [ $? -eq ${SHUNIT_TRUE} ] \
+ || _shunit_fatal "tearDown() returned non-zero return code."
+
+ # Update stats.
+ if command [ ${__shunit_testSuccess} -eq ${SHUNIT_TRUE} ]; then
+ __shunit_testsPassed=`expr ${__shunit_testsPassed} + 1`
+ else
+ __shunit_testsFailed=`expr ${__shunit_testsFailed} + 1`
+ fi
+ done
+
+ unset _shunit_test_
+}
+
+# Generates the user friendly report with appropriate OK/FAILED message.
+#
+# Args:
+# None
+# Output:
+# string: the report of successful and failed tests, as well as totals.
+_shunit_generateReport() {
+ command [ "${__shunit_reportGenerated}" -eq ${SHUNIT_TRUE} ] && return
+
+ _shunit_ok_=${SHUNIT_TRUE}
+
+ # If no exit code was provided, determine an appropriate one.
+ command [ "${__shunit_testsFailed}" -gt 0 \
+ -o ${__shunit_testSuccess} -eq ${SHUNIT_FALSE} ] \
+ && _shunit_ok_=${SHUNIT_FALSE}
+
+ echo
+ _shunit_msg_="Ran ${__shunit_ansi_cyan}${__shunit_testsTotal}${__shunit_ansi_none}"
+ if command [ "${__shunit_testsTotal}" -eq 1 ]; then
+ ${__SHUNIT_CMD_ECHO_ESC} "${_shunit_msg_} test."
+ else
+ ${__SHUNIT_CMD_ECHO_ESC} "${_shunit_msg_} tests."
+ fi
+
+ if command [ ${_shunit_ok_} -eq ${SHUNIT_TRUE} ]; then
+ _shunit_msg_="${__shunit_ansi_green}OK${__shunit_ansi_none}"
+ command [ "${__shunit_assertsSkipped}" -gt 0 ] \
+ && _shunit_msg_="${_shunit_msg_} (${__shunit_ansi_yellow}skipped=${__shunit_assertsSkipped}${__shunit_ansi_none})"
+ else
+ _shunit_msg_="${__shunit_ansi_red}FAILED${__shunit_ansi_none}"
+ _shunit_msg_="${_shunit_msg_} (${__shunit_ansi_red}failures=${__shunit_assertsFailed}${__shunit_ansi_none}"
+ command [ "${__shunit_assertsSkipped}" -gt 0 ] \
+ && _shunit_msg_="${_shunit_msg_},${__shunit_ansi_yellow}skipped=${__shunit_assertsSkipped}${__shunit_ansi_none}"
+ _shunit_msg_="${_shunit_msg_})"
+ fi
+
+ echo
+ ${__SHUNIT_CMD_ECHO_ESC} "${_shunit_msg_}"
+ __shunit_reportGenerated=${SHUNIT_TRUE}
+
+ unset _shunit_msg_ _shunit_ok_
+}
+
+# Test for whether a function should be skipped.
+#
+# Args:
+# None
+# Returns:
+# boolean: whether the test should be skipped (TRUE/FALSE constant)
+_shunit_shouldSkip() {
+ command [ ${__shunit_skip} -eq ${SHUNIT_FALSE} ] && return ${SHUNIT_FALSE}
+ _shunit_assertSkip
+}
+
+# Records a successful test.
+#
+# Args:
+# None
+_shunit_assertPass() {
+ __shunit_assertsPassed=`expr ${__shunit_assertsPassed} + 1`
+ __shunit_assertsTotal=`expr ${__shunit_assertsTotal} + 1`
+}
+
+# Records a test failure.
+#
+# Args:
+# message: string: failure message to provide user
+_shunit_assertFail() {
+ __shunit_testSuccess=${SHUNIT_FALSE}
+ _shunit_incFailedCount
+
+ \[ $# -gt 0 ] && ${__SHUNIT_CMD_ECHO_ESC} \
+ "${__shunit_ansi_red}ASSERT:${__shunit_ansi_none}$*"
+}
+
+# Increment the count of failed asserts.
+#
+# Args:
+# none
+_shunit_incFailedCount() {
+ __shunit_assertsFailed=`expr "${__shunit_assertsFailed}" + 1`
+ __shunit_assertsTotal=`expr "${__shunit_assertsTotal}" + 1`
+}
+
+
+# Records a skipped test.
+#
+# Args:
+# None
+_shunit_assertSkip() {
+ __shunit_assertsSkipped=`expr "${__shunit_assertsSkipped}" + 1`
+ __shunit_assertsTotal=`expr "${__shunit_assertsTotal}" + 1`
+}
+
+# Prepare a script filename for sourcing.
+#
+# Args:
+# script: string: path to a script to source
+# Returns:
+# string: filename prefixed with ./ (if necessary)
+_shunit_prepForSourcing() {
+ _shunit_script_=$1
+ case "${_shunit_script_}" in
+ /*|./*) echo "${_shunit_script_}" ;;
+ *) echo "./${_shunit_script_}" ;;
+ esac
+ unset _shunit_script_
+}
+
+# Escape a character in a string.
+#
+# Args:
+# c: string: unescaped character
+# s: string: to escape character in
+# Returns:
+# string: with escaped character(s)
+_shunit_escapeCharInStr() {
+ command [ -n "$2" ] || return # No point in doing work on an empty string.
+
+ # Note: using shorter variable names to prevent conflicts with
+ # _shunit_escapeCharactersInString().
+ _shunit_c_=$1
+ _shunit_s_=$2
+
+ # Escape the character.
+ # shellcheck disable=SC1003,SC2086
+ echo ''${_shunit_s_}'' |command sed 's/\'${_shunit_c_}'/\\\'${_shunit_c_}'/g'
+
+ unset _shunit_c_ _shunit_s_
+}
+
+# Escape a character in a string.
+#
+# Args:
+# str: string: to escape characters in
+# Returns:
+# string: with escaped character(s)
+_shunit_escapeCharactersInString() {
+ command [ -n "$1" ] || return # No point in doing work on an empty string.
+
+ _shunit_str_=$1
+
+ # Note: using longer variable names to prevent conflicts with
+ # _shunit_escapeCharInStr().
+ for _shunit_char_ in '"' '$' "'" '`'; do
+ _shunit_str_=`_shunit_escapeCharInStr "${_shunit_char_}" "${_shunit_str_}"`
+ done
+
+ echo "${_shunit_str_}"
+ unset _shunit_char_ _shunit_str_
+}
+
+# Extract list of functions to run tests against.
+#
+# Args:
+# script: string: name of script to extract functions from
+# Returns:
+# string: of function names
+_shunit_extractTestFunctions() {
+ _shunit_script_=$1
+
+ # Extract the lines with test function names, strip of anything besides the
+ # function name, and output everything on a single line.
+ _shunit_regex_='^\s*((function test[A-Za-z0-9_-]*)|(test[A-Za-z0-9_-]* *\(\)))'
+ # shellcheck disable=SC2196
+ egrep "${_shunit_regex_}" "${_shunit_script_}" \
+ |command sed 's/^[^A-Za-z0-9_-]*//;s/^function //;s/\([A-Za-z0-9_-]*\).*/\1/g' \
+ |xargs
+
+ unset _shunit_regex_ _shunit_script_
+}
+
+#------------------------------------------------------------------------------
+# Main.
+#
+
+# Determine the operating mode.
+if command [ $# -eq 0 -o "${1:-}" = '--' ]; then
+ __shunit_script=${__SHUNIT_PARENT}
+ __shunit_mode=${__SHUNIT_MODE_SOURCED}
+else
+ __shunit_script=$1
+ command [ -r "${__shunit_script}" ] || \
+ _shunit_fatal "unable to read from ${__shunit_script}"
+ __shunit_mode=${__SHUNIT_MODE_STANDALONE}
+fi
+
+# Create a temporary storage location.
+__shunit_tmpDir=`_shunit_mktempDir`
+
+# Provide a public temporary directory for unit test scripts.
+# TODO(kward): document this.
+SHUNIT_TMPDIR="${__shunit_tmpDir}/tmp"
+command mkdir "${SHUNIT_TMPDIR}"
+
+# Setup traps to clean up after ourselves.
+trap '_shunit_cleanup EXIT' 0
+trap '_shunit_cleanup INT' 2
+trap '_shunit_cleanup TERM' 15
+
+# Create phantom functions to work around issues with Cygwin.
+_shunit_mktempFunc
+PATH="${__shunit_tmpDir}:${PATH}"
+
+# Make sure phantom functions are executable. This will bite if `/tmp` (or the
+# current `$TMPDIR`) points to a path on a partition that was mounted with the
+# 'noexec' option. The noexec command was created with `_shunit_mktempFunc()`.
+noexec 2>/dev/null || _shunit_fatal \
+ 'Please declare TMPDIR with path on partition with exec permission.'
+
+# We must manually source the tests in standalone mode.
+if command [ "${__shunit_mode}" = "${__SHUNIT_MODE_STANDALONE}" ]; then
+ # shellcheck disable=SC1090
+ command . "`_shunit_prepForSourcing \"${__shunit_script}\"`"
+fi
+
+# Configure default output coloring behavior.
+_shunit_configureColor "${SHUNIT_COLOR}"
+
+# Execute the oneTimeSetUp function (if it exists).
+oneTimeSetUp
+command [ $? -eq ${SHUNIT_TRUE} ] \
+ || _shunit_fatal "oneTimeSetUp() returned non-zero return code."
+
+# Command line selected tests or suite selected tests
+if command [ "$#" -ge 2 ]; then
+ # Argument $1 is either the filename of tests or '--'; either way, skip it.
+ shift
+ # Remaining arguments ($2 .. $#) are assumed to be test function names.
+ # Interate through all remaining args in "$@" in a POSIX (likely portable) way.
+ # Helpful tip: https://unix.stackexchange.com/questions/314032/how-to-use-arguments-like-1-2-in-a-for-loop
+ for _shunit_arg_ do
+ suite_addTest "${_shunit_arg_}"
+ done
+ unset _shunit_arg_
+else
+ # Execute the suite function defined in the parent test script.
+ # DEPRECATED as of 2.1.0.
+ suite
+fi
+
+# If no tests or suite specified, dynamically build a list of functions.
+if command [ -z "${__shunit_suite}" ]; then
+ shunit_funcs_=`_shunit_extractTestFunctions "${__shunit_script}"`
+ for shunit_func_ in ${shunit_funcs_}; do
+ suite_addTest "${shunit_func_}"
+ done
+fi
+unset shunit_func_ shunit_funcs_
+
+# Execute the suite of unit tests.
+_shunit_execSuite
+
+# Execute the oneTimeTearDown function (if it exists).
+oneTimeTearDown
+command [ $? -eq ${SHUNIT_TRUE} ] \
+ || _shunit_fatal "oneTimeTearDown() returned non-zero return code."
+
+# Generate a report summary.
+_shunit_generateReport
+
+# That's it folks.
+command [ "${__shunit_testsFailed}" -eq 0 ]
+exit $?
diff --git a/fluent-bit/tests/lib/shunit2/shunit2_args_test.sh b/fluent-bit/tests/lib/shunit2/shunit2_args_test.sh
new file mode 100755
index 00000000..fc252232
--- /dev/null
+++ b/fluent-bit/tests/lib/shunit2/shunit2_args_test.sh
@@ -0,0 +1,59 @@
+#!/bin/sh
+#
+# shunit2 unit test for running subset(s) of tests based upon command line arguments.
+# Also shows how non-default tests or a arbitrary subset of tests can be run.
+#
+# Disable source following.
+# shellcheck disable=SC1090,SC1091
+
+# Load test helpers.
+. ./shunit2_test_helpers
+
+# This test does not nomrally run because it does not begin "test*"
+# Will be run by settting the arguments to the script to include the name of this test.
+non_default_test() {
+ # arbitrary assert
+ assertTrue 0
+ # true intent is to set this variable, which will be tested below
+ NON_DEFAULT_TEST_RAN="yup, we ran"
+}
+
+# Test that the "non_default_test" ran, otherwise fail
+test_non_default_ran() {
+ assertNotNull "'non_default_test' did not run" "$NON_DEFAULT_TEST_RAN"
+}
+
+# fail if this test runs, which is shouldn't if args are set correctly.
+test_will_fail() {
+ fail "test_will_fail should not be run if arg-parsing works"
+}
+
+oneTimeSetUp() {
+ th_oneTimeSetUp
+ # prime with "null" value
+ NON_DEFAULT_TEST_RAN=""
+}
+
+# Load and run shunit2.
+# shellcheck disable=SC2034
+[ -n "${ZSH_VERSION:-}" ] && SHUNIT_PARENT=$0
+
+# If zero/one argument(s) are provided, this test is being run in it's
+# entirety, and therefore we want to set the arguments to the script
+# to (simulate and) test the processing of command-line specified
+# tests. If we don't, then the "test_will_fail" test will run (by
+# default) and the overall test will fail.
+#
+# However, if two or more arguments are provided, then assume this
+# test script is being run by hand to experiment with command-line
+# test specification, and then don't override the user provided
+# arguments.
+if command [ "$#" -le 1 ]; then
+ # We set the arguments in a POSIX way, inasmuch as we can;
+ # helpful tip:
+ # https://unix.stackexchange.com/questions/258512/how-to-remove-a-positional-parameter-from
+ set -- "--" "non_default_test" "test_non_default_ran"
+fi
+
+# Load and run tests, but only if running as a script, not if being sourced by shunit2
+command [ -z "${SHUNIT_VERSION:-}" ] && . "${TH_SHUNIT}"
diff --git a/fluent-bit/tests/lib/shunit2/shunit2_asserts_test.sh b/fluent-bit/tests/lib/shunit2/shunit2_asserts_test.sh
new file mode 100755
index 00000000..7b982ead
--- /dev/null
+++ b/fluent-bit/tests/lib/shunit2/shunit2_asserts_test.sh
@@ -0,0 +1,258 @@
+#! /bin/sh
+# vim:et:ft=sh:sts=2:sw=2
+#
+# shunit2 unit test for assert functions.
+#
+# Copyright 2008-2017 Kate Ward. All Rights Reserved.
+# Released under the Apache 2.0 license.
+#
+# Author: kate.ward@forestent.com (Kate Ward)
+# https://github.com/kward/shunit2
+#
+# Disable source following.
+# shellcheck disable=SC1090,SC1091
+
+# These variables will be overridden by the test helpers.
+stdoutF="${TMPDIR:-/tmp}/STDOUT"
+stderrF="${TMPDIR:-/tmp}/STDERR"
+
+# Load test helpers.
+. ./shunit2_test_helpers
+
+commonEqualsSame() {
+ fn=$1
+
+ ( ${fn} 'x' 'x' >"${stdoutF}" 2>"${stderrF}" )
+ th_assertTrueWithNoOutput 'equal' $? "${stdoutF}" "${stderrF}"
+
+ ( ${fn} "${MSG}" 'x' 'x' >"${stdoutF}" 2>"${stderrF}" )
+ th_assertTrueWithNoOutput 'equal; with msg' $? "${stdoutF}" "${stderrF}"
+
+ ( ${fn} 'abc def' 'abc def' >"${stdoutF}" 2>"${stderrF}" )
+ th_assertTrueWithNoOutput 'equal with spaces' $? "${stdoutF}" "${stderrF}"
+
+ ( ${fn} 'x' 'y' >"${stdoutF}" 2>"${stderrF}" )
+ th_assertFalseWithOutput 'not equal' $? "${stdoutF}" "${stderrF}"
+
+ ( ${fn} '' '' >"${stdoutF}" 2>"${stderrF}" )
+ th_assertTrueWithNoOutput 'null values' $? "${stdoutF}" "${stderrF}"
+
+ ( ${fn} arg1 >"${stdoutF}" 2>"${stderrF}" )
+ th_assertFalseWithError 'too few arguments' $? "${stdoutF}" "${stderrF}"
+
+ ( ${fn} arg1 arg2 arg3 arg4 >"${stdoutF}" 2>"${stderrF}" )
+ th_assertFalseWithError 'too many arguments' $? "${stdoutF}" "${stderrF}"
+}
+
+commonNotEqualsSame() {
+ fn=$1
+
+ ( ${fn} 'x' 'y' >"${stdoutF}" 2>"${stderrF}" )
+ th_assertTrueWithNoOutput 'not same' $? "${stdoutF}" "${stderrF}"
+
+ ( ${fn} "${MSG}" 'x' 'y' >"${stdoutF}" 2>"${stderrF}" )
+ th_assertTrueWithNoOutput 'not same, with msg' $? "${stdoutF}" "${stderrF}"
+
+ ( ${fn} 'x' 'x' >"${stdoutF}" 2>"${stderrF}" )
+ th_assertFalseWithOutput 'same' $? "${stdoutF}" "${stderrF}"
+
+ ( ${fn} '' '' >"${stdoutF}" 2>"${stderrF}" )
+ th_assertFalseWithOutput 'null values' $? "${stdoutF}" "${stderrF}"
+
+ ( ${fn} arg1 >"${stdoutF}" 2>"${stderrF}" )
+ th_assertFalseWithError 'too few arguments' $? "${stdoutF}" "${stderrF}"
+
+ ( ${fn} arg1 arg2 arg3 arg4 >"${stdoutF}" 2>"${stderrF}" )
+ th_assertFalseWithError 'too many arguments' $? "${stdoutF}" "${stderrF}"
+}
+
+testAssertEquals() {
+ commonEqualsSame 'assertEquals'
+}
+
+testAssertNotEquals() {
+ commonNotEqualsSame 'assertNotEquals'
+}
+
+testAssertSame() {
+ commonEqualsSame 'assertSame'
+}
+
+testAssertNotSame() {
+ commonNotEqualsSame 'assertNotSame'
+}
+
+testAssertContains() {
+ ( assertContains 'abcdef' 'abc' >"${stdoutF}" 2>"${stderrF}" )
+ th_assertTrueWithNoOutput 'found' $? "${stdoutF}" "${stderrF}"
+
+ ( assertContains 'abcdef' 'bcd' >"${stdoutF}" 2>"${stderrF}" )
+ th_assertTrueWithNoOutput 'found' $? "${stdoutF}" "${stderrF}"
+
+ ( assertContains 'abcdef' 'def' >"${stdoutF}" 2>"${stderrF}" )
+ th_assertTrueWithNoOutput 'found' $? "${stdoutF}" "${stderrF}"
+
+ ( assertContains 'abc -Xabc def' '-Xabc' >"${stdoutF}" 2>"${stderrF}" )
+ th_assertTrueWithNoOutput 'content starts with "-"' $? "${stdoutF}" "${stderrF}"
+
+ ( assertContains "${MSG}" 'abcdef' 'abc' >"${stdoutF}" 2>"${stderrF}" )
+ th_assertTrueWithNoOutput 'found, with msg' $? "${stdoutF}" "${stderrF}"
+
+ ( assertContains 'abcdef' 'xyz' >"${stdoutF}" 2>"${stderrF}" )
+ th_assertFalseWithOutput 'not found' $? "${stdoutF}" "${stderrF}"
+
+ ( assertContains 'abcdef' 'zab' >"${stdoutF}" 2>"${stderrF}" )
+ th_assertFalseWithOutput 'not found' $? "${stdoutF}" "${stderrF}"
+
+ ( assertContains 'abcdef' 'efg' >"${stdoutF}" 2>"${stderrF}" )
+ th_assertFalseWithOutput 'not found' $? "${stdoutF}" "${stderrF}"
+
+ ( assertContains 'abcdef' 'acf' >"${stdoutF}" 2>"${stderrF}" )
+ th_assertFalseWithOutput 'not found' $? "${stdoutF}" "${stderrF}"
+
+ ( assertContains 'abcdef' >"${stdoutF}" 2>"${stderrF}" )
+ th_assertFalseWithError 'too few arguments' $? "${stdoutF}" "${stderrF}"
+
+ ( assertContains arg1 arg2 arg3 arg4 >"${stdoutF}" 2>"${stderrF}" )
+ th_assertFalseWithError 'too many arguments' $? "${stdoutF}" "${stderrF}"
+}
+
+testAssertNotContains() {
+ ( assertNotContains 'abcdef' 'xyz' >"${stdoutF}" 2>"${stderrF}" )
+ th_assertTrueWithNoOutput 'not found' $? "${stdoutF}" "${stderrF}"
+
+ ( assertNotContains 'abcdef' 'zab' >"${stdoutF}" 2>"${stderrF}" )
+ th_assertTrueWithNoOutput 'not found' $? "${stdoutF}" "${stderrF}"
+
+ ( assertNotContains 'abcdef' 'efg' >"${stdoutF}" 2>"${stderrF}" )
+ th_assertTrueWithNoOutput 'not found' $? "${stdoutF}" "${stderrF}"
+
+ ( assertNotContains 'abcdef' 'acf' >"${stdoutF}" 2>"${stderrF}" )
+ th_assertTrueWithNoOutput 'not found' $? "${stdoutF}" "${stderrF}"
+
+ ( assertNotContains "${MSG}" 'abcdef' 'xyz' >"${stdoutF}" 2>"${stderrF}" )
+ th_assertTrueWithNoOutput 'not found, with msg' $? "${stdoutF}" "${stderrF}"
+
+ ( assertNotContains 'abcdef' 'abc' >"${stdoutF}" 2>"${stderrF}" )
+ th_assertFalseWithOutput 'found' $? "${stdoutF}" "${stderrF}"
+
+ ( assertNotContains 'abcdef' >"${stdoutF}" 2>"${stderrF}" )
+ th_assertFalseWithError 'too few arguments' $? "${stdoutF}" "${stderrF}"
+
+ ( assertNotContains arg1 arg2 arg3 arg4 >"${stdoutF}" 2>"${stderrF}" )
+ th_assertFalseWithError 'too many arguments' $? "${stdoutF}" "${stderrF}"
+}
+
+testAssertNull() {
+ ( assertNull '' >"${stdoutF}" 2>"${stderrF}" )
+ th_assertTrueWithNoOutput 'null' $? "${stdoutF}" "${stderrF}"
+
+ ( assertNull "${MSG}" '' >"${stdoutF}" 2>"${stderrF}" )
+ th_assertTrueWithNoOutput 'null, with msg' $? "${stdoutF}" "${stderrF}"
+
+ ( assertNull 'x' >"${stdoutF}" 2>"${stderrF}" )
+ th_assertFalseWithOutput 'not null' $? "${stdoutF}" "${stderrF}"
+
+ ( assertNull >"${stdoutF}" 2>"${stderrF}" )
+ th_assertFalseWithError 'too few arguments' $? "${stdoutF}" "${stderrF}"
+
+ ( assertNull arg1 arg2 arg3 >"${stdoutF}" 2>"${stderrF}" )
+ th_assertFalseWithError 'too many arguments' $? "${stdoutF}" "${stderrF}"
+}
+
+testAssertNotNull()
+{
+ ( assertNotNull 'x' >"${stdoutF}" 2>"${stderrF}" )
+ th_assertTrueWithNoOutput 'not null' $? "${stdoutF}" "${stderrF}"
+
+ ( assertNotNull "${MSG}" 'x' >"${stdoutF}" 2>"${stderrF}" )
+ th_assertTrueWithNoOutput 'not null, with msg' $? "${stdoutF}" "${stderrF}"
+
+ ( assertNotNull 'x"b' >"${stdoutF}" 2>"${stderrF}" )
+ th_assertTrueWithNoOutput 'not null, with double-quote' $? \
+ "${stdoutF}" "${stderrF}"
+
+ ( assertNotNull "x'b" >"${stdoutF}" 2>"${stderrF}" )
+ th_assertTrueWithNoOutput 'not null, with single-quote' $? \
+ "${stdoutF}" "${stderrF}"
+
+ # shellcheck disable=SC2016
+ ( assertNotNull 'x$b' >"${stdoutF}" 2>"${stderrF}" )
+ th_assertTrueWithNoOutput 'not null, with dollar' $? \
+ "${stdoutF}" "${stderrF}"
+
+ ( assertNotNull 'x`b' >"${stdoutF}" 2>"${stderrF}" )
+ th_assertTrueWithNoOutput 'not null, with backtick' $? \
+ "${stdoutF}" "${stderrF}"
+
+ ( assertNotNull '' >"${stdoutF}" 2>"${stderrF}" )
+ th_assertFalseWithOutput 'null' $? "${stdoutF}" "${stderrF}"
+
+ # There is no test for too few arguments as $1 might actually be null.
+
+ ( assertNotNull arg1 arg2 arg3 >"${stdoutF}" 2>"${stderrF}" )
+ th_assertFalseWithError 'too many arguments' $? "${stdoutF}" "${stderrF}"
+}
+
+testAssertTrue() {
+ ( assertTrue 0 >"${stdoutF}" 2>"${stderrF}" )
+ th_assertTrueWithNoOutput 'true' $? "${stdoutF}" "${stderrF}"
+
+ ( assertTrue "${MSG}" 0 >"${stdoutF}" 2>"${stderrF}" )
+ th_assertTrueWithNoOutput 'true, with msg' $? "${stdoutF}" "${stderrF}"
+
+ ( assertTrue '[ 0 -eq 0 ]' >"${stdoutF}" 2>"${stderrF}" )
+ th_assertTrueWithNoOutput 'true condition' $? "${stdoutF}" "${stderrF}"
+
+ ( assertTrue 1 >"${stdoutF}" 2>"${stderrF}" )
+ th_assertFalseWithOutput 'false' $? "${stdoutF}" "${stderrF}"
+
+ ( assertTrue '[ 0 -eq 1 ]' >"${stdoutF}" 2>"${stderrF}" )
+ th_assertFalseWithOutput 'false condition' $? "${stdoutF}" "${stderrF}"
+
+ ( assertTrue '' >"${stdoutF}" 2>"${stderrF}" )
+ th_assertFalseWithOutput 'null' $? "${stdoutF}" "${stderrF}"
+
+ ( assertTrue >"${stdoutF}" 2>"${stderrF}" )
+ th_assertFalseWithError 'too few arguments' $? "${stdoutF}" "${stderrF}"
+
+ ( assertTrue arg1 arg2 arg3 >"${stdoutF}" 2>"${stderrF}" )
+ th_assertFalseWithError 'too many arguments' $? "${stdoutF}" "${stderrF}"
+}
+
+testAssertFalse() {
+ ( assertFalse 1 >"${stdoutF}" 2>"${stderrF}" )
+ th_assertTrueWithNoOutput 'false' $? "${stdoutF}" "${stderrF}"
+
+ ( assertFalse "${MSG}" 1 >"${stdoutF}" 2>"${stderrF}" )
+ th_assertTrueWithNoOutput 'false, with msg' $? "${stdoutF}" "${stderrF}"
+
+ ( assertFalse '[ 0 -eq 1 ]' >"${stdoutF}" 2>"${stderrF}" )
+ th_assertTrueWithNoOutput 'false condition' $? "${stdoutF}" "${stderrF}"
+
+ ( assertFalse 0 >"${stdoutF}" 2>"${stderrF}" )
+ th_assertFalseWithOutput 'true' $? "${stdoutF}" "${stderrF}"
+
+ ( assertFalse '[ 0 -eq 0 ]' >"${stdoutF}" 2>"${stderrF}" )
+ th_assertFalseWithOutput 'true condition' $? "${stdoutF}" "${stderrF}"
+
+ ( assertFalse '' >"${stdoutF}" 2>"${stderrF}" )
+ th_assertFalseWithOutput 'true condition' $? "${stdoutF}" "${stderrF}"
+
+ ( assertFalse >"${stdoutF}" 2>"${stderrF}" )
+ th_assertFalseWithError 'too few arguments' $? "${stdoutF}" "${stderrF}"
+
+ ( assertFalse arg1 arg2 arg3 >"${stdoutF}" 2>"${stderrF}" )
+ th_assertFalseWithError 'too many arguments' $? "${stdoutF}" "${stderrF}"
+}
+
+oneTimeSetUp() {
+ th_oneTimeSetUp
+
+ MSG='This is a test message'
+}
+
+# Load and run shunit2.
+# shellcheck disable=SC2034
+[ -n "${ZSH_VERSION:-}" ] && SHUNIT_PARENT=$0
+. "${TH_SHUNIT}"
diff --git a/fluent-bit/tests/lib/shunit2/shunit2_failures_test.sh b/fluent-bit/tests/lib/shunit2/shunit2_failures_test.sh
new file mode 100755
index 00000000..3cedd2fa
--- /dev/null
+++ b/fluent-bit/tests/lib/shunit2/shunit2_failures_test.sh
@@ -0,0 +1,85 @@
+#! /bin/sh
+# vim:et:ft=sh:sts=2:sw=2
+#
+# Copyright 2008-2019 Kate Ward. All Rights Reserved.
+# Released under the Apache 2.0 license.
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# shUnit2 -- Unit testing framework for Unix shell scripts.
+# https://github.com/kward/shunit2
+#
+# Author: kate.ward@forestent.com (Kate Ward)
+#
+# shUnit2 unit test for failure functions
+#
+# Disable source following.
+# shellcheck disable=SC1090,SC1091
+
+# These variables will be overridden by the test helpers.
+stdoutF="${TMPDIR:-/tmp}/STDOUT"
+stderrF="${TMPDIR:-/tmp}/STDERR"
+
+# Load test helpers.
+. ./shunit2_test_helpers
+
+testFail() {
+ ( fail >"${stdoutF}" 2>"${stderrF}" )
+ th_assertFalseWithOutput 'fail' $? "${stdoutF}" "${stderrF}"
+
+ ( fail "${MSG}" >"${stdoutF}" 2>"${stderrF}" )
+ th_assertFalseWithOutput 'fail with msg' $? "${stdoutF}" "${stderrF}"
+
+ ( fail arg1 >"${stdoutF}" 2>"${stderrF}" )
+ th_assertFalseWithOutput 'too many arguments' $? "${stdoutF}" "${stderrF}"
+}
+
+testFailNotEquals() {
+ ( failNotEquals 'x' 'x' >"${stdoutF}" 2>"${stderrF}" )
+ th_assertFalseWithOutput 'same' $? "${stdoutF}" "${stderrF}"
+
+ ( failNotEquals "${MSG}" 'x' 'x' >"${stdoutF}" 2>"${stderrF}" )
+ th_assertFalseWithOutput 'same with msg' $? "${stdoutF}" "${stderrF}"
+
+ ( failNotEquals 'x' 'y' >"${stdoutF}" 2>"${stderrF}" )
+ th_assertFalseWithOutput 'not same' $? "${stdoutF}" "${stderrF}"
+
+ ( failNotEquals '' '' >"${stdoutF}" 2>"${stderrF}" )
+ th_assertFalseWithOutput 'null values' $? "${stdoutF}" "${stderrF}"
+
+ ( failNotEquals >"${stdoutF}" 2>"${stderrF}" )
+ th_assertFalseWithError 'too few arguments' $? "${stdoutF}" "${stderrF}"
+
+ ( failNotEquals arg1 arg2 arg3 arg4 >"${stdoutF}" 2>"${stderrF}" )
+ th_assertFalseWithError 'too many arguments' $? "${stdoutF}" "${stderrF}"
+}
+
+testFailSame() {
+ ( failSame 'x' 'x' >"${stdoutF}" 2>"${stderrF}" )
+ th_assertFalseWithOutput 'same' $? "${stdoutF}" "${stderrF}"
+
+ ( failSame "${MSG}" 'x' 'x' >"${stdoutF}" 2>"${stderrF}" )
+ th_assertFalseWithOutput 'same with msg' $? "${stdoutF}" "${stderrF}"
+
+ ( failSame 'x' 'y' >"${stdoutF}" 2>"${stderrF}" )
+ th_assertFalseWithOutput 'not same' $? "${stdoutF}" "${stderrF}"
+
+ ( failSame '' '' >"${stdoutF}" 2>"${stderrF}" )
+ th_assertFalseWithOutput 'null values' $? "${stdoutF}" "${stderrF}"
+
+ ( failSame >"${stdoutF}" 2>"${stderrF}" )
+ th_assertFalseWithError 'too few arguments' $? "${stdoutF}" "${stderrF}"
+
+ ( failSame arg1 arg2 arg3 arg4 >"${stdoutF}" 2>"${stderrF}" )
+ th_assertFalseWithError 'too many arguments' $? "${stdoutF}" "${stderrF}"
+}
+
+oneTimeSetUp() {
+ th_oneTimeSetUp
+
+ MSG='This is a test message'
+}
+
+# Load and run shUnit2.
+# shellcheck disable=SC2034
+[ -n "${ZSH_VERSION:-}" ] && SHUNIT_PARENT=$0
+. "${TH_SHUNIT}"
diff --git a/fluent-bit/tests/lib/shunit2/shunit2_macros_test.sh b/fluent-bit/tests/lib/shunit2/shunit2_macros_test.sh
new file mode 100755
index 00000000..dba7c9bf
--- /dev/null
+++ b/fluent-bit/tests/lib/shunit2/shunit2_macros_test.sh
@@ -0,0 +1,265 @@
+#! /bin/sh
+# vim:et:ft=sh:sts=2:sw=2
+#
+# shunit2 unit test for macros.
+#
+# Copyright 2008-2017 Kate Ward. All Rights Reserved.
+# Released under the Apache 2.0 license.
+#
+# Author: kate.ward@forestent.com (Kate Ward)
+# https://github.com/kward/shunit2
+#
+### ShellCheck http://www.shellcheck.net/
+# Disable source following.
+# shellcheck disable=SC1090,SC1091
+# Presence of LINENO variable is checked.
+# shellcheck disable=SC2039
+
+# These variables will be overridden by the test helpers.
+stdoutF="${TMPDIR:-/tmp}/STDOUT"
+stderrF="${TMPDIR:-/tmp}/STDERR"
+
+# Load test helpers.
+. ./shunit2_test_helpers
+
+testAssertEquals() {
+ # Start skipping if LINENO not available.
+ [ -z "${LINENO:-}" ] && startSkipping
+
+ ( ${_ASSERT_EQUALS_} 'x' 'y' >"${stdoutF}" 2>"${stderrF}" )
+ grep '^ASSERT:\[[0-9]*\] *' "${stdoutF}" >/dev/null
+ rtrn=$?
+ assertTrue '_ASSERT_EQUALS_ failure' ${rtrn}
+ [ "${rtrn}" -ne "${SHUNIT_TRUE}" ] && cat "${stderrF}" >&2
+
+ ( ${_ASSERT_EQUALS_} '"some msg"' 'x' 'y' >"${stdoutF}" 2>"${stderrF}" )
+ grep '^ASSERT:\[[0-9]*\] *' "${stdoutF}" >/dev/null
+ rtrn=$?
+ assertTrue '_ASSERT_EQUALS_ w/ msg failure' ${rtrn}
+ [ "${rtrn}" -ne "${SHUNIT_TRUE}" ] && cat "${stderrF}" >&2
+
+ return 0
+}
+
+testAssertNotEquals() {
+ # Start skipping if LINENO not available.
+ [ -z "${LINENO:-}" ] && startSkipping
+
+ ( ${_ASSERT_NOT_EQUALS_} 'x' 'x' >"${stdoutF}" 2>"${stderrF}" )
+ grep '^ASSERT:\[[0-9]*\] *' "${stdoutF}" >/dev/null
+ rtrn=$?
+ assertTrue '_ASSERT_NOT_EQUALS_ failure' ${rtrn}
+ [ "${rtrn}" -ne "${SHUNIT_TRUE}" ] && cat "${stderrF}" >&2
+
+ ( ${_ASSERT_NOT_EQUALS_} '"some msg"' 'x' 'x' >"${stdoutF}" 2>"${stderrF}" )
+ grep '^ASSERT:\[[0-9]*\] *' "${stdoutF}" >/dev/null
+ rtrn=$?
+ assertTrue '_ASSERT_NOT_EQUALS_ w/ msg failure' ${rtrn}
+ [ "${rtrn}" -ne "${SHUNIT_TRUE}" ] && cat "${stderrF}" >&2
+
+ return 0
+}
+
+testSame() {
+ # Start skipping if LINENO not available.
+ [ -z "${LINENO:-}" ] && startSkipping
+
+ ( ${_ASSERT_SAME_} 'x' 'y' >"${stdoutF}" 2>"${stderrF}" )
+ grep '^ASSERT:\[[0-9]*\] *' "${stdoutF}" >/dev/null
+ rtrn=$?
+ assertTrue '_ASSERT_SAME_ failure' ${rtrn}
+ [ "${rtrn}" -ne "${SHUNIT_TRUE}" ] && cat "${stderrF}" >&2
+
+ ( ${_ASSERT_SAME_} '"some msg"' 'x' 'y' >"${stdoutF}" 2>"${stderrF}" )
+ grep '^ASSERT:\[[0-9]*\] *' "${stdoutF}" >/dev/null
+ rtrn=$?
+ assertTrue '_ASSERT_SAME_ w/ msg failure' ${rtrn}
+ [ "${rtrn}" -ne "${SHUNIT_TRUE}" ] && cat "${stderrF}" >&2
+
+ return 0
+}
+
+testNotSame() {
+ # Start skipping if LINENO not available.
+ [ -z "${LINENO:-}" ] && startSkipping
+
+ ( ${_ASSERT_NOT_SAME_} 'x' 'x' >"${stdoutF}" 2>"${stderrF}" )
+ grep '^ASSERT:\[[0-9]*\] *' "${stdoutF}" >/dev/null
+ rtrn=$?
+ assertTrue '_ASSERT_NOT_SAME_ failure' ${rtrn}
+ [ "${rtrn}" -ne "${SHUNIT_TRUE}" ] && cat "${stderrF}" >&2
+
+ ( ${_ASSERT_NOT_SAME_} '"some msg"' 'x' 'x' >"${stdoutF}" 2>"${stderrF}" )
+ grep '^ASSERT:\[[0-9]*\] *' "${stdoutF}" >/dev/null
+ rtrn=$?
+ assertTrue '_ASSERT_NOT_SAME_ w/ msg failure' ${rtrn}
+ [ "${rtrn}" -ne "${SHUNIT_TRUE}" ] && cat "${stderrF}" >&2
+
+ return 0
+}
+
+testNull() {
+ # Start skipping if LINENO not available.
+ [ -z "${LINENO:-}" ] && startSkipping
+
+ ( ${_ASSERT_NULL_} 'x' >"${stdoutF}" 2>"${stderrF}" )
+ grep '^ASSERT:\[[0-9]*\] *' "${stdoutF}" >/dev/null
+ rtrn=$?
+ assertTrue '_ASSERT_NULL_ failure' ${rtrn}
+ [ "${rtrn}" -ne "${SHUNIT_TRUE}" ] && cat "${stderrF}" >&2
+
+ ( ${_ASSERT_NULL_} '"some msg"' 'x' >"${stdoutF}" 2>"${stderrF}" )
+ grep '^ASSERT:\[[0-9]*\] *' "${stdoutF}" >/dev/null
+ rtrn=$?
+ assertTrue '_ASSERT_NULL_ w/ msg failure' ${rtrn}
+ [ "${rtrn}" -ne "${SHUNIT_TRUE}" ] && cat "${stderrF}" >&2
+
+ return 0
+}
+
+testNotNull()
+{
+ # start skipping if LINENO not available
+ [ -z "${LINENO:-}" ] && startSkipping
+
+ ( ${_ASSERT_NOT_NULL_} '' >"${stdoutF}" 2>"${stderrF}" )
+ grep '^ASSERT:\[[0-9]*\] *' "${stdoutF}" >/dev/null
+ rtrn=$?
+ assertTrue '_ASSERT_NOT_NULL_ failure' ${rtrn}
+ [ "${rtrn}" -ne "${SHUNIT_TRUE}" ] && cat "${stderrF}" >&2
+
+ ( ${_ASSERT_NOT_NULL_} '"some msg"' '""' >"${stdoutF}" 2>"${stderrF}" )
+ grep '^ASSERT:\[[0-9]*\] *' "${stdoutF}" >/dev/null
+ rtrn=$?
+ assertTrue '_ASSERT_NOT_NULL_ w/ msg failure' ${rtrn}
+ [ "${rtrn}" -ne "${SHUNIT_TRUE}" ] && cat "${stdoutF}" "${stderrF}" >&2
+
+ return 0
+}
+
+testAssertTrue() {
+ # Start skipping if LINENO not available.
+ [ -z "${LINENO:-}" ] && startSkipping
+
+ ( ${_ASSERT_TRUE_} "${SHUNIT_FALSE}" >"${stdoutF}" 2>"${stderrF}" )
+ grep '^ASSERT:\[[0-9]*\] *' "${stdoutF}" >/dev/null
+ rtrn=$?
+ assertTrue '_ASSERT_TRUE_ failure' ${rtrn}
+ [ "${rtrn}" -ne "${SHUNIT_TRUE}" ] && cat "${stderrF}" >&2
+
+ ( ${_ASSERT_TRUE_} '"some msg"' "${SHUNIT_FALSE}" >"${stdoutF}" 2>"${stderrF}" )
+ grep '^ASSERT:\[[0-9]*\] *' "${stdoutF}" >/dev/null
+ rtrn=$?
+ assertTrue '_ASSERT_TRUE_ w/ msg failure' ${rtrn}
+ [ "${rtrn}" -ne "${SHUNIT_TRUE}" ] && cat "${stderrF}" >&2
+
+ return 0
+}
+
+testAssertFalse() {
+ # Start skipping if LINENO not available.
+ [ -z "${LINENO:-}" ] && startSkipping
+
+ ( ${_ASSERT_FALSE_} "${SHUNIT_TRUE}" >"${stdoutF}" 2>"${stderrF}" )
+ grep '^ASSERT:\[[0-9]*\] *' "${stdoutF}" >/dev/null
+ rtrn=$?
+ assertTrue '_ASSERT_FALSE_ failure' ${rtrn}
+ [ "${rtrn}" -ne "${SHUNIT_TRUE}" ] && cat "${stderrF}" >&2
+
+ ( ${_ASSERT_FALSE_} '"some msg"' "${SHUNIT_TRUE}" >"${stdoutF}" 2>"${stderrF}" )
+ grep '^ASSERT:\[[0-9]*\] *' "${stdoutF}" >/dev/null
+ rtrn=$?
+ assertTrue '_ASSERT_FALSE_ w/ msg failure' ${rtrn}
+ [ "${rtrn}" -ne "${SHUNIT_TRUE}" ] && cat "${stderrF}" >&2
+
+ return 0
+}
+
+testFail() {
+ # Start skipping if LINENO not available.
+ [ -z "${LINENO:-}" ] && startSkipping
+
+ ( ${_FAIL_} >"${stdoutF}" 2>"${stderrF}" )
+ grep '^ASSERT:\[[0-9]*\] *' "${stdoutF}" >/dev/null
+ rtrn=$?
+ assertTrue '_FAIL_ failure' ${rtrn}
+ [ "${rtrn}" -ne "${SHUNIT_TRUE}" ] && cat "${stderrF}" >&2
+
+ ( ${_FAIL_} '"some msg"' >"${stdoutF}" 2>"${stderrF}" )
+ grep '^ASSERT:\[[0-9]*\] *' "${stdoutF}" >/dev/null
+ rtrn=$?
+ assertTrue '_FAIL_ w/ msg failure' ${rtrn}
+ [ "${rtrn}" -ne "${SHUNIT_TRUE}" ] && cat "${stderrF}" >&2
+
+ return 0
+}
+
+testFailNotEquals()
+{
+ # start skipping if LINENO not available
+ [ -z "${LINENO:-}" ] && startSkipping
+
+ ( ${_FAIL_NOT_EQUALS_} 'x' 'y' >"${stdoutF}" 2>"${stderrF}" )
+ grep '^ASSERT:\[[0-9]*\] *' "${stdoutF}" >/dev/null
+ rtrn=$?
+ assertTrue '_FAIL_NOT_EQUALS_ failure' ${rtrn}
+ [ "${rtrn}" -ne "${SHUNIT_TRUE}" ] && cat "${stderrF}" >&2
+
+ ( ${_FAIL_NOT_EQUALS_} '"some msg"' 'x' 'y' >"${stdoutF}" 2>"${stderrF}" )
+ grep '^ASSERT:\[[0-9]*\] *' "${stdoutF}" >/dev/null
+ rtrn=$?
+ assertTrue '_FAIL_NOT_EQUALS_ w/ msg failure' ${rtrn}
+ [ "${rtrn}" -ne "${SHUNIT_TRUE}" ] && cat "${stderrF}" >&2
+
+ return 0
+}
+
+testFailSame() {
+ # Start skipping if LINENO not available.
+ [ -z "${LINENO:-}" ] && startSkipping
+
+ ( ${_FAIL_SAME_} 'x' 'x' >"${stdoutF}" 2>"${stderrF}" )
+ grep '^ASSERT:\[[0-9]*\] *' "${stdoutF}" >/dev/null
+ rtrn=$?
+ assertTrue '_FAIL_SAME_ failure' ${rtrn}
+ [ "${rtrn}" -ne "${SHUNIT_TRUE}" ] && cat "${stderrF}" >&2
+
+ ( ${_FAIL_SAME_} '"some msg"' 'x' 'x' >"${stdoutF}" 2>"${stderrF}" )
+ grep '^ASSERT:\[[0-9]*\] *' "${stdoutF}" >/dev/null
+ rtrn=$?
+ assertTrue '_FAIL_SAME_ w/ msg failure' ${rtrn}
+ [ "${rtrn}" -ne "${SHUNIT_TRUE}" ] && cat "${stderrF}" >&2
+
+ return 0
+}
+
+testFailNotSame() {
+ # Start skipping if LINENO not available.
+ [ -z "${LINENO:-}" ] && startSkipping
+
+ ( ${_FAIL_NOT_SAME_} 'x' 'y' >"${stdoutF}" 2>"${stderrF}" )
+ grep '^ASSERT:\[[0-9]*\] *' "${stdoutF}" >/dev/null
+ rtrn=$?
+ assertTrue '_FAIL_NOT_SAME_ failure' ${rtrn}
+ [ "${rtrn}" -ne "${SHUNIT_TRUE}" ] && cat "${stderrF}" >&2
+
+ ( ${_FAIL_NOT_SAME_} '"some msg"' 'x' 'y' >"${stdoutF}" 2>"${stderrF}" )
+ grep '^ASSERT:\[[0-9]*\] *' "${stdoutF}" >/dev/null
+ rtrn=$?
+ assertTrue '_FAIL_NOT_SAME_ w/ msg failure' ${rtrn}
+ [ "${rtrn}" -ne "${SHUNIT_TRUE}" ] && cat "${stderrF}" >&2
+
+ return 0
+}
+
+oneTimeSetUp() {
+ th_oneTimeSetUp
+}
+
+# Disable output coloring as it breaks the tests.
+SHUNIT_COLOR='none'; export SHUNIT_COLOR
+
+# Load and run shUnit2.
+# shellcheck disable=SC2034
+[ -n "${ZSH_VERSION:-}" ] && SHUNIT_PARENT="$0"
+. "${TH_SHUNIT}"
diff --git a/fluent-bit/tests/lib/shunit2/shunit2_misc_test.sh b/fluent-bit/tests/lib/shunit2/shunit2_misc_test.sh
new file mode 100755
index 00000000..54267313
--- /dev/null
+++ b/fluent-bit/tests/lib/shunit2/shunit2_misc_test.sh
@@ -0,0 +1,315 @@
+#! /bin/sh
+# vim:et:ft=sh:sts=2:sw=2
+#
+# shUnit2 unit tests of miscellaneous things
+#
+# Copyright 2008-2018 Kate Ward. All Rights Reserved.
+# Released under the Apache 2.0 license.
+#
+# Author: kate.ward@forestent.com (Kate Ward)
+# https://github.com/kward/shunit2
+#
+### ShellCheck http://www.shellcheck.net/
+# $() are not fully portable (POSIX != portable).
+# shellcheck disable=SC2006
+# Disable source following.
+# shellcheck disable=SC1090,SC1091
+# Not wanting to escape single quotes.
+# shellcheck disable=SC1003
+
+# These variables will be overridden by the test helpers.
+stdoutF="${TMPDIR:-/tmp}/STDOUT"
+stderrF="${TMPDIR:-/tmp}/STDERR"
+
+# Load test helpers.
+. ./shunit2_test_helpers
+
+# Note: the test script is prefixed with '#' chars so that shUnit2 does not
+# incorrectly interpret the embedded functions as real functions.
+testUnboundVariable() {
+ unittestF="${SHUNIT_TMPDIR}/unittest"
+ sed 's/^#//' >"${unittestF}" <<EOF
+## Treat unset variables as an error when performing parameter expansion.
+#set -u
+#
+#boom() { x=\$1; } # This function goes boom if no parameters are passed!
+#test_boom() {
+# assertEquals 1 1
+# boom # No parameter given
+# assertEquals 0 \$?
+#}
+#SHUNIT_COLOR='none'
+#. ${TH_SHUNIT}
+EOF
+ ( exec "${SHELL:-sh}" "${unittestF}" >"${stdoutF}" 2>"${stderrF}" )
+ assertFalse 'expected a non-zero exit value' $?
+ grep '^ASSERT:unknown failure' "${stdoutF}" >/dev/null
+ assertTrue 'assert message was not generated' $?
+ grep '^Ran [0-9]* test' "${stdoutF}" >/dev/null
+ assertTrue 'test count message was not generated' $?
+ grep '^FAILED' "${stdoutF}" >/dev/null
+ assertTrue 'failure message was not generated' $?
+}
+
+# assertEquals repeats message argument.
+# https://github.com/kward/shunit2/issues/7
+testIssue7() {
+ # Disable coloring so 'ASSERT:' lines can be matched correctly.
+ _shunit_configureColor 'none'
+
+ ( assertEquals 'Some message.' 1 2 >"${stdoutF}" 2>"${stderrF}" )
+ diff "${stdoutF}" - >/dev/null <<EOF
+ASSERT:Some message. expected:<1> but was:<2>
+EOF
+ rtrn=$?
+ assertEquals "${SHUNIT_TRUE}" "${rtrn}"
+ [ "${rtrn}" -eq "${SHUNIT_TRUE}" ] || cat "${stderrF}" >&2
+}
+
+# Support prefixes on test output.
+# https://github.com/kward/shunit2/issues/29
+testIssue29() {
+ unittestF="${SHUNIT_TMPDIR}/unittest"
+ sed 's/^#//' >"${unittestF}" <<EOF
+## Support test prefixes.
+#test_assert() { assertTrue ${SHUNIT_TRUE}; }
+#SHUNIT_COLOR='none'
+#SHUNIT_TEST_PREFIX='--- '
+#. ${TH_SHUNIT}
+EOF
+ ( exec "${SHELL:-sh}" "${unittestF}" >"${stdoutF}" 2>"${stderrF}" )
+ grep '^--- test_assert' "${stdoutF}" >/dev/null
+ rtrn=$?
+ assertEquals "${SHUNIT_TRUE}" "${rtrn}"
+ [ "${rtrn}" -eq "${SHUNIT_TRUE}" ] || cat "${stdoutF}" >&2
+}
+
+# shUnit2 should not exit with 0 when it has syntax errors.
+# https://github.com/kward/shunit2/issues/69
+testIssue69() {
+ unittestF="${SHUNIT_TMPDIR}/unittest"
+
+ for t in Equals NotEquals Null NotNull Same NotSame True False; do
+ assert="assert${t}"
+ sed 's/^#//' >"${unittestF}" <<EOF
+## Asserts with invalid argument counts should be counted as failures.
+#test_assert() { ${assert}; }
+#SHUNIT_COLOR='none'
+#. ${TH_SHUNIT}
+EOF
+ ( exec "${SHELL:-sh}" "${unittestF}" >"${stdoutF}" 2>"${stderrF}" )
+ grep '^FAILED' "${stdoutF}" >/dev/null
+ assertTrue "failure message for ${assert} was not generated" $?
+ done
+}
+
+# Ensure that test fails if setup/teardown functions fail.
+testIssue77() {
+ unittestF="${SHUNIT_TMPDIR}/unittest"
+ for func in oneTimeSetUp setUp tearDown oneTimeTearDown; do
+ sed 's/^#//' >"${unittestF}" <<EOF
+## Environment failure should end test.
+#${func}() { return ${SHUNIT_FALSE}; }
+#test_true() { assertTrue ${SHUNIT_TRUE}; }
+#SHUNIT_COLOR='none'
+#. ${TH_SHUNIT}
+EOF
+ ( exec "${SHELL:-sh}" "${unittestF}" ) >"${stdoutF}" 2>"${stderrF}"
+ grep '^FAILED' "${stdoutF}" >/dev/null
+ assertTrue "failure of ${func}() did not end test" $?
+ done
+}
+
+# Ensure a test failure is recorded for code containing syntax errors.
+# https://github.com/kward/shunit2/issues/84
+testIssue84() {
+ unittestF="${SHUNIT_TMPDIR}/unittest"
+ sed 's/^#//' >"${unittestF}" <<\EOF
+## Function with syntax error.
+#syntax_error() { ${!#3442} -334 a$@2[1]; }
+#test_syntax_error() {
+# syntax_error
+# assertTrue ${SHUNIT_TRUE}
+#}
+#SHUNIT_COLOR='none'
+#SHUNIT_TEST_PREFIX='--- '
+#. ${TH_SHUNIT}
+EOF
+ ( exec "${SHELL:-sh}" "${unittestF}" >"${stdoutF}" 2>"${stderrF}" )
+ grep '^FAILED' "${stdoutF}" >/dev/null
+ assertTrue "failure message for ${assert} was not generated" $?
+}
+
+testPrepForSourcing() {
+ assertEquals '/abc' "`_shunit_prepForSourcing '/abc'`"
+ assertEquals './abc' "`_shunit_prepForSourcing './abc'`"
+ assertEquals './abc' "`_shunit_prepForSourcing 'abc'`"
+}
+
+testEscapeCharInStr() {
+ while read -r desc char str want; do
+ got=`_shunit_escapeCharInStr "${char}" "${str}"`
+ assertEquals "${desc}" "${want}" "${got}"
+ done <<'EOF'
+backslash \ '' ''
+backslash_pre \ \def \\def
+backslash_mid \ abc\def abc\\def
+backslash_post \ abc\ abc\\
+quote " '' ''
+quote_pre " "def \"def
+quote_mid " abc"def abc\"def
+quote_post " abc" abc\"
+string $ '' ''
+string_pre $ $def \$def
+string_mid $ abc$def abc\$def
+string_post $ abc$ abc\$
+EOF
+
+ # TODO(20170924:kward) fix or remove.
+# actual=`_shunit_escapeCharInStr "'" ''`
+# assertEquals '' "${actual}"
+# assertEquals "abc\\'" `_shunit_escapeCharInStr "'" "abc'"`
+# assertEquals "abc\\'def" `_shunit_escapeCharInStr "'" "abc'def"`
+# assertEquals "\\'def" `_shunit_escapeCharInStr "'" "'def"`
+
+# # Must put the backtick in a variable so the shell doesn't misinterpret it
+# # while inside a backticked sequence (e.g. `echo '`'` would fail).
+# backtick='`'
+# actual=`_shunit_escapeCharInStr ${backtick} ''`
+# assertEquals '' "${actual}"
+# assertEquals '\`abc' \
+# `_shunit_escapeCharInStr "${backtick}" ${backtick}'abc'`
+# assertEquals 'abc\`' \
+# `_shunit_escapeCharInStr "${backtick}" 'abc'${backtick}`
+# assertEquals 'abc\`def' \
+# `_shunit_escapeCharInStr "${backtick}" 'abc'${backtick}'def'`
+}
+
+testEscapeCharInStr_specialChars() {
+ # Make sure our forward slash doesn't upset sed.
+ assertEquals '/' "`_shunit_escapeCharInStr '\' '/'`"
+
+ # Some shells escape these differently.
+ # TODO(20170924:kward) fix or remove.
+ #assertEquals '\\a' `_shunit_escapeCharInStr '\' '\a'`
+ #assertEquals '\\b' `_shunit_escapeCharInStr '\' '\b'`
+}
+
+# Test the various ways of declaring functions.
+#
+# Prefixing (then stripping) with comment symbol so these functions aren't
+# treated as real functions by shUnit2.
+testExtractTestFunctions() {
+ f="${SHUNIT_TMPDIR}/extract_test_functions"
+ sed 's/^#//' <<EOF >"${f}"
+## Function on a single line.
+#testABC() { echo 'ABC'; }
+## Multi-line function with '{' on next line.
+#test_def()
+# {
+# echo 'def'
+#}
+## Multi-line function with '{' on first line.
+#testG3 () {
+# echo 'G3'
+#}
+## Function with numerical values in name.
+#function test4() { echo '4'; }
+## Leading space in front of function.
+# test5() { echo '5'; }
+## Function with '_' chars in name.
+#some_test_function() { echo 'some func'; }
+## Function that sets variables.
+#func_with_test_vars() {
+# testVariable=1234
+#}
+## Function with keyword but no parenthesis
+#function test6 { echo '6'; }
+## Function with keyword but no parenthesis, multi-line
+#function test7 {
+# echo '7';
+#}
+## Function with no parenthesis, '{' on next line
+#function test8
+#{
+# echo '8'
+#}
+## Function with hyphenated name
+#test-9() {
+# echo '9';
+#}
+## Function without parenthesis or keyword
+#test_foobar { echo 'hello world'; }
+## Function with multiple function keywords
+#function function test_test_test() { echo 'lorem'; }
+EOF
+
+ actual=`_shunit_extractTestFunctions "${f}"`
+ assertEquals 'testABC test_def testG3 test4 test5 test6 test7 test8 test-9' "${actual}"
+}
+
+# Test that certain external commands sometimes "stubbed" by users are escaped.
+testIssue54() {
+ for c in mkdir rm cat chmod sed; do
+ grep "^[^#]*${c} " "${TH_SHUNIT}" | grep -qv "command ${c}"
+ assertFalse "external call to ${c} not protected somewhere" $?
+ done
+ grep '^[^#]*[^ ] *\[' "${TH_SHUNIT}" | grep -qv 'command \['
+ assertFalse "call to [ ... ] not protected somewhere" $?
+ grep '^[^#]* *\.' "${TH_SHUNIT}" | grep -qv 'command \.'
+ assertFalse "call to . not protected somewhere" $?
+}
+
+mock_tput() {
+ if [ -z "${TERM}" ]; then
+ # shellcheck disable=SC2016
+ echo 'tput: No value for $TERM and no -T specified'
+ return 2
+ fi
+ if [ "$1" = 'colors' ]; then
+ echo 256
+ return 0
+ fi
+ return 1
+}
+
+testColors() {
+ while read -r desc cmd colors; do
+ SHUNIT_CMD_TPUT=${cmd}
+ got=`_shunit_colors`
+ want=${colors}
+ assertEquals "${got}" "${want}"
+ done <<'EOF'
+missing missing_tput 16
+mock mock_tput 256
+EOF
+}
+
+testColorsWitoutTERM() {
+ SHUNIT_CMD_TPUT='mock_tput'
+ got=`TERM='' _shunit_colors`
+ want=16
+ assertEquals "${got}" "${want}"
+}
+
+setUp() {
+ for f in "${stdoutF}" "${stderrF}"; do
+ cp /dev/null "${f}"
+ done
+
+ # Reconfigure coloring as some tests override default behavior.
+ _shunit_configureColor "${SHUNIT_COLOR_DEFAULT}"
+
+ # shellcheck disable=SC2034,SC2153
+ SHUNIT_CMD_TPUT=${__SHUNIT_CMD_TPUT}
+}
+
+oneTimeSetUp() {
+ SHUNIT_COLOR_DEFAULT="${SHUNIT_COLOR}"
+ th_oneTimeSetUp
+}
+
+# Load and run shUnit2.
+# shellcheck disable=SC2034
+[ -n "${ZSH_VERSION:-}" ] && SHUNIT_PARENT=$0
+. "${TH_SHUNIT}"
diff --git a/fluent-bit/tests/lib/shunit2/shunit2_standalone_test.sh b/fluent-bit/tests/lib/shunit2/shunit2_standalone_test.sh
new file mode 100755
index 00000000..2109d8f7
--- /dev/null
+++ b/fluent-bit/tests/lib/shunit2/shunit2_standalone_test.sh
@@ -0,0 +1,38 @@
+#! /bin/sh
+# vim:et:ft=sh:sts=2:sw=2
+#
+# shUnit2 unit test for standalone operation.
+#
+# Copyright 2010-2017 Kate Ward. All Rights Reserved.
+# Released under the Apache 2.0 license.
+#
+# Author: kate.ward@forestent.com (Kate Ward)
+# https://github.com/kward/shunit2
+#
+# This unit test is purely to test that calling shunit2 directly, while passing
+# the name of a unit test script, works. When run, this script determines if it
+# is running as a standalone program, and calls main() if it is.
+#
+### ShellCheck http://www.shellcheck.net/
+# $() are not fully portable (POSIX != portable).
+# shellcheck disable=SC2006
+# Disable source following.
+# shellcheck disable=SC1090,SC1091
+
+ARGV0="`basename "$0"`"
+
+# Load test helpers.
+. ./shunit2_test_helpers
+
+testStandalone() {
+ assertTrue "${SHUNIT_TRUE}"
+}
+
+main() {
+ ${TH_SHUNIT} "${ARGV0}"
+}
+
+# Are we running as a standalone?
+if [ "${ARGV0}" = 'shunit2_test_standalone.sh' ]; then
+ if [ $# -gt 0 ]; then main "$@"; else main; fi
+fi
diff --git a/fluent-bit/tests/lib/shunit2/shunit2_test_helpers b/fluent-bit/tests/lib/shunit2/shunit2_test_helpers
new file mode 100644
index 00000000..7ff4c9cc
--- /dev/null
+++ b/fluent-bit/tests/lib/shunit2/shunit2_test_helpers
@@ -0,0 +1,234 @@
+# vim:et:ft=sh:sts=2:sw=2
+#
+# shUnit2 unit test common functions
+#
+# Copyright 2008 Kate Ward. All Rights Reserved.
+# Released under the Apache 2.0 license.
+#
+# Author: kate.ward@forestent.com (Kate Ward)
+# https://github.com/kward/shunit2
+#
+### ShellCheck (http://www.shellcheck.net/)
+# Commands are purposely escaped so they can be mocked outside shUnit2.
+# shellcheck disable=SC1001,SC1012
+# expr may be antiquated, but it is the only solution in some cases.
+# shellcheck disable=SC2003
+# $() are not fully portable (POSIX != portable).
+# shellcheck disable=SC2006
+
+# Treat unset variables as an error when performing parameter expansion.
+set -u
+
+# Set shwordsplit for zsh.
+\[ -n "${ZSH_VERSION:-}" ] && setopt shwordsplit
+
+#
+# Constants.
+#
+
+# Path to shUnit2 library. Can be overridden by setting SHUNIT_INC.
+TH_SHUNIT=${SHUNIT_INC:-./shunit2}; export TH_SHUNIT
+
+# Configure debugging. Set the DEBUG environment variable to any
+# non-empty value to enable debug output, or TRACE to enable trace
+# output.
+TRACE=${TRACE:+'th_trace '}
+\[ -n "${TRACE}" ] && DEBUG=1
+\[ -z "${TRACE}" ] && TRACE=':'
+
+DEBUG=${DEBUG:+'th_debug '}
+\[ -z "${DEBUG}" ] && DEBUG=':'
+
+#
+# Variables.
+#
+
+th_RANDOM=0
+
+#
+# Functions.
+#
+
+# Logging functions.
+th_trace() { echo "${MY_NAME}:TRACE $*" >&2; }
+th_debug() { echo "${MY_NAME}:DEBUG $*" >&2; }
+th_info() { echo "${MY_NAME}:INFO $*" >&2; }
+th_warn() { echo "${MY_NAME}:WARN $*" >&2; }
+th_error() { echo "${MY_NAME}:ERROR $*" >&2; }
+th_fatal() { echo "${MY_NAME}:FATAL $*" >&2; }
+
+# Output subtest name.
+th_subtest() { echo " $*" >&2; }
+
+th_oneTimeSetUp() {
+ # These files will be cleaned up automatically by shUnit2.
+ stdoutF="${SHUNIT_TMPDIR}/stdout"
+ stderrF="${SHUNIT_TMPDIR}/stderr"
+ returnF="${SHUNIT_TMPDIR}/return"
+ expectedF="${SHUNIT_TMPDIR}/expected"
+ export stdoutF stderrF returnF expectedF
+}
+
+# Generate a random number.
+th_generateRandom() {
+ tfgr_random=${th_RANDOM}
+
+ while \[ "${tfgr_random}" = "${th_RANDOM}" ]; do
+ # shellcheck disable=SC2039
+ if \[ -n "${RANDOM:-}" ]; then
+ # $RANDOM works
+ # shellcheck disable=SC2039
+ tfgr_random=${RANDOM}${RANDOM}${RANDOM}$$
+ elif \[ -r '/dev/urandom' ]; then
+ tfgr_random=`od -vAn -N4 -tu4 </dev/urandom |sed 's/^[^0-9]*//'`
+ else
+ tfgr_date=`date '+%H%M%S'`
+ tfgr_random=`expr "${tfgr_date}" \* $$`
+ unset tfgr_date
+ fi
+ \[ "${tfgr_random}" = "${th_RANDOM}" ] && sleep 1
+ done
+
+ th_RANDOM=${tfgr_random}
+ unset tfgr_random
+}
+
+# This section returns the data section from the specified section of a file. A
+# data section is defined by a [header], one or more lines of data, and then a
+# blank line.
+th_getDataSect() {
+ th_sgrep "\\[$1\\]" "$2" |sed '1d'
+}
+
+# This function greps a section from a file. a section is defined as a group of
+# lines preceded and followed by blank lines..
+th_sgrep() {
+ th_pattern_=$1
+ shift
+
+ # shellcheck disable=SC2068
+ sed -e '/./{H;$!d;}' -e "x;/${th_pattern_}/"'!d;' $@ |sed '1d'
+
+ unset th_pattern_
+}
+
+# Custom assert that checks for true return value (0), and no output to STDOUT
+# or STDERR. If a non-zero return value is encountered, the output of STDERR
+# will be output.
+#
+# Args:
+# th_test_: string: name of the subtest
+# th_rtrn_: integer: the return value of the subtest performed
+# th_stdout_: string: filename where stdout was redirected to
+# th_stderr_: string: filename where stderr was redirected to
+th_assertTrueWithNoOutput() {
+ th_test_=$1
+ th_rtrn_=$2
+ th_stdout_=$3
+ th_stderr_=$4
+
+ assertTrue "${th_test_}; expected return value of zero" "${th_rtrn_}"
+ \[ "${th_rtrn_}" -ne "${SHUNIT_TRUE}" ] && \cat "${th_stderr_}"
+ assertFalse "${th_test_}; expected no output to STDOUT" \
+ "[ -s '${th_stdout_}' ]"
+ assertFalse "${th_test_}; expected no output to STDERR" \
+ "[ -s '${th_stderr_}' ]"
+
+ unset th_test_ th_rtrn_ th_stdout_ th_stderr_
+}
+
+# Custom assert that checks for non-zero return value, output to STDOUT, but no
+# output to STDERR.
+#
+# Args:
+# th_test_: string: name of the subtest
+# th_rtrn_: integer: the return value of the subtest performed
+# th_stdout_: string: filename where stdout was redirected to
+# th_stderr_: string: filename where stderr was redirected to
+th_assertFalseWithOutput()
+{
+ th_test_=$1
+ th_rtrn_=$2
+ th_stdout_=$3
+ th_stderr_=$4
+
+ assertFalse "${th_test_}; expected non-zero return value" "${th_rtrn_}"
+ assertTrue "${th_test_}; expected output to STDOUT" \
+ "[ -s '${th_stdout_}' ]"
+ assertFalse "${th_test_}; expected no output to STDERR" \
+ "[ -s '${th_stderr_}' ]"
+ \[ -s "${th_stdout_}" -a ! -s "${th_stderr_}" ] || \
+ _th_showOutput "${SHUNIT_FALSE}" "${th_stdout_}" "${th_stderr_}"
+
+ unset th_test_ th_rtrn_ th_stdout_ th_stderr_
+}
+
+# Custom assert that checks for non-zero return value, no output to STDOUT, but
+# output to STDERR.
+#
+# Args:
+# th_test_: string: name of the subtest
+# th_rtrn_: integer: the return value of the subtest performed
+# th_stdout_: string: filename where stdout was redirected to
+# th_stderr_: string: filename where stderr was redirected to
+th_assertFalseWithError() {
+ th_test_=$1
+ th_rtrn_=$2
+ th_stdout_=$3
+ th_stderr_=$4
+
+ assertFalse "${th_test_}; expected non-zero return value" "${th_rtrn_}"
+ assertFalse "${th_test_}; expected no output to STDOUT" \
+ "[ -s '${th_stdout_}' ]"
+ assertTrue "${th_test_}; expected output to STDERR" \
+ "[ -s '${th_stderr_}' ]"
+ \[ ! -s "${th_stdout_}" -a -s "${th_stderr_}" ] || \
+ _th_showOutput "${SHUNIT_FALSE}" "${th_stdout_}" "${th_stderr_}"
+
+ unset th_test_ th_rtrn_ th_stdout_ th_stderr_
+}
+
+# Some shells, zsh on Solaris in particular, return immediately from a sub-shell
+# when a non-zero return value is encountered. To properly catch these values,
+# they are either written to disk, or recognized as an error the file is empty.
+th_clearReturn() { cp /dev/null "${returnF}"; }
+th_queryReturn() {
+ if \[ -s "${returnF}" ]; then
+ th_return=`\cat "${returnF}"`
+ else
+ th_return=${SHUNIT_ERROR}
+ fi
+ export th_return
+}
+
+# Providing external and internal calls to the showOutput helper function.
+th_showOutput() { _th_showOutput "$@"; }
+_th_showOutput() {
+ _th_return_=$1
+ _th_stdout_=$2
+ _th_stderr_=$3
+
+ isSkipping
+ if \[ $? -eq "${SHUNIT_FALSE}" -a "${_th_return_}" != "${SHUNIT_TRUE}" ]; then
+ if \[ -n "${_th_stdout_}" -a -s "${_th_stdout_}" ]; then
+ echo '>>> STDOUT' >&2
+ \cat "${_th_stdout_}" >&2
+ fi
+ if \[ -n "${_th_stderr_}" -a -s "${_th_stderr_}" ]; then
+ echo '>>> STDERR' >&2
+ \cat "${_th_stderr_}" >&2
+ fi
+ if \[ -n "${_th_stdout_}" -o -n "${_th_stderr_}" ]; then
+ echo '<<< end output' >&2
+ fi
+ fi
+
+ unset _th_return_ _th_stdout_ _th_stderr_
+}
+
+#
+# Main.
+#
+
+${TRACE} 'trace output enabled'
+${DEBUG} 'debug output enabled'
diff --git a/fluent-bit/tests/lib/shunit2/test_runner b/fluent-bit/tests/lib/shunit2/test_runner
new file mode 100755
index 00000000..a9871e3f
--- /dev/null
+++ b/fluent-bit/tests/lib/shunit2/test_runner
@@ -0,0 +1,171 @@
+#! /bin/sh
+# vim:et:ft=sh:sts=2:sw=2
+#
+# Unit test suite runner.
+#
+# Copyright 2008-2018 Kate Ward. All Rights Reserved.
+# Released under the Apache 2.0 license.
+#
+# Author: kate.ward@forestent.com (Kate Ward)
+# https://github.com/kward/shlib
+#
+# This script runs all the unit tests that can be found, and generates a nice
+# report of the tests.
+#
+### ShellCheck (http://www.shellcheck.net/)
+# Disable source following.
+# shellcheck disable=SC1090,SC1091
+# expr may be antiquated, but it is the only solution in some cases.
+# shellcheck disable=SC2003
+# $() are not fully portable (POSIX != portable).
+# shellcheck disable=SC2006
+
+# Return if test_runner already loaded.
+[ -z "${RUNNER_LOADED:-}" ] || return 0
+RUNNER_LOADED=0
+
+RUNNER_ARGV0=`basename "$0"`
+RUNNER_SHELLS='/bin/sh ash /bin/bash /bin/dash /bin/ksh /bin/pdksh /bin/zsh'
+RUNNER_TEST_SUFFIX='_test.sh'
+true; RUNNER_TRUE=$?
+false; RUNNER_FALSE=$?
+
+runner_warn() { echo "runner:WARN $*" >&2; }
+runner_error() { echo "runner:ERROR $*" >&2; }
+runner_fatal() { echo "runner:FATAL $*" >&2; exit 1; }
+
+runner_usage() {
+ echo "usage: ${RUNNER_ARGV0} [-e key=val ...] [-s shell(s)] [-t test(s)]"
+}
+
+_runner_tests() { echo ./*${RUNNER_TEST_SUFFIX} |sed 's#./##g'; }
+_runner_testName() {
+ # shellcheck disable=SC1117
+ _runner_testName_=`expr "${1:-}" : "\(.*\)${RUNNER_TEST_SUFFIX}"`
+ if [ -n "${_runner_testName_}" ]; then
+ echo "${_runner_testName_}"
+ else
+ echo 'unknown'
+ fi
+ unset _runner_testName_
+}
+
+main() {
+ # Find and load versions library.
+ for _runner_dir_ in . ${LIB_DIR:-lib}; do
+ if [ -r "${_runner_dir_}/versions" ]; then
+ _runner_lib_dir_="${_runner_dir_}"
+ break
+ fi
+ done
+ [ -n "${_runner_lib_dir_}" ] || runner_fatal 'Unable to find versions library.'
+ . "${_runner_lib_dir_}/versions" || runner_fatal 'Unable to load versions library.'
+ unset _runner_dir_ _runner_lib_dir_
+
+ # Process command line flags.
+ env=''
+ while getopts 'e:hs:t:' opt; do
+ case ${opt} in
+ e) # set an environment variable
+ key=`expr "${OPTARG}" : '\([^=]*\)='`
+ val=`expr "${OPTARG}" : '[^=]*=\(.*\)'`
+ # shellcheck disable=SC2166
+ if [ -z "${key}" -o -z "${val}" ]; then
+ runner_usage
+ exit 1
+ fi
+ eval "${key}='${val}'"
+ eval "export ${key}"
+ env="${env:+${env} }${key}"
+ ;;
+ h) runner_usage; exit 0 ;; # help output
+ s) shells=${OPTARG} ;; # list of shells to run
+ t) tests=${OPTARG} ;; # list of tests to run
+ *) runner_usage; exit 1 ;;
+ esac
+ done
+ shift "`expr ${OPTIND} - 1`"
+
+ # Fill shells and/or tests.
+ shells=${shells:-${RUNNER_SHELLS}}
+ [ -z "${tests}" ] && tests=`_runner_tests`
+
+ # Error checking.
+ if [ -z "${tests}" ]; then
+ runner_error 'no tests found to run; exiting'
+ exit 1
+ fi
+
+ cat <<EOF
+#------------------------------------------------------------------------------
+# System data.
+#
+
+$ uname -mprsv
+`uname -mprsv`
+
+OS Name: `versions_osName`
+OS Version: `versions_osVersion`
+
+### Test run info.
+shells: ${shells}
+tests: ${tests}
+EOF
+for key in ${env}; do
+ eval "echo \"${key}=\$${key}\""
+done
+
+# Run tests.
+runner_passing_=${RUNNER_TRUE}
+for shell in ${shells}; do
+ echo
+
+ cat <<EOF
+
+#------------------------------------------------------------------------------
+# Running the test suite with ${shell}.
+#
+EOF
+
+ # Check for existence of shell.
+ shell_bin=${shell}
+ shell_name=''
+ shell_present=${RUNNER_FALSE}
+ case ${shell} in
+ ash)
+ shell_bin=`command -v busybox`
+ [ $? -eq "${RUNNER_TRUE}" ] && shell_present="${RUNNER_TRUE}"
+ shell_bin="${shell_bin:+${shell_bin} }ash"
+ shell_name=${shell}
+ ;;
+ *)
+ [ -x "${shell_bin}" ] && shell_present="${RUNNER_TRUE}"
+ shell_name=`basename "${shell}"`
+ ;;
+ esac
+ if [ "${shell_present}" -eq "${RUNNER_FALSE}" ]; then
+ runner_warn "unable to run tests with the ${shell_name} shell"
+ continue
+ fi
+
+ shell_version=`versions_shellVersion "${shell}"`
+
+ echo "shell name: ${shell_name}"
+ echo "shell version: ${shell_version}"
+
+ # Execute the tests.
+ for t in ${tests}; do
+ echo
+ echo "--- Executing the '`_runner_testName "${t}"`' test suite. ---"
+ # ${shell_bin} needs word splitting.
+ # shellcheck disable=SC2086
+ ( exec ${shell_bin} "./${t}" 2>&1; )
+ test "${runner_passing_}" -eq ${RUNNER_TRUE} -a $? -eq ${RUNNER_TRUE}
+ runner_passing_=$?
+ done
+ done
+ return ${runner_passing_}
+}
+
+# Execute main() if this is run in standalone mode (i.e. not from a unit test).
+[ -z "${SHUNIT_VERSION}" ] && main "$@"
diff --git a/fluent-bit/tests/runtime/CMakeLists.txt b/fluent-bit/tests/runtime/CMakeLists.txt
new file mode 100644
index 00000000..d6d3e710
--- /dev/null
+++ b/fluent-bit/tests/runtime/CMakeLists.txt
@@ -0,0 +1,173 @@
+find_package(Threads REQUIRED)
+
+include_directories(acutest/)
+
+define_property(GLOBAL PROPERTY CHECK_PROGRAMS
+ BRIEF_DOCS "Runtime Test Programs"
+ FULL_DOCS "Runtime Test Programs")
+set_property(GLOBAL PROPERTY CHECK_PROGRAMS "")
+
+# Macro to set definitions
+macro(FLB_RT_TEST BUILT src)
+ if (${BUILT})
+ list(APPEND CHECK_PROGRAMS
+ ${src}
+ )
+ endif()
+endmacro()
+
+# Macro to set definitions
+macro(FLB_RT_CORE_TEST BUILT src)
+ list(APPEND CHECK_PROGRAMS
+ ${src}
+ )
+endmacro()
+
+# Core
+FLB_RT_CORE_TEST(FLB_COROUTINE_TIMEOUT "core-timeout.c")
+
+FLB_RT_TEST(FLB_CHUNK_TRACE "core_chunk_trace.c")
+
+# Input Plugins
+FLB_RT_TEST(FLB_IN_EVENT_TEST "in_event_test.c")
+
+if(FLB_OUT_LIB)
+ # These plugins works only on Linux
+ if(${CMAKE_SYSTEM_NAME} MATCHES "Linux")
+ FLB_RT_TEST(FLB_IN_CPU "in_cpu.c")
+ FLB_RT_TEST(FLB_IN_DISK "in_disk.c")
+ FLB_RT_TEST(FLB_IN_MEM "in_mem.c")
+ FLB_RT_TEST(FLB_IN_PROC "in_proc.c")
+ FLB_RT_TEST(FLB_IN_NETIF "in_netif.c")
+ FLB_RT_TEST(FLB_IN_PODMAN_METRICS "in_podman_metrics.c")
+ endif()
+ FLB_RT_TEST(FLB_IN_HEAD "in_head.c")
+ FLB_RT_TEST(FLB_IN_DUMMY "in_dummy.c")
+ FLB_RT_TEST(FLB_IN_HTTP "in_http.c")
+ FLB_RT_TEST(FLB_IN_ELASTICSEARCH "in_elasticsearch.c")
+ FLB_RT_TEST(FLB_IN_MQTT "in_mqtt.c")
+ FLB_RT_TEST(FLB_IN_OPENTELEMETRY "in_opentelemetry.c")
+ FLB_RT_TEST(FLB_IN_RANDOM "in_random.c")
+ FLB_RT_TEST(FLB_IN_STATSD "in_statsd.c")
+ FLB_RT_TEST(FLB_IN_SPLUNK "in_splunk.c")
+ FLB_RT_TEST(FLB_IN_SYSLOG "in_syslog.c")
+ FLB_RT_TEST(FLB_IN_TAIL "in_tail.c")
+ FLB_RT_TEST(FLB_IN_UDP "in_udp.c")
+ FLB_RT_TEST(FLB_IN_TCP "in_tcp.c")
+ FLB_RT_TEST(FLB_IN_FORWARD "in_forward.c")
+ FLB_RT_TEST(FLB_IN_FLUENTBIT_METRICS "in_fluentbit_metrics.c")
+endif()
+
+# Filter Plugins
+if(FLB_IN_LIB AND FLB_OUT_LIB)
+ FLB_RT_TEST(FLB_FILTER_AWS "filter_aws.c")
+ FLB_RT_TEST(FLB_FILTER_CHECKLIST "filter_checklist.c")
+ FLB_RT_TEST(FLB_FILTER_EXPECT "filter_expect.c")
+ FLB_RT_TEST(FLB_FILTER_STDOUT "filter_stdout.c")
+ FLB_RT_TEST(FLB_FILTER_GREP "filter_grep.c")
+ FLB_RT_TEST(FLB_FILTER_THROTTLE "filter_throttle.c")
+ FLB_RT_TEST(FLB_FILTER_THROTTLE_SIZE "filter_throttle_size.c")
+ FLB_RT_TEST(FLB_FILTER_NEST "filter_nest.c")
+ FLB_RT_TEST(FLB_FILTER_REWRITE_TAG "filter_rewrite_tag.c")
+ FLB_RT_TEST(FLB_FILTER_KUBERNETES "filter_kubernetes.c")
+ FLB_RT_TEST(FLB_FILTER_PARSER "filter_parser.c")
+ FLB_RT_TEST(FLB_FILTER_MODIFY "filter_modify.c")
+ FLB_RT_TEST(FLB_FILTER_LUA "filter_lua.c")
+ FLB_RT_TEST(FLB_FILTER_TYPE_CONVERTER "filter_type_converter.c")
+ FLB_RT_TEST(FLB_FILTER_RECORD_MODIFIER "filter_record_modifier.c")
+ FLB_RT_TEST(FLB_FILTER_MULTILINE "filter_multiline.c")
+ if (FLB_FILTER_WASM)
+ FLB_RT_TEST(FLB_FILTER_WASM "filter_wasm.c")
+ endif ()
+ FLB_RT_TEST(FLB_FILTER_ECS "filter_ecs.c")
+ FLB_RT_TEST(FLB_FILTER_LOG_TO_METRICS "filter_log_to_metrics.c")
+endif()
+
+
+# Output Plugins
+if(FLB_IN_LIB)
+ FLB_RT_TEST(FLB_OUT_LIB "core_engine.c")
+ FLB_RT_TEST(FLB_OUT_LIB "core_log.c")
+ FLB_RT_TEST(FLB_OUT_LIB "config_map_opts.c")
+ FLB_RT_TEST(FLB_OUT_COUNTER "out_counter.c")
+ FLB_RT_TEST(FLB_OUT_DATADOG "out_datadog.c")
+ FLB_RT_TEST(FLB_OUT_SKYWALKING "out_skywalking.c")
+ FLB_RT_TEST(FLB_OUT_ES "out_elasticsearch.c")
+ FLB_RT_TEST(FLB_OUT_OPENSEARCH "out_opensearch.c")
+ FLB_RT_TEST(FLB_OUT_EXIT "out_exit.c")
+ FLB_RT_TEST(FLB_OUT_FLOWCOUNTER "out_flowcounter.c")
+ FLB_RT_TEST(FLB_OUT_FORWARD "out_forward.c")
+ FLB_RT_TEST(FLB_OUT_HTTP "out_http.c")
+ FLB_RT_TEST(FLB_OUT_LIB "out_lib.c")
+ FLB_RT_TEST(FLB_OUT_LOKI "out_loki.c")
+ FLB_RT_TEST(FLB_OUT_NULL "out_null.c")
+ FLB_RT_TEST(FLB_OUT_PLOT "out_plot.c")
+ FLB_RT_TEST(FLB_OUT_RETRY "out_retry.c")
+ FLB_RT_TEST(FLB_OUT_SPLUNK "out_splunk.c")
+ FLB_RT_TEST(FLB_OUT_STDOUT "out_stdout.c")
+ FLB_RT_TEST(FLB_OUT_SYSLOG "out_syslog.c")
+ FLB_RT_TEST(FLB_OUT_TCP "out_tcp.c")
+
+ if (FLB_RECORD_ACCESSOR)
+ FLB_RT_TEST(FLB_OUT_STACKDRIVER "out_stackdriver.c")
+ endif()
+
+ FLB_RT_TEST(FLB_OUT_CLOUDWATCH_LOGS "out_cloudwatch.c")
+ FLB_RT_TEST(FLB_OUT_KINESIS_FIREHOSE "out_firehose.c")
+ FLB_RT_TEST(FLB_OUT_KINESIS_STREAMS "out_kinesis.c")
+ # These plugins work only on Linux
+ if(NOT FLB_SYSTEM_WINDOWS)
+ FLB_RT_TEST(FLB_OUT_FILE "out_file.c")
+ endif()
+ FLB_RT_TEST(FLB_OUT_S3 "out_s3.c")
+ FLB_RT_TEST(FLB_OUT_TD "out_td.c")
+
+endif()
+
+if (FLB_CUSTOM_CALYPTIA)
+ FLB_RT_TEST(FLB_CUSTOM_CALYPTIA "custom_calyptia_test.c")
+endif()
+
+
+# HTTP Client Debug (requires -DFLB_HTTP_CLIENT_DEBUG=On)
+if(FLB_HTTP_CLIENT_DEBUG)
+ FLB_RT_TEST(FLB_OUT_TD "http_callbacks.c")
+endif()
+
+set(SYSTEMD_LIB, "")
+if(FLB_HAVE_SYSTEMD)
+ set(SYSTEMD_LIB, "systemd")
+endif()
+
+set(FLB_TESTS_DATA_PATH ${CMAKE_CURRENT_SOURCE_DIR})
+configure_file(
+ "${CMAKE_CURRENT_SOURCE_DIR}/flb_tests_runtime.h.in"
+ "${CMAKE_CURRENT_SOURCE_DIR}/flb_tests_runtime.h"
+ )
+
+foreach(source_file ${CHECK_PROGRAMS})
+ get_filename_component(o_source_file_we ${source_file} NAME_WE)
+ set(source_file_we flb-rt-${o_source_file_we})
+ if(FLB_WITHOUT_${source_file_we})
+ message("Skipping test ${source_file_we}")
+ else()
+ add_executable(
+ ${source_file_we}
+ ${source_file}
+ )
+ add_sanitizers(${source_file_we})
+ target_link_libraries(${source_file_we}
+ fluent-bit-static
+ ${CMAKE_THREAD_LIBS_INIT}
+ ${SYSTEMD_LIB}
+ )
+ if(FLB_AVRO_ENCODER)
+ target_link_libraries(${source_file_we} avro-static jansson)
+ endif()
+ add_test(NAME ${source_file_we}
+ COMMAND ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${source_file_we}
+ WORKING_DIRECTORY ${CMAKE_HOME_DIRECTORY}/build)
+ set_tests_properties(${source_file_we} PROPERTIES LABELS "runtime")
+ set_property(TARGET ${source_file_we} APPEND_STRING PROPERTY COMPILE_FLAGS "-D${o_source_file_we}")
+ endif()
+endforeach()
diff --git a/fluent-bit/tests/runtime/config_map_opts.c b/fluent-bit/tests/runtime/config_map_opts.c
new file mode 100644
index 00000000..57b396bb
--- /dev/null
+++ b/fluent-bit/tests/runtime/config_map_opts.c
@@ -0,0 +1,35 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+#include <fluent-bit.h>
+#include "flb_tests_runtime.h"
+
+/* Test functions */
+void flb_test_config_map_opts(void);
+
+/* Test list */
+TEST_LIST = {
+ {"config_map_opts", flb_test_config_map_opts },
+ {NULL, NULL}
+};
+
+void flb_test_config_map_opts(void)
+{
+ flb_ctx_t *ctx = NULL;
+ int in_ffd, r;
+
+ flb_init_env();
+
+ ctx = flb_create();
+ in_ffd = flb_input(ctx, (char *) "tail", NULL);
+ r = flb_input_property_check(ctx, in_ffd, "invalid_option", "invalid value");
+ TEST_CHECK(r != 0);
+
+ in_ffd = flb_filter(ctx, (char *) "kubernetes", NULL);
+ r = flb_filter_property_check(ctx, in_ffd, "invalid_option", "invalid value");
+ TEST_CHECK(r != 0);
+
+ in_ffd = flb_output(ctx, (char *) "stdout", NULL);
+ r = flb_output_property_check(ctx, in_ffd, "invalid_option", "invalid value");
+ TEST_CHECK(r != 0);
+
+ flb_destroy(ctx);
+}
diff --git a/fluent-bit/tests/runtime/core-timeout.c b/fluent-bit/tests/runtime/core-timeout.c
new file mode 100644
index 00000000..10146d3c
--- /dev/null
+++ b/fluent-bit/tests/runtime/core-timeout.c
@@ -0,0 +1,94 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/* Fluent Bit
+ * ==========
+ * Copyright (C) 2015-2022 The Fluent Bit Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <fluent-bit.h>
+#include "flb_tests_runtime.h"
+
+#define ELAPSED_TIME_LIMIT 9
+
+void flb_test_timeout_coroutine_recovery()
+{
+ int output_instance_id;
+ int input_instance_id;
+ time_t elapsed_time;
+ time_t start_time;
+ time_t stop_time;
+ flb_ctx_t *ctx;
+ int64_t ret;
+
+ ctx = flb_create();
+
+ TEST_CHECK(flb_service_set(ctx, "Flush", "0.5",
+ "Grace", "10",
+ NULL) == 0);
+
+ ret = flb_service_set(ctx,
+ "Log_Level", "info",
+ NULL);
+
+ TEST_CHECK_(ret == 0, "setting service options");
+
+ input_instance_id = flb_input(ctx, (char *) "dummy", NULL);
+ TEST_CHECK(input_instance_id >= 0);
+
+ TEST_CHECK(flb_input_set(ctx, input_instance_id,
+ "samples", "1",
+ "rate" , "1",
+ NULL) == 0);
+
+ output_instance_id = flb_output(ctx, (char *) "tcp", NULL);
+ TEST_CHECK(output_instance_id >= 0);
+ TEST_CHECK(flb_output_set(ctx, output_instance_id,
+ "match", "*",
+ "retry_limit", "no_retries",
+ "host", "35.243.247.233",
+ "port", "54321",
+ "net.keepalive", "off",
+ "net.connect_timeout", "5s",
+ NULL) == 0);
+
+ /* Start test */
+ /* Start the engine */
+ ret = flb_start(ctx);
+ TEST_CHECK_(ret == 0, "starting engine");
+
+ sleep(10);
+
+ start_time = time(NULL);
+
+ ret = flb_stop(ctx);
+ TEST_CHECK_(ret == 0, "stopping engine");
+
+ stop_time = time(NULL);
+
+ if (ctx) {
+ flb_destroy(ctx);
+ }
+
+ elapsed_time = stop_time - start_time;
+
+ TEST_CHECK_(ELAPSED_TIME_LIMIT >= elapsed_time,
+ "for hung coroutines");
+}
+
+/* Test list */
+TEST_LIST = {
+ {"timeout_coroutine_recovery", flb_test_timeout_coroutine_recovery},
+ {NULL, NULL}
+};
diff --git a/fluent-bit/tests/runtime/core_chunk_trace.c b/fluent-bit/tests/runtime/core_chunk_trace.c
new file mode 100644
index 00000000..6fb00a9b
--- /dev/null
+++ b/fluent-bit/tests/runtime/core_chunk_trace.c
@@ -0,0 +1,153 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/* Fluent Bit
+ * ==========
+ * Copyright (C) 2015-2016 Treasure Data Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <fluent-bit.h>
+#include <fluent-bit/flb_time.h>
+#include <fluent-bit/flb_chunk_trace.h>
+#include <fluent-bit/flb_router.h>
+#include <pthread.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include "flb_tests_runtime.h"
+
+
+struct callback_record {
+ void *data;
+ size_t size;
+};
+
+struct callback_records {
+ int num_records;
+ struct callback_record *records;
+};
+
+int callback_add_record(void* data, size_t size, void* cb_data)
+{
+ struct callback_records *ctx = (struct callback_records *)cb_data;
+
+ if (size > 0) {
+ flb_info("[test] flush record");
+ /* We should check ctx->num_records has a valid value. */
+ if (ctx->num_records < 0) {
+ return -1;
+ }
+ if (ctx->records == NULL) {
+ ctx->records = (struct callback_record *)
+ flb_calloc(1, sizeof(struct callback_record));
+ } else {
+ ctx->records = (struct callback_record *)
+ flb_realloc(ctx->records,
+ (ctx->num_records+1)*sizeof(struct callback_record));
+ }
+ if (ctx->records == NULL) {
+ return -1;
+ }
+ ctx->records[ctx->num_records].size = size;
+ ctx->records[ctx->num_records].data = data;
+ ctx->num_records++;
+ }
+ return 0;
+}
+
+void do_test_records_trace(void (*records_cb)(struct callback_records *))
+{
+ flb_ctx_t *ctx = NULL;
+ struct flb_input_instance *input;
+ struct flb_output_instance *output;
+ int i;
+ struct flb_lib_out_cb cb;
+ struct callback_records *records;
+
+ records = flb_calloc(1, sizeof(struct callback_records));
+ records->num_records = 0;
+ records->records = NULL;
+ cb.cb = callback_add_record;
+ cb.data = (void *)records;
+
+ ctx = flb_create();
+
+ input = flb_input_new(ctx->config, "dummy", NULL, FLB_TRUE);
+ TEST_CHECK(input != NULL);
+
+ output = flb_output_new(ctx->config, (char *) "stdout", NULL, FLB_TRUE);
+ TEST_CHECK(output != NULL);
+
+ TEST_CHECK(flb_service_set(ctx, "Flush", "0.5",
+ "Grace", "1",
+ "Enable_Chunk_Trace", "On",
+ NULL) == 0);
+
+
+ flb_router_connect_direct(input, output);
+
+ TEST_CHECK(flb_chunk_trace_context_new(input, "lib", "test.", (void *)&cb, NULL) != NULL);
+
+ /* Start test */
+ TEST_CHECK(flb_start(ctx) == 0);
+
+ /* 4 sec passed. It must have flushed */
+ sleep(5);
+
+ records_cb(records);
+
+ flb_stop(ctx);
+
+ for (i = 0; i < records->num_records; i++) {
+ flb_lib_free(records->records[i].data);
+ }
+ flb_free(records->records);
+ flb_free(records);
+
+ flb_destroy(ctx);
+}
+
+void flb_test_dummy_records_trace_simple(struct callback_records *records)
+{
+ int i;
+ msgpack_unpacked result;
+ msgpack_object *obj;
+ size_t off = 0;
+ struct flb_time ftm;
+
+ TEST_CHECK(records->num_records > 0);
+ for (i = 0; i < records->num_records; i++) {
+ msgpack_unpacked_init(&result);
+
+ while (msgpack_unpack_next(&result, records->records[i].data,
+ records->records[i].size, &off) == MSGPACK_UNPACK_SUCCESS) {
+ flb_time_pop_from_msgpack(&ftm, &result, &obj);
+ //TEST_CHECK(ftm.tm.tv_sec == 1234);
+ //TEST_CHECK(ftm.tm.tv_nsec == 1234);
+ }
+ msgpack_unpacked_destroy(&result);
+ }
+}
+
+void flb_test_trace()
+{
+ do_test_records_trace(flb_test_dummy_records_trace_simple);
+}
+
+/* Test list */
+TEST_LIST = {
+#ifdef FLB_HAVE_CHUNK_TRACE
+ {"trace", flb_test_trace},
+#endif
+ {NULL, NULL}
+};
diff --git a/fluent-bit/tests/runtime/core_engine.c b/fluent-bit/tests/runtime/core_engine.c
new file mode 100644
index 00000000..a7f136a3
--- /dev/null
+++ b/fluent-bit/tests/runtime/core_engine.c
@@ -0,0 +1,167 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/* Fluent Bit
+ * ==========
+ * Copyright (C) 2019-2020 The Fluent Bit Authors
+ * Copyright (C) 2015-2018 Treasure Data Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <fluent-bit.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+
+#include "flb_tests_runtime.h"
+
+/* Test data*/
+
+/* Test functions*/
+void flb_test_engine_wildcard(void);
+
+/* Test list */
+TEST_LIST = {
+ {"wildcard", flb_test_engine_wildcard },
+ {NULL, NULL}
+};
+
+#define MAX_WAIT_TIME 1500
+int64_t result_time;
+static inline int64_t set_result(int64_t v)
+{
+ int64_t old = __sync_lock_test_and_set(&result_time, v);
+ return old;
+}
+
+static inline int64_t get_result(void)
+{
+ int64_t old = __sync_fetch_and_add(&result_time, 0);
+
+ return old;
+}
+
+static inline int64_t time_in_ms()
+{
+ int ms;
+ struct timespec s;
+ TEST_CHECK(clock_gettime(CLOCK_MONOTONIC, &s) == 0);
+ ms = s.tv_nsec / 1.0e6;
+ if (ms >= 1000) {
+ ms = 0;
+ }
+ return 1000 * s.tv_sec + ms;
+}
+
+int callback_test(void* data, size_t size, void* cb_data)
+{
+ if (size > 0) {
+ flb_lib_free(data);
+ set_result(time_in_ms()); /* success */
+ }
+ return 0;
+}
+
+int check_routing(const char* tag,
+ const char* match,
+ const char* match_regex,
+ bool expect)
+{
+ int in_ffd;
+ int out_ffd;
+ int64_t ret;
+ int64_t start;
+ flb_ctx_t *ctx = NULL;
+ char *str = (char*)"[1, {\"key\":\"value\"}]";
+
+ struct flb_lib_out_cb cb;
+ cb.cb = callback_test;
+ cb.data = NULL;
+
+ /* initialize */
+ ctx = flb_create();
+
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ TEST_CHECK(in_ffd >= 0);
+ flb_input_set(ctx, in_ffd, "tag", tag, NULL);
+
+ out_ffd = flb_output(ctx, (char *) "lib", &cb);
+ TEST_CHECK(out_ffd >= 0);
+ if (match) {
+ flb_output_set(ctx, out_ffd, "match", match, NULL);
+ }
+ if (match_regex) {
+ flb_output_set(ctx, out_ffd, "match_regex", match_regex, NULL);
+ }
+
+ flb_service_set(ctx, "Flush", "1", "Grace", "1", "Daemon", "false", NULL);
+
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ /* start test */
+ flb_lib_push(ctx, in_ffd, str, strlen(str));
+ set_result(0);
+ start = time_in_ms();
+ while ( (ret = get_result()) == 0 && (time_in_ms() - start < MAX_WAIT_TIME))
+ usleep(10);
+
+ if (expect ? ret == 0 : ret > 0) {
+ flb_error("Mismatch: tag:%s, match:%s, match_regex:%s, expect:%s\n",
+ tag,
+ match ? match : "null",
+ match_regex ? match_regex : "null",
+ expect ? "true" : "false");
+ }
+ TEST_CHECK(expect ? ret > 0 : ret == 0);
+
+ /* finalize */
+ flb_stop(ctx);
+ flb_destroy(ctx);
+
+ return 0;
+}
+
+void flb_test_engine_wildcard(void)
+{
+ struct test_wildcard_fmt {
+ const char* tag;
+ const char* match;
+ const char* match_regex;
+ bool expect;
+ };
+ int i = 0;
+
+ struct test_wildcard_fmt checklist[] =
+ {
+ {"cpu.rpi","cpu.rpi", NULL, true },
+ {"cpu.rpi","cpu.ard", NULL, false },
+ {"cpu.rpi","cpu.*", NULL, true },
+ {"cpu.rpi","*", NULL, true },
+ {"cpu.rpi","*.*", NULL, true },
+ {"cpu.rpi","*.rpi", NULL, true },
+ {"cpu.rpi","mem.*", NULL, false },
+ {"cpu.rpi","*u.r*", NULL, true },
+ {"cpu.rpi",NULL, "[a-z]*", true },
+ {"cpu.rpi",NULL, "[A-Z]*", false },
+ {NULL, NULL, NULL, 0}
+ };
+
+ while(checklist[i].tag != NULL){
+ check_routing(checklist[i].tag,
+ checklist[i].match,
+ checklist[i].match_regex,
+ checklist[i].expect);
+ i++;
+ }
+}
diff --git a/fluent-bit/tests/runtime/core_log.c b/fluent-bit/tests/runtime/core_log.c
new file mode 100644
index 00000000..8d466b70
--- /dev/null
+++ b/fluent-bit/tests/runtime/core_log.c
@@ -0,0 +1,308 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+#include <fluent-bit.h>
+#include <fluent-bit/flb_log.h>
+
+#include "flb_tests_runtime.h"
+
+
+static void check_result(char *level, int ret, int expect_truncated)
+{
+ if (expect_truncated == FLB_TRUE) {
+ if (!TEST_CHECK(ret > 0)) {
+ TEST_MSG("log is not truncated.level=%s ret=%d",level, ret);
+ /*
+ printf("ret=%d\n", ret);
+ */
+ }
+ }
+ else {
+ if (!TEST_CHECK(ret == 0)) {
+ TEST_MSG("log is truncated.level=%s ret=%d",level, ret);
+ /*
+ printf("ret=%d\n", ret);
+ */
+ }
+ }
+
+}
+static void check_if_truncated(char* data, int expect_truncated)
+{
+ int ret;
+ ret = flb_error_is_truncated("%s", data);
+ check_result("error", ret, expect_truncated);
+
+ ret = flb_warn_is_truncated("%s", data);
+ check_result("warn", ret, expect_truncated);
+
+ ret = flb_info_is_truncated("%s", data);
+ check_result("info", ret, expect_truncated);
+
+ ret = flb_debug_is_truncated("%s", data);
+ check_result("debug", ret, expect_truncated);
+}
+
+static int cb_not_truncated_log(void *record, size_t size, void *data)
+{
+ check_if_truncated((char*)data, FLB_FALSE);
+
+ flb_free(record);
+ return 0;
+}
+
+
+static int cb_truncated_log(void *record, size_t size, void *data)
+{
+ check_if_truncated((char*)data, FLB_TRUE);
+
+ flb_free(record);
+ return 0;
+}
+
+static int cb_resize(void *record, size_t size, void *data)
+{
+ int ret;
+ char *log = (char*)data;
+
+ ret = flb_error_is_truncated("%s", log);
+ if (!TEST_CHECK(ret > 0)) {
+ TEST_MSG("log is not truncated.ret=%d", ret);
+ }
+
+ ret = flb_error_is_truncated("%.*s", (int)(strlen(log) - ret), log);
+ if (!TEST_CHECK(ret == 0)) {
+ TEST_MSG("log is truncated.ret=%d", ret);
+ }
+
+ flb_free(record);
+ return 0;
+}
+
+static int cb_log_level(void *record, size_t size, void *data)
+{
+ int ret;
+ char *log = (char*)data;
+
+ ret = flb_error_is_truncated("%s", log);
+ if (!TEST_CHECK(ret > 0)) {
+ TEST_MSG("log is not truncated.ret=%d", ret);
+ }
+
+ /* log_level is error. The function will return 0 */
+ ret = flb_info_is_truncated("%s", log);
+ if (!TEST_CHECK(ret == 0)) {
+ TEST_MSG("log is truncated.ret=%d", ret);
+ }
+
+ flb_free(record);
+ return 0;
+}
+
+void test_not_truncated_log()
+{
+ int ret;
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+ int i;
+ char *msg = "[1, {\"msg\":\"body\"}]";
+ char log[128] = {0};
+ struct flb_lib_out_cb cb_data;
+
+ cb_data.cb = cb_not_truncated_log;
+ cb_data.data = &log[0];
+
+ for (i=0; i<sizeof(log)-1; i++) {
+ log[i] = 'a';
+ }
+
+ ctx = flb_create();
+ TEST_CHECK(ctx != NULL);
+
+ flb_service_set(ctx,
+ "Flush", "0.200000000",
+ "Grace", "1",
+ "Log_Level", "debug",
+ NULL);
+
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ TEST_CHECK(in_ffd >= 0);
+
+ out_ffd = flb_output(ctx, (char *) "lib", (void*)&cb_data);
+ TEST_CHECK(out_ffd >= 0);
+ ret = flb_output_set(ctx, out_ffd,
+ "match", "*",
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ ret = flb_lib_push(ctx, in_ffd, msg, strlen(msg));
+ TEST_CHECK(ret >= 0);
+
+ sleep(1);
+
+ flb_stop(ctx);
+ flb_destroy(ctx);
+
+}
+
+void test_truncated_log()
+{
+ int ret;
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+ int i;
+ char *msg = "[1, {\"msg\":\"body\"}]";
+ char log[4096 * 5] = {0};
+ struct flb_lib_out_cb cb_data;
+
+ cb_data.cb = cb_truncated_log;
+ cb_data.data = &log[0];
+
+ for (i=0; i<sizeof(log)-1; i++) {
+ log[i] = 'a';
+ }
+
+ ctx = flb_create();
+ TEST_CHECK(ctx != NULL);
+
+ flb_service_set(ctx,
+ "Flush", "0.200000000",
+ "Grace", "1",
+ "Log_Level", "debug",
+ NULL);
+
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ TEST_CHECK(in_ffd >= 0);
+
+ out_ffd = flb_output(ctx, (char *) "lib", (void*)&cb_data);
+ TEST_CHECK(out_ffd >= 0);
+ ret = flb_output_set(ctx, out_ffd,
+ "match", "*",
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ ret = flb_lib_push(ctx, in_ffd, msg, strlen(msg));
+ TEST_CHECK(ret >= 0);
+
+ sleep(1);
+
+ flb_stop(ctx);
+ flb_destroy(ctx);
+
+}
+
+void test_resize()
+{
+ int ret;
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+ int i;
+ char *msg = "[1, {\"msg\":\"body\"}]";
+ char log[4096 * 5] = {0};
+ struct flb_lib_out_cb cb_data;
+
+ cb_data.cb = cb_resize;
+ cb_data.data = &log[0];
+
+ for (i=0; i<sizeof(log)-1; i++) {
+ log[i] = 'a';
+ }
+
+ ctx = flb_create();
+ TEST_CHECK(ctx != NULL);
+
+ flb_service_set(ctx,
+ "Flush", "0.200000000",
+ "Grace", "1",
+ "Log_Level", "error",
+ NULL);
+
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ TEST_CHECK(in_ffd >= 0);
+
+ out_ffd = flb_output(ctx, (char *) "lib", (void*)&cb_data);
+ TEST_CHECK(out_ffd >= 0);
+ ret = flb_output_set(ctx, out_ffd,
+ "match", "*",
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ ret = flb_lib_push(ctx, in_ffd, msg, strlen(msg));
+ TEST_CHECK(ret >= 0);
+
+ sleep(1);
+
+ flb_stop(ctx);
+ flb_destroy(ctx);
+
+}
+
+void test_log_level()
+{
+ int ret;
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+ int i;
+ char *msg = "[1, {\"msg\":\"body\"}]";
+ char log[4096 * 5] = {0};
+ struct flb_lib_out_cb cb_data;
+
+ cb_data.cb = cb_log_level;
+ cb_data.data = &log[0];
+
+ for (i=0; i<sizeof(log)-1; i++) {
+ log[i] = 'a';
+ }
+
+ ctx = flb_create();
+ TEST_CHECK(ctx != NULL);
+
+ flb_service_set(ctx,
+ "Flush", "0.200000000",
+ "Grace", "1",
+ "Log_Level", "error",
+ NULL);
+
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ TEST_CHECK(in_ffd >= 0);
+
+ out_ffd = flb_output(ctx, (char *) "lib", (void*)&cb_data);
+ TEST_CHECK(out_ffd >= 0);
+ ret = flb_output_set(ctx, out_ffd,
+ "match", "*",
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ ret = flb_lib_push(ctx, in_ffd, msg, strlen(msg));
+ TEST_CHECK(ret >= 0);
+
+ sleep(1);
+
+ flb_stop(ctx);
+ flb_destroy(ctx);
+
+}
+
+/* Test list */
+TEST_LIST = {
+ {"not_truncated_log", test_not_truncated_log },
+ {"truncated_log", test_truncated_log },
+ {"resize", test_resize },
+ {"log_level", test_log_level },
+ {NULL, NULL}
+};
diff --git a/fluent-bit/tests/runtime/custom_calyptia_test.c b/fluent-bit/tests/runtime/custom_calyptia_test.c
new file mode 100644
index 00000000..5ad30e7a
--- /dev/null
+++ b/fluent-bit/tests/runtime/custom_calyptia_test.c
@@ -0,0 +1,59 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+#include <fluent-bit.h>
+#include <fluent-bit/flb_custom.h>
+#include "flb_tests_runtime.h"
+
+flb_sds_t custom_calyptia_pipeline_config_get(struct flb_config *ctx);
+
+void flb_custom_calyptia_pipeline_config_get_test()
+{
+ const char *cfg_str = "[INPUT]\n name dummy.0\n[INPUT]\n name fluentbit_metrics.1\n tag _calyptia_cloud\n scrape_on_start true\n scrape_interval 30\n\n\n[OUTPUT]\n name stdout.0\n match *\n retry_limit 1\n\n";
+ flb_ctx_t *ctx;
+ int in_ffd_dummy;
+ int in_ffd_metrics;
+ int out_ffd;
+ struct flb_custom_instance *calyptia;
+ flb_sds_t cfg;
+
+ ctx = flb_create();
+ flb_service_set(ctx, "flush", "1", "grace", "1", NULL);
+
+ in_ffd_dummy = flb_input(ctx, (char *) "dummy", NULL);
+ TEST_CHECK(in_ffd_dummy >= 0);
+
+ in_ffd_metrics = flb_input(ctx, (char *) "fluentbit_metrics", NULL);
+ TEST_CHECK(in_ffd_metrics >= 0);
+ flb_input_set(ctx, in_ffd_metrics,
+ "tag", "_calyptia_cloud",
+ "scrape_on_start", "true",
+ "scrape_interval", "30",
+ NULL);
+
+ out_ffd = flb_output(ctx, (char *) "stdout", NULL);
+ TEST_CHECK(out_ffd >= 0);
+ flb_output_set(ctx, out_ffd, "match", "*", NULL);
+
+ calyptia = flb_custom_new(ctx->config, (char *)"calyptia", NULL);
+ TEST_CHECK(calyptia != NULL);
+ flb_custom_set_property(calyptia, "api_key", "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA");
+ flb_custom_set_property(calyptia, "log_level", "debug");
+ flb_custom_set_property(calyptia, "log_level", "7DDD2941-3ED6-4B8C-9F84-DD04C4A018A4");
+ flb_custom_set_property(calyptia, "add_label", "pipeline_id 7DDD2941-3ED6-4B8C-9F84-DD04C4A018A4");
+ flb_custom_set_property(calyptia, "calyptia_host", "cloud-api.calyptia.com");
+ flb_custom_set_property(calyptia, "calyptia_port", "443");
+
+ cfg = custom_calyptia_pipeline_config_get(ctx->config);
+ TEST_CHECK(strcmp(cfg, cfg_str) == 0);
+
+ // fix a thread local storage bug on macos
+ flb_output_prepare();
+ flb_sds_destroy(cfg);
+ flb_destroy(ctx);
+}
+
+/* Test list */
+TEST_LIST = {
+ {"get_config_test", flb_custom_calyptia_pipeline_config_get_test},
+ {NULL, NULL}
+};
diff --git a/fluent-bit/tests/runtime/data/common/json_invalid.h b/fluent-bit/tests/runtime/data/common/json_invalid.h
new file mode 100755
index 00000000..87172d15
--- /dev/null
+++ b/fluent-bit/tests/runtime/data/common/json_invalid.h
@@ -0,0 +1,6 @@
+#define JSON_INVALID "[" \
+ "1448403340," \
+ "{" \
+ "{{{{{{{{" "\"END_KEY\": \"JSON_END\"" \
+ "}]"
+
diff --git a/fluent-bit/tests/runtime/data/common/json_long.h b/fluent-bit/tests/runtime/data/common/json_long.h
new file mode 100755
index 00000000..e579b679
--- /dev/null
+++ b/fluent-bit/tests/runtime/data/common/json_long.h
@@ -0,0 +1,1006 @@
+#define JSON_LONG "[" \
+ "1448403340," \
+ "{" \
+ "\"key_0\": \"val_0\"," \
+ "\"key_1\": \"val_1\"," \
+ "\"key_2\": \"val_2\"," \
+ "\"key_3\": \"val_3\"," \
+ "\"key_4\": \"val_4\"," \
+ "\"key_5\": \"val_5\"," \
+ "\"key_6\": \"val_6\"," \
+ "\"key_7\": \"val_7\"," \
+ "\"key_8\": \"val_8\"," \
+ "\"key_9\": \"val_9\"," \
+ "\"key_10\": \"val_10\"," \
+ "\"key_11\": \"val_11\"," \
+ "\"key_12\": \"val_12\"," \
+ "\"key_13\": \"val_13\"," \
+ "\"key_14\": \"val_14\"," \
+ "\"key_15\": \"val_15\"," \
+ "\"key_16\": \"val_16\"," \
+ "\"key_17\": \"val_17\"," \
+ "\"key_18\": \"val_18\"," \
+ "\"key_19\": \"val_19\"," \
+ "\"key_20\": \"val_20\"," \
+ "\"key_21\": \"val_21\"," \
+ "\"key_22\": \"val_22\"," \
+ "\"key_23\": \"val_23\"," \
+ "\"key_24\": \"val_24\"," \
+ "\"key_25\": \"val_25\"," \
+ "\"key_26\": \"val_26\"," \
+ "\"key_27\": \"val_27\"," \
+ "\"key_28\": \"val_28\"," \
+ "\"key_29\": \"val_29\"," \
+ "\"key_30\": \"val_30\"," \
+ "\"key_31\": \"val_31\"," \
+ "\"key_32\": \"val_32\"," \
+ "\"key_33\": \"val_33\"," \
+ "\"key_34\": \"val_34\"," \
+ "\"key_35\": \"val_35\"," \
+ "\"key_36\": \"val_36\"," \
+ "\"key_37\": \"val_37\"," \
+ "\"key_38\": \"val_38\"," \
+ "\"key_39\": \"val_39\"," \
+ "\"key_40\": \"val_40\"," \
+ "\"key_41\": \"val_41\"," \
+ "\"key_42\": \"val_42\"," \
+ "\"key_43\": \"val_43\"," \
+ "\"key_44\": \"val_44\"," \
+ "\"key_45\": \"val_45\"," \
+ "\"key_46\": \"val_46\"," \
+ "\"key_47\": \"val_47\"," \
+ "\"key_48\": \"val_48\"," \
+ "\"key_49\": \"val_49\"," \
+ "\"key_50\": \"val_50\"," \
+ "\"key_51\": \"val_51\"," \
+ "\"key_52\": \"val_52\"," \
+ "\"key_53\": \"val_53\"," \
+ "\"key_54\": \"val_54\"," \
+ "\"key_55\": \"val_55\"," \
+ "\"key_56\": \"val_56\"," \
+ "\"key_57\": \"val_57\"," \
+ "\"key_58\": \"val_58\"," \
+ "\"key_59\": \"val_59\"," \
+ "\"key_60\": \"val_60\"," \
+ "\"key_61\": \"val_61\"," \
+ "\"key_62\": \"val_62\"," \
+ "\"key_63\": \"val_63\"," \
+ "\"key_64\": \"val_64\"," \
+ "\"key_65\": \"val_65\"," \
+ "\"key_66\": \"val_66\"," \
+ "\"key_67\": \"val_67\"," \
+ "\"key_68\": \"val_68\"," \
+ "\"key_69\": \"val_69\"," \
+ "\"key_70\": \"val_70\"," \
+ "\"key_71\": \"val_71\"," \
+ "\"key_72\": \"val_72\"," \
+ "\"key_73\": \"val_73\"," \
+ "\"key_74\": \"val_74\"," \
+ "\"key_75\": \"val_75\"," \
+ "\"key_76\": \"val_76\"," \
+ "\"key_77\": \"val_77\"," \
+ "\"key_78\": \"val_78\"," \
+ "\"key_79\": \"val_79\"," \
+ "\"key_80\": \"val_80\"," \
+ "\"key_81\": \"val_81\"," \
+ "\"key_82\": \"val_82\"," \
+ "\"key_83\": \"val_83\"," \
+ "\"key_84\": \"val_84\"," \
+ "\"key_85\": \"val_85\"," \
+ "\"key_86\": \"val_86\"," \
+ "\"key_87\": \"val_87\"," \
+ "\"key_88\": \"val_88\"," \
+ "\"key_89\": \"val_89\"," \
+ "\"key_90\": \"val_90\"," \
+ "\"key_91\": \"val_91\"," \
+ "\"key_92\": \"val_92\"," \
+ "\"key_93\": \"val_93\"," \
+ "\"key_94\": \"val_94\"," \
+ "\"key_95\": \"val_95\"," \
+ "\"key_96\": \"val_96\"," \
+ "\"key_97\": \"val_97\"," \
+ "\"key_98\": \"val_98\"," \
+ "\"key_99\": \"val_99\"," \
+ "\"key_100\": \"val_100\"," \
+ "\"key_101\": \"val_101\"," \
+ "\"key_102\": \"val_102\"," \
+ "\"key_103\": \"val_103\"," \
+ "\"key_104\": \"val_104\"," \
+ "\"key_105\": \"val_105\"," \
+ "\"key_106\": \"val_106\"," \
+ "\"key_107\": \"val_107\"," \
+ "\"key_108\": \"val_108\"," \
+ "\"key_109\": \"val_109\"," \
+ "\"key_110\": \"val_110\"," \
+ "\"key_111\": \"val_111\"," \
+ "\"key_112\": \"val_112\"," \
+ "\"key_113\": \"val_113\"," \
+ "\"key_114\": \"val_114\"," \
+ "\"key_115\": \"val_115\"," \
+ "\"key_116\": \"val_116\"," \
+ "\"key_117\": \"val_117\"," \
+ "\"key_118\": \"val_118\"," \
+ "\"key_119\": \"val_119\"," \
+ "\"key_120\": \"val_120\"," \
+ "\"key_121\": \"val_121\"," \
+ "\"key_122\": \"val_122\"," \
+ "\"key_123\": \"val_123\"," \
+ "\"key_124\": \"val_124\"," \
+ "\"key_125\": \"val_125\"," \
+ "\"key_126\": \"val_126\"," \
+ "\"key_127\": \"val_127\"," \
+ "\"key_128\": \"val_128\"," \
+ "\"key_129\": \"val_129\"," \
+ "\"key_130\": \"val_130\"," \
+ "\"key_131\": \"val_131\"," \
+ "\"key_132\": \"val_132\"," \
+ "\"key_133\": \"val_133\"," \
+ "\"key_134\": \"val_134\"," \
+ "\"key_135\": \"val_135\"," \
+ "\"key_136\": \"val_136\"," \
+ "\"key_137\": \"val_137\"," \
+ "\"key_138\": \"val_138\"," \
+ "\"key_139\": \"val_139\"," \
+ "\"key_140\": \"val_140\"," \
+ "\"key_141\": \"val_141\"," \
+ "\"key_142\": \"val_142\"," \
+ "\"key_143\": \"val_143\"," \
+ "\"key_144\": \"val_144\"," \
+ "\"key_145\": \"val_145\"," \
+ "\"key_146\": \"val_146\"," \
+ "\"key_147\": \"val_147\"," \
+ "\"key_148\": \"val_148\"," \
+ "\"key_149\": \"val_149\"," \
+ "\"key_150\": \"val_150\"," \
+ "\"key_151\": \"val_151\"," \
+ "\"key_152\": \"val_152\"," \
+ "\"key_153\": \"val_153\"," \
+ "\"key_154\": \"val_154\"," \
+ "\"key_155\": \"val_155\"," \
+ "\"key_156\": \"val_156\"," \
+ "\"key_157\": \"val_157\"," \
+ "\"key_158\": \"val_158\"," \
+ "\"key_159\": \"val_159\"," \
+ "\"key_160\": \"val_160\"," \
+ "\"key_161\": \"val_161\"," \
+ "\"key_162\": \"val_162\"," \
+ "\"key_163\": \"val_163\"," \
+ "\"key_164\": \"val_164\"," \
+ "\"key_165\": \"val_165\"," \
+ "\"key_166\": \"val_166\"," \
+ "\"key_167\": \"val_167\"," \
+ "\"key_168\": \"val_168\"," \
+ "\"key_169\": \"val_169\"," \
+ "\"key_170\": \"val_170\"," \
+ "\"key_171\": \"val_171\"," \
+ "\"key_172\": \"val_172\"," \
+ "\"key_173\": \"val_173\"," \
+ "\"key_174\": \"val_174\"," \
+ "\"key_175\": \"val_175\"," \
+ "\"key_176\": \"val_176\"," \
+ "\"key_177\": \"val_177\"," \
+ "\"key_178\": \"val_178\"," \
+ "\"key_179\": \"val_179\"," \
+ "\"key_180\": \"val_180\"," \
+ "\"key_181\": \"val_181\"," \
+ "\"key_182\": \"val_182\"," \
+ "\"key_183\": \"val_183\"," \
+ "\"key_184\": \"val_184\"," \
+ "\"key_185\": \"val_185\"," \
+ "\"key_186\": \"val_186\"," \
+ "\"key_187\": \"val_187\"," \
+ "\"key_188\": \"val_188\"," \
+ "\"key_189\": \"val_189\"," \
+ "\"key_190\": \"val_190\"," \
+ "\"key_191\": \"val_191\"," \
+ "\"key_192\": \"val_192\"," \
+ "\"key_193\": \"val_193\"," \
+ "\"key_194\": \"val_194\"," \
+ "\"key_195\": \"val_195\"," \
+ "\"key_196\": \"val_196\"," \
+ "\"key_197\": \"val_197\"," \
+ "\"key_198\": \"val_198\"," \
+ "\"key_199\": \"val_199\"," \
+ "\"key_200\": \"val_200\"," \
+ "\"key_201\": \"val_201\"," \
+ "\"key_202\": \"val_202\"," \
+ "\"key_203\": \"val_203\"," \
+ "\"key_204\": \"val_204\"," \
+ "\"key_205\": \"val_205\"," \
+ "\"key_206\": \"val_206\"," \
+ "\"key_207\": \"val_207\"," \
+ "\"key_208\": \"val_208\"," \
+ "\"key_209\": \"val_209\"," \
+ "\"key_210\": \"val_210\"," \
+ "\"key_211\": \"val_211\"," \
+ "\"key_212\": \"val_212\"," \
+ "\"key_213\": \"val_213\"," \
+ "\"key_214\": \"val_214\"," \
+ "\"key_215\": \"val_215\"," \
+ "\"key_216\": \"val_216\"," \
+ "\"key_217\": \"val_217\"," \
+ "\"key_218\": \"val_218\"," \
+ "\"key_219\": \"val_219\"," \
+ "\"key_220\": \"val_220\"," \
+ "\"key_221\": \"val_221\"," \
+ "\"key_222\": \"val_222\"," \
+ "\"key_223\": \"val_223\"," \
+ "\"key_224\": \"val_224\"," \
+ "\"key_225\": \"val_225\"," \
+ "\"key_226\": \"val_226\"," \
+ "\"key_227\": \"val_227\"," \
+ "\"key_228\": \"val_228\"," \
+ "\"key_229\": \"val_229\"," \
+ "\"key_230\": \"val_230\"," \
+ "\"key_231\": \"val_231\"," \
+ "\"key_232\": \"val_232\"," \
+ "\"key_233\": \"val_233\"," \
+ "\"key_234\": \"val_234\"," \
+ "\"key_235\": \"val_235\"," \
+ "\"key_236\": \"val_236\"," \
+ "\"key_237\": \"val_237\"," \
+ "\"key_238\": \"val_238\"," \
+ "\"key_239\": \"val_239\"," \
+ "\"key_240\": \"val_240\"," \
+ "\"key_241\": \"val_241\"," \
+ "\"key_242\": \"val_242\"," \
+ "\"key_243\": \"val_243\"," \
+ "\"key_244\": \"val_244\"," \
+ "\"key_245\": \"val_245\"," \
+ "\"key_246\": \"val_246\"," \
+ "\"key_247\": \"val_247\"," \
+ "\"key_248\": \"val_248\"," \
+ "\"key_249\": \"val_249\"," \
+ "\"key_250\": \"val_250\"," \
+ "\"key_251\": \"val_251\"," \
+ "\"key_252\": \"val_252\"," \
+ "\"key_253\": \"val_253\"," \
+ "\"key_254\": \"val_254\"," \
+ "\"key_255\": \"val_255\"," \
+ "\"key_256\": \"val_256\"," \
+ "\"key_257\": \"val_257\"," \
+ "\"key_258\": \"val_258\"," \
+ "\"key_259\": \"val_259\"," \
+ "\"key_260\": \"val_260\"," \
+ "\"key_261\": \"val_261\"," \
+ "\"key_262\": \"val_262\"," \
+ "\"key_263\": \"val_263\"," \
+ "\"key_264\": \"val_264\"," \
+ "\"key_265\": \"val_265\"," \
+ "\"key_266\": \"val_266\"," \
+ "\"key_267\": \"val_267\"," \
+ "\"key_268\": \"val_268\"," \
+ "\"key_269\": \"val_269\"," \
+ "\"key_270\": \"val_270\"," \
+ "\"key_271\": \"val_271\"," \
+ "\"key_272\": \"val_272\"," \
+ "\"key_273\": \"val_273\"," \
+ "\"key_274\": \"val_274\"," \
+ "\"key_275\": \"val_275\"," \
+ "\"key_276\": \"val_276\"," \
+ "\"key_277\": \"val_277\"," \
+ "\"key_278\": \"val_278\"," \
+ "\"key_279\": \"val_279\"," \
+ "\"key_280\": \"val_280\"," \
+ "\"key_281\": \"val_281\"," \
+ "\"key_282\": \"val_282\"," \
+ "\"key_283\": \"val_283\"," \
+ "\"key_284\": \"val_284\"," \
+ "\"key_285\": \"val_285\"," \
+ "\"key_286\": \"val_286\"," \
+ "\"key_287\": \"val_287\"," \
+ "\"key_288\": \"val_288\"," \
+ "\"key_289\": \"val_289\"," \
+ "\"key_290\": \"val_290\"," \
+ "\"key_291\": \"val_291\"," \
+ "\"key_292\": \"val_292\"," \
+ "\"key_293\": \"val_293\"," \
+ "\"key_294\": \"val_294\"," \
+ "\"key_295\": \"val_295\"," \
+ "\"key_296\": \"val_296\"," \
+ "\"key_297\": \"val_297\"," \
+ "\"key_298\": \"val_298\"," \
+ "\"key_299\": \"val_299\"," \
+ "\"key_300\": \"val_300\"," \
+ "\"key_301\": \"val_301\"," \
+ "\"key_302\": \"val_302\"," \
+ "\"key_303\": \"val_303\"," \
+ "\"key_304\": \"val_304\"," \
+ "\"key_305\": \"val_305\"," \
+ "\"key_306\": \"val_306\"," \
+ "\"key_307\": \"val_307\"," \
+ "\"key_308\": \"val_308\"," \
+ "\"key_309\": \"val_309\"," \
+ "\"key_310\": \"val_310\"," \
+ "\"key_311\": \"val_311\"," \
+ "\"key_312\": \"val_312\"," \
+ "\"key_313\": \"val_313\"," \
+ "\"key_314\": \"val_314\"," \
+ "\"key_315\": \"val_315\"," \
+ "\"key_316\": \"val_316\"," \
+ "\"key_317\": \"val_317\"," \
+ "\"key_318\": \"val_318\"," \
+ "\"key_319\": \"val_319\"," \
+ "\"key_320\": \"val_320\"," \
+ "\"key_321\": \"val_321\"," \
+ "\"key_322\": \"val_322\"," \
+ "\"key_323\": \"val_323\"," \
+ "\"key_324\": \"val_324\"," \
+ "\"key_325\": \"val_325\"," \
+ "\"key_326\": \"val_326\"," \
+ "\"key_327\": \"val_327\"," \
+ "\"key_328\": \"val_328\"," \
+ "\"key_329\": \"val_329\"," \
+ "\"key_330\": \"val_330\"," \
+ "\"key_331\": \"val_331\"," \
+ "\"key_332\": \"val_332\"," \
+ "\"key_333\": \"val_333\"," \
+ "\"key_334\": \"val_334\"," \
+ "\"key_335\": \"val_335\"," \
+ "\"key_336\": \"val_336\"," \
+ "\"key_337\": \"val_337\"," \
+ "\"key_338\": \"val_338\"," \
+ "\"key_339\": \"val_339\"," \
+ "\"key_340\": \"val_340\"," \
+ "\"key_341\": \"val_341\"," \
+ "\"key_342\": \"val_342\"," \
+ "\"key_343\": \"val_343\"," \
+ "\"key_344\": \"val_344\"," \
+ "\"key_345\": \"val_345\"," \
+ "\"key_346\": \"val_346\"," \
+ "\"key_347\": \"val_347\"," \
+ "\"key_348\": \"val_348\"," \
+ "\"key_349\": \"val_349\"," \
+ "\"key_350\": \"val_350\"," \
+ "\"key_351\": \"val_351\"," \
+ "\"key_352\": \"val_352\"," \
+ "\"key_353\": \"val_353\"," \
+ "\"key_354\": \"val_354\"," \
+ "\"key_355\": \"val_355\"," \
+ "\"key_356\": \"val_356\"," \
+ "\"key_357\": \"val_357\"," \
+ "\"key_358\": \"val_358\"," \
+ "\"key_359\": \"val_359\"," \
+ "\"key_360\": \"val_360\"," \
+ "\"key_361\": \"val_361\"," \
+ "\"key_362\": \"val_362\"," \
+ "\"key_363\": \"val_363\"," \
+ "\"key_364\": \"val_364\"," \
+ "\"key_365\": \"val_365\"," \
+ "\"key_366\": \"val_366\"," \
+ "\"key_367\": \"val_367\"," \
+ "\"key_368\": \"val_368\"," \
+ "\"key_369\": \"val_369\"," \
+ "\"key_370\": \"val_370\"," \
+ "\"key_371\": \"val_371\"," \
+ "\"key_372\": \"val_372\"," \
+ "\"key_373\": \"val_373\"," \
+ "\"key_374\": \"val_374\"," \
+ "\"key_375\": \"val_375\"," \
+ "\"key_376\": \"val_376\"," \
+ "\"key_377\": \"val_377\"," \
+ "\"key_378\": \"val_378\"," \
+ "\"key_379\": \"val_379\"," \
+ "\"key_380\": \"val_380\"," \
+ "\"key_381\": \"val_381\"," \
+ "\"key_382\": \"val_382\"," \
+ "\"key_383\": \"val_383\"," \
+ "\"key_384\": \"val_384\"," \
+ "\"key_385\": \"val_385\"," \
+ "\"key_386\": \"val_386\"," \
+ "\"key_387\": \"val_387\"," \
+ "\"key_388\": \"val_388\"," \
+ "\"key_389\": \"val_389\"," \
+ "\"key_390\": \"val_390\"," \
+ "\"key_391\": \"val_391\"," \
+ "\"key_392\": \"val_392\"," \
+ "\"key_393\": \"val_393\"," \
+ "\"key_394\": \"val_394\"," \
+ "\"key_395\": \"val_395\"," \
+ "\"key_396\": \"val_396\"," \
+ "\"key_397\": \"val_397\"," \
+ "\"key_398\": \"val_398\"," \
+ "\"key_399\": \"val_399\"," \
+ "\"key_400\": \"val_400\"," \
+ "\"key_401\": \"val_401\"," \
+ "\"key_402\": \"val_402\"," \
+ "\"key_403\": \"val_403\"," \
+ "\"key_404\": \"val_404\"," \
+ "\"key_405\": \"val_405\"," \
+ "\"key_406\": \"val_406\"," \
+ "\"key_407\": \"val_407\"," \
+ "\"key_408\": \"val_408\"," \
+ "\"key_409\": \"val_409\"," \
+ "\"key_410\": \"val_410\"," \
+ "\"key_411\": \"val_411\"," \
+ "\"key_412\": \"val_412\"," \
+ "\"key_413\": \"val_413\"," \
+ "\"key_414\": \"val_414\"," \
+ "\"key_415\": \"val_415\"," \
+ "\"key_416\": \"val_416\"," \
+ "\"key_417\": \"val_417\"," \
+ "\"key_418\": \"val_418\"," \
+ "\"key_419\": \"val_419\"," \
+ "\"key_420\": \"val_420\"," \
+ "\"key_421\": \"val_421\"," \
+ "\"key_422\": \"val_422\"," \
+ "\"key_423\": \"val_423\"," \
+ "\"key_424\": \"val_424\"," \
+ "\"key_425\": \"val_425\"," \
+ "\"key_426\": \"val_426\"," \
+ "\"key_427\": \"val_427\"," \
+ "\"key_428\": \"val_428\"," \
+ "\"key_429\": \"val_429\"," \
+ "\"key_430\": \"val_430\"," \
+ "\"key_431\": \"val_431\"," \
+ "\"key_432\": \"val_432\"," \
+ "\"key_433\": \"val_433\"," \
+ "\"key_434\": \"val_434\"," \
+ "\"key_435\": \"val_435\"," \
+ "\"key_436\": \"val_436\"," \
+ "\"key_437\": \"val_437\"," \
+ "\"key_438\": \"val_438\"," \
+ "\"key_439\": \"val_439\"," \
+ "\"key_440\": \"val_440\"," \
+ "\"key_441\": \"val_441\"," \
+ "\"key_442\": \"val_442\"," \
+ "\"key_443\": \"val_443\"," \
+ "\"key_444\": \"val_444\"," \
+ "\"key_445\": \"val_445\"," \
+ "\"key_446\": \"val_446\"," \
+ "\"key_447\": \"val_447\"," \
+ "\"key_448\": \"val_448\"," \
+ "\"key_449\": \"val_449\"," \
+ "\"key_450\": \"val_450\"," \
+ "\"key_451\": \"val_451\"," \
+ "\"key_452\": \"val_452\"," \
+ "\"key_453\": \"val_453\"," \
+ "\"key_454\": \"val_454\"," \
+ "\"key_455\": \"val_455\"," \
+ "\"key_456\": \"val_456\"," \
+ "\"key_457\": \"val_457\"," \
+ "\"key_458\": \"val_458\"," \
+ "\"key_459\": \"val_459\"," \
+ "\"key_460\": \"val_460\"," \
+ "\"key_461\": \"val_461\"," \
+ "\"key_462\": \"val_462\"," \
+ "\"key_463\": \"val_463\"," \
+ "\"key_464\": \"val_464\"," \
+ "\"key_465\": \"val_465\"," \
+ "\"key_466\": \"val_466\"," \
+ "\"key_467\": \"val_467\"," \
+ "\"key_468\": \"val_468\"," \
+ "\"key_469\": \"val_469\"," \
+ "\"key_470\": \"val_470\"," \
+ "\"key_471\": \"val_471\"," \
+ "\"key_472\": \"val_472\"," \
+ "\"key_473\": \"val_473\"," \
+ "\"key_474\": \"val_474\"," \
+ "\"key_475\": \"val_475\"," \
+ "\"key_476\": \"val_476\"," \
+ "\"key_477\": \"val_477\"," \
+ "\"key_478\": \"val_478\"," \
+ "\"key_479\": \"val_479\"," \
+ "\"key_480\": \"val_480\"," \
+ "\"key_481\": \"val_481\"," \
+ "\"key_482\": \"val_482\"," \
+ "\"key_483\": \"val_483\"," \
+ "\"key_484\": \"val_484\"," \
+ "\"key_485\": \"val_485\"," \
+ "\"key_486\": \"val_486\"," \
+ "\"key_487\": \"val_487\"," \
+ "\"key_488\": \"val_488\"," \
+ "\"key_489\": \"val_489\"," \
+ "\"key_490\": \"val_490\"," \
+ "\"key_491\": \"val_491\"," \
+ "\"key_492\": \"val_492\"," \
+ "\"key_493\": \"val_493\"," \
+ "\"key_494\": \"val_494\"," \
+ "\"key_495\": \"val_495\"," \
+ "\"key_496\": \"val_496\"," \
+ "\"key_497\": \"val_497\"," \
+ "\"key_498\": \"val_498\"," \
+ "\"key_499\": \"val_499\"," \
+ "\"key_500\": \"val_500\"," \
+ "\"key_501\": \"val_501\"," \
+ "\"key_502\": \"val_502\"," \
+ "\"key_503\": \"val_503\"," \
+ "\"key_504\": \"val_504\"," \
+ "\"key_505\": \"val_505\"," \
+ "\"key_506\": \"val_506\"," \
+ "\"key_507\": \"val_507\"," \
+ "\"key_508\": \"val_508\"," \
+ "\"key_509\": \"val_509\"," \
+ "\"key_510\": \"val_510\"," \
+ "\"key_511\": \"val_511\"," \
+ "\"key_512\": \"val_512\"," \
+ "\"key_513\": \"val_513\"," \
+ "\"key_514\": \"val_514\"," \
+ "\"key_515\": \"val_515\"," \
+ "\"key_516\": \"val_516\"," \
+ "\"key_517\": \"val_517\"," \
+ "\"key_518\": \"val_518\"," \
+ "\"key_519\": \"val_519\"," \
+ "\"key_520\": \"val_520\"," \
+ "\"key_521\": \"val_521\"," \
+ "\"key_522\": \"val_522\"," \
+ "\"key_523\": \"val_523\"," \
+ "\"key_524\": \"val_524\"," \
+ "\"key_525\": \"val_525\"," \
+ "\"key_526\": \"val_526\"," \
+ "\"key_527\": \"val_527\"," \
+ "\"key_528\": \"val_528\"," \
+ "\"key_529\": \"val_529\"," \
+ "\"key_530\": \"val_530\"," \
+ "\"key_531\": \"val_531\"," \
+ "\"key_532\": \"val_532\"," \
+ "\"key_533\": \"val_533\"," \
+ "\"key_534\": \"val_534\"," \
+ "\"key_535\": \"val_535\"," \
+ "\"key_536\": \"val_536\"," \
+ "\"key_537\": \"val_537\"," \
+ "\"key_538\": \"val_538\"," \
+ "\"key_539\": \"val_539\"," \
+ "\"key_540\": \"val_540\"," \
+ "\"key_541\": \"val_541\"," \
+ "\"key_542\": \"val_542\"," \
+ "\"key_543\": \"val_543\"," \
+ "\"key_544\": \"val_544\"," \
+ "\"key_545\": \"val_545\"," \
+ "\"key_546\": \"val_546\"," \
+ "\"key_547\": \"val_547\"," \
+ "\"key_548\": \"val_548\"," \
+ "\"key_549\": \"val_549\"," \
+ "\"key_550\": \"val_550\"," \
+ "\"key_551\": \"val_551\"," \
+ "\"key_552\": \"val_552\"," \
+ "\"key_553\": \"val_553\"," \
+ "\"key_554\": \"val_554\"," \
+ "\"key_555\": \"val_555\"," \
+ "\"key_556\": \"val_556\"," \
+ "\"key_557\": \"val_557\"," \
+ "\"key_558\": \"val_558\"," \
+ "\"key_559\": \"val_559\"," \
+ "\"key_560\": \"val_560\"," \
+ "\"key_561\": \"val_561\"," \
+ "\"key_562\": \"val_562\"," \
+ "\"key_563\": \"val_563\"," \
+ "\"key_564\": \"val_564\"," \
+ "\"key_565\": \"val_565\"," \
+ "\"key_566\": \"val_566\"," \
+ "\"key_567\": \"val_567\"," \
+ "\"key_568\": \"val_568\"," \
+ "\"key_569\": \"val_569\"," \
+ "\"key_570\": \"val_570\"," \
+ "\"key_571\": \"val_571\"," \
+ "\"key_572\": \"val_572\"," \
+ "\"key_573\": \"val_573\"," \
+ "\"key_574\": \"val_574\"," \
+ "\"key_575\": \"val_575\"," \
+ "\"key_576\": \"val_576\"," \
+ "\"key_577\": \"val_577\"," \
+ "\"key_578\": \"val_578\"," \
+ "\"key_579\": \"val_579\"," \
+ "\"key_580\": \"val_580\"," \
+ "\"key_581\": \"val_581\"," \
+ "\"key_582\": \"val_582\"," \
+ "\"key_583\": \"val_583\"," \
+ "\"key_584\": \"val_584\"," \
+ "\"key_585\": \"val_585\"," \
+ "\"key_586\": \"val_586\"," \
+ "\"key_587\": \"val_587\"," \
+ "\"key_588\": \"val_588\"," \
+ "\"key_589\": \"val_589\"," \
+ "\"key_590\": \"val_590\"," \
+ "\"key_591\": \"val_591\"," \
+ "\"key_592\": \"val_592\"," \
+ "\"key_593\": \"val_593\"," \
+ "\"key_594\": \"val_594\"," \
+ "\"key_595\": \"val_595\"," \
+ "\"key_596\": \"val_596\"," \
+ "\"key_597\": \"val_597\"," \
+ "\"key_598\": \"val_598\"," \
+ "\"key_599\": \"val_599\"," \
+ "\"key_600\": \"val_600\"," \
+ "\"key_601\": \"val_601\"," \
+ "\"key_602\": \"val_602\"," \
+ "\"key_603\": \"val_603\"," \
+ "\"key_604\": \"val_604\"," \
+ "\"key_605\": \"val_605\"," \
+ "\"key_606\": \"val_606\"," \
+ "\"key_607\": \"val_607\"," \
+ "\"key_608\": \"val_608\"," \
+ "\"key_609\": \"val_609\"," \
+ "\"key_610\": \"val_610\"," \
+ "\"key_611\": \"val_611\"," \
+ "\"key_612\": \"val_612\"," \
+ "\"key_613\": \"val_613\"," \
+ "\"key_614\": \"val_614\"," \
+ "\"key_615\": \"val_615\"," \
+ "\"key_616\": \"val_616\"," \
+ "\"key_617\": \"val_617\"," \
+ "\"key_618\": \"val_618\"," \
+ "\"key_619\": \"val_619\"," \
+ "\"key_620\": \"val_620\"," \
+ "\"key_621\": \"val_621\"," \
+ "\"key_622\": \"val_622\"," \
+ "\"key_623\": \"val_623\"," \
+ "\"key_624\": \"val_624\"," \
+ "\"key_625\": \"val_625\"," \
+ "\"key_626\": \"val_626\"," \
+ "\"key_627\": \"val_627\"," \
+ "\"key_628\": \"val_628\"," \
+ "\"key_629\": \"val_629\"," \
+ "\"key_630\": \"val_630\"," \
+ "\"key_631\": \"val_631\"," \
+ "\"key_632\": \"val_632\"," \
+ "\"key_633\": \"val_633\"," \
+ "\"key_634\": \"val_634\"," \
+ "\"key_635\": \"val_635\"," \
+ "\"key_636\": \"val_636\"," \
+ "\"key_637\": \"val_637\"," \
+ "\"key_638\": \"val_638\"," \
+ "\"key_639\": \"val_639\"," \
+ "\"key_640\": \"val_640\"," \
+ "\"key_641\": \"val_641\"," \
+ "\"key_642\": \"val_642\"," \
+ "\"key_643\": \"val_643\"," \
+ "\"key_644\": \"val_644\"," \
+ "\"key_645\": \"val_645\"," \
+ "\"key_646\": \"val_646\"," \
+ "\"key_647\": \"val_647\"," \
+ "\"key_648\": \"val_648\"," \
+ "\"key_649\": \"val_649\"," \
+ "\"key_650\": \"val_650\"," \
+ "\"key_651\": \"val_651\"," \
+ "\"key_652\": \"val_652\"," \
+ "\"key_653\": \"val_653\"," \
+ "\"key_654\": \"val_654\"," \
+ "\"key_655\": \"val_655\"," \
+ "\"key_656\": \"val_656\"," \
+ "\"key_657\": \"val_657\"," \
+ "\"key_658\": \"val_658\"," \
+ "\"key_659\": \"val_659\"," \
+ "\"key_660\": \"val_660\"," \
+ "\"key_661\": \"val_661\"," \
+ "\"key_662\": \"val_662\"," \
+ "\"key_663\": \"val_663\"," \
+ "\"key_664\": \"val_664\"," \
+ "\"key_665\": \"val_665\"," \
+ "\"key_666\": \"val_666\"," \
+ "\"key_667\": \"val_667\"," \
+ "\"key_668\": \"val_668\"," \
+ "\"key_669\": \"val_669\"," \
+ "\"key_670\": \"val_670\"," \
+ "\"key_671\": \"val_671\"," \
+ "\"key_672\": \"val_672\"," \
+ "\"key_673\": \"val_673\"," \
+ "\"key_674\": \"val_674\"," \
+ "\"key_675\": \"val_675\"," \
+ "\"key_676\": \"val_676\"," \
+ "\"key_677\": \"val_677\"," \
+ "\"key_678\": \"val_678\"," \
+ "\"key_679\": \"val_679\"," \
+ "\"key_680\": \"val_680\"," \
+ "\"key_681\": \"val_681\"," \
+ "\"key_682\": \"val_682\"," \
+ "\"key_683\": \"val_683\"," \
+ "\"key_684\": \"val_684\"," \
+ "\"key_685\": \"val_685\"," \
+ "\"key_686\": \"val_686\"," \
+ "\"key_687\": \"val_687\"," \
+ "\"key_688\": \"val_688\"," \
+ "\"key_689\": \"val_689\"," \
+ "\"key_690\": \"val_690\"," \
+ "\"key_691\": \"val_691\"," \
+ "\"key_692\": \"val_692\"," \
+ "\"key_693\": \"val_693\"," \
+ "\"key_694\": \"val_694\"," \
+ "\"key_695\": \"val_695\"," \
+ "\"key_696\": \"val_696\"," \
+ "\"key_697\": \"val_697\"," \
+ "\"key_698\": \"val_698\"," \
+ "\"key_699\": \"val_699\"," \
+ "\"key_700\": \"val_700\"," \
+ "\"key_701\": \"val_701\"," \
+ "\"key_702\": \"val_702\"," \
+ "\"key_703\": \"val_703\"," \
+ "\"key_704\": \"val_704\"," \
+ "\"key_705\": \"val_705\"," \
+ "\"key_706\": \"val_706\"," \
+ "\"key_707\": \"val_707\"," \
+ "\"key_708\": \"val_708\"," \
+ "\"key_709\": \"val_709\"," \
+ "\"key_710\": \"val_710\"," \
+ "\"key_711\": \"val_711\"," \
+ "\"key_712\": \"val_712\"," \
+ "\"key_713\": \"val_713\"," \
+ "\"key_714\": \"val_714\"," \
+ "\"key_715\": \"val_715\"," \
+ "\"key_716\": \"val_716\"," \
+ "\"key_717\": \"val_717\"," \
+ "\"key_718\": \"val_718\"," \
+ "\"key_719\": \"val_719\"," \
+ "\"key_720\": \"val_720\"," \
+ "\"key_721\": \"val_721\"," \
+ "\"key_722\": \"val_722\"," \
+ "\"key_723\": \"val_723\"," \
+ "\"key_724\": \"val_724\"," \
+ "\"key_725\": \"val_725\"," \
+ "\"key_726\": \"val_726\"," \
+ "\"key_727\": \"val_727\"," \
+ "\"key_728\": \"val_728\"," \
+ "\"key_729\": \"val_729\"," \
+ "\"key_730\": \"val_730\"," \
+ "\"key_731\": \"val_731\"," \
+ "\"key_732\": \"val_732\"," \
+ "\"key_733\": \"val_733\"," \
+ "\"key_734\": \"val_734\"," \
+ "\"key_735\": \"val_735\"," \
+ "\"key_736\": \"val_736\"," \
+ "\"key_737\": \"val_737\"," \
+ "\"key_738\": \"val_738\"," \
+ "\"key_739\": \"val_739\"," \
+ "\"key_740\": \"val_740\"," \
+ "\"key_741\": \"val_741\"," \
+ "\"key_742\": \"val_742\"," \
+ "\"key_743\": \"val_743\"," \
+ "\"key_744\": \"val_744\"," \
+ "\"key_745\": \"val_745\"," \
+ "\"key_746\": \"val_746\"," \
+ "\"key_747\": \"val_747\"," \
+ "\"key_748\": \"val_748\"," \
+ "\"key_749\": \"val_749\"," \
+ "\"key_750\": \"val_750\"," \
+ "\"key_751\": \"val_751\"," \
+ "\"key_752\": \"val_752\"," \
+ "\"key_753\": \"val_753\"," \
+ "\"key_754\": \"val_754\"," \
+ "\"key_755\": \"val_755\"," \
+ "\"key_756\": \"val_756\"," \
+ "\"key_757\": \"val_757\"," \
+ "\"key_758\": \"val_758\"," \
+ "\"key_759\": \"val_759\"," \
+ "\"key_760\": \"val_760\"," \
+ "\"key_761\": \"val_761\"," \
+ "\"key_762\": \"val_762\"," \
+ "\"key_763\": \"val_763\"," \
+ "\"key_764\": \"val_764\"," \
+ "\"key_765\": \"val_765\"," \
+ "\"key_766\": \"val_766\"," \
+ "\"key_767\": \"val_767\"," \
+ "\"key_768\": \"val_768\"," \
+ "\"key_769\": \"val_769\"," \
+ "\"key_770\": \"val_770\"," \
+ "\"key_771\": \"val_771\"," \
+ "\"key_772\": \"val_772\"," \
+ "\"key_773\": \"val_773\"," \
+ "\"key_774\": \"val_774\"," \
+ "\"key_775\": \"val_775\"," \
+ "\"key_776\": \"val_776\"," \
+ "\"key_777\": \"val_777\"," \
+ "\"key_778\": \"val_778\"," \
+ "\"key_779\": \"val_779\"," \
+ "\"key_780\": \"val_780\"," \
+ "\"key_781\": \"val_781\"," \
+ "\"key_782\": \"val_782\"," \
+ "\"key_783\": \"val_783\"," \
+ "\"key_784\": \"val_784\"," \
+ "\"key_785\": \"val_785\"," \
+ "\"key_786\": \"val_786\"," \
+ "\"key_787\": \"val_787\"," \
+ "\"key_788\": \"val_788\"," \
+ "\"key_789\": \"val_789\"," \
+ "\"key_790\": \"val_790\"," \
+ "\"key_791\": \"val_791\"," \
+ "\"key_792\": \"val_792\"," \
+ "\"key_793\": \"val_793\"," \
+ "\"key_794\": \"val_794\"," \
+ "\"key_795\": \"val_795\"," \
+ "\"key_796\": \"val_796\"," \
+ "\"key_797\": \"val_797\"," \
+ "\"key_798\": \"val_798\"," \
+ "\"key_799\": \"val_799\"," \
+ "\"key_800\": \"val_800\"," \
+ "\"key_801\": \"val_801\"," \
+ "\"key_802\": \"val_802\"," \
+ "\"key_803\": \"val_803\"," \
+ "\"key_804\": \"val_804\"," \
+ "\"key_805\": \"val_805\"," \
+ "\"key_806\": \"val_806\"," \
+ "\"key_807\": \"val_807\"," \
+ "\"key_808\": \"val_808\"," \
+ "\"key_809\": \"val_809\"," \
+ "\"key_810\": \"val_810\"," \
+ "\"key_811\": \"val_811\"," \
+ "\"key_812\": \"val_812\"," \
+ "\"key_813\": \"val_813\"," \
+ "\"key_814\": \"val_814\"," \
+ "\"key_815\": \"val_815\"," \
+ "\"key_816\": \"val_816\"," \
+ "\"key_817\": \"val_817\"," \
+ "\"key_818\": \"val_818\"," \
+ "\"key_819\": \"val_819\"," \
+ "\"key_820\": \"val_820\"," \
+ "\"key_821\": \"val_821\"," \
+ "\"key_822\": \"val_822\"," \
+ "\"key_823\": \"val_823\"," \
+ "\"key_824\": \"val_824\"," \
+ "\"key_825\": \"val_825\"," \
+ "\"key_826\": \"val_826\"," \
+ "\"key_827\": \"val_827\"," \
+ "\"key_828\": \"val_828\"," \
+ "\"key_829\": \"val_829\"," \
+ "\"key_830\": \"val_830\"," \
+ "\"key_831\": \"val_831\"," \
+ "\"key_832\": \"val_832\"," \
+ "\"key_833\": \"val_833\"," \
+ "\"key_834\": \"val_834\"," \
+ "\"key_835\": \"val_835\"," \
+ "\"key_836\": \"val_836\"," \
+ "\"key_837\": \"val_837\"," \
+ "\"key_838\": \"val_838\"," \
+ "\"key_839\": \"val_839\"," \
+ "\"key_840\": \"val_840\"," \
+ "\"key_841\": \"val_841\"," \
+ "\"key_842\": \"val_842\"," \
+ "\"key_843\": \"val_843\"," \
+ "\"key_844\": \"val_844\"," \
+ "\"key_845\": \"val_845\"," \
+ "\"key_846\": \"val_846\"," \
+ "\"key_847\": \"val_847\"," \
+ "\"key_848\": \"val_848\"," \
+ "\"key_849\": \"val_849\"," \
+ "\"key_850\": \"val_850\"," \
+ "\"key_851\": \"val_851\"," \
+ "\"key_852\": \"val_852\"," \
+ "\"key_853\": \"val_853\"," \
+ "\"key_854\": \"val_854\"," \
+ "\"key_855\": \"val_855\"," \
+ "\"key_856\": \"val_856\"," \
+ "\"key_857\": \"val_857\"," \
+ "\"key_858\": \"val_858\"," \
+ "\"key_859\": \"val_859\"," \
+ "\"key_860\": \"val_860\"," \
+ "\"key_861\": \"val_861\"," \
+ "\"key_862\": \"val_862\"," \
+ "\"key_863\": \"val_863\"," \
+ "\"key_864\": \"val_864\"," \
+ "\"key_865\": \"val_865\"," \
+ "\"key_866\": \"val_866\"," \
+ "\"key_867\": \"val_867\"," \
+ "\"key_868\": \"val_868\"," \
+ "\"key_869\": \"val_869\"," \
+ "\"key_870\": \"val_870\"," \
+ "\"key_871\": \"val_871\"," \
+ "\"key_872\": \"val_872\"," \
+ "\"key_873\": \"val_873\"," \
+ "\"key_874\": \"val_874\"," \
+ "\"key_875\": \"val_875\"," \
+ "\"key_876\": \"val_876\"," \
+ "\"key_877\": \"val_877\"," \
+ "\"key_878\": \"val_878\"," \
+ "\"key_879\": \"val_879\"," \
+ "\"key_880\": \"val_880\"," \
+ "\"key_881\": \"val_881\"," \
+ "\"key_882\": \"val_882\"," \
+ "\"key_883\": \"val_883\"," \
+ "\"key_884\": \"val_884\"," \
+ "\"key_885\": \"val_885\"," \
+ "\"key_886\": \"val_886\"," \
+ "\"key_887\": \"val_887\"," \
+ "\"key_888\": \"val_888\"," \
+ "\"key_889\": \"val_889\"," \
+ "\"key_890\": \"val_890\"," \
+ "\"key_891\": \"val_891\"," \
+ "\"key_892\": \"val_892\"," \
+ "\"key_893\": \"val_893\"," \
+ "\"key_894\": \"val_894\"," \
+ "\"key_895\": \"val_895\"," \
+ "\"key_896\": \"val_896\"," \
+ "\"key_897\": \"val_897\"," \
+ "\"key_898\": \"val_898\"," \
+ "\"key_899\": \"val_899\"," \
+ "\"key_900\": \"val_900\"," \
+ "\"key_901\": \"val_901\"," \
+ "\"key_902\": \"val_902\"," \
+ "\"key_903\": \"val_903\"," \
+ "\"key_904\": \"val_904\"," \
+ "\"key_905\": \"val_905\"," \
+ "\"key_906\": \"val_906\"," \
+ "\"key_907\": \"val_907\"," \
+ "\"key_908\": \"val_908\"," \
+ "\"key_909\": \"val_909\"," \
+ "\"key_910\": \"val_910\"," \
+ "\"key_911\": \"val_911\"," \
+ "\"key_912\": \"val_912\"," \
+ "\"key_913\": \"val_913\"," \
+ "\"key_914\": \"val_914\"," \
+ "\"key_915\": \"val_915\"," \
+ "\"key_916\": \"val_916\"," \
+ "\"key_917\": \"val_917\"," \
+ "\"key_918\": \"val_918\"," \
+ "\"key_919\": \"val_919\"," \
+ "\"key_920\": \"val_920\"," \
+ "\"key_921\": \"val_921\"," \
+ "\"key_922\": \"val_922\"," \
+ "\"key_923\": \"val_923\"," \
+ "\"key_924\": \"val_924\"," \
+ "\"key_925\": \"val_925\"," \
+ "\"key_926\": \"val_926\"," \
+ "\"key_927\": \"val_927\"," \
+ "\"key_928\": \"val_928\"," \
+ "\"key_929\": \"val_929\"," \
+ "\"key_930\": \"val_930\"," \
+ "\"key_931\": \"val_931\"," \
+ "\"key_932\": \"val_932\"," \
+ "\"key_933\": \"val_933\"," \
+ "\"key_934\": \"val_934\"," \
+ "\"key_935\": \"val_935\"," \
+ "\"key_936\": \"val_936\"," \
+ "\"key_937\": \"val_937\"," \
+ "\"key_938\": \"val_938\"," \
+ "\"key_939\": \"val_939\"," \
+ "\"key_940\": \"val_940\"," \
+ "\"key_941\": \"val_941\"," \
+ "\"key_942\": \"val_942\"," \
+ "\"key_943\": \"val_943\"," \
+ "\"key_944\": \"val_944\"," \
+ "\"key_945\": \"val_945\"," \
+ "\"key_946\": \"val_946\"," \
+ "\"key_947\": \"val_947\"," \
+ "\"key_948\": \"val_948\"," \
+ "\"key_949\": \"val_949\"," \
+ "\"key_950\": \"val_950\"," \
+ "\"key_951\": \"val_951\"," \
+ "\"key_952\": \"val_952\"," \
+ "\"key_953\": \"val_953\"," \
+ "\"key_954\": \"val_954\"," \
+ "\"key_955\": \"val_955\"," \
+ "\"key_956\": \"val_956\"," \
+ "\"key_957\": \"val_957\"," \
+ "\"key_958\": \"val_958\"," \
+ "\"key_959\": \"val_959\"," \
+ "\"key_960\": \"val_960\"," \
+ "\"key_961\": \"val_961\"," \
+ "\"key_962\": \"val_962\"," \
+ "\"key_963\": \"val_963\"," \
+ "\"key_964\": \"val_964\"," \
+ "\"key_965\": \"val_965\"," \
+ "\"key_966\": \"val_966\"," \
+ "\"key_967\": \"val_967\"," \
+ "\"key_968\": \"val_968\"," \
+ "\"key_969\": \"val_969\"," \
+ "\"key_970\": \"val_970\"," \
+ "\"key_971\": \"val_971\"," \
+ "\"key_972\": \"val_972\"," \
+ "\"key_973\": \"val_973\"," \
+ "\"key_974\": \"val_974\"," \
+ "\"key_975\": \"val_975\"," \
+ "\"key_976\": \"val_976\"," \
+ "\"key_977\": \"val_977\"," \
+ "\"key_978\": \"val_978\"," \
+ "\"key_979\": \"val_979\"," \
+ "\"key_980\": \"val_980\"," \
+ "\"key_981\": \"val_981\"," \
+ "\"key_982\": \"val_982\"," \
+ "\"key_983\": \"val_983\"," \
+ "\"key_984\": \"val_984\"," \
+ "\"key_985\": \"val_985\"," \
+ "\"key_986\": \"val_986\"," \
+ "\"key_987\": \"val_987\"," \
+ "\"key_988\": \"val_988\"," \
+ "\"key_989\": \"val_989\"," \
+ "\"key_990\": \"val_990\"," \
+ "\"key_991\": \"val_991\"," \
+ "\"key_992\": \"val_992\"," \
+ "\"key_993\": \"val_993\"," \
+ "\"key_994\": \"val_994\"," \
+ "\"key_995\": \"val_995\"," \
+ "\"key_996\": \"val_996\"," \
+ "\"key_997\": \"val_997\"," \
+ "\"key_998\": \"val_998\"," \
+ "\"key_999\": \"val_999\"," \
+ "\"END_KEY\": \"JSON_END\"" \
+ "}]"
+
diff --git a/fluent-bit/tests/runtime/data/common/json_small.h b/fluent-bit/tests/runtime/data/common/json_small.h
new file mode 100755
index 00000000..b4af842f
--- /dev/null
+++ b/fluent-bit/tests/runtime/data/common/json_small.h
@@ -0,0 +1,256 @@
+#define JSON_SMALL "[" \
+ "1448403340," \
+ "{" \
+ "\"key_0\": false," \
+ "\"key_1\": false," \
+ "\"key_2\": false," \
+ "\"key_3\": false," \
+ "\"key_4\": false," \
+ "\"key_5\": false," \
+ "\"key_6\": false," \
+ "\"key_7\": false," \
+ "\"key_8\": false," \
+ "\"key_9\": false," \
+ "\"key_10\": false," \
+ "\"key_11\": false," \
+ "\"key_12\": false," \
+ "\"key_13\": false," \
+ "\"key_14\": false," \
+ "\"key_15\": false," \
+ "\"key_16\": false," \
+ "\"key_17\": false," \
+ "\"key_18\": false," \
+ "\"key_19\": false," \
+ "\"key_20\": false," \
+ "\"key_21\": false," \
+ "\"key_22\": false," \
+ "\"key_23\": false," \
+ "\"key_24\": false," \
+ "\"key_25\": false," \
+ "\"key_26\": false," \
+ "\"key_27\": false," \
+ "\"key_28\": false," \
+ "\"key_29\": false," \
+ "\"key_30\": false," \
+ "\"key_31\": false," \
+ "\"key_32\": false," \
+ "\"key_33\": false," \
+ "\"key_34\": false," \
+ "\"key_35\": false," \
+ "\"key_36\": false," \
+ "\"key_37\": false," \
+ "\"key_38\": false," \
+ "\"key_39\": false," \
+ "\"key_40\": false," \
+ "\"key_41\": false," \
+ "\"key_42\": false," \
+ "\"key_43\": false," \
+ "\"key_44\": false," \
+ "\"key_45\": false," \
+ "\"key_46\": false," \
+ "\"key_47\": false," \
+ "\"key_48\": false," \
+ "\"key_49\": false," \
+ "\"key_50\": false," \
+ "\"key_51\": false," \
+ "\"key_52\": false," \
+ "\"key_53\": false," \
+ "\"key_54\": false," \
+ "\"key_55\": false," \
+ "\"key_56\": false," \
+ "\"key_57\": false," \
+ "\"key_58\": false," \
+ "\"key_59\": false," \
+ "\"key_60\": false," \
+ "\"key_61\": false," \
+ "\"key_62\": false," \
+ "\"key_63\": false," \
+ "\"key_64\": false," \
+ "\"key_65\": false," \
+ "\"key_66\": false," \
+ "\"key_67\": false," \
+ "\"key_68\": false," \
+ "\"key_69\": false," \
+ "\"key_70\": false," \
+ "\"key_71\": false," \
+ "\"key_72\": false," \
+ "\"key_73\": false," \
+ "\"key_74\": false," \
+ "\"key_75\": false," \
+ "\"key_76\": false," \
+ "\"key_77\": false," \
+ "\"key_78\": false," \
+ "\"key_79\": false," \
+ "\"key_80\": false," \
+ "\"key_81\": false," \
+ "\"key_82\": false," \
+ "\"key_83\": false," \
+ "\"key_84\": false," \
+ "\"key_85\": false," \
+ "\"key_86\": false," \
+ "\"key_87\": false," \
+ "\"key_88\": false," \
+ "\"key_89\": false," \
+ "\"key_90\": false," \
+ "\"key_91\": false," \
+ "\"key_92\": false," \
+ "\"key_93\": false," \
+ "\"key_94\": false," \
+ "\"key_95\": false," \
+ "\"key_96\": false," \
+ "\"key_97\": false," \
+ "\"key_98\": false," \
+ "\"key_99\": false," \
+ "\"key_100\": false," \
+ "\"key_101\": false," \
+ "\"key_102\": false," \
+ "\"key_103\": false," \
+ "\"key_104\": false," \
+ "\"key_105\": false," \
+ "\"key_106\": false," \
+ "\"key_107\": false," \
+ "\"key_108\": false," \
+ "\"key_109\": false," \
+ "\"key_110\": false," \
+ "\"key_111\": false," \
+ "\"key_112\": false," \
+ "\"key_113\": false," \
+ "\"key_114\": false," \
+ "\"key_115\": false," \
+ "\"key_116\": false," \
+ "\"key_117\": false," \
+ "\"key_118\": false," \
+ "\"key_119\": false," \
+ "\"key_120\": false," \
+ "\"key_121\": false," \
+ "\"key_122\": false," \
+ "\"key_123\": false," \
+ "\"key_124\": false," \
+ "\"key_125\": false," \
+ "\"key_126\": false," \
+ "\"key_127\": false," \
+ "\"key_128\": false," \
+ "\"key_129\": false," \
+ "\"key_130\": false," \
+ "\"key_131\": false," \
+ "\"key_132\": false," \
+ "\"key_133\": false," \
+ "\"key_134\": false," \
+ "\"key_135\": false," \
+ "\"key_136\": false," \
+ "\"key_137\": false," \
+ "\"key_138\": false," \
+ "\"key_139\": false," \
+ "\"key_140\": false," \
+ "\"key_141\": false," \
+ "\"key_142\": false," \
+ "\"key_143\": false," \
+ "\"key_144\": false," \
+ "\"key_145\": false," \
+ "\"key_146\": false," \
+ "\"key_147\": false," \
+ "\"key_148\": false," \
+ "\"key_149\": false," \
+ "\"key_150\": false," \
+ "\"key_151\": false," \
+ "\"key_152\": false," \
+ "\"key_153\": false," \
+ "\"key_154\": false," \
+ "\"key_155\": false," \
+ "\"key_156\": false," \
+ "\"key_157\": false," \
+ "\"key_158\": false," \
+ "\"key_159\": false," \
+ "\"key_160\": false," \
+ "\"key_161\": false," \
+ "\"key_162\": false," \
+ "\"key_163\": false," \
+ "\"key_164\": false," \
+ "\"key_165\": false," \
+ "\"key_166\": false," \
+ "\"key_167\": false," \
+ "\"key_168\": false," \
+ "\"key_169\": false," \
+ "\"key_170\": false," \
+ "\"key_171\": false," \
+ "\"key_172\": false," \
+ "\"key_173\": false," \
+ "\"key_174\": false," \
+ "\"key_175\": false," \
+ "\"key_176\": false," \
+ "\"key_177\": false," \
+ "\"key_178\": false," \
+ "\"key_179\": false," \
+ "\"key_180\": false," \
+ "\"key_181\": false," \
+ "\"key_182\": false," \
+ "\"key_183\": false," \
+ "\"key_184\": false," \
+ "\"key_185\": false," \
+ "\"key_186\": false," \
+ "\"key_187\": false," \
+ "\"key_188\": false," \
+ "\"key_189\": false," \
+ "\"key_190\": false," \
+ "\"key_191\": false," \
+ "\"key_192\": false," \
+ "\"key_193\": false," \
+ "\"key_194\": false," \
+ "\"key_195\": false," \
+ "\"key_196\": false," \
+ "\"key_197\": false," \
+ "\"key_198\": false," \
+ "\"key_199\": false," \
+ "\"key_200\": false," \
+ "\"key_201\": false," \
+ "\"key_202\": false," \
+ "\"key_203\": false," \
+ "\"key_204\": false," \
+ "\"key_205\": false," \
+ "\"key_206\": false," \
+ "\"key_207\": false," \
+ "\"key_208\": false," \
+ "\"key_209\": false," \
+ "\"key_210\": false," \
+ "\"key_211\": false," \
+ "\"key_212\": false," \
+ "\"key_213\": false," \
+ "\"key_214\": false," \
+ "\"key_215\": false," \
+ "\"key_216\": false," \
+ "\"key_217\": false," \
+ "\"key_218\": false," \
+ "\"key_219\": false," \
+ "\"key_220\": false," \
+ "\"key_221\": false," \
+ "\"key_222\": false," \
+ "\"key_223\": false," \
+ "\"key_224\": false," \
+ "\"key_225\": false," \
+ "\"key_226\": false," \
+ "\"key_227\": false," \
+ "\"key_228\": false," \
+ "\"key_229\": false," \
+ "\"key_230\": false," \
+ "\"key_231\": false," \
+ "\"key_232\": false," \
+ "\"key_233\": false," \
+ "\"key_234\": false," \
+ "\"key_235\": false," \
+ "\"key_236\": false," \
+ "\"key_237\": false," \
+ "\"key_238\": false," \
+ "\"key_239\": false," \
+ "\"key_240\": false," \
+ "\"key_241\": false," \
+ "\"key_242\": false," \
+ "\"key_243\": false," \
+ "\"key_244\": false," \
+ "\"key_245\": false," \
+ "\"key_246\": false," \
+ "\"key_247\": false," \
+ "\"key_248\": false," \
+ "\"key_249\": false," \
+ "\"END_KEY\": \"JSON_END\"" \
+ "}]"
+
diff --git a/fluent-bit/tests/runtime/data/common/parsers.conf b/fluent-bit/tests/runtime/data/common/parsers.conf
new file mode 120000
index 00000000..944111e3
--- /dev/null
+++ b/fluent-bit/tests/runtime/data/common/parsers.conf
@@ -0,0 +1 @@
+../../../../conf/parsers.conf \ No newline at end of file
diff --git a/fluent-bit/tests/runtime/data/datadog/json.h b/fluent-bit/tests/runtime/data/datadog/json.h
new file mode 100644
index 00000000..146dd532
--- /dev/null
+++ b/fluent-bit/tests/runtime/data/datadog/json.h
@@ -0,0 +1,12 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+#define JSON "[" \
+ "1448403340," \
+ "{" \
+ "\"key_0\": false," \
+ "\"key_1\": true," \
+ "\"key_2\": \"some string\"," \
+ "\"key_3\": 0.12345678," \
+ "\"key.4\": 5000," \
+ "\"END_KEY\": \"JSON_END\"" \
+ "}]"
diff --git a/fluent-bit/tests/runtime/data/es/json_es.h b/fluent-bit/tests/runtime/data/es/json_es.h
new file mode 100755
index 00000000..40f8ab1c
--- /dev/null
+++ b/fluent-bit/tests/runtime/data/es/json_es.h
@@ -0,0 +1,17 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+#define JSON_ES "[" \
+ "1448403340," \
+ "{" \
+ "\"key_0\": false," \
+ "\"key_1\": true," \
+ "\"key_2\": \"some string\"," \
+ "\"key_3\": 0.12345678," \
+ "\"key.4\": 5000," \
+ "\"END_KEY\": \"JSON_END\"" \
+ "}]"
+
+
+#define JSON_DOTS \
+ "[1448403340," \
+ "{\".le.vel\":\"error\", \".fo.o\":[{\".o.k\": [{\".b.ar\": \"baz\"}]}]}]"
diff --git a/fluent-bit/tests/runtime/data/in_elasticsearch/json_bulk.h b/fluent-bit/tests/runtime/data/in_elasticsearch/json_bulk.h
new file mode 100644
index 00000000..ca8f751c
--- /dev/null
+++ b/fluent-bit/tests/runtime/data/in_elasticsearch/json_bulk.h
@@ -0,0 +1,25 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+#define NDJSON_BULK "{ \"index\" : { \"_index\" : \"test\", \"_id\" : \"1\" } }\n" \
+ "{ \"field1\" : \"value1\" }\n" \
+ "{ \"delete\" : { \"_index\" : \"test\", \"_id\" : \"2\" } }\n" \
+ "{ \"create\" : { \"_index\" : \"test\", \"_id\" : \"3\" } }\n" \
+ "{ \"field1\" : \"value3\", \"field2\" : \"value4\" }\n" \
+ "{ \"update\" : {\"_id\" : \"1\", \"_index\" : \"test\"} }\n" \
+ "{ \"doc\" : {\"field2\" : \"value2\"} }\n" \
+ "{ \"index\" : { \"_index\" : \"test\", \"_id\" : \"10\" } }\n" \
+ "{ \"field1\" : \"value1\", \"a\": \"line\", \"that\" : \"is\", \"long\": \"line\", \"contained\": \"request\" }\n" \
+ "{ \"delete\" : { \"_index\" : \"test\", \"_id\" : \"20\" } }\n" \
+ "{ \"create\" : { \"_index\" : \"test\", \"_id\" : \"30\" } }\n" \
+ "{ \"field10\" : \"value30\", \"field20\" : \"value40\", \"message\": \"ok\" }\n" \
+ "{ \"update\" : {\"_id\" : \"10\", \"_index\" : \"test\"} }\n" \
+ "{ \"doc\" : {\"field20\" : \"value20\"} }\n" \
+ "{ \"index\" : { \"_index\" : \"test\", \"_id\" : \"11\" } }\n" \
+ "{ \"field11\" : \"value11\", \"nested\": {\"message\":\"ok\"} }\n" \
+ "{ \"delete\" : { \"_index\" : \"test\", \"_id\" : \"21\" } }\n" \
+ "{ \"create\" : { \"_index\" : \"test\", \"_id\" : \"31\" } }\n" \
+ "{ \"field11\" : \"value31\", \"field21\" : \"value41\", \"nested\": { \"multiply\": {\"message\": \"ok\"}} }\n" \
+ "{ \"update\" : {\"_id\" : \"11\", \"_index\" : \"test\"} }\n" \
+ "{ \"doc\" : {\"field21\" : \"value21\"} }\n" \
+ "{ \"index\" : { \"_index\" : \"test\", \"_id\" : \"41\" } }\n" \
+ "{ \"field41\" : \"value41\", \"nested\": {\"message\": \"ok\"} }\n"
diff --git a/fluent-bit/tests/runtime/data/kubernetes/log/annotations-exclude/annotations-exclude_default_text.log b/fluent-bit/tests/runtime/data/kubernetes/log/annotations-exclude/annotations-exclude_default_text.log
new file mode 100644
index 00000000..d5cd3af5
--- /dev/null
+++ b/fluent-bit/tests/runtime/data/kubernetes/log/annotations-exclude/annotations-exclude_default_text.log
@@ -0,0 +1,2 @@
+{"log":"Simple text\n","stream":"stdout","time":"2019-04-01T17:58:33.598656444Z"}
+{"log":"Simple text\n","stream":"stderr","time":"2019-04-01T17:58:33.698656444Z"}
diff --git a/fluent-bit/tests/runtime/data/kubernetes/log/annotations-exclude/annotations-exclude_invalid_text.log b/fluent-bit/tests/runtime/data/kubernetes/log/annotations-exclude/annotations-exclude_invalid_text.log
new file mode 100644
index 00000000..d5cd3af5
--- /dev/null
+++ b/fluent-bit/tests/runtime/data/kubernetes/log/annotations-exclude/annotations-exclude_invalid_text.log
@@ -0,0 +1,2 @@
+{"log":"Simple text\n","stream":"stdout","time":"2019-04-01T17:58:33.598656444Z"}
+{"log":"Simple text\n","stream":"stderr","time":"2019-04-01T17:58:33.698656444Z"}
diff --git a/fluent-bit/tests/runtime/data/kubernetes/log/annotations-exclude/annotations-exclude_multiple-1_container-1.log b/fluent-bit/tests/runtime/data/kubernetes/log/annotations-exclude/annotations-exclude_multiple-1_container-1.log
new file mode 100644
index 00000000..d5cd3af5
--- /dev/null
+++ b/fluent-bit/tests/runtime/data/kubernetes/log/annotations-exclude/annotations-exclude_multiple-1_container-1.log
@@ -0,0 +1,2 @@
+{"log":"Simple text\n","stream":"stdout","time":"2019-04-01T17:58:33.598656444Z"}
+{"log":"Simple text\n","stream":"stderr","time":"2019-04-01T17:58:33.698656444Z"}
diff --git a/fluent-bit/tests/runtime/data/kubernetes/log/annotations-exclude/annotations-exclude_multiple-1_container-2.log b/fluent-bit/tests/runtime/data/kubernetes/log/annotations-exclude/annotations-exclude_multiple-1_container-2.log
new file mode 100644
index 00000000..d5cd3af5
--- /dev/null
+++ b/fluent-bit/tests/runtime/data/kubernetes/log/annotations-exclude/annotations-exclude_multiple-1_container-2.log
@@ -0,0 +1,2 @@
+{"log":"Simple text\n","stream":"stdout","time":"2019-04-01T17:58:33.598656444Z"}
+{"log":"Simple text\n","stream":"stderr","time":"2019-04-01T17:58:33.698656444Z"}
diff --git a/fluent-bit/tests/runtime/data/kubernetes/log/annotations-exclude/annotations-exclude_multiple-1_container-3.log b/fluent-bit/tests/runtime/data/kubernetes/log/annotations-exclude/annotations-exclude_multiple-1_container-3.log
new file mode 100644
index 00000000..d5cd3af5
--- /dev/null
+++ b/fluent-bit/tests/runtime/data/kubernetes/log/annotations-exclude/annotations-exclude_multiple-1_container-3.log
@@ -0,0 +1,2 @@
+{"log":"Simple text\n","stream":"stdout","time":"2019-04-01T17:58:33.598656444Z"}
+{"log":"Simple text\n","stream":"stderr","time":"2019-04-01T17:58:33.698656444Z"}
diff --git a/fluent-bit/tests/runtime/data/kubernetes/log/annotations-exclude/annotations-exclude_multiple-1_container-4.log b/fluent-bit/tests/runtime/data/kubernetes/log/annotations-exclude/annotations-exclude_multiple-1_container-4.log
new file mode 100644
index 00000000..d5cd3af5
--- /dev/null
+++ b/fluent-bit/tests/runtime/data/kubernetes/log/annotations-exclude/annotations-exclude_multiple-1_container-4.log
@@ -0,0 +1,2 @@
+{"log":"Simple text\n","stream":"stdout","time":"2019-04-01T17:58:33.598656444Z"}
+{"log":"Simple text\n","stream":"stderr","time":"2019-04-01T17:58:33.698656444Z"}
diff --git a/fluent-bit/tests/runtime/data/kubernetes/log/annotations-exclude/annotations-exclude_multiple-2_container-1.log b/fluent-bit/tests/runtime/data/kubernetes/log/annotations-exclude/annotations-exclude_multiple-2_container-1.log
new file mode 100644
index 00000000..d5cd3af5
--- /dev/null
+++ b/fluent-bit/tests/runtime/data/kubernetes/log/annotations-exclude/annotations-exclude_multiple-2_container-1.log
@@ -0,0 +1,2 @@
+{"log":"Simple text\n","stream":"stdout","time":"2019-04-01T17:58:33.598656444Z"}
+{"log":"Simple text\n","stream":"stderr","time":"2019-04-01T17:58:33.698656444Z"}
diff --git a/fluent-bit/tests/runtime/data/kubernetes/log/annotations-exclude/annotations-exclude_multiple-2_container-2.log b/fluent-bit/tests/runtime/data/kubernetes/log/annotations-exclude/annotations-exclude_multiple-2_container-2.log
new file mode 100644
index 00000000..d5cd3af5
--- /dev/null
+++ b/fluent-bit/tests/runtime/data/kubernetes/log/annotations-exclude/annotations-exclude_multiple-2_container-2.log
@@ -0,0 +1,2 @@
+{"log":"Simple text\n","stream":"stdout","time":"2019-04-01T17:58:33.598656444Z"}
+{"log":"Simple text\n","stream":"stderr","time":"2019-04-01T17:58:33.698656444Z"}
diff --git a/fluent-bit/tests/runtime/data/kubernetes/log/annotations-exclude/annotations-exclude_multiple-2_container-3.log b/fluent-bit/tests/runtime/data/kubernetes/log/annotations-exclude/annotations-exclude_multiple-2_container-3.log
new file mode 100644
index 00000000..d5cd3af5
--- /dev/null
+++ b/fluent-bit/tests/runtime/data/kubernetes/log/annotations-exclude/annotations-exclude_multiple-2_container-3.log
@@ -0,0 +1,2 @@
+{"log":"Simple text\n","stream":"stdout","time":"2019-04-01T17:58:33.598656444Z"}
+{"log":"Simple text\n","stream":"stderr","time":"2019-04-01T17:58:33.698656444Z"}
diff --git a/fluent-bit/tests/runtime/data/kubernetes/log/annotations-exclude/annotations-exclude_multiple-2_container-4.log b/fluent-bit/tests/runtime/data/kubernetes/log/annotations-exclude/annotations-exclude_multiple-2_container-4.log
new file mode 100644
index 00000000..d5cd3af5
--- /dev/null
+++ b/fluent-bit/tests/runtime/data/kubernetes/log/annotations-exclude/annotations-exclude_multiple-2_container-4.log
@@ -0,0 +1,2 @@
+{"log":"Simple text\n","stream":"stdout","time":"2019-04-01T17:58:33.598656444Z"}
+{"log":"Simple text\n","stream":"stderr","time":"2019-04-01T17:58:33.698656444Z"}
diff --git a/fluent-bit/tests/runtime/data/kubernetes/log/annotations-exclude/annotations-exclude_multiple-3_container-1.log b/fluent-bit/tests/runtime/data/kubernetes/log/annotations-exclude/annotations-exclude_multiple-3_container-1.log
new file mode 100644
index 00000000..d5cd3af5
--- /dev/null
+++ b/fluent-bit/tests/runtime/data/kubernetes/log/annotations-exclude/annotations-exclude_multiple-3_container-1.log
@@ -0,0 +1,2 @@
+{"log":"Simple text\n","stream":"stdout","time":"2019-04-01T17:58:33.598656444Z"}
+{"log":"Simple text\n","stream":"stderr","time":"2019-04-01T17:58:33.698656444Z"}
diff --git a/fluent-bit/tests/runtime/data/kubernetes/log/annotations-exclude/annotations-exclude_multiple-3_container-2.log b/fluent-bit/tests/runtime/data/kubernetes/log/annotations-exclude/annotations-exclude_multiple-3_container-2.log
new file mode 100644
index 00000000..d5cd3af5
--- /dev/null
+++ b/fluent-bit/tests/runtime/data/kubernetes/log/annotations-exclude/annotations-exclude_multiple-3_container-2.log
@@ -0,0 +1,2 @@
+{"log":"Simple text\n","stream":"stdout","time":"2019-04-01T17:58:33.598656444Z"}
+{"log":"Simple text\n","stream":"stderr","time":"2019-04-01T17:58:33.698656444Z"}
diff --git a/fluent-bit/tests/runtime/data/kubernetes/log/annotations-exclude/annotations-exclude_multiple-3_container-3.log b/fluent-bit/tests/runtime/data/kubernetes/log/annotations-exclude/annotations-exclude_multiple-3_container-3.log
new file mode 100644
index 00000000..d5cd3af5
--- /dev/null
+++ b/fluent-bit/tests/runtime/data/kubernetes/log/annotations-exclude/annotations-exclude_multiple-3_container-3.log
@@ -0,0 +1,2 @@
+{"log":"Simple text\n","stream":"stdout","time":"2019-04-01T17:58:33.598656444Z"}
+{"log":"Simple text\n","stream":"stderr","time":"2019-04-01T17:58:33.698656444Z"}
diff --git a/fluent-bit/tests/runtime/data/kubernetes/log/annotations-exclude/annotations-exclude_multiple-3_container-4.log b/fluent-bit/tests/runtime/data/kubernetes/log/annotations-exclude/annotations-exclude_multiple-3_container-4.log
new file mode 100644
index 00000000..d5cd3af5
--- /dev/null
+++ b/fluent-bit/tests/runtime/data/kubernetes/log/annotations-exclude/annotations-exclude_multiple-3_container-4.log
@@ -0,0 +1,2 @@
+{"log":"Simple text\n","stream":"stdout","time":"2019-04-01T17:58:33.598656444Z"}
+{"log":"Simple text\n","stream":"stderr","time":"2019-04-01T17:58:33.698656444Z"}
diff --git a/fluent-bit/tests/runtime/data/kubernetes/log/annotations-exclude/annotations-exclude_multiple-4_container-1.log b/fluent-bit/tests/runtime/data/kubernetes/log/annotations-exclude/annotations-exclude_multiple-4_container-1.log
new file mode 100644
index 00000000..d5cd3af5
--- /dev/null
+++ b/fluent-bit/tests/runtime/data/kubernetes/log/annotations-exclude/annotations-exclude_multiple-4_container-1.log
@@ -0,0 +1,2 @@
+{"log":"Simple text\n","stream":"stdout","time":"2019-04-01T17:58:33.598656444Z"}
+{"log":"Simple text\n","stream":"stderr","time":"2019-04-01T17:58:33.698656444Z"}
diff --git a/fluent-bit/tests/runtime/data/kubernetes/log/annotations-exclude/annotations-exclude_multiple-4_container-2.log b/fluent-bit/tests/runtime/data/kubernetes/log/annotations-exclude/annotations-exclude_multiple-4_container-2.log
new file mode 100644
index 00000000..d5cd3af5
--- /dev/null
+++ b/fluent-bit/tests/runtime/data/kubernetes/log/annotations-exclude/annotations-exclude_multiple-4_container-2.log
@@ -0,0 +1,2 @@
+{"log":"Simple text\n","stream":"stdout","time":"2019-04-01T17:58:33.598656444Z"}
+{"log":"Simple text\n","stream":"stderr","time":"2019-04-01T17:58:33.698656444Z"}
diff --git a/fluent-bit/tests/runtime/data/kubernetes/log/annotations-exclude/annotations-exclude_multiple-4_container-3.log b/fluent-bit/tests/runtime/data/kubernetes/log/annotations-exclude/annotations-exclude_multiple-4_container-3.log
new file mode 100644
index 00000000..d5cd3af5
--- /dev/null
+++ b/fluent-bit/tests/runtime/data/kubernetes/log/annotations-exclude/annotations-exclude_multiple-4_container-3.log
@@ -0,0 +1,2 @@
+{"log":"Simple text\n","stream":"stdout","time":"2019-04-01T17:58:33.598656444Z"}
+{"log":"Simple text\n","stream":"stderr","time":"2019-04-01T17:58:33.698656444Z"}
diff --git a/fluent-bit/tests/runtime/data/kubernetes/log/annotations-exclude/annotations-exclude_multiple-4_container-4.log b/fluent-bit/tests/runtime/data/kubernetes/log/annotations-exclude/annotations-exclude_multiple-4_container-4.log
new file mode 100644
index 00000000..d5cd3af5
--- /dev/null
+++ b/fluent-bit/tests/runtime/data/kubernetes/log/annotations-exclude/annotations-exclude_multiple-4_container-4.log
@@ -0,0 +1,2 @@
+{"log":"Simple text\n","stream":"stdout","time":"2019-04-01T17:58:33.598656444Z"}
+{"log":"Simple text\n","stream":"stderr","time":"2019-04-01T17:58:33.698656444Z"}
diff --git a/fluent-bit/tests/runtime/data/kubernetes/log/annotations-exclude/annotations-exclude_stderr_text.log b/fluent-bit/tests/runtime/data/kubernetes/log/annotations-exclude/annotations-exclude_stderr_text.log
new file mode 100644
index 00000000..a857f5ab
--- /dev/null
+++ b/fluent-bit/tests/runtime/data/kubernetes/log/annotations-exclude/annotations-exclude_stderr_text.log
@@ -0,0 +1,2 @@
+{"log":"Simple text\n","stream":"stderr","time":"2019-04-01T17:58:33.698656444Z"}
+{"log":"Simple text\n","stream":"stdout","time":"2019-04-01T17:58:33.598656444Z"}
diff --git a/fluent-bit/tests/runtime/data/kubernetes/log/annotations-exclude/annotations-exclude_stdout_text.log b/fluent-bit/tests/runtime/data/kubernetes/log/annotations-exclude/annotations-exclude_stdout_text.log
new file mode 100644
index 00000000..d5cd3af5
--- /dev/null
+++ b/fluent-bit/tests/runtime/data/kubernetes/log/annotations-exclude/annotations-exclude_stdout_text.log
@@ -0,0 +1,2 @@
+{"log":"Simple text\n","stream":"stdout","time":"2019-04-01T17:58:33.598656444Z"}
+{"log":"Simple text\n","stream":"stderr","time":"2019-04-01T17:58:33.698656444Z"}
diff --git a/fluent-bit/tests/runtime/data/kubernetes/log/annotations-parser/annotations-parser_invalid_text.log b/fluent-bit/tests/runtime/data/kubernetes/log/annotations-parser/annotations-parser_invalid_text.log
new file mode 100644
index 00000000..a857f5ab
--- /dev/null
+++ b/fluent-bit/tests/runtime/data/kubernetes/log/annotations-parser/annotations-parser_invalid_text.log
@@ -0,0 +1,2 @@
+{"log":"Simple text\n","stream":"stderr","time":"2019-04-01T17:58:33.698656444Z"}
+{"log":"Simple text\n","stream":"stdout","time":"2019-04-01T17:58:33.598656444Z"}
diff --git a/fluent-bit/tests/runtime/data/kubernetes/log/annotations-parser/annotations-parser_json-with-time_invalid-json-1.log b/fluent-bit/tests/runtime/data/kubernetes/log/annotations-parser/annotations-parser_json-with-time_invalid-json-1.log
new file mode 100644
index 00000000..65d77786
--- /dev/null
+++ b/fluent-bit/tests/runtime/data/kubernetes/log/annotations-parser/annotations-parser_json-with-time_invalid-json-1.log
@@ -0,0 +1 @@
+{"log":"{\"time\":\"2019-04-05T14:54:23.584269761\",\"message\":\"Simple text\"]\n","stream":"stdout","time":"2019-04-01T17:58:33.598656444Z"}
diff --git a/fluent-bit/tests/runtime/data/kubernetes/log/annotations-parser/annotations-parser_json-with-time_json.log b/fluent-bit/tests/runtime/data/kubernetes/log/annotations-parser/annotations-parser_json-with-time_json.log
new file mode 100644
index 00000000..d9dc4a39
--- /dev/null
+++ b/fluent-bit/tests/runtime/data/kubernetes/log/annotations-parser/annotations-parser_json-with-time_json.log
@@ -0,0 +1 @@
+{"log":"{\"time\":\"2019-04-05T14:54:23.584269761\",\"message\":\"Simple text\"}\n","stream":"stdout","time":"2019-04-01T17:58:33.598656444Z"}
diff --git a/fluent-bit/tests/runtime/data/kubernetes/log/annotations-parser/annotations-parser_multiple-1_container-1.log b/fluent-bit/tests/runtime/data/kubernetes/log/annotations-parser/annotations-parser_multiple-1_container-1.log
new file mode 100644
index 00000000..db4b14a8
--- /dev/null
+++ b/fluent-bit/tests/runtime/data/kubernetes/log/annotations-parser/annotations-parser_multiple-1_container-1.log
@@ -0,0 +1,2 @@
+{"log":"[2019-04-05T14:54:23.584269761] Container 1 is logging on stdout\n","stream":"stdout","time":"2019-04-01T17:58:33.598656444Z"}
+{"log":"[2019-04-05T14:54:23.584269761] Container 1 is logging on stderr\n","stream":"stderr","time":"2019-04-01T17:58:33.698656444Z"}
diff --git a/fluent-bit/tests/runtime/data/kubernetes/log/annotations-parser/annotations-parser_multiple-1_container-2.log b/fluent-bit/tests/runtime/data/kubernetes/log/annotations-parser/annotations-parser_multiple-1_container-2.log
new file mode 100644
index 00000000..d2f01a8b
--- /dev/null
+++ b/fluent-bit/tests/runtime/data/kubernetes/log/annotations-parser/annotations-parser_multiple-1_container-2.log
@@ -0,0 +1,2 @@
+{"log":"[2019-04-05T14:54:23.584269761] Container 2 is logging on stdout\n","stream":"stdout","time":"2019-04-01T17:58:33.598656444Z"}
+{"log":"[2019-04-05T14:54:23.584269761] Container 2 is logging on stderr\n","stream":"stderr","time":"2019-04-01T17:58:33.698656444Z"}
diff --git a/fluent-bit/tests/runtime/data/kubernetes/log/annotations-parser/annotations-parser_multiple-1_container-3.log b/fluent-bit/tests/runtime/data/kubernetes/log/annotations-parser/annotations-parser_multiple-1_container-3.log
new file mode 100644
index 00000000..edee75aa
--- /dev/null
+++ b/fluent-bit/tests/runtime/data/kubernetes/log/annotations-parser/annotations-parser_multiple-1_container-3.log
@@ -0,0 +1,2 @@
+{"log":"[2019-04-05T14:54:23.584269761] Container 3 is logging on stdout\n","stream":"stdout","time":"2019-04-01T17:58:33.598656444Z"}
+{"log":"[2019-04-05T14:54:23.584269761] Container 3 is logging on stderr\n","stream":"stderr","time":"2019-04-01T17:58:33.698656444Z"}
diff --git a/fluent-bit/tests/runtime/data/kubernetes/log/annotations-parser/annotations-parser_multiple-1_container-4.log b/fluent-bit/tests/runtime/data/kubernetes/log/annotations-parser/annotations-parser_multiple-1_container-4.log
new file mode 100644
index 00000000..48e1dabd
--- /dev/null
+++ b/fluent-bit/tests/runtime/data/kubernetes/log/annotations-parser/annotations-parser_multiple-1_container-4.log
@@ -0,0 +1,2 @@
+{"log":"[2019-04-05T14:54:23.584269761] Container 4 is logging on stdout\n","stream":"stdout","time":"2019-04-01T17:58:33.598656444Z"}
+{"log":"[2019-04-05T14:54:23.584269761] Container 4 is logging on stderr\n","stream":"stderr","time":"2019-04-01T17:58:33.698656444Z"}
diff --git a/fluent-bit/tests/runtime/data/kubernetes/log/annotations-parser/annotations-parser_multiple-1_container-5.log b/fluent-bit/tests/runtime/data/kubernetes/log/annotations-parser/annotations-parser_multiple-1_container-5.log
new file mode 100644
index 00000000..81e67c5f
--- /dev/null
+++ b/fluent-bit/tests/runtime/data/kubernetes/log/annotations-parser/annotations-parser_multiple-1_container-5.log
@@ -0,0 +1,2 @@
+{"log":"[2019-04-05T14:54:23.584269761] Container 5 is logging on stdout\n","stream":"stdout","time":"2019-04-01T17:58:33.598656444Z"}
+{"log":"[2019-04-05T14:54:23.584269761] Container 5 is logging on stderr\n","stream":"stderr","time":"2019-04-01T17:58:33.698656444Z"}
diff --git a/fluent-bit/tests/runtime/data/kubernetes/log/annotations-parser/annotations-parser_multiple-2_container-1.log b/fluent-bit/tests/runtime/data/kubernetes/log/annotations-parser/annotations-parser_multiple-2_container-1.log
new file mode 100644
index 00000000..db4b14a8
--- /dev/null
+++ b/fluent-bit/tests/runtime/data/kubernetes/log/annotations-parser/annotations-parser_multiple-2_container-1.log
@@ -0,0 +1,2 @@
+{"log":"[2019-04-05T14:54:23.584269761] Container 1 is logging on stdout\n","stream":"stdout","time":"2019-04-01T17:58:33.598656444Z"}
+{"log":"[2019-04-05T14:54:23.584269761] Container 1 is logging on stderr\n","stream":"stderr","time":"2019-04-01T17:58:33.698656444Z"}
diff --git a/fluent-bit/tests/runtime/data/kubernetes/log/annotations-parser/annotations-parser_multiple-2_container-2.log b/fluent-bit/tests/runtime/data/kubernetes/log/annotations-parser/annotations-parser_multiple-2_container-2.log
new file mode 100644
index 00000000..d2f01a8b
--- /dev/null
+++ b/fluent-bit/tests/runtime/data/kubernetes/log/annotations-parser/annotations-parser_multiple-2_container-2.log
@@ -0,0 +1,2 @@
+{"log":"[2019-04-05T14:54:23.584269761] Container 2 is logging on stdout\n","stream":"stdout","time":"2019-04-01T17:58:33.598656444Z"}
+{"log":"[2019-04-05T14:54:23.584269761] Container 2 is logging on stderr\n","stream":"stderr","time":"2019-04-01T17:58:33.698656444Z"}
diff --git a/fluent-bit/tests/runtime/data/kubernetes/log/annotations-parser/annotations-parser_multiple-2_container-3.log b/fluent-bit/tests/runtime/data/kubernetes/log/annotations-parser/annotations-parser_multiple-2_container-3.log
new file mode 100644
index 00000000..edee75aa
--- /dev/null
+++ b/fluent-bit/tests/runtime/data/kubernetes/log/annotations-parser/annotations-parser_multiple-2_container-3.log
@@ -0,0 +1,2 @@
+{"log":"[2019-04-05T14:54:23.584269761] Container 3 is logging on stdout\n","stream":"stdout","time":"2019-04-01T17:58:33.598656444Z"}
+{"log":"[2019-04-05T14:54:23.584269761] Container 3 is logging on stderr\n","stream":"stderr","time":"2019-04-01T17:58:33.698656444Z"}
diff --git a/fluent-bit/tests/runtime/data/kubernetes/log/annotations-parser/annotations-parser_multiple-2_container-4.log b/fluent-bit/tests/runtime/data/kubernetes/log/annotations-parser/annotations-parser_multiple-2_container-4.log
new file mode 100644
index 00000000..48e1dabd
--- /dev/null
+++ b/fluent-bit/tests/runtime/data/kubernetes/log/annotations-parser/annotations-parser_multiple-2_container-4.log
@@ -0,0 +1,2 @@
+{"log":"[2019-04-05T14:54:23.584269761] Container 4 is logging on stdout\n","stream":"stdout","time":"2019-04-01T17:58:33.598656444Z"}
+{"log":"[2019-04-05T14:54:23.584269761] Container 4 is logging on stderr\n","stream":"stderr","time":"2019-04-01T17:58:33.698656444Z"}
diff --git a/fluent-bit/tests/runtime/data/kubernetes/log/annotations-parser/annotations-parser_multiple-2_container-5.log b/fluent-bit/tests/runtime/data/kubernetes/log/annotations-parser/annotations-parser_multiple-2_container-5.log
new file mode 100644
index 00000000..81e67c5f
--- /dev/null
+++ b/fluent-bit/tests/runtime/data/kubernetes/log/annotations-parser/annotations-parser_multiple-2_container-5.log
@@ -0,0 +1,2 @@
+{"log":"[2019-04-05T14:54:23.584269761] Container 5 is logging on stdout\n","stream":"stdout","time":"2019-04-01T17:58:33.598656444Z"}
+{"log":"[2019-04-05T14:54:23.584269761] Container 5 is logging on stderr\n","stream":"stderr","time":"2019-04-01T17:58:33.698656444Z"}
diff --git a/fluent-bit/tests/runtime/data/kubernetes/log/annotations-parser/annotations-parser_regex-with-time_invalid-text-1.log b/fluent-bit/tests/runtime/data/kubernetes/log/annotations-parser/annotations-parser_regex-with-time_invalid-text-1.log
new file mode 100644
index 00000000..8e87678b
--- /dev/null
+++ b/fluent-bit/tests/runtime/data/kubernetes/log/annotations-parser/annotations-parser_regex-with-time_invalid-text-1.log
@@ -0,0 +1 @@
+{"log":"2019-04-05T14:54:23.584269761 - Simple text\n","stream":"stdout","time":"2019-04-01T17:58:33.598656444Z"}
diff --git a/fluent-bit/tests/runtime/data/kubernetes/log/annotations-parser/annotations-parser_regex-with-time_text.log b/fluent-bit/tests/runtime/data/kubernetes/log/annotations-parser/annotations-parser_regex-with-time_text.log
new file mode 100644
index 00000000..6e334835
--- /dev/null
+++ b/fluent-bit/tests/runtime/data/kubernetes/log/annotations-parser/annotations-parser_regex-with-time_text.log
@@ -0,0 +1 @@
+{"log":"[2019-04-05T14:54:23.584269761] Simple text\n","stream":"stdout","time":"2019-04-01T17:58:33.598656444Z"}
diff --git a/fluent-bit/tests/runtime/data/kubernetes/log/annotations-parser/annotations-parser_stderr_text.log b/fluent-bit/tests/runtime/data/kubernetes/log/annotations-parser/annotations-parser_stderr_text.log
new file mode 100644
index 00000000..c6e1e05f
--- /dev/null
+++ b/fluent-bit/tests/runtime/data/kubernetes/log/annotations-parser/annotations-parser_stderr_text.log
@@ -0,0 +1,2 @@
+{"log":"[2019-04-05T14:54:23.584269761] Simple text\n","stream":"stderr","time":"2019-04-01T17:58:33.698656444Z"}
+{"log":"[2019-04-05T14:54:23.584269761] Simple text\n","stream":"stdout","time":"2019-04-01T17:58:33.598656444Z"}
diff --git a/fluent-bit/tests/runtime/data/kubernetes/log/annotations-parser/annotations-parser_stdout_text.log b/fluent-bit/tests/runtime/data/kubernetes/log/annotations-parser/annotations-parser_stdout_text.log
new file mode 100644
index 00000000..c47aa000
--- /dev/null
+++ b/fluent-bit/tests/runtime/data/kubernetes/log/annotations-parser/annotations-parser_stdout_text.log
@@ -0,0 +1,2 @@
+{"log":"[2019-04-05T14:54:23.584269761] Simple text\n","stream":"stdout","time":"2019-04-01T17:58:33.598656444Z"}
+{"log":"[2019-04-05T14:54:23.584269761] Simple text\n","stream":"stderr","time":"2019-04-01T17:58:33.698656444Z"}
diff --git a/fluent-bit/tests/runtime/data/kubernetes/log/annotations/annotations_invalid_text.log b/fluent-bit/tests/runtime/data/kubernetes/log/annotations/annotations_invalid_text.log
new file mode 100644
index 00000000..59ad227f
--- /dev/null
+++ b/fluent-bit/tests/runtime/data/kubernetes/log/annotations/annotations_invalid_text.log
@@ -0,0 +1 @@
+{"log":"Simple text\n","stream":"stdout","time":"2019-04-01T17:58:33.598656444Z"}
diff --git a/fluent-bit/tests/runtime/data/kubernetes/log/core/core_base_fluent-bit.log b/fluent-bit/tests/runtime/data/kubernetes/log/core/core_base_fluent-bit.log
new file mode 100644
index 00000000..25972313
--- /dev/null
+++ b/fluent-bit/tests/runtime/data/kubernetes/log/core/core_base_fluent-bit.log
@@ -0,0 +1 @@
+{"log":"Fluent Bit is logging\n","stream":"stdout","time":"2019-04-01T17:58:33.598656444Z"}
diff --git a/fluent-bit/tests/runtime/data/kubernetes/log/core/core_no-meta_text.log b/fluent-bit/tests/runtime/data/kubernetes/log/core/core_no-meta_text.log
new file mode 100644
index 00000000..59ad227f
--- /dev/null
+++ b/fluent-bit/tests/runtime/data/kubernetes/log/core/core_no-meta_text.log
@@ -0,0 +1 @@
+{"log":"Simple text\n","stream":"stdout","time":"2019-04-01T17:58:33.598656444Z"}
diff --git a/fluent-bit/tests/runtime/data/kubernetes/log/core/core_unescaping_json.log b/fluent-bit/tests/runtime/data/kubernetes/log/core/core_unescaping_json.log
new file mode 100644
index 00000000..0cf6e441
--- /dev/null
+++ b/fluent-bit/tests/runtime/data/kubernetes/log/core/core_unescaping_json.log
@@ -0,0 +1 @@
+{"log":"{\"stringified-json\":\"{\\\"stringified-json\\\":\\\"{\\\\\\\"stringified-json\\\\\\\":\\\\\\\"\\\\\\\"}\\\"}\",\"text\":\"quoted \\\"text with embedded quoted \\\\\\\"text\\\\\\\"\\\"\"}\n","stream":"stdout","time":"2019-04-01T17:58:33.598656444Z"}
diff --git a/fluent-bit/tests/runtime/data/kubernetes/log/core/core_unescaping_text.log b/fluent-bit/tests/runtime/data/kubernetes/log/core/core_unescaping_text.log
new file mode 100644
index 00000000..55c67368
--- /dev/null
+++ b/fluent-bit/tests/runtime/data/kubernetes/log/core/core_unescaping_text.log
@@ -0,0 +1 @@
+{"log":"Text with embedded quoted \"text with embedded quoted \\\"text with embedded quoted \\\\\\\"text\\\\\\\"\\\"\"\n","stream":"stdout","time":"2019-04-01T17:58:33.598656444Z"}
diff --git a/fluent-bit/tests/runtime/data/kubernetes/log/options/options_k8s-logging-exclude-disabled_text.log b/fluent-bit/tests/runtime/data/kubernetes/log/options/options_k8s-logging-exclude-disabled_text.log
new file mode 100644
index 00000000..d5cd3af5
--- /dev/null
+++ b/fluent-bit/tests/runtime/data/kubernetes/log/options/options_k8s-logging-exclude-disabled_text.log
@@ -0,0 +1,2 @@
+{"log":"Simple text\n","stream":"stdout","time":"2019-04-01T17:58:33.598656444Z"}
+{"log":"Simple text\n","stream":"stderr","time":"2019-04-01T17:58:33.698656444Z"}
diff --git a/fluent-bit/tests/runtime/data/kubernetes/log/options/options_k8s-logging-parser-disabled_text.log b/fluent-bit/tests/runtime/data/kubernetes/log/options/options_k8s-logging-parser-disabled_text.log
new file mode 100644
index 00000000..d5cd3af5
--- /dev/null
+++ b/fluent-bit/tests/runtime/data/kubernetes/log/options/options_k8s-logging-parser-disabled_text.log
@@ -0,0 +1,2 @@
+{"log":"Simple text\n","stream":"stdout","time":"2019-04-01T17:58:33.598656444Z"}
+{"log":"Simple text\n","stream":"stderr","time":"2019-04-01T17:58:33.698656444Z"}
diff --git a/fluent-bit/tests/runtime/data/kubernetes/log/options/options_keep-log-disabled_json.log b/fluent-bit/tests/runtime/data/kubernetes/log/options/options_keep-log-disabled_json.log
new file mode 100644
index 00000000..e1410b0f
--- /dev/null
+++ b/fluent-bit/tests/runtime/data/kubernetes/log/options/options_keep-log-disabled_json.log
@@ -0,0 +1 @@
+{"log":"{\"text\":\"Simple text\"}\n","stream":"stdout","time":"2019-04-01T17:58:33.598656444Z"}
diff --git a/fluent-bit/tests/runtime/data/kubernetes/log/options/options_keep-log-enabled_json.log b/fluent-bit/tests/runtime/data/kubernetes/log/options/options_keep-log-enabled_json.log
new file mode 100644
index 00000000..e1410b0f
--- /dev/null
+++ b/fluent-bit/tests/runtime/data/kubernetes/log/options/options_keep-log-enabled_json.log
@@ -0,0 +1 @@
+{"log":"{\"text\":\"Simple text\"}\n","stream":"stdout","time":"2019-04-01T17:58:33.598656444Z"}
diff --git a/fluent-bit/tests/runtime/data/kubernetes/log/options/options_merge-log-disabled_json.log b/fluent-bit/tests/runtime/data/kubernetes/log/options/options_merge-log-disabled_json.log
new file mode 100644
index 00000000..e1410b0f
--- /dev/null
+++ b/fluent-bit/tests/runtime/data/kubernetes/log/options/options_merge-log-disabled_json.log
@@ -0,0 +1 @@
+{"log":"{\"text\":\"Simple text\"}\n","stream":"stdout","time":"2019-04-01T17:58:33.598656444Z"}
diff --git a/fluent-bit/tests/runtime/data/kubernetes/log/options/options_merge-log-enabled_invalid-json.log b/fluent-bit/tests/runtime/data/kubernetes/log/options/options_merge-log-enabled_invalid-json.log
new file mode 100644
index 00000000..9b4e0dcb
--- /dev/null
+++ b/fluent-bit/tests/runtime/data/kubernetes/log/options/options_merge-log-enabled_invalid-json.log
@@ -0,0 +1 @@
+{"log":"{\"text\":\"Simple text\"]\n","stream":"stdout","time":"2019-04-01T17:58:33.598656444Z"}
diff --git a/fluent-bit/tests/runtime/data/kubernetes/log/options/options_merge-log-enabled_json.log b/fluent-bit/tests/runtime/data/kubernetes/log/options/options_merge-log-enabled_json.log
new file mode 100644
index 00000000..e1410b0f
--- /dev/null
+++ b/fluent-bit/tests/runtime/data/kubernetes/log/options/options_merge-log-enabled_json.log
@@ -0,0 +1 @@
+{"log":"{\"text\":\"Simple text\"}\n","stream":"stdout","time":"2019-04-01T17:58:33.598656444Z"}
diff --git a/fluent-bit/tests/runtime/data/kubernetes/log/options/options_merge-log-enabled_text.log b/fluent-bit/tests/runtime/data/kubernetes/log/options/options_merge-log-enabled_text.log
new file mode 100644
index 00000000..59ad227f
--- /dev/null
+++ b/fluent-bit/tests/runtime/data/kubernetes/log/options/options_merge-log-enabled_text.log
@@ -0,0 +1 @@
+{"log":"Simple text\n","stream":"stdout","time":"2019-04-01T17:58:33.598656444Z"}
diff --git a/fluent-bit/tests/runtime/data/kubernetes/log/options/options_merge-log-key_json.log b/fluent-bit/tests/runtime/data/kubernetes/log/options/options_merge-log-key_json.log
new file mode 100644
index 00000000..e1410b0f
--- /dev/null
+++ b/fluent-bit/tests/runtime/data/kubernetes/log/options/options_merge-log-key_json.log
@@ -0,0 +1 @@
+{"log":"{\"text\":\"Simple text\"}\n","stream":"stdout","time":"2019-04-01T17:58:33.598656444Z"}
diff --git a/fluent-bit/tests/runtime/data/kubernetes/log/options/options_merge-log-trim-disabled_json.log b/fluent-bit/tests/runtime/data/kubernetes/log/options/options_merge-log-trim-disabled_json.log
new file mode 100644
index 00000000..6dc59dc1
--- /dev/null
+++ b/fluent-bit/tests/runtime/data/kubernetes/log/options/options_merge-log-trim-disabled_json.log
@@ -0,0 +1 @@
+{"log":"{\"text\":\"Simple text\n\"}\n","stream":"stdout","time":"2019-04-01T17:58:33.598656444Z"}
diff --git a/fluent-bit/tests/runtime/data/kubernetes/log/options/options_merge-log-trim-enabled_json.log b/fluent-bit/tests/runtime/data/kubernetes/log/options/options_merge-log-trim-enabled_json.log
new file mode 100644
index 00000000..6dc59dc1
--- /dev/null
+++ b/fluent-bit/tests/runtime/data/kubernetes/log/options/options_merge-log-trim-enabled_json.log
@@ -0,0 +1 @@
+{"log":"{\"text\":\"Simple text\n\"}\n","stream":"stdout","time":"2019-04-01T17:58:33.598656444Z"}
diff --git a/fluent-bit/tests/runtime/data/kubernetes/log/options/options_use-kubelet-disabled_fluent-bit.log b/fluent-bit/tests/runtime/data/kubernetes/log/options/options_use-kubelet-disabled_fluent-bit.log
new file mode 100644
index 00000000..25972313
--- /dev/null
+++ b/fluent-bit/tests/runtime/data/kubernetes/log/options/options_use-kubelet-disabled_fluent-bit.log
@@ -0,0 +1 @@
+{"log":"Fluent Bit is logging\n","stream":"stdout","time":"2019-04-01T17:58:33.598656444Z"}
diff --git a/fluent-bit/tests/runtime/data/kubernetes/log/options/options_use-kubelet-enabled_fluent-bit.log b/fluent-bit/tests/runtime/data/kubernetes/log/options/options_use-kubelet-enabled_fluent-bit.log
new file mode 100644
index 00000000..25972313
--- /dev/null
+++ b/fluent-bit/tests/runtime/data/kubernetes/log/options/options_use-kubelet-enabled_fluent-bit.log
@@ -0,0 +1 @@
+{"log":"Fluent Bit is logging\n","stream":"stdout","time":"2019-04-01T17:58:33.598656444Z"}
diff --git a/fluent-bit/tests/runtime/data/kubernetes/meta/annotations-exclude_default.meta b/fluent-bit/tests/runtime/data/kubernetes/meta/annotations-exclude_default.meta
new file mode 100644
index 00000000..e986f164
--- /dev/null
+++ b/fluent-bit/tests/runtime/data/kubernetes/meta/annotations-exclude_default.meta
@@ -0,0 +1,7 @@
+{
+ "metadata": {
+ "annotations": {
+ "fluentbit.io/exclude": "true"
+ }
+ }
+}
diff --git a/fluent-bit/tests/runtime/data/kubernetes/meta/annotations-exclude_invalid.meta b/fluent-bit/tests/runtime/data/kubernetes/meta/annotations-exclude_invalid.meta
new file mode 100644
index 00000000..9f529a42
--- /dev/null
+++ b/fluent-bit/tests/runtime/data/kubernetes/meta/annotations-exclude_invalid.meta
@@ -0,0 +1,7 @@
+{
+ "metadata": {
+ "annotations": {
+ "fluentbit.io/exclude": "neither-true-nor-false"
+ }
+ }
+}
diff --git a/fluent-bit/tests/runtime/data/kubernetes/meta/annotations-exclude_multiple-1.meta b/fluent-bit/tests/runtime/data/kubernetes/meta/annotations-exclude_multiple-1.meta
new file mode 100644
index 00000000..137d88e6
--- /dev/null
+++ b/fluent-bit/tests/runtime/data/kubernetes/meta/annotations-exclude_multiple-1.meta
@@ -0,0 +1,9 @@
+{
+ "metadata": {
+ "annotations": {
+ "fluentbit.io/exclude-container-1": "true",
+ "fluentbit.io/exclude_stdout-container-2": "true",
+ "fluentbit.io/exclude_stderr-container-3": "true"
+ }
+ }
+}
diff --git a/fluent-bit/tests/runtime/data/kubernetes/meta/annotations-exclude_multiple-2.meta b/fluent-bit/tests/runtime/data/kubernetes/meta/annotations-exclude_multiple-2.meta
new file mode 100644
index 00000000..f9d1ded2
--- /dev/null
+++ b/fluent-bit/tests/runtime/data/kubernetes/meta/annotations-exclude_multiple-2.meta
@@ -0,0 +1,10 @@
+{
+ "metadata": {
+ "annotations": {
+ "fluentbit.io/exclude": "true",
+ "fluentbit.io/exclude_stderr-container-2": "false",
+ "fluentbit.io/exclude_stdout-container-3": "false",
+ "fluentbit.io/exclude-container-4": "false"
+ }
+ }
+}
diff --git a/fluent-bit/tests/runtime/data/kubernetes/meta/annotations-exclude_multiple-3.meta b/fluent-bit/tests/runtime/data/kubernetes/meta/annotations-exclude_multiple-3.meta
new file mode 100644
index 00000000..bfcbb44b
--- /dev/null
+++ b/fluent-bit/tests/runtime/data/kubernetes/meta/annotations-exclude_multiple-3.meta
@@ -0,0 +1,11 @@
+{
+ "metadata": {
+ "annotations": {
+ "fluentbit.io/exclude_stdout": "true",
+ "fluentbit.io/exclude_stderr-container-1": "true",
+ "fluentbit.io/exclude_stdout-container-3": "false",
+ "fluentbit.io/exclude_stderr-container-3": "true",
+ "fluentbit.io/exclude_stdout-container-4": "false"
+ }
+ }
+}
diff --git a/fluent-bit/tests/runtime/data/kubernetes/meta/annotations-exclude_multiple-4.meta b/fluent-bit/tests/runtime/data/kubernetes/meta/annotations-exclude_multiple-4.meta
new file mode 100644
index 00000000..1295d0ed
--- /dev/null
+++ b/fluent-bit/tests/runtime/data/kubernetes/meta/annotations-exclude_multiple-4.meta
@@ -0,0 +1,11 @@
+{
+ "metadata": {
+ "annotations": {
+ "fluentbit.io/exclude_stderr": "true",
+ "fluentbit.io/exclude_stdout-container-1": "true",
+ "fluentbit.io/exclude_stdout-container-2": "true",
+ "fluentbit.io/exclude_stderr-container-2": "false",
+ "fluentbit.io/exclude_stderr-container-4": "false"
+ }
+ }
+}
diff --git a/fluent-bit/tests/runtime/data/kubernetes/meta/annotations-exclude_stderr.meta b/fluent-bit/tests/runtime/data/kubernetes/meta/annotations-exclude_stderr.meta
new file mode 100644
index 00000000..b57831f5
--- /dev/null
+++ b/fluent-bit/tests/runtime/data/kubernetes/meta/annotations-exclude_stderr.meta
@@ -0,0 +1,7 @@
+{
+ "metadata": {
+ "annotations": {
+ "fluentbit.io/exclude_stderr": "true"
+ }
+ }
+}
diff --git a/fluent-bit/tests/runtime/data/kubernetes/meta/annotations-exclude_stdout.meta b/fluent-bit/tests/runtime/data/kubernetes/meta/annotations-exclude_stdout.meta
new file mode 100644
index 00000000..db187f2e
--- /dev/null
+++ b/fluent-bit/tests/runtime/data/kubernetes/meta/annotations-exclude_stdout.meta
@@ -0,0 +1,7 @@
+{
+ "metadata": {
+ "annotations": {
+ "fluentbit.io/exclude_stdout": "true"
+ }
+ }
+}
diff --git a/fluent-bit/tests/runtime/data/kubernetes/meta/annotations-parser_invalid.meta b/fluent-bit/tests/runtime/data/kubernetes/meta/annotations-parser_invalid.meta
new file mode 100644
index 00000000..676f2063
--- /dev/null
+++ b/fluent-bit/tests/runtime/data/kubernetes/meta/annotations-parser_invalid.meta
@@ -0,0 +1,7 @@
+{
+ "metadata": {
+ "annotations": {
+ "fluentbit.io/parser": "invalid-parser"
+ }
+ }
+}
diff --git a/fluent-bit/tests/runtime/data/kubernetes/meta/annotations-parser_json-with-time.meta b/fluent-bit/tests/runtime/data/kubernetes/meta/annotations-parser_json-with-time.meta
new file mode 100644
index 00000000..9bdba685
--- /dev/null
+++ b/fluent-bit/tests/runtime/data/kubernetes/meta/annotations-parser_json-with-time.meta
@@ -0,0 +1,7 @@
+{
+ "metadata": {
+ "annotations": {
+ "fluentbit.io/parser": "json-parser-with-time"
+ }
+ }
+}
diff --git a/fluent-bit/tests/runtime/data/kubernetes/meta/annotations-parser_json-without-time.meta b/fluent-bit/tests/runtime/data/kubernetes/meta/annotations-parser_json-without-time.meta
new file mode 100644
index 00000000..2195ae0d
--- /dev/null
+++ b/fluent-bit/tests/runtime/data/kubernetes/meta/annotations-parser_json-without-time.meta
@@ -0,0 +1,7 @@
+{
+ "metadata": {
+ "annotations": {
+ "fluentbit.io/parser": "json-parser-without-time"
+ }
+ }
+}
diff --git a/fluent-bit/tests/runtime/data/kubernetes/meta/annotations-parser_multiple-1.meta b/fluent-bit/tests/runtime/data/kubernetes/meta/annotations-parser_multiple-1.meta
new file mode 100644
index 00000000..51420a87
--- /dev/null
+++ b/fluent-bit/tests/runtime/data/kubernetes/meta/annotations-parser_multiple-1.meta
@@ -0,0 +1,12 @@
+{
+ "metadata": {
+ "annotations": {
+ "fluentbit.io/parser-container-1": "container-1-parser",
+ "fluentbit.io/parser_stdout-container-2": "container-2-stdout-parser",
+ "fluentbit.io/parser_stderr-container-2": "container-2-stderr-parser",
+ "fluentbit.io/parser_stdout-container-3": "container-3-stdout-parser",
+ "fluentbit.io/parser_stderr-container-4": "container-4-stderr-parser",
+ "fluentbit.io/parser": "default-parser"
+ }
+ }
+}
diff --git a/fluent-bit/tests/runtime/data/kubernetes/meta/annotations-parser_multiple-2.meta b/fluent-bit/tests/runtime/data/kubernetes/meta/annotations-parser_multiple-2.meta
new file mode 100644
index 00000000..b3bb2bdc
--- /dev/null
+++ b/fluent-bit/tests/runtime/data/kubernetes/meta/annotations-parser_multiple-2.meta
@@ -0,0 +1,13 @@
+{
+ "metadata": {
+ "annotations": {
+ "fluentbit.io/parser-container-1": "container-1-parser",
+ "fluentbit.io/parser_stdout-container-2": "container-2-stdout-parser",
+ "fluentbit.io/parser_stderr-container-2": "container-2-stderr-parser",
+ "fluentbit.io/parser_stdout-container-3": "container-3-stdout-parser",
+ "fluentbit.io/parser_stderr-container-4": "container-4-stderr-parser",
+ "fluentbit.io/parser_stdout": "default-stdout-parser",
+ "fluentbit.io/parser_stderr": "default-stderr-parser"
+ }
+ }
+}
diff --git a/fluent-bit/tests/runtime/data/kubernetes/meta/annotations-parser_regex-with-time.meta b/fluent-bit/tests/runtime/data/kubernetes/meta/annotations-parser_regex-with-time.meta
new file mode 100644
index 00000000..e098a81d
--- /dev/null
+++ b/fluent-bit/tests/runtime/data/kubernetes/meta/annotations-parser_regex-with-time.meta
@@ -0,0 +1,7 @@
+{
+ "metadata": {
+ "annotations": {
+ "fluentbit.io/parser": "regex-parser-with-time"
+ }
+ }
+}
diff --git a/fluent-bit/tests/runtime/data/kubernetes/meta/annotations-parser_regex-without-time.meta b/fluent-bit/tests/runtime/data/kubernetes/meta/annotations-parser_regex-without-time.meta
new file mode 100644
index 00000000..8a43354b
--- /dev/null
+++ b/fluent-bit/tests/runtime/data/kubernetes/meta/annotations-parser_regex-without-time.meta
@@ -0,0 +1,7 @@
+{
+ "metadata": {
+ "annotations": {
+ "fluentbit.io/parser": "regex-parser-without-time"
+ }
+ }
+}
diff --git a/fluent-bit/tests/runtime/data/kubernetes/meta/annotations-parser_stderr.meta b/fluent-bit/tests/runtime/data/kubernetes/meta/annotations-parser_stderr.meta
new file mode 100644
index 00000000..6a869276
--- /dev/null
+++ b/fluent-bit/tests/runtime/data/kubernetes/meta/annotations-parser_stderr.meta
@@ -0,0 +1,7 @@
+{
+ "metadata": {
+ "annotations": {
+ "fluentbit.io/parser_stderr": "default-stderr-parser"
+ }
+ }
+}
diff --git a/fluent-bit/tests/runtime/data/kubernetes/meta/annotations-parser_stdout.meta b/fluent-bit/tests/runtime/data/kubernetes/meta/annotations-parser_stdout.meta
new file mode 100644
index 00000000..1b9fd148
--- /dev/null
+++ b/fluent-bit/tests/runtime/data/kubernetes/meta/annotations-parser_stdout.meta
@@ -0,0 +1,7 @@
+{
+ "metadata": {
+ "annotations": {
+ "fluentbit.io/parser_stdout": "default-stdout-parser"
+ }
+ }
+}
diff --git a/fluent-bit/tests/runtime/data/kubernetes/meta/annotations_invalid.meta b/fluent-bit/tests/runtime/data/kubernetes/meta/annotations_invalid.meta
new file mode 100644
index 00000000..a1bc934a
--- /dev/null
+++ b/fluent-bit/tests/runtime/data/kubernetes/meta/annotations_invalid.meta
@@ -0,0 +1,12 @@
+{
+ "metadata": {
+ "annotations": {
+ "fluentbit.io/invalid": "true",
+ "fluentbit.io/exclude_": "true",
+ "fluentbit.io/exclude_invalid": "true",
+ "fluentbit.io/exclude_stdout-": "true",
+ "fluentbit.io/exclude-": "true",
+ "fluentbit.io/exclude+": "true"
+ }
+ }
+}
diff --git a/fluent-bit/tests/runtime/data/kubernetes/meta/core_base.meta b/fluent-bit/tests/runtime/data/kubernetes/meta/core_base.meta
new file mode 100644
index 00000000..3ccca443
--- /dev/null
+++ b/fluent-bit/tests/runtime/data/kubernetes/meta/core_base.meta
@@ -0,0 +1,116 @@
+{
+ "apiVersion": "v1",
+ "kind": "Pod",
+ "metadata": {
+ "annotations": {
+ "prometheus.io/path": "/api/v1/metrics/prometheus",
+ "prometheus.io/port": "2020",
+ "prometheus.io/scrape": "true"
+ },
+ "creationTimestamp": "2019-04-03T09:29:00Z",
+ "labels": {
+ "app.kubernetes.io/name": "fluent-bit"
+ },
+ "name": "base",
+ "namespace": "core",
+ "resourceVersion": "74466568",
+ "selfLink": "/api/v1/namespaces/core/pods/base",
+ "uid": "e9f2963f-55f2-11e9-84c5-02e422b8a84a"
+ },
+ "spec": {
+ "containers": [
+ {
+ "image": "fluent/fluent-bit",
+ "imagePullPolicy": "Always",
+ "name": "fluent-bit",
+ "resources": {},
+ "stdin": true,
+ "stdinOnce": true,
+ "terminationMessagePath": "/dev/termination-log",
+ "terminationMessagePolicy": "File",
+ "tty": true,
+ "volumeMounts": [
+ {
+ "mountPath": "/var/run/secrets/kubernetes.io/serviceaccount",
+ "name": "default-token-9ffht",
+ "readOnly": true
+ }
+ ]
+ }
+ ],
+ "dnsPolicy": "ClusterFirst",
+ "nodeName": "ip-10-49-18-80.eu-west-1.compute.internal",
+ "restartPolicy": "Never",
+ "schedulerName": "default-scheduler",
+ "securityContext": {},
+ "serviceAccount": "default",
+ "serviceAccountName": "default",
+ "terminationGracePeriodSeconds": 30,
+ "tolerations": [
+ {
+ "effect": "NoExecute",
+ "key": "node.kubernetes.io/not-ready",
+ "operator": "Exists",
+ "tolerationSeconds": 300
+ },
+ {
+ "effect": "NoExecute",
+ "key": "node.kubernetes.io/unreachable",
+ "operator": "Exists",
+ "tolerationSeconds": 300
+ }
+ ],
+ "volumes": [
+ {
+ "name": "default-token-9ffht",
+ "secret": {
+ "defaultMode": 420,
+ "secretName": "default-token-9ffht"
+ }
+ }
+ ]
+ },
+ "status": {
+ "conditions": [
+ {
+ "lastProbeTime": null,
+ "lastTransitionTime": "2019-04-03T09:29:00Z",
+ "status": "True",
+ "type": "Initialized"
+ },
+ {
+ "lastProbeTime": null,
+ "lastTransitionTime": "2019-04-03T09:29:06Z",
+ "status": "True",
+ "type": "Ready"
+ },
+ {
+ "lastProbeTime": null,
+ "lastTransitionTime": "2019-04-03T09:29:00Z",
+ "status": "True",
+ "type": "PodScheduled"
+ }
+ ],
+ "containerStatuses": [
+ {
+ "containerID": "docker://c9898099f6d235126d564ed38a020007ea7a6fac6e25e718de683c9dd0076c16",
+ "image": "fluent/fluent-bit:latest",
+ "imageID": "docker-pullable://fluent/fluent-bit@sha256:7ac0fd3569af866e9a6a22eb592744200d2dbe098cf066162453f8d0b06c531f",
+ "lastState": {},
+ "name": "fluent-bit",
+ "ready": true,
+ "restartCount": 0,
+ "state": {
+ "running": {
+ "startedAt": "2019-04-03T09:29:05Z"
+ }
+ }
+ }
+ ],
+ "hostIP": "10.49.18.80",
+ "phase": "Running",
+ "podIP": "100.116.192.42",
+ "qosClass": "BestEffort",
+ "startTime": "2019-04-03T09:29:00Z"
+ }
+}
diff --git a/fluent-bit/tests/runtime/data/kubernetes/meta/core_no-meta.meta b/fluent-bit/tests/runtime/data/kubernetes/meta/core_no-meta.meta
new file mode 100644
index 00000000..0967ef42
--- /dev/null
+++ b/fluent-bit/tests/runtime/data/kubernetes/meta/core_no-meta.meta
@@ -0,0 +1 @@
+{}
diff --git a/fluent-bit/tests/runtime/data/kubernetes/meta/core_unescaping.meta b/fluent-bit/tests/runtime/data/kubernetes/meta/core_unescaping.meta
new file mode 100644
index 00000000..4e9f5aa3
--- /dev/null
+++ b/fluent-bit/tests/runtime/data/kubernetes/meta/core_unescaping.meta
@@ -0,0 +1,4 @@
+{
+ "metadata": {
+ }
+}
diff --git a/fluent-bit/tests/runtime/data/kubernetes/meta/default_kairosdb-914055854-b63vq.meta b/fluent-bit/tests/runtime/data/kubernetes/meta/default_kairosdb-914055854-b63vq.meta
new file mode 100644
index 00000000..90109bfe
--- /dev/null
+++ b/fluent-bit/tests/runtime/data/kubernetes/meta/default_kairosdb-914055854-b63vq.meta
@@ -0,0 +1,155 @@
+{
+ "kind": "Pod",
+ "apiVersion": "v1",
+ "metadata": {
+ "name": "kairosdb-914055854-b63vq",
+ "generateName": "kairosdb-914055854-",
+ "namespace": "default",
+ "selfLink": "/api/v1/namespaces/default/pods/kairosdb-914055854-b63vq",
+ "uid": "d6c53deb-05a4-11e8-a8c4-080027435fb7",
+ "resourceVersion": "25205",
+ "creationTimestamp": "2018-01-30T10:03:37Z",
+ "labels": {
+ "name": "kairosdb",
+ "pod-template-hash": "914055854"
+ },
+ "annotations": {
+ "kubernetes.io/created-by": "{\"kind\":\"SerializedReference\",\"apiVersion\":\"v1\",\"reference\":{\"kind\":\"ReplicaSet\",\"namespace\":\"default\",\"name\":\"kairosdb-914055854\",\"uid\":\"d6c2f841-05a4-11e8-a8c4-080027435fb7\",\"apiVersion\":\"extensions\",\"resourceVersion\":\"1592\"}}\n"
+ },
+ "ownerReferences": [
+ {
+ "apiVersion": "extensions/v1beta1",
+ "kind": "ReplicaSet",
+ "name": "kairosdb-914055854",
+ "uid": "d6c2f841-05a4-11e8-a8c4-080027435fb7",
+ "controller": true,
+ "blockOwnerDeletion": true
+ }
+ ]
+ },
+ "spec": {
+ "volumes": [
+ {
+ "name": "default-token-vv11j",
+ "secret": {
+ "secretName": "default-token-vv11j",
+ "defaultMode": 420
+ }
+ }
+ ],
+ "containers": [
+ {
+ "name": "kairosdb",
+ "image": "docker.io/davradocker/docker-kairosdb:1.1.1_9",
+ "env": [
+ {
+ "name": "kairosdb_http_port",
+ "value": "58080"
+ },
+ {
+ "name": "kairosdb_datastore_cassandra_host_list",
+ "valueFrom": {
+ "configMapKeyRef": {
+ "name": "cassandra-config",
+ "key": "cassandra.nodeports"
+ }
+ }
+ },
+ {
+ "name": "kairosdb_ttl",
+ "value": "315360000"
+ },
+ {
+ "name": "_JAVA_OPTIONS",
+ "value": "-Xms128m -Xmx512m"
+ }
+ ],
+ "resources": {},
+ "volumeMounts": [
+ {
+ "name": "default-token-vv11j",
+ "readOnly": true,
+ "mountPath": "/var/run/secrets/kubernetes.io/serviceaccount"
+ }
+ ],
+ "livenessProbe": {
+ "httpGet": {
+ "path": "/api/v1/metricnames",
+ "port": 58080,
+ "scheme": "HTTP"
+ },
+ "initialDelaySeconds": 20,
+ "timeoutSeconds": 1,
+ "periodSeconds": 1,
+ "successThreshold": 1,
+ "failureThreshold": 1
+ },
+ "terminationMessagePath": "/dev/termination-log",
+ "terminationMessagePolicy": "File",
+ "imagePullPolicy": "IfNotPresent",
+ "securityContext": {
+ "privileged": false
+ }
+ }
+ ],
+ "restartPolicy": "Always",
+ "terminationGracePeriodSeconds": 30,
+ "dnsPolicy": "ClusterFirst",
+ "serviceAccountName": "default",
+ "serviceAccount": "default",
+ "nodeName": "192.168.10.169",
+ "securityContext": {},
+ "schedulerName": "default-scheduler"
+ },
+ "status": {
+ "phase": "Running",
+ "conditions": [
+ {
+ "type": "Initialized",
+ "status": "True",
+ "lastProbeTime": null,
+ "lastTransitionTime": "2018-01-30T10:03:37Z"
+ },
+ {
+ "type": "Ready",
+ "status": "True",
+ "lastProbeTime": null,
+ "lastTransitionTime": "2018-02-27T07:28:45Z"
+ },
+ {
+ "type": "PodScheduled",
+ "status": "True",
+ "lastProbeTime": null,
+ "lastTransitionTime": "2018-01-30T10:03:37Z"
+ }
+ ],
+ "hostIP": "192.168.10.169",
+ "podIP": "10.2.39.12",
+ "startTime": "2018-01-30T10:03:37Z",
+ "containerStatuses": [
+ {
+ "name": "kairosdb",
+ "state": {
+ "running": {
+ "startedAt": "2018-02-27T07:28:45Z"
+ }
+ },
+ "lastState": {
+ "terminated": {
+ "exitCode": 0,
+ "reason": "Completed",
+ "startedAt": "2018-02-27T07:27:13Z",
+ "finishedAt": "2018-02-27T07:27:16Z",
+ "containerID": "docker://5bc9f63b02c09d7726ed3669f734b365f90e077e77a88c4a2f004459772bf912"
+ }
+ },
+ "ready": true,
+ "restartCount": 20,
+ "image": "davradocker/docker-kairosdb:1.1.1_9",
+ "imageID": "docker-pullable://davradocker/docker-kairosdb@sha256:2ee07e3f3ba61b96597c78ab0aa7b837d771b375888b020da7a7316bb524b6e8",
+ "containerID": "docker://02d45318f220ad01109d13df31fbbb0668b87fdd06ef6607abfdecf5eba4b311"
+ }
+ ],
+ "qosClass": "BestEffort"
+ }
+}
diff --git a/fluent-bit/tests/runtime/data/kubernetes/meta/options_k8s-logging-exclude-disabled.meta b/fluent-bit/tests/runtime/data/kubernetes/meta/options_k8s-logging-exclude-disabled.meta
new file mode 100644
index 00000000..e986f164
--- /dev/null
+++ b/fluent-bit/tests/runtime/data/kubernetes/meta/options_k8s-logging-exclude-disabled.meta
@@ -0,0 +1,7 @@
+{
+ "metadata": {
+ "annotations": {
+ "fluentbit.io/exclude": "true"
+ }
+ }
+}
diff --git a/fluent-bit/tests/runtime/data/kubernetes/meta/options_k8s-logging-parser-disabled.meta b/fluent-bit/tests/runtime/data/kubernetes/meta/options_k8s-logging-parser-disabled.meta
new file mode 100644
index 00000000..b922088e
--- /dev/null
+++ b/fluent-bit/tests/runtime/data/kubernetes/meta/options_k8s-logging-parser-disabled.meta
@@ -0,0 +1,7 @@
+{
+ "metadata": {
+ "annotations": {
+ "fluentbit.io/parser": "default-parser"
+ }
+ }
+}
diff --git a/fluent-bit/tests/runtime/data/kubernetes/meta/options_keep-log-disabled.meta b/fluent-bit/tests/runtime/data/kubernetes/meta/options_keep-log-disabled.meta
new file mode 100644
index 00000000..4e9f5aa3
--- /dev/null
+++ b/fluent-bit/tests/runtime/data/kubernetes/meta/options_keep-log-disabled.meta
@@ -0,0 +1,4 @@
+{
+ "metadata": {
+ }
+}
diff --git a/fluent-bit/tests/runtime/data/kubernetes/meta/options_keep-log-enabled.meta b/fluent-bit/tests/runtime/data/kubernetes/meta/options_keep-log-enabled.meta
new file mode 100644
index 00000000..4e9f5aa3
--- /dev/null
+++ b/fluent-bit/tests/runtime/data/kubernetes/meta/options_keep-log-enabled.meta
@@ -0,0 +1,4 @@
+{
+ "metadata": {
+ }
+}
diff --git a/fluent-bit/tests/runtime/data/kubernetes/meta/options_merge-log-disabled.meta b/fluent-bit/tests/runtime/data/kubernetes/meta/options_merge-log-disabled.meta
new file mode 100644
index 00000000..4e9f5aa3
--- /dev/null
+++ b/fluent-bit/tests/runtime/data/kubernetes/meta/options_merge-log-disabled.meta
@@ -0,0 +1,4 @@
+{
+ "metadata": {
+ }
+}
diff --git a/fluent-bit/tests/runtime/data/kubernetes/meta/options_merge-log-enabled.meta b/fluent-bit/tests/runtime/data/kubernetes/meta/options_merge-log-enabled.meta
new file mode 100644
index 00000000..4e9f5aa3
--- /dev/null
+++ b/fluent-bit/tests/runtime/data/kubernetes/meta/options_merge-log-enabled.meta
@@ -0,0 +1,4 @@
+{
+ "metadata": {
+ }
+}
diff --git a/fluent-bit/tests/runtime/data/kubernetes/meta/options_merge-log-key.meta b/fluent-bit/tests/runtime/data/kubernetes/meta/options_merge-log-key.meta
new file mode 100644
index 00000000..4e9f5aa3
--- /dev/null
+++ b/fluent-bit/tests/runtime/data/kubernetes/meta/options_merge-log-key.meta
@@ -0,0 +1,4 @@
+{
+ "metadata": {
+ }
+}
diff --git a/fluent-bit/tests/runtime/data/kubernetes/meta/options_merge-log-trim-disabled.meta b/fluent-bit/tests/runtime/data/kubernetes/meta/options_merge-log-trim-disabled.meta
new file mode 100644
index 00000000..4e9f5aa3
--- /dev/null
+++ b/fluent-bit/tests/runtime/data/kubernetes/meta/options_merge-log-trim-disabled.meta
@@ -0,0 +1,4 @@
+{
+ "metadata": {
+ }
+}
diff --git a/fluent-bit/tests/runtime/data/kubernetes/meta/options_merge-log-trim-enabled.meta b/fluent-bit/tests/runtime/data/kubernetes/meta/options_merge-log-trim-enabled.meta
new file mode 100644
index 00000000..4e9f5aa3
--- /dev/null
+++ b/fluent-bit/tests/runtime/data/kubernetes/meta/options_merge-log-trim-enabled.meta
@@ -0,0 +1,4 @@
+{
+ "metadata": {
+ }
+}
diff --git a/fluent-bit/tests/runtime/data/kubernetes/meta/options_use-kubelet-disabled.meta b/fluent-bit/tests/runtime/data/kubernetes/meta/options_use-kubelet-disabled.meta
new file mode 100644
index 00000000..ce693695
--- /dev/null
+++ b/fluent-bit/tests/runtime/data/kubernetes/meta/options_use-kubelet-disabled.meta
@@ -0,0 +1,116 @@
+{
+ "apiVersion": "v1",
+ "kind": "Pod",
+ "metadata": {
+ "annotations": {
+ "prometheus.io/path": "/api/v1/metrics/prometheus",
+ "prometheus.io/port": "2020",
+ "prometheus.io/scrape": "true"
+ },
+ "creationTimestamp": "2019-04-03T09:29:00Z",
+ "labels": {
+ "app.kubernetes.io/name": "fluent-bit"
+ },
+ "name": "use-kubelet-disabled",
+ "namespace": "options",
+ "resourceVersion": "74466568",
+ "selfLink": "/api/v1/namespaces/core/pods/base",
+ "uid": "e9f2963f-55f2-11e9-84c5-02e422b8a84a"
+ },
+ "spec": {
+ "containers": [
+ {
+ "image": "fluent/fluent-bit",
+ "imagePullPolicy": "Always",
+ "name": "fluent-bit",
+ "resources": {},
+ "stdin": true,
+ "stdinOnce": true,
+ "terminationMessagePath": "/dev/termination-log",
+ "terminationMessagePolicy": "File",
+ "tty": true,
+ "volumeMounts": [
+ {
+ "mountPath": "/var/run/secrets/kubernetes.io/serviceaccount",
+ "name": "default-token-9ffht",
+ "readOnly": true
+ }
+ ]
+ }
+ ],
+ "dnsPolicy": "ClusterFirst",
+ "nodeName": "ip-10-49-18-80.eu-west-1.compute.internal",
+ "restartPolicy": "Never",
+ "schedulerName": "default-scheduler",
+ "securityContext": {},
+ "serviceAccount": "default",
+ "serviceAccountName": "default",
+ "terminationGracePeriodSeconds": 30,
+ "tolerations": [
+ {
+ "effect": "NoExecute",
+ "key": "node.kubernetes.io/not-ready",
+ "operator": "Exists",
+ "tolerationSeconds": 300
+ },
+ {
+ "effect": "NoExecute",
+ "key": "node.kubernetes.io/unreachable",
+ "operator": "Exists",
+ "tolerationSeconds": 300
+ }
+ ],
+ "volumes": [
+ {
+ "name": "default-token-9ffht",
+ "secret": {
+ "defaultMode": 420,
+ "secretName": "default-token-9ffht"
+ }
+ }
+ ]
+ },
+ "status": {
+ "conditions": [
+ {
+ "lastProbeTime": null,
+ "lastTransitionTime": "2019-04-03T09:29:00Z",
+ "status": "True",
+ "type": "Initialized"
+ },
+ {
+ "lastProbeTime": null,
+ "lastTransitionTime": "2019-04-03T09:29:06Z",
+ "status": "True",
+ "type": "Ready"
+ },
+ {
+ "lastProbeTime": null,
+ "lastTransitionTime": "2019-04-03T09:29:00Z",
+ "status": "True",
+ "type": "PodScheduled"
+ }
+ ],
+ "containerStatuses": [
+ {
+ "containerID": "docker://c9898099f6d235126d564ed38a020007ea7a6fac6e25e718de683c9dd0076c16",
+ "image": "fluent/fluent-bit:latest",
+ "imageID": "docker-pullable://fluent/fluent-bit@sha256:7ac0fd3569af866e9a6a22eb592744200d2dbe098cf066162453f8d0b06c531f",
+ "lastState": {},
+ "name": "fluent-bit",
+ "ready": true,
+ "restartCount": 0,
+ "state": {
+ "running": {
+ "startedAt": "2019-04-03T09:29:05Z"
+ }
+ }
+ }
+ ],
+ "hostIP": "10.49.18.80",
+ "phase": "Running",
+ "podIP": "100.116.192.42",
+ "qosClass": "BestEffort",
+ "startTime": "2019-04-03T09:29:00Z"
+ }
+}
diff --git a/fluent-bit/tests/runtime/data/kubernetes/meta/options_use-kubelet-enabled.meta b/fluent-bit/tests/runtime/data/kubernetes/meta/options_use-kubelet-enabled.meta
new file mode 100644
index 00000000..af9fb1a3
--- /dev/null
+++ b/fluent-bit/tests/runtime/data/kubernetes/meta/options_use-kubelet-enabled.meta
@@ -0,0 +1,109 @@
+{
+ "apiVersion": "v1",
+ "kind": "Pod",
+ "metadata": {},
+ "items": [{
+ "metadata": {
+ "annotations": {
+ "prometheus.io/path": "/api/v1/metrics/prometheus",
+ "prometheus.io/port": "2020",
+ "prometheus.io/scrape": "true"
+ },
+ "creationTimestamp": "2019-04-03T09:29:00Z",
+ "labels": {
+ "app.kubernetes.io/name": "fluent-bit"
+ },
+ "name": "use-kubelet-enabled",
+ "namespace": "options",
+ "resourceVersion": "74466568",
+ "selfLink": "/api/v1/namespaces/core/pods/base",
+ "uid": "e9f2963f-55f2-11e9-84c5-02e422b8a84a"
+ },
+ "spec": {
+ "containers": [{
+ "image": "fluent/fluent-bit",
+ "imagePullPolicy": "Always",
+ "name": "fluent-bit",
+ "resources": {},
+ "stdin": true,
+ "stdinOnce": true,
+ "terminationMessagePath": "/dev/termination-log",
+ "terminationMessagePolicy": "File",
+ "tty": true,
+ "volumeMounts": [{
+ "mountPath": "/var/run/secrets/kubernetes.io/serviceaccount",
+ "name": "default-token-9ffht",
+ "readOnly": true
+ }]
+ }],
+ "dnsPolicy": "ClusterFirst",
+ "nodeName": "ip-10-49-18-80.eu-west-1.compute.internal",
+ "restartPolicy": "Never",
+ "schedulerName": "default-scheduler",
+ "securityContext": {},
+ "serviceAccount": "default",
+ "serviceAccountName": "default",
+ "terminationGracePeriodSeconds": 30,
+ "tolerations": [{
+ "effect": "NoExecute",
+ "key": "node.kubernetes.io/not-ready",
+ "operator": "Exists",
+ "tolerationSeconds": 300
+ },
+ {
+ "effect": "NoExecute",
+ "key": "node.kubernetes.io/unreachable",
+ "operator": "Exists",
+ "tolerationSeconds": 300
+ }
+ ],
+ "volumes": [{
+ "name": "default-token-9ffht",
+ "secret": {
+ "defaultMode": 420,
+ "secretName": "default-token-9ffht"
+ }
+ }]
+ },
+ "status": {
+ "conditions": [{
+ "lastProbeTime": null,
+ "lastTransitionTime": "2019-04-03T09:29:00Z",
+ "status": "True",
+ "type": "Initialized"
+ },
+ {
+ "lastProbeTime": null,
+ "lastTransitionTime": "2019-04-03T09:29:06Z",
+ "status": "True",
+ "type": "Ready"
+ },
+ {
+ "lastProbeTime": null,
+ "lastTransitionTime": "2019-04-03T09:29:00Z",
+ "status": "True",
+ "type": "PodScheduled"
+ }
+ ],
+ "containerStatuses": [{
+ "containerID": "docker://c9898099f6d235126d564ed38a020007ea7a6fac6e25e718de683c9dd0076c16",
+ "image": "fluent/fluent-bit:latest",
+ "imageID": "docker-pullable://fluent/fluent-bit@sha256:7ac0fd3569af866e9a6a22eb592744200d2dbe098cf066162453f8d0b06c531f",
+ "lastState": {},
+ "name": "fluent-bit",
+ "ready": true,
+ "restartCount": 0,
+ "state": {
+ "running": {
+ "startedAt": "2019-04-03T09:29:05Z"
+ }
+ }
+ }],
+ "hostIP": "10.49.18.80",
+ "phase": "Running",
+ "podIP": "100.116.192.42",
+ "qosClass": "BestEffort",
+ "startTime": "2019-04-03T09:29:00Z"
+ }
+ }]
+}
diff --git a/fluent-bit/tests/runtime/data/kubernetes/out/annotations-exclude/annotations-exclude_invalid_text_stderr.out b/fluent-bit/tests/runtime/data/kubernetes/out/annotations-exclude/annotations-exclude_invalid_text_stderr.out
new file mode 100644
index 00000000..14703ca6
--- /dev/null
+++ b/fluent-bit/tests/runtime/data/kubernetes/out/annotations-exclude/annotations-exclude_invalid_text_stderr.out
@@ -0,0 +1 @@
+[1554141513.698657,{"log":"Simple text\n","stream":"stderr","kubernetes":{"pod_name":"invalid","namespace_name":"annotations-exclude","annotations":{"fluentbit.io/exclude":"neither-true-nor-false"},"container_name":"text"}}]
diff --git a/fluent-bit/tests/runtime/data/kubernetes/out/annotations-exclude/annotations-exclude_invalid_text_stdout.out b/fluent-bit/tests/runtime/data/kubernetes/out/annotations-exclude/annotations-exclude_invalid_text_stdout.out
new file mode 100644
index 00000000..66bcbba9
--- /dev/null
+++ b/fluent-bit/tests/runtime/data/kubernetes/out/annotations-exclude/annotations-exclude_invalid_text_stdout.out
@@ -0,0 +1 @@
+[1554141513.598656,{"log":"Simple text\n","stream":"stdout","kubernetes":{"pod_name":"invalid","namespace_name":"annotations-exclude","annotations":{"fluentbit.io/exclude":"neither-true-nor-false"},"container_name":"text"}}]
diff --git a/fluent-bit/tests/runtime/data/kubernetes/out/annotations-exclude/annotations-exclude_multiple-1_container-2_stderr.out b/fluent-bit/tests/runtime/data/kubernetes/out/annotations-exclude/annotations-exclude_multiple-1_container-2_stderr.out
new file mode 100644
index 00000000..8f22e1d4
--- /dev/null
+++ b/fluent-bit/tests/runtime/data/kubernetes/out/annotations-exclude/annotations-exclude_multiple-1_container-2_stderr.out
@@ -0,0 +1 @@
+[1554141513.698657,{"log":"Simple text\n","stream":"stderr","kubernetes":{"pod_name":"multiple-1","namespace_name":"annotations-exclude","annotations":{"fluentbit.io/exclude-container-1":"true","fluentbit.io/exclude_stdout-container-2":"true","fluentbit.io/exclude_stderr-container-3":"true"},"container_name":"container-2"}}]
diff --git a/fluent-bit/tests/runtime/data/kubernetes/out/annotations-exclude/annotations-exclude_multiple-1_container-3_stdout.out b/fluent-bit/tests/runtime/data/kubernetes/out/annotations-exclude/annotations-exclude_multiple-1_container-3_stdout.out
new file mode 100644
index 00000000..f534e361
--- /dev/null
+++ b/fluent-bit/tests/runtime/data/kubernetes/out/annotations-exclude/annotations-exclude_multiple-1_container-3_stdout.out
@@ -0,0 +1 @@
+[1554141513.598656,{"log":"Simple text\n","stream":"stdout","kubernetes":{"pod_name":"multiple-1","namespace_name":"annotations-exclude","annotations":{"fluentbit.io/exclude-container-1":"true","fluentbit.io/exclude_stdout-container-2":"true","fluentbit.io/exclude_stderr-container-3":"true"},"container_name":"container-3"}}]
diff --git a/fluent-bit/tests/runtime/data/kubernetes/out/annotations-exclude/annotations-exclude_multiple-1_container-4_stderr.out b/fluent-bit/tests/runtime/data/kubernetes/out/annotations-exclude/annotations-exclude_multiple-1_container-4_stderr.out
new file mode 100644
index 00000000..08ae820a
--- /dev/null
+++ b/fluent-bit/tests/runtime/data/kubernetes/out/annotations-exclude/annotations-exclude_multiple-1_container-4_stderr.out
@@ -0,0 +1 @@
+[1554141513.698657,{"log":"Simple text\n","stream":"stderr","kubernetes":{"pod_name":"multiple-1","namespace_name":"annotations-exclude","annotations":{"fluentbit.io/exclude-container-1":"true","fluentbit.io/exclude_stdout-container-2":"true","fluentbit.io/exclude_stderr-container-3":"true"},"container_name":"container-4"}}]
diff --git a/fluent-bit/tests/runtime/data/kubernetes/out/annotations-exclude/annotations-exclude_multiple-1_container-4_stdout.out b/fluent-bit/tests/runtime/data/kubernetes/out/annotations-exclude/annotations-exclude_multiple-1_container-4_stdout.out
new file mode 100644
index 00000000..236481af
--- /dev/null
+++ b/fluent-bit/tests/runtime/data/kubernetes/out/annotations-exclude/annotations-exclude_multiple-1_container-4_stdout.out
@@ -0,0 +1 @@
+[1554141513.598656,{"log":"Simple text\n","stream":"stdout","kubernetes":{"pod_name":"multiple-1","namespace_name":"annotations-exclude","annotations":{"fluentbit.io/exclude-container-1":"true","fluentbit.io/exclude_stdout-container-2":"true","fluentbit.io/exclude_stderr-container-3":"true"},"container_name":"container-4"}}]
diff --git a/fluent-bit/tests/runtime/data/kubernetes/out/annotations-exclude/annotations-exclude_multiple-2_container-2_stderr.out b/fluent-bit/tests/runtime/data/kubernetes/out/annotations-exclude/annotations-exclude_multiple-2_container-2_stderr.out
new file mode 100644
index 00000000..50520e92
--- /dev/null
+++ b/fluent-bit/tests/runtime/data/kubernetes/out/annotations-exclude/annotations-exclude_multiple-2_container-2_stderr.out
@@ -0,0 +1 @@
+[1554141513.698657,{"log":"Simple text\n","stream":"stderr","kubernetes":{"pod_name":"multiple-2","namespace_name":"annotations-exclude","annotations":{"fluentbit.io/exclude":"true","fluentbit.io/exclude_stderr-container-2":"false","fluentbit.io/exclude_stdout-container-3":"false","fluentbit.io/exclude-container-4":"false"},"container_name":"container-2"}}]
diff --git a/fluent-bit/tests/runtime/data/kubernetes/out/annotations-exclude/annotations-exclude_multiple-2_container-3_stdout.out b/fluent-bit/tests/runtime/data/kubernetes/out/annotations-exclude/annotations-exclude_multiple-2_container-3_stdout.out
new file mode 100644
index 00000000..73e7e5db
--- /dev/null
+++ b/fluent-bit/tests/runtime/data/kubernetes/out/annotations-exclude/annotations-exclude_multiple-2_container-3_stdout.out
@@ -0,0 +1 @@
+[1554141513.598656,{"log":"Simple text\n","stream":"stdout","kubernetes":{"pod_name":"multiple-2","namespace_name":"annotations-exclude","annotations":{"fluentbit.io/exclude":"true","fluentbit.io/exclude_stderr-container-2":"false","fluentbit.io/exclude_stdout-container-3":"false","fluentbit.io/exclude-container-4":"false"},"container_name":"container-3"}}]
diff --git a/fluent-bit/tests/runtime/data/kubernetes/out/annotations-exclude/annotations-exclude_multiple-2_container-4_stderr.out b/fluent-bit/tests/runtime/data/kubernetes/out/annotations-exclude/annotations-exclude_multiple-2_container-4_stderr.out
new file mode 100644
index 00000000..0b7da35c
--- /dev/null
+++ b/fluent-bit/tests/runtime/data/kubernetes/out/annotations-exclude/annotations-exclude_multiple-2_container-4_stderr.out
@@ -0,0 +1 @@
+[1554141513.698657,{"log":"Simple text\n","stream":"stderr","kubernetes":{"pod_name":"multiple-2","namespace_name":"annotations-exclude","annotations":{"fluentbit.io/exclude":"true","fluentbit.io/exclude_stderr-container-2":"false","fluentbit.io/exclude_stdout-container-3":"false","fluentbit.io/exclude-container-4":"false"},"container_name":"container-4"}}]
diff --git a/fluent-bit/tests/runtime/data/kubernetes/out/annotations-exclude/annotations-exclude_multiple-2_container-4_stdout.out b/fluent-bit/tests/runtime/data/kubernetes/out/annotations-exclude/annotations-exclude_multiple-2_container-4_stdout.out
new file mode 100644
index 00000000..427b67c7
--- /dev/null
+++ b/fluent-bit/tests/runtime/data/kubernetes/out/annotations-exclude/annotations-exclude_multiple-2_container-4_stdout.out
@@ -0,0 +1 @@
+[1554141513.598656,{"log":"Simple text\n","stream":"stdout","kubernetes":{"pod_name":"multiple-2","namespace_name":"annotations-exclude","annotations":{"fluentbit.io/exclude":"true","fluentbit.io/exclude_stderr-container-2":"false","fluentbit.io/exclude_stdout-container-3":"false","fluentbit.io/exclude-container-4":"false"},"container_name":"container-4"}}]
diff --git a/fluent-bit/tests/runtime/data/kubernetes/out/annotations-exclude/annotations-exclude_multiple-3_container-2_stderr.out b/fluent-bit/tests/runtime/data/kubernetes/out/annotations-exclude/annotations-exclude_multiple-3_container-2_stderr.out
new file mode 100644
index 00000000..de5af8a1
--- /dev/null
+++ b/fluent-bit/tests/runtime/data/kubernetes/out/annotations-exclude/annotations-exclude_multiple-3_container-2_stderr.out
@@ -0,0 +1 @@
+[1554141513.698657,{"log":"Simple text\n","stream":"stderr","kubernetes":{"pod_name":"multiple-3","namespace_name":"annotations-exclude","annotations":{"fluentbit.io/exclude_stdout":"true","fluentbit.io/exclude_stderr-container-1":"true","fluentbit.io/exclude_stdout-container-3":"false","fluentbit.io/exclude_stderr-container-3":"true","fluentbit.io/exclude_stdout-container-4":"false"},"container_name":"container-2"}}]
diff --git a/fluent-bit/tests/runtime/data/kubernetes/out/annotations-exclude/annotations-exclude_multiple-3_container-3_stdout.out b/fluent-bit/tests/runtime/data/kubernetes/out/annotations-exclude/annotations-exclude_multiple-3_container-3_stdout.out
new file mode 100644
index 00000000..de7d598e
--- /dev/null
+++ b/fluent-bit/tests/runtime/data/kubernetes/out/annotations-exclude/annotations-exclude_multiple-3_container-3_stdout.out
@@ -0,0 +1 @@
+[1554141513.598656,{"log":"Simple text\n","stream":"stdout","kubernetes":{"pod_name":"multiple-3","namespace_name":"annotations-exclude","annotations":{"fluentbit.io/exclude_stdout":"true","fluentbit.io/exclude_stderr-container-1":"true","fluentbit.io/exclude_stdout-container-3":"false","fluentbit.io/exclude_stderr-container-3":"true","fluentbit.io/exclude_stdout-container-4":"false"},"container_name":"container-3"}}]
diff --git a/fluent-bit/tests/runtime/data/kubernetes/out/annotations-exclude/annotations-exclude_multiple-3_container-4_stderr.out b/fluent-bit/tests/runtime/data/kubernetes/out/annotations-exclude/annotations-exclude_multiple-3_container-4_stderr.out
new file mode 100644
index 00000000..cff4f21e
--- /dev/null
+++ b/fluent-bit/tests/runtime/data/kubernetes/out/annotations-exclude/annotations-exclude_multiple-3_container-4_stderr.out
@@ -0,0 +1 @@
+[1554141513.698657,{"log":"Simple text\n","stream":"stderr","kubernetes":{"pod_name":"multiple-3","namespace_name":"annotations-exclude","annotations":{"fluentbit.io/exclude_stdout":"true","fluentbit.io/exclude_stderr-container-1":"true","fluentbit.io/exclude_stdout-container-3":"false","fluentbit.io/exclude_stderr-container-3":"true","fluentbit.io/exclude_stdout-container-4":"false"},"container_name":"container-4"}}]
diff --git a/fluent-bit/tests/runtime/data/kubernetes/out/annotations-exclude/annotations-exclude_multiple-3_container-4_stdout.out b/fluent-bit/tests/runtime/data/kubernetes/out/annotations-exclude/annotations-exclude_multiple-3_container-4_stdout.out
new file mode 100644
index 00000000..0329d218
--- /dev/null
+++ b/fluent-bit/tests/runtime/data/kubernetes/out/annotations-exclude/annotations-exclude_multiple-3_container-4_stdout.out
@@ -0,0 +1 @@
+[1554141513.598656,{"log":"Simple text\n","stream":"stdout","kubernetes":{"pod_name":"multiple-3","namespace_name":"annotations-exclude","annotations":{"fluentbit.io/exclude_stdout":"true","fluentbit.io/exclude_stderr-container-1":"true","fluentbit.io/exclude_stdout-container-3":"false","fluentbit.io/exclude_stderr-container-3":"true","fluentbit.io/exclude_stdout-container-4":"false"},"container_name":"container-4"}}]
diff --git a/fluent-bit/tests/runtime/data/kubernetes/out/annotations-exclude/annotations-exclude_multiple-4_container-2_stderr.out b/fluent-bit/tests/runtime/data/kubernetes/out/annotations-exclude/annotations-exclude_multiple-4_container-2_stderr.out
new file mode 100644
index 00000000..ad9fa8d4
--- /dev/null
+++ b/fluent-bit/tests/runtime/data/kubernetes/out/annotations-exclude/annotations-exclude_multiple-4_container-2_stderr.out
@@ -0,0 +1 @@
+[1554141513.698657,{"log":"Simple text\n","stream":"stderr","kubernetes":{"pod_name":"multiple-4","namespace_name":"annotations-exclude","annotations":{"fluentbit.io/exclude_stderr":"true","fluentbit.io/exclude_stdout-container-1":"true","fluentbit.io/exclude_stdout-container-2":"true","fluentbit.io/exclude_stderr-container-2":"false","fluentbit.io/exclude_stderr-container-4":"false"},"container_name":"container-2"}}]
diff --git a/fluent-bit/tests/runtime/data/kubernetes/out/annotations-exclude/annotations-exclude_multiple-4_container-3_stdout.out b/fluent-bit/tests/runtime/data/kubernetes/out/annotations-exclude/annotations-exclude_multiple-4_container-3_stdout.out
new file mode 100644
index 00000000..730abcc7
--- /dev/null
+++ b/fluent-bit/tests/runtime/data/kubernetes/out/annotations-exclude/annotations-exclude_multiple-4_container-3_stdout.out
@@ -0,0 +1 @@
+[1554141513.598656,{"log":"Simple text\n","stream":"stdout","kubernetes":{"pod_name":"multiple-4","namespace_name":"annotations-exclude","annotations":{"fluentbit.io/exclude_stderr":"true","fluentbit.io/exclude_stdout-container-1":"true","fluentbit.io/exclude_stdout-container-2":"true","fluentbit.io/exclude_stderr-container-2":"false","fluentbit.io/exclude_stderr-container-4":"false"},"container_name":"container-3"}}]
diff --git a/fluent-bit/tests/runtime/data/kubernetes/out/annotations-exclude/annotations-exclude_multiple-4_container-4_stderr.out b/fluent-bit/tests/runtime/data/kubernetes/out/annotations-exclude/annotations-exclude_multiple-4_container-4_stderr.out
new file mode 100644
index 00000000..22865762
--- /dev/null
+++ b/fluent-bit/tests/runtime/data/kubernetes/out/annotations-exclude/annotations-exclude_multiple-4_container-4_stderr.out
@@ -0,0 +1 @@
+[1554141513.698657,{"log":"Simple text\n","stream":"stderr","kubernetes":{"pod_name":"multiple-4","namespace_name":"annotations-exclude","annotations":{"fluentbit.io/exclude_stderr":"true","fluentbit.io/exclude_stdout-container-1":"true","fluentbit.io/exclude_stdout-container-2":"true","fluentbit.io/exclude_stderr-container-2":"false","fluentbit.io/exclude_stderr-container-4":"false"},"container_name":"container-4"}}]
diff --git a/fluent-bit/tests/runtime/data/kubernetes/out/annotations-exclude/annotations-exclude_multiple-4_container-4_stdout.out b/fluent-bit/tests/runtime/data/kubernetes/out/annotations-exclude/annotations-exclude_multiple-4_container-4_stdout.out
new file mode 100644
index 00000000..75b6ae3a
--- /dev/null
+++ b/fluent-bit/tests/runtime/data/kubernetes/out/annotations-exclude/annotations-exclude_multiple-4_container-4_stdout.out
@@ -0,0 +1 @@
+[1554141513.598656,{"log":"Simple text\n","stream":"stdout","kubernetes":{"pod_name":"multiple-4","namespace_name":"annotations-exclude","annotations":{"fluentbit.io/exclude_stderr":"true","fluentbit.io/exclude_stdout-container-1":"true","fluentbit.io/exclude_stdout-container-2":"true","fluentbit.io/exclude_stderr-container-2":"false","fluentbit.io/exclude_stderr-container-4":"false"},"container_name":"container-4"}}]
diff --git a/fluent-bit/tests/runtime/data/kubernetes/out/annotations-exclude/annotations-exclude_stderr_text_stdout.out b/fluent-bit/tests/runtime/data/kubernetes/out/annotations-exclude/annotations-exclude_stderr_text_stdout.out
new file mode 100644
index 00000000..0e237f7a
--- /dev/null
+++ b/fluent-bit/tests/runtime/data/kubernetes/out/annotations-exclude/annotations-exclude_stderr_text_stdout.out
@@ -0,0 +1 @@
+[1554141513.598656,{"log":"Simple text\n","stream":"stdout","kubernetes":{"pod_name":"stderr","namespace_name":"annotations-exclude","annotations":{"fluentbit.io/exclude_stderr":"true"},"container_name":"text"}}]
diff --git a/fluent-bit/tests/runtime/data/kubernetes/out/annotations-exclude/annotations-exclude_stdout_text_stderr.out b/fluent-bit/tests/runtime/data/kubernetes/out/annotations-exclude/annotations-exclude_stdout_text_stderr.out
new file mode 100644
index 00000000..8204d04d
--- /dev/null
+++ b/fluent-bit/tests/runtime/data/kubernetes/out/annotations-exclude/annotations-exclude_stdout_text_stderr.out
@@ -0,0 +1 @@
+[1554141513.698657,{"log":"Simple text\n","stream":"stderr","kubernetes":{"pod_name":"stdout","namespace_name":"annotations-exclude","annotations":{"fluentbit.io/exclude_stdout":"true"},"container_name":"text"}}]
diff --git a/fluent-bit/tests/runtime/data/kubernetes/out/annotations-parser/annotations-parser_invalid_text_stderr.out b/fluent-bit/tests/runtime/data/kubernetes/out/annotations-parser/annotations-parser_invalid_text_stderr.out
new file mode 100644
index 00000000..dbb0d7a0
--- /dev/null
+++ b/fluent-bit/tests/runtime/data/kubernetes/out/annotations-parser/annotations-parser_invalid_text_stderr.out
@@ -0,0 +1 @@
+[1554141513.698657,{"log":"Simple text\n","stream":"stderr","kubernetes":{"pod_name":"invalid","namespace_name":"annotations-parser","annotations":{"fluentbit.io/parser":"invalid-parser"},"container_name":"text"}}]
diff --git a/fluent-bit/tests/runtime/data/kubernetes/out/annotations-parser/annotations-parser_invalid_text_stdout.out b/fluent-bit/tests/runtime/data/kubernetes/out/annotations-parser/annotations-parser_invalid_text_stdout.out
new file mode 100644
index 00000000..5bb03765
--- /dev/null
+++ b/fluent-bit/tests/runtime/data/kubernetes/out/annotations-parser/annotations-parser_invalid_text_stdout.out
@@ -0,0 +1 @@
+[1554141513.598656,{"log":"Simple text\n","stream":"stdout","kubernetes":{"pod_name":"invalid","namespace_name":"annotations-parser","annotations":{"fluentbit.io/parser":"invalid-parser"},"container_name":"text"}}]
diff --git a/fluent-bit/tests/runtime/data/kubernetes/out/annotations-parser/annotations-parser_json-with-time_invalid-json-1.out b/fluent-bit/tests/runtime/data/kubernetes/out/annotations-parser/annotations-parser_json-with-time_invalid-json-1.out
new file mode 100644
index 00000000..16835827
--- /dev/null
+++ b/fluent-bit/tests/runtime/data/kubernetes/out/annotations-parser/annotations-parser_json-with-time_invalid-json-1.out
@@ -0,0 +1 @@
+[1554141513.598656,{"log":"{\"time\":\"2019-04-05T14:54:23.584269761\",\"message\":\"Simple text\"]\n","stream":"stdout","kubernetes":{"pod_name":"json-with-time","namespace_name":"annotations-parser","annotations":{"fluentbit.io/parser":"json-parser-with-time"},"container_name":"invalid-json-1"}}]
diff --git a/fluent-bit/tests/runtime/data/kubernetes/out/annotations-parser/annotations-parser_json-with-time_json.out b/fluent-bit/tests/runtime/data/kubernetes/out/annotations-parser/annotations-parser_json-with-time_json.out
new file mode 100644
index 00000000..2a472dad
--- /dev/null
+++ b/fluent-bit/tests/runtime/data/kubernetes/out/annotations-parser/annotations-parser_json-with-time_json.out
@@ -0,0 +1 @@
+[1554476063.584270,{"stream":"stdout","message":"Simple text","kubernetes":{"pod_name":"json-with-time","namespace_name":"annotations-parser","annotations":{"fluentbit.io/parser":"json-parser-with-time"},"container_name":"json"}}]
diff --git a/fluent-bit/tests/runtime/data/kubernetes/out/annotations-parser/annotations-parser_multiple-1_container-1_stderr.out b/fluent-bit/tests/runtime/data/kubernetes/out/annotations-parser/annotations-parser_multiple-1_container-1_stderr.out
new file mode 100644
index 00000000..937c3add
--- /dev/null
+++ b/fluent-bit/tests/runtime/data/kubernetes/out/annotations-parser/annotations-parser_multiple-1_container-1_stderr.out
@@ -0,0 +1 @@
+[1554476063.584270,{"stream":"stderr","container_1_parser":"Container 1 is logging on stderr","kubernetes":{"pod_name":"multiple-1","namespace_name":"annotations-parser","annotations":{"fluentbit.io/parser-container-1":"container-1-parser","fluentbit.io/parser_stdout-container-2":"container-2-stdout-parser","fluentbit.io/parser_stderr-container-2":"container-2-stderr-parser","fluentbit.io/parser_stdout-container-3":"container-3-stdout-parser","fluentbit.io/parser_stderr-container-4":"container-4-stderr-parser","fluentbit.io/parser":"default-parser"},"container_name":"container-1"}}]
diff --git a/fluent-bit/tests/runtime/data/kubernetes/out/annotations-parser/annotations-parser_multiple-1_container-1_stdout.out b/fluent-bit/tests/runtime/data/kubernetes/out/annotations-parser/annotations-parser_multiple-1_container-1_stdout.out
new file mode 100644
index 00000000..c672970c
--- /dev/null
+++ b/fluent-bit/tests/runtime/data/kubernetes/out/annotations-parser/annotations-parser_multiple-1_container-1_stdout.out
@@ -0,0 +1 @@
+[1554476063.584270,{"stream":"stdout","container_1_parser":"Container 1 is logging on stdout","kubernetes":{"pod_name":"multiple-1","namespace_name":"annotations-parser","annotations":{"fluentbit.io/parser-container-1":"container-1-parser","fluentbit.io/parser_stdout-container-2":"container-2-stdout-parser","fluentbit.io/parser_stderr-container-2":"container-2-stderr-parser","fluentbit.io/parser_stdout-container-3":"container-3-stdout-parser","fluentbit.io/parser_stderr-container-4":"container-4-stderr-parser","fluentbit.io/parser":"default-parser"},"container_name":"container-1"}}]
diff --git a/fluent-bit/tests/runtime/data/kubernetes/out/annotations-parser/annotations-parser_multiple-1_container-2_stderr.out b/fluent-bit/tests/runtime/data/kubernetes/out/annotations-parser/annotations-parser_multiple-1_container-2_stderr.out
new file mode 100644
index 00000000..02f9ce66
--- /dev/null
+++ b/fluent-bit/tests/runtime/data/kubernetes/out/annotations-parser/annotations-parser_multiple-1_container-2_stderr.out
@@ -0,0 +1 @@
+[1554476063.584270,{"stream":"stderr","container_2_stderr_parser":"Container 2 is logging on stderr","kubernetes":{"pod_name":"multiple-1","namespace_name":"annotations-parser","annotations":{"fluentbit.io/parser-container-1":"container-1-parser","fluentbit.io/parser_stdout-container-2":"container-2-stdout-parser","fluentbit.io/parser_stderr-container-2":"container-2-stderr-parser","fluentbit.io/parser_stdout-container-3":"container-3-stdout-parser","fluentbit.io/parser_stderr-container-4":"container-4-stderr-parser","fluentbit.io/parser":"default-parser"},"container_name":"container-2"}}]
diff --git a/fluent-bit/tests/runtime/data/kubernetes/out/annotations-parser/annotations-parser_multiple-1_container-2_stdout.out b/fluent-bit/tests/runtime/data/kubernetes/out/annotations-parser/annotations-parser_multiple-1_container-2_stdout.out
new file mode 100644
index 00000000..9d3d358c
--- /dev/null
+++ b/fluent-bit/tests/runtime/data/kubernetes/out/annotations-parser/annotations-parser_multiple-1_container-2_stdout.out
@@ -0,0 +1 @@
+[1554476063.584270,{"stream":"stdout","container_2_stdout_parser":"Container 2 is logging on stdout","kubernetes":{"pod_name":"multiple-1","namespace_name":"annotations-parser","annotations":{"fluentbit.io/parser-container-1":"container-1-parser","fluentbit.io/parser_stdout-container-2":"container-2-stdout-parser","fluentbit.io/parser_stderr-container-2":"container-2-stderr-parser","fluentbit.io/parser_stdout-container-3":"container-3-stdout-parser","fluentbit.io/parser_stderr-container-4":"container-4-stderr-parser","fluentbit.io/parser":"default-parser"},"container_name":"container-2"}}]
diff --git a/fluent-bit/tests/runtime/data/kubernetes/out/annotations-parser/annotations-parser_multiple-1_container-3_stderr.out b/fluent-bit/tests/runtime/data/kubernetes/out/annotations-parser/annotations-parser_multiple-1_container-3_stderr.out
new file mode 100644
index 00000000..3908d07e
--- /dev/null
+++ b/fluent-bit/tests/runtime/data/kubernetes/out/annotations-parser/annotations-parser_multiple-1_container-3_stderr.out
@@ -0,0 +1 @@
+[1554476063.584270,{"stream":"stderr","default_parser":"Container 3 is logging on stderr","kubernetes":{"pod_name":"multiple-1","namespace_name":"annotations-parser","annotations":{"fluentbit.io/parser-container-1":"container-1-parser","fluentbit.io/parser_stdout-container-2":"container-2-stdout-parser","fluentbit.io/parser_stderr-container-2":"container-2-stderr-parser","fluentbit.io/parser_stdout-container-3":"container-3-stdout-parser","fluentbit.io/parser_stderr-container-4":"container-4-stderr-parser","fluentbit.io/parser":"default-parser"},"container_name":"container-3"}}]
diff --git a/fluent-bit/tests/runtime/data/kubernetes/out/annotations-parser/annotations-parser_multiple-1_container-3_stdout.out b/fluent-bit/tests/runtime/data/kubernetes/out/annotations-parser/annotations-parser_multiple-1_container-3_stdout.out
new file mode 100644
index 00000000..77ca2ce0
--- /dev/null
+++ b/fluent-bit/tests/runtime/data/kubernetes/out/annotations-parser/annotations-parser_multiple-1_container-3_stdout.out
@@ -0,0 +1 @@
+[1554476063.584270,{"stream":"stdout","container_3_stdout_parser":"Container 3 is logging on stdout","kubernetes":{"pod_name":"multiple-1","namespace_name":"annotations-parser","annotations":{"fluentbit.io/parser-container-1":"container-1-parser","fluentbit.io/parser_stdout-container-2":"container-2-stdout-parser","fluentbit.io/parser_stderr-container-2":"container-2-stderr-parser","fluentbit.io/parser_stdout-container-3":"container-3-stdout-parser","fluentbit.io/parser_stderr-container-4":"container-4-stderr-parser","fluentbit.io/parser":"default-parser"},"container_name":"container-3"}}]
diff --git a/fluent-bit/tests/runtime/data/kubernetes/out/annotations-parser/annotations-parser_multiple-1_container-4_stderr.out b/fluent-bit/tests/runtime/data/kubernetes/out/annotations-parser/annotations-parser_multiple-1_container-4_stderr.out
new file mode 100644
index 00000000..f3752f59
--- /dev/null
+++ b/fluent-bit/tests/runtime/data/kubernetes/out/annotations-parser/annotations-parser_multiple-1_container-4_stderr.out
@@ -0,0 +1 @@
+[1554476063.584270,{"stream":"stderr","container_4_stderr_parser":"Container 4 is logging on stderr","kubernetes":{"pod_name":"multiple-1","namespace_name":"annotations-parser","annotations":{"fluentbit.io/parser-container-1":"container-1-parser","fluentbit.io/parser_stdout-container-2":"container-2-stdout-parser","fluentbit.io/parser_stderr-container-2":"container-2-stderr-parser","fluentbit.io/parser_stdout-container-3":"container-3-stdout-parser","fluentbit.io/parser_stderr-container-4":"container-4-stderr-parser","fluentbit.io/parser":"default-parser"},"container_name":"container-4"}}]
diff --git a/fluent-bit/tests/runtime/data/kubernetes/out/annotations-parser/annotations-parser_multiple-1_container-4_stdout.out b/fluent-bit/tests/runtime/data/kubernetes/out/annotations-parser/annotations-parser_multiple-1_container-4_stdout.out
new file mode 100644
index 00000000..bf81796a
--- /dev/null
+++ b/fluent-bit/tests/runtime/data/kubernetes/out/annotations-parser/annotations-parser_multiple-1_container-4_stdout.out
@@ -0,0 +1 @@
+[1554476063.584270,{"stream":"stdout","default_parser":"Container 4 is logging on stdout","kubernetes":{"pod_name":"multiple-1","namespace_name":"annotations-parser","annotations":{"fluentbit.io/parser-container-1":"container-1-parser","fluentbit.io/parser_stdout-container-2":"container-2-stdout-parser","fluentbit.io/parser_stderr-container-2":"container-2-stderr-parser","fluentbit.io/parser_stdout-container-3":"container-3-stdout-parser","fluentbit.io/parser_stderr-container-4":"container-4-stderr-parser","fluentbit.io/parser":"default-parser"},"container_name":"container-4"}}]
diff --git a/fluent-bit/tests/runtime/data/kubernetes/out/annotations-parser/annotations-parser_multiple-1_container-5_stderr.out b/fluent-bit/tests/runtime/data/kubernetes/out/annotations-parser/annotations-parser_multiple-1_container-5_stderr.out
new file mode 100644
index 00000000..b633eb0e
--- /dev/null
+++ b/fluent-bit/tests/runtime/data/kubernetes/out/annotations-parser/annotations-parser_multiple-1_container-5_stderr.out
@@ -0,0 +1 @@
+[1554476063.584270,{"stream":"stderr","default_parser":"Container 5 is logging on stderr","kubernetes":{"pod_name":"multiple-1","namespace_name":"annotations-parser","annotations":{"fluentbit.io/parser-container-1":"container-1-parser","fluentbit.io/parser_stdout-container-2":"container-2-stdout-parser","fluentbit.io/parser_stderr-container-2":"container-2-stderr-parser","fluentbit.io/parser_stdout-container-3":"container-3-stdout-parser","fluentbit.io/parser_stderr-container-4":"container-4-stderr-parser","fluentbit.io/parser":"default-parser"},"container_name":"container-5"}}]
diff --git a/fluent-bit/tests/runtime/data/kubernetes/out/annotations-parser/annotations-parser_multiple-1_container-5_stdout.out b/fluent-bit/tests/runtime/data/kubernetes/out/annotations-parser/annotations-parser_multiple-1_container-5_stdout.out
new file mode 100644
index 00000000..f6137799
--- /dev/null
+++ b/fluent-bit/tests/runtime/data/kubernetes/out/annotations-parser/annotations-parser_multiple-1_container-5_stdout.out
@@ -0,0 +1 @@
+[1554476063.584270,{"stream":"stdout","default_parser":"Container 5 is logging on stdout","kubernetes":{"pod_name":"multiple-1","namespace_name":"annotations-parser","annotations":{"fluentbit.io/parser-container-1":"container-1-parser","fluentbit.io/parser_stdout-container-2":"container-2-stdout-parser","fluentbit.io/parser_stderr-container-2":"container-2-stderr-parser","fluentbit.io/parser_stdout-container-3":"container-3-stdout-parser","fluentbit.io/parser_stderr-container-4":"container-4-stderr-parser","fluentbit.io/parser":"default-parser"},"container_name":"container-5"}}]
diff --git a/fluent-bit/tests/runtime/data/kubernetes/out/annotations-parser/annotations-parser_multiple-2_container-1_stderr.out b/fluent-bit/tests/runtime/data/kubernetes/out/annotations-parser/annotations-parser_multiple-2_container-1_stderr.out
new file mode 100644
index 00000000..d4d401d0
--- /dev/null
+++ b/fluent-bit/tests/runtime/data/kubernetes/out/annotations-parser/annotations-parser_multiple-2_container-1_stderr.out
@@ -0,0 +1 @@
+[1554476063.584270,{"stream":"stderr","container_1_parser":"Container 1 is logging on stderr","kubernetes":{"pod_name":"multiple-2","namespace_name":"annotations-parser","annotations":{"fluentbit.io/parser-container-1":"container-1-parser","fluentbit.io/parser_stdout-container-2":"container-2-stdout-parser","fluentbit.io/parser_stderr-container-2":"container-2-stderr-parser","fluentbit.io/parser_stdout-container-3":"container-3-stdout-parser","fluentbit.io/parser_stderr-container-4":"container-4-stderr-parser","fluentbit.io/parser_stdout":"default-stdout-parser","fluentbit.io/parser_stderr":"default-stderr-parser"},"container_name":"container-1"}}]
diff --git a/fluent-bit/tests/runtime/data/kubernetes/out/annotations-parser/annotations-parser_multiple-2_container-1_stdout.out b/fluent-bit/tests/runtime/data/kubernetes/out/annotations-parser/annotations-parser_multiple-2_container-1_stdout.out
new file mode 100644
index 00000000..fc0de55c
--- /dev/null
+++ b/fluent-bit/tests/runtime/data/kubernetes/out/annotations-parser/annotations-parser_multiple-2_container-1_stdout.out
@@ -0,0 +1 @@
+[1554476063.584270,{"stream":"stdout","container_1_parser":"Container 1 is logging on stdout","kubernetes":{"pod_name":"multiple-2","namespace_name":"annotations-parser","annotations":{"fluentbit.io/parser-container-1":"container-1-parser","fluentbit.io/parser_stdout-container-2":"container-2-stdout-parser","fluentbit.io/parser_stderr-container-2":"container-2-stderr-parser","fluentbit.io/parser_stdout-container-3":"container-3-stdout-parser","fluentbit.io/parser_stderr-container-4":"container-4-stderr-parser","fluentbit.io/parser_stdout":"default-stdout-parser","fluentbit.io/parser_stderr":"default-stderr-parser"},"container_name":"container-1"}}]
diff --git a/fluent-bit/tests/runtime/data/kubernetes/out/annotations-parser/annotations-parser_multiple-2_container-2_stderr.out b/fluent-bit/tests/runtime/data/kubernetes/out/annotations-parser/annotations-parser_multiple-2_container-2_stderr.out
new file mode 100644
index 00000000..38214f3c
--- /dev/null
+++ b/fluent-bit/tests/runtime/data/kubernetes/out/annotations-parser/annotations-parser_multiple-2_container-2_stderr.out
@@ -0,0 +1 @@
+[1554476063.584270,{"stream":"stderr","container_2_stderr_parser":"Container 2 is logging on stderr","kubernetes":{"pod_name":"multiple-2","namespace_name":"annotations-parser","annotations":{"fluentbit.io/parser-container-1":"container-1-parser","fluentbit.io/parser_stdout-container-2":"container-2-stdout-parser","fluentbit.io/parser_stderr-container-2":"container-2-stderr-parser","fluentbit.io/parser_stdout-container-3":"container-3-stdout-parser","fluentbit.io/parser_stderr-container-4":"container-4-stderr-parser","fluentbit.io/parser_stdout":"default-stdout-parser","fluentbit.io/parser_stderr":"default-stderr-parser"},"container_name":"container-2"}}]
diff --git a/fluent-bit/tests/runtime/data/kubernetes/out/annotations-parser/annotations-parser_multiple-2_container-2_stdout.out b/fluent-bit/tests/runtime/data/kubernetes/out/annotations-parser/annotations-parser_multiple-2_container-2_stdout.out
new file mode 100644
index 00000000..6c4dc1d7
--- /dev/null
+++ b/fluent-bit/tests/runtime/data/kubernetes/out/annotations-parser/annotations-parser_multiple-2_container-2_stdout.out
@@ -0,0 +1 @@
+[1554476063.584270,{"stream":"stdout","container_2_stdout_parser":"Container 2 is logging on stdout","kubernetes":{"pod_name":"multiple-2","namespace_name":"annotations-parser","annotations":{"fluentbit.io/parser-container-1":"container-1-parser","fluentbit.io/parser_stdout-container-2":"container-2-stdout-parser","fluentbit.io/parser_stderr-container-2":"container-2-stderr-parser","fluentbit.io/parser_stdout-container-3":"container-3-stdout-parser","fluentbit.io/parser_stderr-container-4":"container-4-stderr-parser","fluentbit.io/parser_stdout":"default-stdout-parser","fluentbit.io/parser_stderr":"default-stderr-parser"},"container_name":"container-2"}}]
diff --git a/fluent-bit/tests/runtime/data/kubernetes/out/annotations-parser/annotations-parser_multiple-2_container-3_stderr.out b/fluent-bit/tests/runtime/data/kubernetes/out/annotations-parser/annotations-parser_multiple-2_container-3_stderr.out
new file mode 100644
index 00000000..8d5e8cf7
--- /dev/null
+++ b/fluent-bit/tests/runtime/data/kubernetes/out/annotations-parser/annotations-parser_multiple-2_container-3_stderr.out
@@ -0,0 +1 @@
+[1554476063.584270,{"stream":"stderr","default_stderr_parser":"Container 3 is logging on stderr","kubernetes":{"pod_name":"multiple-2","namespace_name":"annotations-parser","annotations":{"fluentbit.io/parser-container-1":"container-1-parser","fluentbit.io/parser_stdout-container-2":"container-2-stdout-parser","fluentbit.io/parser_stderr-container-2":"container-2-stderr-parser","fluentbit.io/parser_stdout-container-3":"container-3-stdout-parser","fluentbit.io/parser_stderr-container-4":"container-4-stderr-parser","fluentbit.io/parser_stdout":"default-stdout-parser","fluentbit.io/parser_stderr":"default-stderr-parser"},"container_name":"container-3"}}]
diff --git a/fluent-bit/tests/runtime/data/kubernetes/out/annotations-parser/annotations-parser_multiple-2_container-3_stdout.out b/fluent-bit/tests/runtime/data/kubernetes/out/annotations-parser/annotations-parser_multiple-2_container-3_stdout.out
new file mode 100644
index 00000000..1371805e
--- /dev/null
+++ b/fluent-bit/tests/runtime/data/kubernetes/out/annotations-parser/annotations-parser_multiple-2_container-3_stdout.out
@@ -0,0 +1 @@
+[1554476063.584270,{"stream":"stdout","container_3_stdout_parser":"Container 3 is logging on stdout","kubernetes":{"pod_name":"multiple-2","namespace_name":"annotations-parser","annotations":{"fluentbit.io/parser-container-1":"container-1-parser","fluentbit.io/parser_stdout-container-2":"container-2-stdout-parser","fluentbit.io/parser_stderr-container-2":"container-2-stderr-parser","fluentbit.io/parser_stdout-container-3":"container-3-stdout-parser","fluentbit.io/parser_stderr-container-4":"container-4-stderr-parser","fluentbit.io/parser_stdout":"default-stdout-parser","fluentbit.io/parser_stderr":"default-stderr-parser"},"container_name":"container-3"}}]
diff --git a/fluent-bit/tests/runtime/data/kubernetes/out/annotations-parser/annotations-parser_multiple-2_container-4_stderr.out b/fluent-bit/tests/runtime/data/kubernetes/out/annotations-parser/annotations-parser_multiple-2_container-4_stderr.out
new file mode 100644
index 00000000..bca461e5
--- /dev/null
+++ b/fluent-bit/tests/runtime/data/kubernetes/out/annotations-parser/annotations-parser_multiple-2_container-4_stderr.out
@@ -0,0 +1 @@
+[1554476063.584270,{"stream":"stderr","container_4_stderr_parser":"Container 4 is logging on stderr","kubernetes":{"pod_name":"multiple-2","namespace_name":"annotations-parser","annotations":{"fluentbit.io/parser-container-1":"container-1-parser","fluentbit.io/parser_stdout-container-2":"container-2-stdout-parser","fluentbit.io/parser_stderr-container-2":"container-2-stderr-parser","fluentbit.io/parser_stdout-container-3":"container-3-stdout-parser","fluentbit.io/parser_stderr-container-4":"container-4-stderr-parser","fluentbit.io/parser_stdout":"default-stdout-parser","fluentbit.io/parser_stderr":"default-stderr-parser"},"container_name":"container-4"}}]
diff --git a/fluent-bit/tests/runtime/data/kubernetes/out/annotations-parser/annotations-parser_multiple-2_container-4_stdout.out b/fluent-bit/tests/runtime/data/kubernetes/out/annotations-parser/annotations-parser_multiple-2_container-4_stdout.out
new file mode 100644
index 00000000..79aa4d99
--- /dev/null
+++ b/fluent-bit/tests/runtime/data/kubernetes/out/annotations-parser/annotations-parser_multiple-2_container-4_stdout.out
@@ -0,0 +1 @@
+[1554476063.584270,{"stream":"stdout","default_stdout_parser":"Container 4 is logging on stdout","kubernetes":{"pod_name":"multiple-2","namespace_name":"annotations-parser","annotations":{"fluentbit.io/parser-container-1":"container-1-parser","fluentbit.io/parser_stdout-container-2":"container-2-stdout-parser","fluentbit.io/parser_stderr-container-2":"container-2-stderr-parser","fluentbit.io/parser_stdout-container-3":"container-3-stdout-parser","fluentbit.io/parser_stderr-container-4":"container-4-stderr-parser","fluentbit.io/parser_stdout":"default-stdout-parser","fluentbit.io/parser_stderr":"default-stderr-parser"},"container_name":"container-4"}}]
diff --git a/fluent-bit/tests/runtime/data/kubernetes/out/annotations-parser/annotations-parser_multiple-2_container-5_stderr.out b/fluent-bit/tests/runtime/data/kubernetes/out/annotations-parser/annotations-parser_multiple-2_container-5_stderr.out
new file mode 100644
index 00000000..860dbeba
--- /dev/null
+++ b/fluent-bit/tests/runtime/data/kubernetes/out/annotations-parser/annotations-parser_multiple-2_container-5_stderr.out
@@ -0,0 +1 @@
+[1554476063.584270,{"stream":"stderr","default_stderr_parser":"Container 5 is logging on stderr","kubernetes":{"pod_name":"multiple-2","namespace_name":"annotations-parser","annotations":{"fluentbit.io/parser-container-1":"container-1-parser","fluentbit.io/parser_stdout-container-2":"container-2-stdout-parser","fluentbit.io/parser_stderr-container-2":"container-2-stderr-parser","fluentbit.io/parser_stdout-container-3":"container-3-stdout-parser","fluentbit.io/parser_stderr-container-4":"container-4-stderr-parser","fluentbit.io/parser_stdout":"default-stdout-parser","fluentbit.io/parser_stderr":"default-stderr-parser"},"container_name":"container-5"}}]
diff --git a/fluent-bit/tests/runtime/data/kubernetes/out/annotations-parser/annotations-parser_multiple-2_container-5_stdout.out b/fluent-bit/tests/runtime/data/kubernetes/out/annotations-parser/annotations-parser_multiple-2_container-5_stdout.out
new file mode 100644
index 00000000..141fc422
--- /dev/null
+++ b/fluent-bit/tests/runtime/data/kubernetes/out/annotations-parser/annotations-parser_multiple-2_container-5_stdout.out
@@ -0,0 +1 @@
+[1554476063.584270,{"stream":"stdout","default_stdout_parser":"Container 5 is logging on stdout","kubernetes":{"pod_name":"multiple-2","namespace_name":"annotations-parser","annotations":{"fluentbit.io/parser-container-1":"container-1-parser","fluentbit.io/parser_stdout-container-2":"container-2-stdout-parser","fluentbit.io/parser_stderr-container-2":"container-2-stderr-parser","fluentbit.io/parser_stdout-container-3":"container-3-stdout-parser","fluentbit.io/parser_stderr-container-4":"container-4-stderr-parser","fluentbit.io/parser_stdout":"default-stdout-parser","fluentbit.io/parser_stderr":"default-stderr-parser"},"container_name":"container-5"}}]
diff --git a/fluent-bit/tests/runtime/data/kubernetes/out/annotations-parser/annotations-parser_regex-with-time_invalid-text-1.out b/fluent-bit/tests/runtime/data/kubernetes/out/annotations-parser/annotations-parser_regex-with-time_invalid-text-1.out
new file mode 100644
index 00000000..a563c4e4
--- /dev/null
+++ b/fluent-bit/tests/runtime/data/kubernetes/out/annotations-parser/annotations-parser_regex-with-time_invalid-text-1.out
@@ -0,0 +1 @@
+[1554141513.598656,{"log":"2019-04-05T14:54:23.584269761 - Simple text\n","stream":"stdout","kubernetes":{"pod_name":"regex-with-time","namespace_name":"annotations-parser","annotations":{"fluentbit.io/parser":"regex-parser-with-time"},"container_name":"invalid-text-1"}}]
diff --git a/fluent-bit/tests/runtime/data/kubernetes/out/annotations-parser/annotations-parser_regex-with-time_text.out b/fluent-bit/tests/runtime/data/kubernetes/out/annotations-parser/annotations-parser_regex-with-time_text.out
new file mode 100644
index 00000000..dad7fcda
--- /dev/null
+++ b/fluent-bit/tests/runtime/data/kubernetes/out/annotations-parser/annotations-parser_regex-with-time_text.out
@@ -0,0 +1 @@
+[1554476063.584270,{"stream":"stdout","message":"Simple text","kubernetes":{"pod_name":"regex-with-time","namespace_name":"annotations-parser","annotations":{"fluentbit.io/parser":"regex-parser-with-time"},"container_name":"text"}}]
diff --git a/fluent-bit/tests/runtime/data/kubernetes/out/annotations-parser/annotations-parser_stderr_text_stderr.out b/fluent-bit/tests/runtime/data/kubernetes/out/annotations-parser/annotations-parser_stderr_text_stderr.out
new file mode 100644
index 00000000..85954f7b
--- /dev/null
+++ b/fluent-bit/tests/runtime/data/kubernetes/out/annotations-parser/annotations-parser_stderr_text_stderr.out
@@ -0,0 +1 @@
+[1554476063.584270,{"stream":"stderr","default_stderr_parser":"Simple text","kubernetes":{"pod_name":"stderr","namespace_name":"annotations-parser","annotations":{"fluentbit.io/parser_stderr":"default-stderr-parser"},"container_name":"text"}}]
diff --git a/fluent-bit/tests/runtime/data/kubernetes/out/annotations-parser/annotations-parser_stderr_text_stdout.out b/fluent-bit/tests/runtime/data/kubernetes/out/annotations-parser/annotations-parser_stderr_text_stdout.out
new file mode 100644
index 00000000..a56cb809
--- /dev/null
+++ b/fluent-bit/tests/runtime/data/kubernetes/out/annotations-parser/annotations-parser_stderr_text_stdout.out
@@ -0,0 +1 @@
+[1554141513.598656,{"log":"[2019-04-05T14:54:23.584269761] Simple text\n","stream":"stdout","kubernetes":{"pod_name":"stderr","namespace_name":"annotations-parser","annotations":{"fluentbit.io/parser_stderr":"default-stderr-parser"},"container_name":"text"}}]
diff --git a/fluent-bit/tests/runtime/data/kubernetes/out/annotations-parser/annotations-parser_stdout_text_stderr.out b/fluent-bit/tests/runtime/data/kubernetes/out/annotations-parser/annotations-parser_stdout_text_stderr.out
new file mode 100644
index 00000000..65e31d0d
--- /dev/null
+++ b/fluent-bit/tests/runtime/data/kubernetes/out/annotations-parser/annotations-parser_stdout_text_stderr.out
@@ -0,0 +1 @@
+[1554141513.698657,{"log":"[2019-04-05T14:54:23.584269761] Simple text\n","stream":"stderr","kubernetes":{"pod_name":"stdout","namespace_name":"annotations-parser","annotations":{"fluentbit.io/parser_stdout":"default-stdout-parser"},"container_name":"text"}}]
diff --git a/fluent-bit/tests/runtime/data/kubernetes/out/annotations-parser/annotations-parser_stdout_text_stdout.out b/fluent-bit/tests/runtime/data/kubernetes/out/annotations-parser/annotations-parser_stdout_text_stdout.out
new file mode 100644
index 00000000..07c2d913
--- /dev/null
+++ b/fluent-bit/tests/runtime/data/kubernetes/out/annotations-parser/annotations-parser_stdout_text_stdout.out
@@ -0,0 +1 @@
+[1554476063.584270,{"stream":"stdout","default_stdout_parser":"Simple text","kubernetes":{"pod_name":"stdout","namespace_name":"annotations-parser","annotations":{"fluentbit.io/parser_stdout":"default-stdout-parser"},"container_name":"text"}}]
diff --git a/fluent-bit/tests/runtime/data/kubernetes/out/annotations/annotations_invalid_text.out b/fluent-bit/tests/runtime/data/kubernetes/out/annotations/annotations_invalid_text.out
new file mode 100644
index 00000000..900560d8
--- /dev/null
+++ b/fluent-bit/tests/runtime/data/kubernetes/out/annotations/annotations_invalid_text.out
@@ -0,0 +1 @@
+[1554141513.598656,{"log":"Simple text\n","stream":"stdout","kubernetes":{"pod_name":"invalid","namespace_name":"annotations","annotations":{"fluentbit.io/invalid":"true","fluentbit.io/exclude_":"true","fluentbit.io/exclude_invalid":"true","fluentbit.io/exclude_stdout-":"true","fluentbit.io/exclude-":"true","fluentbit.io/exclude+":"true"},"container_name":"text"}}]
diff --git a/fluent-bit/tests/runtime/data/kubernetes/out/core/core_base_fluent-bit.out b/fluent-bit/tests/runtime/data/kubernetes/out/core/core_base_fluent-bit.out
new file mode 100644
index 00000000..6a1a3ac5
--- /dev/null
+++ b/fluent-bit/tests/runtime/data/kubernetes/out/core/core_base_fluent-bit.out
@@ -0,0 +1 @@
+[1554141513.598656,{"log":"Fluent Bit is logging\n","stream":"stdout","kubernetes":{"pod_name":"base","namespace_name":"core","pod_id":"e9f2963f-55f2-11e9-84c5-02e422b8a84a","labels":{"app.kubernetes.io/name":"fluent-bit"},"annotations":{"prometheus.io/path":"/api/v1/metrics/prometheus","prometheus.io/port":"2020","prometheus.io/scrape":"true"},"host":"ip-10-49-18-80.eu-west-1.compute.internal","container_name":"fluent-bit","docker_id":"c9898099f6d235126d564ed38a020007ea7a6fac6e25e718de683c9dd0076c16","container_hash":"fluent/fluent-bit@sha256:7ac0fd3569af866e9a6a22eb592744200d2dbe098cf066162453f8d0b06c531f","container_image":"fluent/fluent-bit:latest"}}]
diff --git a/fluent-bit/tests/runtime/data/kubernetes/out/core/core_no-meta_text.out b/fluent-bit/tests/runtime/data/kubernetes/out/core/core_no-meta_text.out
new file mode 100644
index 00000000..f72dc98b
--- /dev/null
+++ b/fluent-bit/tests/runtime/data/kubernetes/out/core/core_no-meta_text.out
@@ -0,0 +1 @@
+[1554141513.598656,{"log":"Simple text\n","stream":"stdout","kubernetes":{"pod_name":"no-meta","namespace_name":"core","container_name":"text"}}]
diff --git a/fluent-bit/tests/runtime/data/kubernetes/out/core/core_unescaping_json.out b/fluent-bit/tests/runtime/data/kubernetes/out/core/core_unescaping_json.out
new file mode 100644
index 00000000..8063a7fa
--- /dev/null
+++ b/fluent-bit/tests/runtime/data/kubernetes/out/core/core_unescaping_json.out
@@ -0,0 +1 @@
+[1554141513.598656,{"log":"{\"stringified-json\":\"{\\\"stringified-json\\\":\\\"{\\\\\\\"stringified-json\\\\\\\":\\\\\\\"\\\\\\\"}\\\"}\",\"text\":\"quoted \\\"text with embedded quoted \\\\\\\"text\\\\\\\"\\\"\"}\n","stream":"stdout","kubernetes":{"pod_name":"unescaping","namespace_name":"core","container_name":"json"}}]
diff --git a/fluent-bit/tests/runtime/data/kubernetes/out/core/core_unescaping_text.out b/fluent-bit/tests/runtime/data/kubernetes/out/core/core_unescaping_text.out
new file mode 100644
index 00000000..f9dad5be
--- /dev/null
+++ b/fluent-bit/tests/runtime/data/kubernetes/out/core/core_unescaping_text.out
@@ -0,0 +1 @@
+[1554141513.598656,{"log":"Text with embedded quoted \"text with embedded quoted \\\"text with embedded quoted \\\\\\\"text\\\\\\\"\\\"\"\n","stream":"stdout","kubernetes":{"pod_name":"unescaping","namespace_name":"core","container_name":"text"}}]
diff --git a/fluent-bit/tests/runtime/data/kubernetes/out/kairosdb-914055854-b63vq.out b/fluent-bit/tests/runtime/data/kubernetes/out/kairosdb-914055854-b63vq.out
new file mode 100644
index 00000000..efaaf0d8
--- /dev/null
+++ b/fluent-bit/tests/runtime/data/kubernetes/out/kairosdb-914055854-b63vq.out
@@ -0,0 +1 @@
+[1541358747.464754,{"_UID":"1000","_GID":"1000","_CAP_EFFECTIVE":"0","_SELINUX_CONTEXT":"unconfined\n","_AUDIT_LOGINUID":"1000","_SYSTEMD_OWNER_UID":"1000","_SYSTEMD_SLICE":"user-1000.slice","_SYSTEMD_USER_SLICE":"-.slice","_BOOT_ID":"e3abb4093a904a169e2ac21af241392f","_MACHINE_ID":"28d81df105e641e9b7d85e9d41433662","_HOSTNAME":"cube","PRIORITY":"6","CODE_FILE":"filter_kubernetes.c","CODE_LINE":"456","CODE_FUNC":"flb_test_systemd_logs","CONTAINER_NAME":"k8s_kairosdb_kairosdb-914055854-b63vq_default_d6c53deb-05a4-11e8-a8c4-080027435fb7_23","CONTAINER_TAG":"","CONTAINER_ID":"56e257661383","CONTAINER_ID_FULL":"56e257661383836fac4cd90a23ee8a7a02ee1538c8f35657d1a90f3de1065a22","MESSAGE":"08:58:45.839 [qtp151442075-47] DEBUG [HttpParser.java:281] - filled 157/157","KUBE_TEST":"2018","SYSLOG_IDENTIFIER":"flb-rt-filter_kubernetes","_TRANSPORT":"journal","_PID":"32318","_COMM":"flb-rt-filter_k","_EXE":"build/bin/flb-rt-filter_kubernetes","_CMDLINE":"bin/flb-rt-filter_kubernetes","_AUDIT_SESSION":"7417","_SYSTEMD_CGROUP":"/user.slice/user-1000.slice/session-7417.scope","_SYSTEMD_SESSION":"7417","_SYSTEMD_UNIT":"session-7417.scope","_SYSTEMD_INVOCATION_ID":"899c0f7241ae4db0af3cb4088dca76b0","_SOURCE_REALTIME_TIMESTAMP":"1541358747464738","kubernetes":{"pod_name":"kairosdb-914055854-b63vq","namespace_name":"default","pod_id":"d6c53deb-05a4-11e8-a8c4-080027435fb7","labels":{"name":"kairosdb","pod-template-hash":"914055854"},"annotations":{"kubernetes.io/created-by":"{\"kind\":\"SerializedReference\",\"apiVersion\":\"v1\",\"reference\":{\"kind\":\"ReplicaSet\",\"namespace\":\"default\",\"name\":\"kairosdb-914055854\",\"uid\":\"d6c2f841-05a4-11e8-a8c4-080027435fb7\",\"apiVersion\":\"extensions\",\"resourceVersion\":\"1592\"}}\n"},"host":"192.168.10.169","container_name":"kairosdb","docker_id":"02d45318f220ad01109d13df31fbbb0668b87fdd06ef6607abfdecf5eba4b311","container_hash":"davradocker/docker-kairosdb@sha256:2ee07e3f3ba61b96597c78ab0aa7b837d771b375888b020da7a7316bb524b6e8","container_image":"davradocker/docker-kairosdb:1.1.1_9"}}]
diff --git a/fluent-bit/tests/runtime/data/kubernetes/out/options/options_k8s-logging-exclude-disabled_text_stderr.out b/fluent-bit/tests/runtime/data/kubernetes/out/options/options_k8s-logging-exclude-disabled_text_stderr.out
new file mode 100644
index 00000000..2b148329
--- /dev/null
+++ b/fluent-bit/tests/runtime/data/kubernetes/out/options/options_k8s-logging-exclude-disabled_text_stderr.out
@@ -0,0 +1 @@
+[1554141513.698657,{"log":"Simple text\n","stream":"stderr","kubernetes":{"pod_name":"k8s-logging-exclude-disabled","namespace_name":"options","annotations":{"fluentbit.io/exclude":"true"},"container_name":"text"}}]
diff --git a/fluent-bit/tests/runtime/data/kubernetes/out/options/options_k8s-logging-exclude-disabled_text_stdout.out b/fluent-bit/tests/runtime/data/kubernetes/out/options/options_k8s-logging-exclude-disabled_text_stdout.out
new file mode 100644
index 00000000..4a87d121
--- /dev/null
+++ b/fluent-bit/tests/runtime/data/kubernetes/out/options/options_k8s-logging-exclude-disabled_text_stdout.out
@@ -0,0 +1 @@
+[1554141513.598656,{"log":"Simple text\n","stream":"stdout","kubernetes":{"pod_name":"k8s-logging-exclude-disabled","namespace_name":"options","annotations":{"fluentbit.io/exclude":"true"},"container_name":"text"}}]
diff --git a/fluent-bit/tests/runtime/data/kubernetes/out/options/options_k8s-logging-parser-disabled_text_stderr.out b/fluent-bit/tests/runtime/data/kubernetes/out/options/options_k8s-logging-parser-disabled_text_stderr.out
new file mode 100644
index 00000000..0c35402f
--- /dev/null
+++ b/fluent-bit/tests/runtime/data/kubernetes/out/options/options_k8s-logging-parser-disabled_text_stderr.out
@@ -0,0 +1 @@
+[1554141513.698657,{"log":"Simple text\n","stream":"stderr","kubernetes":{"pod_name":"k8s-logging-parser-disabled","namespace_name":"options","annotations":{"fluentbit.io/parser":"default-parser"},"container_name":"text"}}]
diff --git a/fluent-bit/tests/runtime/data/kubernetes/out/options/options_k8s-logging-parser-disabled_text_stdout.out b/fluent-bit/tests/runtime/data/kubernetes/out/options/options_k8s-logging-parser-disabled_text_stdout.out
new file mode 100644
index 00000000..5eb80966
--- /dev/null
+++ b/fluent-bit/tests/runtime/data/kubernetes/out/options/options_k8s-logging-parser-disabled_text_stdout.out
@@ -0,0 +1 @@
+[1554141513.598656,{"log":"Simple text\n","stream":"stdout","kubernetes":{"pod_name":"k8s-logging-parser-disabled","namespace_name":"options","annotations":{"fluentbit.io/parser":"default-parser"},"container_name":"text"}}]
diff --git a/fluent-bit/tests/runtime/data/kubernetes/out/options/options_keep-log-disabled_json.out b/fluent-bit/tests/runtime/data/kubernetes/out/options/options_keep-log-disabled_json.out
new file mode 100644
index 00000000..6f95c3ba
--- /dev/null
+++ b/fluent-bit/tests/runtime/data/kubernetes/out/options/options_keep-log-disabled_json.out
@@ -0,0 +1 @@
+[1554141513.598656,{"stream":"stdout","text":"Simple text","kubernetes":{"pod_name":"keep-log-disabled","namespace_name":"options","container_name":"json"}}]
diff --git a/fluent-bit/tests/runtime/data/kubernetes/out/options/options_keep-log-enabled_json.out b/fluent-bit/tests/runtime/data/kubernetes/out/options/options_keep-log-enabled_json.out
new file mode 100644
index 00000000..b09efae8
--- /dev/null
+++ b/fluent-bit/tests/runtime/data/kubernetes/out/options/options_keep-log-enabled_json.out
@@ -0,0 +1 @@
+[1554141513.598656,{"log":"{\"text\":\"Simple text\"}\n","stream":"stdout","text":"Simple text","kubernetes":{"pod_name":"keep-log-enabled","namespace_name":"options","container_name":"json"}}]
diff --git a/fluent-bit/tests/runtime/data/kubernetes/out/options/options_merge-log-disabled_json.out b/fluent-bit/tests/runtime/data/kubernetes/out/options/options_merge-log-disabled_json.out
new file mode 100644
index 00000000..7d28a9c9
--- /dev/null
+++ b/fluent-bit/tests/runtime/data/kubernetes/out/options/options_merge-log-disabled_json.out
@@ -0,0 +1 @@
+[1554141513.598656,{"log":"{\"text\":\"Simple text\"}\n","stream":"stdout","kubernetes":{"pod_name":"merge-log-disabled","namespace_name":"options","container_name":"json"}}]
diff --git a/fluent-bit/tests/runtime/data/kubernetes/out/options/options_merge-log-enabled_invalid-json.out b/fluent-bit/tests/runtime/data/kubernetes/out/options/options_merge-log-enabled_invalid-json.out
new file mode 100644
index 00000000..66cfe547
--- /dev/null
+++ b/fluent-bit/tests/runtime/data/kubernetes/out/options/options_merge-log-enabled_invalid-json.out
@@ -0,0 +1 @@
+[1554141513.598656,{"log":"{\"text\":\"Simple text\"]\n","stream":"stdout","kubernetes":{"pod_name":"merge-log-enabled","namespace_name":"options","container_name":"invalid-json"}}]
diff --git a/fluent-bit/tests/runtime/data/kubernetes/out/options/options_merge-log-enabled_json.out b/fluent-bit/tests/runtime/data/kubernetes/out/options/options_merge-log-enabled_json.out
new file mode 100644
index 00000000..25717717
--- /dev/null
+++ b/fluent-bit/tests/runtime/data/kubernetes/out/options/options_merge-log-enabled_json.out
@@ -0,0 +1 @@
+[1554141513.598656,{"log":"{\"text\":\"Simple text\"}\n","stream":"stdout","text":"Simple text","kubernetes":{"pod_name":"merge-log-enabled","namespace_name":"options","container_name":"json"}}]
diff --git a/fluent-bit/tests/runtime/data/kubernetes/out/options/options_merge-log-enabled_text.out b/fluent-bit/tests/runtime/data/kubernetes/out/options/options_merge-log-enabled_text.out
new file mode 100644
index 00000000..7c6363bc
--- /dev/null
+++ b/fluent-bit/tests/runtime/data/kubernetes/out/options/options_merge-log-enabled_text.out
@@ -0,0 +1 @@
+[1554141513.598656,{"log":"Simple text\n","stream":"stdout","kubernetes":{"pod_name":"merge-log-enabled","namespace_name":"options","container_name":"text"}}]
diff --git a/fluent-bit/tests/runtime/data/kubernetes/out/options/options_merge-log-key_json.out b/fluent-bit/tests/runtime/data/kubernetes/out/options/options_merge-log-key_json.out
new file mode 100644
index 00000000..130e92f2
--- /dev/null
+++ b/fluent-bit/tests/runtime/data/kubernetes/out/options/options_merge-log-key_json.out
@@ -0,0 +1 @@
+[1554141513.598656,{"log":"{\"text\":\"Simple text\"}\n","stream":"stdout","merge-log-key":{"text":"Simple text"},"kubernetes":{"pod_name":"merge-log-key","namespace_name":"options","container_name":"json"}}]
diff --git a/fluent-bit/tests/runtime/data/kubernetes/out/options/options_merge-log-trim-disabled_json.out b/fluent-bit/tests/runtime/data/kubernetes/out/options/options_merge-log-trim-disabled_json.out
new file mode 100644
index 00000000..0fee8645
--- /dev/null
+++ b/fluent-bit/tests/runtime/data/kubernetes/out/options/options_merge-log-trim-disabled_json.out
@@ -0,0 +1 @@
+[1554141513.598656,{"log":"{\"text\":\"Simple text\n\"}\n","stream":"stdout","text":"Simple text\n","kubernetes":{"pod_name":"merge-log-trim-disabled","namespace_name":"options","container_name":"json"}}]
diff --git a/fluent-bit/tests/runtime/data/kubernetes/out/options/options_merge-log-trim-enabled_json.out b/fluent-bit/tests/runtime/data/kubernetes/out/options/options_merge-log-trim-enabled_json.out
new file mode 100644
index 00000000..adcdf765
--- /dev/null
+++ b/fluent-bit/tests/runtime/data/kubernetes/out/options/options_merge-log-trim-enabled_json.out
@@ -0,0 +1 @@
+[1554141513.598656,{"log":"{\"text\":\"Simple text\n\"}\n","stream":"stdout","text":"Simple text","kubernetes":{"pod_name":"merge-log-trim-enabled","namespace_name":"options","container_name":"json"}}]
diff --git a/fluent-bit/tests/runtime/data/kubernetes/out/options/options_use-kubelet-disabled_fluent-bit.out b/fluent-bit/tests/runtime/data/kubernetes/out/options/options_use-kubelet-disabled_fluent-bit.out
new file mode 100644
index 00000000..699da8bd
--- /dev/null
+++ b/fluent-bit/tests/runtime/data/kubernetes/out/options/options_use-kubelet-disabled_fluent-bit.out
@@ -0,0 +1 @@
+[1554141513.598656,{"log":"Fluent Bit is logging\n","stream":"stdout","kubernetes":{"pod_name":"use-kubelet-disabled","namespace_name":"options","pod_id":"e9f2963f-55f2-11e9-84c5-02e422b8a84a","labels":{"app.kubernetes.io/name":"fluent-bit"},"annotations":{"prometheus.io/path":"/api/v1/metrics/prometheus","prometheus.io/port":"2020","prometheus.io/scrape":"true"},"host":"ip-10-49-18-80.eu-west-1.compute.internal","container_name":"fluent-bit","docker_id":"c9898099f6d235126d564ed38a020007ea7a6fac6e25e718de683c9dd0076c16","container_hash":"fluent/fluent-bit@sha256:7ac0fd3569af866e9a6a22eb592744200d2dbe098cf066162453f8d0b06c531f","container_image":"fluent/fluent-bit:latest"}}]
diff --git a/fluent-bit/tests/runtime/data/kubernetes/out/options/options_use-kubelet-enabled_fluent-bit.out b/fluent-bit/tests/runtime/data/kubernetes/out/options/options_use-kubelet-enabled_fluent-bit.out
new file mode 100644
index 00000000..dbd97f1a
--- /dev/null
+++ b/fluent-bit/tests/runtime/data/kubernetes/out/options/options_use-kubelet-enabled_fluent-bit.out
@@ -0,0 +1 @@
+[1554141513.598656,{"log":"Fluent Bit is logging\n","stream":"stdout","kubernetes":{"pod_name":"use-kubelet-enabled","namespace_name":"options","pod_id":"e9f2963f-55f2-11e9-84c5-02e422b8a84a","labels":{"app.kubernetes.io/name":"fluent-bit"},"annotations":{"prometheus.io/path":"/api/v1/metrics/prometheus","prometheus.io/port":"2020","prometheus.io/scrape":"true"},"host":"ip-10-49-18-80.eu-west-1.compute.internal","container_name":"fluent-bit","docker_id":"c9898099f6d235126d564ed38a020007ea7a6fac6e25e718de683c9dd0076c16","container_hash":"fluent/fluent-bit@sha256:7ac0fd3569af866e9a6a22eb592744200d2dbe098cf066162453f8d0b06c531f","container_image":"fluent/fluent-bit:latest"}}]
diff --git a/fluent-bit/tests/runtime/data/kubernetes/parsers.conf b/fluent-bit/tests/runtime/data/kubernetes/parsers.conf
new file mode 100644
index 00000000..40014ec3
--- /dev/null
+++ b/fluent-bit/tests/runtime/data/kubernetes/parsers.conf
@@ -0,0 +1,88 @@
+[PARSER]
+ Name docker
+ Format json
+ Time_Key time
+ Time_Format %Y-%m-%dT%H:%M:%S.%L%z
+
+[PARSER]
+ Name kubernetes-tag
+ Format regex
+ Regex ^(?<namespace_name>[^.]+).(?<pod_name>[^.]+).(?<container_name>[^.]+)$
+
+[PARSER]
+ Name regex-parser-without-time
+ Format regex
+ Regex ^(?<message>.+)$
+
+[PARSER]
+ Name regex-parser-with-time
+ Format regex
+ Regex ^\[(?<time>[^\]]+)\] (?<message>.+)$
+ Time_Key time
+ Time_Format %Y-%m-%dT%H:%M:%S.%L
+
+[PARSER]
+ Name json-parser-without-time
+ Format json
+
+[PARSER]
+ Name json-parser-with-time
+ Format json
+ Time_Key time
+ Time_Format %Y-%m-%dT%H:%M:%S.%L
+
+[PARSER]
+ Name default-parser
+ Format regex
+ Regex ^\[(?<time>[^\]]+)\] (?<default_parser>.+)$
+ Time_Key time
+ Time_Format %Y-%m-%dT%H:%M:%S.%L
+
+[PARSER]
+ Name default-stdout-parser
+ Format regex
+ Regex ^\[(?<time>[^\]]+)\] (?<default_stdout_parser>.+)$
+ Time_Key time
+ Time_Format %Y-%m-%dT%H:%M:%S.%L
+
+[PARSER]
+ Name default-stderr-parser
+ Format regex
+ Regex ^\[(?<time>[^\]]+)\] (?<default_stderr_parser>.+)$
+ Time_Key time
+ Time_Format %Y-%m-%dT%H:%M:%S.%L
+
+[PARSER]
+ Name container-1-parser
+ Format regex
+ Regex ^\[(?<time>[^\]]+)\] (?<container_1_parser>.+)$
+ Time_Key time
+ Time_Format %Y-%m-%dT%H:%M:%S.%L
+
+[PARSER]
+ Name container-2-stdout-parser
+ Format regex
+ Regex ^\[(?<time>[^\]]+)\] (?<container_2_stdout_parser>.+)$
+ Time_Key time
+ Time_Format %Y-%m-%dT%H:%M:%S.%L
+
+[PARSER]
+ Name container-2-stderr-parser
+ Format regex
+ Regex ^\[(?<time>[^\]]+)\] (?<container_2_stderr_parser>.+)$
+ Time_Key time
+ Time_Format %Y-%m-%dT%H:%M:%S.%L
+
+[PARSER]
+ Name container-3-stdout-parser
+ Format regex
+ Regex ^\[(?<time>[^\]]+)\] (?<container_3_stdout_parser>.+)$
+ Time_Key time
+ Time_Format %Y-%m-%dT%H:%M:%S.%L
+
+[PARSER]
+ Name container-4-stderr-parser
+ Format regex
+ Regex ^\[(?<time>[^\]]+)\] (?<container_4_stderr_parser>.+)$
+ Time_Key time
+ Time_Format %Y-%m-%dT%H:%M:%S.%L
diff --git a/fluent-bit/tests/runtime/data/loki/labelmap.json b/fluent-bit/tests/runtime/data/loki/labelmap.json
new file mode 100644
index 00000000..b24ad434
--- /dev/null
+++ b/fluent-bit/tests/runtime/data/loki/labelmap.json
@@ -0,0 +1,10 @@
+{
+ "kubernetes": {
+ "container_name": "container",
+ "pod_name": "pod",
+ "namespace_name": "namespace",
+ "labels" : {
+ "team": "team"
+ }
+ }
+}
diff --git a/fluent-bit/tests/runtime/data/podman/cgroupv2/42/net/dev b/fluent-bit/tests/runtime/data/podman/cgroupv2/42/net/dev
new file mode 100644
index 00000000..835131a9
--- /dev/null
+++ b/fluent-bit/tests/runtime/data/podman/cgroupv2/42/net/dev
@@ -0,0 +1,6 @@
+Inter-| Receive | Transmit
+ face |bytes packets errs drop fifo frame compressed multicast|bytes packets errs drop fifo colls carrier compressed
+ lo: 34275905 6362 0 0 0 0 0 0 34275905 6362 0 0 0 0 0 0
+enp6s0: 9089298401 6228223 0 0 0 0 0 5669 173774157 2534993 0 0 0 0 0 0
+wlp5s0: 2802033 11801 0 0 0 0 0 0 790512 6492 0 0 0 0 0 0
+virbr0: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
diff --git a/fluent-bit/tests/runtime/data/podman/cgroupv2/cgroup.controllers b/fluent-bit/tests/runtime/data/podman/cgroupv2/cgroup.controllers
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/fluent-bit/tests/runtime/data/podman/cgroupv2/cgroup.controllers
diff --git a/fluent-bit/tests/runtime/data/podman/cgroupv2/config.json b/fluent-bit/tests/runtime/data/podman/cgroupv2/config.json
new file mode 100644
index 00000000..dcf6ab4a
--- /dev/null
+++ b/fluent-bit/tests/runtime/data/podman/cgroupv2/config.json
@@ -0,0 +1 @@
+[{"id":"8a19d6058bfbe88cd0548eba9047d94c70161f5d74b545c7504b2f27491686d9","names":["determined_mcnulty"],"image":"27941809078cc9b2802deb2b0bb6feed6c236cde01e487f200e24653533701ee","layer":"633c06ce1a553c72c4fbf12686acd5129a453f57b73385f8e7afc975fa657d86","metadata":"{\"image-name\":\"docker.io/library/ubuntu:latest\",\"image-id\":\"27941809078cc9b2802deb2b0bb6feed6c236cde01e487f200e24653533701ee\",\"name\":\"determined_mcnulty\",\"created-at\":1657980361}","created":"2022-07-16T14:06:01.521179687Z","flags":{"MountLabel":"system_u:object_r:container_file_t:s0:c664,c969","ProcessLabel":"system_u:system_r:container_t:s0:c664,c969"}}]
diff --git a/fluent-bit/tests/runtime/data/podman/cgroupv2/libpod-8a19d6058bfbe88cd0548eba9047d94c70161f5d74b545c7504b2f27491686d9/containers/cgroup.procs b/fluent-bit/tests/runtime/data/podman/cgroupv2/libpod-8a19d6058bfbe88cd0548eba9047d94c70161f5d74b545c7504b2f27491686d9/containers/cgroup.procs
new file mode 100644
index 00000000..d81cc071
--- /dev/null
+++ b/fluent-bit/tests/runtime/data/podman/cgroupv2/libpod-8a19d6058bfbe88cd0548eba9047d94c70161f5d74b545c7504b2f27491686d9/containers/cgroup.procs
@@ -0,0 +1 @@
+42
diff --git a/fluent-bit/tests/runtime/data/podman/cgroupv2/libpod-8a19d6058bfbe88cd0548eba9047d94c70161f5d74b545c7504b2f27491686d9/memory.current b/fluent-bit/tests/runtime/data/podman/cgroupv2/libpod-8a19d6058bfbe88cd0548eba9047d94c70161f5d74b545c7504b2f27491686d9/memory.current
new file mode 100644
index 00000000..d81cc071
--- /dev/null
+++ b/fluent-bit/tests/runtime/data/podman/cgroupv2/libpod-8a19d6058bfbe88cd0548eba9047d94c70161f5d74b545c7504b2f27491686d9/memory.current
@@ -0,0 +1 @@
+42
diff --git a/fluent-bit/tests/runtime/data/podman/garbage/42/net/dev b/fluent-bit/tests/runtime/data/podman/garbage/42/net/dev
new file mode 100644
index 00000000..9ce7649d
--- /dev/null
+++ b/fluent-bit/tests/runtime/data/podman/garbage/42/net/dev
@@ -0,0 +1 @@
+garbage
diff --git a/fluent-bit/tests/runtime/data/podman/garbage/config.json b/fluent-bit/tests/runtime/data/podman/garbage/config.json
new file mode 100644
index 00000000..dcf6ab4a
--- /dev/null
+++ b/fluent-bit/tests/runtime/data/podman/garbage/config.json
@@ -0,0 +1 @@
+[{"id":"8a19d6058bfbe88cd0548eba9047d94c70161f5d74b545c7504b2f27491686d9","names":["determined_mcnulty"],"image":"27941809078cc9b2802deb2b0bb6feed6c236cde01e487f200e24653533701ee","layer":"633c06ce1a553c72c4fbf12686acd5129a453f57b73385f8e7afc975fa657d86","metadata":"{\"image-name\":\"docker.io/library/ubuntu:latest\",\"image-id\":\"27941809078cc9b2802deb2b0bb6feed6c236cde01e487f200e24653533701ee\",\"name\":\"determined_mcnulty\",\"created-at\":1657980361}","created":"2022-07-16T14:06:01.521179687Z","flags":{"MountLabel":"system_u:object_r:container_file_t:s0:c664,c969","ProcessLabel":"system_u:system_r:container_t:s0:c664,c969"}}]
diff --git a/fluent-bit/tests/runtime/data/podman/garbage/libpod-8a19d6058bfbe88cd0548eba9047d94c70161f5d74b545c7504b2f27491686d9/memory.current b/fluent-bit/tests/runtime/data/podman/garbage/libpod-8a19d6058bfbe88cd0548eba9047d94c70161f5d74b545c7504b2f27491686d9/memory.current
new file mode 100644
index 00000000..9ce7649d
--- /dev/null
+++ b/fluent-bit/tests/runtime/data/podman/garbage/libpod-8a19d6058bfbe88cd0548eba9047d94c70161f5d74b545c7504b2f27491686d9/memory.current
@@ -0,0 +1 @@
+garbage
diff --git a/fluent-bit/tests/runtime/data/podman/garbage/memory/libpod-8a19d6058bfbe88cd0548eba9047d94c70161f5d74b545c7504b2f27491686d9/memory.usage_in_bytes b/fluent-bit/tests/runtime/data/podman/garbage/memory/libpod-8a19d6058bfbe88cd0548eba9047d94c70161f5d74b545c7504b2f27491686d9/memory.usage_in_bytes
new file mode 100644
index 00000000..9ce7649d
--- /dev/null
+++ b/fluent-bit/tests/runtime/data/podman/garbage/memory/libpod-8a19d6058bfbe88cd0548eba9047d94c70161f5d74b545c7504b2f27491686d9/memory.usage_in_bytes
@@ -0,0 +1 @@
+garbage
diff --git a/fluent-bit/tests/runtime/data/podman/garbage_config/config.json b/fluent-bit/tests/runtime/data/podman/garbage_config/config.json
new file mode 100644
index 00000000..9ce7649d
--- /dev/null
+++ b/fluent-bit/tests/runtime/data/podman/garbage_config/config.json
@@ -0,0 +1 @@
+garbage
diff --git a/fluent-bit/tests/runtime/data/podman/no_config/libpod-8a19d6058bfbe88cd0548eba9047d94c70161f5d74b545c7504b2f27491686d9/memory.current b/fluent-bit/tests/runtime/data/podman/no_config/libpod-8a19d6058bfbe88cd0548eba9047d94c70161f5d74b545c7504b2f27491686d9/memory.current
new file mode 100644
index 00000000..d81cc071
--- /dev/null
+++ b/fluent-bit/tests/runtime/data/podman/no_config/libpod-8a19d6058bfbe88cd0548eba9047d94c70161f5d74b545c7504b2f27491686d9/memory.current
@@ -0,0 +1 @@
+42
diff --git a/fluent-bit/tests/runtime/data/podman/no_config/libpod-8a19d6058bfbe88cd0548eba9047d94c70161f5d74b545c7504b2f27491686d9/memory.usage_in_bytes b/fluent-bit/tests/runtime/data/podman/no_config/libpod-8a19d6058bfbe88cd0548eba9047d94c70161f5d74b545c7504b2f27491686d9/memory.usage_in_bytes
new file mode 100644
index 00000000..d81cc071
--- /dev/null
+++ b/fluent-bit/tests/runtime/data/podman/no_config/libpod-8a19d6058bfbe88cd0548eba9047d94c70161f5d74b545c7504b2f27491686d9/memory.usage_in_bytes
@@ -0,0 +1 @@
+42
diff --git a/fluent-bit/tests/runtime/data/podman/no_config/memory/libpod-8a19d6058bfbe88cd0548eba9047d94c70161f5d74b545c7504b2f27491686d9/memory.usage_in_bytes b/fluent-bit/tests/runtime/data/podman/no_config/memory/libpod-8a19d6058bfbe88cd0548eba9047d94c70161f5d74b545c7504b2f27491686d9/memory.usage_in_bytes
new file mode 100644
index 00000000..d81cc071
--- /dev/null
+++ b/fluent-bit/tests/runtime/data/podman/no_config/memory/libpod-8a19d6058bfbe88cd0548eba9047d94c70161f5d74b545c7504b2f27491686d9/memory.usage_in_bytes
@@ -0,0 +1 @@
+42
diff --git a/fluent-bit/tests/runtime/data/podman/no_proc/config.json b/fluent-bit/tests/runtime/data/podman/no_proc/config.json
new file mode 100644
index 00000000..dcf6ab4a
--- /dev/null
+++ b/fluent-bit/tests/runtime/data/podman/no_proc/config.json
@@ -0,0 +1 @@
+[{"id":"8a19d6058bfbe88cd0548eba9047d94c70161f5d74b545c7504b2f27491686d9","names":["determined_mcnulty"],"image":"27941809078cc9b2802deb2b0bb6feed6c236cde01e487f200e24653533701ee","layer":"633c06ce1a553c72c4fbf12686acd5129a453f57b73385f8e7afc975fa657d86","metadata":"{\"image-name\":\"docker.io/library/ubuntu:latest\",\"image-id\":\"27941809078cc9b2802deb2b0bb6feed6c236cde01e487f200e24653533701ee\",\"name\":\"determined_mcnulty\",\"created-at\":1657980361}","created":"2022-07-16T14:06:01.521179687Z","flags":{"MountLabel":"system_u:object_r:container_file_t:s0:c664,c969","ProcessLabel":"system_u:system_r:container_t:s0:c664,c969"}}]
diff --git a/fluent-bit/tests/runtime/data/podman/no_proc/libpod-8a19d6058bfbe88cd0548eba9047d94c70161f5d74b545c7504b2f27491686d9/memory.current b/fluent-bit/tests/runtime/data/podman/no_proc/libpod-8a19d6058bfbe88cd0548eba9047d94c70161f5d74b545c7504b2f27491686d9/memory.current
new file mode 100644
index 00000000..d81cc071
--- /dev/null
+++ b/fluent-bit/tests/runtime/data/podman/no_proc/libpod-8a19d6058bfbe88cd0548eba9047d94c70161f5d74b545c7504b2f27491686d9/memory.current
@@ -0,0 +1 @@
+42
diff --git a/fluent-bit/tests/runtime/data/podman/no_proc/memory/libpod-8a19d6058bfbe88cd0548eba9047d94c70161f5d74b545c7504b2f27491686d9/memory.usage_in_bytes b/fluent-bit/tests/runtime/data/podman/no_proc/memory/libpod-8a19d6058bfbe88cd0548eba9047d94c70161f5d74b545c7504b2f27491686d9/memory.usage_in_bytes
new file mode 100644
index 00000000..d81cc071
--- /dev/null
+++ b/fluent-bit/tests/runtime/data/podman/no_proc/memory/libpod-8a19d6058bfbe88cd0548eba9047d94c70161f5d74b545c7504b2f27491686d9/memory.usage_in_bytes
@@ -0,0 +1 @@
+42
diff --git a/fluent-bit/tests/runtime/data/podman/no_sysfs/42/net/dev b/fluent-bit/tests/runtime/data/podman/no_sysfs/42/net/dev
new file mode 100644
index 00000000..835131a9
--- /dev/null
+++ b/fluent-bit/tests/runtime/data/podman/no_sysfs/42/net/dev
@@ -0,0 +1,6 @@
+Inter-| Receive | Transmit
+ face |bytes packets errs drop fifo frame compressed multicast|bytes packets errs drop fifo colls carrier compressed
+ lo: 34275905 6362 0 0 0 0 0 0 34275905 6362 0 0 0 0 0 0
+enp6s0: 9089298401 6228223 0 0 0 0 0 5669 173774157 2534993 0 0 0 0 0 0
+wlp5s0: 2802033 11801 0 0 0 0 0 0 790512 6492 0 0 0 0 0 0
+virbr0: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
diff --git a/fluent-bit/tests/runtime/data/podman/no_sysfs/config.json b/fluent-bit/tests/runtime/data/podman/no_sysfs/config.json
new file mode 100644
index 00000000..dcf6ab4a
--- /dev/null
+++ b/fluent-bit/tests/runtime/data/podman/no_sysfs/config.json
@@ -0,0 +1 @@
+[{"id":"8a19d6058bfbe88cd0548eba9047d94c70161f5d74b545c7504b2f27491686d9","names":["determined_mcnulty"],"image":"27941809078cc9b2802deb2b0bb6feed6c236cde01e487f200e24653533701ee","layer":"633c06ce1a553c72c4fbf12686acd5129a453f57b73385f8e7afc975fa657d86","metadata":"{\"image-name\":\"docker.io/library/ubuntu:latest\",\"image-id\":\"27941809078cc9b2802deb2b0bb6feed6c236cde01e487f200e24653533701ee\",\"name\":\"determined_mcnulty\",\"created-at\":1657980361}","created":"2022-07-16T14:06:01.521179687Z","flags":{"MountLabel":"system_u:object_r:container_file_t:s0:c664,c969","ProcessLabel":"system_u:system_r:container_t:s0:c664,c969"}}]
diff --git a/fluent-bit/tests/runtime/data/podman/regular/42/net/dev b/fluent-bit/tests/runtime/data/podman/regular/42/net/dev
new file mode 100644
index 00000000..835131a9
--- /dev/null
+++ b/fluent-bit/tests/runtime/data/podman/regular/42/net/dev
@@ -0,0 +1,6 @@
+Inter-| Receive | Transmit
+ face |bytes packets errs drop fifo frame compressed multicast|bytes packets errs drop fifo colls carrier compressed
+ lo: 34275905 6362 0 0 0 0 0 0 34275905 6362 0 0 0 0 0 0
+enp6s0: 9089298401 6228223 0 0 0 0 0 5669 173774157 2534993 0 0 0 0 0 0
+wlp5s0: 2802033 11801 0 0 0 0 0 0 790512 6492 0 0 0 0 0 0
+virbr0: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
diff --git a/fluent-bit/tests/runtime/data/podman/regular/config.json b/fluent-bit/tests/runtime/data/podman/regular/config.json
new file mode 100644
index 00000000..dcf6ab4a
--- /dev/null
+++ b/fluent-bit/tests/runtime/data/podman/regular/config.json
@@ -0,0 +1 @@
+[{"id":"8a19d6058bfbe88cd0548eba9047d94c70161f5d74b545c7504b2f27491686d9","names":["determined_mcnulty"],"image":"27941809078cc9b2802deb2b0bb6feed6c236cde01e487f200e24653533701ee","layer":"633c06ce1a553c72c4fbf12686acd5129a453f57b73385f8e7afc975fa657d86","metadata":"{\"image-name\":\"docker.io/library/ubuntu:latest\",\"image-id\":\"27941809078cc9b2802deb2b0bb6feed6c236cde01e487f200e24653533701ee\",\"name\":\"determined_mcnulty\",\"created-at\":1657980361}","created":"2022-07-16T14:06:01.521179687Z","flags":{"MountLabel":"system_u:object_r:container_file_t:s0:c664,c969","ProcessLabel":"system_u:system_r:container_t:s0:c664,c969"}}]
diff --git a/fluent-bit/tests/runtime/data/podman/regular/libpod-8a19d6058bfbe88cd0548eba9047d94c70161f5d74b545c7504b2f27491686d9/memory.current b/fluent-bit/tests/runtime/data/podman/regular/libpod-8a19d6058bfbe88cd0548eba9047d94c70161f5d74b545c7504b2f27491686d9/memory.current
new file mode 100644
index 00000000..d81cc071
--- /dev/null
+++ b/fluent-bit/tests/runtime/data/podman/regular/libpod-8a19d6058bfbe88cd0548eba9047d94c70161f5d74b545c7504b2f27491686d9/memory.current
@@ -0,0 +1 @@
+42
diff --git a/fluent-bit/tests/runtime/data/podman/regular/memory/libpod-8a19d6058bfbe88cd0548eba9047d94c70161f5d74b545c7504b2f27491686d9/memory.usage_in_bytes b/fluent-bit/tests/runtime/data/podman/regular/memory/libpod-8a19d6058bfbe88cd0548eba9047d94c70161f5d74b545c7504b2f27491686d9/memory.usage_in_bytes
new file mode 100644
index 00000000..d81cc071
--- /dev/null
+++ b/fluent-bit/tests/runtime/data/podman/regular/memory/libpod-8a19d6058bfbe88cd0548eba9047d94c70161f5d74b545c7504b2f27491686d9/memory.usage_in_bytes
@@ -0,0 +1 @@
+42
diff --git a/fluent-bit/tests/runtime/data/podman/regular/systemd/libpod-8a19d6058bfbe88cd0548eba9047d94c70161f5d74b545c7504b2f27491686d9/cgroup.procs b/fluent-bit/tests/runtime/data/podman/regular/systemd/libpod-8a19d6058bfbe88cd0548eba9047d94c70161f5d74b545c7504b2f27491686d9/cgroup.procs
new file mode 100644
index 00000000..a2a31c00
--- /dev/null
+++ b/fluent-bit/tests/runtime/data/podman/regular/systemd/libpod-8a19d6058bfbe88cd0548eba9047d94c70161f5d74b545c7504b2f27491686d9/cgroup.procs
@@ -0,0 +1,3 @@
+42
+73
+12
diff --git a/fluent-bit/tests/runtime/data/podman/reversed/42/net/dev b/fluent-bit/tests/runtime/data/podman/reversed/42/net/dev
new file mode 100644
index 00000000..835131a9
--- /dev/null
+++ b/fluent-bit/tests/runtime/data/podman/reversed/42/net/dev
@@ -0,0 +1,6 @@
+Inter-| Receive | Transmit
+ face |bytes packets errs drop fifo frame compressed multicast|bytes packets errs drop fifo colls carrier compressed
+ lo: 34275905 6362 0 0 0 0 0 0 34275905 6362 0 0 0 0 0 0
+enp6s0: 9089298401 6228223 0 0 0 0 0 5669 173774157 2534993 0 0 0 0 0 0
+wlp5s0: 2802033 11801 0 0 0 0 0 0 790512 6492 0 0 0 0 0 0
+virbr0: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
diff --git a/fluent-bit/tests/runtime/data/podman/reversed/config.json b/fluent-bit/tests/runtime/data/podman/reversed/config.json
new file mode 100644
index 00000000..6cbcc690
--- /dev/null
+++ b/fluent-bit/tests/runtime/data/podman/reversed/config.json
@@ -0,0 +1 @@
+[{"id":"8a19d6058bfbe88cd0548eba9047d94c70161f5d74b545c7504b2f27491686d9","names":["determined_mcnulty"],"image":"27941809078cc9b2802deb2b0bb6feed6c236cde01e487f200e24653533701ee","layer":"633c06ce1a553c72c4fbf12686acd5129a453f57b73385f8e7afc975fa657d86","metadata":"{\"image-id\":\"27941809078cc9b2802deb2b0bb6feed6c236cde01e487f200e24653533701ee\",\"name\":\"determined_mcnulty\",\"created-at\":1657980361,\"image-name\":\"docker.io/library/ubuntu:latest\"}","created":"2022-07-16T14:06:01.521179687Z","flags":{"MountLabel":"system_u:object_r:container_file_t:s0:c664,c969","ProcessLabel":"system_u:system_r:container_t:s0:c664,c969"}}]
diff --git a/fluent-bit/tests/runtime/data/podman/reversed/libpod-8a19d6058bfbe88cd0548eba9047d94c70161f5d74b545c7504b2f27491686d9/memory.current b/fluent-bit/tests/runtime/data/podman/reversed/libpod-8a19d6058bfbe88cd0548eba9047d94c70161f5d74b545c7504b2f27491686d9/memory.current
new file mode 100644
index 00000000..d81cc071
--- /dev/null
+++ b/fluent-bit/tests/runtime/data/podman/reversed/libpod-8a19d6058bfbe88cd0548eba9047d94c70161f5d74b545c7504b2f27491686d9/memory.current
@@ -0,0 +1 @@
+42
diff --git a/fluent-bit/tests/runtime/data/podman/reversed/memory/libpod-8a19d6058bfbe88cd0548eba9047d94c70161f5d74b545c7504b2f27491686d9/memory.usage_in_bytes b/fluent-bit/tests/runtime/data/podman/reversed/memory/libpod-8a19d6058bfbe88cd0548eba9047d94c70161f5d74b545c7504b2f27491686d9/memory.usage_in_bytes
new file mode 100644
index 00000000..d81cc071
--- /dev/null
+++ b/fluent-bit/tests/runtime/data/podman/reversed/memory/libpod-8a19d6058bfbe88cd0548eba9047d94c70161f5d74b545c7504b2f27491686d9/memory.usage_in_bytes
@@ -0,0 +1 @@
+42
diff --git a/fluent-bit/tests/runtime/data/podman/reversed/systemd/libpod-8a19d6058bfbe88cd0548eba9047d94c70161f5d74b545c7504b2f27491686d9/cgroup.procs b/fluent-bit/tests/runtime/data/podman/reversed/systemd/libpod-8a19d6058bfbe88cd0548eba9047d94c70161f5d74b545c7504b2f27491686d9/cgroup.procs
new file mode 100644
index 00000000..a2a31c00
--- /dev/null
+++ b/fluent-bit/tests/runtime/data/podman/reversed/systemd/libpod-8a19d6058bfbe88cd0548eba9047d94c70161f5d74b545c7504b2f27491686d9/cgroup.procs
@@ -0,0 +1,3 @@
+42
+73
+12
diff --git a/fluent-bit/tests/runtime/data/stackdriver/json.h b/fluent-bit/tests/runtime/data/stackdriver/json.h
new file mode 100755
index 00000000..146dd532
--- /dev/null
+++ b/fluent-bit/tests/runtime/data/stackdriver/json.h
@@ -0,0 +1,12 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+#define JSON "[" \
+ "1448403340," \
+ "{" \
+ "\"key_0\": false," \
+ "\"key_1\": true," \
+ "\"key_2\": \"some string\"," \
+ "\"key_3\": 0.12345678," \
+ "\"key.4\": 5000," \
+ "\"END_KEY\": \"JSON_END\"" \
+ "}]"
diff --git a/fluent-bit/tests/runtime/data/stackdriver/stackdriver-credentials.json b/fluent-bit/tests/runtime/data/stackdriver/stackdriver-credentials.json
new file mode 100644
index 00000000..1ea8afd9
--- /dev/null
+++ b/fluent-bit/tests/runtime/data/stackdriver/stackdriver-credentials.json
@@ -0,0 +1,12 @@
+{
+ "type": "service_account",
+ "project_id": "fluent-bit",
+ "private_key_id": "837364849384b143e4e21243a2cbcbd26f4fabcdfe",
+ "private_key": "-----BEGIN PRIVATE KEY-----\nasldjkasdjksa98327498327498327489qajdsijasdjlksajdlksajdlks7+TK\n-----END PRIVATE KEY-----\n",
+ "client_email": "stackdriver@fluent-bit.iam.gserviceaccount.com",
+ "client_id": "0123456789",
+ "auth_uri": "https://accounts.google.com/o/oauth2/auth",
+ "token_uri": "https://accounts.google.com/o/oauth2/token",
+ "auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
+ "client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/stackdriver%40fluent-bit.iam.gserviceaccount.com"
+}
diff --git a/fluent-bit/tests/runtime/data/stackdriver/stackdriver_multi_entries_severity.log b/fluent-bit/tests/runtime/data/stackdriver/stackdriver_multi_entries_severity.log
new file mode 100644
index 00000000..157cbea6
--- /dev/null
+++ b/fluent-bit/tests/runtime/data/stackdriver/stackdriver_multi_entries_severity.log
@@ -0,0 +1,4 @@
+{"message":"test severity1", "severity":"INFO"}
+{"message":"test severity2"}
+{"message":"test severity3", "severity":"DEBUG"}
+{"message":"test severity4"}
diff --git a/fluent-bit/tests/runtime/data/stackdriver/stackdriver_test_http_request.h b/fluent-bit/tests/runtime/data/stackdriver/stackdriver_test_http_request.h
new file mode 100644
index 00000000..2b7ad82d
--- /dev/null
+++ b/fluent-bit/tests/runtime/data/stackdriver/stackdriver_test_http_request.h
@@ -0,0 +1,134 @@
+#define HTTPREQUEST_COMMON_CASE "[" \
+ "1591111124," \
+ "{" \
+ "\"logging.googleapis.com/http_request\": " \
+ "{" \
+ "\"requestMethod\": \"test_requestMethod\"," \
+ "\"requestUrl\": \"test_requestUrl\"," \
+ "\"userAgent\": \"test_userAgent\"," \
+ "\"remoteIp\": \"test_remoteIp\"," \
+ "\"serverIp\": \"test_serverIp\"," \
+ "\"referer\": \"test_referer\"," \
+ "\"latency\": \"0s\"," \
+ "\"protocol\": \"test_protocol\"," \
+ "\"requestSize\": 123," \
+ "\"responseSize\": 123," \
+ "\"status\": 200," \
+ "\"cacheFillBytes\": 123," \
+ "\"cacheLookup\": true," \
+ "\"cacheHit\": true," \
+ "\"cacheValidatedWithOriginServer\": true" \
+ "}" \
+ "}]"
+
+#define EMPTY_HTTPREQUEST "[" \
+ "1591111124," \
+ "{" \
+ "\"logging.googleapis.com/http_request\": " \
+ "{" \
+ "}" \
+ "}]"
+
+#define HTTPREQUEST_IN_STRING "[" \
+ "1591111124," \
+ "{" \
+ "\"logging.googleapis.com/http_request\": \"some string\"" \
+ "}]"
+
+#define PARTIAL_HTTPREQUEST "[" \
+ "1591111124," \
+ "{" \
+ "\"logging.googleapis.com/http_request\": " \
+ "{" \
+ "\"cacheLookup\": true," \
+ "\"cacheHit\": true," \
+ "\"cacheValidatedWithOriginServer\": true" \
+ "}" \
+ "}]"
+
+#define HTTPREQUEST_SUBFIELDS_IN_INCORRECT_TYPE "[" \
+ "1591111124," \
+ "{" \
+ "\"logging.googleapis.com/http_request\": " \
+ "{" \
+ "\"requestMethod\": 123," \
+ "\"requestUrl\": 123," \
+ "\"userAgent\": 123," \
+ "\"remoteIp\": 123," \
+ "\"serverIp\": true," \
+ "\"referer\": true," \
+ "\"latency\": false," \
+ "\"protocol\": false," \
+ "\"requestSize\": \"some string\"," \
+ "\"responseSize\": true," \
+ "\"status\": false," \
+ "\"cacheFillBytes\": false," \
+ "\"cacheLookup\": \"some string\"," \
+ "\"cacheHit\": 123," \
+ "\"cacheValidatedWithOriginServer\": 123" \
+ "}" \
+ "}]"
+
+#define HTTPREQUEST_EXTRA_SUBFIELDS_EXISTED "[" \
+ "1591111124," \
+ "{" \
+ "\"logging.googleapis.com/http_request\": " \
+ "{" \
+ "\"requestMethod\": \"test_requestMethod\"," \
+ "\"requestUrl\": \"test_requestUrl\"," \
+ "\"userAgent\": \"test_userAgent\"," \
+ "\"remoteIp\": \"test_remoteIp\"," \
+ "\"serverIp\": \"test_serverIp\"," \
+ "\"referer\": \"test_referer\"," \
+ "\"latency\": \"0s\"," \
+ "\"protocol\": \"test_protocol\"," \
+ "\"requestSize\": 123," \
+ "\"responseSize\": 123," \
+ "\"status\": 200," \
+ "\"cacheFillBytes\": 123," \
+ "\"cacheLookup\": true," \
+ "\"cacheHit\": true," \
+ "\"cacheValidatedWithOriginServer\": true," \
+ "\"extra_key1\": \"extra_val1\"," \
+ "\"extra_key2\": 123," \
+ "\"extra_key3\": true" \
+ "}" \
+ "}]"
+
+
+/* Tests for 'latency' */
+#define HTTPREQUEST_LATENCY_COMMON_CASE "[" \
+ "1591111124," \
+ "{" \
+ "\"logging.googleapis.com/http_request\": " \
+ "{" \
+ "\"latency\": \" 100.00 s \"" \
+ "}" \
+ "}]"
+
+#define HTTPREQUEST_LATENCY_INVALID_SPACES "[" \
+ "1591111124," \
+ "{" \
+ "\"logging.googleapis.com/http_request\": " \
+ "{" \
+ "\"latency\": \" 100. 00 s \"" \
+ "}" \
+ "}]"
+
+#define HTTPREQUEST_LATENCY_INVALID_STRING "[" \
+ "1591111124," \
+ "{" \
+ "\"logging.googleapis.com/http_request\": " \
+ "{" \
+ "\"latency\": \" s100.00 s \"" \
+ "}" \
+ "}]"
+
+#define HTTPREQUEST_LATENCY_INVALID_END "[" \
+ "1591111124," \
+ "{" \
+ "\"logging.googleapis.com/http_request\": " \
+ "{" \
+ "\"latency\": \" 100.00 \"" \
+ "}" \
+ "}]"
diff --git a/fluent-bit/tests/runtime/data/stackdriver/stackdriver_test_insert_id.h b/fluent-bit/tests/runtime/data/stackdriver/stackdriver_test_insert_id.h
new file mode 100644
index 00000000..9cdf3f54
--- /dev/null
+++ b/fluent-bit/tests/runtime/data/stackdriver/stackdriver_test_insert_id.h
@@ -0,0 +1,26 @@
+
+#define INSERTID_COMMON_CASE "[" \
+ "1591111124," \
+ "{" \
+ "\"logging.googleapis.com/insertId\": \"test_insertId\" " \
+ "}]"
+
+#define EMPTY_INSERTID "[" \
+ "1591111124," \
+ "{" \
+ "\"logging.googleapis.com/insertId\": \"\" " \
+ "}]"
+
+#define INSERTID_INCORRECT_TYPE_INT "[" \
+ "1591111124," \
+ "{" \
+ "\"logging.googleapis.com/insertId\": 123 " \
+ "}]"
+
+#define INSERTID_INCORRECT_TYPE_MAP "[" \
+ "1591111124," \
+ "{" \
+ "\"logging.googleapis.com/insertId\": " \
+ "{" \
+ "}" \
+ "}]"
diff --git a/fluent-bit/tests/runtime/data/stackdriver/stackdriver_test_k8s_resource.h b/fluent-bit/tests/runtime/data/stackdriver/stackdriver_test_k8s_resource.h
new file mode 100644
index 00000000..9fe1604a
--- /dev/null
+++ b/fluent-bit/tests/runtime/data/stackdriver/stackdriver_test_k8s_resource.h
@@ -0,0 +1,89 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/* k8s_container */
+#define K8S_CONTAINER_COMMON "[" \
+ "1591649196," \
+ "{" \
+ "\"message\": \"K8S_CONTAINER_COMMON\"," \
+ "\"logging.googleapis.com/local_resource_id\": \"k8s_container.testnamespace.testpod.testctr\"" \
+ "}]"
+
+#define K8S_CONTAINER_COMMON_DIFF_TAGS "[" \
+ "1591649196," \
+ "{" \
+ "\"message\": \"K8S_CONTAINER_COMMON\"," \
+ "\"logging.googleapis.com/local_resource_id\": \"k8s_container.diffnamespace.diffpod.diffctr\"" \
+ "}]"
+
+#define K8S_CONTAINER_NO_LOCAL_RESOURCE_ID "[" \
+ "1591649196," \
+ "{" \
+ "\"message\": \"K8S_CONTAINER_COMMON_NO_LOCAL_RESOURCE_ID\"" \
+ "}]"
+
+/* k8s_node */
+#define K8S_NODE_COMMON "[" \
+ "1591649196," \
+ "{" \
+ "\"message\": \"K8S_NODE_COMMON\"," \
+ "\"logging.googleapis.com/local_resource_id\": \"k8s_node.testnode\"," \
+ "\"PRIORITY\": 6," \
+ "\"SYSLOG_FACILITY\": 3," \
+ "\"_CAP_EFFECTIVE\": \"3fffffffff\"," \
+ "\"_PID\": 1387," \
+ "\"_SYSTEMD_UNIT\": \"docker.service\"," \
+ "\"END_KEY\": \"JSON_END\"" \
+ "}]"
+
+#define K8S_NODE_NO_LOCAL_RESOURCE_ID "[" \
+ "1591649196," \
+ "{" \
+ "\"message\": \"K8S_NODE_NO_LOCAL_RESOURCE_ID\"," \
+ "\"PRIORITY\": 6," \
+ "\"SYSLOG_FACILITY\": 3," \
+ "\"_CAP_EFFECTIVE\": \"3fffffffff\"," \
+ "\"_PID\": 1387," \
+ "\"_SYSTEMD_UNIT\": \"docker.service\"," \
+ "\"END_KEY\": \"JSON_END\"" \
+ "}]"
+
+#define K8S_NODE_LOCAL_RESOURCE_ID_WITH_DOT "[" \
+ "1591649196," \
+ "{" \
+ "\"message\": \"K8S_NODE_LOCAL_RESOURCE_ID_WITH_DOT\"," \
+ "\"logging.googleapis.com/local_resource_id\": \"k8s_node.testnode.withdot.dot\"," \
+ "\"PRIORITY\": 6," \
+ "\"SYSLOG_FACILITY\": 3," \
+ "\"_CAP_EFFECTIVE\": \"3fffffffff\"," \
+ "\"_PID\": 1387," \
+ "\"_SYSTEMD_UNIT\": \"docker.service\"," \
+ "\"END_KEY\": \"JSON_END\"" \
+ "}]"
+
+/* k8s_pod */
+#define K8S_POD_COMMON "[" \
+ "1591649196," \
+ "{" \
+ "\"message\": \"K8S_POD_COMMON\"," \
+ "\"logging.googleapis.com/local_resource_id\": \"k8s_pod.testnamespace.testpod\"," \
+ "\"key_0\": false," \
+ "\"key_1\": true," \
+ "\"key_2\": \"some string\"," \
+ "\"key_3\": 0.12345678," \
+ "\"key_4\": 5000," \
+ "\"END_KEY\": \"JSON_END\"" \
+ "}]"
+
+#define K8S_POD_NO_LOCAL_RESOURCE_ID "[" \
+ "1591649196," \
+ "{" \
+ "\"message\": \"K8S_POD_NO_LOCAL_RESOURCE_ID\"," \
+ "\"key_0\": false," \
+ "\"key_1\": true," \
+ "\"key_2\": \"some string\"," \
+ "\"key_3\": 0.12345678," \
+ "\"key_4\": 5000," \
+ "\"END_KEY\": \"JSON_END\"" \
+ "}]"
+
+ \ No newline at end of file
diff --git a/fluent-bit/tests/runtime/data/stackdriver/stackdriver_test_labels.h b/fluent-bit/tests/runtime/data/stackdriver/stackdriver_test_labels.h
new file mode 100644
index 00000000..ec3d80b3
--- /dev/null
+++ b/fluent-bit/tests/runtime/data/stackdriver/stackdriver_test_labels.h
@@ -0,0 +1,44 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+#define DEFAULT_LABELS "[" \
+ "1591649196," \
+ "{" \
+ "\"logging.googleapis.com/labels\": " \
+ "{" \
+ "\"testA\": \"valA\"," \
+ "\"testB\": \"valB\"" \
+ "}" \
+ "}]"
+
+#define CUSTOM_LABELS "[" \
+ "1591649196," \
+ "{" \
+ "\"logging.googleapis.com/customlabels\": " \
+ "{" \
+ "\"testA\": \"valA\"," \
+ "\"testB\": \"valB\"" \
+ "}" \
+ "}]"
+
+#define DEFAULT_LABELS_K8S_RESOURCE_TYPE "[" \
+ "1591649196," \
+ "{" \
+ "\"logging.googleapis.com/local_resource_id\": \"k8s_container.testnamespace.testpod.testctr\"," \
+ "\"logging.googleapis.com/labels\": " \
+ "{" \
+ "\"testA\": \"valA\"," \
+ "\"testB\": \"valB\"" \
+ "}" \
+ "}]"
+
+#define CUSTOM_LABELS_K8S_RESOURCE_TYPE "[" \
+ "1591649196," \
+ "{" \
+ "\"logging.googleapis.com/local_resource_id\": \"k8s_container.testnamespace.testpod.testctr\"," \
+ "\"logging.googleapis.com/customlabels\": " \
+ "{" \
+ "\"testA\": \"valA\"," \
+ "\"testB\": \"valB\"" \
+ "}" \
+ "}]"
+
diff --git a/fluent-bit/tests/runtime/data/stackdriver/stackdriver_test_log_name.h b/fluent-bit/tests/runtime/data/stackdriver/stackdriver_test_log_name.h
new file mode 100644
index 00000000..4d39e242
--- /dev/null
+++ b/fluent-bit/tests/runtime/data/stackdriver/stackdriver_test_log_name.h
@@ -0,0 +1,10 @@
+#define LOG_NAME_OVERRIDE "[" \
+ "1591111124," \
+ "{" \
+ "\"custom_log_name_key\": \"custom_log_name\"" \
+ "}]"
+
+#define LOG_NAME_NO_OVERRIDE "[" \
+ "1591111124," \
+ "{" \
+ "}]"
diff --git a/fluent-bit/tests/runtime/data/stackdriver/stackdriver_test_monitored_resource.h b/fluent-bit/tests/runtime/data/stackdriver/stackdriver_test_monitored_resource.h
new file mode 100644
index 00000000..d1874928
--- /dev/null
+++ b/fluent-bit/tests/runtime/data/stackdriver/stackdriver_test_monitored_resource.h
@@ -0,0 +1,46 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+#define MONITORED_RESOURCE_COMMON_CASE "[" \
+ "1591649196," \
+ "{" \
+ "\"logging.googleapis.com/monitored_resource\": " \
+ "{" \
+ "\"labels\": " \
+ "{" \
+ "\"project_id\": \"monitored_resource_project_id\"," \
+ "\"location\": \"monitored_resource_location\"," \
+ "\"testA\": \"valA\"" \
+ "}" \
+ "}" \
+ "}]"
+
+#define MONITORED_RESOURCE_PRIORITY_HIGHER_THAN_LOCAL_RESOURCE_ID "[" \
+ "1591649196," \
+ "{" \
+ "\"logging.googleapis.com/monitored_resource\": " \
+ "{" \
+ "\"labels\": " \
+ "{" \
+ "\"project_id\": \"monitored_resource_project_id\"," \
+ "\"location\": \"monitored_resource_location\"," \
+ "\"cluster_name\": \"monitored_resource_cluster_name\"," \
+ "\"namespace_name\": \"monitored_resource_namespace_name\"," \
+ "\"pod_name\": \"monitored_resource_pod_name\"," \
+ "\"container_name\": \"monitored_resource_container_name\"" \
+ "}" \
+ "}," \
+ "\"logging.googleapis.com/local_resource_id\": \"k8s_container.testnamespace.testpod.testctr\"" \
+ "}]"
+
+#define MONITORED_RESOURCE_PRIORITY_HIGHER_THAN_GCE_INSTANCE "[" \
+ "1448403340," \
+ "{" \
+ "\"logging.googleapis.com/monitored_resource\": " \
+ "{" \
+ "\"labels\": " \
+ "{" \
+ "\"project_id\": \"monitored_resource_project_id\"," \
+ "\"zone\": \"monitored_resource_zone\"," \
+ "\"instance_id\": \"monitored_resource_instance_id\"" \
+ "}" \
+ "}" \
+ "}]"
diff --git a/fluent-bit/tests/runtime/data/stackdriver/stackdriver_test_operation.h b/fluent-bit/tests/runtime/data/stackdriver/stackdriver_test_operation.h
new file mode 100644
index 00000000..b3af3722
--- /dev/null
+++ b/fluent-bit/tests/runtime/data/stackdriver/stackdriver_test_operation.h
@@ -0,0 +1,62 @@
+#define OPERATION_COMMON_CASE "[" \
+ "1591111124," \
+ "{" \
+ "\"logging.googleapis.com/operation\": " \
+ "{" \
+ "\"id\": \"test_id\"," \
+ "\"producer\": \"test_producer\"," \
+ "\"first\": true," \
+ "\"last\": true" \
+ "}" \
+ "}]"
+
+#define EMPTY_OPERATION "[" \
+ "1591111124," \
+ "{" \
+ "\"logging.googleapis.com/operation\": " \
+ "{" \
+ "}" \
+ "}]"
+
+#define OPERATION_IN_STRING "[" \
+ "1591111124," \
+ "{" \
+ "\"logging.googleapis.com/operation\": \"some string\"" \
+ "}]"
+
+#define PARTIAL_SUBFIELDS "[" \
+ "1591111124," \
+ "{" \
+ "\"logging.googleapis.com/operation\": " \
+ "{" \
+ "\"first\": false," \
+ "\"last\": false" \
+ "}" \
+ "}]"
+
+#define SUBFIELDS_IN_INCORRECT_TYPE "[" \
+ "1591111124," \
+ "{" \
+ "\"logging.googleapis.com/operation\": " \
+ "{" \
+ "\"id\": 123," \
+ "\"producer\": true," \
+ "\"first\": \"some string\"," \
+ "\"last\": 123" \
+ "}" \
+ "}]"
+
+#define EXTRA_SUBFIELDS_EXISTED "[" \
+ "1591111124," \
+ "{" \
+ "\"logging.googleapis.com/operation\": " \
+ "{" \
+ "\"id\": \"test_id\"," \
+ "\"producer\": \"test_producer\"," \
+ "\"first\": true," \
+ "\"last\": true," \
+ "\"extra_key1\": \"extra_val1\"," \
+ "\"extra_key2\": 123," \
+ "\"extra_key3\": true" \
+ "}" \
+ "}]"
diff --git a/fluent-bit/tests/runtime/data/stackdriver/stackdriver_test_resource_labels.h b/fluent-bit/tests/runtime/data/stackdriver/stackdriver_test_resource_labels.h
new file mode 100644
index 00000000..a65a4714
--- /dev/null
+++ b/fluent-bit/tests/runtime/data/stackdriver/stackdriver_test_resource_labels.h
@@ -0,0 +1,37 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+#define ONE_FIELD "[" \
+ "1591649196," \
+ "{" \
+ "\"keyA\": \"valA\"" \
+ "}]"
+
+#define MULTIPLE_FIELDS "[" \
+ "1591649196," \
+ "{" \
+ "\"keyA\": \"valA\"," \
+ "\"keyB\": \"valB\"" \
+ "}]"
+
+#define NESTED_FIELDS "[" \
+ "1591649196," \
+ "{" \
+ "\"toplevel\": " \
+ "{" \
+ "\"keyA\": \"valA\"," \
+ "\"keyB\": \"valB\"" \
+ "}" \
+ "}]"
+
+#define LAYERED_NESTED_FIELDS "[" \
+ "1591649196," \
+ "{" \
+ "\"toplevel\": " \
+ "{" \
+ "\"midlevel\": " \
+ "{" \
+ "\"keyA\": \"valA\"" \
+ "}," \
+ "\"keyB\": \"valB\"" \
+ "}" \
+ "}]"
diff --git a/fluent-bit/tests/runtime/data/stackdriver/stackdriver_test_source_location.h b/fluent-bit/tests/runtime/data/stackdriver/stackdriver_test_source_location.h
new file mode 100644
index 00000000..0bf419f5
--- /dev/null
+++ b/fluent-bit/tests/runtime/data/stackdriver/stackdriver_test_source_location.h
@@ -0,0 +1,70 @@
+#define SOURCELOCATION_COMMON_CASE "[" \
+ "1591111124," \
+ "{" \
+ "\"logging.googleapis.com/sourceLocation\": " \
+ "{" \
+ "\"file\": \"test_file\"," \
+ "\"line\": 123," \
+ "\"function\": \"test_function\"" \
+ "}" \
+ "}]"
+
+#define SOURCELOCATION_COMMON_CASE_LINE_IN_STRING "[" \
+ "1591111124," \
+ "{" \
+ "\"logging.googleapis.com/sourceLocation\": " \
+ "{" \
+ "\"file\": \"test_file\"," \
+ "\"line\": \"123\"," \
+ "\"function\": \"test_function\"" \
+ "}" \
+ "}]"
+
+#define EMPTY_SOURCELOCATION "[" \
+ "1591111124," \
+ "{" \
+ "\"logging.googleapis.com/sourceLocation\": " \
+ "{" \
+ "}" \
+ "}]"
+
+#define SOURCELOCATION_IN_STRING "[" \
+ "1591111124," \
+ "{" \
+ "\"logging.googleapis.com/sourceLocation\": \"some string\"" \
+ "}]"
+
+#define PARTIAL_SOURCELOCATION "[" \
+ "1591111124," \
+ "{" \
+ "\"logging.googleapis.com/sourceLocation\": " \
+ "{" \
+ "\"function\": \"test_function\"" \
+ "}" \
+ "}]"
+
+#define SOURCELOCATION_SUBFIELDS_IN_INCORRECT_TYPE "[" \
+ "1591111124," \
+ "{" \
+ "\"logging.googleapis.com/sourceLocation\": " \
+ "{" \
+ "\"file\": 123," \
+ "\"line\": \"some string\"," \
+ "\"function\": true" \
+ "}" \
+ "}]"
+
+#define SOURCELOCATION_EXTRA_SUBFIELDS_EXISTED "[" \
+ "1591111124," \
+ "{" \
+ "\"logging.googleapis.com/sourceLocation\": " \
+ "{" \
+ "\"file\": \"test_file\"," \
+ "\"line\": 123," \
+ "\"function\": \"test_function\"," \
+ "\"extra_key1\": \"extra_val1\"," \
+ "\"extra_key2\": 123," \
+ "\"extra_key3\": true" \
+ "}" \
+ "}]"
+ \ No newline at end of file
diff --git a/fluent-bit/tests/runtime/data/stackdriver/stackdriver_test_span_id.h b/fluent-bit/tests/runtime/data/stackdriver/stackdriver_test_span_id.h
new file mode 100644
index 00000000..5b7b823e
--- /dev/null
+++ b/fluent-bit/tests/runtime/data/stackdriver/stackdriver_test_span_id.h
@@ -0,0 +1,6 @@
+#define SPAN_ID_COMMON_CASE "[" \
+ "1591111124," \
+ "{" \
+ "\"logging.googleapis.com/spanId\": \"000000000000004a\"" \
+ "}]"
+ \ No newline at end of file
diff --git a/fluent-bit/tests/runtime/data/stackdriver/stackdriver_test_timestamp.h b/fluent-bit/tests/runtime/data/stackdriver/stackdriver_test_timestamp.h
new file mode 100644
index 00000000..f80cc37d
--- /dev/null
+++ b/fluent-bit/tests/runtime/data/stackdriver/stackdriver_test_timestamp.h
@@ -0,0 +1,62 @@
+/* timestamp after parsing: 2020-07-21T16:40:42.000012345Z */
+#define TIMESTAMP_FORMAT_OBJECT_COMMON_CASE "[" \
+ "1591111124," \
+ "{" \
+ "\"timestamp\": " \
+ "{" \
+ "\"seconds\": \"1595349642\"," \
+ "\"nanos\": \"12345\"" \
+ "}" \
+ "}]"
+
+/* "1595349600" in RFC3339 format: 2020-07-21T16:40:00Z */
+#define TIMESTAMP_FORMAT_OBJECT_NOT_A_MAP "[" \
+ "1595349600," \
+ "{" \
+ "\"timestamp\": \"string\"" \
+ "}]"
+
+/* "1595349600" in RFC3339 format: 2020-07-21T16:40:00Z */
+#define TIMESTAMP_FORMAT_OBJECT_MISSING_SUBFIELD "[" \
+ "1595349600," \
+ "{" \
+ "\"timestamp\": " \
+ "{" \
+ "\"nanos\": \"12345\"" \
+ "}" \
+ "}]"
+
+/* "1595349600" in RFC3339 format: 2020-07-21T16:40:00Z */
+#define TIMESTAMP_FORMAT_OBJECT_INCORRECT_TYPE_SUBFIELDS "[" \
+ "1595349600," \
+ "{" \
+ "\"timestamp\": " \
+ "{" \
+ "\"seconds\": \"string\"," \
+ "\"nanos\": true" \
+ "}" \
+ "}]"
+
+
+/* timestamp after parsing: 2020-07-21T16:40:42.000012345Z */
+#define TIMESTAMP_FORMAT_DUO_FIELDS_COMMON_CASE "[" \
+ "1595349600," \
+ "{" \
+ "\"timestampSeconds\": \"1595349642\"," \
+ "\"timestampNanos\": \"12345\"" \
+ "}]"
+
+/* "1595349600" in RFC3339 format: 2020-07-21T16:40:00Z */
+#define TIMESTAMP_FORMAT_DUO_FIELDS_MISSING_NANOS "[" \
+ "1595349600," \
+ "{" \
+ "\"timestampSeconds\": \"1595349642\"" \
+ "}]"
+
+/* "1595349600" in RFC3339 format: 2020-07-21T16:40:00Z */
+#define TIMESTAMP_FORMAT_DUO_FIELDS_INCORRECT_TYPE "[" \
+ "1595349600," \
+ "{" \
+ "\"timestampSeconds\": \"string\"," \
+ "\"timestampNanos\": true" \
+ "}]"
diff --git a/fluent-bit/tests/runtime/data/stackdriver/stackdriver_test_trace.h b/fluent-bit/tests/runtime/data/stackdriver/stackdriver_test_trace.h
new file mode 100644
index 00000000..15c7bc91
--- /dev/null
+++ b/fluent-bit/tests/runtime/data/stackdriver/stackdriver_test_trace.h
@@ -0,0 +1,6 @@
+#define TRACE_COMMON_CASE "[" \
+ "1591111124," \
+ "{" \
+ "\"trace\": \"test-trace-id-xyz\"" \
+ "}]"
+ \ No newline at end of file
diff --git a/fluent-bit/tests/runtime/data/stackdriver/stackdriver_test_trace_sampled.h b/fluent-bit/tests/runtime/data/stackdriver/stackdriver_test_trace_sampled.h
new file mode 100644
index 00000000..fad628e9
--- /dev/null
+++ b/fluent-bit/tests/runtime/data/stackdriver/stackdriver_test_trace_sampled.h
@@ -0,0 +1,11 @@
+#define TRACE_SAMPLED_CASE_TRUE "[" \
+ "1591111124," \
+ "{" \
+ "\"logging.googleapis.com/traceSampled\": true" \
+ "}]"
+
+#define TRACE_SAMPLED_CASE_FALSE "[" \
+ "1591111124," \
+ "{" \
+ "\"logging.googleapis.com/traceSampled\": false" \
+ "}]"
diff --git a/fluent-bit/tests/runtime/data/tail/log/3943.log b/fluent-bit/tests/runtime/data/tail/log/3943.log
new file mode 100644
index 00000000..8f3d91d9
--- /dev/null
+++ b/fluent-bit/tests/runtime/data/tail/log/3943.log
@@ -0,0 +1,2 @@
+bbb
+
diff --git a/fluent-bit/tests/runtime/data/tail/log/dockermode.log b/fluent-bit/tests/runtime/data/tail/log/dockermode.log
new file mode 100644
index 00000000..a5a05ee4
--- /dev/null
+++ b/fluent-bit/tests/runtime/data/tail/log/dockermode.log
@@ -0,0 +1,3 @@
+{"log":"Single log\n"}
+{"log":"Second log\n"}
+{"log":"Third log\n"}
diff --git a/fluent-bit/tests/runtime/data/tail/log/dockermode_firstline_detection.log b/fluent-bit/tests/runtime/data/tail/log/dockermode_firstline_detection.log
new file mode 100644
index 00000000..56a2711a
--- /dev/null
+++ b/fluent-bit/tests/runtime/data/tail/log/dockermode_firstline_detection.log
@@ -0,0 +1,9 @@
+{"log":"Single log\n"}
+{"log":"Second log\n"}
+{"log":"Third log\n"}
+{"log":"2020-03-24 Multiple lines: \n"}
+{"log":"first bullet point\n"}
+{"log":"second bullet point\n"}
+{"log":"third bullet point\n"}
+{"log":"fourth bullet point\n"}
+{"log":"2020-03-24 Single line\n"}
diff --git a/fluent-bit/tests/runtime/data/tail/log/dockermode_multiple_lines.log b/fluent-bit/tests/runtime/data/tail/log/dockermode_multiple_lines.log
new file mode 100644
index 00000000..564dabbb
--- /dev/null
+++ b/fluent-bit/tests/runtime/data/tail/log/dockermode_multiple_lines.log
@@ -0,0 +1,6 @@
+{"log":"2020-03-24 Multiple lines: \n"}
+{"log":"first bullet point\n"}
+{"log":"second bullet point\n"}
+{"log":"third bullet point\n"}
+{"log":"fourth bullet point\n"}
+{"log":"2020-03-24 Single line\n"}
diff --git a/fluent-bit/tests/runtime/data/tail/log/dockermode_splitted_line.log b/fluent-bit/tests/runtime/data/tail/log/dockermode_splitted_line.log
new file mode 100644
index 00000000..8f80f1b3
--- /dev/null
+++ b/fluent-bit/tests/runtime/data/tail/log/dockermode_splitted_line.log
@@ -0,0 +1,6 @@
+{"log":"2020-03-24 Single "}
+{"log":"li"}
+{"log":"ne\n"}
+{"log":"2020-03-24 Another "}
+{"log":"single "}
+{"log":"line\n"}
diff --git a/fluent-bit/tests/runtime/data/tail/log/dockermode_splitted_multiple_lines.log b/fluent-bit/tests/runtime/data/tail/log/dockermode_splitted_multiple_lines.log
new file mode 100644
index 00000000..2c7d36dd
--- /dev/null
+++ b/fluent-bit/tests/runtime/data/tail/log/dockermode_splitted_multiple_lines.log
@@ -0,0 +1,8 @@
+{"log":"2020-03-24 Multiple lines: \n"}
+{"log":"first bullet point\n"}
+{"log":"second bullet point\n"}
+{"log":"third "}
+{"log":"bullet "}
+{"log":"point\n"}
+{"log":"fourth bullet point\n"}
+{"log":"2020-03-24 Single line\n"}
diff --git a/fluent-bit/tests/runtime/data/tail/log/multiline_001.log b/fluent-bit/tests/runtime/data/tail/log/multiline_001.log
new file mode 100644
index 00000000..986223d8
--- /dev/null
+++ b/fluent-bit/tests/runtime/data/tail/log/multiline_001.log
@@ -0,0 +1,6 @@
+{"key1": 12345, "key2": "abc", "time": "2006-07-28T13:22:04Z","message":"Dec 14 06:41:08 Exception in thread main java.lang.RuntimeException: Something has gone wrong, aborting!"}
+{"key1": 12345, "key2": "abc", "time": "2006-07-28T13:22:04Z","message":" at com.myproject.module.MyProject.badMethod(MyProject.java:22)"}
+{"key1": 12345, "key2": "abc", "time": "2006-07-28T13:22:04Z","message":" at com.myproject.module.MyProject.oneMoreMethod(MyProject.java:18)"}
+{"key1": 12345, "key2": "abc", "time": "2006-07-28T13:22:04Z","message":" at com.myproject.module.MyProject.anotherMethod(MyProject.java:14)"}
+{"key1": 12345, "key2": "abc", "time": "2006-07-28T13:22:04Z","message":" at com.myproject.module.MyProject.oneMoreMethod(MyProject.java:13)"}
+{"key1": 12345, "key2": "abc", "time": "2006-07-28T13:22:04Z","message":" at com.myproject.module.MyProject.oneMoreMethod(MyProject.java:12)"}
diff --git a/fluent-bit/tests/runtime/data/tail/out/3943.out b/fluent-bit/tests/runtime/data/tail/out/3943.out
new file mode 100644
index 00000000..52a0f10a
--- /dev/null
+++ b/fluent-bit/tests/runtime/data/tail/out/3943.out
@@ -0,0 +1,2 @@
+{"log":"bbb"}
+{"log":""} \ No newline at end of file
diff --git a/fluent-bit/tests/runtime/data/tail/out/dockermode.out b/fluent-bit/tests/runtime/data/tail/out/dockermode.out
new file mode 100644
index 00000000..a5a05ee4
--- /dev/null
+++ b/fluent-bit/tests/runtime/data/tail/out/dockermode.out
@@ -0,0 +1,3 @@
+{"log":"Single log\n"}
+{"log":"Second log\n"}
+{"log":"Third log\n"}
diff --git a/fluent-bit/tests/runtime/data/tail/out/dockermode_firstline_detection.out b/fluent-bit/tests/runtime/data/tail/out/dockermode_firstline_detection.out
new file mode 100644
index 00000000..00eecce9
--- /dev/null
+++ b/fluent-bit/tests/runtime/data/tail/out/dockermode_firstline_detection.out
@@ -0,0 +1,5 @@
+{"log":"Single log\n"}
+{"log":"Second log\n"}
+{"log":"Third log\n"}
+{"log":"2020-03-24 Multiple lines: \nfirst bullet point\nsecond bullet point\nthird bullet point\nfourth bullet point\n"}
+{"log":"2020-03-24 Single line\n"}
diff --git a/fluent-bit/tests/runtime/data/tail/out/dockermode_multiple_lines.out b/fluent-bit/tests/runtime/data/tail/out/dockermode_multiple_lines.out
new file mode 100644
index 00000000..27fba1b9
--- /dev/null
+++ b/fluent-bit/tests/runtime/data/tail/out/dockermode_multiple_lines.out
@@ -0,0 +1,2 @@
+{"log":"2020-03-24 Multiple lines: \nfirst bullet point\nsecond bullet point\nthird bullet point\nfourth bullet point\n"}
+{"log":"2020-03-24 Single line\n"}
diff --git a/fluent-bit/tests/runtime/data/tail/out/dockermode_splitted_line.out b/fluent-bit/tests/runtime/data/tail/out/dockermode_splitted_line.out
new file mode 100644
index 00000000..14d0aa8a
--- /dev/null
+++ b/fluent-bit/tests/runtime/data/tail/out/dockermode_splitted_line.out
@@ -0,0 +1,2 @@
+{"log":"2020-03-24 Single line\n"}
+{"log":"2020-03-24 Another single line\n"}
diff --git a/fluent-bit/tests/runtime/data/tail/out/dockermode_splitted_multiple_lines.out b/fluent-bit/tests/runtime/data/tail/out/dockermode_splitted_multiple_lines.out
new file mode 100644
index 00000000..27fba1b9
--- /dev/null
+++ b/fluent-bit/tests/runtime/data/tail/out/dockermode_splitted_multiple_lines.out
@@ -0,0 +1,2 @@
+{"log":"2020-03-24 Multiple lines: \nfirst bullet point\nsecond bullet point\nthird bullet point\nfourth bullet point\n"}
+{"log":"2020-03-24 Single line\n"}
diff --git a/fluent-bit/tests/runtime/data/tail/out/multiline_001.out b/fluent-bit/tests/runtime/data/tail/out/multiline_001.out
new file mode 100644
index 00000000..73f48580
--- /dev/null
+++ b/fluent-bit/tests/runtime/data/tail/out/multiline_001.out
@@ -0,0 +1 @@
+{"key1":12345,"key2":"abc","time":"2006-07-28T13:22:04Z","message":"Dec 14 06:41:08 Exception in thread main java.lang.RuntimeException: Something has gone wrong, aborting!\n at com.myproject.module.MyProject.badMethod(MyProject.java:22)\n at com.myproject.module.MyProject.oneMoreMethod(MyProject.java:18)\n at com.myproject.module.MyProject.anotherMethod(MyProject.java:14)\n at com.myproject.module.MyProject.oneMoreMethod(MyProject.java:13)\n at com.myproject.module.MyProject.oneMoreMethod(MyProject.java:12)"}
diff --git a/fluent-bit/tests/runtime/data/tail/out/skip_long_lines.out b/fluent-bit/tests/runtime/data/tail/out/skip_long_lines.out
new file mode 100644
index 00000000..c7c32913
--- /dev/null
+++ b/fluent-bit/tests/runtime/data/tail/out/skip_long_lines.out
@@ -0,0 +1,2 @@
+{"log":"before_long_line"}
+{"log":"after_long_line"} \ No newline at end of file
diff --git a/fluent-bit/tests/runtime/data/tail/parsers.conf b/fluent-bit/tests/runtime/data/tail/parsers.conf
new file mode 100644
index 00000000..28bde475
--- /dev/null
+++ b/fluent-bit/tests/runtime/data/tail/parsers.conf
@@ -0,0 +1,10 @@
+[PARSER]
+ Name docker
+ Format json
+ Time_Key time
+ Time_Format %Y-%m-%dT%H:%M:%S.%L%z
+
+[PARSER]
+ Name docker_multiline
+ Format regex
+ Regex (?<log>^{"log":"\d{4}-\d{2}-\d{2}.*)
diff --git a/fluent-bit/tests/runtime/data/tail/parsers_multiline_json.conf b/fluent-bit/tests/runtime/data/tail/parsers_multiline_json.conf
new file mode 100644
index 00000000..1ed1cfdf
--- /dev/null
+++ b/fluent-bit/tests/runtime/data/tail/parsers_multiline_json.conf
@@ -0,0 +1,24 @@
+[PARSER]
+ Name json
+ Format json
+
+[MULTILINE_PARSER]
+ name multiline-json-regex
+ key_content message
+ type regex
+ parser json
+ flush_timeout 2000
+
+ #
+ # Regex rules for multiline parsing
+ # ---------------------------------
+ #
+ # configuration hints:
+ #
+ # - first state always has the name: start_state
+ # - every field in the rule must be inside double quotes
+ #
+ # rules | state name | regex pattern | next state
+ # ------|---------------|--------------------------------------------
+ rule "start_state" "/(Dec \d+ \d+\:\d+\:\d+)(.*)/" "cont"
+ rule "cont" "/^\s+at.*/" "cont"
diff --git a/fluent-bit/tests/runtime/data/td/json_td.h b/fluent-bit/tests/runtime/data/td/json_td.h
new file mode 100755
index 00000000..c9f4e01d
--- /dev/null
+++ b/fluent-bit/tests/runtime/data/td/json_td.h
@@ -0,0 +1,506 @@
+#define JSON_TD "[" \
+ "1448403340," \
+ "{" \
+ "\"key_0\": \"val_0\"," \
+ "\"key_1\": \"val_1\"," \
+ "\"key_2\": \"val_2\"," \
+ "\"key_3\": \"val_3\"," \
+ "\"key_4\": \"val_4\"," \
+ "\"key_5\": \"val_5\"," \
+ "\"key_6\": \"val_6\"," \
+ "\"key_7\": \"val_7\"," \
+ "\"key_8\": \"val_8\"," \
+ "\"key_9\": \"val_9\"," \
+ "\"key_10\": \"val_10\"," \
+ "\"key_11\": \"val_11\"," \
+ "\"key_12\": \"val_12\"," \
+ "\"key_13\": \"val_13\"," \
+ "\"key_14\": \"val_14\"," \
+ "\"key_15\": \"val_15\"," \
+ "\"key_16\": \"val_16\"," \
+ "\"key_17\": \"val_17\"," \
+ "\"key_18\": \"val_18\"," \
+ "\"key_19\": \"val_19\"," \
+ "\"key_20\": \"val_20\"," \
+ "\"key_21\": \"val_21\"," \
+ "\"key_22\": \"val_22\"," \
+ "\"key_23\": \"val_23\"," \
+ "\"key_24\": \"val_24\"," \
+ "\"key_25\": \"val_25\"," \
+ "\"key_26\": \"val_26\"," \
+ "\"key_27\": \"val_27\"," \
+ "\"key_28\": \"val_28\"," \
+ "\"key_29\": \"val_29\"," \
+ "\"key_30\": \"val_30\"," \
+ "\"key_31\": \"val_31\"," \
+ "\"key_32\": \"val_32\"," \
+ "\"key_33\": \"val_33\"," \
+ "\"key_34\": \"val_34\"," \
+ "\"key_35\": \"val_35\"," \
+ "\"key_36\": \"val_36\"," \
+ "\"key_37\": \"val_37\"," \
+ "\"key_38\": \"val_38\"," \
+ "\"key_39\": \"val_39\"," \
+ "\"key_40\": \"val_40\"," \
+ "\"key_41\": \"val_41\"," \
+ "\"key_42\": \"val_42\"," \
+ "\"key_43\": \"val_43\"," \
+ "\"key_44\": \"val_44\"," \
+ "\"key_45\": \"val_45\"," \
+ "\"key_46\": \"val_46\"," \
+ "\"key_47\": \"val_47\"," \
+ "\"key_48\": \"val_48\"," \
+ "\"key_49\": \"val_49\"," \
+ "\"key_50\": \"val_50\"," \
+ "\"key_51\": \"val_51\"," \
+ "\"key_52\": \"val_52\"," \
+ "\"key_53\": \"val_53\"," \
+ "\"key_54\": \"val_54\"," \
+ "\"key_55\": \"val_55\"," \
+ "\"key_56\": \"val_56\"," \
+ "\"key_57\": \"val_57\"," \
+ "\"key_58\": \"val_58\"," \
+ "\"key_59\": \"val_59\"," \
+ "\"key_60\": \"val_60\"," \
+ "\"key_61\": \"val_61\"," \
+ "\"key_62\": \"val_62\"," \
+ "\"key_63\": \"val_63\"," \
+ "\"key_64\": \"val_64\"," \
+ "\"key_65\": \"val_65\"," \
+ "\"key_66\": \"val_66\"," \
+ "\"key_67\": \"val_67\"," \
+ "\"key_68\": \"val_68\"," \
+ "\"key_69\": \"val_69\"," \
+ "\"key_70\": \"val_70\"," \
+ "\"key_71\": \"val_71\"," \
+ "\"key_72\": \"val_72\"," \
+ "\"key_73\": \"val_73\"," \
+ "\"key_74\": \"val_74\"," \
+ "\"key_75\": \"val_75\"," \
+ "\"key_76\": \"val_76\"," \
+ "\"key_77\": \"val_77\"," \
+ "\"key_78\": \"val_78\"," \
+ "\"key_79\": \"val_79\"," \
+ "\"key_80\": \"val_80\"," \
+ "\"key_81\": \"val_81\"," \
+ "\"key_82\": \"val_82\"," \
+ "\"key_83\": \"val_83\"," \
+ "\"key_84\": \"val_84\"," \
+ "\"key_85\": \"val_85\"," \
+ "\"key_86\": \"val_86\"," \
+ "\"key_87\": \"val_87\"," \
+ "\"key_88\": \"val_88\"," \
+ "\"key_89\": \"val_89\"," \
+ "\"key_90\": \"val_90\"," \
+ "\"key_91\": \"val_91\"," \
+ "\"key_92\": \"val_92\"," \
+ "\"key_93\": \"val_93\"," \
+ "\"key_94\": \"val_94\"," \
+ "\"key_95\": \"val_95\"," \
+ "\"key_96\": \"val_96\"," \
+ "\"key_97\": \"val_97\"," \
+ "\"key_98\": \"val_98\"," \
+ "\"key_99\": \"val_99\"," \
+ "\"key_100\": \"val_100\"," \
+ "\"key_101\": \"val_101\"," \
+ "\"key_102\": \"val_102\"," \
+ "\"key_103\": \"val_103\"," \
+ "\"key_104\": \"val_104\"," \
+ "\"key_105\": \"val_105\"," \
+ "\"key_106\": \"val_106\"," \
+ "\"key_107\": \"val_107\"," \
+ "\"key_108\": \"val_108\"," \
+ "\"key_109\": \"val_109\"," \
+ "\"key_110\": \"val_110\"," \
+ "\"key_111\": \"val_111\"," \
+ "\"key_112\": \"val_112\"," \
+ "\"key_113\": \"val_113\"," \
+ "\"key_114\": \"val_114\"," \
+ "\"key_115\": \"val_115\"," \
+ "\"key_116\": \"val_116\"," \
+ "\"key_117\": \"val_117\"," \
+ "\"key_118\": \"val_118\"," \
+ "\"key_119\": \"val_119\"," \
+ "\"key_120\": \"val_120\"," \
+ "\"key_121\": \"val_121\"," \
+ "\"key_122\": \"val_122\"," \
+ "\"key_123\": \"val_123\"," \
+ "\"key_124\": \"val_124\"," \
+ "\"key_125\": \"val_125\"," \
+ "\"key_126\": \"val_126\"," \
+ "\"key_127\": \"val_127\"," \
+ "\"key_128\": \"val_128\"," \
+ "\"key_129\": \"val_129\"," \
+ "\"key_130\": \"val_130\"," \
+ "\"key_131\": \"val_131\"," \
+ "\"key_132\": \"val_132\"," \
+ "\"key_133\": \"val_133\"," \
+ "\"key_134\": \"val_134\"," \
+ "\"key_135\": \"val_135\"," \
+ "\"key_136\": \"val_136\"," \
+ "\"key_137\": \"val_137\"," \
+ "\"key_138\": \"val_138\"," \
+ "\"key_139\": \"val_139\"," \
+ "\"key_140\": \"val_140\"," \
+ "\"key_141\": \"val_141\"," \
+ "\"key_142\": \"val_142\"," \
+ "\"key_143\": \"val_143\"," \
+ "\"key_144\": \"val_144\"," \
+ "\"key_145\": \"val_145\"," \
+ "\"key_146\": \"val_146\"," \
+ "\"key_147\": \"val_147\"," \
+ "\"key_148\": \"val_148\"," \
+ "\"key_149\": \"val_149\"," \
+ "\"key_150\": \"val_150\"," \
+ "\"key_151\": \"val_151\"," \
+ "\"key_152\": \"val_152\"," \
+ "\"key_153\": \"val_153\"," \
+ "\"key_154\": \"val_154\"," \
+ "\"key_155\": \"val_155\"," \
+ "\"key_156\": \"val_156\"," \
+ "\"key_157\": \"val_157\"," \
+ "\"key_158\": \"val_158\"," \
+ "\"key_159\": \"val_159\"," \
+ "\"key_160\": \"val_160\"," \
+ "\"key_161\": \"val_161\"," \
+ "\"key_162\": \"val_162\"," \
+ "\"key_163\": \"val_163\"," \
+ "\"key_164\": \"val_164\"," \
+ "\"key_165\": \"val_165\"," \
+ "\"key_166\": \"val_166\"," \
+ "\"key_167\": \"val_167\"," \
+ "\"key_168\": \"val_168\"," \
+ "\"key_169\": \"val_169\"," \
+ "\"key_170\": \"val_170\"," \
+ "\"key_171\": \"val_171\"," \
+ "\"key_172\": \"val_172\"," \
+ "\"key_173\": \"val_173\"," \
+ "\"key_174\": \"val_174\"," \
+ "\"key_175\": \"val_175\"," \
+ "\"key_176\": \"val_176\"," \
+ "\"key_177\": \"val_177\"," \
+ "\"key_178\": \"val_178\"," \
+ "\"key_179\": \"val_179\"," \
+ "\"key_180\": \"val_180\"," \
+ "\"key_181\": \"val_181\"," \
+ "\"key_182\": \"val_182\"," \
+ "\"key_183\": \"val_183\"," \
+ "\"key_184\": \"val_184\"," \
+ "\"key_185\": \"val_185\"," \
+ "\"key_186\": \"val_186\"," \
+ "\"key_187\": \"val_187\"," \
+ "\"key_188\": \"val_188\"," \
+ "\"key_189\": \"val_189\"," \
+ "\"key_190\": \"val_190\"," \
+ "\"key_191\": \"val_191\"," \
+ "\"key_192\": \"val_192\"," \
+ "\"key_193\": \"val_193\"," \
+ "\"key_194\": \"val_194\"," \
+ "\"key_195\": \"val_195\"," \
+ "\"key_196\": \"val_196\"," \
+ "\"key_197\": \"val_197\"," \
+ "\"key_198\": \"val_198\"," \
+ "\"key_199\": \"val_199\"," \
+ "\"key_200\": \"val_200\"," \
+ "\"key_201\": \"val_201\"," \
+ "\"key_202\": \"val_202\"," \
+ "\"key_203\": \"val_203\"," \
+ "\"key_204\": \"val_204\"," \
+ "\"key_205\": \"val_205\"," \
+ "\"key_206\": \"val_206\"," \
+ "\"key_207\": \"val_207\"," \
+ "\"key_208\": \"val_208\"," \
+ "\"key_209\": \"val_209\"," \
+ "\"key_210\": \"val_210\"," \
+ "\"key_211\": \"val_211\"," \
+ "\"key_212\": \"val_212\"," \
+ "\"key_213\": \"val_213\"," \
+ "\"key_214\": \"val_214\"," \
+ "\"key_215\": \"val_215\"," \
+ "\"key_216\": \"val_216\"," \
+ "\"key_217\": \"val_217\"," \
+ "\"key_218\": \"val_218\"," \
+ "\"key_219\": \"val_219\"," \
+ "\"key_220\": \"val_220\"," \
+ "\"key_221\": \"val_221\"," \
+ "\"key_222\": \"val_222\"," \
+ "\"key_223\": \"val_223\"," \
+ "\"key_224\": \"val_224\"," \
+ "\"key_225\": \"val_225\"," \
+ "\"key_226\": \"val_226\"," \
+ "\"key_227\": \"val_227\"," \
+ "\"key_228\": \"val_228\"," \
+ "\"key_229\": \"val_229\"," \
+ "\"key_230\": \"val_230\"," \
+ "\"key_231\": \"val_231\"," \
+ "\"key_232\": \"val_232\"," \
+ "\"key_233\": \"val_233\"," \
+ "\"key_234\": \"val_234\"," \
+ "\"key_235\": \"val_235\"," \
+ "\"key_236\": \"val_236\"," \
+ "\"key_237\": \"val_237\"," \
+ "\"key_238\": \"val_238\"," \
+ "\"key_239\": \"val_239\"," \
+ "\"key_240\": \"val_240\"," \
+ "\"key_241\": \"val_241\"," \
+ "\"key_242\": \"val_242\"," \
+ "\"key_243\": \"val_243\"," \
+ "\"key_244\": \"val_244\"," \
+ "\"key_245\": \"val_245\"," \
+ "\"key_246\": \"val_246\"," \
+ "\"key_247\": \"val_247\"," \
+ "\"key_248\": \"val_248\"," \
+ "\"key_249\": \"val_249\"," \
+ "\"key_250\": \"val_250\"," \
+ "\"key_251\": \"val_251\"," \
+ "\"key_252\": \"val_252\"," \
+ "\"key_253\": \"val_253\"," \
+ "\"key_254\": \"val_254\"," \
+ "\"key_255\": \"val_255\"," \
+ "\"key_256\": \"val_256\"," \
+ "\"key_257\": \"val_257\"," \
+ "\"key_258\": \"val_258\"," \
+ "\"key_259\": \"val_259\"," \
+ "\"key_260\": \"val_260\"," \
+ "\"key_261\": \"val_261\"," \
+ "\"key_262\": \"val_262\"," \
+ "\"key_263\": \"val_263\"," \
+ "\"key_264\": \"val_264\"," \
+ "\"key_265\": \"val_265\"," \
+ "\"key_266\": \"val_266\"," \
+ "\"key_267\": \"val_267\"," \
+ "\"key_268\": \"val_268\"," \
+ "\"key_269\": \"val_269\"," \
+ "\"key_270\": \"val_270\"," \
+ "\"key_271\": \"val_271\"," \
+ "\"key_272\": \"val_272\"," \
+ "\"key_273\": \"val_273\"," \
+ "\"key_274\": \"val_274\"," \
+ "\"key_275\": \"val_275\"," \
+ "\"key_276\": \"val_276\"," \
+ "\"key_277\": \"val_277\"," \
+ "\"key_278\": \"val_278\"," \
+ "\"key_279\": \"val_279\"," \
+ "\"key_280\": \"val_280\"," \
+ "\"key_281\": \"val_281\"," \
+ "\"key_282\": \"val_282\"," \
+ "\"key_283\": \"val_283\"," \
+ "\"key_284\": \"val_284\"," \
+ "\"key_285\": \"val_285\"," \
+ "\"key_286\": \"val_286\"," \
+ "\"key_287\": \"val_287\"," \
+ "\"key_288\": \"val_288\"," \
+ "\"key_289\": \"val_289\"," \
+ "\"key_290\": \"val_290\"," \
+ "\"key_291\": \"val_291\"," \
+ "\"key_292\": \"val_292\"," \
+ "\"key_293\": \"val_293\"," \
+ "\"key_294\": \"val_294\"," \
+ "\"key_295\": \"val_295\"," \
+ "\"key_296\": \"val_296\"," \
+ "\"key_297\": \"val_297\"," \
+ "\"key_298\": \"val_298\"," \
+ "\"key_299\": \"val_299\"," \
+ "\"key_300\": \"val_300\"," \
+ "\"key_301\": \"val_301\"," \
+ "\"key_302\": \"val_302\"," \
+ "\"key_303\": \"val_303\"," \
+ "\"key_304\": \"val_304\"," \
+ "\"key_305\": \"val_305\"," \
+ "\"key_306\": \"val_306\"," \
+ "\"key_307\": \"val_307\"," \
+ "\"key_308\": \"val_308\"," \
+ "\"key_309\": \"val_309\"," \
+ "\"key_310\": \"val_310\"," \
+ "\"key_311\": \"val_311\"," \
+ "\"key_312\": \"val_312\"," \
+ "\"key_313\": \"val_313\"," \
+ "\"key_314\": \"val_314\"," \
+ "\"key_315\": \"val_315\"," \
+ "\"key_316\": \"val_316\"," \
+ "\"key_317\": \"val_317\"," \
+ "\"key_318\": \"val_318\"," \
+ "\"key_319\": \"val_319\"," \
+ "\"key_320\": \"val_320\"," \
+ "\"key_321\": \"val_321\"," \
+ "\"key_322\": \"val_322\"," \
+ "\"key_323\": \"val_323\"," \
+ "\"key_324\": \"val_324\"," \
+ "\"key_325\": \"val_325\"," \
+ "\"key_326\": \"val_326\"," \
+ "\"key_327\": \"val_327\"," \
+ "\"key_328\": \"val_328\"," \
+ "\"key_329\": \"val_329\"," \
+ "\"key_330\": \"val_330\"," \
+ "\"key_331\": \"val_331\"," \
+ "\"key_332\": \"val_332\"," \
+ "\"key_333\": \"val_333\"," \
+ "\"key_334\": \"val_334\"," \
+ "\"key_335\": \"val_335\"," \
+ "\"key_336\": \"val_336\"," \
+ "\"key_337\": \"val_337\"," \
+ "\"key_338\": \"val_338\"," \
+ "\"key_339\": \"val_339\"," \
+ "\"key_340\": \"val_340\"," \
+ "\"key_341\": \"val_341\"," \
+ "\"key_342\": \"val_342\"," \
+ "\"key_343\": \"val_343\"," \
+ "\"key_344\": \"val_344\"," \
+ "\"key_345\": \"val_345\"," \
+ "\"key_346\": \"val_346\"," \
+ "\"key_347\": \"val_347\"," \
+ "\"key_348\": \"val_348\"," \
+ "\"key_349\": \"val_349\"," \
+ "\"key_350\": \"val_350\"," \
+ "\"key_351\": \"val_351\"," \
+ "\"key_352\": \"val_352\"," \
+ "\"key_353\": \"val_353\"," \
+ "\"key_354\": \"val_354\"," \
+ "\"key_355\": \"val_355\"," \
+ "\"key_356\": \"val_356\"," \
+ "\"key_357\": \"val_357\"," \
+ "\"key_358\": \"val_358\"," \
+ "\"key_359\": \"val_359\"," \
+ "\"key_360\": \"val_360\"," \
+ "\"key_361\": \"val_361\"," \
+ "\"key_362\": \"val_362\"," \
+ "\"key_363\": \"val_363\"," \
+ "\"key_364\": \"val_364\"," \
+ "\"key_365\": \"val_365\"," \
+ "\"key_366\": \"val_366\"," \
+ "\"key_367\": \"val_367\"," \
+ "\"key_368\": \"val_368\"," \
+ "\"key_369\": \"val_369\"," \
+ "\"key_370\": \"val_370\"," \
+ "\"key_371\": \"val_371\"," \
+ "\"key_372\": \"val_372\"," \
+ "\"key_373\": \"val_373\"," \
+ "\"key_374\": \"val_374\"," \
+ "\"key_375\": \"val_375\"," \
+ "\"key_376\": \"val_376\"," \
+ "\"key_377\": \"val_377\"," \
+ "\"key_378\": \"val_378\"," \
+ "\"key_379\": \"val_379\"," \
+ "\"key_380\": \"val_380\"," \
+ "\"key_381\": \"val_381\"," \
+ "\"key_382\": \"val_382\"," \
+ "\"key_383\": \"val_383\"," \
+ "\"key_384\": \"val_384\"," \
+ "\"key_385\": \"val_385\"," \
+ "\"key_386\": \"val_386\"," \
+ "\"key_387\": \"val_387\"," \
+ "\"key_388\": \"val_388\"," \
+ "\"key_389\": \"val_389\"," \
+ "\"key_390\": \"val_390\"," \
+ "\"key_391\": \"val_391\"," \
+ "\"key_392\": \"val_392\"," \
+ "\"key_393\": \"val_393\"," \
+ "\"key_394\": \"val_394\"," \
+ "\"key_395\": \"val_395\"," \
+ "\"key_396\": \"val_396\"," \
+ "\"key_397\": \"val_397\"," \
+ "\"key_398\": \"val_398\"," \
+ "\"key_399\": \"val_399\"," \
+ "\"key_400\": \"val_400\"," \
+ "\"key_401\": \"val_401\"," \
+ "\"key_402\": \"val_402\"," \
+ "\"key_403\": \"val_403\"," \
+ "\"key_404\": \"val_404\"," \
+ "\"key_405\": \"val_405\"," \
+ "\"key_406\": \"val_406\"," \
+ "\"key_407\": \"val_407\"," \
+ "\"key_408\": \"val_408\"," \
+ "\"key_409\": \"val_409\"," \
+ "\"key_410\": \"val_410\"," \
+ "\"key_411\": \"val_411\"," \
+ "\"key_412\": \"val_412\"," \
+ "\"key_413\": \"val_413\"," \
+ "\"key_414\": \"val_414\"," \
+ "\"key_415\": \"val_415\"," \
+ "\"key_416\": \"val_416\"," \
+ "\"key_417\": \"val_417\"," \
+ "\"key_418\": \"val_418\"," \
+ "\"key_419\": \"val_419\"," \
+ "\"key_420\": \"val_420\"," \
+ "\"key_421\": \"val_421\"," \
+ "\"key_422\": \"val_422\"," \
+ "\"key_423\": \"val_423\"," \
+ "\"key_424\": \"val_424\"," \
+ "\"key_425\": \"val_425\"," \
+ "\"key_426\": \"val_426\"," \
+ "\"key_427\": \"val_427\"," \
+ "\"key_428\": \"val_428\"," \
+ "\"key_429\": \"val_429\"," \
+ "\"key_430\": \"val_430\"," \
+ "\"key_431\": \"val_431\"," \
+ "\"key_432\": \"val_432\"," \
+ "\"key_433\": \"val_433\"," \
+ "\"key_434\": \"val_434\"," \
+ "\"key_435\": \"val_435\"," \
+ "\"key_436\": \"val_436\"," \
+ "\"key_437\": \"val_437\"," \
+ "\"key_438\": \"val_438\"," \
+ "\"key_439\": \"val_439\"," \
+ "\"key_440\": \"val_440\"," \
+ "\"key_441\": \"val_441\"," \
+ "\"key_442\": \"val_442\"," \
+ "\"key_443\": \"val_443\"," \
+ "\"key_444\": \"val_444\"," \
+ "\"key_445\": \"val_445\"," \
+ "\"key_446\": \"val_446\"," \
+ "\"key_447\": \"val_447\"," \
+ "\"key_448\": \"val_448\"," \
+ "\"key_449\": \"val_449\"," \
+ "\"key_450\": \"val_450\"," \
+ "\"key_451\": \"val_451\"," \
+ "\"key_452\": \"val_452\"," \
+ "\"key_453\": \"val_453\"," \
+ "\"key_454\": \"val_454\"," \
+ "\"key_455\": \"val_455\"," \
+ "\"key_456\": \"val_456\"," \
+ "\"key_457\": \"val_457\"," \
+ "\"key_458\": \"val_458\"," \
+ "\"key_459\": \"val_459\"," \
+ "\"key_460\": \"val_460\"," \
+ "\"key_461\": \"val_461\"," \
+ "\"key_462\": \"val_462\"," \
+ "\"key_463\": \"val_463\"," \
+ "\"key_464\": \"val_464\"," \
+ "\"key_465\": \"val_465\"," \
+ "\"key_466\": \"val_466\"," \
+ "\"key_467\": \"val_467\"," \
+ "\"key_468\": \"val_468\"," \
+ "\"key_469\": \"val_469\"," \
+ "\"key_470\": \"val_470\"," \
+ "\"key_471\": \"val_471\"," \
+ "\"key_472\": \"val_472\"," \
+ "\"key_473\": \"val_473\"," \
+ "\"key_474\": \"val_474\"," \
+ "\"key_475\": \"val_475\"," \
+ "\"key_476\": \"val_476\"," \
+ "\"key_477\": \"val_477\"," \
+ "\"key_478\": \"val_478\"," \
+ "\"key_479\": \"val_479\"," \
+ "\"key_480\": \"val_480\"," \
+ "\"key_481\": \"val_481\"," \
+ "\"key_482\": \"val_482\"," \
+ "\"key_483\": \"val_483\"," \
+ "\"key_484\": \"val_484\"," \
+ "\"key_485\": \"val_485\"," \
+ "\"key_486\": \"val_486\"," \
+ "\"key_487\": \"val_487\"," \
+ "\"key_488\": \"val_488\"," \
+ "\"key_489\": \"val_489\"," \
+ "\"key_490\": \"val_490\"," \
+ "\"key_491\": \"val_491\"," \
+ "\"key_492\": \"val_492\"," \
+ "\"key_493\": \"val_493\"," \
+ "\"key_494\": \"val_494\"," \
+ "\"key_495\": \"val_495\"," \
+ "\"key_496\": \"val_496\"," \
+ "\"key_497\": \"val_497\"," \
+ "\"key_498\": \"val_498\"," \
+ "\"key_499\": \"val_499\"," \
+ "\"END_KEY\": \"JSON_END\"" \
+ "}]"
+
diff --git a/fluent-bit/tests/runtime/data/tls/certificate.pem b/fluent-bit/tests/runtime/data/tls/certificate.pem
new file mode 100644
index 00000000..b7971987
--- /dev/null
+++ b/fluent-bit/tests/runtime/data/tls/certificate.pem
@@ -0,0 +1,22 @@
+-----BEGIN CERTIFICATE-----
+MIIDlzCCAn+gAwIBAgIUCl24aoQh3HuyObboivsozKKOb3cwDQYJKoZIhvcNAQEL
+BQAwWzELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoM
+GEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDEUMBIGA1UEAwwLbGVvLnZjYXAubWUw
+HhcNMjIwODE3MTM0OTAyWhcNMjMwODE3MTM0OTAyWjBbMQswCQYDVQQGEwJBVTET
+MBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0cyBQ
+dHkgTHRkMRQwEgYDVQQDDAtsZW8udmNhcC5tZTCCASIwDQYJKoZIhvcNAQEBBQAD
+ggEPADCCAQoCggEBAJ2cbLtGhSeGPAXKitw+PN7U8CkaSGL7BssPFSuqtuVkJ1AH
+puluHq62FHm4UgTyJB/Iundf1kBxiOehGFr9E1fGp6kUety6iBZK3mmmX0NdXY2A
+ObqfvxTgPNVPEj+5Qp0CJW6fEBRhHrFgpf+OdKzoKnjSCbl1EyJ1wFZeSNDTx98I
+h951xEOCobcXj1qXHDd0snRW5QNKTdSnj+3amqrNqV5DrkCyyb5Rq/VQX9LIyGV8
+tOyq50mUKeK+vOu/oNwfkZqgZ5wlY/AUyPMNvd8UKitnWeQMiMmr3ImQRZLkFnoE
+2mi2Xr6ZQhmsweWbTmmUYxr+ej7L5WvBVQEkbd0CAwEAAaNTMFEwHQYDVR0OBBYE
+FGliHqbvEsRN36C2w1cCq5M/gGCRMB8GA1UdIwQYMBaAFGliHqbvEsRN36C2w1cC
+q5M/gGCRMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBACWCNohz
+YbnnDzGXMhGlfV5PywH6+gguqTrtzJlfJB/FGHDMybBvEp9gda5ssPZfPdv6aAvo
+hz2XC+uwJqYn76ZcX+LOixsqhoXJ367pT0ggMSUB/UNvGkzMFWuGerxcB5Q7xvsh
+0Ui37Ojok7Ozj9Ofk/zroJB1WG3kPDtwoo+GwNmJaDtXhOutRPxCqp5Dw+dC0N0c
+TlPCBRB17hx49Zjw+h9sv1VmOUtQcplNZgRL9lguv7eXB6y0sSdpDyDLlTBbRMGl
+uVnQ20uKnqqALRfpXDWFL8g01xPNEIfV3KctpJfu1inLWtPuklerl0yeyUh9M1f0
+zCAi0URsGEHMFA0=
+-----END CERTIFICATE-----
diff --git a/fluent-bit/tests/runtime/data/tls/private_key.pem b/fluent-bit/tests/runtime/data/tls/private_key.pem
new file mode 100644
index 00000000..b6a70539
--- /dev/null
+++ b/fluent-bit/tests/runtime/data/tls/private_key.pem
@@ -0,0 +1,28 @@
+-----BEGIN PRIVATE KEY-----
+MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCdnGy7RoUnhjwF
+yorcPjze1PApGkhi+wbLDxUrqrblZCdQB6bpbh6uthR5uFIE8iQfyLp3X9ZAcYjn
+oRha/RNXxqepFHrcuogWSt5ppl9DXV2NgDm6n78U4DzVTxI/uUKdAiVunxAUYR6x
+YKX/jnSs6Cp40gm5dRMidcBWXkjQ08ffCIfedcRDgqG3F49alxw3dLJ0VuUDSk3U
+p4/t2pqqzaleQ65Assm+Uav1UF/SyMhlfLTsqudJlCnivrzrv6DcH5GaoGecJWPw
+FMjzDb3fFCorZ1nkDIjJq9yJkEWS5BZ6BNpotl6+mUIZrMHlm05plGMa/no+y+Vr
+wVUBJG3dAgMBAAECggEANXQ/AEkLkfsZ0lD+RXIqTNzlUttiH4fJpwbHhFbSzvvn
+xWHC/zpk15ZTXXDhCGJjVBBNBX2QeazH5N8jFoDslYF/jX2vqbrturnLswNFHeDF
+gN8zNRNGyDrBBwtZQhl/+SYoMdtqpa7GrRv9UK4s7hOTjASYXbjSM4bCI8i4Y3JX
+5iKYnejTJ4GNXm1S5g4H8QpgXxrxYU7HY1UlMBGni1BSDPb5SxANnFvgw0oypaDG
+4a0KpnUmziBtZo8wIQ7+UeStS4xikIeaT2SQM/ilplPNx0HgPHbTMeTBHQylG1JU
+ztEE04Y1F9UF7cqZnxqQLlKmIqsOepLeDOkRXjDtNQKBgQDQtWYbewCmGuo1+AR7
+wStOXpfhqdu6NxAwX+S06ZdAV7f4duMFb//6uJ/c997qwRlNvRvpPlJBdcT3qziS
+zjvR8uk2YN5N9D+ZN0Yi11vltUYVPeGpoAk4L7+Ruj5RTVqk5iZEZ1RbAv5cRIm0
+D24H4W0Cj/aldDED2pwTb9ukEwKBgQDBUv/s7gUNM3LCkNLc1EDg2adSzoUopxHU
+jIQCitXt5iFpY2Lv2tpaIz4ux4HQGwhmao2MiUYOMit0++/pFaKur66tgRQRsr7F
+uS1fpjqtS51/5uFuc1xcWjVquidZXH9u9iChrYvim6SBkxzFsbu4kXNOj4I562cm
+E2VWH5GETwKBgQCo5ePX4VbJFYbsXeXi8JRHO63V5Uv4Co+DVlcTQOYyH8q1vCBE
+Sjrxf29/tugjOllr29o2i0StzMy1UU7bHyKx6M5qP0In+71sFJshnv6ziltI3Wc9
+ilFrstho6jt8OAle4RGe0bAmZunJaX22xbXZkshRBognpTv1Tnh4ElHBGQKBgHIW
+nVohjXGg7xTLiuUvjaokSI6hugunrOoWksE9VcqziPw83uJV8Y5IRiYtLvq1OVvX
+ffl1+ZXfHa5ID+kqD3uvyhIynrljFxpwkcpkuzQR77zPcDJSeis2QVfey+H8qGe/
+cLp5RJhS6d5eBxjULshZbgbqwhuURKc/wwn0T1gZAoGAXu2wqwBE8ikhpaDVFYZR
+EZe0G1iq03CtabFL0oPiddytV9jJy4Mg457tkCj58uCVkxw/8qjXEnm5OoJAzSlJ
+Goc4wh1N1rngYRbSGG+LxD0DeI/Hj3T5tWezMBLCE7J+hieUuVjOB9ELKip7aTj3
+KLNVudOATWYGXyvzux6ciu0=
+-----END PRIVATE KEY-----
diff --git a/fluent-bit/tests/runtime/data/wasm/append_tag.wasm b/fluent-bit/tests/runtime/data/wasm/append_tag.wasm
new file mode 100755
index 00000000..437fee4e
--- /dev/null
+++ b/fluent-bit/tests/runtime/data/wasm/append_tag.wasm
Binary files differ
diff --git a/fluent-bit/tests/runtime/data/wasm/drop_record.wasm b/fluent-bit/tests/runtime/data/wasm/drop_record.wasm
new file mode 100755
index 00000000..5b884f38
--- /dev/null
+++ b/fluent-bit/tests/runtime/data/wasm/drop_record.wasm
Binary files differ
diff --git a/fluent-bit/tests/runtime/data/wasm/modify_record.wasm b/fluent-bit/tests/runtime/data/wasm/modify_record.wasm
new file mode 100755
index 00000000..30db4157
--- /dev/null
+++ b/fluent-bit/tests/runtime/data/wasm/modify_record.wasm
Binary files differ
diff --git a/fluent-bit/tests/runtime/data/wasm/numeric_records.wasm b/fluent-bit/tests/runtime/data/wasm/numeric_records.wasm
new file mode 100755
index 00000000..8fc35f0d
--- /dev/null
+++ b/fluent-bit/tests/runtime/data/wasm/numeric_records.wasm
Binary files differ
diff --git a/fluent-bit/tests/runtime/data/wasm/say_hello.wasm b/fluent-bit/tests/runtime/data/wasm/say_hello.wasm
new file mode 100755
index 00000000..7e9d01d2
--- /dev/null
+++ b/fluent-bit/tests/runtime/data/wasm/say_hello.wasm
Binary files differ
diff --git a/fluent-bit/tests/runtime/filter_aws.c b/fluent-bit/tests/runtime/filter_aws.c
new file mode 100644
index 00000000..770915a0
--- /dev/null
+++ b/fluent-bit/tests/runtime/filter_aws.c
@@ -0,0 +1,846 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+#include <stdlib.h>
+#include <fluent-bit.h>
+#include <fluent-bit/flb_time.h>
+#include "flb_tests_runtime.h"
+#include "../../plugins/filter_aws/aws.h"
+
+#include "../include/aws_client_mock.h"
+#include "../include/aws_client_mock.c"
+
+pthread_mutex_t result_mutex = PTHREAD_MUTEX_INITIALIZER;
+char *output = NULL;
+
+void set_output(char *val)
+{
+ pthread_mutex_lock(&result_mutex);
+ if (output) {
+ free(output);
+ }
+ output = val;
+ pthread_mutex_unlock(&result_mutex);
+}
+
+char *get_output(void)
+{
+ char *val;
+
+ pthread_mutex_lock(&result_mutex);
+ val = output;
+ pthread_mutex_unlock(&result_mutex);
+
+ return val;
+}
+
+int callback_test(void* data, size_t size, void* cb_data)
+{
+ if (size > 0) {
+ flb_debug("[test_filter_aws] received message: %s", (char*)data);
+ set_output(data); /* success */
+ }
+ return 0;
+}
+
+void flb_test_aws_ec2_tags_present() {
+ int ret;
+ int bytes;
+ char *p = "[0, {\"log\": \"hello, from my ec2 instance\"}]";
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+ int filter_ffd;
+ struct flb_lib_out_cb cb_data;
+ struct flb_aws_client_generator *client_generator;
+ struct flb_filter_aws_init_options *ops;
+ struct flb_aws_client_mock_request_chain *request_chain;
+ char *output = NULL;
+ char *result;
+
+ request_chain = FLB_AWS_CLIENT_MOCK(
+ response(
+ expect(URI, "/latest/meta-data/tags/instance"),
+ expect(METHOD, FLB_HTTP_GET),
+ set(STATUS, 200),
+ set(PAYLOAD, "Name\nCUSTOMER_ID\nthis-would-be-my-very-long-tag-name-does-it-work"),
+ set(PAYLOAD_SIZE, 65)
+ ),
+ response(
+ expect(URI, "/latest/meta-data/tags/instance/Name"),
+ expect(METHOD, FLB_HTTP_GET),
+ set(STATUS, 200),
+ set(PAYLOAD, "my_ec2_instance"),
+ set(PAYLOAD_SIZE, 15)
+ ),
+ response(
+ expect(URI, "/latest/meta-data/tags/instance/CUSTOMER_ID"),
+ expect(METHOD, FLB_HTTP_GET),
+ set(STATUS, 200),
+ set(PAYLOAD, "70ec5c04-3a6e-11ed-a261-0242ac120002"),
+ set(PAYLOAD_SIZE, 36)
+ ),
+ response(
+ expect(URI, "/latest/meta-data/tags/instance/this-would-be-my-very-long-tag-name-does-it-work"),
+ expect(METHOD, FLB_HTTP_GET),
+ set(STATUS, 200),
+ set(PAYLOAD, "yes-it-does"),
+ set(PAYLOAD_SIZE, 11)
+ )
+ );
+ flb_aws_client_mock_configure_generator(request_chain);
+
+ client_generator = flb_aws_client_get_mock_generator();
+ ops = flb_calloc(1, sizeof(struct flb_filter_aws_init_options));
+ if (ops == NULL) {
+ TEST_MSG("calloc for aws plugin options failed\n");
+ TEST_CHECK(false);
+ return;
+ }
+ ops->client_generator = client_generator;
+
+ ctx = flb_create();
+
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ TEST_CHECK(in_ffd >= 0);
+ flb_input_set(ctx, in_ffd, "tag", "test", NULL);
+
+
+ /* Prepare output callback context*/
+ cb_data.cb = callback_test;
+ cb_data.data = NULL;
+
+ /* Lib output */
+ out_ffd = flb_output(ctx, (char *) "lib", (void *)&cb_data);
+ TEST_CHECK(out_ffd >= 0);
+ flb_output_set(ctx, out_ffd,
+ "match", "*",
+ "format", "json",
+ NULL);
+
+ filter_ffd = flb_filter(ctx, (char *) "aws", ops);
+ TEST_CHECK(filter_ffd >= 0);
+ ret = flb_filter_set(ctx, filter_ffd, "match", "*", NULL);
+ TEST_CHECK(ret == 0);
+ ret = flb_filter_set(ctx, filter_ffd, "ec2_instance_id", "false", NULL);
+ TEST_CHECK(ret == 0);
+ ret = flb_filter_set(ctx, filter_ffd, "az", "false", NULL);
+ TEST_CHECK(ret == 0);
+ ret = flb_filter_set(ctx, filter_ffd, "tags_enabled", "true", NULL);
+ TEST_CHECK(ret == 0);
+
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ bytes = flb_lib_push(ctx, in_ffd, p, strlen(p));
+ if (!TEST_CHECK(bytes > 0)) {
+ TEST_MSG("zero bytes were pushed\n");
+ }
+
+ flb_time_msleep(1500); /* waiting flush */
+
+ output = get_output();
+ if (output) {
+ result = strstr(output, "\"Name\":\"my_ec2_instance\"");
+ if (!TEST_CHECK(result != NULL)) {
+ TEST_MSG("output:%s\n", output);
+ }
+ result = strstr(output, "\"CUSTOMER_ID\":\"70ec5c04-3a6e-11ed-a261-0242ac120002\"");
+ if (!TEST_CHECK(result != NULL)) {
+ TEST_MSG("output:%s\n", output);
+ }
+ result = strstr(output, "\"this-would-be-my-very-long-tag-name-does-it-work\":\"yes-it-does\"");
+ if (!TEST_CHECK(result != NULL)) {
+ TEST_MSG("output:%s\n", output);
+ }
+ result = strstr(output, "hello, from my ec2 instance");
+ if (!TEST_CHECK(result != NULL)) {
+ TEST_MSG("output:%s\n", output);
+ }
+ }
+ else {
+ TEST_CHECK(false);
+ TEST_MSG("output is empty\n");
+ }
+
+ flb_stop(ctx);
+ flb_aws_client_mock_destroy_generator();
+ flb_destroy(ctx);
+ flb_free(ops);
+
+ set_output(NULL);
+}
+
+void flb_test_aws_ec2_tags_404() {
+ int ret;
+ int bytes;
+ char *p = "[0, {\"log\": \"hello, from my ec2 instance\"}]";
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+ int filter_ffd;
+ struct flb_lib_out_cb cb_data;
+ struct flb_aws_client_generator *client_generator;
+ struct flb_filter_aws_init_options *ops;
+ struct flb_aws_client_mock_request_chain *request_chain;
+ char *output = NULL;
+ char *result;
+
+ request_chain = FLB_AWS_CLIENT_MOCK(
+ response(
+ expect(URI, "/latest/meta-data/tags/instance"),
+ expect(METHOD, FLB_HTTP_GET),
+ set(STATUS, 404)
+ )
+ );
+ flb_aws_client_mock_configure_generator(request_chain);
+
+ client_generator = flb_aws_client_get_mock_generator();
+ ops = flb_calloc(1, sizeof(struct flb_filter_aws_init_options));
+ if (ops == NULL) {
+ TEST_MSG("calloc for aws plugin options failed\n");
+ TEST_CHECK(false);
+ return;
+ }
+ ops->client_generator = client_generator;
+
+ ctx = flb_create();
+
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ TEST_CHECK(in_ffd >= 0);
+ flb_input_set(ctx, in_ffd, "tag", "test", NULL);
+
+
+ /* Prepare output callback context*/
+ cb_data.cb = callback_test;
+ cb_data.data = NULL;
+
+ /* Lib output */
+ out_ffd = flb_output(ctx, (char *) "lib", (void *)&cb_data);
+ TEST_CHECK(out_ffd >= 0);
+ flb_output_set(ctx, out_ffd,
+ "match", "*",
+ "format", "json",
+ NULL);
+
+ filter_ffd = flb_filter(ctx, (char *) "aws", ops);
+ TEST_CHECK(filter_ffd >= 0);
+ ret = flb_filter_set(ctx, filter_ffd, "match", "*", NULL);
+ TEST_CHECK(ret == 0);
+ ret = flb_filter_set(ctx, filter_ffd, "ec2_instance_id", "false", NULL);
+ TEST_CHECK(ret == 0);
+ ret = flb_filter_set(ctx, filter_ffd, "az", "false", NULL);
+ TEST_CHECK(ret == 0);
+ ret = flb_filter_set(ctx, filter_ffd, "tags_enabled", "true", NULL);
+ TEST_CHECK(ret == 0);
+
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ bytes = flb_lib_push(ctx, in_ffd, p, strlen(p));
+ if (!TEST_CHECK(bytes > 0)) {
+ TEST_MSG("zero bytes were pushed\n");
+ }
+
+ flb_time_msleep(1500); /* waiting flush */
+
+ output = get_output();
+ if (output) {
+ result = strstr(output, "\"Name\":\"my_ec2_instance\"");
+ if (!TEST_CHECK(result == NULL)) {
+ TEST_MSG("output:%s\n", output);
+ }
+ result = strstr(output, "\"CUSTOMER_ID\":\"70ec5c04-3a6e-11ed-a261-0242ac120002\"");
+ if (!TEST_CHECK(result == NULL)) {
+ TEST_MSG("output:%s\n", output);
+ }
+ result = strstr(output, "hello, from my ec2 instance");
+ if (!TEST_CHECK(result != NULL)) {
+ TEST_MSG("output:%s\n", output);
+ }
+ }
+ else {
+ TEST_CHECK(false);
+ TEST_MSG("output is empty");
+ }
+
+ flb_stop(ctx);
+ flb_aws_client_mock_destroy_generator();
+ flb_destroy(ctx);
+ flb_free(ops);
+
+ set_output(NULL);
+}
+
+void flb_test_aws_ec2_tags_list_500() {
+ int ret;
+ int bytes;
+ char *p = "[0, {\"log\": \"hello, from my ec2 instance\"}]";
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+ int filter_ffd;
+ struct flb_lib_out_cb cb_data;
+ struct flb_aws_client_generator *client_generator;
+ struct flb_filter_aws_init_options *ops;
+ struct flb_aws_client_mock_request_chain *request_chain;
+ char *output = NULL;
+ char *result;
+
+ request_chain = FLB_AWS_CLIENT_MOCK(
+ response(
+ expect(URI, "/latest/meta-data/tags/instance"),
+ expect(METHOD, FLB_HTTP_GET),
+ set(STATUS, 500)
+ ),
+ response(
+ expect(URI, "/latest/meta-data/tags/instance"),
+ expect(METHOD, FLB_HTTP_GET),
+ set(STATUS, 500)
+ )
+ );
+ flb_aws_client_mock_configure_generator(request_chain);
+
+ client_generator = flb_aws_client_get_mock_generator();
+ ops = flb_calloc(1, sizeof(struct flb_filter_aws_init_options));
+ if (ops == NULL) {
+ TEST_MSG("calloc for aws plugin options failed\n");
+ TEST_CHECK(false);
+ return;
+ }
+ ops->client_generator = client_generator;
+
+ ctx = flb_create();
+
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ TEST_CHECK(in_ffd >= 0);
+ flb_input_set(ctx, in_ffd, "tag", "test", NULL);
+
+
+ /* Prepare output callback context*/
+ cb_data.cb = callback_test;
+ cb_data.data = NULL;
+
+ /* Lib output */
+ out_ffd = flb_output(ctx, (char *) "lib", (void *)&cb_data);
+ TEST_CHECK(out_ffd >= 0);
+ flb_output_set(ctx, out_ffd,
+ "match", "*",
+ "format", "json",
+ NULL);
+
+ filter_ffd = flb_filter(ctx, (char *) "aws", ops);
+ TEST_CHECK(filter_ffd >= 0);
+ ret = flb_filter_set(ctx, filter_ffd, "match", "*", NULL);
+ TEST_CHECK(ret == 0);
+ ret = flb_filter_set(ctx, filter_ffd, "ec2_instance_id", "false", NULL);
+ TEST_CHECK(ret == 0);
+ ret = flb_filter_set(ctx, filter_ffd, "az", "false", NULL);
+ TEST_CHECK(ret == 0);
+ ret = flb_filter_set(ctx, filter_ffd, "tags_enabled", "true", NULL);
+ TEST_CHECK(ret == 0);
+
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ bytes = flb_lib_push(ctx, in_ffd, p, strlen(p));
+ if (!TEST_CHECK(bytes > 0)) {
+ TEST_MSG("zero bytes were pushed\n");
+ }
+
+ flb_time_msleep(1500); /* waiting flush */
+
+ output = get_output();
+ if (output) {
+ result = strstr(output, "hello, from my ec2 instance");
+ if (!TEST_CHECK(result != NULL)) {
+ TEST_MSG("output:%s\n", output);
+ }
+ }
+ else {
+ TEST_CHECK(false);
+ TEST_MSG("output is empty");
+ }
+
+ flb_stop(ctx);
+ flb_aws_client_mock_destroy_generator();
+ flb_destroy(ctx);
+ flb_free(ops);
+
+ set_output(NULL);
+}
+
+void flb_test_aws_ec2_tags_value_404() {
+ int ret;
+ int bytes;
+ char *p = "[0, {\"log\": \"hello, from my ec2 instance\"}]";
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+ int filter_ffd;
+ struct flb_lib_out_cb cb_data;
+ struct flb_aws_client_generator *client_generator;
+ struct flb_filter_aws_init_options *ops;
+ struct flb_aws_client_mock_request_chain *request_chain;
+ char *output = NULL;
+ char *result;
+
+ request_chain = FLB_AWS_CLIENT_MOCK(
+ response(
+ expect(URI, "/latest/meta-data/tags/instance"),
+ expect(METHOD, FLB_HTTP_GET),
+ set(STATUS, 200),
+ set(PAYLOAD, "Name"),
+ set(PAYLOAD_SIZE, 4)
+ ),
+ response(
+ expect(URI, "/latest/meta-data/tags/instance/Name"),
+ expect(METHOD, FLB_HTTP_GET),
+ set(STATUS, 404)
+ ),
+ response(
+ expect(URI, "/latest/meta-data/tags/instance"),
+ expect(METHOD, FLB_HTTP_GET),
+ set(STATUS, 200),
+ set(PAYLOAD, "Name"),
+ set(PAYLOAD_SIZE, 4)
+ ),
+ response(
+ expect(URI, "/latest/meta-data/tags/instance/Name"),
+ expect(METHOD, FLB_HTTP_GET),
+ set(STATUS, 404)
+ )
+ );
+ flb_aws_client_mock_configure_generator(request_chain);
+
+ client_generator = flb_aws_client_get_mock_generator();
+ ops = flb_calloc(1, sizeof(struct flb_filter_aws_init_options));
+ if (ops == NULL) {
+ TEST_MSG("calloc for aws plugin options failed\n");
+ TEST_CHECK(false);
+ return;
+ }
+ ops->client_generator = client_generator;
+
+ ctx = flb_create();
+
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ TEST_CHECK(in_ffd >= 0);
+ flb_input_set(ctx, in_ffd, "tag", "test", NULL);
+
+
+ /* Prepare output callback context*/
+ cb_data.cb = callback_test;
+ cb_data.data = NULL;
+
+ /* Lib output */
+ out_ffd = flb_output(ctx, (char *) "lib", (void *)&cb_data);
+ TEST_CHECK(out_ffd >= 0);
+ flb_output_set(ctx, out_ffd,
+ "match", "*",
+ "format", "json",
+ NULL);
+
+ filter_ffd = flb_filter(ctx, (char *) "aws", ops);
+ TEST_CHECK(filter_ffd >= 0);
+ ret = flb_filter_set(ctx, filter_ffd, "match", "*", NULL);
+ TEST_CHECK(ret == 0);
+ ret = flb_filter_set(ctx, filter_ffd, "ec2_instance_id", "false", NULL);
+ TEST_CHECK(ret == 0);
+ ret = flb_filter_set(ctx, filter_ffd, "az", "false", NULL);
+ TEST_CHECK(ret == 0);
+ ret = flb_filter_set(ctx, filter_ffd, "tags_enabled", "true", NULL);
+ TEST_CHECK(ret == 0);
+
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ bytes = flb_lib_push(ctx, in_ffd, p, strlen(p));
+ if (!TEST_CHECK(bytes > 0)) {
+ TEST_MSG("zero bytes were pushed\n");
+ }
+
+ flb_time_msleep(1500); /* waiting flush */
+
+ output = get_output();
+ if (output) {
+ result = strstr(output, "hello, from my ec2 instance");
+ if (!TEST_CHECK(result != NULL)) {
+ TEST_MSG("output:%s\n", output);
+ }
+ }
+ else {
+ TEST_CHECK(false);
+ TEST_MSG("output is empty");
+ }
+
+ flb_stop(ctx);
+ flb_aws_client_mock_destroy_generator();
+ flb_destroy(ctx);
+ flb_free(ops);
+
+ set_output(NULL);
+}
+
+void flb_test_aws_ec2_tags_value_500() {
+ int ret;
+ int bytes;
+ char *p = "[0, {\"log\": \"hello, from my ec2 instance\"}]";
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+ int filter_ffd;
+ struct flb_lib_out_cb cb_data;
+ struct flb_aws_client_generator *client_generator;
+ struct flb_filter_aws_init_options *ops;
+ struct flb_aws_client_mock_request_chain *request_chain;
+ char *output = NULL;
+ char *result;
+
+ request_chain = FLB_AWS_CLIENT_MOCK(
+ response(
+ expect(URI, "/latest/meta-data/tags/instance"),
+ expect(METHOD, FLB_HTTP_GET),
+ set(STATUS, 200),
+ set(PAYLOAD, "Name"),
+ set(PAYLOAD_SIZE, 4)
+ ),
+ response(
+ expect(URI, "/latest/meta-data/tags/instance/Name"),
+ expect(METHOD, FLB_HTTP_GET),
+ set(STATUS, 500)
+ ),
+ response(
+ expect(URI, "/latest/meta-data/tags/instance"),
+ expect(METHOD, FLB_HTTP_GET),
+ set(STATUS, 200),
+ set(PAYLOAD, "Name"),
+ set(PAYLOAD_SIZE, 4)
+ ),
+ response(
+ expect(URI, "/latest/meta-data/tags/instance/Name"),
+ expect(METHOD, FLB_HTTP_GET),
+ set(STATUS, 500)
+ )
+ );
+ flb_aws_client_mock_configure_generator(request_chain);
+
+ client_generator = flb_aws_client_get_mock_generator();
+ ops = flb_calloc(1, sizeof(struct flb_filter_aws_init_options));
+ if (ops == NULL) {
+ TEST_MSG("calloc for aws plugin options failed\n");
+ TEST_CHECK(false);
+ return;
+ }
+ ops->client_generator = client_generator;
+
+ ctx = flb_create();
+
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ TEST_CHECK(in_ffd >= 0);
+ flb_input_set(ctx, in_ffd, "tag", "test", NULL);
+
+
+ /* Prepare output callback context*/
+ cb_data.cb = callback_test;
+ cb_data.data = NULL;
+
+ /* Lib output */
+ out_ffd = flb_output(ctx, (char *) "lib", (void *)&cb_data);
+ TEST_CHECK(out_ffd >= 0);
+ flb_output_set(ctx, out_ffd,
+ "match", "*",
+ "format", "json",
+ NULL);
+
+ filter_ffd = flb_filter(ctx, (char *) "aws", ops);
+ TEST_CHECK(filter_ffd >= 0);
+ ret = flb_filter_set(ctx, filter_ffd, "match", "*", NULL);
+ TEST_CHECK(ret == 0);
+ ret = flb_filter_set(ctx, filter_ffd, "ec2_instance_id", "false", NULL);
+ TEST_CHECK(ret == 0);
+ ret = flb_filter_set(ctx, filter_ffd, "az", "false", NULL);
+ TEST_CHECK(ret == 0);
+ ret = flb_filter_set(ctx, filter_ffd, "tags_enabled", "true", NULL);
+ TEST_CHECK(ret == 0);
+
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ bytes = flb_lib_push(ctx, in_ffd, p, strlen(p));
+ if (!TEST_CHECK(bytes > 0)) {
+ TEST_MSG("zero bytes were pushed\n");
+ }
+
+ flb_time_msleep(1500); /* waiting flush */
+
+ output = get_output();
+ if (output) {
+ result = strstr(output, "hello, from my ec2 instance");
+ if (!TEST_CHECK(result != NULL)) {
+ TEST_MSG("output:%s\n", output);
+ }
+ }
+ else {
+ TEST_CHECK(false);
+ TEST_MSG("output is empty");
+ }
+
+ flb_stop(ctx);
+ flb_aws_client_mock_destroy_generator();
+ flb_destroy(ctx);
+ flb_free(ops);
+
+ set_output(NULL);
+}
+
+void flb_test_aws_ec2_tags_include() {
+ int ret;
+ int bytes;
+ char *p = "[0, {\"log\": \"hello, from my ec2 instance\"}]";
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+ int filter_ffd;
+ struct flb_lib_out_cb cb_data;
+ struct flb_aws_client_generator *client_generator;
+ struct flb_filter_aws_init_options *ops;
+ struct flb_aws_client_mock_request_chain *request_chain;
+ char *output = NULL;
+ char *result;
+
+ request_chain = FLB_AWS_CLIENT_MOCK(
+ response(
+ expect(URI, "/latest/meta-data/tags/instance"),
+ expect(METHOD, FLB_HTTP_GET),
+ set(STATUS, 200),
+ set(PAYLOAD, "Name\nCUSTOMER_ID"),
+ set(PAYLOAD_SIZE, 16)
+ ),
+ response(
+ expect(URI, "/latest/meta-data/tags/instance/Name"),
+ expect(METHOD, FLB_HTTP_GET),
+ set(STATUS, 200),
+ set(PAYLOAD, "my_ec2_instance"),
+ set(PAYLOAD_SIZE, 15)
+ ),
+ response(
+ expect(URI, "/latest/meta-data/tags/instance/CUSTOMER_ID"),
+ expect(METHOD, FLB_HTTP_GET),
+ set(STATUS, 200),
+ set(PAYLOAD, "70ec5c04-3a6e-11ed-a261-0242ac120002"),
+ set(PAYLOAD_SIZE, 36)
+ )
+ );
+ flb_aws_client_mock_configure_generator(request_chain);
+
+ client_generator = flb_aws_client_get_mock_generator();
+ ops = flb_calloc(1, sizeof(struct flb_filter_aws_init_options));
+ if (ops == NULL) {
+ TEST_MSG("calloc for aws plugin options failed\n");
+ TEST_CHECK(false);
+ return;
+ }
+ ops->client_generator = client_generator;
+
+ ctx = flb_create();
+
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ TEST_CHECK(in_ffd >= 0);
+ flb_input_set(ctx, in_ffd, "tag", "test", NULL);
+
+
+ /* Prepare output callback context*/
+ cb_data.cb = callback_test;
+ cb_data.data = NULL;
+
+ /* Lib output */
+ out_ffd = flb_output(ctx, (char *) "lib", (void *)&cb_data);
+ TEST_CHECK(out_ffd >= 0);
+ flb_output_set(ctx, out_ffd,
+ "match", "*",
+ "format", "json",
+ NULL);
+
+ filter_ffd = flb_filter(ctx, (char *) "aws", ops);
+ TEST_CHECK(filter_ffd >= 0);
+ ret = flb_filter_set(ctx, filter_ffd, "match", "*", NULL);
+ TEST_CHECK(ret == 0);
+ ret = flb_filter_set(ctx, filter_ffd, "ec2_instance_id", "false", NULL);
+ TEST_CHECK(ret == 0);
+ ret = flb_filter_set(ctx, filter_ffd, "az", "false", NULL);
+ TEST_CHECK(ret == 0);
+ ret = flb_filter_set(ctx, filter_ffd, "tags_enabled", "true", NULL);
+ TEST_CHECK(ret == 0);
+ ret = flb_filter_set(ctx, filter_ffd, "tags_include", "Namee,MyTag,Name", NULL);
+ TEST_CHECK(ret == 0);
+
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ bytes = flb_lib_push(ctx, in_ffd, p, strlen(p));
+ if (!TEST_CHECK(bytes > 0)) {
+ TEST_MSG("zero bytes were pushed\n");
+ }
+
+ flb_time_msleep(1500); /* waiting flush */
+
+ output = get_output();
+ if (output) {
+ result = strstr(output, "\"Name\":\"my_ec2_instance\"");
+ if (!TEST_CHECK(result != NULL)) {
+ TEST_MSG("output:%s\n", output);
+ }
+ result = strstr(output, "hello, from my ec2 instance");
+ if (!TEST_CHECK(result != NULL)) {
+ TEST_MSG("output:%s\n", output);
+ }
+ /* CUSTOMER_ID is not included, so we don't expect it in the log */
+ result = strstr(output, "\"CUSTOMER_ID\":\"70ec5c04-3a6e-11ed-a261-0242ac120002\"");
+ if (!TEST_CHECK(result == NULL)) {
+ TEST_MSG("output:%s\n", output);
+ }
+ }
+ else {
+ TEST_CHECK(false);
+ TEST_MSG("output is empty\n");
+ }
+
+ flb_stop(ctx);
+ flb_aws_client_mock_destroy_generator();
+ flb_destroy(ctx);
+ flb_free(ops);
+
+ set_output(NULL);
+}
+
+void flb_test_aws_ec2_tags_exclude() {
+ int ret;
+ int bytes;
+ char *p = "[0, {\"log\": \"hello, from my ec2 instance\"}]";
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+ int filter_ffd;
+ struct flb_lib_out_cb cb_data;
+ struct flb_aws_client_generator *client_generator;
+ struct flb_filter_aws_init_options *ops;
+ struct flb_aws_client_mock_request_chain *request_chain;
+ char *output = NULL;
+ char *result;
+
+ request_chain = FLB_AWS_CLIENT_MOCK(
+ response(
+ expect(URI, "/latest/meta-data/tags/instance"),
+ expect(METHOD, FLB_HTTP_GET),
+ set(STATUS, 200),
+ set(PAYLOAD, "Name\nCUSTOMER_ID"),
+ set(PAYLOAD_SIZE, 16)
+ ),
+ response(
+ expect(URI, "/latest/meta-data/tags/instance/Name"),
+ expect(METHOD, FLB_HTTP_GET),
+ set(STATUS, 200),
+ set(PAYLOAD, "my_ec2_instance"),
+ set(PAYLOAD_SIZE, 15)
+ ),
+ response(
+ expect(URI, "/latest/meta-data/tags/instance/CUSTOMER_ID"),
+ expect(METHOD, FLB_HTTP_GET),
+ set(STATUS, 200),
+ set(PAYLOAD, "70ec5c04-3a6e-11ed-a261-0242ac120002"),
+ set(PAYLOAD_SIZE, 36)
+ )
+ );
+ flb_aws_client_mock_configure_generator(request_chain);
+
+ client_generator = flb_aws_client_get_mock_generator();
+ ops = flb_calloc(1, sizeof(struct flb_filter_aws_init_options));
+ if (ops == NULL) {
+ TEST_MSG("calloc for aws plugin options failed\n");
+ TEST_CHECK(false);
+ return;
+ }
+ ops->client_generator = client_generator;
+
+ ctx = flb_create();
+
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ TEST_CHECK(in_ffd >= 0);
+ flb_input_set(ctx, in_ffd, "tag", "test", NULL);
+
+
+ /* Prepare output callback context*/
+ cb_data.cb = callback_test;
+ cb_data.data = NULL;
+
+ /* Lib output */
+ out_ffd = flb_output(ctx, (char *) "lib", (void *)&cb_data);
+ TEST_CHECK(out_ffd >= 0);
+ flb_output_set(ctx, out_ffd,
+ "match", "*",
+ "format", "json",
+ NULL);
+
+ filter_ffd = flb_filter(ctx, (char *) "aws", ops);
+ TEST_CHECK(filter_ffd >= 0);
+ ret = flb_filter_set(ctx, filter_ffd, "match", "*", NULL);
+ TEST_CHECK(ret == 0);
+ ret = flb_filter_set(ctx, filter_ffd, "ec2_instance_id", "false", NULL);
+ TEST_CHECK(ret == 0);
+ ret = flb_filter_set(ctx, filter_ffd, "az", "false", NULL);
+ TEST_CHECK(ret == 0);
+ ret = flb_filter_set(ctx, filter_ffd, "tags_enabled", "true", NULL);
+ TEST_CHECK(ret == 0);
+ ret = flb_filter_set(ctx, filter_ffd, "tags_exclude", "Name,Name2", NULL);
+ TEST_CHECK(ret == 0);
+
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ bytes = flb_lib_push(ctx, in_ffd, p, strlen(p));
+ if (!TEST_CHECK(bytes > 0)) {
+ TEST_MSG("zero bytes were pushed\n");
+ }
+
+ flb_time_msleep(1500); /* waiting flush */
+
+ output = get_output();
+ if (output) {
+ /* Name is excluded, so we don't expect it in the log */
+ result = strstr(output, "\"Name\":\"my_ec2_instance\"");
+ if (!TEST_CHECK(result == NULL)) {
+ TEST_MSG("output:%s\n", output);
+ }
+ result = strstr(output, "hello, from my ec2 instance");
+ if (!TEST_CHECK(result != NULL)) {
+ TEST_MSG("output:%s\n", output);
+ }
+ result = strstr(output, "\"CUSTOMER_ID\":\"70ec5c04-3a6e-11ed-a261-0242ac120002\"");
+ if (!TEST_CHECK(result != NULL)) {
+ TEST_MSG("output:%s\n", output);
+ }
+ }
+ else {
+ TEST_CHECK(false);
+ TEST_MSG("output is empty\n");
+ }
+
+ flb_stop(ctx);
+ flb_aws_client_mock_destroy_generator();
+ flb_destroy(ctx);
+ flb_free(ops);
+
+ set_output(NULL);
+}
+
+
+TEST_LIST = {
+ {"aws_ec2_tags_present", flb_test_aws_ec2_tags_present},
+ {"aws_ec2_tags_404", flb_test_aws_ec2_tags_404},
+ {"aws_ec2_tags_list_500", flb_test_aws_ec2_tags_list_500},
+ {"aws_ec2_tags_value_404", flb_test_aws_ec2_tags_value_404},
+ {"aws_ec2_tags_value_500", flb_test_aws_ec2_tags_value_500},
+ {"aws_ec2_tags_include", flb_test_aws_ec2_tags_include},
+ {"aws_ec2_tags_exclude", flb_test_aws_ec2_tags_exclude},
+ {NULL, NULL}
+};
diff --git a/fluent-bit/tests/runtime/filter_checklist.c b/fluent-bit/tests/runtime/filter_checklist.c
new file mode 100644
index 00000000..72d1e481
--- /dev/null
+++ b/fluent-bit/tests/runtime/filter_checklist.c
@@ -0,0 +1,405 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/* Fluent Bit
+ * ==========
+ * Copyright (C) 2019-2022 The Fluent Bit Authors
+ * Copyright (C) 2015-2018 Treasure Data Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <fluent-bit.h>
+#include <fluent-bit/flb_time.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include "flb_tests_runtime.h"
+
+#define TMP_CHECKLIST_PATH "checklist.txt"
+
+struct test_ctx {
+ flb_ctx_t *flb; /* Fluent Bit library context */
+ int i_ffd; /* Input fd */
+ int f_ffd; /* Filter fd (unused) */
+ int o_ffd; /* Output fd */
+};
+
+static struct test_ctx *test_ctx_create(struct flb_lib_out_cb *data)
+{
+ int i_ffd;
+ int o_ffd;
+ int f_ffd;
+ struct test_ctx *ctx = NULL;
+
+ ctx = flb_malloc(sizeof(struct test_ctx));
+ if (!TEST_CHECK(ctx != NULL)) {
+ TEST_MSG("malloc failed");
+ flb_errno();
+ return NULL;
+ }
+
+ /* Service config */
+ ctx->flb = flb_create();
+ flb_service_set(ctx->flb,
+ "Flush", "0.200000000",
+ "Grace", "1",
+ "Log_Level", "error",
+ NULL);
+
+ /* Input */
+ i_ffd = flb_input(ctx->flb, (char *) "lib", NULL);
+ TEST_CHECK(i_ffd >= 0);
+ flb_input_set(ctx->flb, i_ffd, "tag", "test", NULL);
+ ctx->i_ffd = i_ffd;
+
+ /* Filter */
+ f_ffd = flb_filter(ctx->flb, (char *) "checklist", NULL);
+ TEST_CHECK(f_ffd >= 0);
+ ctx->f_ffd = f_ffd;
+
+ /* Output */
+ o_ffd = flb_output(ctx->flb, (char *) "lib", (void *) data);
+ ctx->o_ffd = o_ffd;
+ TEST_CHECK(o_ffd >= 0);
+ flb_output_set(ctx->flb, o_ffd,
+ "match", "test",
+ NULL);
+
+ return ctx;
+}
+
+static void test_ctx_destroy(struct test_ctx *ctx)
+{
+ TEST_CHECK(ctx != NULL);
+
+ sleep(1);
+ flb_stop(ctx->flb);
+ flb_destroy(ctx->flb);
+ flb_free(ctx);
+}
+
+pthread_mutex_t result_mutex = PTHREAD_MUTEX_INITIALIZER;
+char *output = NULL;
+
+void set_output(char *val)
+{
+ pthread_mutex_lock(&result_mutex);
+ output = val;
+ pthread_mutex_unlock(&result_mutex);
+}
+
+char *get_output(void)
+{
+ char *val;
+
+ pthread_mutex_lock(&result_mutex);
+ val = output;
+ pthread_mutex_unlock(&result_mutex);
+
+ return val;
+}
+
+/* Callback to check expected results */
+static int cb_check_result(void *record, size_t size, void *data)
+{
+ char *p;
+ char *expected;
+ char *result;
+
+ expected = (char *) data;
+ result = (char *) record;
+
+ p = strstr(result, expected);
+ TEST_CHECK(p != NULL);
+
+ if (p==NULL) {
+ flb_error("Expected to find: '%s' in result '%s'",
+ expected, result);
+ }
+ /*
+ * If you want to debug your test
+ *
+ * printf("Expect: '%s' in result '%s'", expected, result);
+ */
+ flb_free(record);
+ return 0;
+}
+
+void delete_checklist()
+{
+ unlink(TMP_CHECKLIST_PATH);
+ flb_debug("remove checklist\n");
+}
+
+
+int create_checklist(char *checklist_body, size_t body_size)
+{
+ FILE *fp = NULL;
+ fp = fopen(TMP_CHECKLIST_PATH, "w+");
+ if (fp == NULL) {
+ TEST_MSG("fopen error\n");
+ return -1;
+ }
+ fwrite(checklist_body, body_size, 1, fp);
+ fflush(fp);
+ fclose(fp);
+ return 0;
+}
+
+void flb_test_lookup_key(void)
+{
+ int ret;
+ int bytes;
+ struct test_ctx *ctx;
+ struct flb_lib_out_cb cb_data;
+ char *checklist_body = ""
+ "malicious word\n";
+ char *input = "[0, {\"secret\": \"malicious word\"}]";
+
+ cb_data.cb = cb_check_result;
+ cb_data.data = "\"secret\":\"----\"";
+
+ ctx = test_ctx_create(&cb_data);
+ if (!TEST_CHECK(ctx != NULL)) {
+ TEST_MSG("test_ctx_create failed");
+ exit(EXIT_FAILURE);
+ }
+
+ ret = create_checklist(checklist_body, strlen(checklist_body));
+ TEST_CHECK(ret == 0);
+ /* Filter */
+ ret = flb_filter_set(ctx->flb, ctx->f_ffd,
+ "Match", "*",
+ "file", TMP_CHECKLIST_PATH,
+ "lookup_key", "secret",
+ "record", "secret ----",
+ NULL);
+ TEST_CHECK(ret==0);
+
+ ret = flb_output_set(ctx->flb, ctx->o_ffd,
+ "format", "json",
+ NULL);
+ TEST_CHECK(ret==0);
+
+ ret = flb_start(ctx->flb);
+ TEST_CHECK(ret==0);
+
+ bytes = flb_lib_push(ctx->flb, ctx->i_ffd, input, strlen(input));
+ TEST_CHECK(bytes == strlen(input));
+ flb_time_msleep(1500); /* waiting flush */
+ delete_checklist();
+
+ test_ctx_destroy(ctx);
+}
+
+void flb_test_lookup_keys(void)
+{
+ int ret;
+ int bytes;
+ struct test_ctx *ctx;
+ struct flb_lib_out_cb cb_data;
+ char *checklist_body = ""
+ "malicious word\n"
+ "confidential\n";
+
+ char *input = "[0, {\"secret\": \"malicious word\"}]";
+ char *input2 = "[0, {\"secret\": \"confidential\"}]";
+
+ cb_data.cb = cb_check_result;
+ cb_data.data = "\"secret\":\"----\"";
+
+ ctx = test_ctx_create(&cb_data);
+ if (!TEST_CHECK(ctx != NULL)) {
+ TEST_MSG("test_ctx_create failed");
+ exit(EXIT_FAILURE);
+ }
+
+ ret = create_checklist(checklist_body, strlen(checklist_body));
+ TEST_CHECK(ret == 0);
+ /* Filter */
+ ret = flb_filter_set(ctx->flb, ctx->f_ffd,
+ "Match", "*",
+ "file", TMP_CHECKLIST_PATH,
+ "lookup_key", "secret",
+ "record", "secret ----",
+ NULL);
+ TEST_CHECK(ret==0);
+
+ ret = flb_output_set(ctx->flb, ctx->o_ffd,
+ "format", "json",
+ NULL);
+ TEST_CHECK(ret==0);
+
+ ret = flb_start(ctx->flb);
+ TEST_CHECK(ret==0);
+
+ bytes = flb_lib_push(ctx->flb, ctx->i_ffd, input, strlen(input));
+ TEST_CHECK(bytes == strlen(input));
+ flb_time_msleep(1500); /* waiting flush */
+ bytes = flb_lib_push(ctx->flb, ctx->i_ffd, input2, strlen(input2));
+ TEST_CHECK(bytes == strlen(input2));
+ flb_time_msleep(1500); /* waiting flush */
+ delete_checklist();
+
+ test_ctx_destroy(ctx);
+}
+
+void flb_test_records(void)
+{
+ int ret;
+ int bytes;
+ struct test_ctx *ctx;
+ struct flb_lib_out_cb cb_data;
+ char *checklist_body = ""
+ "malicious word\n";
+ char *input = "[0, {\"secret\": \"malicious word\"}]";
+
+ cb_data.cb = cb_check_result;
+ cb_data.data = "\"secret\":\"----\",\"checklist\":true";
+
+ ctx = test_ctx_create(&cb_data);
+ if (!TEST_CHECK(ctx != NULL)) {
+ TEST_MSG("test_ctx_create failed");
+ exit(EXIT_FAILURE);
+ }
+
+ ret = create_checklist(checklist_body, strlen(checklist_body));
+ TEST_CHECK(ret == 0);
+ /* Filter */
+ ret = flb_filter_set(ctx->flb, ctx->f_ffd,
+ "Match", "*",
+ "file", TMP_CHECKLIST_PATH,
+ "lookup_key", "secret",
+ "record", "secret ----",
+ "record", "checklist true",
+ NULL);
+ TEST_CHECK(ret==0);
+
+ ret = flb_output_set(ctx->flb, ctx->o_ffd,
+ "format", "json",
+ NULL);
+ TEST_CHECK(ret==0);
+
+ ret = flb_start(ctx->flb);
+ TEST_CHECK(ret==0);
+
+ bytes = flb_lib_push(ctx->flb, ctx->i_ffd, input, strlen(input));
+ TEST_CHECK(bytes == strlen(input));
+ flb_time_msleep(1500); /* waiting flush */
+ delete_checklist();
+
+ test_ctx_destroy(ctx);
+}
+
+void flb_test_ignore_case(void)
+{
+ int ret;
+ int bytes;
+ struct test_ctx *ctx;
+ struct flb_lib_out_cb cb_data;
+ char *checklist_body = ""
+ "MaliCioUs Word\n";
+ char *input = "[0, {\"secret\": \"malicious word\"}]";
+
+ cb_data.cb = cb_check_result;
+ cb_data.data = "\"secret\":\"----\"";
+
+ ctx = test_ctx_create(&cb_data);
+ if (!TEST_CHECK(ctx != NULL)) {
+ TEST_MSG("test_ctx_create failed");
+ exit(EXIT_FAILURE);
+ }
+
+ ret = create_checklist(checklist_body, strlen(checklist_body));
+ TEST_CHECK(ret == 0);
+ /* Filter */
+ ret = flb_filter_set(ctx->flb, ctx->f_ffd,
+ "Match", "*",
+ "file", TMP_CHECKLIST_PATH,
+ "lookup_key", "secret",
+ "record", "secret ----",
+ "ignore_case", "true",
+ NULL);
+ TEST_CHECK(ret==0);
+
+ ret = flb_output_set(ctx->flb, ctx->o_ffd,
+ "format", "json",
+ NULL);
+ TEST_CHECK(ret==0);
+
+ ret = flb_start(ctx->flb);
+ TEST_CHECK(ret==0);
+
+ bytes = flb_lib_push(ctx->flb, ctx->i_ffd, input, strlen(input));
+ TEST_CHECK(bytes == strlen(input));
+ flb_time_msleep(1500); /* waiting flush */
+ delete_checklist();
+
+ test_ctx_destroy(ctx);
+}
+
+void flb_test_mode_partial(void)
+{
+ int ret;
+ int bytes;
+ struct test_ctx *ctx;
+ struct flb_lib_out_cb cb_data;
+ char *checklist_body = ""
+ "malicious\n";
+ char *input = "[0, {\"secret\": \"malicious word\"}]";
+
+ cb_data.cb = cb_check_result;
+ cb_data.data = "\"secret\":\"----\"";
+
+ ctx = test_ctx_create(&cb_data);
+ if (!TEST_CHECK(ctx != NULL)) {
+ TEST_MSG("test_ctx_create failed");
+ exit(EXIT_FAILURE);
+ }
+
+ ret = create_checklist(checklist_body, strlen(checklist_body));
+ TEST_CHECK(ret == 0);
+ /* Filter */
+ ret = flb_filter_set(ctx->flb, ctx->f_ffd,
+ "Match", "*",
+ "file", TMP_CHECKLIST_PATH,
+ "lookup_key", "secret",
+ "record", "secret ----",
+ "mode", "partial",
+ NULL);
+ TEST_CHECK(ret==0);
+
+ ret = flb_output_set(ctx->flb, ctx->o_ffd,
+ "format", "json",
+ NULL);
+ TEST_CHECK(ret==0);
+
+ ret = flb_start(ctx->flb);
+ TEST_CHECK(ret==0);
+
+ bytes = flb_lib_push(ctx->flb, ctx->i_ffd, input, strlen(input));
+ TEST_CHECK(bytes == strlen(input));
+ flb_time_msleep(1500); /* waiting flush */
+ delete_checklist();
+
+ test_ctx_destroy(ctx);
+}
+
+TEST_LIST = {
+ {"lookup_key", flb_test_lookup_key},
+ {"lookup_keys", flb_test_lookup_keys},
+ {"records", flb_test_records},
+ {"ignore_case", flb_test_ignore_case},
+ {"mode_partial", flb_test_mode_partial},
+ {NULL, NULL}
+};
diff --git a/fluent-bit/tests/runtime/filter_ecs.c b/fluent-bit/tests/runtime/filter_ecs.c
new file mode 100644
index 00000000..79868294
--- /dev/null
+++ b/fluent-bit/tests/runtime/filter_ecs.c
@@ -0,0 +1,445 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+#include <fluent-bit.h>
+#include <fluent-bit/flb_sds.h>
+#include "flb_tests_runtime.h"
+
+#define ERROR_RESPONSE "NOT FOUND"
+
+
+struct filter_test {
+ flb_ctx_t *flb; /* Fluent Bit library context */
+ int i_ffd; /* Input fd */
+ int f_ffd; /* Filter fd */
+};
+
+struct filter_test_result {
+ char *expected_pattern; /* string that must occur in output */
+ int expected_pattern_index; /* which record to check for the pattern */
+ int expected_records; /* expected number of outputted records */
+ int actual_records; /* actual number of outputted records */
+};
+
+/* Callback to check expected results */
+static int cb_check_result(void *record, size_t size, void *data)
+{
+ char *p;
+ struct filter_test_result *expected;
+ char *result;
+
+ expected = (struct filter_test_result *) data;
+ result = (char *) record;
+
+ if (expected->expected_pattern_index == expected->actual_records) {
+ p = strstr(result, expected->expected_pattern);
+ TEST_CHECK(p != NULL);
+
+ if (!p) {
+ flb_error("Expected to find: '%s' in result '%s'",
+ expected->expected_pattern, result);
+ }
+ /*
+ * If you want to debug your test
+ *
+ * printf("Expect: '%s' in result '%s'\n", expected->expected_pattern, result);
+ */
+ }
+
+ expected->actual_records++;
+
+ flb_free(record);
+ return 0;
+}
+
+
+struct str_list {
+ size_t size; /* size of lists */
+ int ignore_min_line_num; /* ignore line if the length is less than this value */
+ char **lists; /* string lists */
+};
+
+
+static struct filter_test *filter_test_create(struct flb_lib_out_cb *data,
+ char *tag)
+{
+ int i_ffd;
+ int f_ffd;
+ int o_ffd;
+ struct filter_test *ctx;
+
+ ctx = flb_malloc(sizeof(struct filter_test));
+ if (!ctx) {
+ flb_errno();
+ return NULL;
+ }
+
+ /* Service config */
+ ctx->flb = flb_create();
+ flb_service_set(ctx->flb,
+ "Flush", "0.200000000",
+ "Grace", "1",
+ NULL);
+
+ /* Input */
+ i_ffd = flb_input(ctx->flb, (char *) "lib", NULL);
+ TEST_CHECK(i_ffd >= 0);
+ /* filter relies on the tag container 12 char short container ID */
+ flb_input_set(ctx->flb, i_ffd, "tag", tag, NULL);
+ ctx->i_ffd = i_ffd;
+
+ /* Filter configuration */
+ f_ffd = flb_filter(ctx->flb, (char *) "ecs", NULL);
+ TEST_CHECK(f_ffd >= 0);
+ flb_filter_set(ctx->flb, f_ffd, "match", "*", NULL);
+ ctx->f_ffd = f_ffd;
+
+ /* Output */
+ o_ffd = flb_output(ctx->flb, (char *) "lib", (void *) data);
+ TEST_CHECK(o_ffd >= 0);
+ flb_output_set(ctx->flb, o_ffd,
+ "match", "*",
+ "format", "json",
+ NULL);
+
+ return ctx;
+}
+
+static void filter_test_destroy(struct filter_test *ctx)
+{
+ flb_stop(ctx->flb);
+ flb_destroy(ctx->flb);
+ flb_free(ctx);
+}
+
+static void flb_test_ecs_filter()
+{
+ int len;
+ int ret;
+ int bytes;
+ char *p;
+ struct flb_lib_out_cb cb_data;
+ struct filter_test *ctx;
+ struct filter_test_result expected = { 0 };
+
+ /* mocks calls- signals that we are in test mode */
+ setenv("FLB_ECS_PLUGIN_UNDER_TEST", "true", 1);
+
+ /* Create test context */
+ ctx = filter_test_create((void *) &cb_data, "testprefix-79c796ed2a7f");
+ if (!ctx) {
+ exit(EXIT_FAILURE);
+ }
+
+ /* Configure filter */
+ ret = flb_filter_set(ctx->flb, ctx->f_ffd,
+ "ecs_tag_prefix", "testprefix-",
+ "ADD", "resource $ClusterName.$TaskID.$ECSContainerName",
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ /* Prepare output callback with expected result */
+ expected.expected_records = 1; /* 1 record with metadata added */
+ expected.expected_pattern = "cluster_name.e01d58a8-151b-40e8-bc01-22647b9ecfec.nginx";
+ expected.expected_pattern_index = 0;
+ cb_data.cb = cb_check_result;
+ cb_data.data = (void *) &expected;
+
+ /* Start the engine */
+ ret = flb_start(ctx->flb);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data samples */
+ p = "[0, {\"log\":\"error: my error\"}]";
+ len = strlen(p);
+ bytes = flb_lib_push(ctx->flb, ctx->i_ffd, p, len);
+ TEST_CHECK(bytes == len);
+
+ /* check number of outputted records */
+ sleep(2);
+ TEST_CHECK(expected.actual_records == expected.expected_records);
+ filter_test_destroy(ctx);
+}
+
+/*
+ * First release of ECS filter could crash
+ * when saving that it faild to get metadata for a tag
+ */
+static void flb_test_ecs_filter_mark_tag_failed()
+{
+ int len;
+ int ret;
+ int bytes;
+ char *p;
+ struct flb_lib_out_cb cb_data;
+ struct filter_test *ctx;
+ struct filter_test_result expected = { 0 };
+
+ /* mocks calls- signals that we are in test mode */
+ setenv("FLB_ECS_PLUGIN_UNDER_TEST", "true", 1);
+ setenv("TEST_TASK_ERROR", ERROR_RESPONSE, 1);
+
+ /* Create test context */
+ ctx = filter_test_create((void *) &cb_data, "testprefix-79c796ed2a7f");
+ if (!ctx) {
+ exit(EXIT_FAILURE);
+ }
+
+ /* Configure filter */
+ ret = flb_filter_set(ctx->flb, ctx->f_ffd,
+ "ecs_tag_prefix", "testprefix-",
+ "ADD", "resource $ClusterName.$TaskID.$ECSContainerName",
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ /* Prepare output callback with expected result */
+ expected.expected_records = 4; /* 4 records with no metadata */
+ expected.expected_pattern = "";
+ expected.expected_pattern_index = 0;
+ cb_data.cb = cb_check_result;
+ cb_data.data = (void *) &expected;
+
+ /* Start the engine */
+ ret = flb_start(ctx->flb);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data samples */
+ p = "[0, {\"log\":\"error: my error\"}]";
+ len = strlen(p);
+ bytes = flb_lib_push(ctx->flb, ctx->i_ffd, p, len);
+ TEST_CHECK(bytes == len);
+ sleep(1);
+
+ p = "[0, {\"log\":\"error: my error\"}]";
+ len = strlen(p);
+ bytes = flb_lib_push(ctx->flb, ctx->i_ffd, p, len);
+ TEST_CHECK(bytes == len);
+ sleep(1);
+
+ p = "[0, {\"log\":\"error: my error\"}]";
+ len = strlen(p);
+ bytes = flb_lib_push(ctx->flb, ctx->i_ffd, p, len);
+ TEST_CHECK(bytes == len);
+ sleep(1);
+
+ p = "[0, {\"log\":\"error: my error\"}]";
+ len = strlen(p);
+ bytes = flb_lib_push(ctx->flb, ctx->i_ffd, p, len);
+ TEST_CHECK(bytes == len);
+ sleep(2);
+
+ /* check number of outputted records */
+ TEST_CHECK(expected.actual_records == expected.expected_records);
+ filter_test_destroy(ctx);
+}
+
+static void flb_test_ecs_filter_no_prefix()
+{
+ int len;
+ int ret;
+ int bytes;
+ char *p;
+ struct flb_lib_out_cb cb_data;
+ struct filter_test *ctx;
+ struct filter_test_result expected = { 0 };
+
+ /* mocks calls- signals that we are in test mode */
+ setenv("FLB_ECS_PLUGIN_UNDER_TEST", "true", 1);
+
+ /* Create test context */
+ ctx = filter_test_create((void *) &cb_data, "79c796ed2a7f");
+ if (!ctx) {
+ exit(EXIT_FAILURE);
+ }
+
+ /* Configure filter */
+ ret = flb_filter_set(ctx->flb, ctx->f_ffd,
+ "ecs_tag_prefix", "",
+ "ADD", "resource $ClusterName.$TaskID.$ECSContainerName",
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ /* Prepare output callback with expected result */
+ expected.expected_records = 1; /* 1 record with metadata added */
+ expected.expected_pattern = "cluster_name.e01d58a8-151b-40e8-bc01-22647b9ecfec.nginx";
+ expected.expected_pattern_index = 0;
+ cb_data.cb = cb_check_result;
+ cb_data.data = (void *) &expected;
+
+ /* Start the engine */
+ ret = flb_start(ctx->flb);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data samples */
+ p = "[0, {\"log\":\"error: my error\"}]";
+ len = strlen(p);
+ bytes = flb_lib_push(ctx->flb, ctx->i_ffd, p, len);
+ TEST_CHECK(bytes == len);
+
+ /* check number of outputted records */
+ sleep(2);
+ TEST_CHECK(expected.actual_records == expected.expected_records);
+ filter_test_destroy(ctx);
+}
+
+static void flb_test_ecs_filter_cluster_metadata_only()
+{
+ int len;
+ int ret;
+ int bytes;
+ char *p;
+ struct flb_lib_out_cb cb_data;
+ struct filter_test *ctx;
+ struct filter_test_result expected = { 0 };
+
+ /* mocks calls- signals that we are in test mode */
+ setenv("FLB_ECS_PLUGIN_UNDER_TEST", "true", 1);
+
+ /* Create test context */
+ ctx = filter_test_create((void *) &cb_data, "var.lib.ecs.79c796ed2a7f");
+ if (!ctx) {
+ exit(EXIT_FAILURE);
+ }
+
+ /* Configure filter */
+ ret = flb_filter_set(ctx->flb, ctx->f_ffd,
+ "ecs_tag_prefix", "",
+ "cluster_metadata_only", "on",
+ /* only cluster value will be populated */
+ "ADD", "resource $ClusterName.$TaskID.$ECSContainerName",
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ /* Prepare output callback with expected result */
+ expected.expected_records = 1; /* 1 record with only cluster metadata values added */
+ expected.expected_pattern = "cluster_name..";
+ expected.expected_pattern_index = 0;
+ cb_data.cb = cb_check_result;
+ cb_data.data = (void *) &expected;
+
+ /* Start the engine */
+ ret = flb_start(ctx->flb);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data samples */
+ p = "[0, {\"log\":\"error: my error\"}]";
+ len = strlen(p);
+ bytes = flb_lib_push(ctx->flb, ctx->i_ffd, p, len);
+ TEST_CHECK(bytes == len);
+
+ /* check number of outputted records */
+ sleep(2);
+ TEST_CHECK(expected.actual_records == expected.expected_records);
+ filter_test_destroy(ctx);
+}
+
+static void flb_test_ecs_filter_cluster_error()
+{
+ int len;
+ int ret;
+ int bytes;
+ char *p;
+ struct flb_lib_out_cb cb_data;
+ struct filter_test *ctx;
+ struct filter_test_result expected = { 0 };
+
+ /* mocks calls- signals that we are in test mode */
+ setenv("FLB_ECS_PLUGIN_UNDER_TEST", "true", 1);
+ setenv("TEST_CLUSTER_ERROR", ERROR_RESPONSE, 1);
+
+ /* Create test context */
+ ctx = filter_test_create((void *) &cb_data, "79c796ed2a7f");
+ if (!ctx) {
+ exit(EXIT_FAILURE);
+ }
+
+ /* Configure filter */
+ ret = flb_filter_set(ctx->flb, ctx->f_ffd,
+ "ecs_tag_prefix", "",
+ "ADD", "resource $ClusterName.$TaskID.$ECSContainerName",
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ /* this test is mainly for leak checking on error, not for checking result record */
+ expected.expected_records = 1; /* 1 record with no metadata */
+ expected.expected_pattern = "";
+ expected.expected_pattern_index = 0;
+ cb_data.cb = cb_check_result;
+ cb_data.data = (void *) &expected;
+
+ /* Start the engine */
+ ret = flb_start(ctx->flb);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data samples */
+ p = "[0, {\"log\":\"error: my error\"}]";
+ len = strlen(p);
+ bytes = flb_lib_push(ctx->flb, ctx->i_ffd, p, len);
+ TEST_CHECK(bytes == len);
+
+ /* check number of outputted records */
+ sleep(2);
+ TEST_CHECK(expected.actual_records == expected.expected_records);
+ filter_test_destroy(ctx);
+}
+
+static void flb_test_ecs_filter_task_error()
+{
+ int len;
+ int ret;
+ int bytes;
+ char *p;
+ struct flb_lib_out_cb cb_data;
+ struct filter_test *ctx;
+ struct filter_test_result expected = { 0 };
+
+ /* mocks calls- signals that we are in test mode */
+ setenv("FLB_ECS_PLUGIN_UNDER_TEST", "true", 1);
+ setenv("TEST_TASK_ERROR", ERROR_RESPONSE, 1);
+
+ /* Create test context */
+ ctx = filter_test_create((void *) &cb_data, "79c796ed2a7f");
+ if (!ctx) {
+ exit(EXIT_FAILURE);
+ }
+
+ /* Configure filter */
+ ret = flb_filter_set(ctx->flb, ctx->f_ffd,
+ "ecs_tag_prefix", "",
+ "ADD", "resource $ClusterName.$TaskID.$ECSContainerName",
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ /* this test is mainly for leak checking on error, not for checking result record */
+ expected.expected_records = 1; /* 1 record with no metadata */
+ expected.expected_pattern = "";
+ expected.expected_pattern_index = 0;
+ cb_data.cb = cb_check_result;
+ cb_data.data = (void *) &expected;
+
+ /* Start the engine */
+ ret = flb_start(ctx->flb);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data samples */
+ p = "[0, {\"log\":\"error: my error\"}]";
+ len = strlen(p);
+ bytes = flb_lib_push(ctx->flb, ctx->i_ffd, p, len);
+ TEST_CHECK(bytes == len);
+
+ /* check number of outputted records */
+ sleep(2);
+ TEST_CHECK(expected.actual_records == expected.expected_records);
+ filter_test_destroy(ctx);
+}
+
+TEST_LIST = {
+
+ {"flb_test_ecs_filter_mark_tag_failed" , flb_test_ecs_filter_mark_tag_failed },
+ {"flb_test_ecs_filter" , flb_test_ecs_filter },
+ {"flb_test_ecs_filter_no_prefix" , flb_test_ecs_filter_no_prefix },
+ {"flb_test_ecs_filter_cluster_metadata_only" , flb_test_ecs_filter_cluster_metadata_only },
+ {"flb_test_ecs_filter_cluster_error" , flb_test_ecs_filter_cluster_error },
+ {"flb_test_ecs_filter_task_error" , flb_test_ecs_filter_task_error },
+
+ {NULL, NULL}
+};
diff --git a/fluent-bit/tests/runtime/filter_expect.c b/fluent-bit/tests/runtime/filter_expect.c
new file mode 100644
index 00000000..dad1b637
--- /dev/null
+++ b/fluent-bit/tests/runtime/filter_expect.c
@@ -0,0 +1,555 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+#include <fluent-bit.h>
+#include <fluent-bit/flb_time.h>
+#include <fluent-bit/flb_sds.h>
+#include "flb_tests_runtime.h"
+
+struct test_ctx {
+ flb_ctx_t *flb; /* Fluent Bit library context */
+ int i_ffd; /* Input fd */
+ int f_ffd; /* Filter fd */
+ int o_ffd; /* Output fd */
+};
+
+pthread_mutex_t result_mutex = PTHREAD_MUTEX_INITIALIZER;
+int num_output = 0;
+static int get_output_num()
+{
+ int ret;
+ pthread_mutex_lock(&result_mutex);
+ ret = num_output;
+ pthread_mutex_unlock(&result_mutex);
+
+ return ret;
+}
+
+static void set_output_num(int num)
+{
+ pthread_mutex_lock(&result_mutex);
+ num_output = num;
+ pthread_mutex_unlock(&result_mutex);
+}
+
+static void clear_output_num()
+{
+ set_output_num(0);
+}
+
+/* Callback to check expected results */
+static int cb_check_result_json(void *record, size_t size, void *data)
+{
+ char *p;
+ char *expected;
+ char *result;
+ int num = get_output_num();
+
+ set_output_num(num+1);
+
+ expected = (char *) data;
+ result = (char *) record;
+
+ p = strstr(result, expected);
+ if (!TEST_CHECK(p != NULL)) {
+ TEST_MSG("Expected to find: '%s' in result '%s'",
+ expected, result);
+ }
+ /*
+ * If you want to debug your test
+ *
+ * printf("Expect: '%s' in result '%s'", expected, result);
+ */
+ flb_free(record);
+ return 0;
+}
+
+
+static struct test_ctx *test_ctx_create(struct flb_lib_out_cb *data)
+{
+ int i_ffd;
+ int f_ffd;
+ int o_ffd;
+ struct test_ctx *ctx;
+
+ ctx = flb_malloc(sizeof(struct test_ctx));
+ if (!ctx) {
+ flb_errno();
+ return NULL;
+ }
+
+ /* Service config */
+ ctx->flb = flb_create();
+ flb_service_set(ctx->flb,
+ "Flush", "0.200000000",
+ "Grace", "1",
+ "Log_Level", "Error",
+ NULL);
+
+ /* Input */
+ i_ffd = flb_input(ctx->flb, (char *) "lib", NULL);
+ TEST_CHECK(i_ffd >= 0);
+ flb_input_set(ctx->flb, i_ffd, "tag", "test", NULL);
+ ctx->i_ffd = i_ffd;
+
+ /* Filter configuration */
+ f_ffd = flb_filter(ctx->flb, (char *) "expect", NULL);
+ TEST_CHECK(f_ffd >= 0);
+ flb_filter_set(ctx->flb, f_ffd,
+ "match", "*", "action", "result_key",
+ "result_key", "result",
+ NULL);
+ ctx->f_ffd = f_ffd;
+
+ /* Output */
+ o_ffd = flb_output(ctx->flb, (char *) "lib", (void *) data);
+ TEST_CHECK(o_ffd >= 0);
+ flb_output_set(ctx->flb, o_ffd,
+ "match", "test",
+ "format", "json",
+ NULL);
+ ctx->o_ffd = o_ffd;
+
+ return ctx;
+}
+
+static void test_ctx_destroy(struct test_ctx *ctx)
+{
+ flb_stop(ctx->flb);
+ flb_destroy(ctx->flb);
+ flb_free(ctx);
+}
+
+void flb_test_key_exists_matched()
+{
+ struct flb_lib_out_cb cb_data;
+ struct test_ctx *ctx;
+ int len;
+ int ret;
+ int bytes;
+ char *input = "[0, {\"key\":\"val\"}]";
+
+ clear_output_num();
+ cb_data.cb = cb_check_result_json;
+ cb_data.data = "\"result\":true";
+
+ ctx = test_ctx_create(&cb_data);
+ if (!TEST_CHECK(ctx != NULL)) {
+ TEST_MSG("test_ctx_create failed");
+ exit(EXIT_FAILURE);
+ }
+
+ ret = flb_filter_set(ctx->flb, ctx->f_ffd,
+ "key_exists", "key",
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ /* Start the engine */
+ ret = flb_start(ctx->flb);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data samples */
+ len = strlen(input);
+ bytes = flb_lib_push(ctx->flb, ctx->i_ffd, input, len);
+ TEST_CHECK(bytes == len);
+ flb_time_msleep(500);
+
+ ret = get_output_num();
+ if (!TEST_CHECK(ret > 0)) {
+ TEST_MSG("no output");
+ }
+
+ test_ctx_destroy(ctx);
+}
+
+void flb_test_key_exists_not_matched()
+{
+ struct flb_lib_out_cb cb_data;
+ struct test_ctx *ctx;
+ int len;
+ int ret;
+ int bytes;
+ char *input = "[0, {\"key\":\"val\"}]";
+
+ clear_output_num();
+ cb_data.cb = cb_check_result_json;
+ cb_data.data = "\"result\":false";
+
+ ctx = test_ctx_create(&cb_data);
+ if (!TEST_CHECK(ctx != NULL)) {
+ TEST_MSG("test_ctx_create failed");
+ exit(EXIT_FAILURE);
+ }
+
+ ret = flb_filter_set(ctx->flb, ctx->f_ffd,
+ "key_exists", "not_key",
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ /* Start the engine */
+ ret = flb_start(ctx->flb);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data samples */
+ len = strlen(input);
+ bytes = flb_lib_push(ctx->flb, ctx->i_ffd, input, len);
+ TEST_CHECK(bytes == len);
+ flb_time_msleep(500);
+
+ ret = get_output_num();
+ if (!TEST_CHECK(ret > 0)) {
+ TEST_MSG("no output");
+ }
+
+ test_ctx_destroy(ctx);
+}
+
+void flb_test_key_not_exists_matched()
+{
+ struct flb_lib_out_cb cb_data;
+ struct test_ctx *ctx;
+ int len;
+ int ret;
+ int bytes;
+ char *input = "[0, {\"key\":\"val\"}]";
+
+ clear_output_num();
+ cb_data.cb = cb_check_result_json;
+ cb_data.data = "\"result\":true";
+
+ ctx = test_ctx_create(&cb_data);
+ if (!TEST_CHECK(ctx != NULL)) {
+ TEST_MSG("test_ctx_create failed");
+ exit(EXIT_FAILURE);
+ }
+
+ ret = flb_filter_set(ctx->flb, ctx->f_ffd,
+ "key_not_exists", "not_key",
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ /* Start the engine */
+ ret = flb_start(ctx->flb);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data samples */
+ len = strlen(input);
+ bytes = flb_lib_push(ctx->flb, ctx->i_ffd, input, len);
+ TEST_CHECK(bytes == len);
+ flb_time_msleep(500);
+
+ ret = get_output_num();
+ if (!TEST_CHECK(ret > 0)) {
+ TEST_MSG("no output");
+ }
+
+ test_ctx_destroy(ctx);
+}
+
+void flb_test_key_not_exists_not_matched()
+{
+ struct flb_lib_out_cb cb_data;
+ struct test_ctx *ctx;
+ int len;
+ int ret;
+ int bytes;
+ char *input = "[0, {\"key\":\"val\"}]";
+
+ clear_output_num();
+ cb_data.cb = cb_check_result_json;
+ cb_data.data = "\"result\":false";
+
+ ctx = test_ctx_create(&cb_data);
+ if (!TEST_CHECK(ctx != NULL)) {
+ TEST_MSG("test_ctx_create failed");
+ exit(EXIT_FAILURE);
+ }
+
+ ret = flb_filter_set(ctx->flb, ctx->f_ffd,
+ "key_not_exists", "key",
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ /* Start the engine */
+ ret = flb_start(ctx->flb);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data samples */
+ len = strlen(input);
+ bytes = flb_lib_push(ctx->flb, ctx->i_ffd, input, len);
+ TEST_CHECK(bytes == len);
+ flb_time_msleep(500);
+
+ ret = get_output_num();
+ if (!TEST_CHECK(ret > 0)) {
+ TEST_MSG("no output");
+ }
+
+ test_ctx_destroy(ctx);
+}
+
+void flb_test_key_val_is_null_matched()
+{
+ struct flb_lib_out_cb cb_data;
+ struct test_ctx *ctx;
+ int len;
+ int ret;
+ int bytes;
+ char *input = "[0, {\"key\":null}]";
+
+ clear_output_num();
+ cb_data.cb = cb_check_result_json;
+ cb_data.data = "\"result\":true";
+
+ ctx = test_ctx_create(&cb_data);
+ if (!TEST_CHECK(ctx != NULL)) {
+ TEST_MSG("test_ctx_create failed");
+ exit(EXIT_FAILURE);
+ }
+
+ ret = flb_filter_set(ctx->flb, ctx->f_ffd,
+ "key_val_is_null", "key",
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ /* Start the engine */
+ ret = flb_start(ctx->flb);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data samples */
+ len = strlen(input);
+ bytes = flb_lib_push(ctx->flb, ctx->i_ffd, input, len);
+ TEST_CHECK(bytes == len);
+ flb_time_msleep(500);
+
+ ret = get_output_num();
+ if (!TEST_CHECK(ret > 0)) {
+ TEST_MSG("no output");
+ }
+
+ test_ctx_destroy(ctx);
+}
+
+void flb_test_key_val_is_null_not_matched()
+{
+ struct flb_lib_out_cb cb_data;
+ struct test_ctx *ctx;
+ int len;
+ int ret;
+ int bytes;
+ char *input = "[0, {\"key\":\"val\"}]";
+
+ clear_output_num();
+ cb_data.cb = cb_check_result_json;
+ cb_data.data = "\"result\":false";
+
+ ctx = test_ctx_create(&cb_data);
+ if (!TEST_CHECK(ctx != NULL)) {
+ TEST_MSG("test_ctx_create failed");
+ exit(EXIT_FAILURE);
+ }
+
+ ret = flb_filter_set(ctx->flb, ctx->f_ffd,
+ "key_val_is_null", "key",
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ /* Start the engine */
+ ret = flb_start(ctx->flb);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data samples */
+ len = strlen(input);
+ bytes = flb_lib_push(ctx->flb, ctx->i_ffd, input, len);
+ TEST_CHECK(bytes == len);
+ flb_time_msleep(500);
+
+ ret = get_output_num();
+ if (!TEST_CHECK(ret > 0)) {
+ TEST_MSG("no output");
+ }
+
+ test_ctx_destroy(ctx);
+}
+
+void flb_test_key_val_is_not_null_matched()
+{
+ struct flb_lib_out_cb cb_data;
+ struct test_ctx *ctx;
+ int len;
+ int ret;
+ int bytes;
+ char *input = "[0, {\"key\":\"val\"}]";
+
+ clear_output_num();
+ cb_data.cb = cb_check_result_json;
+ cb_data.data = "\"result\":true";
+
+ ctx = test_ctx_create(&cb_data);
+ if (!TEST_CHECK(ctx != NULL)) {
+ TEST_MSG("test_ctx_create failed");
+ exit(EXIT_FAILURE);
+ }
+
+ ret = flb_filter_set(ctx->flb, ctx->f_ffd,
+ "key_val_is_not_null", "key",
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ /* Start the engine */
+ ret = flb_start(ctx->flb);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data samples */
+ len = strlen(input);
+ bytes = flb_lib_push(ctx->flb, ctx->i_ffd, input, len);
+ TEST_CHECK(bytes == len);
+ flb_time_msleep(500);
+
+ ret = get_output_num();
+ if (!TEST_CHECK(ret > 0)) {
+ TEST_MSG("no output");
+ }
+
+ test_ctx_destroy(ctx);
+}
+
+void flb_test_key_val_is_not_null_not_matched()
+{
+ struct flb_lib_out_cb cb_data;
+ struct test_ctx *ctx;
+ int len;
+ int ret;
+ int bytes;
+ char *input = "[0, {\"key\":null}]";
+
+ clear_output_num();
+ cb_data.cb = cb_check_result_json;
+ cb_data.data = "\"result\":false";
+
+ ctx = test_ctx_create(&cb_data);
+ if (!TEST_CHECK(ctx != NULL)) {
+ TEST_MSG("test_ctx_create failed");
+ exit(EXIT_FAILURE);
+ }
+
+ ret = flb_filter_set(ctx->flb, ctx->f_ffd,
+ "key_val_is_not_null", "key",
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ /* Start the engine */
+ ret = flb_start(ctx->flb);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data samples */
+ len = strlen(input);
+ bytes = flb_lib_push(ctx->flb, ctx->i_ffd, input, len);
+ TEST_CHECK(bytes == len);
+ flb_time_msleep(500);
+
+ ret = get_output_num();
+ if (!TEST_CHECK(ret > 0)) {
+ TEST_MSG("no output");
+ }
+
+ test_ctx_destroy(ctx);
+}
+
+void flb_test_key_val_eq_matched()
+{
+ struct flb_lib_out_cb cb_data;
+ struct test_ctx *ctx;
+ int len;
+ int ret;
+ int bytes;
+ char *input = "[0, {\"key\":\"val\"}]";
+
+ clear_output_num();
+ cb_data.cb = cb_check_result_json;
+ cb_data.data = "\"result\":true";
+
+ ctx = test_ctx_create(&cb_data);
+ if (!TEST_CHECK(ctx != NULL)) {
+ TEST_MSG("test_ctx_create failed");
+ exit(EXIT_FAILURE);
+ }
+
+ ret = flb_filter_set(ctx->flb, ctx->f_ffd,
+ "key_val_eq", "key val",
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ /* Start the engine */
+ ret = flb_start(ctx->flb);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data samples */
+ len = strlen(input);
+ bytes = flb_lib_push(ctx->flb, ctx->i_ffd, input, len);
+ TEST_CHECK(bytes == len);
+ flb_time_msleep(500);
+
+ ret = get_output_num();
+ if (!TEST_CHECK(ret > 0)) {
+ TEST_MSG("no output");
+ }
+
+ test_ctx_destroy(ctx);
+}
+
+void flb_test_key_val_eq_not_matched()
+{
+ struct flb_lib_out_cb cb_data;
+ struct test_ctx *ctx;
+ int len;
+ int ret;
+ int bytes;
+ char *input = "[0, {\"key\":\"val\"}]";
+
+ clear_output_num();
+ cb_data.cb = cb_check_result_json;
+ cb_data.data = "\"result\":false";
+
+ ctx = test_ctx_create(&cb_data);
+ if (!TEST_CHECK(ctx != NULL)) {
+ TEST_MSG("test_ctx_create failed");
+ exit(EXIT_FAILURE);
+ }
+
+ ret = flb_filter_set(ctx->flb, ctx->f_ffd,
+ "key_val_eq", "not_key val",
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ /* Start the engine */
+ ret = flb_start(ctx->flb);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data samples */
+ len = strlen(input);
+ bytes = flb_lib_push(ctx->flb, ctx->i_ffd, input, len);
+ TEST_CHECK(bytes == len);
+ flb_time_msleep(500);
+
+ ret = get_output_num();
+ if (!TEST_CHECK(ret > 0)) {
+ TEST_MSG("no output");
+ }
+
+ test_ctx_destroy(ctx);
+}
+
+
+TEST_LIST = {
+ {"key_exists_matched", flb_test_key_exists_matched},
+ {"key_exists_not_matched", flb_test_key_exists_not_matched},
+ {"key_not_exists_matched", flb_test_key_not_exists_matched},
+ {"key_not_exists_not_matched", flb_test_key_not_exists_not_matched},
+ {"key_val_is_null_matched", flb_test_key_val_is_null_matched},
+ {"key_val_is_null_not_matched", flb_test_key_val_is_null_not_matched},
+ {"key_val_is_not_null_matched", flb_test_key_val_is_not_null_matched},
+ {"key_val_is_not_null_not_matched", flb_test_key_val_is_not_null_not_matched},
+ {"key_val_eq_matched", flb_test_key_val_eq_matched},
+ {"key_val_eq_not_matched", flb_test_key_val_eq_not_matched},
+ {NULL, NULL}
+};
diff --git a/fluent-bit/tests/runtime/filter_grep.c b/fluent-bit/tests/runtime/filter_grep.c
new file mode 100644
index 00000000..6ae5721f
--- /dev/null
+++ b/fluent-bit/tests/runtime/filter_grep.c
@@ -0,0 +1,840 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+#include <fluent-bit.h>
+#include <fluent-bit/flb_time.h>
+#include "flb_tests_runtime.h"
+
+/* Test data */
+
+/* Test functions */
+void flb_test_filter_grep_regex(void);
+void flb_test_filter_grep_exclude(void);
+void flb_test_filter_grep_invalid(void);
+
+pthread_mutex_t result_mutex = PTHREAD_MUTEX_INITIALIZER;
+int num_output = 0;
+
+static int cb_count_msgpack(void *record, size_t size, void *data)
+{
+ msgpack_unpacked result;
+ size_t off = 0;
+
+ if (!TEST_CHECK(data != NULL)) {
+ flb_error("data is NULL");
+ }
+
+ /* Iterate each item array and apply rules */
+ msgpack_unpacked_init(&result);
+ while (msgpack_unpack_next(&result, record, size, &off) == MSGPACK_UNPACK_SUCCESS) {
+ pthread_mutex_lock(&result_mutex);
+ num_output++;
+ pthread_mutex_unlock(&result_mutex);
+ }
+ msgpack_unpacked_destroy(&result);
+
+ flb_free(record);
+ return 0;
+}
+
+static void clear_output_num()
+{
+ pthread_mutex_lock(&result_mutex);
+ num_output = 0;
+ pthread_mutex_unlock(&result_mutex);
+}
+
+static int get_output_num()
+{
+ int ret;
+ pthread_mutex_lock(&result_mutex);
+ ret = num_output;
+ pthread_mutex_unlock(&result_mutex);
+
+ return ret;
+}
+
+void flb_test_filter_grep_regex(void)
+{
+ int i;
+ int ret;
+ int bytes;
+ char p[100];
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+ int filter_ffd;
+
+ ctx = flb_create();
+
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ TEST_CHECK(in_ffd >= 0);
+ flb_input_set(ctx, in_ffd, "tag", "test", NULL);
+
+ out_ffd = flb_output(ctx, (char *) "stdout", NULL);
+ TEST_CHECK(out_ffd >= 0);
+ flb_output_set(ctx, out_ffd, "match", "test", NULL);
+
+ filter_ffd = flb_filter(ctx, (char *) "grep", NULL);
+ TEST_CHECK(filter_ffd >= 0);
+ ret = flb_filter_set(ctx, filter_ffd, "match", "*", NULL);
+ TEST_CHECK(ret == 0);
+ ret = flb_filter_set(ctx, filter_ffd, "Regex", "val 1", NULL);
+ TEST_CHECK(ret == 0);
+
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ for (i = 0; i < 256; i++) {
+ memset(p, '\0', sizeof(p));
+ snprintf(p, sizeof(p), "[%d, {\"val\": \"%d\",\"END_KEY\": \"JSON_END\"}]", i, (i * i));
+ bytes = flb_lib_push(ctx, in_ffd, p, strlen(p));
+ TEST_CHECK(bytes == strlen(p));
+ }
+
+ flb_time_msleep(1500); /* waiting flush */
+
+ flb_stop(ctx);
+ flb_destroy(ctx);
+}
+
+void flb_test_filter_grep_exclude(void)
+{
+ int i;
+ int ret;
+ int bytes;
+ char p[100];
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+ int filter_ffd;
+
+ ctx = flb_create();
+
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ TEST_CHECK(in_ffd >= 0);
+ flb_input_set(ctx, in_ffd, "tag", "test", NULL);
+
+ out_ffd = flb_output(ctx, (char *) "stdout", NULL);
+ TEST_CHECK(out_ffd >= 0);
+ flb_output_set(ctx, out_ffd, "match", "test", NULL);
+
+ filter_ffd = flb_filter(ctx, (char *) "grep", NULL);
+ TEST_CHECK(filter_ffd >= 0);
+ ret = flb_filter_set(ctx, filter_ffd, "match", "*", NULL);
+ TEST_CHECK(ret == 0);
+ ret = flb_filter_set(ctx, filter_ffd, "Exclude", "val 1", NULL);
+ TEST_CHECK(ret == 0);
+
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ for (i = 0; i < 256; i++) {
+ memset(p, '\0', sizeof(p));
+ snprintf(p, sizeof(p), "[%d, {\"val\": \"%d\",\"END_KEY\": \"JSON_END\"}]", i, (i * i));
+ bytes = flb_lib_push(ctx, in_ffd, p, strlen(p));
+ TEST_CHECK(bytes == strlen(p));
+ }
+
+ flb_time_msleep(1500); /* waiting flush */
+
+ flb_stop(ctx);
+ flb_destroy(ctx);
+}
+
+void flb_test_filter_grep_invalid(void)
+{
+ int i;
+ int ret;
+ int bytes;
+ char p[100];
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+ int filter_ffd;
+
+ ctx = flb_create();
+
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ TEST_CHECK(in_ffd >= 0);
+ flb_input_set(ctx, in_ffd, "tag", "test", NULL);
+
+ out_ffd = flb_output(ctx, (char *) "stdout", NULL);
+ TEST_CHECK(out_ffd >= 0);
+ flb_output_set(ctx, out_ffd, "match", "test", NULL);
+
+ filter_ffd = flb_filter(ctx, (char *) "grep", NULL);
+ TEST_CHECK(filter_ffd >= 0);
+ ret = flb_filter_set(ctx, filter_ffd, "match", "*", NULL);
+ TEST_CHECK(ret == 0);
+ ret = flb_filter_set(ctx, filter_ffd, "Regex", "val", NULL);
+ TEST_CHECK(ret == 0);
+ ret = flb_filter_set(ctx, filter_ffd, "Exclude", "val", NULL);
+ TEST_CHECK(ret == 0);
+
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == -1);
+
+ for (i = 0; i < 256; i++) {
+ memset(p, '\0', sizeof(p));
+ snprintf(p, sizeof(p), "[%d, {\"val\": \"%d\",\"END_KEY\": \"JSON_END\"}]", i, (i * i));
+ bytes = flb_lib_push(ctx, in_ffd, p, strlen(p));
+ TEST_CHECK(bytes == -1);
+ }
+
+ flb_stop(ctx);
+ flb_destroy(ctx);
+}
+
+/* filter_grep supports multiple 'Exclude's.
+ * If user sets multiple 'Exclude's, fluent-bit uses as OR conditions.
+ */
+void flb_test_filter_grep_multi_exclude(void)
+{
+ int i;
+ int ret;
+ int bytes;
+ char p[512];
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+ int filter_ffd;
+ int got;
+ int n_loop = 256;
+ int not_used = 0;
+ struct flb_lib_out_cb cb_data;
+
+ /* Prepare output callback with expected result */
+ cb_data.cb = cb_count_msgpack;
+ cb_data.data = &not_used;
+
+ ctx = flb_create();
+
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ TEST_CHECK(in_ffd >= 0);
+ flb_input_set(ctx, in_ffd, "tag", "test", NULL);
+
+ out_ffd = flb_output(ctx, (char *) "lib", &cb_data);
+ TEST_CHECK(out_ffd >= 0);
+ flb_output_set(ctx, out_ffd, "match", "test", NULL);
+
+ filter_ffd = flb_filter(ctx, (char *) "grep", NULL);
+ TEST_CHECK(filter_ffd >= 0);
+ ret = flb_filter_set(ctx, filter_ffd, "match", "*", NULL);
+ TEST_CHECK(ret == 0);
+ ret = flb_filter_set(ctx, filter_ffd,
+ "Exclude", "log deprecated",
+ "Exclude", "log hoge",
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ clear_output_num();
+
+ ret = flb_start(ctx);
+ if(!TEST_CHECK(ret == 0)) {
+ TEST_MSG("flb_start failed");
+ exit(EXIT_FAILURE);
+ }
+
+ /* Ingest 2 records per loop. One of them should be excluded. */
+ for (i = 0; i < n_loop; i++) {
+ memset(p, '\0', sizeof(p));
+ snprintf(p, sizeof(p), "[%d, {\"val\": \"%d\",\"log\": \"Using deprecated option\"}]", i, (i * i));
+ bytes = flb_lib_push(ctx, in_ffd, p, strlen(p));
+ TEST_CHECK(bytes == strlen(p));
+
+ /* Below record will be included */
+ memset(p, '\0', sizeof(p));
+ snprintf(p, sizeof(p), "[%d, {\"val\": \"%d\",\"log\": \"Using option\"}]", i, (i * i));
+ bytes = flb_lib_push(ctx, in_ffd, p, strlen(p));
+ TEST_CHECK(bytes == strlen(p));
+ }
+
+ flb_time_msleep(1500); /* waiting flush */
+
+ got = get_output_num();
+ if (!TEST_CHECK(got == n_loop)) {
+ TEST_MSG("expect: %d got: %d", n_loop, got);
+ }
+
+ flb_stop(ctx);
+ flb_destroy(ctx);
+}
+
+void flb_test_filter_grep_unknown_property(void)
+{
+ int ret;
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+ int filter_ffd;
+ int not_used = 0;
+ struct flb_lib_out_cb cb_data;
+
+ /* Prepare output callback with expected result */
+ cb_data.cb = cb_count_msgpack;
+ cb_data.data = &not_used;
+
+ ctx = flb_create();
+
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ TEST_CHECK(in_ffd >= 0);
+ flb_input_set(ctx, in_ffd, "tag", "test", NULL);
+
+ out_ffd = flb_output(ctx, (char *) "lib", &cb_data);
+ TEST_CHECK(out_ffd >= 0);
+ flb_output_set(ctx, out_ffd, "match", "test", NULL);
+
+ filter_ffd = flb_filter(ctx, (char *) "grep", NULL);
+ TEST_CHECK(filter_ffd >= 0);
+ ret = flb_filter_set(ctx, filter_ffd, "match", "*", NULL);
+ TEST_CHECK(ret == 0);
+ ret = flb_filter_set(ctx, filter_ffd, "UNKNOWN_PROPERTY", "aaaaaa", NULL);
+ TEST_CHECK(ret == 0);
+
+ clear_output_num();
+
+ ret = flb_start(ctx);
+ if(!TEST_CHECK(ret != 0)) {
+ TEST_MSG("flb_start should be failed");
+ exit(EXIT_FAILURE);
+ }
+
+ flb_destroy(ctx);
+}
+
+/*
+ * https://github.com/fluent/fluent-bit/issues/5209
+ * To support /REGEX/ style.
+ */
+void flb_test_issue_5209(void)
+{
+ int i;
+ int ret;
+ int bytes;
+ char p[512];
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+ int filter_ffd;
+ int got;
+ int n_loop = 256;
+ int not_used = 0;
+ struct flb_lib_out_cb cb_data;
+
+ /* Prepare output callback with expected result */
+ cb_data.cb = cb_count_msgpack;
+ cb_data.data = &not_used;
+
+ ctx = flb_create();
+
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ TEST_CHECK(in_ffd >= 0);
+ flb_input_set(ctx, in_ffd, "tag", "test", NULL);
+
+ out_ffd = flb_output(ctx, (char *) "lib", &cb_data);
+ TEST_CHECK(out_ffd >= 0);
+ flb_output_set(ctx, out_ffd, "match", "test", NULL);
+
+ filter_ffd = flb_filter(ctx, (char *) "grep", NULL);
+ TEST_CHECK(filter_ffd >= 0);
+ ret = flb_filter_set(ctx, filter_ffd, "match", "*", NULL);
+ TEST_CHECK(ret == 0);
+ ret = flb_filter_set(ctx, filter_ffd, "Exclude", "log /Using deprecated option/", NULL);
+ TEST_CHECK(ret == 0);
+
+ clear_output_num();
+
+ ret = flb_start(ctx);
+ if(!TEST_CHECK(ret == 0)) {
+ TEST_MSG("flb_start failed");
+ exit(EXIT_FAILURE);
+ }
+
+ /* Ingest 2 records per loop. One of them should be excluded. */
+ for (i = 0; i < n_loop; i++) {
+ memset(p, '\0', sizeof(p));
+ snprintf(p, sizeof(p), "[%d, {\"val\": \"%d\",\"END_KEY\": \"JSON_END\"}]", i, (i * i));
+ bytes = flb_lib_push(ctx, in_ffd, p, strlen(p));
+ TEST_CHECK(bytes == strlen(p));
+
+ /* Below record will be excluded */
+ memset(p, '\0', sizeof(p));
+ snprintf(p, sizeof(p), "[%d, {\"val\": \"%d\",\"log\": \"Using deprecated option\"}]", i, (i * i));
+ bytes = flb_lib_push(ctx, in_ffd, p, strlen(p));
+ TEST_CHECK(bytes == strlen(p));
+ }
+
+ flb_time_msleep(1500); /* waiting flush */
+
+ got = get_output_num();
+ if (!TEST_CHECK(got == n_loop)) {
+ TEST_MSG("expect: %d got: %d", n_loop, got);
+ }
+
+ flb_stop(ctx);
+ flb_destroy(ctx);
+}
+
+
+/* filter_grep supports multiple 'Regex's.
+ * If user sets multiple 'Regex's, fluent-bit uses as AND conditions.
+ */
+void flb_test_filter_grep_multi_regex(void)
+{
+ int i;
+ int ret;
+ int bytes;
+ char p[512];
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+ int filter_ffd;
+ int got;
+ int n_loop = 256;
+ int not_used = 0;
+ struct flb_lib_out_cb cb_data;
+
+ /* Prepare output callback with expected result */
+ cb_data.cb = cb_count_msgpack;
+ cb_data.data = &not_used;
+
+ ctx = flb_create();
+
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ TEST_CHECK(in_ffd >= 0);
+ flb_input_set(ctx, in_ffd, "tag", "test", NULL);
+
+ out_ffd = flb_output(ctx, (char *) "lib", &cb_data);
+ TEST_CHECK(out_ffd >= 0);
+ flb_output_set(ctx, out_ffd, "match", "test", NULL);
+
+ filter_ffd = flb_filter(ctx, (char *) "grep", NULL);
+ TEST_CHECK(filter_ffd >= 0);
+ ret = flb_filter_set(ctx, filter_ffd, "match", "*", NULL);
+ TEST_CHECK(ret == 0);
+ ret = flb_filter_set(ctx, filter_ffd,
+ "Regex", "log deprecated",
+ "Regex", "log option",
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ clear_output_num();
+
+ ret = flb_start(ctx);
+ if(!TEST_CHECK(ret == 0)) {
+ TEST_MSG("flb_start failed");
+ exit(EXIT_FAILURE);
+ }
+
+ /* Ingest 2 records per loop. One of them should be excluded. */
+ for (i = 0; i < n_loop; i++) {
+ memset(p, '\0', sizeof(p));
+ /* Below record will be included */
+ snprintf(p, sizeof(p), "[%d, {\"val\": \"%d\",\"log\": \"Using deprecated option\"}]", i, (i * i));
+ bytes = flb_lib_push(ctx, in_ffd, p, strlen(p));
+ TEST_CHECK(bytes == strlen(p));
+
+ /* Below record will be excluded */
+ memset(p, '\0', sizeof(p));
+ snprintf(p, sizeof(p), "[%d, {\"val\": \"%d\",\"log\": \"Using option\"}]", i, (i * i));
+ bytes = flb_lib_push(ctx, in_ffd, p, strlen(p));
+ TEST_CHECK(bytes == strlen(p));
+ }
+
+ flb_time_msleep(1500); /* waiting flush */
+
+ got = get_output_num();
+ if (!TEST_CHECK(got == n_loop)) {
+ TEST_MSG("expect: %d got: %d", n_loop, got);
+ }
+
+ flb_stop(ctx);
+ flb_destroy(ctx);
+}
+
+void flb_test_error_AND_regex_exclude(void)
+{
+ int ret;
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+ int filter_ffd;
+
+ ctx = flb_create();
+
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ TEST_CHECK(in_ffd >= 0);
+ flb_input_set(ctx, in_ffd, "tag", "test", NULL);
+
+ out_ffd = flb_output(ctx, (char *) "stdout", NULL);
+ TEST_CHECK(out_ffd >= 0);
+ flb_output_set(ctx, out_ffd, "match", "test", NULL);
+
+ filter_ffd = flb_filter(ctx, (char *) "grep", NULL);
+ TEST_CHECK(filter_ffd >= 0);
+ ret = flb_filter_set(ctx, filter_ffd, "match", "*", NULL);
+ TEST_CHECK(ret == 0);
+ ret = flb_filter_set(ctx, filter_ffd,
+ "Regex", "val 1",
+ "Exclude", "val2 3",
+ "Logical_Op", "AND",
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ ret = flb_start(ctx);
+ TEST_CHECK(ret != 0);
+
+ flb_stop(ctx);
+ flb_destroy(ctx);
+}
+
+void flb_test_error_OR_regex_exclude(void)
+{
+ int ret;
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+ int filter_ffd;
+
+ ctx = flb_create();
+
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ TEST_CHECK(in_ffd >= 0);
+ flb_input_set(ctx, in_ffd, "tag", "test", NULL);
+
+ out_ffd = flb_output(ctx, (char *) "stdout", NULL);
+ TEST_CHECK(out_ffd >= 0);
+ flb_output_set(ctx, out_ffd, "match", "test", NULL);
+
+ filter_ffd = flb_filter(ctx, (char *) "grep", NULL);
+ TEST_CHECK(filter_ffd >= 0);
+ ret = flb_filter_set(ctx, filter_ffd, "match", "*", NULL);
+ TEST_CHECK(ret == 0);
+ ret = flb_filter_set(ctx, filter_ffd,
+ "Regex", "val 1",
+ "Exclude", "val2 3",
+ "Logical_Op", "OR",
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ ret = flb_start(ctx);
+ TEST_CHECK(ret != 0);
+
+ flb_stop(ctx);
+ flb_destroy(ctx);
+}
+
+void flb_test_AND_regex(void)
+{
+ int i;
+ int ret;
+ int bytes;
+ char p[512];
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+ int filter_ffd;
+ int got;
+ int n_loop = 256;
+ int not_used = 0;
+ struct flb_lib_out_cb cb_data;
+
+ /* Prepare output callback with expected result */
+ cb_data.cb = cb_count_msgpack;
+ cb_data.data = &not_used;
+
+ ctx = flb_create();
+
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ TEST_CHECK(in_ffd >= 0);
+ flb_input_set(ctx, in_ffd, "tag", "test", NULL);
+
+ out_ffd = flb_output(ctx, (char *) "lib", &cb_data);
+ TEST_CHECK(out_ffd >= 0);
+ flb_output_set(ctx, out_ffd, "match", "test", NULL);
+
+ filter_ffd = flb_filter(ctx, (char *) "grep", NULL);
+ TEST_CHECK(filter_ffd >= 0);
+ ret = flb_filter_set(ctx, filter_ffd, "match", "*", NULL);
+ TEST_CHECK(ret == 0);
+ ret = flb_filter_set(ctx, filter_ffd,
+ "Regex", "log deprecated",
+ "Regex", "log option",
+ "Logical_Op", "AND",
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ clear_output_num();
+
+ ret = flb_start(ctx);
+ if(!TEST_CHECK(ret == 0)) {
+ TEST_MSG("flb_start failed");
+ exit(EXIT_FAILURE);
+ }
+
+ /* Ingest 2 records per loop. One of them should be excluded. */
+ for (i = 0; i < n_loop; i++) {
+ memset(p, '\0', sizeof(p));
+ /* Below record will be included */
+ snprintf(p, sizeof(p), "[%d, {\"val\": \"%d\",\"log\": \"Using deprecated option\"}]", i, (i * i));
+ bytes = flb_lib_push(ctx, in_ffd, p, strlen(p));
+ TEST_CHECK(bytes == strlen(p));
+
+ /* Below record will be excluded */
+ memset(p, '\0', sizeof(p));
+ snprintf(p, sizeof(p), "[%d, {\"val\": \"%d\",\"log\": \"Using option\"}]", i, (i * i));
+ bytes = flb_lib_push(ctx, in_ffd, p, strlen(p));
+ TEST_CHECK(bytes == strlen(p));
+ }
+
+ flb_time_msleep(1500); /* waiting flush */
+
+ got = get_output_num();
+ if (!TEST_CHECK(got == n_loop)) {
+ TEST_MSG("expect: %d got: %d", n_loop, got);
+ }
+
+ flb_stop(ctx);
+ flb_destroy(ctx);
+}
+
+void flb_test_OR_regex(void)
+{
+ int i;
+ int ret;
+ int bytes;
+ char p[512];
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+ int filter_ffd;
+ int got;
+ int n_loop = 256;
+ int not_used = 0;
+ struct flb_lib_out_cb cb_data;
+
+ /* Prepare output callback with expected result */
+ cb_data.cb = cb_count_msgpack;
+ cb_data.data = &not_used;
+
+ ctx = flb_create();
+
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ TEST_CHECK(in_ffd >= 0);
+ flb_input_set(ctx, in_ffd, "tag", "test", NULL);
+
+ out_ffd = flb_output(ctx, (char *) "lib", &cb_data);
+ TEST_CHECK(out_ffd >= 0);
+ flb_output_set(ctx, out_ffd, "match", "test", NULL);
+
+ filter_ffd = flb_filter(ctx, (char *) "grep", NULL);
+ TEST_CHECK(filter_ffd >= 0);
+ ret = flb_filter_set(ctx, filter_ffd, "match", "*", NULL);
+ TEST_CHECK(ret == 0);
+ ret = flb_filter_set(ctx, filter_ffd,
+ "Regex", "log deprecated",
+ "Regex", "log option",
+ "Logical_Op", "OR",
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ clear_output_num();
+
+ ret = flb_start(ctx);
+ if(!TEST_CHECK(ret == 0)) {
+ TEST_MSG("flb_start failed");
+ exit(EXIT_FAILURE);
+ }
+
+ /* Ingest 2 records per loop. One of them should be excluded. */
+ for (i = 0; i < n_loop; i++) {
+ memset(p, '\0', sizeof(p));
+ /* Below record will be included */
+ snprintf(p, sizeof(p), "[%d, {\"val\": \"%d\",\"log\": \"Using deprecated option\"}]", i, (i * i));
+ bytes = flb_lib_push(ctx, in_ffd, p, strlen(p));
+ TEST_CHECK(bytes == strlen(p));
+
+ /* Below record will be excluded */
+ memset(p, '\0', sizeof(p));
+ snprintf(p, sizeof(p), "[%d, {\"val\": \"%d\",\"log\": \"Using option\"}]", i, (i * i));
+ bytes = flb_lib_push(ctx, in_ffd, p, strlen(p));
+ TEST_CHECK(bytes == strlen(p));
+ }
+
+ flb_time_msleep(1500); /* waiting flush */
+
+ got = get_output_num();
+ if (!TEST_CHECK(got == n_loop * 2)) {
+ TEST_MSG("expect: %d got: %d", n_loop * 2, got);
+ }
+
+ flb_stop(ctx);
+ flb_destroy(ctx);
+}
+
+void flb_test_AND_exclude(void)
+{
+ int i;
+ int ret;
+ int bytes;
+ char p[512];
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+ int filter_ffd;
+ int got;
+ int n_loop = 256;
+ int not_used = 0;
+ struct flb_lib_out_cb cb_data;
+
+ /* Prepare output callback with expected result */
+ cb_data.cb = cb_count_msgpack;
+ cb_data.data = &not_used;
+
+ ctx = flb_create();
+
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ TEST_CHECK(in_ffd >= 0);
+ flb_input_set(ctx, in_ffd, "tag", "test", NULL);
+
+ out_ffd = flb_output(ctx, (char *) "lib", &cb_data);
+ TEST_CHECK(out_ffd >= 0);
+ flb_output_set(ctx, out_ffd, "match", "test", NULL);
+
+ filter_ffd = flb_filter(ctx, (char *) "grep", NULL);
+ TEST_CHECK(filter_ffd >= 0);
+ ret = flb_filter_set(ctx, filter_ffd, "match", "*", NULL);
+ TEST_CHECK(ret == 0);
+ ret = flb_filter_set(ctx, filter_ffd,
+ "Exclude", "log deprecated",
+ "Exclude", "log option",
+ "Logical_Op", "AND",
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ clear_output_num();
+
+ ret = flb_start(ctx);
+ if(!TEST_CHECK(ret == 0)) {
+ TEST_MSG("flb_start failed");
+ exit(EXIT_FAILURE);
+ }
+
+ /* Ingest 2 records per loop. One of them should be excluded. */
+ for (i = 0; i < n_loop; i++) {
+ memset(p, '\0', sizeof(p));
+ /* Below record will be included */
+ snprintf(p, sizeof(p), "[%d, {\"val\": \"%d\",\"log\": \"Using deprecated option\"}]", i, (i * i));
+ bytes = flb_lib_push(ctx, in_ffd, p, strlen(p));
+ TEST_CHECK(bytes == strlen(p));
+
+ /* Below record will be excluded */
+ memset(p, '\0', sizeof(p));
+ snprintf(p, sizeof(p), "[%d, {\"val\": \"%d\",\"log\": \"Using option\"}]", i, (i * i));
+ bytes = flb_lib_push(ctx, in_ffd, p, strlen(p));
+ TEST_CHECK(bytes == strlen(p));
+ }
+
+ flb_time_msleep(1500); /* waiting flush */
+
+ got = get_output_num();
+ if (!TEST_CHECK(got == n_loop)) {
+ TEST_MSG("expect: %d got: %d", n_loop, got);
+ }
+
+ flb_stop(ctx);
+ flb_destroy(ctx);
+}
+
+void flb_test_OR_exclude(void)
+{
+ int i;
+ int ret;
+ int bytes;
+ char p[512];
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+ int filter_ffd;
+ int got;
+ int n_loop = 256;
+ int not_used = 0;
+ struct flb_lib_out_cb cb_data;
+
+ /* Prepare output callback with expected result */
+ cb_data.cb = cb_count_msgpack;
+ cb_data.data = &not_used;
+
+ ctx = flb_create();
+
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ TEST_CHECK(in_ffd >= 0);
+ flb_input_set(ctx, in_ffd, "tag", "test", NULL);
+
+ out_ffd = flb_output(ctx, (char *) "lib", &cb_data);
+ TEST_CHECK(out_ffd >= 0);
+ flb_output_set(ctx, out_ffd, "match", "test", NULL);
+
+ filter_ffd = flb_filter(ctx, (char *) "grep", NULL);
+ TEST_CHECK(filter_ffd >= 0);
+ ret = flb_filter_set(ctx, filter_ffd, "match", "*", NULL);
+ TEST_CHECK(ret == 0);
+ ret = flb_filter_set(ctx, filter_ffd,
+ "Exclude", "log deprecated",
+ "Exclude", "log other",
+ "Logical_Op", "OR",
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ clear_output_num();
+
+ ret = flb_start(ctx);
+ if(!TEST_CHECK(ret == 0)) {
+ TEST_MSG("flb_start failed");
+ exit(EXIT_FAILURE);
+ }
+
+ /* Ingest 2 records per loop. One of them should be excluded. */
+ for (i = 0; i < n_loop; i++) {
+ memset(p, '\0', sizeof(p));
+ /* Below record will be included */
+ snprintf(p, sizeof(p), "[%d, {\"val\": \"%d\",\"log\": \"Using deprecated option\"}]", i, (i * i));
+ bytes = flb_lib_push(ctx, in_ffd, p, strlen(p));
+ TEST_CHECK(bytes == strlen(p));
+
+ /* Below record will be excluded */
+ memset(p, '\0', sizeof(p));
+ snprintf(p, sizeof(p), "[%d, {\"val\": \"%d\",\"log\": \"Using option\"}]", i, (i * i));
+ bytes = flb_lib_push(ctx, in_ffd, p, strlen(p));
+ TEST_CHECK(bytes == strlen(p));
+ }
+
+ flb_time_msleep(1500); /* waiting flush */
+
+ got = get_output_num();
+ if (!TEST_CHECK(got == n_loop)) {
+ TEST_MSG("expect: %d got: %d", n_loop, got);
+ }
+
+ flb_stop(ctx);
+ flb_destroy(ctx);
+}
+
+/* Test list */
+TEST_LIST = {
+ {"regex", flb_test_filter_grep_regex },
+ {"exclude", flb_test_filter_grep_exclude },
+ {"invalid", flb_test_filter_grep_invalid },
+ {"multi_regex", flb_test_filter_grep_multi_regex },
+ {"multi_exclude", flb_test_filter_grep_multi_exclude },
+ {"unknown_property", flb_test_filter_grep_unknown_property },
+ {"AND_regex", flb_test_AND_regex},
+ {"OR_regex", flb_test_OR_regex},
+ {"AND_exclude", flb_test_AND_exclude},
+ {"OR_exclude", flb_test_OR_exclude},
+ {"error_OR_regex_exclude", flb_test_error_OR_regex_exclude},
+ {"error_AND_regex_exclude", flb_test_error_AND_regex_exclude},
+ {"error_OR_regex_exclude", flb_test_error_OR_regex_exclude},
+ {"issue_5209", flb_test_issue_5209 },
+ {NULL, NULL}
+};
diff --git a/fluent-bit/tests/runtime/filter_kubernetes.c b/fluent-bit/tests/runtime/filter_kubernetes.c
new file mode 100644
index 00000000..46c9a966
--- /dev/null
+++ b/fluent-bit/tests/runtime/filter_kubernetes.c
@@ -0,0 +1,1086 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+#define _GNU_SOURCE /* for accept4 */
+#include <fluent-bit.h>
+#include <fluent-bit/flb_time.h>
+#include <fluent-bit/flb_info.h>
+#include "flb_tests_runtime.h"
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#ifdef _WIN32
+ #define TIME_EPSILON_MS 30
+#else
+ #define TIME_EPSILON_MS 10
+#endif
+
+struct kube_test {
+ flb_ctx_t *flb;
+};
+
+struct kube_test_result {
+ const char *target;
+ const char *suffix;
+ int type;
+ int nMatched;
+};
+
+void wait_with_timeout(uint32_t timeout_ms, struct kube_test_result *result, int nExpected)
+{
+ struct flb_time start_time;
+ struct flb_time end_time;
+ struct flb_time diff_time;
+ uint64_t elapsed_time_flb = 0;
+
+ flb_time_get(&start_time);
+
+ while (true) {
+ if (result->nMatched == nExpected) {
+ break;
+ }
+
+ flb_time_msleep(100);
+ flb_time_get(&end_time);
+ flb_time_diff(&end_time, &start_time, &diff_time);
+ elapsed_time_flb = flb_time_to_nanosec(&diff_time) / 1000000;
+
+ if (elapsed_time_flb > timeout_ms - TIME_EPSILON_MS) {
+ flb_warn("[timeout] elapsed_time: %ld", elapsed_time_flb);
+ // Reached timeout.
+ break;
+ }
+ }
+}
+
+/* Test target mode */
+#define KUBE_TAIL 0
+#define KUBE_SYSTEMD 1
+
+#ifdef FLB_HAVE_SYSTEMD
+int flb_test_systemd_send(void);
+char kube_test_id[64];
+#endif
+
+/* Constants */
+#define KUBE_IP "127.0.0.1"
+#define KUBE_PORT "8002"
+#define KUBE_URL "http://" KUBE_IP ":" KUBE_PORT
+#define DPATH FLB_TESTS_DATA_PATH "/data/kubernetes"
+
+static int file_to_buf(const char *path, char **out_buf, size_t *out_size)
+{
+ int ret;
+ long bytes;
+ char *buf;
+ FILE *fp;
+ struct stat st;
+
+ ret = stat(path, &st);
+ if (ret == -1) {
+ return -1;
+ }
+
+ fp = fopen(path, "r");
+ if (!fp) {
+ return -1;
+ }
+
+ buf = flb_malloc(st.st_size);
+ if (!buf) {
+ flb_errno();
+ fclose(fp);
+ return -1;
+ }
+
+ bytes = fread(buf, st.st_size, 1, fp);
+ if (bytes != 1) {
+ flb_errno();
+ flb_free(buf);
+ fclose(fp);
+ return -1;
+ }
+
+ fclose(fp);
+ *out_buf = buf;
+ *out_size = st.st_size;
+
+ return 0;
+}
+
+/* Given a target, lookup the .out file and return it content in a new buffer */
+static char *get_out_file_content(const char *target, const char *suffix)
+{
+ int ret;
+ char file[PATH_MAX];
+ char *p;
+ char *out_buf;
+ size_t out_size;
+
+ if (suffix) {
+ snprintf(file, sizeof(file) - 1, DPATH "/out/%s_%s.out", target, suffix);
+ }
+ else {
+ snprintf(file, sizeof(file) - 1, DPATH "/out/%s.out", target);
+ }
+
+ ret = file_to_buf(file, &out_buf, &out_size);
+ TEST_CHECK_(ret == 0, "getting output file content: %s", file);
+ if (ret != 0) {
+ return NULL;
+ }
+
+ /* Sanitize content, get rid of ending \n */
+ p = out_buf + (out_size - 1);
+ while (*p == '\n' || *p == '\r') p--;
+ *++p = '\0';
+
+ return out_buf;
+}
+
+static int cb_check_result(void *record, size_t size, void *data)
+{
+ struct kube_test_result *result;
+ char *out = NULL;
+
+ result = (struct kube_test_result *) data;
+
+ if (result->type == KUBE_SYSTEMD) {
+ char *skip_record, *skip_out;
+ int check;
+
+ out = get_out_file_content(result->target, result->suffix);
+ if (!out) {
+ goto exit;
+ }
+ /* Skip the other records since some are created by systemd,
+ only check the kubernetes annotations
+ */
+ skip_out = strstr(out, "\"kubernetes\":");
+ skip_record = strstr(record, "\"kubernetes\":");
+ if (skip_out && skip_record) {
+ check = strcmp(skip_record, skip_out);
+ TEST_CHECK(check == 0);
+ if (check != 0) {
+ printf("skip_record: %s\nskip_out: %s\n",
+ skip_record, skip_out);
+ }
+ result->nMatched++;
+ }
+ } else {
+ char *check;
+ char streamfilter[64] = {'\0'};
+
+ if (result->suffix && *result->suffix) {
+ sprintf(streamfilter, "\"stream\":\"%s\"", result->suffix);
+ }
+ if (!*streamfilter ||
+ strstr(record, streamfilter)) {
+ out = get_out_file_content(result->target, result->suffix);
+ if (!out) {
+ goto exit;
+ }
+ /*
+ * Our validation is: check that the content of out file is found
+ * in the output record.
+ */
+ check = strstr(record, out);
+ TEST_CHECK_(check != NULL,
+ "comparing expected record with actual record");
+ if (check == NULL) {
+ if (result->suffix) {
+ printf("Target: %s, suffix: %s\n",
+ result->target, result->suffix);
+ }
+ else
+ {
+ printf("Target: %s\n",
+ result->target);
+ }
+ printf("Expected record:\n%s\n"
+ "Actual record:\n%s\n",
+ out, (char *)record);
+ }
+ result->nMatched++;
+ }
+ }
+
+exit:
+ if (size > 0) {
+ flb_free(record);
+ }
+ if (out) {
+ flb_free(out);
+ }
+ return 0;
+}
+
+static void kube_test(const char *target, int type, const char *suffix, int nExpected, ...)
+{
+ int ret;
+ int in_ffd;
+ int filter_ffd;
+ int out_ffd;
+ char *key;
+ char *value;
+ char path[PATH_MAX];
+ va_list va;
+ struct kube_test ctx;
+ struct flb_lib_out_cb cb_data;
+ struct kube_test_result result = {0};
+
+ result.nMatched = 0;
+ result.target = target;
+ result.suffix = suffix;
+ result.type = type;
+
+ ctx.flb = flb_create();
+ TEST_CHECK_(ctx.flb != NULL, "initialising service");
+ if (!ctx.flb) {
+ goto exit;
+ }
+
+ ret = flb_service_set(ctx.flb,
+ "Flush", "1",
+ "Grace", "1",
+ "Log_Level", "error",
+ "Parsers_File", DPATH "/parsers.conf",
+ NULL);
+ TEST_CHECK_(ret == 0, "setting service options");
+
+ if (type == KUBE_TAIL) {
+ /* Compose path based on target */
+ snprintf(path, sizeof(path) - 1, DPATH "/log/%s.log", target);
+ TEST_CHECK_(access(path, R_OK) == 0, "accessing log file: %s", path);
+ in_ffd = flb_input(ctx.flb, "tail", NULL);
+ TEST_CHECK_(in_ffd >= 0, "initialising input");
+ ret = flb_input_set(ctx.flb, in_ffd,
+ "Tag", "kube.<namespace>.<pod>.<container>",
+ "Tag_Regex", "^" DPATH "/log/(?:[^/]+/)?(?<namespace>.+)_(?<pod>.+)_(?<container>.+)\\.log$",
+ "Path", path,
+ "Parser", "docker",
+ "Docker_Mode", "On",
+ "read_from_head", "on",
+ NULL);
+ TEST_CHECK_(ret == 0, "setting input options");
+ }
+#ifdef FLB_HAVE_SYSTEMD
+ else if (type == KUBE_SYSTEMD) {
+ sprintf(kube_test_id, "KUBE_TEST=%u%lu", getpid(), random());
+ in_ffd = flb_input(ctx.flb, "systemd", NULL);
+ TEST_CHECK_(in_ffd >= 0, "initialising input");
+ ret = flb_input_set(ctx.flb, in_ffd,
+ "Tag", "kube.*",
+ "Systemd_Filter", kube_test_id,
+ NULL);
+ TEST_CHECK_(ret == 0, "setting input options");
+ }
+#endif
+
+ filter_ffd = flb_filter(ctx.flb, "kubernetes", NULL);
+ TEST_CHECK_(filter_ffd >= 0, "initialising filter");
+ ret = flb_filter_set(ctx.flb, filter_ffd,
+ "Match", "kube.*",
+ "Kube_Url", KUBE_URL,
+ "Kube_Meta_Preload_Cache_Dir", DPATH "/meta",
+ NULL);
+ TEST_CHECK_(ret == 0, "setting filter options");
+
+ /* Iterate number of arguments for filter_kubernetes additional options */
+ va_start(va, nExpected);
+ while ((key = va_arg(va, char *))) {
+ value = va_arg(va, char *);
+ if (!value) {
+ /* Wrong parameter */
+ break;
+ }
+ ret = flb_filter_set(ctx.flb, filter_ffd, key, value, NULL);
+ TEST_CHECK_(ret == 0, "setting filter additional options");
+ }
+ va_end(va);
+
+ if (type == KUBE_TAIL) {
+ ret = flb_filter_set(ctx.flb, filter_ffd,
+ "Regex_Parser", "kubernetes-tag",
+ "Kube_Tag_Prefix", "kube.",
+ NULL);
+ TEST_CHECK_(ret == 0, "setting filter specific options");
+ }
+#ifdef FLB_HAVE_SYSTEMD
+ else if (type == KUBE_SYSTEMD) {
+ ret = flb_filter_set(ctx.flb, filter_ffd,
+ "Use_Journal", "On",
+ NULL);
+ TEST_CHECK_(ret == 0, "setting filter specific options");
+ }
+#endif
+
+ /* Prepare output callback context*/
+ cb_data.cb = cb_check_result;
+ cb_data.data = &result;
+
+ /* Output */
+ out_ffd = flb_output(ctx.flb, "lib", (void *) &cb_data);
+ TEST_CHECK_(out_ffd >= 0, "initialising output");
+ flb_output_set(ctx.flb, out_ffd,
+ "Match", "kube.*",
+ "format", "json",
+ NULL);
+ TEST_CHECK_(ret == 0, "setting output options");
+
+#ifdef FLB_HAVE_SYSTEMD
+ /*
+ * If the source of data is Systemd, just let the output lib plugin
+ * to process one record only, otherwise when the test case stop after
+ * the first callback it destroy the contexts, but out_lib still have
+ * pending data to flush. This option solves the problem.
+ */
+ if (type == KUBE_SYSTEMD) {
+ flb_output_set(ctx.flb, out_ffd,
+ "Max_Records", "1",
+ NULL);
+ TEST_CHECK_(ret == 0, "setting output specific options");
+ }
+#endif
+
+ /* Start the engine */
+ ret = flb_start(ctx.flb);
+ TEST_CHECK_(ret == 0, "starting engine");
+ if (ret == -1) {
+ goto exit;
+ }
+#ifdef FLB_HAVE_SYSTEMD
+ if (type == KUBE_SYSTEMD) {
+ TEST_CHECK_(flb_test_systemd_send() >= 0,
+ "sending sample message to journal");
+ }
+#endif
+
+ /* Poll for up to 2 seconds or until we got a match */
+ for (ret = 0; ret < 2000 && result.nMatched == 0; ret++) {
+ usleep(1000);
+ }
+
+ /* Wait until matching nExpected results */
+ wait_with_timeout(5000, &result, nExpected);
+
+ TEST_CHECK(result.nMatched == nExpected);
+ TEST_MSG("result.nMatched: %i\nnExpected: %i", result.nMatched, nExpected);
+
+ ret = flb_stop(ctx.flb);
+ TEST_CHECK_(ret == 0, "stopping engine");
+
+exit:
+ if (ctx.flb) {
+ flb_destroy(ctx.flb);
+ }
+}
+
+
+#define flb_test_core(target, suffix, nExpected) \
+ kube_test("core/" target, KUBE_TAIL, suffix, nExpected, NULL);
+
+static void flb_test_core_base()
+{
+ flb_test_core("core_base_fluent-bit", NULL, 1);
+}
+
+static void flb_test_core_no_meta()
+{
+ flb_test_core("core_no-meta_text", NULL, 1);
+}
+
+static void flb_test_core_unescaping_text()
+{
+ flb_test_core("core_unescaping_text", NULL, 1);
+}
+
+static void flb_test_core_unescaping_json()
+{
+ flb_test_core("core_unescaping_json", NULL, 1);
+}
+
+
+#define flb_test_options_use_kubelet_enabled(target, suffix, nExpected) \
+ kube_test("options/" target, KUBE_TAIL, suffix, nExpected, \
+ "use_kubelet", "true", \
+ "kubelet_port", "8002", \
+ NULL); \
+
+#define flb_test_options_use_kubelet_disabled(target, suffix, nExpected) \
+ kube_test("options/" target, KUBE_TAIL, suffix, nExpected, \
+ "use_kubelet", "false", \
+ "kubelet_port", "8002", \
+ NULL); \
+
+
+static void flb_test_options_use_kubelet_enabled_json()
+{
+ flb_test_options_use_kubelet_enabled("options_use-kubelet-enabled_fluent-bit", NULL, 1);
+}
+
+static void flb_test_options_use_kubelet_disabled_json()
+{
+ flb_test_options_use_kubelet_disabled("options_use-kubelet-disabled_fluent-bit", NULL, 1);
+}
+
+#define flb_test_options_merge_log_enabled(target, suffix, nExpected) \
+ kube_test("options/" target, KUBE_TAIL, suffix, nExpected, \
+ "Merge_Log", "On", \
+ NULL); \
+
+#define flb_test_options_merge_log_disabled(target, suffix, nExpected) \
+ kube_test("options/" target, KUBE_TAIL, suffix, nExpected, \
+ NULL); \
+
+static void flb_test_options_merge_log_enabled_text()
+{
+ flb_test_options_merge_log_enabled("options_merge-log-enabled_text", NULL, 1);
+}
+
+static void flb_test_options_merge_log_enabled_json()
+{
+ flb_test_options_merge_log_enabled("options_merge-log-enabled_json", NULL, 1);
+}
+
+static void flb_test_options_merge_log_enabled_invalid_json()
+{
+ flb_test_options_merge_log_enabled("options_merge-log-enabled_invalid-json", NULL, 1);
+}
+
+static void flb_test_options_merge_log_disabled_json()
+{
+ flb_test_options_merge_log_disabled("options_merge-log-disabled_json", NULL, 1);
+}
+
+#define flb_test_options_merge_log_trim_enabled(target, suffix, nExpected) \
+ kube_test("options/" target, KUBE_TAIL, suffix, nExpected, \
+ "Merge_Log", "On", \
+ NULL); \
+
+#define flb_test_options_merge_log_trim_disabled(target, suffix, nExpected) \
+ kube_test("options/" target, KUBE_TAIL, suffix, nExpected, \
+ "Merge_Log", "On", \
+ "Merge_Log_Trim", "Off", \
+ NULL); \
+
+static void flb_test_options_merge_log_trim_enabled_json()
+{
+ flb_test_options_merge_log_trim_enabled("options_merge-log-trim-enabled_json", NULL, 1);
+}
+
+static void flb_test_options_merge_log_trim_disabled_json()
+{
+ flb_test_options_merge_log_trim_disabled("options_merge-log-trim-disabled_json", NULL, 1);
+}
+
+#define flb_test_options_merge_log_key(target, suffix, nExpected) \
+ kube_test("options/" target, KUBE_TAIL, suffix, nExpected, \
+ "Merge_Log", "On", \
+ "Merge_Log_Key", "merge-log-key", \
+ NULL); \
+
+static void flb_test_options_merge_log_key_json()
+{
+ flb_test_options_merge_log_key("options_merge-log-key_json", NULL, 1);
+}
+
+#define flb_test_options_keep_log_enabled(target, suffix, nExpected) \
+ kube_test("options/" target, KUBE_TAIL, suffix, nExpected, \
+ "Merge_Log", "On", \
+ NULL); \
+
+#define flb_test_options_keep_log_disabled(target, suffix, nExpected) \
+ kube_test("options/" target, KUBE_TAIL, suffix, nExpected, \
+ "Merge_Log", "On", \
+ "Keep_Log", "Off", \
+ NULL); \
+
+static void flb_test_options_keep_log_enabled_json()
+{
+ flb_test_options_keep_log_enabled("options_keep-log-enabled_json", NULL, 1);
+}
+
+static void flb_test_options_keep_log_disabled_json()
+{
+ flb_test_options_keep_log_disabled("options_keep-log-disabled_json", NULL, 1);
+}
+
+#define flb_test_options_k8s_logging_parser_disabled(target, suffix, nExpected) \
+ kube_test("options/" target, KUBE_TAIL, suffix, nExpected, \
+ "Merge_Log", "On", \
+ NULL); \
+
+static void flb_test_options_k8s_logging_parser_disabled_text_stdout()
+{
+ flb_test_options_k8s_logging_parser_disabled("options_k8s-logging-parser-disabled_text", "stdout", 1);
+}
+
+static void flb_test_options_k8s_logging_parser_disabled_text_stderr()
+{
+ flb_test_options_k8s_logging_parser_disabled("options_k8s-logging-parser-disabled_text", "stderr", 1);
+}
+
+#define flb_test_options_k8s_logging_exclude_disabled(target, suffix, nExpected) \
+ kube_test("options/" target, KUBE_TAIL, suffix, nExpected, \
+ "Merge_Log", "On", \
+ NULL); \
+
+static void flb_test_options_k8s_logging_exclude_disabled_text_stdout()
+{
+ flb_test_options_k8s_logging_exclude_disabled("options_k8s-logging-exclude-disabled_text", "stdout", 1);
+}
+
+static void flb_test_options_k8s_logging_exclude_disabled_text_stderr()
+{
+ flb_test_options_k8s_logging_exclude_disabled("options_k8s-logging-exclude-disabled_text", "stderr", 1);
+}
+
+#define flb_test_annotations(target, suffix, nExpected) \
+ kube_test("annotations/" target, KUBE_TAIL, suffix, nExpected, \
+ "K8s-Logging.Parser", "On", \
+ "K8s-Logging.Exclude", "On", \
+ NULL); \
+
+static void flb_test_annotations_invalid_text()
+{
+ flb_test_annotations("annotations_invalid_text", NULL, 1);
+}
+
+#define flb_test_annotations_parser(target, suffix, nExpected) \
+ kube_test("annotations-parser/" target, KUBE_TAIL, suffix, nExpected, \
+ "K8s-Logging.Parser", "On", \
+ "Merge_Log", "On", \
+ "Keep_Log", "Off", \
+ NULL); \
+
+static void flb_test_annotations_parser_regex_with_time_text()
+{
+ flb_test_annotations_parser("annotations-parser_regex-with-time_text", NULL, 1);
+}
+
+static void flb_test_annotations_parser_regex_with_time_invalid_text_1()
+{
+ flb_test_annotations_parser("annotations-parser_regex-with-time_invalid-text-1", NULL, 1);
+}
+
+static void flb_test_annotations_parser_json_with_time_json()
+{
+ flb_test_annotations_parser("annotations-parser_json-with-time_json", NULL, 1);
+}
+
+static void flb_test_annotations_parser_json_with_time_invalid_json_1()
+{
+ flb_test_annotations_parser("annotations-parser_json-with-time_invalid-json-1", NULL, 1);
+}
+
+static void flb_test_annotations_parser_invalid_text_stdout()
+{
+ flb_test_annotations_parser("annotations-parser_invalid_text", "stdout", 1);
+}
+
+static void flb_test_annotations_parser_invalid_text_stderr()
+{
+ flb_test_annotations_parser("annotations-parser_invalid_text", "stderr", 1);
+}
+
+static void flb_test_annotations_parser_stdout_text_stdout()
+{
+ flb_test_annotations_parser("annotations-parser_stdout_text", "stdout", 1);
+}
+
+static void flb_test_annotations_parser_stdout_text_stderr()
+{
+ flb_test_annotations_parser("annotations-parser_stdout_text", "stderr", 1);
+}
+
+static void flb_test_annotations_parser_stderr_text_stdout()
+{
+ flb_test_annotations_parser("annotations-parser_stderr_text", "stdout", 1);
+}
+
+static void flb_test_annotations_parser_stderr_text_stderr()
+{
+ flb_test_annotations_parser("annotations-parser_stderr_text", "stderr", 1);
+}
+
+static void flb_test_annotations_parser_multiple_1_container_1_stdout()
+{
+ flb_test_annotations_parser("annotations-parser_multiple-1_container-1", "stdout", 1);
+}
+
+static void flb_test_annotations_parser_multiple_1_container_1_stderr()
+{
+ flb_test_annotations_parser("annotations-parser_multiple-1_container-1", "stderr", 1);
+}
+
+static void flb_test_annotations_parser_multiple_1_container_2_stdout()
+{
+ flb_test_annotations_parser("annotations-parser_multiple-1_container-2", "stdout", 1);
+}
+
+static void flb_test_annotations_parser_multiple_1_container_2_stderr()
+{
+ flb_test_annotations_parser("annotations-parser_multiple-1_container-2", "stderr", 1);
+}
+
+static void flb_test_annotations_parser_multiple_1_container_3_stdout()
+{
+ flb_test_annotations_parser("annotations-parser_multiple-1_container-3", "stdout", 1);
+}
+
+static void flb_test_annotations_parser_multiple_1_container_3_stderr()
+{
+ flb_test_annotations_parser("annotations-parser_multiple-1_container-3", "stderr", 1);
+}
+
+static void flb_test_annotations_parser_multiple_1_container_4_stdout()
+{
+ flb_test_annotations_parser("annotations-parser_multiple-1_container-4", "stdout", 1);
+}
+
+static void flb_test_annotations_parser_multiple_1_container_4_stderr()
+{
+ flb_test_annotations_parser("annotations-parser_multiple-1_container-4", "stderr", 1);
+}
+
+static void flb_test_annotations_parser_multiple_1_container_5_stdout()
+{
+ flb_test_annotations_parser("annotations-parser_multiple-1_container-5", "stdout", 1);
+}
+
+static void flb_test_annotations_parser_multiple_1_container_5_stderr()
+{
+ flb_test_annotations_parser("annotations-parser_multiple-1_container-5", "stderr", 1);
+}
+
+static void flb_test_annotations_parser_multiple_2_container_1_stdout()
+{
+ flb_test_annotations_parser("annotations-parser_multiple-2_container-1", "stdout", 1);
+}
+
+static void flb_test_annotations_parser_multiple_2_container_1_stderr()
+{
+ flb_test_annotations_parser("annotations-parser_multiple-2_container-1", "stderr", 1);
+}
+
+static void flb_test_annotations_parser_multiple_2_container_2_stdout()
+{
+ flb_test_annotations_parser("annotations-parser_multiple-2_container-2", "stdout", 1);
+}
+
+static void flb_test_annotations_parser_multiple_2_container_2_stderr()
+{
+ flb_test_annotations_parser("annotations-parser_multiple-2_container-2", "stderr", 1);
+}
+
+static void flb_test_annotations_parser_multiple_2_container_3_stdout()
+{
+ flb_test_annotations_parser("annotations-parser_multiple-2_container-3", "stdout", 1);
+}
+
+static void flb_test_annotations_parser_multiple_2_container_3_stderr()
+{
+ flb_test_annotations_parser("annotations-parser_multiple-2_container-3", "stderr", 1);
+}
+
+static void flb_test_annotations_parser_multiple_2_container_4_stdout()
+{
+ flb_test_annotations_parser("annotations-parser_multiple-2_container-4", "stdout", 1);
+}
+
+static void flb_test_annotations_parser_multiple_2_container_4_stderr()
+{
+ flb_test_annotations_parser("annotations-parser_multiple-2_container-4", "stderr", 1);
+}
+
+static void flb_test_annotations_parser_multiple_2_container_5_stdout()
+{
+ flb_test_annotations_parser("annotations-parser_multiple-2_container-5", "stdout", 1);
+}
+
+static void flb_test_annotations_parser_multiple_2_container_5_stderr()
+{
+ flb_test_annotations_parser("annotations-parser_multiple-2_container-5", "stderr", 1);
+}
+
+#define flb_test_annotations_exclude(target, suffix, nExpected) \
+ kube_test("annotations-exclude/" target, KUBE_TAIL, suffix, nExpected, \
+ "K8s-Logging.Exclude", "On", \
+ NULL); \
+
+static void flb_test_annotations_exclude_default_text()
+{
+ flb_test_annotations_exclude("annotations-exclude_default_text", NULL, 0);
+}
+
+static void flb_test_annotations_exclude_invalid_text_stdout()
+{
+ flb_test_annotations_exclude("annotations-exclude_invalid_text", "stdout", 1);
+}
+
+static void flb_test_annotations_exclude_invalid_text_stderr()
+{
+ flb_test_annotations_exclude("annotations-exclude_invalid_text", "stderr", 1);
+}
+
+static void flb_test_annotations_exclude_stdout_text_stdout()
+{
+ flb_test_annotations_exclude("annotations-exclude_stdout_text", "stdout", 0);
+}
+
+static void flb_test_annotations_exclude_stdout_text_stderr()
+{
+ flb_test_annotations_exclude("annotations-exclude_stdout_text", "stderr", 1);
+}
+
+static void flb_test_annotations_exclude_stderr_text_stdout()
+{
+ flb_test_annotations_exclude("annotations-exclude_stderr_text", "stdout", 1);
+}
+
+static void flb_test_annotations_exclude_stderr_text_stderr()
+{
+ flb_test_annotations_exclude("annotations-exclude_stderr_text", "stderr", 0);
+}
+
+static void flb_test_annotations_exclude_multiple_1_container_1_stdout()
+{
+ flb_test_annotations_exclude("annotations-exclude_multiple-1_container-1", "stdout", 0);
+}
+
+static void flb_test_annotations_exclude_multiple_1_container_1_stderr()
+{
+ flb_test_annotations_exclude("annotations-exclude_multiple-1_container-1", "stderr", 0);
+}
+
+static void flb_test_annotations_exclude_multiple_1_container_2_stdout()
+{
+ flb_test_annotations_exclude("annotations-exclude_multiple-1_container-2", "stdout", 0);
+}
+
+static void flb_test_annotations_exclude_multiple_1_container_2_stderr()
+{
+ flb_test_annotations_exclude("annotations-exclude_multiple-1_container-2", "stderr", 1);
+}
+
+static void flb_test_annotations_exclude_multiple_1_container_3_stdout()
+{
+ flb_test_annotations_exclude("annotations-exclude_multiple-1_container-3", "stdout", 1);
+}
+
+static void flb_test_annotations_exclude_multiple_1_container_3_stderr()
+{
+ flb_test_annotations_exclude("annotations-exclude_multiple-1_container-3", "stderr", 0);
+}
+
+static void flb_test_annotations_exclude_multiple_1_container_4_stdout()
+{
+ flb_test_annotations_exclude("annotations-exclude_multiple-1_container-4", "stdout", 1);
+}
+
+static void flb_test_annotations_exclude_multiple_1_container_4_stderr()
+{
+ flb_test_annotations_exclude("annotations-exclude_multiple-1_container-4", "stderr", 1);
+}
+
+static void flb_test_annotations_exclude_multiple_2_container_1_stdout()
+{
+ flb_test_annotations_exclude("annotations-exclude_multiple-2_container-1", "stdout", 0);
+}
+
+static void flb_test_annotations_exclude_multiple_2_container_1_stderr()
+{
+ flb_test_annotations_exclude("annotations-exclude_multiple-2_container-1", "stderr", 0);
+}
+
+static void flb_test_annotations_exclude_multiple_2_container_2_stdout()
+{
+ flb_test_annotations_exclude("annotations-exclude_multiple-2_container-2", "stdout", 0);
+}
+
+static void flb_test_annotations_exclude_multiple_2_container_2_stderr()
+{
+ flb_test_annotations_exclude("annotations-exclude_multiple-2_container-2", "stderr", 1);
+}
+
+static void flb_test_annotations_exclude_multiple_2_container_3_stdout()
+{
+ flb_test_annotations_exclude("annotations-exclude_multiple-2_container-3", "stdout", 1);
+}
+
+static void flb_test_annotations_exclude_multiple_2_container_3_stderr()
+{
+ flb_test_annotations_exclude("annotations-exclude_multiple-2_container-3", "stderr", 0);
+}
+
+static void flb_test_annotations_exclude_multiple_2_container_4_stdout()
+{
+ flb_test_annotations_exclude("annotations-exclude_multiple-2_container-4", "stdout", 1);
+}
+
+static void flb_test_annotations_exclude_multiple_2_container_4_stderr()
+{
+ flb_test_annotations_exclude("annotations-exclude_multiple-2_container-4", "stderr", 1);
+}
+
+static void flb_test_annotations_exclude_multiple_3_container_1_stdout()
+{
+ flb_test_annotations_exclude("annotations-exclude_multiple-3_container-1", "stdout", 0);
+}
+
+static void flb_test_annotations_exclude_multiple_3_container_1_stderr()
+{
+ flb_test_annotations_exclude("annotations-exclude_multiple-3_container-1", "stderr", 0);
+}
+
+static void flb_test_annotations_exclude_multiple_3_container_2_stdout()
+{
+ flb_test_annotations_exclude("annotations-exclude_multiple-3_container-2", "stdout", 0);
+}
+
+static void flb_test_annotations_exclude_multiple_3_container_2_stderr()
+{
+ flb_test_annotations_exclude("annotations-exclude_multiple-3_container-2", "stderr", 1);
+}
+
+static void flb_test_annotations_exclude_multiple_3_container_3_stdout()
+{
+ flb_test_annotations_exclude("annotations-exclude_multiple-3_container-3", "stdout", 1);
+}
+
+static void flb_test_annotations_exclude_multiple_3_container_3_stderr()
+{
+ flb_test_annotations_exclude("annotations-exclude_multiple-3_container-3", "stderr", 0);
+}
+
+static void flb_test_annotations_exclude_multiple_3_container_4_stdout()
+{
+ flb_test_annotations_exclude("annotations-exclude_multiple-3_container-4", "stdout", 1);
+}
+
+static void flb_test_annotations_exclude_multiple_3_container_4_stderr()
+{
+ flb_test_annotations_exclude("annotations-exclude_multiple-3_container-4", "stderr", 1);
+}
+
+static void flb_test_annotations_exclude_multiple_4_container_1_stdout()
+{
+ flb_test_annotations_exclude("annotations-exclude_multiple-4_container-1", "stdout", 0);
+}
+
+static void flb_test_annotations_exclude_multiple_4_container_1_stderr()
+{
+ flb_test_annotations_exclude("annotations-exclude_multiple-4_container-1", "stderr", 0);
+}
+
+static void flb_test_annotations_exclude_multiple_4_container_2_stdout()
+{
+ flb_test_annotations_exclude("annotations-exclude_multiple-4_container-2", "stdout", 0);
+}
+
+static void flb_test_annotations_exclude_multiple_4_container_2_stderr()
+{
+ flb_test_annotations_exclude("annotations-exclude_multiple-4_container-2", "stderr", 1);
+}
+
+static void flb_test_annotations_exclude_multiple_4_container_3_stdout()
+{
+ flb_test_annotations_exclude("annotations-exclude_multiple-4_container-3", "stdout", 1);
+}
+
+static void flb_test_annotations_exclude_multiple_4_container_3_stderr()
+{
+ flb_test_annotations_exclude("annotations-exclude_multiple-4_container-3", "stderr", 0);
+}
+
+static void flb_test_annotations_exclude_multiple_4_container_4_stdout()
+{
+ flb_test_annotations_exclude("annotations-exclude_multiple-4_container-4", "stdout", 1);
+}
+
+static void flb_test_annotations_exclude_multiple_4_container_4_stderr()
+{
+ flb_test_annotations_exclude("annotations-exclude_multiple-4_container-4", "stderr", 1);
+}
+
+#ifdef FLB_HAVE_SYSTEMD
+#define CONTAINER_NAME "CONTAINER_NAME=k8s_kairosdb_kairosdb-914055854-b63vq_default_d6c53deb-05a4-11e8-a8c4-080027435fb7_23"
+#include <systemd/sd-journal.h>
+
+int flb_test_systemd_send()
+{
+ return sd_journal_send(
+ "@timestamp=2018-02-23T08:58:45.0Z",
+ "PRIORITY=6",
+ CONTAINER_NAME,
+ "CONTAINER_TAG=",
+ "CONTAINER_ID=56e257661383",
+ "CONTAINER_ID_FULL=56e257661383836fac4cd90a23ee8a7a02ee1538c8f35657d1a90f3de1065a22",
+ "MESSAGE=08:58:45.839 [qtp151442075-47] DEBUG [HttpParser.java:281] - filled 157/157",
+ kube_test_id,
+ NULL);
+}
+
+static void flb_test_systemd_logs()
+{
+ struct stat statb;
+ /* We want to avoid possibly getting a log from a previous run,
+ so create a unique-id for each 'send' */
+ sprintf(kube_test_id, "KUBE_TEST=%u%lu", getpid(), random());
+
+ if (stat("/run/systemd/journal/socket", &statb) == 0 &&
+ statb.st_mode & S_IFSOCK) {
+
+ int r;
+ sd_journal *journal;
+ r = sd_journal_open(&journal, 0);
+ if (r < 0) {
+ flb_error("Skip test: journal error: %s", strerror(-r));
+ return;
+ }
+
+ r = sd_journal_get_fd(journal);
+ if (r < 0) {
+ flb_error("Skip test: journal fd error: %s", strerror(-r));
+ sd_journal_close(journal);
+ return;
+ }
+ sd_journal_add_match(journal, CONTAINER_NAME, 0);
+ sd_journal_seek_tail(journal);
+
+ /*
+ * Send test message to Journal. If this fails (e.g. journal is
+ * not running then skip the test.
+ */
+ if (flb_test_systemd_send() < 0) {
+
+ flb_error("Skip test: journal send error: %s", strerror(-r));
+ sd_journal_close(journal);
+ return;
+ }
+
+ r = sd_journal_previous(journal);
+ if (r < 0) {
+ flb_error("Skip test: journal previous error: %s", strerror(-r));
+ sd_journal_close(journal);
+ return;
+ }
+
+ r = sd_journal_next(journal);
+ if (r < 0) {
+ flb_error("Skip test: journal next error: %s", strerror(-r));
+ sd_journal_close(journal);
+ return;
+ }
+
+ r = sd_journal_wait(journal, 2000);
+ if (r < 0) {
+ flb_error("Skip test: journal wait error: %s", strerror(-r));
+ sd_journal_close(journal);
+ return;
+ }
+ sd_journal_close(journal);
+
+ kube_test("kairosdb-914055854-b63vq", KUBE_SYSTEMD, NULL, 1,
+ "Merge_Log", "On",
+ NULL);
+ }
+}
+#endif
+
+TEST_LIST = {
+ {"kube_core_base", flb_test_core_base},
+ {"kube_core_no_meta", flb_test_core_no_meta},
+ {"kube_core_unescaping_text", flb_test_core_unescaping_text},
+ {"kube_core_unescaping_json", flb_test_core_unescaping_json},
+ {"kube_options_use-kubelet_enabled_json", flb_test_options_use_kubelet_enabled_json},
+ {"kube_options_use-kubelet_disabled_json", flb_test_options_use_kubelet_disabled_json},
+ {"kube_options_merge_log_enabled_text", flb_test_options_merge_log_enabled_text},
+ {"kube_options_merge_log_enabled_json", flb_test_options_merge_log_enabled_json},
+ {"kube_options_merge_log_enabled_invalid_json", flb_test_options_merge_log_enabled_invalid_json},
+ {"kube_options_merge_log_disabled_json", flb_test_options_merge_log_disabled_json},
+ {"kube_options_merge_log_trim_enabled_json", flb_test_options_merge_log_trim_enabled_json},
+ {"kube_options_merge_log_trim_disabled_json", flb_test_options_merge_log_trim_disabled_json},
+ {"kube_options_merge_log_key_json", flb_test_options_merge_log_key_json},
+ {"kube_options_keep_log_enabled_json", flb_test_options_keep_log_enabled_json},
+ {"kube_options_keep_log_disabled_json", flb_test_options_keep_log_disabled_json},
+ {"kube_options_k8s_logging_parser_disabled_text_stdout", flb_test_options_k8s_logging_parser_disabled_text_stdout},
+ {"kube_options_k8s_logging_parser_disabled_text_stderr", flb_test_options_k8s_logging_parser_disabled_text_stderr},
+ {"kube_options_k8s_logging_exclude_disabled_text_stdout", flb_test_options_k8s_logging_exclude_disabled_text_stdout},
+ {"kube_options_k8s_logging_exclude_disabled_text_stderr", flb_test_options_k8s_logging_exclude_disabled_text_stderr},
+ {"kube_annotations_invalid_text", flb_test_annotations_invalid_text},
+ {"kube_annotations_parser_regex_with_time_text", flb_test_annotations_parser_regex_with_time_text},
+ {"kube_annotations_parser_regex_with_time_invalid_text_1", flb_test_annotations_parser_regex_with_time_invalid_text_1},
+ {"kube_annotations_parser_json_with_time_json", flb_test_annotations_parser_json_with_time_json},
+ {"kube_annotations_parser_json_with_time_invalid_json_1", flb_test_annotations_parser_json_with_time_invalid_json_1},
+ {"kube_annotations_parser_invalid_text_stdout", flb_test_annotations_parser_invalid_text_stdout},
+ {"kube_annotations_parser_invalid_text_stderr", flb_test_annotations_parser_invalid_text_stderr},
+ {"kube_annotations_parser_stdout_text_stdout", flb_test_annotations_parser_stdout_text_stdout},
+ {"kube_annotations_parser_stdout_text_stderr", flb_test_annotations_parser_stdout_text_stderr},
+ {"kube_annotations_parser_stderr_text_stdout", flb_test_annotations_parser_stderr_text_stdout},
+ {"kube_annotations_parser_stderr_text_stderr", flb_test_annotations_parser_stderr_text_stderr},
+ {"kube_annotations_parser_multiple_1_container_1_stdout", flb_test_annotations_parser_multiple_1_container_1_stdout},
+ {"kube_annotations_parser_multiple_1_container_1_stderr", flb_test_annotations_parser_multiple_1_container_1_stderr},
+ {"kube_annotations_parser_multiple_1_container_2_stdout", flb_test_annotations_parser_multiple_1_container_2_stdout},
+ {"kube_annotations_parser_multiple_1_container_2_stderr", flb_test_annotations_parser_multiple_1_container_2_stderr},
+ {"kube_annotations_parser_multiple_1_container_3_stdout", flb_test_annotations_parser_multiple_1_container_3_stdout},
+ {"kube_annotations_parser_multiple_1_container_3_stderr", flb_test_annotations_parser_multiple_1_container_3_stderr},
+ {"kube_annotations_parser_multiple_1_container_4_stdout", flb_test_annotations_parser_multiple_1_container_4_stdout},
+ {"kube_annotations_parser_multiple_1_container_4_stderr", flb_test_annotations_parser_multiple_1_container_4_stderr},
+ {"kube_annotations_parser_multiple_1_container_5_stdout", flb_test_annotations_parser_multiple_1_container_5_stdout},
+ {"kube_annotations_parser_multiple_1_container_5_stderr", flb_test_annotations_parser_multiple_1_container_5_stderr},
+ {"kube_annotations_parser_multiple_2_container_1_stdout", flb_test_annotations_parser_multiple_2_container_1_stdout},
+ {"kube_annotations_parser_multiple_2_container_1_stderr", flb_test_annotations_parser_multiple_2_container_1_stderr},
+ {"kube_annotations_parser_multiple_2_container_2_stdout", flb_test_annotations_parser_multiple_2_container_2_stdout},
+ {"kube_annotations_parser_multiple_2_container_2_stderr", flb_test_annotations_parser_multiple_2_container_2_stderr},
+ {"kube_annotations_parser_multiple_2_container_3_stdout", flb_test_annotations_parser_multiple_2_container_3_stdout},
+ {"kube_annotations_parser_multiple_2_container_3_stderr", flb_test_annotations_parser_multiple_2_container_3_stderr},
+ {"kube_annotations_parser_multiple_2_container_4_stdout", flb_test_annotations_parser_multiple_2_container_4_stdout},
+ {"kube_annotations_parser_multiple_2_container_4_stderr", flb_test_annotations_parser_multiple_2_container_4_stderr},
+ {"kube_annotations_parser_multiple_2_container_5_stdout", flb_test_annotations_parser_multiple_2_container_5_stdout},
+ {"kube_annotations_parser_multiple_2_container_5_stderr", flb_test_annotations_parser_multiple_2_container_5_stderr},
+ {"kube_annotations_exclude_default_text", flb_test_annotations_exclude_default_text},
+ {"kube_annotations_exclude_invalid_text_stdout", flb_test_annotations_exclude_invalid_text_stdout},
+ {"kube_annotations_exclude_invalid_text_stderr", flb_test_annotations_exclude_invalid_text_stderr},
+ {"kube_annotations_exclude_stdout_text_stdout", flb_test_annotations_exclude_stdout_text_stdout},
+ {"kube_annotations_exclude_stdout_text_stderr", flb_test_annotations_exclude_stdout_text_stderr},
+ {"kube_annotations_exclude_stderr_text_stdout", flb_test_annotations_exclude_stderr_text_stdout},
+ {"kube_annotations_exclude_stderr_text_stderr", flb_test_annotations_exclude_stderr_text_stderr},
+ {"kube_annotations_exclude_multiple_1_container_1_stdout", flb_test_annotations_exclude_multiple_1_container_1_stdout},
+ {"kube_annotations_exclude_multiple_1_container_1_stderr", flb_test_annotations_exclude_multiple_1_container_1_stderr},
+ {"kube_annotations_exclude_multiple_1_container_2_stdout", flb_test_annotations_exclude_multiple_1_container_2_stdout},
+ {"kube_annotations_exclude_multiple_1_container_2_stderr", flb_test_annotations_exclude_multiple_1_container_2_stderr},
+ {"kube_annotations_exclude_multiple_1_container_3_stdout", flb_test_annotations_exclude_multiple_1_container_3_stdout},
+ {"kube_annotations_exclude_multiple_1_container_3_stderr", flb_test_annotations_exclude_multiple_1_container_3_stderr},
+ {"kube_annotations_exclude_multiple_1_container_4_stdout", flb_test_annotations_exclude_multiple_1_container_4_stdout},
+ {"kube_annotations_exclude_multiple_1_container_4_stderr", flb_test_annotations_exclude_multiple_1_container_4_stderr},
+ {"kube_annotations_exclude_multiple_2_container_1_stdout", flb_test_annotations_exclude_multiple_2_container_1_stdout},
+ {"kube_annotations_exclude_multiple_2_container_1_stderr", flb_test_annotations_exclude_multiple_2_container_1_stderr},
+ {"kube_annotations_exclude_multiple_2_container_2_stdout", flb_test_annotations_exclude_multiple_2_container_2_stdout},
+ {"kube_annotations_exclude_multiple_2_container_2_stderr", flb_test_annotations_exclude_multiple_2_container_2_stderr},
+ {"kube_annotations_exclude_multiple_2_container_3_stdout", flb_test_annotations_exclude_multiple_2_container_3_stdout},
+ {"kube_annotations_exclude_multiple_2_container_3_stderr", flb_test_annotations_exclude_multiple_2_container_3_stderr},
+ {"kube_annotations_exclude_multiple_2_container_4_stdout", flb_test_annotations_exclude_multiple_2_container_4_stdout},
+ {"kube_annotations_exclude_multiple_2_container_4_stderr", flb_test_annotations_exclude_multiple_2_container_4_stderr},
+ {"kube_annotations_exclude_multiple_3_container_1_stdout", flb_test_annotations_exclude_multiple_3_container_1_stdout},
+ {"kube_annotations_exclude_multiple_3_container_1_stderr", flb_test_annotations_exclude_multiple_3_container_1_stderr},
+ {"kube_annotations_exclude_multiple_3_container_2_stdout", flb_test_annotations_exclude_multiple_3_container_2_stdout},
+ {"kube_annotations_exclude_multiple_3_container_2_stderr", flb_test_annotations_exclude_multiple_3_container_2_stderr},
+ {"kube_annotations_exclude_multiple_3_container_3_stdout", flb_test_annotations_exclude_multiple_3_container_3_stdout},
+ {"kube_annotations_exclude_multiple_3_container_3_stderr", flb_test_annotations_exclude_multiple_3_container_3_stderr},
+ {"kube_annotations_exclude_multiple_3_container_4_stdout", flb_test_annotations_exclude_multiple_3_container_4_stdout},
+ {"kube_annotations_exclude_multiple_3_container_4_stderr", flb_test_annotations_exclude_multiple_3_container_4_stderr},
+ {"kube_annotations_exclude_multiple_4_container_1_stdout", flb_test_annotations_exclude_multiple_4_container_1_stdout},
+ {"kube_annotations_exclude_multiple_4_container_1_stderr", flb_test_annotations_exclude_multiple_4_container_1_stderr},
+ {"kube_annotations_exclude_multiple_4_container_2_stdout", flb_test_annotations_exclude_multiple_4_container_2_stdout},
+ {"kube_annotations_exclude_multiple_4_container_2_stderr", flb_test_annotations_exclude_multiple_4_container_2_stderr},
+ {"kube_annotations_exclude_multiple_4_container_3_stdout", flb_test_annotations_exclude_multiple_4_container_3_stdout},
+ {"kube_annotations_exclude_multiple_4_container_3_stderr", flb_test_annotations_exclude_multiple_4_container_3_stderr},
+ {"kube_annotations_exclude_multiple_4_container_4_stdout", flb_test_annotations_exclude_multiple_4_container_4_stdout},
+ {"kube_annotations_exclude_multiple_4_container_4_stderr", flb_test_annotations_exclude_multiple_4_container_4_stderr},
+#ifdef FLB_HAVE_SYSTEMD
+ {"kube_systemd_logs", flb_test_systemd_logs},
+#endif
+ {NULL, NULL}
+};
diff --git a/fluent-bit/tests/runtime/filter_kubernetes.md b/fluent-bit/tests/runtime/filter_kubernetes.md
new file mode 100644
index 00000000..8f213abc
--- /dev/null
+++ b/fluent-bit/tests/runtime/filter_kubernetes.md
@@ -0,0 +1,69 @@
+# Filter Kubernetes Tests
+
+The tests implemented on [filter_kubernets.c]() file aims to take different Kubernetes Pod log files (formats) and play with different setups. These files resides in the [data/kubernetes]() directory and each .log file have it corresponding .meta which contains the metadata associated with the Pod.
+
+The unit test implements a fake Kubernetes API server to expose .meta files when the filter query for such data. This make things easier to test without to spawn a real cluster.
+
+If you find that a specific test is missing, don't hesitate to open an issue on our Github repository.
+
+## Tests Available
+
+All log files used in these tests have been generated in a single Kubernetes cluster with Minikube.
+
+- [Apache Logs](#apache-logs)
+
+- [Apache Logs Annotated](#apache-logs-annotated)
+
+- [Apache Logs Annotated Invalid](#apache-logs-annotated-invalid)
+
+- [JSON Stringify](#json-stringify)
+
+- [JSON Invalid](#json-invalid)
+
+- No Log
+
+ ​
+
+#### Apache Logs
+
+|||
+|-|-|
+| Description | Simple Apache access log line |
+| Log File | apache-logs_default_apache-logs-ac6095b6c715d823d732dcc9067f75b1299de5cc69a012b08d616a6058bdc0ad.log |
+| Command | $ kubectl run apache-logs --rm --attach --restart=Never --image=edsiper/apache_logs |
+
+#### Apache Logs Annotated
+
+| | |
+| ----------- | ------------------------------------------------------------ |
+| Description | Simple Apache access log line with annotation suggesting a registered Parser |
+| Log File | apache-logs-annotated_default_apache-logs-annotated-5c79b78d458d86fff56127cc8657058c10b837d0f2c147b61afea4c8bc65fad7.log |
+| Command | $ kubectl run apache-logs-annotated --rm --attach --restart=Never --image=edsiper/apache_logs |
+| Command | $ kubectl annotate pods apache-logs-annotated logging.parser='apache' |
+
+
+
+#### Apache Logs Annotated Invalid
+
+| | |
+| ----------- | ------------------------------------------------------------ |
+| Description | Simple Apache access log line with annotation suggesting an invalid Parser |
+| Log File | apache-logs-annotated-invalid_default_apache-logs-annotated-invalid-b8aab41f6104d7d7ea121852cd00276d8fe42d2a3192b3ae8f949477a272b91b.log |
+| Command | $ kubectl run apache-logs-annotated-invalid --rm --attach --restart=Never --image=edsiper/apache_logs |
+| Command | $ kubectl annotate pods apache-logs-annotated-invalid logging.parser='404' |
+
+#### JSON Stringify
+
+| | |
+| ----------- | ------------------------------------------------------------ |
+| Description | Application writes a JSON message, it become stringified by Docker. |
+| Log File | json-logs_default_json-logs-c053db7370be9c33d64677f9759863d850ebe35104069bec241cd1bb4674bd19.log |
+| Command | $ kubectl run json-logs --rm --attach --restart=Never --image=edsiper/json_logs |
+
+#### JSON Invalid
+
+| | |
+| ----------- | ------------------------------------------------------------ |
+| Description | Application writes an invalid JSON message. |
+| Log File | json-logs-invalid_default_json-logs-invalid-054e8bb83c2cc890bae4a184e7a2f96f18dfb121f83e4c5c5541dd452fa4e58e.log |
+| Command | $ kubectl run json-logs-invalid --rm --attach --restart=Never --image=edsiper/json_logs_invalid | \ No newline at end of file
diff --git a/fluent-bit/tests/runtime/filter_log_to_metrics.c b/fluent-bit/tests/runtime/filter_log_to_metrics.c
new file mode 100644
index 00000000..eb38af61
--- /dev/null
+++ b/fluent-bit/tests/runtime/filter_log_to_metrics.c
@@ -0,0 +1,698 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/* Fluent Bit
+ * ==========
+ * Copyright (C) 2015-2023 The Fluent Bit Authors
+ * Copyright (C) 2023 SAP SE
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <fluent-bit.h>
+#include <fluent-bit/flb_time.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include "flb_tests_runtime.h"
+
+/*
+* +-------------------------------+-------------------------------+
+* | | |
+* | +--------------------+ | +--------------------+ |
+* | | INPUTS | | ->| INPUT (IN_EMITTER | |
+* | | | |/ | METRICS)| |
+* | +--------------------+ / +--------------------+ |
+* | | /| | |
+* | v / | | |
+* | +--------------------+ | | | |
+* | | FILTER (KUBERNETES)| | | | |
+* | | (NOT USED IN TEST)| | | | |
+* | +--------------------+ | | | |
+* | | | | | |
+* | v / | | |
+* | +--------------------+ / | | |
+* | | FILTER (LOG METRIC)| / | | |
+* | | |-/ | | |
+* | +--------------------+ | | |
+* | | | | |
+* | v | v |
+* | +--------------------+ | +--------------------+ |
+* | | OUTPUT | | | OUTPUT (METRICS) | |
+* | | (NONE IN THIS TEST)| | | | |
+* | +--------------------+ | +--------------------+ |
+* | | |
+* +-------------------------------+-------------------------------+
+*/
+
+/* Test functions */
+void flb_test_log_to_metrics_counter_k8s(void);
+void flb_test_log_to_metrics_counter(void);
+void flb_test_log_to_metrics_counter_k8s_two_tuples(void);
+void flb_test_log_to_metrics_gauge(void);
+void flb_test_log_to_metrics_histogram(void);
+void flb_test_log_to_metrics_reg(void);
+void flb_test_log_to_metrics_empty_label_keys_regex(void);
+void flb_test_log_to_metrics_label(void);
+
+
+/* Test data */
+#define JSON_MSG1 "[" \
+ "1448403340," \
+ "{" \
+ "\"message\": \"dummy\"," \
+ "\"kubernetes\":{" \
+ "\"container_name\": \"mycontainer\"," \
+ "\"namespace_name\": \"k8s-dummy\"," \
+ "\"docker_id\": \"abc123\"," \
+ "\"pod_name\": \"testpod\"," \
+ "\"pod_id\": \"def456\"," \
+ "}," \
+ "\"duration\": \"20\"," \
+ "\"color\": \"red\"," \
+ "\"direction\": \"right\"" \
+ "}]"
+
+#define JSON_MSG2 "[" \
+ "1448403341," \
+ "{" \
+ "\"message\": \"dummy\"," \
+ "\"kubernetes\":{" \
+ "\"container_name\": \"mycontainer\"," \
+ "\"namespace_name\": \"k8s-dummy\"," \
+ "\"docker_id\": \"abc123\"," \
+ "\"pod_name\": \"testpod\"," \
+ "\"pod_id\": \"def456\"," \
+ "}," \
+ "\"duration\": \"20\"," \
+ "\"color\": \"red\"," \
+ "\"direction\": \"left\"" \
+ "}]"
+
+#define JSON_MSG3 "[" \
+ "1448403341," \
+ "{" \
+ "\"message\": \"hello\"," \
+ "\"kubernetes\":{" \
+ "\"container_name\": \"mycontainer\"," \
+ "\"namespace_name\": \"k8s-dummy\"," \
+ "\"docker_id\": \"abc123\"," \
+ "\"pod_name\": \"testpod\"," \
+ "\"pod_id\": \"def456\"," \
+ "}," \
+ "\"duration\": \"20\"," \
+ "\"color\": \"red\"," \
+ "\"direction\": \"left\"" \
+ "}]"
+
+/* Test list */
+TEST_LIST = {
+ {"counter_k8s", flb_test_log_to_metrics_counter_k8s },
+ {"counter", flb_test_log_to_metrics_counter },
+ {"counter_k8s_two_tuples", flb_test_log_to_metrics_counter_k8s_two_tuples },
+ {"gauge", flb_test_log_to_metrics_gauge },
+ {"histogram", flb_test_log_to_metrics_histogram },
+ {"counter_regex", flb_test_log_to_metrics_reg },
+ {"regex_empty_label_keys", flb_test_log_to_metrics_empty_label_keys_regex },
+ {"label", flb_test_log_to_metrics_label },
+ {NULL, NULL}
+};
+
+pthread_mutex_t result_mutex = PTHREAD_MUTEX_INITIALIZER;
+int data_size = 0;
+bool new_data = false;
+char output[32768];
+
+
+int callback_test(void* data, size_t size, void* cb_data)
+{
+ if (size > 0) {
+ new_data = true;
+ flb_debug("[test_filter_log_to_metrics] received message: %s", (char*)data);
+ pthread_mutex_lock(&result_mutex);
+ strncat(output, data, size);
+ data_size = size;
+ pthread_mutex_unlock(&result_mutex);
+ }
+ flb_free(data);
+ return 0;
+}
+
+static void filter_test_destroy(flb_ctx_t *ctx)
+{
+ sleep(1);
+ flb_stop(ctx);
+ flb_destroy(ctx);
+}
+
+void wait_with_timeout(uint32_t timeout_ms, char *out_result)
+{
+ struct flb_time start_time;
+ struct flb_time end_time;
+ struct flb_time diff_time;
+ uint64_t elapsed_time_flb = 0;
+ flb_time_get(&start_time);
+
+ while (true) {
+ if(new_data){
+ pthread_mutex_lock(&result_mutex);
+ new_data = false;
+ strcat(out_result, output);
+ pthread_mutex_unlock(&result_mutex);
+
+ }
+
+ flb_time_msleep(100);
+ flb_time_get(&end_time);
+ flb_time_diff(&end_time, &start_time, &diff_time);
+ elapsed_time_flb = flb_time_to_nanosec(&diff_time) / 1000000;
+
+ if (elapsed_time_flb > timeout_ms) {
+ // Reached timeout.
+ break;
+ }
+ }
+}
+
+void flb_test_log_to_metrics_counter_k8s(void)
+{
+ int ret;
+ int i;
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int filter_ffd;
+ int out_ffd;
+ char *result = NULL;
+ struct flb_lib_out_cb cb_data;
+ char *input = JSON_MSG1;
+ char finalString[32768] = "";
+
+ const char *expected = "\"value\":5.0,\"labels\":[\"k8s-dummy\","
+ "\"testpod\",\"mycontainer\",\"abc123\","
+ "\"def456\",\"red\",\"right\"]";
+
+ ctx = flb_create();
+ flb_service_set(ctx, "Flush", "0.200000000", "Grace", "1", "Log_Level",
+ "error", NULL);
+
+ cb_data.cb = callback_test;
+ cb_data.data = NULL;
+
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ TEST_CHECK(in_ffd >= 0);
+ flb_input_set(ctx, in_ffd, "tag", "test", NULL);
+
+ filter_ffd = flb_filter(ctx, (char *) "log_to_metrics", NULL);
+ TEST_CHECK(filter_ffd >= 0);
+ ret = flb_filter_set(ctx, filter_ffd,
+ "Match", "*",
+ "Tag", "test_metric",
+ "metric_mode", "counter",
+ "metric_name", "test",
+ "metric_description", "Counts messages",
+ "kubernetes_mode", "on",
+ "label_field", "color",
+ "label_field", "direction",
+ NULL);
+
+ out_ffd = flb_output(ctx, (char *) "lib", (void *)&cb_data);
+ TEST_CHECK(out_ffd >= 0);
+ flb_output_set(ctx, out_ffd,
+ "match", "*",
+ "format", "json",
+ NULL);
+
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ for (i = 0; i < 5; i++){
+ flb_lib_push(ctx, in_ffd, input, strlen(input));
+ }
+ wait_with_timeout(2000, finalString);
+ result = strstr(finalString, expected);
+ if (!TEST_CHECK(result != NULL)) {
+ TEST_MSG("expected substring:\n%s\ngot:\n%s\n", expected, finalString);
+ }
+
+ filter_test_destroy(ctx);
+
+}
+
+void flb_test_log_to_metrics_counter(void)
+{
+ int ret;
+ int i;
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int filter_ffd;
+ int out_ffd;
+ char *result = NULL;
+ struct flb_lib_out_cb cb_data;
+ char *input = JSON_MSG1;
+ char finalString[32768] = "";
+ const char *expected = "\"value\":5.0,\"labels\":[\"red\",\"right\"]";
+
+ ctx = flb_create();
+ flb_service_set(ctx, "Flush", "0.200000000", "Grace", "1", "Log_Level",
+ "error", NULL);
+
+ cb_data.cb = callback_test;
+ cb_data.data = NULL;
+
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ TEST_CHECK(in_ffd >= 0);
+ flb_input_set(ctx, in_ffd, "tag", "test", NULL);
+
+ filter_ffd = flb_filter(ctx, (char *) "log_to_metrics", NULL);
+ TEST_CHECK(filter_ffd >= 0);
+ ret = flb_filter_set(ctx, filter_ffd,
+ "Match", "*",
+ "Tag", "test_metric",
+ "metric_mode", "counter",
+ "metric_name", "test",
+ "metric_description", "Counts messages",
+ "kubernetes_mode", "off",
+ "label_field", "color",
+ "label_field", "direction",
+ NULL);
+
+ out_ffd = flb_output(ctx, (char *) "lib", (void *)&cb_data);
+ TEST_CHECK(out_ffd >= 0);
+ flb_output_set(ctx, out_ffd,
+ "match", "*",
+ "format", "json",
+ NULL);
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ for (i = 0; i < 5; i++){
+ flb_lib_push(ctx, in_ffd, input, strlen(input));
+ }
+ wait_with_timeout(2000, finalString);
+ result = strstr(finalString, expected);
+ if (!TEST_CHECK(result != NULL)) {
+ TEST_MSG("expected substring:\n%s\ngot:\n%s\n", expected, finalString);
+ }
+ filter_test_destroy(ctx);
+
+}
+
+void flb_test_log_to_metrics_counter_k8s_two_tuples(void)
+{
+ int ret;
+ int i;
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int filter_ffd;
+ int out_ffd;
+ char *result = NULL;
+ struct flb_lib_out_cb cb_data;
+ char *input1 = JSON_MSG1;
+ char *input2 = JSON_MSG2;
+ char finalString[32768] = "";
+ const char *expected1 = "\"value\":5.0,\"labels\":[\"k8s-dummy\","
+ "\"testpod\",\"mycontainer\",\"abc123\","
+ "\"def456\",\"red\",\"right\"]";
+ const char *expected2 = "\"value\":3.0,\"labels\":[\"k8s-dummy\","
+ "\"testpod\",\"mycontainer\",\"abc123\","
+ "\"def456\",\"red\",\"left\"]";
+
+
+ ctx = flb_create();
+ flb_service_set(ctx, "Flush", "0.200000000", "Grace", "1", "Log_Level",
+ "error", NULL);
+
+ cb_data.cb = callback_test;
+ cb_data.data = NULL;
+
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ TEST_CHECK(in_ffd >= 0);
+ flb_input_set(ctx, in_ffd, "tag", "test", NULL);
+
+ filter_ffd = flb_filter(ctx, (char *) "log_to_metrics", NULL);
+ TEST_CHECK(filter_ffd >= 0);
+ ret = flb_filter_set(ctx, filter_ffd,
+ "Match", "*",
+ "Tag", "test_metric",
+ "metric_mode", "counter",
+ "metric_name", "test",
+ "metric_description", "Counts two different messages",
+ "kubernetes_mode", "on",
+ "label_field", "color",
+ "label_field", "direction",
+ NULL);
+
+ out_ffd = flb_output(ctx, (char *) "lib", (void *)&cb_data);
+ TEST_CHECK(out_ffd >= 0);
+ flb_output_set(ctx, out_ffd,
+ "match", "*",
+ "format", "json",
+ NULL);
+
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ for (i = 0; i < 5; i++){
+ flb_lib_push(ctx, in_ffd, input1, strlen(input1));
+ }
+ for (i = 0; i < 3; i++){
+ flb_lib_push(ctx, in_ffd, input2, strlen(input2));
+ }
+ wait_with_timeout(2000, finalString);
+ result = strstr(finalString, expected1);
+ if (!TEST_CHECK(result != NULL)) {
+ TEST_MSG("expected substring:\n%s\ngot:\n%s\n", expected1, finalString);
+ }
+
+ result = strstr(finalString, expected2);
+ if (!TEST_CHECK(result != NULL)) {
+ TEST_MSG("expected substring:\n%s\ngot:\n%s\n", expected2, finalString);
+ }
+
+ filter_test_destroy(ctx);
+
+}
+
+void flb_test_log_to_metrics_gauge(void)
+{
+ int ret;
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int filter_ffd;
+ int out_ffd;
+ char *result = NULL;
+ struct flb_lib_out_cb cb_data;
+ char *input = JSON_MSG1;
+ char finalString[32768] = "";
+ const char *expected = "\"value\":20.0,\"labels\":[\"red\",\"right\"]";
+
+ ctx = flb_create();
+ flb_service_set(ctx, "Flush", "0.200000000", "Grace", "1", "Log_Level",
+ "error", NULL);
+
+ cb_data.cb = callback_test;
+ cb_data.data = NULL;
+
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ TEST_CHECK(in_ffd >= 0);
+ flb_input_set(ctx, in_ffd, "tag", "test", NULL);
+
+ filter_ffd = flb_filter(ctx, (char *) "log_to_metrics", NULL);
+ TEST_CHECK(filter_ffd >= 0);
+ ret = flb_filter_set(ctx, filter_ffd,
+ "Match", "*",
+ "Tag", "test_metric",
+ "metric_mode", "gauge",
+ "metric_name", "test",
+ "metric_description", "Reports gauge from messages",
+ "kubernetes_mode", "off",
+ "value_field", "duration",
+ "label_field", "color",
+ "label_field", "direction",
+ NULL);
+
+ out_ffd = flb_output(ctx, (char *) "lib", (void *)&cb_data);
+ TEST_CHECK(out_ffd >= 0);
+ flb_output_set(ctx, out_ffd,
+ "match", "*",
+ "format", "json",
+ NULL);
+
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ flb_lib_push(ctx, in_ffd, input, strlen(input));
+
+ wait_with_timeout(2000, finalString);
+ result = strstr(finalString, expected);
+ if (!TEST_CHECK(result != NULL)) {
+ TEST_MSG("expected substring:\n%s\ngot:\n%s\n", expected, finalString);
+ }
+
+ filter_test_destroy(ctx);
+
+}
+
+
+void flb_test_log_to_metrics_histogram(void)
+{
+ int ret;
+ int i;
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int filter_ffd;
+ int out_ffd;
+ char *result = NULL;
+ struct flb_lib_out_cb cb_data;
+ char *input = JSON_MSG1;
+ char finalString[32768] = "";
+ const char *expected = "\"histogram\":{\"buckets\":" \
+ "[0,0,0,0,0,0,0,0,0,0,0,5],\"" \
+ "sum\":100.0,\"count\":5},\"" \
+ "labels\":[\"red\",\"right\"]";
+ ctx = flb_create();
+ flb_service_set(ctx, "Flush", "0.200000000", "Grace", "1", "Log_Level",
+ "error", NULL);
+
+ cb_data.cb = callback_test;
+ cb_data.data = NULL;
+
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ TEST_CHECK(in_ffd >= 0);
+ flb_input_set(ctx, in_ffd, "tag", "test", NULL);
+
+ filter_ffd = flb_filter(ctx, (char *) "log_to_metrics", NULL);
+ TEST_CHECK(filter_ffd >= 0);
+ ret = flb_filter_set(ctx, filter_ffd,
+ "Match", "*",
+ "Tag", "test_metric",
+ "metric_mode", "histogram",
+ "metric_name", "test",
+ "metric_description", "Histogram of duration",
+ "kubernetes_mode", "off",
+ "value_field", "duration",
+ "label_field", "color",
+ "label_field", "direction",
+ NULL);
+
+ out_ffd = flb_output(ctx, (char *) "lib", (void *)&cb_data);
+ TEST_CHECK(out_ffd >= 0);
+ flb_output_set(ctx, out_ffd,
+ "match", "*",
+ "format", "json",
+ NULL);
+
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ for(i = 0; i < 5; i++){
+ flb_lib_push(ctx, in_ffd, input, strlen(input));
+ }
+
+ wait_with_timeout(2000, finalString);
+ result = strstr(finalString, expected);
+ if (!TEST_CHECK(result != NULL)) {
+ TEST_MSG("expected substring:\n%s\ngot:\n%s\n", expected, finalString);
+ }
+ filter_test_destroy(ctx);
+
+}
+
+void flb_test_log_to_metrics_reg(void)
+{
+ int ret;
+ int i;
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int filter_ffd;
+ int out_ffd;
+ char *result = NULL;
+ struct flb_lib_out_cb cb_data;
+ char *input1 = JSON_MSG1;
+ char *input2 = JSON_MSG3;
+ char finalString[32768] = "";
+ const char *expected = "\"value\":3.0,\"labels\":[\"red\",\"left\"]";
+
+
+ ctx = flb_create();
+ flb_service_set(ctx, "Flush", "0.200000000", "Grace", "1", "Log_Level",
+ "error", NULL);
+
+ cb_data.cb = callback_test;
+ cb_data.data = NULL;
+
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ TEST_CHECK(in_ffd >= 0);
+ flb_input_set(ctx, in_ffd, "tag", "test", NULL);
+
+ filter_ffd = flb_filter(ctx, (char *) "log_to_metrics", NULL);
+ TEST_CHECK(filter_ffd >= 0);
+ ret = flb_filter_set(ctx, filter_ffd,
+ "Match", "*",
+ "Tag", "test_metric",
+ "metric_mode", "counter",
+ "metric_name", "test",
+ "metric_description", "Counts messages with regex",
+ "kubernetes_mode", "off",
+ "label_field", "color",
+ "label_field", "direction",
+ "regex", "message .*el.*",
+ NULL);
+
+ out_ffd = flb_output(ctx, (char *) "lib", (void *)&cb_data);
+ TEST_CHECK(out_ffd >= 0);
+ flb_output_set(ctx, out_ffd,
+ "match", "*",
+ "format", "json",
+ NULL);
+
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+
+ for (i = 0; i < 3; i++){
+ flb_lib_push(ctx, in_ffd, input1, strlen(input1));
+ flb_lib_push(ctx, in_ffd, input2, strlen(input2));
+ }
+ wait_with_timeout(2000, finalString);
+ result = strstr(finalString, expected);
+ if (!TEST_CHECK(result != NULL)) {
+ TEST_MSG("expected substring:\n%s\ngot:\n%s\n", expected, finalString);
+ }
+
+ filter_test_destroy(ctx);
+
+}
+
+void flb_test_log_to_metrics_empty_label_keys_regex(void)
+{
+ int ret;
+ int i;
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int filter_ffd;
+ int out_ffd;
+ char *result = NULL;
+ struct flb_lib_out_cb cb_data;
+ char *input = JSON_MSG3;
+ char finalString[32768] = "";
+ const char *expected = "\"value\":3.0,";
+
+
+ ctx = flb_create();
+ flb_service_set(ctx, "Flush", "0.200000000", "Grace", "1", "Log_Level",
+ "error", NULL);
+
+ cb_data.cb = callback_test;
+ cb_data.data = NULL;
+
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ TEST_CHECK(in_ffd >= 0);
+ flb_input_set(ctx, in_ffd, "tag", "test", NULL);
+
+ filter_ffd = flb_filter(ctx, (char *) "log_to_metrics", NULL);
+ TEST_CHECK(filter_ffd >= 0);
+ ret = flb_filter_set(ctx, filter_ffd,
+ "Match", "*",
+ "Tag", "test_metric",
+ "metric_mode", "counter",
+ "metric_name", "test",
+ "metric_description", "Counts messages with regex",
+ "kubernetes_mode", "off",
+ "regex", "message .*el.*",
+ NULL);
+
+ out_ffd = flb_output(ctx, (char *) "lib", (void *)&cb_data);
+ TEST_CHECK(out_ffd >= 0);
+ flb_output_set(ctx, out_ffd,
+ "match", "*",
+ "format", "json",
+ NULL);
+
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+
+ for (i = 0; i < 3; i++){
+ flb_lib_push(ctx, in_ffd, input, strlen(input));
+ }
+ wait_with_timeout(2000, finalString);
+ result = strstr(finalString, expected);
+ if (!TEST_CHECK(result != NULL)) {
+ TEST_MSG("expected substring:\n%s\ngot:\n%s\n", expected, finalString);
+ }
+
+ filter_test_destroy(ctx);
+}
+
+void flb_test_log_to_metrics_label(void)
+{
+ int ret;
+ int i;
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int filter_ffd;
+ int out_ffd;
+ char *result = NULL;
+ struct flb_lib_out_cb cb_data;
+ char *input = JSON_MSG1;
+ char finalString[32768] = "";
+ const char *expected_label_name = ",\"labels\":[\"pod_name\"],";
+ const char *expected_label_value = "\"value\":2.0,\"labels\":[\"testpod\"]";
+
+ ctx = flb_create();
+ flb_service_set(ctx, "Flush", "0.200000000", "Grace", "1", "Log_Level",
+ "error", NULL);
+
+ cb_data.cb = callback_test;
+ cb_data.data = NULL;
+
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ TEST_CHECK(in_ffd >= 0);
+ flb_input_set(ctx, in_ffd, "tag", "test", NULL);
+
+ filter_ffd = flb_filter(ctx, (char *) "log_to_metrics", NULL);
+ TEST_CHECK(filter_ffd >= 0);
+ ret = flb_filter_set(ctx, filter_ffd,
+ "Match", "*",
+ "Tag", "test_metric",
+ "metric_mode", "counter",
+ "metric_name", "test",
+ "metric_description", "Counts messages",
+ "kubernetes_mode", "off",
+ "add_label", "pod_name $kubernetes['pod_name']",
+ NULL);
+
+ out_ffd = flb_output(ctx, (char *) "lib", (void *)&cb_data);
+ TEST_CHECK(out_ffd >= 0);
+ flb_output_set(ctx, out_ffd,
+ "match", "*",
+ "format", "json",
+ NULL);
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ for (i = 0; i < 2; i++){
+ flb_lib_push(ctx, in_ffd, input, strlen(input));
+ }
+ wait_with_timeout(500, finalString);
+ result = strstr(finalString, expected_label_name);
+ if (!TEST_CHECK(result != NULL)) {
+ TEST_MSG("expected substring:\n%s\ngot:\n%s\n", expected_label_name, finalString);
+ }
+ result = strstr(finalString, expected_label_value);
+ if (!TEST_CHECK(result != NULL)) {
+ TEST_MSG("expected substring:\n%s\ngot:\n%s\n", expected_label_value, finalString);
+ }
+ filter_test_destroy(ctx);
+
+}
diff --git a/fluent-bit/tests/runtime/filter_lua.c b/fluent-bit/tests/runtime/filter_lua.c
new file mode 100644
index 00000000..1e311126
--- /dev/null
+++ b/fluent-bit/tests/runtime/filter_lua.c
@@ -0,0 +1,984 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/* Fluent Bit
+ * ==========
+ * Copyright (C) 2019-2021 The Fluent Bit Authors
+ * Copyright (C) 2015-2018 Treasure Data Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <fluent-bit.h>
+#include <fluent-bit/flb_time.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include "flb_tests_runtime.h"
+
+#define TMP_LUA_PATH "a.lua"
+#define FLUSH_INTERVAL "1.0"
+
+pthread_mutex_t result_mutex = PTHREAD_MUTEX_INITIALIZER;
+char *output = NULL;
+int num_output = 0;
+
+void set_output(char *val)
+{
+ pthread_mutex_lock(&result_mutex);
+ output = val;
+ pthread_mutex_unlock(&result_mutex);
+}
+
+void clear_output()
+{
+ pthread_mutex_lock(&result_mutex);
+ output = NULL;
+ pthread_mutex_unlock(&result_mutex);
+}
+
+char *get_output(void)
+{
+ char *val;
+
+ pthread_mutex_lock(&result_mutex);
+ val = output;
+ pthread_mutex_unlock(&result_mutex);
+
+ return val;
+}
+
+static void clear_output_num()
+{
+ pthread_mutex_lock(&result_mutex);
+ num_output = 0;
+ pthread_mutex_unlock(&result_mutex);
+}
+
+static int get_output_num()
+{
+ int ret;
+ pthread_mutex_lock(&result_mutex);
+ ret = num_output;
+ pthread_mutex_unlock(&result_mutex);
+
+ return ret;
+}
+
+static int cb_count_msgpack_events(void *record, size_t size, void *data)
+{
+ msgpack_unpacked result;
+ size_t off = 0;
+
+ if (!TEST_CHECK(data != NULL)) {
+ flb_error("data is NULL");
+ }
+
+ /* Iterate each item array and apply rules */
+ msgpack_unpacked_init(&result);
+ while (msgpack_unpack_next(&result, record, size, &off) == MSGPACK_UNPACK_SUCCESS) {
+ pthread_mutex_lock(&result_mutex);
+ num_output++;
+ pthread_mutex_unlock(&result_mutex);
+ }
+ msgpack_unpacked_destroy(&result);
+
+ flb_free(record);
+ return 0;
+}
+
+int callback_test(void* data, size_t size, void* cb_data)
+{
+ if (size > 0) {
+ flb_debug("[test_filter_lua] received message: %s", (char*)data);
+ set_output(data); /* success */
+ }
+ return 0;
+}
+
+int callback_cat(void* data, size_t size, void* cb_data)
+{
+ flb_sds_t *outbuf = cb_data;
+ if (size > 0) {
+ flb_debug("[test_filter_lua] received message: %s", (char*)data);
+ pthread_mutex_lock(&result_mutex);
+ flb_sds_cat_safe(outbuf, data, size);
+ flb_free(data);
+ pthread_mutex_unlock(&result_mutex);
+ }
+ return 0;
+}
+
+void delete_script()
+{
+ unlink(TMP_LUA_PATH);
+ flb_debug("remove script\n");
+}
+
+
+int create_script(char *script_body, size_t body_size)
+{
+ FILE *fp = NULL;
+ fp = fopen(TMP_LUA_PATH, "w+");
+ if (fp == NULL) {
+ TEST_MSG("fopen error\n");
+ return -1;
+ }
+ fwrite(script_body, body_size, 1, fp);
+ fflush(fp);
+ fclose(fp);
+ return 0;
+}
+
+void wait_with_timeout(uint32_t timeout_ms, char **out_result)
+{
+ struct flb_time start_time;
+ struct flb_time end_time;
+ struct flb_time diff_time;
+ uint64_t elapsed_time_flb = 0;
+ char *output = NULL;
+
+ flb_time_get(&start_time);
+
+ while (true) {
+ output = get_output();
+
+ if (output != NULL) {
+ *out_result = output;
+ break;
+ }
+
+ flb_time_msleep(100);
+ flb_time_get(&end_time);
+ flb_time_diff(&end_time, &start_time, &diff_time);
+ elapsed_time_flb = flb_time_to_nanosec(&diff_time) / 1000000;
+
+ if (elapsed_time_flb > timeout_ms) {
+ flb_warn("[timeout] elapsed_time: %ld", elapsed_time_flb);
+ // Reached timeout.
+ break;
+ }
+ }
+}
+
+
+void flb_test_type_int_key(void)
+{
+ int ret;
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+ int filter_ffd;
+ char *output = NULL;
+ char *input = "[0, {\"key\":\"val\"}]";
+ char *result;
+ struct flb_lib_out_cb cb_data;
+
+ char *script_body = ""
+ "function lua_main(tag, timestamp, record)\n"
+ " new_record = record\n"
+ " new_record[\"lua_int\"] = 10.2\n"
+ " return 1, timestamp, new_record\n"
+ "end\n";
+
+ clear_output();
+
+ /* Create context, flush every second (some checks omitted here) */
+ ctx = flb_create();
+ flb_service_set(ctx, "flush", FLUSH_INTERVAL, "grace", "1", NULL);
+
+ /* Prepare output callback context*/
+ cb_data.cb = callback_test;
+ cb_data.data = NULL;
+
+ ret = create_script(script_body, strlen(script_body));
+ TEST_CHECK(ret == 0);
+ /* Filter */
+ filter_ffd = flb_filter(ctx, (char *) "lua", NULL);
+ TEST_CHECK(filter_ffd >= 0);
+ ret = flb_filter_set(ctx, filter_ffd,
+ "Match", "*",
+ "call", "lua_main",
+ "type_int_key", "lua_int",
+ "script", TMP_LUA_PATH,
+ NULL);
+
+ /* Input */
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ flb_input_set(ctx, in_ffd, "tag", "test", NULL);
+ TEST_CHECK(in_ffd >= 0);
+
+ /* Lib output */
+ out_ffd = flb_output(ctx, (char *) "lib", (void *)&cb_data);
+ TEST_CHECK(out_ffd >= 0);
+ flb_output_set(ctx, out_ffd,
+ "match", "test",
+ "format", "json",
+ NULL);
+
+ ret = flb_start(ctx);
+ TEST_CHECK(ret==0);
+
+ flb_lib_push(ctx, in_ffd, input, strlen(input));
+ wait_with_timeout(2000, &output);
+ result = strstr(output, "\"lua_int\":10.");
+ if(!TEST_CHECK(result == NULL)) {
+ TEST_MSG("output:%s\n", output);
+ }
+ result = strstr(output, "\"lua_int\":10");
+ if(!TEST_CHECK(result != NULL)) {
+ TEST_MSG("output:%s\n", output);
+ }
+
+ /* clean up */
+ flb_lib_free(output);
+ delete_script();
+
+ flb_stop(ctx);
+ flb_destroy(ctx);
+}
+
+void flb_test_type_int_key_multi(void)
+{
+ int ret;
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+ int filter_ffd;
+ char *output = NULL;
+ char *input = "[0, {\"key\":\"val\"}]";
+ char *result;
+ struct flb_lib_out_cb cb_data;
+
+ char *script_body = ""
+ "function lua_main(tag, timestamp, record)\n"
+ " new_record = record\n"
+ " new_record[\"lua_int_1\"] = 10.1\n"
+ " new_record[\"lua_int_2\"] = 100.2\n"
+ " return 1, timestamp, new_record\n"
+ "end\n";
+
+ clear_output();
+
+ /* Create context, flush every second (some checks omitted here) */
+ ctx = flb_create();
+ flb_service_set(ctx, "flush", FLUSH_INTERVAL, "grace", "1", NULL);
+
+ /* Prepare output callback context*/
+ cb_data.cb = callback_test;
+ cb_data.data = NULL;
+
+ ret = create_script(script_body, strlen(script_body));
+ TEST_CHECK(ret == 0);
+ /* Filter */
+ filter_ffd = flb_filter(ctx, (char *) "lua", NULL);
+ TEST_CHECK(filter_ffd >= 0);
+ ret = flb_filter_set(ctx, filter_ffd,
+ "Match", "*",
+ "call", "lua_main",
+ "type_int_key", "lua_int_1 lua_int_2",
+ "script", TMP_LUA_PATH,
+ NULL);
+
+ /* Input */
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ flb_input_set(ctx, in_ffd, "tag", "test", NULL);
+ TEST_CHECK(in_ffd >= 0);
+
+ /* Lib output */
+ out_ffd = flb_output(ctx, (char *) "lib", (void *)&cb_data);
+ TEST_CHECK(out_ffd >= 0);
+ flb_output_set(ctx, out_ffd,
+ "match", "test",
+ "format", "json",
+ NULL);
+
+ ret = flb_start(ctx);
+ TEST_CHECK(ret==0);
+
+ flb_lib_push(ctx, in_ffd, input, strlen(input));
+ wait_with_timeout(2000, &output);
+
+ /* check if float */
+ result = strstr(output, "\"lua_int_1\":10.");
+ if (!TEST_CHECK(result == NULL)) {
+ TEST_MSG("output:%s\n", output);
+ }
+ result = strstr(output, "\"lua_int_2\":100.");
+ if (!TEST_CHECK(result == NULL)) {
+ TEST_MSG("output:%s\n", output);
+ }
+
+ /* check if int */
+ result = strstr(output, "\"lua_int_1\":10");
+ if (!TEST_CHECK(result != NULL)) {
+ TEST_MSG("output:%s\n", output);
+ }
+ result = strstr(output, "\"lua_int_2\":100");
+ if (!TEST_CHECK(result != NULL)) {
+ TEST_MSG("output:%s\n", output);
+ }
+
+ /* clean up */
+ flb_lib_free(output);
+ delete_script();
+
+ flb_stop(ctx);
+ flb_destroy(ctx);
+}
+
+
+void flb_test_append_tag(void)
+{
+ int ret;
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+ int filter_ffd;
+ char *output = NULL;
+ char *input = "[0, {\"key\":\"val\"}]";
+ char *result;
+ struct flb_lib_out_cb cb_data;
+
+ char *script_body = ""
+ "function lua_main(tag, timestamp, record)\n"
+ " new_record = record\n"
+ " new_record[\"tag\"] = tag\n"
+ " return 1, timestamp, new_record\n"
+ "end\n";
+
+ /* Create context, flush every second (some checks omitted here) */
+ ctx = flb_create();
+ flb_service_set(ctx, "flush", FLUSH_INTERVAL, "grace", "1", NULL);
+
+ /* Prepare output callback context*/
+ cb_data.cb = callback_test;
+ cb_data.data = NULL;
+
+ ret = create_script(script_body, strlen(script_body));
+ TEST_CHECK(ret == 0);
+ /* Filter */
+ filter_ffd = flb_filter(ctx, (char *) "lua", NULL);
+ TEST_CHECK(filter_ffd >= 0);
+ ret = flb_filter_set(ctx, filter_ffd,
+ "Match", "*",
+ "call", "lua_main",
+ "script", TMP_LUA_PATH,
+ NULL);
+
+ /* Input */
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ flb_input_set(ctx, in_ffd, "tag", "test", NULL);
+ TEST_CHECK(in_ffd >= 0);
+
+ /* Lib output */
+ out_ffd = flb_output(ctx, (char *) "lib", (void *)&cb_data);
+ TEST_CHECK(out_ffd >= 0);
+ flb_output_set(ctx, out_ffd,
+ "match", "test",
+ "format", "json",
+ NULL);
+
+ ret = flb_start(ctx);
+ TEST_CHECK(ret==0);
+
+ flb_lib_push(ctx, in_ffd, input, strlen(input));
+ wait_with_timeout(2000, &output);
+ result = strstr(output, "\"tag\":\"test\"");
+ TEST_CHECK(result != NULL);
+
+ /* clean up */
+ flb_lib_free(output);
+ delete_script();
+
+ flb_stop(ctx);
+ flb_destroy(ctx);
+}
+
+
+void flb_test_helloworld(void)
+{
+ int ret;
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+ int filter_ffd;
+
+ char *script_body = ""
+ "function lua_main(tag, timestamp, record)\n"
+ " print(\"hello world\")\n"
+ " return 0, timestamp, record\n"
+ "end\n";
+
+ /* Create context, flush every second (some checks omitted here) */
+ ctx = flb_create();
+ flb_service_set(ctx, "flush", FLUSH_INTERVAL, "grace", "1", NULL);
+
+ ret = create_script(script_body, strlen(script_body));
+ TEST_CHECK(ret == 0);
+ /* Filter */
+ filter_ffd = flb_filter(ctx, (char *) "lua", NULL);
+ TEST_CHECK(filter_ffd >= 0);
+ ret = flb_filter_set(ctx, filter_ffd,
+ "Match", "*",
+ "call", "lua_main",
+ "script", TMP_LUA_PATH,
+ NULL);
+
+ /* Input */
+ in_ffd = flb_input(ctx, (char *) "dummy", NULL);
+ flb_input_set(ctx, in_ffd, "tag", "test", NULL);
+ TEST_CHECK(in_ffd >= 0);
+
+ /* Lib output */
+ out_ffd = flb_output(ctx, (char *) "stdout", NULL);
+ TEST_CHECK(out_ffd >= 0);
+ flb_output_set(ctx, out_ffd,
+ "match", "test",
+ NULL);
+
+ ret = flb_start(ctx);
+ TEST_CHECK(ret==0);
+
+ delete_script();
+ flb_stop(ctx);
+ flb_destroy(ctx);
+}
+
+// https://github.com/fluent/fluent-bit/issues/3343
+void flb_test_type_array_key(void)
+{
+ int ret;
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+ int filter_ffd;
+ char *output = NULL;
+ char *input = "[0, {\"key\":\"val\"}]";
+ char *result;
+ struct flb_lib_out_cb cb_data;
+
+ char *script_body = ""
+ "function lua_main(tag, timestamp, record)\n"
+ " new_record = record\n"
+ " new_record[\"lua_array\"] = {};\n"
+ " new_record[\"lua_array2\"] = {1,2,3};\n"
+ " return 1, timestamp, new_record\n"
+ "end\n";
+
+ clear_output();
+
+ /* Create context, flush every second (some checks omitted here) */
+ ctx = flb_create();
+ flb_service_set(ctx, "flush", FLUSH_INTERVAL, "grace", "1", NULL);
+
+ /* Prepare output callback context*/
+ cb_data.cb = callback_test;
+ cb_data.data = NULL;
+
+ ret = create_script(script_body, strlen(script_body));
+ TEST_CHECK(ret == 0);
+ /* Filter */
+ filter_ffd = flb_filter(ctx, (char *) "lua", NULL);
+ TEST_CHECK(filter_ffd >= 0);
+ ret = flb_filter_set(ctx, filter_ffd,
+ "Match", "*",
+ "call", "lua_main",
+ "type_array_key", "lua_array lua_array2",
+ "script", TMP_LUA_PATH,
+ NULL);
+
+ /* Input */
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ flb_input_set(ctx, in_ffd, "tag", "test", NULL);
+ TEST_CHECK(in_ffd >= 0);
+
+ /* Lib output */
+ out_ffd = flb_output(ctx, (char *) "lib", (void *)&cb_data);
+ TEST_CHECK(out_ffd >= 0);
+ flb_output_set(ctx, out_ffd,
+ "match", "test",
+ "format", "json",
+ NULL);
+
+ ret = flb_start(ctx);
+ TEST_CHECK(ret==0);
+
+ flb_lib_push(ctx, in_ffd, input, strlen(input));
+ wait_with_timeout(2000, &output);
+ result = strstr(output, "\"lua_array\":[]");
+ if(!TEST_CHECK(result != NULL)) {
+ TEST_MSG("output:%s\n", output);
+ }
+ result = strstr(output, "\"lua_array2\":[1,2,3]");
+ if(!TEST_CHECK(result != NULL)) {
+ TEST_MSG("output:%s\n", output);
+ }
+
+ /* clean up */
+ flb_lib_free(output);
+ delete_script();
+
+ flb_stop(ctx);
+ flb_destroy(ctx);
+}
+
+/* https://github.com/fluent/fluent-bit/issues/3433 */
+void flb_test_array_contains_null(void)
+{
+ int ret;
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+ int filter_ffd;
+ char *output = NULL;
+ char *input = "[0, {\"hello\": [1, null, \"world\"]}]";
+ char *result;
+ struct flb_lib_out_cb cb_data;
+
+ char *script_body = ""
+ "function lua_main(tag, timestamp, record)\n"
+ " new_record = record\n"
+ " new_record[\"modify\"] = \"yes\"\n"
+ " return 1, timestamp, new_record\n"
+ "end\n";
+
+ clear_output();
+
+ /* Create context, flush every second (some checks omitted here) */
+ ctx = flb_create();
+ flb_service_set(ctx, "flush", FLUSH_INTERVAL, "grace", "1", NULL);
+
+ /* Prepare output callback context*/
+ cb_data.cb = callback_test;
+ cb_data.data = NULL;
+
+ ret = create_script(script_body, strlen(script_body));
+ TEST_CHECK(ret == 0);
+ /* Filter */
+ filter_ffd = flb_filter(ctx, (char *) "lua", NULL);
+ TEST_CHECK(filter_ffd >= 0);
+ ret = flb_filter_set(ctx, filter_ffd,
+ "Match", "*",
+ "call", "lua_main",
+ "script", TMP_LUA_PATH,
+ NULL);
+
+ /* Input */
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ flb_input_set(ctx, in_ffd, "tag", "test", NULL);
+ TEST_CHECK(in_ffd >= 0);
+
+ /* Lib output */
+ out_ffd = flb_output(ctx, (char *) "lib", (void *)&cb_data);
+ TEST_CHECK(out_ffd >= 0);
+ flb_output_set(ctx, out_ffd,
+ "match", "test",
+ "format", "json",
+ NULL);
+
+ ret = flb_start(ctx);
+ TEST_CHECK(ret==0);
+
+ flb_lib_push(ctx, in_ffd, input, strlen(input));
+ wait_with_timeout(2000, &output);
+ result = strstr(output, "[1,null,\"world\"]");
+ if(!TEST_CHECK(result != NULL)) {
+ TEST_MSG("output:%s\n", output);
+ }
+ result = strstr(output, "\"modify\":\"yes\"");
+ if(!TEST_CHECK(result != NULL)) {
+ TEST_MSG("output:%s\n", output);
+ }
+
+ /* clean up */
+ flb_lib_free(output);
+ delete_script();
+
+ flb_stop(ctx);
+ flb_destroy(ctx);
+}
+
+/* https://github.com/fluent/fluent-bit/issues/5251 */
+void flb_test_drop_all_records(void)
+{
+ int ret;
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+ int filter_ffd;
+ char *output = NULL;
+ char *input = "[0, {\"key\":\"val\"}]";
+ struct flb_lib_out_cb cb_data;
+
+ char *script_body = ""
+ "function lua_main(tag, timestamp, record)\n"
+ " return -1, 0, 0\n"
+ "end\n";
+
+ clear_output_num();
+
+ /* Create context, flush every second (some checks omitted here) */
+ ctx = flb_create();
+ flb_service_set(ctx, "flush", FLUSH_INTERVAL, "grace", "1", NULL);
+
+ /* Prepare output callback context*/
+ cb_data.cb = cb_count_msgpack_events;
+ cb_data.data = NULL;
+
+ ret = create_script(script_body, strlen(script_body));
+ TEST_CHECK(ret == 0);
+ /* Filter */
+ filter_ffd = flb_filter(ctx, (char *) "lua", NULL);
+ TEST_CHECK(filter_ffd >= 0);
+ ret = flb_filter_set(ctx, filter_ffd,
+ "Match", "*",
+ "call", "lua_main",
+ "script", TMP_LUA_PATH,
+ NULL);
+
+ /* Input */
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ flb_input_set(ctx, in_ffd, "tag", "test", NULL);
+ TEST_CHECK(in_ffd >= 0);
+
+ /* Lib output */
+ out_ffd = flb_output(ctx, (char *) "lib", (void *)&cb_data);
+ TEST_CHECK(out_ffd >= 0);
+ flb_output_set(ctx, out_ffd,
+ "match", "test",
+ NULL);
+
+ ret = flb_start(ctx);
+ TEST_CHECK(ret==0);
+
+ flb_lib_push(ctx, in_ffd, input, strlen(input));
+ flb_time_msleep(1500); /* waiting flush */
+
+ ret = get_output_num();
+ if (!TEST_CHECK(ret == 0)) {
+ TEST_MSG("error. got %d expect 0", ret);
+ }
+
+ /* clean up */
+ flb_lib_free(output);
+ delete_script();
+
+ flb_stop(ctx);
+ flb_destroy(ctx);
+}
+
+void flb_test_enable_flb_null(void)
+{
+ int ret;
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+ int filter_ffd;
+ char *output = NULL;
+ char *input = "[0, {\"hello\":null}]";
+ char *result;
+ struct flb_lib_out_cb cb_data;
+
+ char *script_body = ""
+ "function lua_main(tag, timestamp, record)\n"
+ " return 1, timestamp, record\n"
+ "end\n";
+
+ clear_output();
+
+ /* Create context, flush every second (some checks omitted here) */
+ ctx = flb_create();
+ flb_service_set(ctx, "flush", FLUSH_INTERVAL, "grace", "1", NULL);
+
+ /* Prepare output callback context*/
+ cb_data.cb = callback_test;
+ cb_data.data = NULL;
+
+ ret = create_script(script_body, strlen(script_body));
+ TEST_CHECK(ret == 0);
+ /* Filter */
+ filter_ffd = flb_filter(ctx, (char *) "lua", NULL);
+ TEST_CHECK(filter_ffd >= 0);
+ ret = flb_filter_set(ctx, filter_ffd,
+ "Match", "*",
+ "call", "lua_main",
+ "script", TMP_LUA_PATH,
+ "enable_flb_null", "true",
+ NULL);
+
+ /* Input */
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ flb_input_set(ctx, in_ffd, "tag", "test", NULL);
+ TEST_CHECK(in_ffd >= 0);
+
+ /* Lib output */
+ out_ffd = flb_output(ctx, (char *) "lib", (void *)&cb_data);
+ TEST_CHECK(out_ffd >= 0);
+ flb_output_set(ctx, out_ffd,
+ "match", "test",
+ "format", "json",
+ NULL);
+
+ ret = flb_start(ctx);
+ TEST_CHECK(ret==0);
+
+ flb_lib_push(ctx, in_ffd, input, strlen(input));
+ wait_with_timeout(2000, &output);
+ result = strstr(output, "\"hello\":null");
+ if(!TEST_CHECK(result != NULL)) {
+ TEST_MSG("output:%s\n", output);
+ }
+
+ /* clean up */
+ flb_lib_free(output);
+ delete_script();
+
+ flb_stop(ctx);
+ flb_destroy(ctx);
+}
+
+/* https://github.com/fluent/fluent-bit/issues/5496 */
+void flb_test_split_record(void)
+{
+ int ret;
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+ int filter_ffd;
+ struct flb_lib_out_cb cb_data;
+ char *output = NULL;
+ flb_sds_t outbuf = flb_sds_create("");
+ char *input = "[0, {\"x\": [ "
+ "{\"a1\":\"aa\"}, "
+ "{\"b1\":\"bb\"}, "
+ "{\"c1\":\"cc\"} ]}]";
+ const char *expected =
+ "[5.000000,{\"a1\":\"aa\"}]"
+ "[5.000000,{\"b1\":\"bb\"}]"
+ "[5.000000,{\"c1\":\"cc\"}]";
+ char *script_body = ""
+ "function lua_main(tag, timestamp, record)\n"
+ " return 1, 5, record.x\n"
+ "end\n";
+
+ clear_output();
+
+ /* Create context, flush every second (some checks omitted here) */
+ ctx = flb_create();
+ flb_service_set(ctx, "flush", FLUSH_INTERVAL, "grace", "1", NULL);
+
+ /* Prepare output callback context*/
+ cb_data.cb = callback_cat;
+ cb_data.data = &outbuf;
+
+ ret = create_script(script_body, strlen(script_body));
+ TEST_CHECK(ret == 0);
+ /* Filter */
+ filter_ffd = flb_filter(ctx, (char *) "lua", NULL);
+ TEST_CHECK(filter_ffd >= 0);
+ ret = flb_filter_set(ctx, filter_ffd,
+ "Match", "*",
+ "call", "lua_main",
+ "script", TMP_LUA_PATH,
+ NULL);
+
+ /* Input */
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ flb_input_set(ctx, in_ffd, "tag", "test", NULL);
+ TEST_CHECK(in_ffd >= 0);
+
+ /* Lib output */
+ out_ffd = flb_output(ctx, (char *) "lib", (void *)&cb_data);
+ TEST_CHECK(out_ffd >= 0);
+ flb_output_set(ctx, out_ffd,
+ "match", "test",
+ "format", "json",
+ NULL);
+
+ ret = flb_start(ctx);
+ TEST_CHECK(ret==0);
+
+ flb_lib_push(ctx, in_ffd, input, strlen(input));
+ wait_with_timeout(2000, &output);
+ if (!TEST_CHECK(!strcmp(outbuf, expected))) {
+ TEST_MSG("expected:\n%s\ngot:\n%s\n", expected, outbuf);
+ }
+
+ /* clean up */
+ flb_lib_free(output);
+ delete_script();
+
+ flb_stop(ctx);
+ flb_destroy(ctx);
+ flb_sds_destroy(outbuf);
+}
+
+void flb_test_empty_array(void)
+{
+ int ret;
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+ int filter_ffd;
+ flb_sds_t outbuf = flb_sds_create("");
+ char *input = "[0, {\"key\":[]}]";
+ struct flb_lib_out_cb cb_data;
+
+ const char *expected =
+ "[5.000000,{\"key\":[]}]";
+
+ char *script_body = ""
+ "function lua_main(tag, timestamp, record)\n"
+ " return 1, 5, record\n"
+ "end\n";
+
+ clear_output_num();
+
+ /* Create context, flush every second (some checks omitted here) */
+ ctx = flb_create();
+ flb_service_set(ctx, "flush", FLUSH_INTERVAL, "grace", "1", NULL);
+
+ /* Prepare output callback context*/
+ cb_data.cb = callback_cat;
+ cb_data.data = &outbuf;
+
+ ret = create_script(script_body, strlen(script_body));
+ TEST_CHECK(ret == 0);
+ /* Filter */
+ filter_ffd = flb_filter(ctx, (char *) "lua", NULL);
+ TEST_CHECK(filter_ffd >= 0);
+ ret = flb_filter_set(ctx, filter_ffd,
+ "Match", "*",
+ "call", "lua_main",
+ "script", TMP_LUA_PATH,
+ NULL);
+
+ /* Input */
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ flb_input_set(ctx, in_ffd, "tag", "test", NULL);
+ TEST_CHECK(in_ffd >= 0);
+
+ /* Lib output */
+ out_ffd = flb_output(ctx, (char *) "lib", (void *)&cb_data);
+ TEST_CHECK(out_ffd >= 0);
+ flb_output_set(ctx, out_ffd,
+ "match", "test",
+ "format", "json",
+ NULL);
+
+ ret = flb_start(ctx);
+ TEST_CHECK(ret==0);
+
+ flb_lib_push(ctx, in_ffd, input, strlen(input));
+ wait_with_timeout(2000, &output);
+ if (!TEST_CHECK(!strcmp(outbuf, expected))) {
+ TEST_MSG("expected:\n%s\ngot:\n%s\n", expected, outbuf);
+ }
+
+ /* clean up */
+ flb_lib_free(output);
+ delete_script();
+
+ flb_stop(ctx);
+ flb_destroy(ctx);
+ flb_sds_destroy(outbuf);
+}
+
+void flb_test_invalid_metatable(void)
+{
+ int ret;
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+ int unused = 0;
+ int filter_ffd;
+ char *output = NULL;
+ char *input = "[0, {\"key\":\"val\"}]";
+ struct flb_lib_out_cb cb_data;
+
+ char *script_body = ""
+ "function lua_main(tag, timestamp, record)\n"
+ " meta = getmetatable(record)\n"
+ " meta[10] = \"hoge\"\n"
+ " return 1, timestamp, record\n"
+ "end\n";
+
+ clear_output_num();
+
+ /* Create context, flush every second (some checks omitted here) */
+ ctx = flb_create();
+ flb_service_set(ctx, "flush", FLUSH_INTERVAL, "grace", "1", NULL);
+
+ /* Prepare output callback context*/
+ cb_data.cb = cb_count_msgpack_events;
+ cb_data.data = &unused;
+
+ ret = create_script(script_body, strlen(script_body));
+ TEST_CHECK(ret == 0);
+ /* Filter */
+ filter_ffd = flb_filter(ctx, (char *) "lua", NULL);
+ TEST_CHECK(filter_ffd >= 0);
+ ret = flb_filter_set(ctx, filter_ffd,
+ "Match", "*",
+ "call", "lua_main",
+ "script", TMP_LUA_PATH,
+ NULL);
+
+ /* Input */
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ flb_input_set(ctx, in_ffd, "tag", "test", NULL);
+ TEST_CHECK(in_ffd >= 0);
+
+ /* Lib output */
+ out_ffd = flb_output(ctx, (char *) "lib", (void *)&cb_data);
+ TEST_CHECK(out_ffd >= 0);
+ flb_output_set(ctx, out_ffd,
+ "match", "test",
+ NULL);
+
+ ret = flb_start(ctx);
+ TEST_CHECK(ret==0);
+
+ ret = flb_lib_push(ctx, in_ffd, input, strlen(input));
+ if (!TEST_CHECK(ret != -1)) {
+ TEST_MSG("flb_lib_push error");
+ }
+ flb_time_msleep(1500); /* waiting flush */
+
+ ret = get_output_num();
+ if (!TEST_CHECK(ret > 0)) {
+ TEST_MSG("error. no output");
+ }
+
+ /* clean up */
+ flb_lib_free(output);
+ delete_script();
+
+ flb_stop(ctx);
+ flb_destroy(ctx);
+}
+
+TEST_LIST = {
+ {"hello_world", flb_test_helloworld},
+ {"append_tag", flb_test_append_tag},
+ {"type_int_key", flb_test_type_int_key},
+ {"type_int_key_multi", flb_test_type_int_key_multi},
+ {"type_array_key", flb_test_type_array_key},
+ {"array_contains_null", flb_test_array_contains_null},
+ {"drop_all_records", flb_test_drop_all_records},
+ {"enable_flb_null", flb_test_enable_flb_null},
+ {"split_record", flb_test_split_record},
+ {"empty_array", flb_test_empty_array},
+ {"invalid_metatable", flb_test_invalid_metatable},
+ {NULL, NULL}
+};
diff --git a/fluent-bit/tests/runtime/filter_modify.c b/fluent-bit/tests/runtime/filter_modify.c
new file mode 100644
index 00000000..ac5a166b
--- /dev/null
+++ b/fluent-bit/tests/runtime/filter_modify.c
@@ -0,0 +1,1674 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+#include <fluent-bit.h>
+#include "flb_tests_runtime.h"
+
+struct filter_test {
+ flb_ctx_t *flb; /* Fluent Bit library context */
+ int i_ffd; /* Input fd */
+ int f_ffd; /* Filter fd */
+};
+
+/* Callback to check expected results */
+static int cb_check_result(void *record, size_t size, void *data)
+{
+ char *p;
+ char *expected;
+ char *result;
+
+ expected = (char *) data;
+ result = (char *) record;
+
+ p = strstr(result, expected);
+ TEST_CHECK(p != NULL);
+
+ if (!p) {
+ flb_error("Expected to find: '%s' in result '%s'",
+ expected, result);
+ }
+ /*
+ * If you want to debug your test
+ *
+ * printf("Expect: '%s' in result '%s'", expected, result);
+ */
+
+ flb_free(record);
+ return 0;
+}
+
+
+
+static struct filter_test *filter_test_create(struct flb_lib_out_cb *data)
+{
+ int i_ffd;
+ int f_ffd;
+ int o_ffd;
+ struct filter_test *ctx;
+
+ ctx = flb_malloc(sizeof(struct filter_test));
+ if (!ctx) {
+ flb_errno();
+ return NULL;
+ }
+
+ /* Service config */
+ ctx->flb = flb_create();
+ flb_service_set(ctx->flb,
+ "Flush", "0.200000000",
+ "Grace", "1",
+ NULL);
+
+ /* Input */
+ i_ffd = flb_input(ctx->flb, (char *) "lib", NULL);
+ TEST_CHECK(i_ffd >= 0);
+ flb_input_set(ctx->flb, i_ffd, "tag", "test", NULL);
+ ctx->i_ffd = i_ffd;
+
+ /* Filter configuration */
+ f_ffd = flb_filter(ctx->flb, (char *) "modify", NULL);
+ TEST_CHECK(f_ffd >= 0);
+ flb_filter_set(ctx->flb, f_ffd, "match", "*", NULL);
+ ctx->f_ffd = f_ffd;
+
+ /* Output */
+ o_ffd = flb_output(ctx->flb, (char *) "lib", (void *) data);
+ TEST_CHECK(o_ffd >= 0);
+ flb_output_set(ctx->flb, o_ffd,
+ "match", "test",
+ "format", "json",
+ NULL);
+
+ return ctx;
+}
+
+static void filter_test_destroy(struct filter_test *ctx)
+{
+ sleep(1);
+ flb_stop(ctx->flb);
+ flb_destroy(ctx->flb);
+ flb_free(ctx);
+}
+
+/* Operation: SET / append new record */
+static void flb_test_op_set_append()
+{
+ int len;
+ int ret;
+ int bytes;
+ char *p;
+ struct flb_lib_out_cb cb_data;
+ struct filter_test *ctx;
+
+ /* Create test context */
+ ctx = filter_test_create((void *) &cb_data);
+ if (!ctx) {
+ exit(EXIT_FAILURE);
+ }
+
+ /* Configure filter */
+ ret = flb_filter_set(ctx->flb, ctx->f_ffd,
+ "set", "test_key test_value",
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ /* Prepare output callback with expected result */
+ cb_data.cb = cb_check_result;
+ cb_data.data = "\"test_key\":\"test_value\"";
+
+ /* Start the engine */
+ ret = flb_start(ctx->flb);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data samples */
+ p = "[0, {\"k\":\"sample\"}]";
+ len = strlen(p);
+ bytes = flb_lib_push(ctx->flb, ctx->i_ffd, p, len);
+ TEST_CHECK(bytes == len);
+
+ filter_test_destroy(ctx);
+}
+
+/* Operation: SET / replace value of existing key */
+static void flb_test_op_set_replace()
+{
+ int len;
+ int ret;
+ int bytes;
+ char *p;
+ struct flb_lib_out_cb cb_data;
+ struct filter_test *ctx;
+
+ /* Create test context */
+ ctx = filter_test_create((void *) &cb_data);
+ if (!ctx) {
+ exit(EXIT_FAILURE);
+ }
+
+ /* Configure filter */
+ ret = flb_filter_set(ctx->flb, ctx->f_ffd,
+ "set", "k test_value",
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ /* Prepare output callback with expected result */
+ cb_data.cb = cb_check_result;
+ cb_data.data = "\"k\":\"test_value\"";
+
+ /* Start the engine */
+ ret = flb_start(ctx->flb);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data samples */
+ p = "[0, {\"k\":\"sample\"}]";
+ len = strlen(p);
+ bytes = flb_lib_push(ctx->flb, ctx->i_ffd, p, len);
+ TEST_CHECK(bytes == len);
+
+ filter_test_destroy(ctx);
+}
+
+/* Operation: REMOVE */
+static void flb_test_op_remove()
+{
+ int len;
+ int ret;
+ int bytes;
+ char *p;
+ struct flb_lib_out_cb cb_data;
+ struct filter_test *ctx;
+
+ /* Create test context */
+ ctx = filter_test_create((void *) &cb_data);
+ if (!ctx) {
+ exit(EXIT_FAILURE);
+ }
+
+ /* Configure filter */
+ ret = flb_filter_set(ctx->flb, ctx->f_ffd,
+ "remove", "remove",
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ /* Prepare output callback with expected result */
+ cb_data.cb = cb_check_result;
+ cb_data.data = "\"k\":\"sample\"";
+
+ /* Start the engine */
+ ret = flb_start(ctx->flb);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data samples */
+ p = "[0, {\"k\":\"sample\",\"remove\":\"sample to remove\"}]";
+ len = strlen(p);
+ bytes = flb_lib_push(ctx->flb, ctx->i_ffd, p, len);
+ TEST_CHECK(bytes == len);
+
+ filter_test_destroy(ctx);
+}
+
+/* Operation: REMOVE_WILDCARD */
+static void flb_test_op_remove_wildcard()
+{
+ int len;
+ int ret;
+ int bytes;
+ char *p;
+ struct flb_lib_out_cb cb_data;
+ struct filter_test *ctx;
+
+ /* Create test context */
+ ctx = filter_test_create((void *) &cb_data);
+ if (!ctx) {
+ exit(EXIT_FAILURE);
+ }
+
+ /* Configure filter */
+ ret = flb_filter_set(ctx->flb, ctx->f_ffd,
+ "remove_wildcard", "k",
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ /* Prepare output callback with expected result */
+ cb_data.cb = cb_check_result;
+ cb_data.data = "\"a3\":\"sample3\"";
+
+ /* Start the engine */
+ ret = flb_start(ctx->flb);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data samples */
+ p = "[0,{\"k1\":\"sample1\",\"k2\":\"sample2\",\"a3\":\"sample3\"}]";
+ len = strlen(p);
+ bytes = flb_lib_push(ctx->flb, ctx->i_ffd, p, len);
+ TEST_CHECK(bytes == len);
+
+ filter_test_destroy(ctx);
+}
+
+/* Operation: REMOVE_REGEX */
+static void flb_test_op_remove_regex()
+{
+ int len;
+ int ret;
+ int bytes;
+ char *p;
+ struct flb_lib_out_cb cb_data;
+ struct filter_test *ctx;
+
+ /* Create test context */
+ ctx = filter_test_create((void *) &cb_data);
+ if (!ctx) {
+ exit(EXIT_FAILURE);
+ }
+
+ /* Configure filter */
+ ret = flb_filter_set(ctx->flb, ctx->f_ffd,
+ "remove_regex", "^[a-z][0-9]$",
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ /* Prepare output callback with expected result */
+ cb_data.cb = cb_check_result;
+ cb_data.data = "\"A3\":\"sample3\"";
+
+ /* Start the engine */
+ ret = flb_start(ctx->flb);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data samples */
+ p = "[0,{\"k1\":\"sample1\",\"k2\":\"sample2\",\"A3\":\"sample3\"}]";
+ len = strlen(p);
+ bytes = flb_lib_push(ctx->flb, ctx->i_ffd, p, len);
+ TEST_CHECK(bytes == len);
+
+ filter_test_destroy(ctx);
+}
+
+/*
+ * Operation: RENAME / Try to rename Key where 'renamed' key already
+ * exists: do nothing.
+ */
+static void flb_test_op_rename_exists()
+{
+ int len;
+ int ret;
+ int bytes;
+ char *p;
+ struct flb_lib_out_cb cb_data;
+ struct filter_test *ctx;
+
+ /* Create test context */
+ ctx = filter_test_create((void *) &cb_data);
+ if (!ctx) {
+ exit(EXIT_FAILURE);
+ }
+
+ /* Configure filter */
+ ret = flb_filter_set(ctx->flb, ctx->f_ffd,
+ "rename", "A3 k2",
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ /* Prepare output callback with expected result */
+ cb_data.cb = cb_check_result;
+ cb_data.data = "\"k1\":\"sample1\",\"k2\":\"sample2\",\"A3\":\"sample3\"";
+
+ /* Start the engine */
+ ret = flb_start(ctx->flb);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data samples */
+ p = "[0,{\"k1\":\"sample1\",\"k2\":\"sample2\",\"A3\":\"sample3\"}]";
+ len = strlen(p);
+ bytes = flb_lib_push(ctx->flb, ctx->i_ffd, p, len);
+ TEST_CHECK(bytes == len);
+
+ filter_test_destroy(ctx);
+}
+
+/* Operation: RENAME / Rename when key DON'T exists */
+static void flb_test_op_rename_no_exists()
+{
+ int len;
+ int ret;
+ int bytes;
+ char *p;
+ struct flb_lib_out_cb cb_data;
+ struct filter_test *ctx;
+
+ /* Create test context */
+ ctx = filter_test_create((void *) &cb_data);
+ if (!ctx) {
+ exit(EXIT_FAILURE);
+ }
+
+ /* Configure filter */
+ ret = flb_filter_set(ctx->flb, ctx->f_ffd,
+ "rename", "A3 a3",
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ /* Prepare output callback with expected result */
+ cb_data.cb = cb_check_result;
+ cb_data.data = "\"k1\":\"sample1\",\"k2\":\"sample2\",\"a3\":\"sample3\"";
+ /* Start the engine */
+ ret = flb_start(ctx->flb);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data samples */
+ p = "[0,{\"k1\":\"sample1\",\"k2\":\"sample2\",\"A3\":\"sample3\"}]";
+ len = strlen(p);
+ bytes = flb_lib_push(ctx->flb, ctx->i_ffd, p, len);
+ TEST_CHECK(bytes == len);
+
+ filter_test_destroy(ctx);
+}
+
+/*
+ * Operation: HARD_RENAME / Try to rename Key where 'renamed' key already
+ * exists: do nothing.
+ */
+static void flb_test_op_hard_rename_exists()
+{
+ int len;
+ int ret;
+ int bytes;
+ char *p;
+ struct flb_lib_out_cb cb_data;
+ struct filter_test *ctx;
+
+ /* Create test context */
+ ctx = filter_test_create((void *) &cb_data);
+ if (!ctx) {
+ exit(EXIT_FAILURE);
+ }
+
+ /* Configure filter */
+ ret = flb_filter_set(ctx->flb, ctx->f_ffd,
+ "hard_rename", "k2 k1",
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ /* Prepare output callback with expected result */
+ cb_data.cb = cb_check_result;
+ cb_data.data = "\"k1\":\"sample2\"";
+
+ /* Start the engine */
+ ret = flb_start(ctx->flb);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data samples */
+ p = "[0,{\"k1\":\"sample1\",\"k2\":\"sample2\"}]";
+ len = strlen(p);
+ bytes = flb_lib_push(ctx->flb, ctx->i_ffd, p, len);
+ TEST_CHECK(bytes == len);
+
+ filter_test_destroy(ctx);
+}
+
+/* Operation: HARD_RENAME / Rename when key DON'T exists */
+static void flb_test_op_hard_rename_no_exists()
+{
+ int len;
+ int ret;
+ int bytes;
+ char *p;
+ struct flb_lib_out_cb cb_data;
+ struct filter_test *ctx;
+
+ /* Create test context */
+ ctx = filter_test_create((void *) &cb_data);
+ if (!ctx) {
+ exit(EXIT_FAILURE);
+ }
+
+ /* Configure filter */
+ ret = flb_filter_set(ctx->flb, ctx->f_ffd,
+ "hard_rename", "k2 k3",
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ /* Prepare output callback with expected result */
+ cb_data.cb = cb_check_result;
+ cb_data.data = "\"k3\":\"sample2\"";
+
+ /* Start the engine */
+ ret = flb_start(ctx->flb);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data samples */
+ p = "[0,{\"k1\":\"sample1\",\"k2\":\"sample2\"}]";
+ len = strlen(p);
+ bytes = flb_lib_push(ctx->flb, ctx->i_ffd, p, len);
+ TEST_CHECK(bytes == len);
+
+ filter_test_destroy(ctx);
+}
+
+/* Operation: COPY / Target key already exists, do nothing */
+static void flb_test_op_copy_exists()
+{
+ int len;
+ int ret;
+ int bytes;
+ char *p;
+ struct flb_lib_out_cb cb_data;
+ struct filter_test *ctx;
+
+ /* Create test context */
+ ctx = filter_test_create((void *) &cb_data);
+ if (!ctx) {
+ exit(EXIT_FAILURE);
+ }
+
+ /* Configure filter */
+ ret = flb_filter_set(ctx->flb, ctx->f_ffd,
+ "copy", "k1 k2",
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ /* Prepare output callback with expected result */
+ cb_data.cb = cb_check_result;
+ cb_data.data = "\"k1\":\"sample1\",\"k2\":\"sample2\"";
+
+ /* Start the engine */
+ ret = flb_start(ctx->flb);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data samples */
+ p = "[0,{\"k1\":\"sample1\",\"k2\":\"sample2\"}]";
+ len = strlen(p);
+ bytes = flb_lib_push(ctx->flb, ctx->i_ffd, p, len);
+ TEST_CHECK(bytes == len);
+
+ filter_test_destroy(ctx);
+}
+
+/* Operation: COPY / Target key no exists, make a copy */
+static void flb_test_op_copy_no_exists()
+{
+ int len;
+ int ret;
+ int bytes;
+ char *p;
+ struct flb_lib_out_cb cb_data;
+ struct filter_test *ctx;
+
+ /* Create test context */
+ ctx = filter_test_create((void *) &cb_data);
+ if (!ctx) {
+ exit(EXIT_FAILURE);
+ }
+
+ /* Configure filter */
+ ret = flb_filter_set(ctx->flb, ctx->f_ffd,
+ "copy", "k1 k3",
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ /* Prepare output callback with expected result */
+ cb_data.cb = cb_check_result;
+ cb_data.data = "\"k3\":\"sample1\"";
+
+ /* Start the engine */
+ ret = flb_start(ctx->flb);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data samples */
+ p = "[0,{\"k1\":\"sample1\",\"k2\":\"sample2\"}]";
+ len = strlen(p);
+ bytes = flb_lib_push(ctx->flb, ctx->i_ffd, p, len);
+ TEST_CHECK(bytes == len);
+
+ filter_test_destroy(ctx);
+}
+
+/* Operation: HARD_COPY / if target key exists, replace value */
+static void flb_test_op_hard_copy_exists()
+{
+ int len;
+ int ret;
+ int bytes;
+ char *p;
+ struct flb_lib_out_cb cb_data;
+ struct filter_test *ctx;
+
+ /* Create test context */
+ ctx = filter_test_create((void *) &cb_data);
+ if (!ctx) {
+ exit(EXIT_FAILURE);
+ }
+
+ /* Configure filter */
+ ret = flb_filter_set(ctx->flb, ctx->f_ffd,
+ "hard_copy", "k1 k2",
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ /* Prepare output callback with expected result */
+ cb_data.cb = cb_check_result;
+ cb_data.data = "\"k1\":\"sample1\",\"k2\":\"sample1\"";
+
+ /* Start the engine */
+ ret = flb_start(ctx->flb);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data samples */
+ p = "[0, {\"k1\":\"sample1\",\"k2\":\"sample2\"}]";
+ len = strlen(p);
+ bytes = flb_lib_push(ctx->flb, ctx->i_ffd, p, len);
+ TEST_CHECK(bytes == len);
+
+ filter_test_destroy(ctx);
+}
+
+/* Operation: HARD_COPY / if key don't exists make a copy */
+static void flb_test_op_hard_copy_no_exists()
+{
+ int len;
+ int ret;
+ int bytes;
+ char *p;
+ struct flb_lib_out_cb cb_data;
+ struct filter_test *ctx;
+
+ /* Create test context */
+ ctx = filter_test_create((void *) &cb_data);
+ if (!ctx) {
+ exit(EXIT_FAILURE);
+ }
+
+ /* Configure filter */
+ ret = flb_filter_set(ctx->flb, ctx->f_ffd,
+ "hard_copy", "k1 k3",
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ /* Prepare output callback with expected result */
+ cb_data.cb = cb_check_result;
+ cb_data.data = "\"k3\":\"sample1\"";
+
+ /* Start the engine */
+ ret = flb_start(ctx->flb);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data samples */
+ p = "[0,{\"k1\":\"sample1\",\"k2\":\"sample2\"}]";
+ len = strlen(p);
+ bytes = flb_lib_push(ctx->flb, ctx->i_ffd, p, len);
+ TEST_CHECK(bytes == len);
+
+ filter_test_destroy(ctx);
+}
+
+
+/* Condition: KEY_EXISTS / If key exists, make a copy */
+static void flb_test_cond_key_exists()
+{
+ int len;
+ int ret;
+ int bytes;
+ char *p;
+ struct flb_lib_out_cb cb_data;
+ struct filter_test *ctx;
+
+ /* Create test context */
+ ctx = filter_test_create((void *) &cb_data);
+ if (!ctx) {
+ exit(EXIT_FAILURE);
+ }
+
+ /* Configure filter */
+ ret = flb_filter_set(ctx->flb, ctx->f_ffd,
+ "condition", "key_exists k1",
+ "copy", "k1 k3_copy",
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ /* Prepare output callback with expected result */
+ cb_data.cb = cb_check_result;
+ cb_data.data = "\"k3_copy\":\"sample1\"";
+
+ /* Start the engine */
+ ret = flb_start(ctx->flb);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data samples */
+ p = "[0,{\"k1\":\"sample1\",\"k2\":\"sample2\"}]";
+ len = strlen(p);
+ bytes = flb_lib_push(ctx->flb, ctx->i_ffd, p, len);
+ TEST_CHECK(bytes == len);
+
+ filter_test_destroy(ctx);
+}
+
+/* Condition: KEY_EXISTS / If nested key exists, make a copy */
+static void flb_test_cond_key_exists_nest()
+{
+ int len;
+ int ret;
+ int bytes;
+ char *p;
+ struct flb_lib_out_cb cb_data;
+ struct filter_test *ctx;
+
+ /* Create test context */
+ ctx = filter_test_create((void *) &cb_data);
+ if (!ctx) {
+ exit(EXIT_FAILURE);
+ }
+
+ /* Configure filter */
+ ret = flb_filter_set(ctx->flb, ctx->f_ffd,
+ "condition", "key_exists $nest['k1']",
+ "add", "key found",
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ /* Prepare output callback with expected result */
+ cb_data.cb = cb_check_result;
+ cb_data.data = "\"key\":\"found\"";
+
+ /* Start the engine */
+ ret = flb_start(ctx->flb);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data samples */
+ p = "[0,{\"k1\":\"sample1\",\"k2\":\"sample2\", \"nest\":{\"k1\":\"nest\"}}]";
+ len = strlen(p);
+ bytes = flb_lib_push(ctx->flb, ctx->i_ffd, p, len);
+ TEST_CHECK(bytes == len);
+
+ filter_test_destroy(ctx);
+}
+
+/* Condition: KEY_DOES_NOT_EXISTS / If key does not exists, add a dummy key */
+static void flb_test_cond_key_does_not_exist()
+{
+ int len;
+ int ret;
+ int bytes;
+ char *p;
+ struct flb_lib_out_cb cb_data;
+ struct filter_test *ctx;
+
+ /* Create test context */
+ ctx = filter_test_create((void *) &cb_data);
+ if (!ctx) {
+ exit(EXIT_FAILURE);
+ }
+
+ /* Configure filter */
+ ret = flb_filter_set(ctx->flb, ctx->f_ffd,
+ "condition", "key_does_not_exist k3",
+ "copy", "k1 k3",
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ /* Prepare output callback with expected result */
+ cb_data.cb = cb_check_result;
+ cb_data.data = "\"k3\":\"sample1\"";
+
+ /* Start the engine */
+ ret = flb_start(ctx->flb);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data samples */
+ p = "[0,{\"k1\":\"sample1\",\"k2\":\"sample2\"}]";
+ len = strlen(p);
+ bytes = flb_lib_push(ctx->flb, ctx->i_ffd, p, len);
+ TEST_CHECK(bytes == len);
+
+ filter_test_destroy(ctx);
+}
+
+/* Condition: KEY_DOES_NOT_EXISTS / If key does not exists, add a dummy key */
+static void flb_test_cond_key_does_not_exist_nest()
+{
+ int len;
+ int ret;
+ int bytes;
+ char *p;
+ struct flb_lib_out_cb cb_data;
+ struct filter_test *ctx;
+
+ /* Create test context */
+ ctx = filter_test_create((void *) &cb_data);
+ if (!ctx) {
+ exit(EXIT_FAILURE);
+ }
+
+ /* Configure filter */
+ ret = flb_filter_set(ctx->flb, ctx->f_ffd,
+ "condition", "key_does_not_exist $nest['k1']",
+ "add", "key not_found",
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ /* Prepare output callback with expected result */
+ cb_data.cb = cb_check_result;
+ cb_data.data = "\"key\":\"not_found\"";
+
+ /* Start the engine */
+ ret = flb_start(ctx->flb);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data samples */
+ p = "[0,{\"k1\":\"sample1\",\"k2\":\"sample2\", \"nest\":{\"k\":\"sample\"}}]";
+ len = strlen(p);
+ bytes = flb_lib_push(ctx->flb, ctx->i_ffd, p, len);
+ TEST_CHECK(bytes == len);
+
+ filter_test_destroy(ctx);
+}
+
+/* Condition: A_KEY_MATCHES / If key matches, add a dummy key */
+static void flb_test_cond_a_key_matches()
+{
+ int len;
+ int ret;
+ int bytes;
+ char *p;
+ struct flb_lib_out_cb cb_data;
+ struct filter_test *ctx;
+
+ /* Create test context */
+ ctx = filter_test_create((void *) &cb_data);
+ if (!ctx) {
+ exit(EXIT_FAILURE);
+ }
+
+ /* Configure filter */
+ ret = flb_filter_set(ctx->flb, ctx->f_ffd,
+ "condition", "a_key_matches ^[a-z][0-9]$",
+ "copy", "c1 c1_matched",
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ /* Prepare output callback with expected result */
+ cb_data.cb = cb_check_result;
+ cb_data.data = "\"c1_matched\":\"sample3\"";
+
+ /* Start the engine */
+ ret = flb_start(ctx->flb);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data samples */
+ p = "[0,{\"aa\":\"sample1\",\"bb\":\"sample2\",\"c1\":\"sample3\"}]";
+ len = strlen(p);
+ bytes = flb_lib_push(ctx->flb, ctx->i_ffd, p, len);
+ TEST_CHECK(bytes == len);
+
+ filter_test_destroy(ctx);
+}
+
+/* Condition: NO_KEY_MATCHES / If no key matches, add a dummy key */
+static void flb_test_cond_no_key_matches()
+{
+ int len;
+ int ret;
+ int bytes;
+ char *p;
+ struct flb_lib_out_cb cb_data;
+ struct filter_test *ctx;
+
+ /* Create test context */
+ ctx = filter_test_create((void *) &cb_data);
+ if (!ctx) {
+ exit(EXIT_FAILURE);
+ }
+
+ /* Configure filter */
+ ret = flb_filter_set(ctx->flb, ctx->f_ffd,
+ "condition", "no_key_matches ^[0-9]$",
+ "copy", "c1 no_matches",
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ /* Prepare output callback with expected result */
+ cb_data.cb = cb_check_result;
+ cb_data.data = "\"no_matches\":\"sample3\"";
+
+ /* Start the engine */
+ ret = flb_start(ctx->flb);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data samples */
+ p = "[0,{\"aa\":\"sample1\",\"bb\":\"sample2\",\"c1\":\"sample3\"}]";
+ len = strlen(p);
+ bytes = flb_lib_push(ctx->flb, ctx->i_ffd, p, len);
+ TEST_CHECK(bytes == len);
+
+ filter_test_destroy(ctx);
+}
+
+/* Condition: KEY_VALUE_EQUALS / If key value matches, add a dummy key */
+static void flb_test_cond_key_value_equals()
+{
+ int len;
+ int ret;
+ int bytes;
+ char *p;
+ struct flb_lib_out_cb cb_data;
+ struct filter_test *ctx;
+
+ /* Create test context */
+ ctx = filter_test_create((void *) &cb_data);
+ if (!ctx) {
+ exit(EXIT_FAILURE);
+ }
+
+ /* Configure filter */
+ ret = flb_filter_set(ctx->flb, ctx->f_ffd,
+ "condition", "key_value_equals bb sample2",
+ "copy", "c1 no_matches",
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ /* Prepare output callback with expected result */
+ cb_data.cb = cb_check_result;
+ cb_data.data = "\"no_matches\":\"sample3\"";
+
+ /* Start the engine */
+ ret = flb_start(ctx->flb);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data samples */
+ p = "[0,{\"aa\":\"sample1\",\"bb\":\"sample2\",\"c1\":\"sample3\"}]";
+ len = strlen(p);
+ bytes = flb_lib_push(ctx->flb, ctx->i_ffd, p, len);
+ TEST_CHECK(bytes == len);
+
+ filter_test_destroy(ctx);
+}
+
+/* Condition: KEY_VALUE_EQUALS / If key value matches, add a dummy key */
+static void flb_test_cond_key_value_equals_nest()
+{
+ int len;
+ int ret;
+ int bytes;
+ char *p;
+ struct flb_lib_out_cb cb_data;
+ struct filter_test *ctx;
+
+ /* Create test context */
+ ctx = filter_test_create((void *) &cb_data);
+ if (!ctx) {
+ exit(EXIT_FAILURE);
+ }
+
+ /* Configure filter */
+ ret = flb_filter_set(ctx->flb, ctx->f_ffd,
+ "condition", "key_value_equals $nest['k1'] sample2",
+ "add", "key found",
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ /* Prepare output callback with expected result */
+ cb_data.cb = cb_check_result;
+ cb_data.data = "\"key\":\"found\"";
+
+ /* Start the engine */
+ ret = flb_start(ctx->flb);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data samples */
+ p = "[0,{\"aa\":\"sample1\",\"bb\":\"sample2\",\"c1\":\"sample3\", \"nest\":{\"k1\":\"sample2\"}}]";
+ len = strlen(p);
+ bytes = flb_lib_push(ctx->flb, ctx->i_ffd, p, len);
+ TEST_CHECK(bytes == len);
+
+ filter_test_destroy(ctx);
+}
+
+/* Condition: KEY_VALUE_DOES_NOT_EQUAL / If key value mismatch, add a key */
+static void flb_test_cond_key_value_does_not_equal()
+{
+ int len;
+ int ret;
+ int bytes;
+ char *p;
+ struct flb_lib_out_cb cb_data;
+ struct filter_test *ctx;
+
+ /* Create test context */
+ ctx = filter_test_create((void *) &cb_data);
+ if (!ctx) {
+ exit(EXIT_FAILURE);
+ }
+
+ /* Configure filter */
+ ret = flb_filter_set(ctx->flb, ctx->f_ffd,
+ "condition", "key_value_does_not_equal bb sample3",
+ "copy", "c1 no_matches",
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ /* Prepare output callback with expected result */
+ cb_data.cb = cb_check_result;
+ cb_data.data = "\"no_matches\":\"sample3\"";
+
+ /* Start the engine */
+ ret = flb_start(ctx->flb);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data samples */
+ p = "[0,{\"aa\":\"sample1\",\"bb\":\"sample2\",\"c1\":\"sample3\"}]";
+ len = strlen(p);
+ bytes = flb_lib_push(ctx->flb, ctx->i_ffd, p, len);
+ TEST_CHECK(bytes == len);
+
+ filter_test_destroy(ctx);
+}
+
+/* Condition: KEY_VALUE_DOES_NOT_EQUAL / If key value mismatch, add a key */
+static void flb_test_cond_key_value_does_not_equal_nest()
+{
+ int len;
+ int ret;
+ int bytes;
+ char *p;
+ struct flb_lib_out_cb cb_data;
+ struct filter_test *ctx;
+
+ /* Create test context */
+ ctx = filter_test_create((void *) &cb_data);
+ if (!ctx) {
+ exit(EXIT_FAILURE);
+ }
+
+ /* Configure filter */
+ ret = flb_filter_set(ctx->flb, ctx->f_ffd,
+ "condition", "key_value_does_not_equal $nest['k1'] sample2",
+ "add", "key not_found",
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ /* Prepare output callback with expected result */
+ cb_data.cb = cb_check_result;
+ cb_data.data = "\"key\":\"not_found\"";
+
+ /* Start the engine */
+ ret = flb_start(ctx->flb);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data samples */
+ p = "[0,{\"aa\":\"sample1\",\"bb\":\"sample2\",\"c1\":\"sample3\", \"nest\":{\"k1\":\"sample3\"}}]";
+ len = strlen(p);
+ bytes = flb_lib_push(ctx->flb, ctx->i_ffd, p, len);
+ TEST_CHECK(bytes == len);
+
+ filter_test_destroy(ctx);
+}
+
+/* Condition: KEY_VALUE_MATCHES / If key match, add a key */
+static void flb_test_cond_key_value_matches()
+{
+ int len;
+ int ret;
+ int bytes;
+ char *p;
+ struct flb_lib_out_cb cb_data;
+ struct filter_test *ctx;
+
+ /* Create test context */
+ ctx = filter_test_create((void *) &cb_data);
+ if (!ctx) {
+ exit(EXIT_FAILURE);
+ }
+
+ /* Configure filter */
+ ret = flb_filter_set(ctx->flb, ctx->f_ffd,
+ "condition", "key_value_matches k2 ^[a-z][0-9]$",
+ "copy", "k1 matches",
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ /* Prepare output callback with expected result */
+ cb_data.cb = cb_check_result;
+ cb_data.data = "\"matches\":\"sample1\"";
+
+ /* Start the engine */
+ ret = flb_start(ctx->flb);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data samples */
+ p = "[0,{\"k1\":\"sample1\",\"k2\":\"z2\"}]";
+ len = strlen(p);
+ bytes = flb_lib_push(ctx->flb, ctx->i_ffd, p, len);
+ TEST_CHECK(bytes == len);
+
+ filter_test_destroy(ctx);
+}
+
+/* Condition: KEY_VALUE_MATCHES / If key match, add a key */
+static void flb_test_cond_key_value_matches_nest()
+{
+ int len;
+ int ret;
+ int bytes;
+ char *p;
+ struct flb_lib_out_cb cb_data;
+ struct filter_test *ctx;
+
+ /* Create test context */
+ ctx = filter_test_create((void *) &cb_data);
+ if (!ctx) {
+ exit(EXIT_FAILURE);
+ }
+
+ /* Configure filter */
+ ret = flb_filter_set(ctx->flb, ctx->f_ffd,
+ "condition", "key_value_matches $nest['k2'] ^[a-z][0-9]$",
+ "add", "kv matches",
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ /* Prepare output callback with expected result */
+ cb_data.cb = cb_check_result;
+ cb_data.data = "\"kv\":\"matches\"";
+
+ /* Start the engine */
+ ret = flb_start(ctx->flb);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data samples */
+ p = "[0,{\"k1\":\"sample1\",\"k2\":\"z2\", \"nest\":{\"k2\":\"z2\"}}]";
+ len = strlen(p);
+ bytes = flb_lib_push(ctx->flb, ctx->i_ffd, p, len);
+ TEST_CHECK(bytes == len);
+
+ filter_test_destroy(ctx);
+}
+
+/* Condition: KEY_VALUE_DOES_NOT_MATCH / If key mismatch, add a key */
+static void flb_test_cond_key_value_does_not_match()
+{
+ int len;
+ int ret;
+ int bytes;
+ char *p;
+ struct flb_lib_out_cb cb_data;
+ struct filter_test *ctx;
+
+ /* Create test context */
+ ctx = filter_test_create((void *) &cb_data);
+ if (!ctx) {
+ exit(EXIT_FAILURE);
+ }
+
+ /* Configure filter */
+ ret = flb_filter_set(ctx->flb, ctx->f_ffd,
+ "condition", "key_value_does_not_match k2 ^[a-z][0-9]$",
+ "copy", "k1 no_matches",
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ /* Prepare output callback with expected result */
+ cb_data.cb = cb_check_result;
+ cb_data.data = "\"no_matches\":\"sample1\"";
+
+ /* Start the engine */
+ ret = flb_start(ctx->flb);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data samples */
+ p = "[0,{\"k1\":\"sample1\",\"k2\":\"22\"}]";
+ len = strlen(p);
+ bytes = flb_lib_push(ctx->flb, ctx->i_ffd, p, len);
+ TEST_CHECK(bytes == len);
+
+ filter_test_destroy(ctx);
+}
+
+/* Condition: KEY_VALUE_DOES_NOT_MATCH / If key mismatch, add a key */
+static void flb_test_cond_key_value_does_not_match_nest()
+{
+ int len;
+ int ret;
+ int bytes;
+ char *p;
+ struct flb_lib_out_cb cb_data;
+ struct filter_test *ctx;
+
+ /* Create test context */
+ ctx = filter_test_create((void *) &cb_data);
+ if (!ctx) {
+ exit(EXIT_FAILURE);
+ }
+
+ /* Configure filter */
+ ret = flb_filter_set(ctx->flb, ctx->f_ffd,
+ "condition", "key_value_does_not_match $nest['k2'] ^[a-z][0-9]$",
+ "add", "kv no_matches",
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ /* Prepare output callback with expected result */
+ cb_data.cb = cb_check_result;
+ cb_data.data = "\"kv\":\"no_matches\"";
+
+ /* Start the engine */
+ ret = flb_start(ctx->flb);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data samples */
+ p = "[0,{\"k1\":\"sample1\",\"k2\":\"22\",\"nest\":{\"k2\":\"22\"}}]";
+ len = strlen(p);
+ bytes = flb_lib_push(ctx->flb, ctx->i_ffd, p, len);
+ TEST_CHECK(bytes == len);
+
+ filter_test_destroy(ctx);
+}
+
+/* Condition: MATCHING_KEYS_HAVE_MATCHING_VALUES / If key match, add a key */
+static void flb_test_cond_matching_keys_have_matching_values()
+{
+ int len;
+ int ret;
+ int bytes;
+ char *p;
+ struct flb_lib_out_cb cb_data;
+ struct filter_test *ctx;
+
+ /* Create test context */
+ ctx = filter_test_create((void *) &cb_data);
+ if (!ctx) {
+ exit(EXIT_FAILURE);
+ }
+
+ /* Configure filter */
+ ret = flb_filter_set(ctx->flb, ctx->f_ffd,
+ "condition",
+ "matching_keys_have_matching_values "\
+ "^[a-z][0-9]$ ^[a-z][0-9]$",
+ "copy", "k1 matches",
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ /* Prepare output callback with expected result */
+ cb_data.cb = cb_check_result;
+ cb_data.data = "\"matches\":\"n1\"";
+
+ /* Start the engine */
+ ret = flb_start(ctx->flb);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data samples */
+ p = "[0,{\"k1\":\"n1\",\"k2\":\"n3\"}]";
+ len = strlen(p);
+ bytes = flb_lib_push(ctx->flb, ctx->i_ffd, p, len);
+ TEST_CHECK(bytes == len);
+
+ filter_test_destroy(ctx);
+}
+
+/* Condition: MATCHING_KEYS_DOES_NOT_HAVE_MATCHING_VALUES */
+static void flb_test_cond_matching_keys_do_not_have_matching_values()
+{
+ int len;
+ int ret;
+ int bytes;
+ char *p;
+ struct flb_lib_out_cb cb_data;
+ struct filter_test *ctx;
+
+ /* Create test context */
+ ctx = filter_test_create((void *) &cb_data);
+ if (!ctx) {
+ exit(EXIT_FAILURE);
+ }
+
+ /* Configure filter */
+ ret = flb_filter_set(ctx->flb, ctx->f_ffd,
+ "condition",
+ "matching_keys_do_not_have_matching_values "\
+ "^[a-z][0-9]$ ^[a-z][0-9]$",
+ "copy", "k1 no_matches",
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ /* Prepare output callback with expected result */
+ cb_data.cb = cb_check_result;
+ cb_data.data = "\"no_matches\":\"aa\"";
+
+ /* Start the engine */
+ ret = flb_start(ctx->flb);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data samples */
+ p = "[0,{\"k1\":\"aa\",\"k2\":\"bb\"}]";
+ len = strlen(p);
+ bytes = flb_lib_push(ctx->flb, ctx->i_ffd, p, len);
+ TEST_CHECK(bytes == len);
+
+ filter_test_destroy(ctx);
+}
+
+/* Test all operations */
+static void flb_test_cond_chain()
+{
+ int len;
+ int ret;
+ int bytes;
+ char *p;
+ struct flb_lib_out_cb cb_data;
+ struct filter_test *ctx;
+
+ /* Create test context */
+ ctx = filter_test_create((void *) &cb_data);
+ if (!ctx) {
+ exit(EXIT_FAILURE);
+ }
+
+ /* Configure filter */
+ ret = flb_filter_set(ctx->flb, ctx->f_ffd,
+ "condition", "key_exists k1",
+ "condition", "key_does_not_exist k2",
+ "add", "k2 sample_2",
+ "condition", "a_key_matches ^[a-z]1$",
+ "add", "k3 3",
+ "condition", "no_key_matches ^[0-9]$",
+ "condition", "key_value_equals k1 sample",
+ "add", "k4 4",
+ "condition", "key_value_does_not_equal k1 sampl",
+ "condition", "key_value_matches k1 ^[a-z]+$",
+ "condition", "key_value_does_not_match k1 aa",
+
+ "condition",
+ "matching_keys_have_matching_values " \
+ "^[a-z][0-9]$ ^[a-z]+$",
+
+ "condition",
+ "matching_keys_do_not_have_matching_values " \
+ "^[a-z][0-9]$ ^[a-z][0-9]$",
+
+ "add", "k5 5",
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ /* Prepare output callback with expected result */
+ cb_data.cb = cb_check_result;
+ cb_data.data = "\"k5\":\"5\"";
+
+ /* Start the engine */
+ ret = flb_start(ctx->flb);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data samples */
+ p = "[0,{\"k1\":\"sample\"}]";
+ len = strlen(p);
+ bytes = flb_lib_push(ctx->flb, ctx->i_ffd, p, len);
+ TEST_CHECK(bytes == len);
+
+ filter_test_destroy(ctx);
+}
+
+
+pthread_mutex_t result_mutex = PTHREAD_MUTEX_INITIALIZER;
+int num_output = 0;
+
+
+static void add_output_num()
+{
+ pthread_mutex_lock(&result_mutex);
+ num_output++;
+ pthread_mutex_unlock(&result_mutex);
+}
+
+static void clear_output_num()
+{
+ pthread_mutex_lock(&result_mutex);
+ num_output = 0;
+ pthread_mutex_unlock(&result_mutex);
+}
+
+static int get_output_num()
+{
+ int ret;
+ pthread_mutex_lock(&result_mutex);
+ ret = num_output;
+ pthread_mutex_unlock(&result_mutex);
+
+ return ret;
+}
+
+static int callback_count(void* data, size_t size, void* cb_data)
+{
+ if (size > 0) {
+ flb_debug("[test_filter_modify] received message: %s", (char*)data);
+ add_output_num(); /* success */
+ flb_free(data);
+ }
+ return 0;
+}
+
+
+// to check issue https://github.com/fluent/fluent-bit/issues/1077
+static void flb_test_not_drop_multi_event()
+{
+ int count = 0;
+ int expected = 3;
+
+ char *p;
+ int len;
+ int ret;
+ int bytes;
+
+ struct filter_test *ctx;
+ struct flb_lib_out_cb cb_data;
+
+
+ clear_output_num();
+ cb_data.cb = callback_count;
+
+ /* Create test context */
+ ctx = filter_test_create((void *) &cb_data);
+ if (!ctx) {
+ exit(EXIT_FAILURE);
+ }
+
+ /* Configure filter */
+ ret = flb_filter_set(ctx->flb, ctx->f_ffd,
+ "condition", "key_value_equals cond true",
+ "add", "data matched",
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ /* Start the engine */
+ ret = flb_start(ctx->flb);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest multiple events */
+ p = "[0, {\"cond\":\"false\", \"data\": \"something\"}]";
+ len = strlen(p);
+ bytes = flb_lib_push(ctx->flb, ctx->i_ffd, p, len);
+ TEST_CHECK(bytes == len);
+
+ p = "[0, {\"cond\":\"true\", \"data\": \"something\"}]";
+ len = strlen(p);
+ bytes = flb_lib_push(ctx->flb, ctx->i_ffd, p, len);
+ TEST_CHECK(bytes == len);
+
+ p = "[0, {\"data\": \"something\"}]";
+ len = strlen(p);
+ bytes = flb_lib_push(ctx->flb, ctx->i_ffd, p, len);
+ TEST_CHECK(bytes == len);
+
+ sleep(1); /* waiting flush */
+ count = get_output_num();
+
+ TEST_CHECK_(count == expected, "Expected number of events %d, got %d", expected, count );
+
+
+ filter_test_destroy(ctx);
+
+}
+
+/* to check issue https://github.com/fluent/fluent-bit/issues/4319 */
+static void flb_test_issue_4319()
+{
+ char *p;
+ int len;
+ int ret;
+ int bytes;
+
+ struct filter_test *ctx;
+ struct flb_lib_out_cb cb_data;
+
+
+ clear_output_num();
+
+ /* Prepare output callback with expected result */
+ cb_data.cb = cb_check_result;
+ cb_data.data = "\"ok\":\"sample\"";
+
+ /* Create test context */
+ ctx = filter_test_create((void *) &cb_data);
+ if (!ctx) {
+ exit(EXIT_FAILURE);
+ }
+
+ /* Configure filter */
+ ret = flb_filter_set(ctx->flb, ctx->f_ffd,
+ /* set key which doesn't exist */
+ "condition", "key_value_does_not_equal aaa sample",
+ "rename", "ok error",
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ /* Start the engine */
+ ret = flb_start(ctx->flb);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest event */
+ p = "[0, {\"ok\":\"sample\"}]";
+ len = strlen(p);
+ bytes = flb_lib_push(ctx->flb, ctx->i_ffd, p, len);
+ TEST_CHECK(bytes == len);
+
+ sleep(1); /* waiting flush */
+
+ filter_test_destroy(ctx);
+}
+
+
+/*
+ * to check issue https://github.com/fluent/fluent-bit/issues/4319
+ Key_value_does_not_match case
+*/
+static void flb_test_issue_4319_2()
+{
+ char *p;
+ int len;
+ int ret;
+ int bytes;
+
+ struct filter_test *ctx;
+ struct flb_lib_out_cb cb_data;
+
+
+ clear_output_num();
+
+ /* Prepare output callback with expected result */
+ cb_data.cb = cb_check_result;
+ cb_data.data = "\"ok\":\"sample\"";
+
+ /* Create test context */
+ ctx = filter_test_create((void *) &cb_data);
+ if (!ctx) {
+ exit(EXIT_FAILURE);
+ }
+
+ /* Configure filter */
+ ret = flb_filter_set(ctx->flb, ctx->f_ffd,
+ /* set key which doesn't exist */
+ "condition", "key_value_does_not_match aaa sample",
+ "rename", "ok error",
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ /* Start the engine */
+ ret = flb_start(ctx->flb);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest event */
+ p = "[0, {\"ok\":\"sample\"}]";
+ len = strlen(p);
+ bytes = flb_lib_push(ctx->flb, ctx->i_ffd, p, len);
+ TEST_CHECK(bytes == len);
+
+ sleep(1); /* waiting flush */
+
+ filter_test_destroy(ctx);
+}
+
+/* https://github.com/fluent/fluent-bit/issues/1225 */
+static void flb_test_issue_1225()
+{
+ int len;
+ int ret;
+ int bytes;
+ char *p;
+ struct flb_lib_out_cb cb_data;
+ struct filter_test *ctx;
+
+ /* Create test context */
+ ctx = filter_test_create((void *) &cb_data);
+ if (!ctx) {
+ exit(EXIT_FAILURE);
+ }
+
+ /* Configure filter */
+ ret = flb_filter_set(ctx->flb, ctx->f_ffd,
+ "condition", "key_value_matches \"key 1\" \".*with spaces.*\"",
+ "add", "\"key 2\" \"second value with spaces\"",
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ /* Prepare output callback with expected result */
+ cb_data.cb = cb_check_result;
+ cb_data.data = "\"key 1\":\"first value with spaces\","\
+ "\"key 2\":\"second value with spaces\"";
+
+ /* Start the engine */
+ ret = flb_start(ctx->flb);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data samples */
+ p = "[0,{\"key 1\":\"first value with spaces\"}]";
+ len = strlen(p);
+ bytes = flb_lib_push(ctx->flb, ctx->i_ffd, p, len);
+ TEST_CHECK(bytes == len);
+
+ filter_test_destroy(ctx);
+}
+
+
+/*
+ * to check issue https://github.com/fluent/fluent-bit/issues/7075
+*/
+static void flb_test_issue_7075()
+{
+ char *p;
+ int len;
+ int ret;
+ int bytes;
+
+ struct filter_test *ctx;
+ struct flb_lib_out_cb cb_data;
+
+ clear_output_num();
+
+ /* Prepare output callback with expected result */
+ cb_data.cb = cb_check_result;
+ cb_data.data = "\"matched\":true";
+
+ /* Create test context */
+ ctx = filter_test_create((void *) &cb_data);
+ if (!ctx) {
+ exit(EXIT_FAILURE);
+ }
+
+ /* Configure filter */
+ ret = flb_filter_set(ctx->flb, ctx->f_ffd,
+ /* set key which doesn't exist */
+ "condition", "key_value_matches ok true",
+ "rename", "ok matched",
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ /* Start the engine */
+ ret = flb_start(ctx->flb);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest event */
+ p = "[0, {\"ok\":true}]";
+ len = strlen(p);
+ bytes = flb_lib_push(ctx->flb, ctx->i_ffd, p, len);
+ TEST_CHECK(bytes == len);
+
+ sleep(1); /* waiting flush */
+
+ filter_test_destroy(ctx);
+}
+
+static void flb_test_issue_7368()
+{
+ int ret;
+ struct flb_lib_out_cb cb_data;
+ struct filter_test *ctx;
+
+ /* Create test context */
+ ctx = filter_test_create((void *) &cb_data);
+ if (!ctx) {
+ exit(EXIT_FAILURE);
+ }
+
+ /* Configure filter */
+ ret = flb_filter_set(ctx->flb, ctx->f_ffd,
+ "remove_wildcard", "*s3",
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ /* Prepare output callback with expected result */
+ cb_data.cb = cb_check_result;
+ cb_data.data = "\"r1\":\"someval\"";
+
+ /* Start the engine */
+ ret = flb_start(ctx->flb);
+ TEST_CHECK(ret != 0);
+
+ filter_test_destroy(ctx);
+}
+
+TEST_LIST = {
+ /* Operations / Commands */
+ {"op_set_append" , flb_test_op_set_append },
+ {"op_set_replace" , flb_test_op_set_replace },
+ {"op_remove" , flb_test_op_remove },
+ {"op_remove_wildcard" , flb_test_op_remove_wildcard },
+ {"op_remove_regex" , flb_test_op_remove_regex },
+ {"op_rename_exists" , flb_test_op_rename_exists },
+ {"op_rename_no_exists" , flb_test_op_rename_no_exists },
+ {"op_hard_rename_exists" , flb_test_op_hard_rename_exists },
+ {"op_hard_rename_no_exists" , flb_test_op_hard_rename_no_exists },
+ {"op_copy_exists" , flb_test_op_copy_exists },
+ {"op_copy_no_exists" , flb_test_op_copy_no_exists },
+ {"op_hard_copy_exists" , flb_test_op_hard_copy_exists },
+ {"op_hard_copy_no_exists" , flb_test_op_hard_copy_no_exists },
+
+ /* Conditions (nested) */
+ {"cond_key_value_matches_nest", flb_test_cond_key_value_matches_nest },
+ {"cond_key_value_does_not_match_nest", flb_test_cond_key_value_does_not_match_nest },
+ {"cond_key_exists_nest", flb_test_cond_key_exists_nest },
+ {"cond_key_does_not_exist_nest", flb_test_cond_key_does_not_exist_nest },
+ {"cond_key_value_equals_nest", flb_test_cond_key_value_equals_nest },
+ {"cond_key_value_does_not_equal_nest", flb_test_cond_key_value_does_not_equal_nest },
+
+ /* Conditions */
+ {"cond_key_exists", flb_test_cond_key_exists },
+ {"cond_key_does_not_exist", flb_test_cond_key_does_not_exist },
+ {"cond_a_key_matches", flb_test_cond_a_key_matches },
+ {"cond_no_key_matches", flb_test_cond_no_key_matches },
+ {"cond_key_value_equals", flb_test_cond_key_value_equals },
+ {"cond_key_value_does_not_equal", flb_test_cond_key_value_does_not_equal },
+ {"cond_key_value_matches", flb_test_cond_key_value_matches },
+ {"cond_key_value_does_not_match", flb_test_cond_key_value_does_not_match },
+ {"cond_matching_keys_have_matching_values",
+ flb_test_cond_matching_keys_have_matching_values },
+ {"cond_matching_keys_do_not_have_matching_values",
+ flb_test_cond_matching_keys_do_not_have_matching_values },
+ {"cond_chain", flb_test_cond_chain },
+
+ /* Bug fixes */
+ {"multiple events are not dropped", flb_test_not_drop_multi_event },
+ {"cond_key_value_does_not_equal and key does not exist", flb_test_issue_4319 },
+ {"cond_key_value_does_not_matches and key does not exist", flb_test_issue_4319_2 },
+ {"Key_value_matches and value is bool type", flb_test_issue_7075},
+ {"operation_with_whitespace", flb_test_issue_1225 },
+ {"invalid_wildcard", flb_test_issue_7368 },
+
+ {NULL, NULL}
+};
diff --git a/fluent-bit/tests/runtime/filter_multiline.c b/fluent-bit/tests/runtime/filter_multiline.c
new file mode 100644
index 00000000..18253a5b
--- /dev/null
+++ b/fluent-bit/tests/runtime/filter_multiline.c
@@ -0,0 +1,699 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+#include <fluent-bit.h>
+#include <fluent-bit/flb_sds.h>
+#include "flb_tests_runtime.h"
+
+struct filter_test {
+ flb_ctx_t *flb; /* Fluent Bit library context */
+ int i_ffd; /* Input fd */
+ int f_ffd; /* Filter fd */
+};
+
+struct filter_test_result {
+ char *expected_pattern; /* string that must occur in output */
+ int expected_pattern_index; /* which record to check for the pattern */
+ int expected_records; /* expected number of outputted records */
+ int actual_records; /* actual number of outputted records */
+};
+
+/* Callback to check expected results */
+static int cb_check_result(void *record, size_t size, void *data)
+{
+ char *p;
+ struct filter_test_result *expected;
+ char *result;
+
+ expected = (struct filter_test_result *) data;
+ result = (char *) record;
+
+ if (expected->expected_pattern_index == expected->actual_records) {
+ p = strstr(result, expected->expected_pattern);
+ TEST_CHECK(p != NULL);
+
+ if (!p) {
+ flb_error("Expected to find: '%s' in result '%s'",
+ expected->expected_pattern, result);
+ }
+ /*
+ * If you want to debug your test
+ *
+ * printf("Expect: '%s' in result '%s'\n", expected->expected_pattern, result);
+ */
+ }
+
+ expected->actual_records++;
+
+ flb_free(record);
+ return 0;
+}
+
+
+pthread_mutex_t result_mutex = PTHREAD_MUTEX_INITIALIZER;
+int num_output = 0;
+static int get_output_num()
+{
+ int ret;
+ pthread_mutex_lock(&result_mutex);
+ ret = num_output;
+ pthread_mutex_unlock(&result_mutex);
+
+ return ret;
+}
+
+static void set_output_num(int num)
+{
+ pthread_mutex_lock(&result_mutex);
+ num_output = num;
+ pthread_mutex_unlock(&result_mutex);
+}
+
+static void clear_output_num()
+{
+ set_output_num(0);
+}
+
+struct str_list {
+ size_t size; /* size of lists */
+ int ignore_min_line_num; /* ignore line if the length is less than this value */
+ char **lists; /* string lists */
+};
+
+/* Callback to check expected results */
+static int cb_check_str_list(void *record, size_t size, void *data)
+{
+ char *p;
+ char *out_line = record;
+ int num = get_output_num();
+ int count = 0;
+ size_t i;
+ struct str_list *l = (struct str_list *)data;
+
+ if (!TEST_CHECK(out_line != NULL)) {
+ TEST_MSG("out_line is NULL");
+ return -1;
+ }
+
+ if (!TEST_CHECK(l != NULL)) {
+ TEST_MSG("l is NULL");
+ flb_free(out_line);
+ return -1;
+ }
+
+ if (strlen(out_line) < l->ignore_min_line_num) {
+ flb_free(out_line);
+ return 0;
+ }
+
+ for (i=0; i<l->size; i++) {
+ p = strstr(out_line, l->lists[i]);
+ if (p != NULL) {
+ count++;
+ }
+ }
+ if(!TEST_CHECK(count != 0)) {
+ TEST_MSG("%s is not matched", out_line);
+ }
+ set_output_num(num+count);
+
+ flb_free(out_line);
+ return 0;
+}
+
+
+
+static struct filter_test *filter_test_create(struct flb_lib_out_cb *data)
+{
+ int i_ffd;
+ int f_ffd;
+ int o_ffd;
+ struct filter_test *ctx;
+
+ ctx = flb_malloc(sizeof(struct filter_test));
+ if (!ctx) {
+ flb_errno();
+ return NULL;
+ }
+
+ /* Service config */
+ ctx->flb = flb_create();
+ flb_service_set(ctx->flb,
+ "Flush", "0.200000000",
+ "Grace", "1",
+ NULL);
+
+ /* Input */
+ i_ffd = flb_input(ctx->flb, (char *) "lib", NULL);
+ TEST_CHECK(i_ffd >= 0);
+ flb_input_set(ctx->flb, i_ffd, "tag", "test", NULL);
+ ctx->i_ffd = i_ffd;
+
+ /* Filter configuration */
+ f_ffd = flb_filter(ctx->flb, (char *) "multiline", NULL);
+ TEST_CHECK(f_ffd >= 0);
+ flb_filter_set(ctx->flb, f_ffd, "match", "*", NULL);
+ ctx->f_ffd = f_ffd;
+
+ /* Output */
+ o_ffd = flb_output(ctx->flb, (char *) "lib", (void *) data);
+ TEST_CHECK(o_ffd >= 0);
+ flb_output_set(ctx->flb, o_ffd,
+ "match", "test*",
+ "format", "json",
+ NULL);
+
+ return ctx;
+}
+
+static void filter_test_destroy(struct filter_test *ctx)
+{
+ flb_stop(ctx->flb);
+ flb_destroy(ctx->flb);
+ flb_free(ctx);
+}
+
+static void flb_test_multiline_buffered_two_output_record()
+{
+ int len;
+ int ret;
+ int bytes;
+ char *p;
+ struct flb_lib_out_cb cb_data;
+ struct filter_test *ctx;
+ struct filter_test_result expected = { 0 };
+
+ /* Create test context */
+ ctx = filter_test_create((void *) &cb_data);
+ if (!ctx) {
+ exit(EXIT_FAILURE);
+ }
+
+ /* Configure filter */
+ ret = flb_filter_set(ctx->flb, ctx->f_ffd,
+ "multiline.key_content", "log",
+ "multiline.parser", "go",
+ "buffer", "on",
+ "flush_ms", "1500",
+ "debug_flush", "on",
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ /* Prepare output callback with expected result */
+ expected.expected_records = 1; /* 1 record with all lines concatenated */
+ expected.expected_pattern = "main.main.func1(0xc420024120)";
+ expected.expected_pattern_index = 0;
+ cb_data.cb = cb_check_result;
+ cb_data.data = (void *) &expected;
+
+ /* Start the engine */
+ ret = flb_start(ctx->flb);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data samples */
+ p = "[0, {\"log\":\"panic: my panic\"}]";
+ len = strlen(p);
+ bytes = flb_lib_push(ctx->flb, ctx->i_ffd, p, len);
+ TEST_CHECK(bytes == len);
+ p = "[0, {\"log\":\"\n\"}]";
+ len = strlen(p);
+ bytes = flb_lib_push(ctx->flb, ctx->i_ffd, p, len);
+ TEST_CHECK(bytes == len);
+ p = "[0, {\"log\":\"goroutine 4 [running]:\"}]";
+ len = strlen(p);
+ bytes = flb_lib_push(ctx->flb, ctx->i_ffd, p, len);
+ TEST_CHECK(bytes == len);
+ p = "[0, {\"log\":\"panic(0x45cb40, 0x47ad70)\"}]";
+ len = strlen(p);
+ bytes = flb_lib_push(ctx->flb, ctx->i_ffd, p, len);
+ TEST_CHECK(bytes == len);
+ sleep(1); /* ensure records get sent separately */
+ p = "[0, {\"log\":\" /usr/local/go/src/runtime/panic.go:542 +0x46c fp=0xc42003f7b8 sp=0xc42003f710 pc=0x422f7c\"}]";
+ len = strlen(p);
+ bytes = flb_lib_push(ctx->flb, ctx->i_ffd, p, len);
+ TEST_CHECK(bytes == len);
+ p = "[0, {\"log\":\"main.main.func1(0xc420024120)\"}]";
+ len = strlen(p);
+ bytes = flb_lib_push(ctx->flb, ctx->i_ffd, p, len);
+ TEST_CHECK(bytes == len);
+
+ /* check number of outputted records */
+ sleep(3);
+ TEST_CHECK(expected.actual_records == expected.expected_records);
+ filter_test_destroy(ctx);
+}
+
+static void flb_test_multiline_buffered_one_output_record()
+{
+ int len;
+ int ret;
+ int bytes;
+ char *p;
+ struct flb_lib_out_cb cb_data;
+ struct filter_test *ctx;
+ struct filter_test_result expected = { 0 };
+
+ /* Create test context */
+ ctx = filter_test_create((void *) &cb_data);
+ if (!ctx) {
+ exit(EXIT_FAILURE);
+ }
+
+ /* Configure filter */
+ ret = flb_filter_set(ctx->flb, ctx->f_ffd,
+ "multiline.key_content", "log",
+ "multiline.parser", "go",
+ "buffer", "on",
+ "debug_flush", "on",
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ /* Prepare output callback with expected result */
+ expected.expected_records = 2; /* 1 record with all lines concatenated */
+ expected.expected_pattern = "main.main.func1(0xc420024120)";
+ cb_data.cb = cb_check_result;
+ cb_data.data = (void *) &expected;
+
+ /* Start the engine */
+ ret = flb_start(ctx->flb);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data samples */
+ p = "[0, {\"log\":\"panic: my panic\"}]";
+ len = strlen(p);
+ bytes = flb_lib_push(ctx->flb, ctx->i_ffd, p, len);
+ TEST_CHECK(bytes == len);
+ p = "[0, {\"log\":\"\n\"}]";
+ len = strlen(p);
+ bytes = flb_lib_push(ctx->flb, ctx->i_ffd, p, len);
+ TEST_CHECK(bytes == len);
+ p = "[0, {\"log\":\"goroutine 4 [running]:\"}]";
+ len = strlen(p);
+ bytes = flb_lib_push(ctx->flb, ctx->i_ffd, p, len);
+ TEST_CHECK(bytes == len);
+ p = "[0, {\"log\":\"panic(0x45cb40, 0x47ad70)\"}]";
+ len = strlen(p);
+ bytes = flb_lib_push(ctx->flb, ctx->i_ffd, p, len);
+ TEST_CHECK(bytes == len);
+ sleep(1); /* ensure records get sent separately */
+ p = "[0, {\"log\":\" /usr/local/go/src/runtime/panic.go:542 +0x46c fp=0xc42003f7b8 sp=0xc42003f710 pc=0x422f7c\"}]";
+ len = strlen(p);
+ bytes = flb_lib_push(ctx->flb, ctx->i_ffd, p, len);
+ TEST_CHECK(bytes == len);
+ p = "[0, {\"log\":\"main.main.func1(0xc420024120)\"}]";
+ len = strlen(p);
+ bytes = flb_lib_push(ctx->flb, ctx->i_ffd, p, len);
+ TEST_CHECK(bytes == len);
+ p = "[0, {\"log\":\"one more line, no multiline\"}]";
+ len = strlen(p);
+ bytes = flb_lib_push(ctx->flb, ctx->i_ffd, p, len);
+ TEST_CHECK(bytes == len);
+
+ /* check number of outputted records */
+ sleep(2);
+ TEST_CHECK(expected.actual_records == expected.expected_records);
+ filter_test_destroy(ctx);
+}
+
+static void flb_test_multiline_unbuffered()
+{
+ int len;
+ int ret;
+ int bytes;
+ char *p;
+ struct flb_lib_out_cb cb_data;
+ struct filter_test *ctx;
+ struct filter_test_result expected = { 0 };
+
+ /* Create test context */
+ ctx = filter_test_create((void *) &cb_data);
+ if (!ctx) {
+ exit(EXIT_FAILURE);
+ }
+
+ /* Configure filter */
+ ret = flb_filter_set(ctx->flb, ctx->f_ffd,
+ "multiline.key_content", "log",
+ "multiline.parser", "go",
+ "buffer", "off",
+ "debug_flush", "on",
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ /* Prepare output callback with expected result */
+ expected.expected_records = 6; /* no concatenation */
+ expected.expected_pattern = "panic";
+ expected.expected_pattern_index = 0;
+ cb_data.cb = cb_check_result;
+ cb_data.data = (void *) &expected;
+
+ /* Start the engine */
+ ret = flb_start(ctx->flb);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data samples */
+ p = "[0, {\"log\":\"panic: my panic\"}]";
+ len = strlen(p);
+ bytes = flb_lib_push(ctx->flb, ctx->i_ffd, p, len);
+ TEST_CHECK(bytes == len);
+ sleep(1); /* ensure records get sent one by one */
+ p = "[0, {\"log\":\"\n\"}]";
+ len = strlen(p);
+ bytes = flb_lib_push(ctx->flb, ctx->i_ffd, p, len);
+ TEST_CHECK(bytes == len);
+ sleep(1); /* ensure records get sent one by one */
+ p = "[0, {\"log\":\"goroutine 4 [running]:\"}]";
+ len = strlen(p);
+ bytes = flb_lib_push(ctx->flb, ctx->i_ffd, p, len);
+ TEST_CHECK(bytes == len);
+ sleep(1); /* ensure records get sent one by one */
+ p = "[0, {\"log\":\"panic(0x45cb40, 0x47ad70)\"}]";
+ len = strlen(p);
+ bytes = flb_lib_push(ctx->flb, ctx->i_ffd, p, len);
+ TEST_CHECK(bytes == len);
+ sleep(1); /* ensure records get sent one by one */
+ p = "[0, {\"log\":\" /usr/local/go/src/runtime/panic.go:542 +0x46c fp=0xc42003f7b8 sp=0xc42003f710 pc=0x422f7c\"}]";
+ len = strlen(p);
+ bytes = flb_lib_push(ctx->flb, ctx->i_ffd, p, len);
+ TEST_CHECK(bytes == len);
+ sleep(1); /* ensure records get sent one by one */
+ p = "[0, {\"log\":\"main.main.func1(0xc420024120)\"}]";
+ len = strlen(p);
+ bytes = flb_lib_push(ctx->flb, ctx->i_ffd, p, len);
+ TEST_CHECK(bytes == len);
+
+ /* check number of outputted records */
+ sleep(2);
+ TEST_CHECK(expected.actual_records == expected.expected_records);
+ filter_test_destroy(ctx);
+}
+
+static void flb_test_multiline_partial_message_concat()
+{
+ int len;
+ int ret;
+ int bytes;
+ char *p;
+ struct flb_lib_out_cb cb_data;
+ struct filter_test *ctx;
+ struct filter_test_result expected = { 0 };
+
+ /* Create test context */
+ ctx = filter_test_create((void *) &cb_data);
+ if (!ctx) {
+ exit(EXIT_FAILURE);
+ }
+
+ /* Configure filter */
+ ret = flb_filter_set(ctx->flb, ctx->f_ffd,
+ "multiline.key_content", "log",
+ "mode", "partial_message",
+ "buffer", "on",
+ "debug_flush", "on",
+ "flush_ms", "666",
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ /* Prepare output callback with expected result */
+ expected.expected_records = 1; /* 1 record with all lines concatenated */
+ expected.expected_pattern = "one..two";
+ cb_data.cb = cb_check_result;
+ cb_data.data = (void *) &expected;
+
+ /* Start the engine */
+ ret = flb_start(ctx->flb);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data samples */
+ p = "[0, {\"log\":\"one..\", \"partial_message\":\"true\", \"partial_id\": \"1\", \"partial_ordinal\": \"1\", \"partial_last\": \"false\"}]";
+ len = strlen(p);
+ bytes = flb_lib_push(ctx->flb, ctx->i_ffd, p, len);
+ TEST_CHECK(bytes == len);
+ p = "[0, {\"log\":\"two..\", \"partial_message\":\"true\", \"partial_id\": \"1\", \"partial_ordinal\": \"2\", \"partial_last\": \"false\"}]";
+ len = strlen(p);
+ bytes = flb_lib_push(ctx->flb, ctx->i_ffd, p, len);
+ TEST_CHECK(bytes == len);
+
+ /* check number of outputted records */
+ sleep(2);
+ TEST_CHECK(expected.actual_records == expected.expected_records);
+ filter_test_destroy(ctx);
+}
+
+static void flb_test_multiline_partial_message_concat_two_ids()
+{
+ int len;
+ int ret;
+ int bytes;
+ char *p;
+ struct flb_lib_out_cb cb_data;
+ struct filter_test *ctx;
+ struct filter_test_result expected = { 0 };
+
+ /* Create test context */
+ ctx = filter_test_create((void *) &cb_data);
+ if (!ctx) {
+ exit(EXIT_FAILURE);
+ }
+
+ /* Configure filter */
+ ret = flb_filter_set(ctx->flb, ctx->f_ffd,
+ "multiline.key_content", "log",
+ "mode", "partial_message",
+ "buffer", "on",
+ "debug_flush", "on",
+ "flush_ms", "666",
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ /* Prepare output callback with expected result */
+ expected.expected_records = 2; /* 2 records, one for each partial_id*/
+ expected.expected_pattern = "one..two";
+ cb_data.cb = cb_check_result;
+ cb_data.data = (void *) &expected;
+
+ /* Start the engine */
+ ret = flb_start(ctx->flb);
+ TEST_CHECK(ret == 0);
+
+ /* two different partial IDs, interlaced */
+ p = "[0, {\"log\":\"one..\", \"partial_message\":\"true\", \"partial_id\": \"1\", \"partial_ordinal\": \"1\", \"partial_last\": \"false\"}]";
+ len = strlen(p);
+ bytes = flb_lib_push(ctx->flb, ctx->i_ffd, p, len);
+ TEST_CHECK(bytes == len);
+ p = "[0, {\"log\":\"three..\", \"partial_message\":\"true\", \"partial_id\": \"2\", \"partial_ordinal\": \"1\", \"partial_last\": \"false\"}]";
+ len = strlen(p);
+ bytes = flb_lib_push(ctx->flb, ctx->i_ffd, p, len);
+ TEST_CHECK(bytes == len);
+ p = "[0, {\"log\":\"two..\", \"partial_message\":\"true\", \"partial_id\": \"1\", \"partial_ordinal\": \"2\", \"partial_last\": \"true\"}]";
+ len = strlen(p);
+ bytes = flb_lib_push(ctx->flb, ctx->i_ffd, p, len);
+ TEST_CHECK(bytes == len);
+ p = "[0, {\"log\":\"two..\", \"partial_message\":\"true\", \"partial_id\": \"2\", \"partial_ordinal\": \"2\", \"partial_last\": \"true\"}]";
+ len = strlen(p);
+ bytes = flb_lib_push(ctx->flb, ctx->i_ffd, p, len);
+ TEST_CHECK(bytes == len);
+
+ /* check number of outputted records */
+ sleep(2);
+ TEST_CHECK(expected.actual_records == expected.expected_records);
+ filter_test_destroy(ctx);
+}
+
+
+/*
+ * create 2 in_lib instances and pass multiline
+ * https://github.com/fluent/fluent-bit/issues/5524
+*/
+static void flb_test_ml_buffered_two_streams()
+{
+ struct flb_lib_out_cb cb_data;
+ struct filter_test *ctx;
+ int i_ffd_2;
+ int ret;
+ int i;
+ int bytes;
+ int len;
+ char line_buf[2048] = {0};
+ int line_num;
+ int num;
+
+ char *expected_strs[] = {"Exception in thread main java.lang.IllegalStateException: ..null property\\n at com.example.myproject.Author.getBookIds(xx.java:38)\\n at com.example.myproject.Bootstrap.main(Bootstrap.java:14)\\nCaused by: java.lang.NullPointerException\\n at com.example.myproject.Book.getId(Book.java:22)\\n at com.example.myproject.Author.getBookIds(Author.java:35)\\n ... 1 more",
+ "Dec 14 06:41:08 Exception in thread main java.lang.RuntimeException: Something has gone wrong, aborting!\\n at com.myproject.module.MyProject.badMethod(MyProject.java:22)\\n at com.myproject.module.MyProject.oneMoreMethod(MyProject.java:18)\\n at com.myproject.module.MyProject.anotherMethod(MyProject.java:14)\\n at com.myproject.module.MyProject.someMethod(MyProject.java:10)\\n at com.myproject.module.MyProject.main(MyProject.java:6)"};
+ struct str_list expected = {
+ .size = sizeof(expected_strs)/sizeof(char*),
+ .lists = &expected_strs[0],
+ .ignore_min_line_num = 64,
+ };
+
+ char *ml_logs_1[] = {"Exception in thread main java.lang.IllegalStateException: ..null property",
+ " at com.example.myproject.Author.getBookIds(xx.java:38)",
+ " at com.example.myproject.Bootstrap.main(Bootstrap.java:14)",
+ "Caused by: java.lang.NullPointerException",
+ " at com.example.myproject.Book.getId(Book.java:22)",
+ " at com.example.myproject.Author.getBookIds(Author.java:35)",
+ " ... 1 more",
+ "single line"};
+ char *ml_logs_2[] = {
+ "single line...",
+ "Dec 14 06:41:08 Exception in thread main java.lang.RuntimeException: Something has gone wrong, aborting!",
+ " at com.myproject.module.MyProject.badMethod(MyProject.java:22)",
+ " at com.myproject.module.MyProject.oneMoreMethod(MyProject.java:18)",
+ " at com.myproject.module.MyProject.anotherMethod(MyProject.java:14)",
+ " at com.myproject.module.MyProject.someMethod(MyProject.java:10)",
+ " at com.myproject.module.MyProject.main(MyProject.java:6)",
+ "another line..."};
+
+ cb_data.cb = cb_check_str_list;
+ cb_data.data = (void *)&expected;
+
+ clear_output_num();
+
+ TEST_CHECK(sizeof(ml_logs_1)/sizeof(char*) == sizeof(ml_logs_2)/sizeof(char*));
+ line_num = sizeof(ml_logs_1)/sizeof(char*);
+
+ /* Create test context */
+ ctx = filter_test_create((void *) &cb_data);
+ if (!ctx) {
+ exit(EXIT_FAILURE);
+ }
+ i_ffd_2 = flb_input(ctx->flb, (char *) "lib", NULL);
+ TEST_CHECK(i_ffd_2 >= 0);
+ flb_input_set(ctx->flb, i_ffd_2, "tag", "test2", NULL);
+
+ /* Configure filter */
+ ret = flb_filter_set(ctx->flb, ctx->f_ffd,
+ "multiline.key_content", "log",
+ "multiline.parser", "java",
+ "buffer", "on",
+ "debug_flush", "on",
+ NULL);
+ TEST_CHECK(ret == 0);
+
+
+ /* Start the engine */
+ ret = flb_start(ctx->flb);
+ TEST_CHECK(ret == 0);
+
+ for (i=0; i<line_num; i++) {
+ sprintf(&line_buf[0], "[%d, {\"log\":\"%s\"}]", i, ml_logs_1[i]);
+ len = strlen(line_buf);
+ bytes = flb_lib_push(ctx->flb, ctx->i_ffd, &line_buf[0], len);
+ TEST_CHECK(bytes == len);
+
+
+ sprintf(&line_buf[0], "[%d, {\"log\":\"%s\"}]", i, ml_logs_2[i]);
+ len = strlen(line_buf);
+ bytes = flb_lib_push(ctx->flb, i_ffd_2, &line_buf[0], len);
+ TEST_CHECK(bytes == len);
+ }
+ sleep(3);
+
+ num = get_output_num();
+ if (!TEST_CHECK(num == 2)) {
+ TEST_MSG("output error. got %d expect 2", num);
+ }
+
+ filter_test_destroy(ctx);
+}
+
+static void flb_test_ml_buffered_16_streams()
+{
+ struct flb_lib_out_cb cb_data;
+ struct filter_test *ctx;
+ int i_ffds[16] = {0};
+ int ffd_num = sizeof(i_ffds)/sizeof(int);
+ int ret;
+ int i;
+ int j;
+ int bytes;
+ int len;
+ char line_buf[2048] = {0};
+ char tag_buf[32] = {0};
+ int line_num;
+ int num;
+
+ char *expected_strs[] = {"Exception in thread main java.lang.IllegalStateException: ..null property\\n at com.example.myproject.Author.getBookIds(xx.java:38)\\n at com.example.myproject.Bootstrap.main(Bootstrap.java:14)\\nCaused by: java.lang.NullPointerException\\n at com.example.myproject.Book.getId(Book.java:22)\\n at com.example.myproject.Author.getBookIds(Author.java:35)\\n ... 1 more"};
+
+ struct str_list expected = {
+ .size = sizeof(expected_strs)/sizeof(char*),
+ .lists = &expected_strs[0],
+ .ignore_min_line_num = 64,
+ };
+
+ char *ml_logs[] = {"Exception in thread main java.lang.IllegalStateException: ..null property",
+ " at com.example.myproject.Author.getBookIds(xx.java:38)",
+ " at com.example.myproject.Bootstrap.main(Bootstrap.java:14)",
+ "Caused by: java.lang.NullPointerException",
+ " at com.example.myproject.Book.getId(Book.java:22)",
+ " at com.example.myproject.Author.getBookIds(Author.java:35)",
+ " ... 1 more",
+ "single line"};
+
+ cb_data.cb = cb_check_str_list;
+ cb_data.data = (void *)&expected;
+
+ clear_output_num();
+
+ line_num = sizeof(ml_logs)/sizeof(char*);
+
+ /* Create test context */
+ ctx = filter_test_create((void *) &cb_data);
+ if (!ctx) {
+ exit(EXIT_FAILURE);
+ }
+
+ i_ffds[0] = ctx->i_ffd;
+ for (i=1; i<ffd_num; i++) {
+ i_ffds[i] = flb_input(ctx->flb, (char *) "lib", NULL);
+ TEST_CHECK(i_ffds[i] >= 0);
+ sprintf(&tag_buf[0], "test%d", i);
+ flb_input_set(ctx->flb, i_ffds[i], "tag", tag_buf, NULL);
+ }
+
+ /* Configure filter */
+ ret = flb_filter_set(ctx->flb, ctx->f_ffd,
+ "multiline.key_content", "log",
+ "multiline.parser", "java",
+ "buffer", "on",
+ "debug_flush", "on",
+ NULL);
+ TEST_CHECK(ret == 0);
+
+
+ /* Start the engine */
+ ret = flb_start(ctx->flb);
+ TEST_CHECK(ret == 0);
+
+ for (i=0; i<line_num; i++) {
+ sprintf(&line_buf[0], "[%d, {\"log\":\"%s\"}]", i, ml_logs[i]);
+ len = strlen(line_buf);
+ for (j=0; j<ffd_num; j++) {
+ bytes = flb_lib_push(ctx->flb, i_ffds[j], &line_buf[0], len);
+ TEST_CHECK(bytes == len);
+ }
+ }
+ sleep(3);
+
+ num = get_output_num();
+ if (!TEST_CHECK(num == ffd_num)) {
+ TEST_MSG("output error. got %d expect %d", num, ffd_num);
+ }
+
+ filter_test_destroy(ctx);
+}
+
+
+
+
+TEST_LIST = {
+ {"ml_buffered_two_streams" , flb_test_ml_buffered_two_streams},
+ {"ml_buffered_16_streams" , flb_test_ml_buffered_16_streams},
+
+ {"multiline_buffered_one_record" , flb_test_multiline_buffered_one_output_record },
+ {"multiline_buffered_two_record" , flb_test_multiline_buffered_two_output_record },
+ {"flb_test_multiline_unbuffered" , flb_test_multiline_unbuffered },
+
+ {"flb_test_multiline_partial_message_concat" , flb_test_multiline_partial_message_concat },
+ {"flb_test_multiline_partial_message_concat_two_ids" , flb_test_multiline_partial_message_concat_two_ids },
+ {NULL, NULL}
+};
diff --git a/fluent-bit/tests/runtime/filter_nest.c b/fluent-bit/tests/runtime/filter_nest.c
new file mode 100644
index 00000000..36e6a3fe
--- /dev/null
+++ b/fluent-bit/tests/runtime/filter_nest.c
@@ -0,0 +1,302 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+#include <fluent-bit.h>
+#include <fluent-bit/flb_time.h>
+#include "flb_tests_runtime.h"
+
+pthread_mutex_t result_mutex = PTHREAD_MUTEX_INITIALIZER;
+char *output = NULL;
+int num_output = 0;
+
+/* Test data */
+
+/* Test functions */
+void flb_test_filter_nest_single(void);
+void flb_test_filter_nest_multi_nest(void);
+void flb_test_filter_nest_multi_lift(void);
+/* Test list */
+TEST_LIST = {
+ {"single", flb_test_filter_nest_single },
+ {"multiple events are not dropped(nest)", flb_test_filter_nest_multi_nest},
+ {"multiple events are not dropped(lift)", flb_test_filter_nest_multi_lift},
+ {NULL, NULL}
+};
+
+
+void add_output_num()
+{
+ pthread_mutex_lock(&result_mutex);
+ num_output++;
+ pthread_mutex_unlock(&result_mutex);
+}
+
+void clear_output_num()
+{
+ pthread_mutex_lock(&result_mutex);
+ num_output = 0;
+ pthread_mutex_unlock(&result_mutex);
+}
+
+int get_output_num()
+{
+ int ret;
+ pthread_mutex_lock(&result_mutex);
+ ret = num_output;
+ pthread_mutex_unlock(&result_mutex);
+
+ return ret;
+}
+
+int callback_count(void* data, size_t size, void* cb_data)
+{
+ if (size > 0) {
+ flb_debug("[test_filter_nest] received message: %s", (char*)data);
+ add_output_num(); /* success */
+ flb_free(data);
+ }
+ return 0;
+}
+
+void set_output(char *val)
+{
+ pthread_mutex_lock(&result_mutex);
+ output = val;
+ pthread_mutex_unlock(&result_mutex);
+}
+
+char *get_output(void)
+{
+ char *val;
+
+ pthread_mutex_lock(&result_mutex);
+ val = output;
+ pthread_mutex_unlock(&result_mutex);
+
+ return val;
+}
+
+int callback_test(void* data, size_t size, void* cb_data)
+{
+ if (size > 0) {
+ flb_debug("[test_filter_nest] received message: %s", (char*)data);
+ set_output(data); /* success */
+ }
+ return 0;
+}
+
+void flb_test_filter_nest_multi_nest(void)
+{
+ int ret;
+ int bytes;
+ char *p;
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+ int filter_ffd;
+ struct flb_lib_out_cb cb_data;
+
+ int count = 0; // should be number of events.
+ int expected = 2;
+
+ clear_output_num();
+
+ ctx = flb_create();
+ flb_service_set(ctx,
+ "Flush", "1",
+ "Grace", "1",
+ "Log_Level", "debug",
+ NULL);
+
+ /* Input */
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ TEST_CHECK(in_ffd >= 0);
+ flb_input_set(ctx, in_ffd, "tag", "test", NULL);
+
+ /* Prepare output callback context*/
+ cb_data.cb = callback_count;
+ cb_data.data = NULL;
+
+ /* Filter */
+ filter_ffd = flb_filter(ctx, (char *) "nest", NULL);
+ TEST_CHECK(filter_ffd >= 0);
+
+ ret = flb_filter_set(ctx, filter_ffd,
+ "Match", "*",
+ "Operation", "nest",
+ "Wildcard", "to_nest",
+ "Nest_under", "nested_key",
+ NULL);
+
+ TEST_CHECK(ret == 0);
+
+
+ /* Output */
+ out_ffd = flb_output(ctx, (char *) "lib", (void *) &cb_data);
+ TEST_CHECK(out_ffd >= 0);
+ flb_output_set(ctx, out_ffd,
+ "match", "test",
+ "format", "json",
+ NULL);
+
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ p = "[1448403340, {\"to_nest\":\"This is the data to nest\", \"extra\":\"Some more data\"}]";
+ bytes = flb_lib_push(ctx, in_ffd, p, strlen(p));
+ TEST_CHECK(bytes == strlen(p));
+
+ p = "[1448403341, {\"not_nest\":\"dummy data\", \"extra\":\"dummy more data\"}]";
+ bytes = flb_lib_push(ctx, in_ffd, p, strlen(p));
+ TEST_CHECK(bytes == strlen(p));
+
+ flb_time_msleep(1500); /* waiting flush */
+ count = get_output_num();
+
+ TEST_CHECK_(count == expected, "Expected number of events %d, got %d", expected, count );
+
+ flb_stop(ctx);
+ flb_destroy(ctx);
+}
+
+void flb_test_filter_nest_multi_lift(void)
+{
+ int ret;
+ int bytes;
+ char *p;
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+ int filter_ffd;
+ struct flb_lib_out_cb cb_data;
+
+ int count = 0; // should be number of events.
+ int expected = 2;
+
+ clear_output_num();
+
+ ctx = flb_create();
+ flb_service_set(ctx,
+ "Flush", "1",
+ "Grace", "1",
+ "Log_Level", "debug",
+ NULL);
+
+ /* Input */
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ TEST_CHECK(in_ffd >= 0);
+ flb_input_set(ctx, in_ffd, "tag", "test", NULL);
+
+ /* Prepare output callback context*/
+ cb_data.cb = callback_count;
+ cb_data.data = NULL;
+
+ /* Filter */
+ filter_ffd = flb_filter(ctx, (char *) "nest", NULL);
+ TEST_CHECK(filter_ffd >= 0);
+
+ ret = flb_filter_set(ctx, filter_ffd,
+ "Match", "*",
+ "Operation", "lift",
+ "Nested_under", "nested",
+ NULL);
+
+ TEST_CHECK(ret == 0);
+
+
+ /* Output */
+ out_ffd = flb_output(ctx, (char *) "lib", (void *) &cb_data);
+ TEST_CHECK(out_ffd >= 0);
+ flb_output_set(ctx, out_ffd,
+ "match", "test",
+ "format", "json",
+ NULL);
+
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ p = "[1448403340, {\"nested\": {\"child\":\"nested data\"}, \"not_nestd\":\"not nested data\" }]";
+ bytes = flb_lib_push(ctx, in_ffd, p, strlen(p));
+ TEST_CHECK(bytes == strlen(p));
+
+ p = "[1448403341, {\"not_nest\":\"dummy data\", \"extra\":\"dummy more data\"}]";
+ bytes = flb_lib_push(ctx, in_ffd, p, strlen(p));
+ TEST_CHECK(bytes == strlen(p));
+
+ flb_time_msleep(1500); /* waiting flush */
+ count = get_output_num();
+
+ TEST_CHECK_(count == expected, "Expected number of events %d, got %d", expected, count );
+
+ flb_stop(ctx);
+ flb_destroy(ctx);
+}
+
+void flb_test_filter_nest_single(void)
+{
+ int ret;
+ int bytes;
+ char *p, *output, *expected;
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+ int filter_ffd;
+ struct flb_lib_out_cb cb_data;
+
+ ctx = flb_create();
+ flb_service_set(ctx,
+ "Flush", "1",
+ "Grace", "1",
+ "Log_Level", "debug",
+ NULL);
+
+ /* Input */
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ TEST_CHECK(in_ffd >= 0);
+ flb_input_set(ctx, in_ffd, "tag", "test", NULL);
+
+ /* Prepare output callback context*/
+ cb_data.cb = callback_test;
+ cb_data.data = NULL;
+
+ /* Filter */
+ filter_ffd = flb_filter(ctx, (char *) "nest", NULL);
+ TEST_CHECK(filter_ffd >= 0);
+
+ ret = flb_filter_set(ctx, filter_ffd,
+ "Match", "*",
+ "Operation", "nest",
+ "Wildcard", "to_nest",
+ "Nest_under", "nested_key",
+ NULL);
+
+ TEST_CHECK(ret == 0);
+
+
+ /* Output */
+ out_ffd = flb_output(ctx, (char *) "lib", (void *) &cb_data);
+ TEST_CHECK(out_ffd >= 0);
+ flb_output_set(ctx, out_ffd,
+ "match", "test",
+ "format", "json",
+ NULL);
+
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ p = "[1448403340, {\"to_nest\":\"This is the data to nest\", \"extra\":\"Some more data\"}]";
+ bytes = flb_lib_push(ctx, in_ffd, p, strlen(p));
+ TEST_CHECK(bytes == strlen(p));
+
+ flb_time_msleep(1500); /* waiting flush */
+ output = get_output();
+
+ TEST_CHECK_(output != NULL, "Expected output to not be NULL");
+
+ if (output != NULL) {
+ expected = "\"nested_key\":{\"to_nest\":\"This is the data to nest\"}}]";
+ TEST_CHECK_(strstr(output, expected) != NULL, "Expected output to contain '%s', got '%s'", expected, output);
+ free(output);
+ }
+ flb_stop(ctx);
+ flb_destroy(ctx);
+}
diff --git a/fluent-bit/tests/runtime/filter_parser.c b/fluent-bit/tests/runtime/filter_parser.c
new file mode 100644
index 00000000..c31fbcf8
--- /dev/null
+++ b/fluent-bit/tests/runtime/filter_parser.c
@@ -0,0 +1,833 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+#include <fluent-bit.h>
+#include <fluent-bit/flb_time.h>
+#include <fluent-bit/flb_parser.h>
+#include "flb_tests_runtime.h"
+
+#define FLUSH_INTERVAL "1.0"
+
+pthread_mutex_t result_mutex = PTHREAD_MUTEX_INITIALIZER;
+char *output = NULL;
+
+void set_output(char *val)
+{
+ pthread_mutex_lock(&result_mutex);
+ output = val;
+ pthread_mutex_unlock(&result_mutex);
+}
+
+char *get_output(void)
+{
+ char *val;
+
+ pthread_mutex_lock(&result_mutex);
+ val = output;
+ pthread_mutex_unlock(&result_mutex);
+
+ return val;
+}
+
+void clear_output()
+{
+ pthread_mutex_lock(&result_mutex);
+ output = NULL;
+ pthread_mutex_unlock(&result_mutex);
+}
+
+int callback_test(void* data, size_t size, void* cb_data)
+{
+ if (size > 0) {
+ flb_debug("[test_filter_parser] received message: %s", (char*)data);
+ set_output(data); /* success */
+ }
+ return 0;
+}
+
+void wait_with_timeout(uint32_t timeout_ms, char **out_result)
+{
+ struct flb_time start_time;
+ struct flb_time end_time;
+ struct flb_time diff_time;
+ uint64_t elapsed_time_flb = 0;
+ char *output = NULL;
+
+ flb_time_get(&start_time);
+
+ while (true) {
+ output = get_output();
+
+ if (output != NULL) {
+ *out_result = output;
+ break;
+ }
+
+ flb_time_msleep(100);
+ flb_time_get(&end_time);
+ flb_time_diff(&end_time, &start_time, &diff_time);
+ elapsed_time_flb = flb_time_to_nanosec(&diff_time) / 1000000;
+
+ if (elapsed_time_flb > timeout_ms) {
+ flb_warn("[timeout] elapsed_time: %ld", elapsed_time_flb);
+ // Reached timeout.
+ break;
+ }
+ }
+}
+
+
+void flb_test_filter_parser_extract_fields()
+{
+ int ret;
+ int bytes;
+ char *p, *output, *expected;
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+ int filter_ffd;
+ struct flb_parser *parser;
+
+ struct flb_lib_out_cb cb;
+ cb.cb = callback_test;
+ cb.data = NULL;
+
+ clear_output();
+
+ ctx = flb_create();
+
+ /* Configure service */
+ flb_service_set(ctx, "Flush", FLUSH_INTERVAL, "Grace" "1", "Log_Level", "debug", NULL);
+
+ /* Input */
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ TEST_CHECK(in_ffd >= 0);
+ flb_input_set(ctx, in_ffd,
+ "Tag", "test",
+ NULL);
+
+ /* Parser */
+ parser = flb_parser_create("dummy_test", "regex", "^(?<INT>[^ ]+) (?<FLOAT>[^ ]+) (?<BOOL>[^ ]+) (?<STRING>.+)$",
+ FLB_TRUE,
+ NULL, NULL, NULL, MK_FALSE, MK_TRUE, FLB_FALSE, NULL, 0,
+ NULL, ctx->config);
+ TEST_CHECK(parser != NULL);
+
+ /* Filter */
+ filter_ffd = flb_filter(ctx, (char *) "parser", NULL);
+ TEST_CHECK(filter_ffd >= 0);
+ ret = flb_filter_set(ctx, filter_ffd,
+ "Match", "test",
+ "Key_Name", "data",
+ "Parser", "dummy_test",
+ "Reserve_Data", "On",
+ "Preserve_Key", "Off",
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ /* Output */
+ out_ffd = flb_output(ctx, (char *) "lib", &cb);
+ TEST_CHECK(out_ffd >= 0);
+ flb_output_set(ctx, out_ffd,
+ "Match", "*",
+ "format", "json",
+ NULL);
+
+ /* Start the engine */
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data */
+ p = "[1448403340, {\"data\":\"100 0.5 true This is an example\", \"extra\":\"Some more data\"}]";
+ bytes = flb_lib_push(ctx, in_ffd, p, strlen(p));
+ TEST_CHECK(bytes == strlen(p));
+
+ wait_with_timeout(2000, &output); /* waiting flush and ensuring data flush */
+ TEST_CHECK_(output != NULL, "Expected output to not be NULL");
+ if (output != NULL) {
+ /* check timestamp */
+ expected = "[1448403340.000000,{";
+ TEST_CHECK_(strstr(output, expected) != NULL, "Expected output to contain '%s', got '%s'", expected, output);
+ /* check fields were extracted */
+ expected = "\"INT\":\"100\",\"FLOAT\":\"0.5\",\"BOOL\":\"true\",\"STRING\":\"This is an example\"";
+ TEST_CHECK_(strstr(output, expected) != NULL, "Expected output to contain '%s', got '%s'", expected, output);
+ /* check original field was not preserved */
+ expected = "\"data\":\"100 0.5 true This is an example\"";
+ TEST_CHECK_(strstr(output, expected) == NULL, "Expected output to not contain '%s', got '%s'", expected, output);
+ /* check extra data was preserved */
+ expected = "\"extra\":\"Some more data\"";
+ TEST_CHECK_(strstr(output, expected) != NULL, "Expected output to preserve extra field, got '%s'", output);
+ free(output);
+ }
+
+ flb_stop(ctx);
+ flb_destroy(ctx);
+}
+
+void flb_test_filter_parser_reserve_data_off()
+{
+ int ret;
+ int bytes;
+ char *p, *output, *expected;
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+ int filter_ffd;
+ struct flb_parser *parser;
+
+ struct flb_lib_out_cb cb;
+ cb.cb = callback_test;
+ cb.data = NULL;
+
+ clear_output();
+
+ ctx = flb_create();
+
+ /* Configure service */
+ flb_service_set(ctx, "Flush", FLUSH_INTERVAL, "Grace", "1", "Log_Level", "debug", NULL);
+
+ /* Input */
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ TEST_CHECK(in_ffd >= 0);
+ flb_input_set(ctx, in_ffd,
+ "Tag", "test",
+ NULL);
+
+ /* Parser */
+ parser = flb_parser_create("dummy_test", "regex", "^(?<INT>[^ ]+) (?<FLOAT>[^ ]+) (?<BOOL>[^ ]+) (?<STRING>.+)$",
+ FLB_TRUE,
+ NULL, NULL, NULL, MK_FALSE, MK_TRUE, FLB_FALSE, NULL, 0,
+ NULL, ctx->config);
+ TEST_CHECK(parser != NULL);
+
+ /* Filter */
+ filter_ffd = flb_filter(ctx, (char *) "parser", NULL);
+ TEST_CHECK(filter_ffd >= 0);
+ ret = flb_filter_set(ctx, filter_ffd,
+ "Match", "test",
+ "Key_Name", "data",
+ "Parser", "dummy_test",
+ "Reserve_Data", "Off",
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ /* Output */
+ out_ffd = flb_output(ctx, (char *) "lib", &cb);
+ TEST_CHECK(out_ffd >= 0);
+ flb_output_set(ctx, out_ffd,
+ "Match", "*",
+ "format", "json",
+ NULL);
+
+ /* Start the engine */
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data */
+ p = "[1448403340,{\"data\":\"100 0.5 true This is an example\",\"extra\":\"Some more data\"}]";
+ bytes = flb_lib_push(ctx, in_ffd, p, strlen(p));
+ TEST_CHECK(bytes == strlen(p));
+
+ wait_with_timeout(2000, &output); /* waiting flush and ensuring data flush */
+ TEST_CHECK_(output != NULL, "Expected output to not be NULL");
+ if (output != NULL) {
+ /* check extra data was not preserved */
+ expected = "\"extra\":\"Some more data\"";
+ TEST_CHECK_(strstr(output, expected) == NULL, "Expected output to not preserve extra field, got '%s'", output);
+ free(output);
+ }
+
+ flb_stop(ctx);
+ flb_destroy(ctx);
+}
+
+void flb_test_filter_parser_handle_time_key()
+{
+ int ret;
+ int bytes;
+ char *p, *output, *expected;
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+ int filter_ffd;
+ struct flb_parser *parser;
+
+ struct flb_lib_out_cb cb;
+ cb.cb = callback_test;
+ cb.data = NULL;
+
+ clear_output();
+
+ ctx = flb_create();
+
+ /* Configure service */
+ flb_service_set(ctx, "Flush", FLUSH_INTERVAL, "Grace", "1", "Log_Level", "debug", NULL);
+
+ /* Input */
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ TEST_CHECK(in_ffd >= 0);
+ flb_input_set(ctx, in_ffd,
+ "Tag", "test",
+ NULL);
+
+ /* Parser */
+ parser = flb_parser_create("timestamp", "regex", "^(?<time>.*)$", FLB_TRUE,
+ "%Y-%m-%dT%H:%M:%S.%L",
+ "time",
+ NULL, MK_FALSE, MK_TRUE, FLB_FALSE,
+ NULL, 0, NULL, ctx->config);
+ TEST_CHECK(parser != NULL);
+
+ /* Filter */
+ filter_ffd = flb_filter(ctx, (char *) "parser", NULL);
+ TEST_CHECK(filter_ffd >= 0);
+ ret = flb_filter_set(ctx, filter_ffd,
+ "Match", "test",
+ "Key_Name", "@timestamp",
+ "Parser", "timestamp",
+ "Reserve_Data", "On",
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ /* Output */
+ out_ffd = flb_output(ctx, (char *) "lib", &cb);
+ TEST_CHECK(out_ffd >= 0);
+ flb_output_set(ctx, out_ffd,
+ "Match", "*",
+ "format", "json",
+ NULL);
+
+ /* Start the engine */
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data */
+ p = "[1448403340, {\"@timestamp\":\"2017-11-01T22:25:21.648+00:00\", \"message\":\"This is an example\"}]";
+ bytes = flb_lib_push(ctx, in_ffd, p, strlen(p));
+ TEST_CHECK(bytes == strlen(p));
+
+ wait_with_timeout(2000, &output); /* waiting flush and ensuring data flush */
+ TEST_CHECK_(output != NULL, "Expected output to not be NULL");
+ if (output != NULL) {
+ /* check the timestamp field was updated correctly */
+ /* this is in fluent-bits extended timestamp format */
+ expected = "[1509575121.648000,{";
+ TEST_CHECK_(strstr(output, expected) != NULL, "Expected output to contain '%s', got '%s'", expected, output);
+ /* check additional field is preserved */
+ expected = "\"message\":\"This is an example\"";
+ TEST_CHECK_(strstr(output, expected) != NULL, "Expected output to contain '%s', got '%s'", expected, output);
+ free(output);
+ }
+
+ flb_stop(ctx);
+ flb_destroy(ctx);
+}
+
+void flb_test_filter_parser_handle_time_key_with_fractional_timestamp()
+{
+ int ret;
+ int bytes;
+ char *p, *output, *expected;
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+ int filter_ffd;
+ struct flb_parser *parser;
+
+ clear_output();
+
+ ctx = flb_create();
+
+ /* Configure service */
+ flb_service_set(ctx, "Flush", FLUSH_INTERVAL, "Grace", "1", "Log_Level", "debug", NULL);
+
+ /* Input */
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ TEST_CHECK(in_ffd >= 0);
+ flb_input_set(ctx, in_ffd,
+ "Tag", "test",
+ NULL);
+
+ /* Parser */
+ parser = flb_parser_create("timestamp", "regex", "^(?<time>.*)$", FLB_TRUE,
+ "%s.%L",
+ "time",
+ NULL, MK_FALSE, MK_TRUE, FLB_FALSE,
+ NULL, 0, NULL, ctx->config);
+ TEST_CHECK(parser != NULL);
+
+ /* Filter */
+ filter_ffd = flb_filter(ctx, (char *) "parser", NULL);
+ TEST_CHECK(filter_ffd >= 0);
+ ret = flb_filter_set(ctx, filter_ffd,
+ "Match", "test",
+ "Key_Name", "@timestamp",
+ "Parser", "timestamp",
+ "Reserve_Data", "On",
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ /* Output */
+ out_ffd = flb_output(ctx, (char *) "lib", (void*)callback_test);
+ TEST_CHECK(out_ffd >= 0);
+ flb_output_set(ctx, out_ffd,
+ "Match", "*",
+ "format", "json",
+ NULL);
+
+ /* Start the engine */
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data */
+ p = "[1448403340, {\"@timestamp\":\"1509575121.648\", \"message\":\"This is an example\"}]";
+ bytes = flb_lib_push(ctx, in_ffd, p, strlen(p));
+ TEST_CHECK(bytes == strlen(p));
+
+ wait_with_timeout(2000, &output); /* waiting flush and ensuring data flush */
+ TEST_CHECK_(output != NULL, "Expected output to not be NULL");
+ if (output != NULL) {
+ /* check the timestamp field was updated correctly */
+ /* this is in fluent-bits extended timestamp format */
+ expected = "[\"\\x59\\xfffffffa\\x49\\xffffffd1\\x26\\xffffff9f\\xffffffb2\\x00\", {";
+ TEST_CHECK_(strstr(output, expected) != NULL, "Expected output to contain '%s', got '%s'", expected, output);
+ /* check additional field is preserved */
+ expected = "\"message\":\"This is an example\"";
+ TEST_CHECK_(strstr(output, expected) != NULL, "Expected output to contain '%s', got '%s'", expected, output);
+ free(output);
+ }
+
+ flb_stop(ctx);
+ flb_destroy(ctx);
+}
+
+void flb_test_filter_parser_handle_time_key_with_time_zone()
+{
+ int ret;
+ int bytes;
+ char *p, *output, *expected;
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+ int filter_ffd;
+ struct flb_parser *parser;
+
+ struct flb_lib_out_cb cb;
+ cb.cb = callback_test;
+ cb.data = NULL;
+
+ clear_output();
+
+ ctx = flb_create();
+
+ /* Configure service */
+ flb_service_set(ctx, "Flush", FLUSH_INTERVAL, "Grace", "1", "Log_Level", "debug", NULL);
+
+ /* Input */
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ TEST_CHECK(in_ffd >= 0);
+ flb_input_set(ctx, in_ffd,
+ "Tag", "test",
+ NULL);
+
+ /* Parser */
+ parser = flb_parser_create("timestamp", // name
+ "regex", // format
+ "^(?<time>.*)$", // regex
+ FLB_TRUE, // skip_empty
+ "%Y-%m-%dT%H:%M:%S.%L %z", // time_fmt
+ "time", // time_key
+ NULL, // time_offset
+ MK_FALSE, // time_keep
+ MK_TRUE, // time_strict
+ MK_FALSE, // logfmt_no_bare_keys
+ NULL, // types
+ 0, // types_len
+ NULL, // decoders
+ ctx->config); // config
+ TEST_CHECK(parser != NULL);
+
+ /* Filter */
+ filter_ffd = flb_filter(ctx, (char *) "parser", NULL);
+ TEST_CHECK(filter_ffd >= 0);
+ ret = flb_filter_set(ctx, filter_ffd,
+ "Match", "test",
+ "Key_Name", "@timestamp",
+ "Parser", "timestamp",
+ "Reserve_Data", "On",
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ /* Output */
+ out_ffd = flb_output(ctx, (char *) "lib", &cb);
+ TEST_CHECK(out_ffd >= 0);
+ flb_output_set(ctx, out_ffd,
+ "Match", "*",
+ "format", "json",
+ NULL);
+
+ /* Start the engine */
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data */
+ p = "[1448403340, {\"@timestamp\":\"2017-11-01T22:25:21.648-04:00\", \"message\":\"This is an example\"}]";
+ bytes = flb_lib_push(ctx, in_ffd, p, strlen(p));
+ TEST_CHECK(bytes == strlen(p));
+
+ wait_with_timeout(2000, &output); /* waiting flush and ensuring data flush */
+ TEST_CHECK_(output != NULL, "Expected output to not be NULL");
+ if (output != NULL) {
+ /* check the timestamp field was updated correctly */
+ /* this is in fluent-bits extended timestamp format */
+ expected = "[1509589521.648000,{";
+ TEST_CHECK_(strstr(output, expected) != NULL, "Expected output to contain '%s', got '%s'", expected, output);
+ /* check additional field is preserved */
+ expected = "\"message\":\"This is an example\"";
+ TEST_CHECK_(strstr(output, expected) != NULL, "Expected output to contain '%s', got '%s'", expected, output);
+ free(output);
+ }
+
+ flb_stop(ctx);
+ flb_destroy(ctx);
+}
+
+void flb_test_filter_parser_ignore_malformed_time()
+{
+ int ret;
+ int bytes;
+ char *p, *output, *expected;
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+ int filter_ffd;
+ struct flb_parser *parser;
+
+ struct flb_lib_out_cb cb;
+ cb.cb = callback_test;
+ cb.data = NULL;
+
+ clear_output();
+
+ ctx = flb_create();
+
+ /* Configure service */
+ flb_service_set(ctx, "Flush", FLUSH_INTERVAL, "Grace", "1", "Log_Level", "debug", NULL);
+
+ /* Input */
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ TEST_CHECK(in_ffd >= 0);
+ flb_input_set(ctx, in_ffd,
+ "Tag", "test",
+ NULL);
+
+ /* Parser */
+ parser = flb_parser_create("timestamp", "regex",
+ "^(?<time>.*)$", FLB_TRUE,
+ "%Y-%m-%dT%H:%M:%S.%L", "time",
+ NULL, FLB_FALSE, MK_TRUE, FLB_FALSE,
+ NULL, 0, NULL, ctx->config);
+ TEST_CHECK(parser != NULL);
+
+ /* Filter */
+ filter_ffd = flb_filter(ctx, (char *) "parser", NULL);
+ TEST_CHECK(filter_ffd >= 0);
+ ret = flb_filter_set(ctx, filter_ffd,
+ "Match", "test",
+ "Key_Name", "@timestamp",
+ "Parser", "timestamp",
+ "Reserve_Data", "On",
+ "Preserve_Key", "On",
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ /* Output */
+ out_ffd = flb_output(ctx, (char *) "lib", &cb);
+ TEST_CHECK(out_ffd >= 0);
+ flb_output_set(ctx, out_ffd,
+ "Match", "*",
+ "format", "json",
+ NULL);
+
+ /* Start the engine */
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data */
+ p = "[1448403340, {\"@timestamp\":\"2017_$!^-11-01T22:25:21.648\", \"log\":\"An example\"}]";
+ bytes = flb_lib_push(ctx, in_ffd, p, strlen(p));
+ TEST_CHECK(bytes == strlen(p));
+
+ wait_with_timeout(2000, &output); /* waiting flush and ensuring data flush */
+ TEST_CHECK_(output != NULL, "Expected output to not be NULL");
+ if (output != NULL) {
+ /* check the timestamp field was ignored and we received everything else */
+ expected = "[1448403340.000000,{\"@timestamp\":\"2017_$!^-11-01T22:25:21.648\",\"log\":\"An example\"}]";
+ TEST_CHECK_(strcmp(output, expected) == 0, "Expected output to be '%s', got '%s'", expected, output);
+ free(output);
+ }
+
+ flb_stop(ctx);
+ flb_destroy(ctx);
+}
+
+void flb_test_filter_parser_preserve_original_field()
+{
+ int ret;
+ int bytes;
+ char *p, *output, *expected;
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+ int filter_ffd;
+ struct flb_parser *parser;
+
+ struct flb_lib_out_cb cb;
+ cb.cb = callback_test;
+ cb.data = NULL;
+
+ clear_output();
+
+ ctx = flb_create();
+
+ /* Configure service */
+ flb_service_set(ctx, "Flush", FLUSH_INTERVAL, "Grace", "1", "Log_Level", "debug", NULL);
+
+ /* Input */
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ TEST_CHECK(in_ffd >= 0);
+ flb_input_set(ctx, in_ffd,
+ "Tag", "test",
+ NULL);
+
+ /* Parser */
+ parser = flb_parser_create("dummy_test", "regex", "^(?<INT>[^ ]+) (?<FLOAT>[^ ]+) (?<BOOL>[^ ]+) (?<STRING>.+)$",
+ FLB_TRUE,
+ NULL, NULL, NULL, MK_FALSE, MK_TRUE, FLB_FALSE, NULL, 0,
+ NULL, ctx->config);
+ TEST_CHECK(parser != NULL);
+
+ /* Filter */
+ filter_ffd = flb_filter(ctx, (char *) "parser", NULL);
+ TEST_CHECK(filter_ffd >= 0);
+ ret = flb_filter_set(ctx, filter_ffd,
+ "Match", "test",
+ "Key_Name", "data",
+ "Parser", "dummy_test",
+ "Reserve_Data", "On",
+ "Preserve_Key", "On",
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ /* Output */
+ out_ffd = flb_output(ctx, (char *) "lib", &cb);
+ TEST_CHECK(out_ffd >= 0);
+ flb_output_set(ctx, out_ffd,
+ "Match", "*",
+ "format", "json",
+ NULL);
+
+ /* Start the engine */
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data */
+ p = "[1448403340,{\"data\":\"100 0.5 true This is an example\",\"log\":\"An example\"}]";
+ bytes = flb_lib_push(ctx, in_ffd, p, strlen(p));
+ TEST_CHECK(bytes == strlen(p));
+
+ wait_with_timeout(2000, &output); /* waiting flush and ensuring data flush */
+ TEST_CHECK_(output != NULL, "Expected output to not be NULL");
+ if (output != NULL) {
+ /* check original field is preserved */
+ expected = "\"data\":\"100 0.5 true This is an example\"";
+ TEST_CHECK_(strstr(output, expected) != NULL, "Expected output to contain '%s', got '%s'", expected, output);
+ /* check fields were extracted */
+ expected = "\"INT\":\"100\",\"FLOAT\":\"0.5\",\"BOOL\":\"true\",\"STRING\":\"This is an example\"";
+ TEST_CHECK_(strstr(output, expected) != NULL, "Expected output to contain '%s', got '%s'", expected, output);
+ /* check other fields are preserved */
+ expected = "\"log\":\"An example\"";
+ TEST_CHECK_(strstr(output, expected) != NULL, "Expected output to contain '%s', got '%s'", expected, output);
+ free(output);
+ }
+
+ flb_stop(ctx);
+ flb_destroy(ctx);
+}
+
+// https://github.com/fluent/fluent-bit/issues/2250
+void flb_test_filter_parser_first_matched_when_mutilple_parser()
+{
+ int ret;
+ int bytes;
+ char *p, *output, *expected;
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+ int filter_ffd;
+ struct flb_parser *parser;
+
+ struct flb_lib_out_cb cb;
+ cb.cb = callback_test;
+ cb.data = NULL;
+
+ clear_output();
+
+ ctx = flb_create();
+
+ /* Configure service */
+ flb_service_set(ctx, "Flush", FLUSH_INTERVAL, "Grace" "1", "Log_Level", "debug", NULL);
+
+ /* Input */
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ TEST_CHECK(in_ffd >= 0);
+ flb_input_set(ctx, in_ffd,
+ "Tag", "test",
+ NULL);
+
+ /* Parser */
+ parser = flb_parser_create("one", "regex", "^(?<one>.+?)$",
+ FLB_TRUE,
+ NULL, NULL, NULL, MK_FALSE, MK_TRUE, FLB_FALSE,
+ NULL, 0, NULL, ctx->config);
+ TEST_CHECK(parser != NULL);
+
+ parser = flb_parser_create("two", "regex", "^(?<two>.+?)$",
+ FLB_TRUE,
+ NULL, NULL, NULL, MK_FALSE, MK_TRUE, FLB_FALSE,
+ NULL, 0, NULL, ctx->config);
+ TEST_CHECK(parser != NULL);
+
+ /* Filter */
+ filter_ffd = flb_filter(ctx, (char *) "parser", NULL);
+ TEST_CHECK(filter_ffd >= 0);
+ ret = flb_filter_set(ctx, filter_ffd,
+ "Match", "test",
+ "Key_Name", "data",
+ "Parser", "one",
+ "Parser", "two",
+ "Reserve_Data", "On",
+ "Preserve_Key", "On",
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ /* Output */
+ out_ffd = flb_output(ctx, (char *) "lib", &cb);
+ TEST_CHECK(out_ffd >= 0);
+ flb_output_set(ctx, out_ffd,
+ "Match", "*",
+ "format", "json",
+ NULL);
+
+ /* Start the engine */
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data */
+ p = "[1,{\"data\":\"hoge\"}]";
+ bytes = flb_lib_push(ctx, in_ffd, p, strlen(p));
+ TEST_CHECK(bytes == strlen(p));
+
+ wait_with_timeout(2000, &output); /* waiting flush and ensuring data flush */
+ TEST_CHECK_(output != NULL, "Expected output to not be NULL");
+ if (output != NULL) {
+ /* check extra data was not preserved */
+ expected = "\"one\":\"hoge\",\"data\":\"hoge\"";
+ TEST_CHECK_(strstr(output, expected) != NULL, "Expected output to contain key one , got '%s'", output);
+ free(output);
+ }
+
+ flb_stop(ctx);
+ flb_destroy(ctx);
+}
+
+// https://github.com/fluent/fluent-bit/issues/1486
+// https://github.com/fluent/fluent-bit/issues/2939
+void flb_test_filter_parser_skip_empty_values_false()
+{
+ int ret;
+ int bytes;
+ char *p, *output, *expected;
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+ int filter_ffd;
+ struct flb_parser *parser;
+
+ struct flb_lib_out_cb cb;
+ cb.cb = callback_test;
+ cb.data = NULL;
+
+ clear_output();
+
+ ctx = flb_create();
+
+ /* Configure service */
+ flb_service_set(ctx, "Flush", FLUSH_INTERVAL, "Grace" "1", "Log_Level", "debug", NULL);
+
+ /* Input */
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ TEST_CHECK(in_ffd >= 0);
+ flb_input_set(ctx, in_ffd,
+ "Tag", "test",
+ NULL);
+
+ /* Parser */
+ parser = flb_parser_create("one", "regex", "^(?<one>.+?)$",
+ FLB_FALSE,
+ NULL, NULL, NULL, MK_FALSE, MK_TRUE, FLB_FALSE,
+ NULL, 0, NULL, ctx->config);
+ TEST_CHECK(parser != NULL);
+
+ /* Filter */
+ filter_ffd = flb_filter(ctx, (char *) "parser", NULL);
+ TEST_CHECK(filter_ffd >= 0);
+ ret = flb_filter_set(ctx, filter_ffd,
+ "Match", "test",
+ "Key_Name", "data",
+ "Parser", "one",
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ /* Output */
+ out_ffd = flb_output(ctx, (char *) "lib", &cb);
+ TEST_CHECK(out_ffd >= 0);
+ flb_output_set(ctx, out_ffd,
+ "Match", "*",
+ "format", "json",
+ NULL);
+
+ /* Start the engine */
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data */
+ p = "[1,{\"data\":\"\"}]";
+ bytes = flb_lib_push(ctx, in_ffd, p, strlen(p));
+ TEST_CHECK(bytes == strlen(p));
+
+ wait_with_timeout(1500, &output); /* waiting flush and ensuring data flush */
+ TEST_CHECK_(output != NULL, "Expected output to not be NULL");
+ if (output != NULL) {
+ /* check extra data was not preserved */
+ expected = "\"data\":\"\"";
+ TEST_CHECK_(strstr(output, expected) != NULL, "Expected output to contain key one , got '%s'", output);
+ free(output);
+ }
+
+ flb_stop(ctx);
+ flb_destroy(ctx);
+}
+
+
+TEST_LIST = {
+ {"filter_parser_extract_fields", flb_test_filter_parser_extract_fields },
+ {"filter_parser_reserve_data_off", flb_test_filter_parser_reserve_data_off },
+ {"filter_parser_handle_time_key", flb_test_filter_parser_handle_time_key },
+ {"filter_parser_handle_time_key_with_time_zone", flb_test_filter_parser_handle_time_key_with_time_zone },
+ {"filter_parser_ignore_malformed_time", flb_test_filter_parser_ignore_malformed_time },
+ {"filter_parser_preserve_original_field", flb_test_filter_parser_preserve_original_field },
+ {"filter_parser_first_matched_when_multiple_parser", flb_test_filter_parser_first_matched_when_mutilple_parser },
+ {"filter_parser_skip_empty_values_false", flb_test_filter_parser_skip_empty_values_false},
+ {NULL, NULL}
+};
+
diff --git a/fluent-bit/tests/runtime/filter_record_modifier.c b/fluent-bit/tests/runtime/filter_record_modifier.c
new file mode 100644
index 00000000..2dbc9322
--- /dev/null
+++ b/fluent-bit/tests/runtime/filter_record_modifier.c
@@ -0,0 +1,595 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/* Fluent Bit
+ * ==========
+ * Copyright (C) 2019-2021 The Fluent Bit Authors
+ * Copyright (C) 2015-2018 Treasure Data Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <fluent-bit.h>
+#include <fluent-bit/flb_time.h>
+#include <msgpack.h>
+#include "flb_tests_runtime.h"
+#include "data/common/json_long.h" /* JSON_LONG */
+
+struct filter_test {
+ flb_ctx_t *flb; /* Fluent Bit library context */
+ int i_ffd; /* Input fd */
+ int f_ffd; /* Filter fd */
+ int o_ffd; /* Output fd */
+};
+
+struct expect_str {
+ char *str;
+ int found;
+};
+
+
+/* Callback to check expected results */
+static int cb_check_result(void *record, size_t size, void *data)
+{
+ char *p;
+ char *result;
+ struct expect_str *expected;
+
+ expected = (struct expect_str*)data;
+ result = (char *) record;
+
+ if (!TEST_CHECK(expected != NULL)) {
+ flb_error("expected is NULL");
+ }
+ if (!TEST_CHECK(result != NULL)) {
+ flb_error("result is NULL");
+ }
+
+ while(expected != NULL && expected->str != NULL) {
+ if (expected->found == FLB_TRUE) {
+ p = strstr(result, expected->str);
+ if(!TEST_CHECK(p != NULL)) {
+ flb_error("Expected to find: '%s' in result '%s'",
+ expected->str, result);
+ }
+ }
+ else {
+ p = strstr(result, expected->str);
+ if(!TEST_CHECK(p == NULL)) {
+ flb_error("'%s' should be removed in result '%s'",
+ expected->str, result);
+ }
+ }
+
+ /*
+ * If you want to debug your test
+ *
+ * printf("Expect: '%s' in result '%s'", expected, result);
+ */
+
+ expected++;
+ }
+
+ flb_free(record);
+ return 0;
+}
+
+static struct filter_test *filter_test_create(struct flb_lib_out_cb *data)
+{
+ int i_ffd;
+ int f_ffd;
+ int o_ffd;
+ struct filter_test *ctx;
+
+ ctx = flb_malloc(sizeof(struct filter_test));
+ if (!ctx) {
+ flb_errno();
+ return NULL;
+ }
+
+ /* Service config */
+ ctx->flb = flb_create();
+ flb_service_set(ctx->flb,
+ "Flush", "0.200000000",
+ "Grace", "1",
+ "Log_Level", "error",
+ NULL);
+
+ /* Input */
+ i_ffd = flb_input(ctx->flb, (char *) "lib", NULL);
+ TEST_CHECK(i_ffd >= 0);
+ flb_input_set(ctx->flb, i_ffd, "tag", "test", NULL);
+ ctx->i_ffd = i_ffd;
+
+ /* Filter configuration */
+ f_ffd = flb_filter(ctx->flb, (char *) "record_modifier", NULL);
+ TEST_CHECK(f_ffd >= 0);
+ flb_filter_set(ctx->flb, f_ffd, "match", "*", NULL);
+ ctx->f_ffd = f_ffd;
+
+ /* Output */
+ o_ffd = flb_output(ctx->flb, (char *) "lib", (void *) data);
+ TEST_CHECK(o_ffd >= 0);
+ flb_output_set(ctx->flb, o_ffd,
+ "match", "test",
+ NULL);
+ ctx->o_ffd = o_ffd;
+
+ return ctx;
+}
+
+static void filter_test_destroy(struct filter_test *ctx)
+{
+ sleep(1);
+ flb_stop(ctx->flb);
+ flb_destroy(ctx->flb);
+ flb_free(ctx);
+}
+
+static void flb_records()
+{
+ int len;
+ int ret;
+ int bytes;
+ char *p;
+ struct flb_lib_out_cb cb_data;
+ struct filter_test *ctx;
+ struct expect_str expect[] = {
+ {"\"new_key\":\"new_val\"", FLB_TRUE},
+ {"\"add_key\":\"add_val\"", FLB_TRUE},
+ {"\"k\":\"sample\"", FLB_TRUE},
+ {NULL, FLB_TRUE}
+ };
+
+ /* Create test context */
+ ctx = filter_test_create((void *) &cb_data);
+ if (!ctx) {
+ exit(EXIT_FAILURE);
+ }
+
+ /* Configure filter */
+ ret = flb_filter_set(ctx->flb, ctx->f_ffd,
+ "record", "new_key new_val",
+ "record", "add_key add_val",
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ ret = flb_output_set(ctx->flb, ctx->o_ffd,
+ "format", "json",
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ /* Prepare output callback with expected result */
+ cb_data.cb = cb_check_result;
+ cb_data.data = &expect;
+
+ /* Start the engine */
+ ret = flb_start(ctx->flb);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data samples */
+ p = "[0, {\"k\":\"sample\"}]";
+ len = strlen(p);
+ bytes = flb_lib_push(ctx->flb, ctx->i_ffd, p, len);
+ TEST_CHECK(bytes == len);
+
+ filter_test_destroy(ctx);
+}
+
+static void flb_allowlist_keys()
+{
+ int len;
+ int ret;
+ int bytes;
+ char *p;
+ struct flb_lib_out_cb cb_data;
+ struct filter_test *ctx;
+ struct expect_str expect[] = {
+ {"\"aaa\":\"ok\"", FLB_TRUE},
+ {"\"bbb\":\"ok\"", FLB_TRUE},
+ {"\"ccc\":\"removed\"", FLB_FALSE},
+ {NULL, FLB_TRUE}
+ };
+
+ /* Create test context */
+ ctx = filter_test_create((void *) &cb_data);
+ if (!ctx) {
+ exit(EXIT_FAILURE);
+ }
+
+ /* Configure filter */
+ ret = flb_filter_set(ctx->flb, ctx->f_ffd,
+ "allowlist_key", "aaa",
+ "allowlist_key", "bbb",
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ ret = flb_output_set(ctx->flb, ctx->o_ffd,
+ "format", "json",
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ /* Prepare output callback with expected result */
+ cb_data.cb = cb_check_result;
+ cb_data.data = &expect;
+
+ /* Start the engine */
+ ret = flb_start(ctx->flb);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data samples */
+ /* The pair "ccc":"removed" should be removed */
+ p = "[0, {\"aaa\":\"ok\",\"ccc\":\"removed\",\"bbb\":\"ok\"}]";
+ len = strlen(p);
+ bytes = flb_lib_push(ctx->flb, ctx->i_ffd, p, len);
+ TEST_CHECK(bytes == len);
+
+ filter_test_destroy(ctx);
+}
+
+static void flb_whitelist_keys()
+{
+ int len;
+ int ret;
+ int bytes;
+ char *p;
+ struct flb_lib_out_cb cb_data;
+ struct filter_test *ctx;
+ struct expect_str expect[] = {
+ {"\"aaa\":\"ok\"", FLB_TRUE},
+ {"\"bbb\":\"ok\"", FLB_TRUE},
+ {"\"ccc\":\"removed\"", FLB_FALSE},
+ {NULL, FLB_TRUE}
+ };
+
+ /* Create test context */
+ ctx = filter_test_create((void *) &cb_data);
+ if (!ctx) {
+ exit(EXIT_FAILURE);
+ }
+
+ /* Configure filter */
+ ret = flb_filter_set(ctx->flb, ctx->f_ffd,
+ "whitelist_key", "aaa",
+ "whitelist_key", "bbb",
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ ret = flb_output_set(ctx->flb, ctx->o_ffd,
+ "format", "json",
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ /* Prepare output callback with expected result */
+ cb_data.cb = cb_check_result;
+ cb_data.data = &expect;
+
+ /* Start the engine */
+ ret = flb_start(ctx->flb);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data samples */
+ /* The pair "ccc":"removed" should be removed */
+ p = "[0, {\"aaa\":\"ok\",\"ccc\":\"removed\",\"bbb\":\"ok\"}]";
+ len = strlen(p);
+ bytes = flb_lib_push(ctx->flb, ctx->i_ffd, p, len);
+ TEST_CHECK(bytes == len);
+
+ filter_test_destroy(ctx);
+}
+
+static void flb_remove_keys()
+{
+ int len;
+ int ret;
+ int bytes;
+ char *p;
+ struct flb_lib_out_cb cb_data;
+ struct filter_test *ctx;
+ struct expect_str expect[] = {
+ {"\"aaa\":\"ok\"", FLB_TRUE},
+ {"\"bbb\":\"ok\"", FLB_TRUE},
+ {"\"ccc\":\"removed\"", FLB_FALSE},
+ {"\"ddd\":\"removed\"", FLB_FALSE},
+ {NULL, FLB_TRUE}
+ };
+
+ /* Create test context */
+ ctx = filter_test_create((void *) &cb_data);
+ if (!ctx) {
+ exit(EXIT_FAILURE);
+ }
+
+ /* Configure filter */
+ ret = flb_filter_set(ctx->flb, ctx->f_ffd,
+ "remove_key", "ccc",
+ "remove_key", "ddd",
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ ret = flb_output_set(ctx->flb, ctx->o_ffd,
+ "format", "json",
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ /* Prepare output callback with expected result */
+ cb_data.cb = cb_check_result;
+ cb_data.data = &expect;
+
+ /* Start the engine */
+ ret = flb_start(ctx->flb);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data samples */
+ /* The pairs "ccc" and "ddd" should be removed */
+ p = "[0, {\"aaa\":\"ok\",\"ccc\":\"removed\",\"ddd\":\"removed\",\"bbb\":\"ok\"}]";
+ len = strlen(p);
+ bytes = flb_lib_push(ctx->flb, ctx->i_ffd, p, len);
+ TEST_CHECK(bytes == len);
+
+ filter_test_destroy(ctx);
+}
+
+static void flb_multiple()
+{
+ int len;
+ int ret;
+ int bytes;
+ char *p;
+ struct flb_lib_out_cb cb_data;
+ struct filter_test *ctx;
+ struct expect_str expect[] = {
+ {"\"aaa\":\"ok\"", FLB_TRUE},
+ {"\"new_key\":\"new_val\"", FLB_TRUE},
+ {"\"ddd\":\"removed\"", FLB_FALSE},
+ {NULL, FLB_TRUE}
+ };
+
+ /* Create test context */
+ ctx = filter_test_create((void *) &cb_data);
+ if (!ctx) {
+ exit(EXIT_FAILURE);
+ }
+
+ /* Configure filter */
+ ret = flb_filter_set(ctx->flb, ctx->f_ffd,
+ "record", "new_key new_val",
+ "allowlist_key", "new_key",
+ "allowlist_key", "aaa",
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ ret = flb_output_set(ctx->flb, ctx->o_ffd,
+ "format", "json",
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ /* Prepare output callback with expected result */
+ cb_data.cb = cb_check_result;
+ cb_data.data = &expect;
+
+ /* Start the engine */
+ ret = flb_start(ctx->flb);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data samples */
+ p = "[0, {\"aaa\":\"ok\",\"ddd\":\"removed\",\"bbb\":\"ok\"}]";
+ len = strlen(p);
+ bytes = flb_lib_push(ctx->flb, ctx->i_ffd, p, len);
+ TEST_CHECK(bytes == len);
+
+ filter_test_destroy(ctx);
+}
+
+
+/* https://github.com/fluent/fluent-bit/issues/3968 */
+void flb_test_json_long()
+{
+ int ret;
+ int size = sizeof(JSON_LONG) - 1;
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+ int filter_ffd;
+
+ /* Create context, flush every second (some checks omitted here) */
+ ctx = flb_create();
+ flb_service_set(ctx, "flush", "1", "grace", "1",
+ "log_level", "error",
+ NULL);
+
+ /* Lib input mode */
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ flb_input_set(ctx, in_ffd, "tag", "test", NULL);
+ TEST_CHECK(in_ffd >= 0);
+
+ out_ffd = flb_output(ctx, (char *) "null", NULL);
+ TEST_CHECK(out_ffd >= 0);
+ flb_output_set(ctx, out_ffd, "match", "test", NULL);
+
+ filter_ffd = flb_filter(ctx, (char *) "record_modifier", NULL);
+ TEST_CHECK(filter_ffd >= 0);
+ flb_filter_set(ctx, filter_ffd, "match", "test", NULL);
+
+ /* Start */
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data sample */
+ flb_lib_push(ctx, in_ffd, (char *) JSON_LONG, size);
+
+ flb_time_msleep(1500); /* waiting flush */
+ flb_stop(ctx);
+ flb_destroy(ctx);
+}
+
+void flb_exclusive_setting()
+{
+ int ret;
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+ int filter_ffd;
+
+ /* Create context, flush every second (some checks omitted here) */
+ ctx = flb_create();
+ flb_service_set(ctx, "flush", "1", "grace", "1",
+ "log_level", "error",
+ NULL);
+
+ /* Lib input mode */
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ flb_input_set(ctx, in_ffd, "tag", "test", NULL);
+ TEST_CHECK(in_ffd >= 0);
+
+ out_ffd = flb_output(ctx, (char *) "null", NULL);
+ TEST_CHECK(out_ffd >= 0);
+ flb_output_set(ctx, out_ffd, "match", "test", NULL);
+
+ filter_ffd = flb_filter(ctx, (char *) "record_modifier", NULL);
+ TEST_CHECK(filter_ffd >= 0);
+ flb_filter_set(ctx, filter_ffd, "match", "test", NULL);
+
+ ret = flb_filter_set(ctx, filter_ffd,
+ "allowlist_key", "aaa",
+ "remove_key", "bbb",
+ NULL);
+
+ /* Start */
+ ret = flb_start(ctx);
+
+ /* It should be error since "allowlist_key" and "remove_key" are exclusive */
+ TEST_CHECK(ret != 0);
+
+ flb_stop(ctx);
+ flb_destroy(ctx);
+}
+
+#define UUID_KEY_NAME "uuid"
+static int cb_check_uuid(void *record, size_t size, void *data)
+{
+ char uuid[256] = {0};
+ msgpack_unpacked result;
+ msgpack_object obj;
+ size_t off = 0;
+ int ret;
+ int i_map;
+ int map_size;
+ int uuid_found = FLB_FALSE;
+ char uuid_part[5][16];
+
+#ifndef FLB_HAVE_OPENSSL
+ if (!TEST_CHECK(1 == 0)) {
+ /* flb_utils_uuid_v4_gen function needs openssl */
+ TEST_MSG("uuid_key needs OpenSSL");
+ return -1;
+ }
+#endif
+
+ if (!TEST_CHECK(record != NULL)) {
+ TEST_MSG("data is null");
+ return -1;
+ }
+
+ msgpack_unpacked_init(&result);
+ while (msgpack_unpack_next(&result, record, size, &off) == MSGPACK_UNPACK_SUCCESS) {
+ obj = result.data;
+ /*
+ msgpack_object_print(stdout, obj);
+ */
+ if (!TEST_CHECK(obj.type == MSGPACK_OBJECT_ARRAY && obj.via.array.size == 2)) {
+ TEST_MSG("array error. type = %d", obj.type);
+ continue;
+ }
+ obj = obj.via.array.ptr[1];
+ if (!TEST_CHECK(obj.type == MSGPACK_OBJECT_MAP)) {
+ TEST_MSG("map error. type = %d", obj.type);
+ continue;
+ }
+ map_size = obj.via.map.size;
+ for (i_map=0; i_map<map_size; i_map++) {
+ if (obj.via.map.ptr[i_map].key.type != MSGPACK_OBJECT_STR) {
+ continue;
+ }
+ if (strncmp(UUID_KEY_NAME, obj.via.map.ptr[i_map].key.via.str.ptr, strlen(UUID_KEY_NAME)) == 0) {
+ memcpy(&uuid[0], obj.via.map.ptr[i_map].val.via.str.ptr, obj.via.map.ptr[i_map].val.via.str.size);
+ ret = sscanf(&uuid[0], "%8s-%4s-%4s-%4s-%12s", &uuid_part[0][0], &uuid_part[1][0], &uuid_part[2][0], &uuid_part[3][0], &uuid_part[4][0]);
+ if (!TEST_CHECK(ret == 5)) {
+ TEST_MSG("ret should be 5. ret=%d", ret);
+ }
+ uuid_found = FLB_TRUE;
+ }
+ }
+ }
+
+ if (!TEST_CHECK(uuid_found == FLB_TRUE)) {
+ TEST_MSG("uuid not found");
+ }
+
+ msgpack_unpacked_destroy(&result);
+ flb_free(record);
+
+ return 0;
+}
+
+static void flb_uuid_key()
+{
+ int len;
+ int ret;
+ int bytes;
+ int not_used;
+ char *p;
+ struct flb_lib_out_cb cb_data;
+ struct filter_test *ctx;
+
+ /* Prepare output callback with expected result */
+ cb_data.cb = cb_check_uuid;
+ cb_data.data = &not_used;
+
+ /* Create test context */
+ ctx = filter_test_create((void *) &cb_data);
+ if (!ctx) {
+ exit(EXIT_FAILURE);
+ }
+
+ /* Configure filter */
+ ret = flb_filter_set(ctx->flb, ctx->f_ffd,
+ "uuid_key", UUID_KEY_NAME,
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ /* Start the engine */
+ ret = flb_start(ctx->flb);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data samples */
+ p = "[0, {\"key_name\":\"sample\"}]";
+ len = strlen(p);
+ bytes = flb_lib_push(ctx->flb, ctx->i_ffd, p, len);
+ TEST_CHECK(bytes == len);
+
+ filter_test_destroy(ctx);
+}
+
+
+/* test list */
+TEST_LIST = {
+ {"json_long" , flb_test_json_long },
+ {"remove_keys" , flb_remove_keys},
+ {"records" , flb_records},
+ {"allowlist_keys" , flb_allowlist_keys},
+ {"whitelist_keys" , flb_whitelist_keys},
+ {"multiple" , flb_multiple},
+ {"exclusive_setting" , flb_exclusive_setting},
+ {"uuid_key" , flb_uuid_key},
+ {NULL, NULL}
+};
diff --git a/fluent-bit/tests/runtime/filter_rewrite_tag.c b/fluent-bit/tests/runtime/filter_rewrite_tag.c
new file mode 100644
index 00000000..08cf3751
--- /dev/null
+++ b/fluent-bit/tests/runtime/filter_rewrite_tag.c
@@ -0,0 +1,564 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/* Fluent Bit
+ * ==========
+ * Copyright (C) 2019-2022 The Fluent Bit Authors
+ * Copyright (C) 2015-2018 Treasure Data Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <fluent-bit.h>
+#include <fluent-bit/flb_time.h>
+#include <msgpack.h>
+#include "flb_tests_runtime.h"
+
+struct filter_test {
+ flb_ctx_t *flb; /* Fluent Bit library context */
+ int i_ffd; /* Input fd */
+ int f_ffd; /* Filter fd */
+ int o_ffd; /* Output fd */
+};
+
+struct expect_str {
+ char *str;
+ int found;
+};
+
+pthread_mutex_t result_mutex = PTHREAD_MUTEX_INITIALIZER;
+int num_output = 0;
+
+static int cb_count_msgpack(void *record, size_t size, void *data)
+{
+ msgpack_unpacked result;
+ size_t off = 0;
+
+ if (!TEST_CHECK(data != NULL)) {
+ flb_error("data is NULL");
+ }
+
+ /* Iterate each item array and apply rules */
+ msgpack_unpacked_init(&result);
+ while (msgpack_unpack_next(&result, record, size, &off) == MSGPACK_UNPACK_SUCCESS) {
+ pthread_mutex_lock(&result_mutex);
+ num_output++;
+ pthread_mutex_unlock(&result_mutex);
+ }
+ msgpack_unpacked_destroy(&result);
+
+ flb_free(record);
+ return 0;
+}
+
+static void clear_output_num()
+{
+ pthread_mutex_lock(&result_mutex);
+ num_output = 0;
+ pthread_mutex_unlock(&result_mutex);
+}
+
+static int get_output_num()
+{
+ int ret;
+ pthread_mutex_lock(&result_mutex);
+ ret = num_output;
+ pthread_mutex_unlock(&result_mutex);
+
+ return ret;
+}
+
+static struct filter_test *filter_test_create(struct flb_lib_out_cb *data)
+{
+ int i_ffd;
+ int f_ffd;
+ int o_ffd;
+ struct filter_test *ctx;
+
+ ctx = flb_malloc(sizeof(struct filter_test));
+ if (!ctx) {
+ flb_errno();
+ return NULL;
+ }
+
+ /* Service config */
+ ctx->flb = flb_create();
+ flb_service_set(ctx->flb,
+ "Flush", "0.200000000",
+ "Grace", "1",
+ "Log_Level", "error",
+ NULL);
+
+ /* Input */
+ i_ffd = flb_input(ctx->flb, (char *) "lib", NULL);
+ TEST_CHECK(i_ffd >= 0);
+ flb_input_set(ctx->flb, i_ffd, "tag", "rewrite", NULL);
+ ctx->i_ffd = i_ffd;
+
+ /* Filter configuration */
+ f_ffd = flb_filter(ctx->flb, (char *) "rewrite_tag", NULL);
+ TEST_CHECK(f_ffd >= 0);
+ flb_filter_set(ctx->flb, f_ffd, "match", "rewrite", NULL);
+ ctx->f_ffd = f_ffd;
+
+ /* Output */
+ o_ffd = flb_output(ctx->flb, (char *) "lib", (void *) data);
+ ctx->o_ffd = o_ffd;
+
+ return ctx;
+}
+
+static void filter_test_destroy(struct filter_test *ctx)
+{
+ sleep(1);
+ flb_stop(ctx->flb);
+ flb_destroy(ctx->flb);
+ flb_free(ctx);
+}
+
+
+/*
+ * Original tag: rewrite
+ * Rewritten tag: updated
+ */
+static void flb_test_matched()
+{
+ struct flb_lib_out_cb cb_data;
+ struct filter_test *ctx;
+ int ret;
+ int not_used = 0;
+ int bytes;
+ int got;
+ char *p = "[0, {\"key\":\"rewrite\"}]";
+
+ /* Prepare output callback with expected result */
+ cb_data.cb = cb_count_msgpack;
+ cb_data.data = &not_used;
+
+ /* Create test context */
+ ctx = filter_test_create((void *) &cb_data);
+ if (!ctx) {
+ exit(EXIT_FAILURE);
+ }
+ clear_output_num();
+ /* Configure filter */
+ ret = flb_filter_set(ctx->flb, ctx->f_ffd,
+ "Rule", "$key ^(rewrite)$ updated false",
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ /* Configure output */
+ ret = flb_output_set(ctx->flb, ctx->o_ffd,
+ "Match", "updated",
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ /* Start the engine */
+ ret = flb_start(ctx->flb);
+ TEST_CHECK(ret == 0);
+
+ /* ingest record */
+ bytes = flb_lib_push(ctx->flb, ctx->i_ffd, p, strlen(p));
+ TEST_CHECK(bytes == strlen(p));
+
+ flb_time_msleep(1500); /* waiting flush */
+ got = get_output_num();
+
+ if (!TEST_CHECK(got != 0)) {
+ TEST_MSG("expect: %d got: %d", 1, got);
+ }
+
+ filter_test_destroy(ctx);
+}
+
+/*
+ * Original tag: rewrite
+ * Rewritten tag: updated
+ */
+static void flb_test_not_matched()
+{
+ struct flb_lib_out_cb cb_data;
+ struct filter_test *ctx;
+ int ret;
+ int not_used = 0;
+ int bytes;
+ int got;
+ char *p = "[0, {\"key\":\"not_match\"}]";
+
+ /* Prepare output callback with expected result */
+ cb_data.cb = cb_count_msgpack;
+ cb_data.data = &not_used;
+
+ /* Create test context */
+ ctx = filter_test_create((void *) &cb_data);
+ if (!ctx) {
+ exit(EXIT_FAILURE);
+ }
+ clear_output_num();
+ /* Configure filter */
+ ret = flb_filter_set(ctx->flb, ctx->f_ffd,
+ "Rule", "$key ^(rewrite)$ updated false",
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ /* Configure output */
+ ret = flb_output_set(ctx->flb, ctx->o_ffd,
+ "Match", "rewrite",
+ NULL);
+ TEST_CHECK(ret == 0);
+
+
+ /* Start the engine */
+ ret = flb_start(ctx->flb);
+ TEST_CHECK(ret == 0);
+
+ /* ingest record */
+ bytes = flb_lib_push(ctx->flb, ctx->i_ffd, p, strlen(p));
+ TEST_CHECK(bytes == strlen(p));
+
+ flb_time_msleep(1500); /* waiting flush */
+ got = get_output_num();
+
+ if (!TEST_CHECK(got != 0)) {
+ TEST_MSG("expect: %d got: %d", 1, got);
+ }
+
+ filter_test_destroy(ctx);
+}
+
+/*
+ * Original tag: rewrite
+ * Rewritten tag: updated
+ */
+static void flb_test_keep_true()
+{
+ struct flb_lib_out_cb cb_data;
+ struct filter_test *ctx;
+ int ret;
+ int not_used = 0;
+ int bytes;
+ int got;
+ char *p = "[0, {\"key\":\"rewrite\"}]";
+
+ /* Prepare output callback with expected result */
+ cb_data.cb = cb_count_msgpack;
+ cb_data.data = &not_used;
+
+ /* Create test context */
+ ctx = filter_test_create((void *) &cb_data);
+ if (!ctx) {
+ exit(EXIT_FAILURE);
+ }
+ clear_output_num();
+ /* Configure filter */
+ ret = flb_filter_set(ctx->flb, ctx->f_ffd,
+ "Rule", "$key ^(rewrite)$ updated true",
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ /* Configure output to count up all record */
+ ret = flb_output_set(ctx->flb, ctx->o_ffd, "Match", "*", NULL);
+ TEST_CHECK(ret == 0);
+
+ /* Start the engine */
+ ret = flb_start(ctx->flb);
+ TEST_CHECK(ret == 0);
+
+ /* ingest record */
+ bytes = flb_lib_push(ctx->flb, ctx->i_ffd, p, strlen(p));
+ TEST_CHECK(bytes == strlen(p));
+
+ flb_time_msleep(1500); /* waiting flush */
+ got = get_output_num();
+
+ /* original record(keep) + rewritten record */
+ if (!TEST_CHECK(got == 2)) {
+ TEST_MSG("expect: %d got: %d", 2, got);
+ }
+
+ filter_test_destroy(ctx);
+}
+
+/* https://github.com/fluent/fluent-bit/issues/4049
+ * Emitter should pause if tons of input come.
+ */
+static void flb_test_heavy_input_pause_emitter()
+{
+ struct flb_lib_out_cb cb_data;
+ struct filter_test *ctx;
+ int ret;
+ int not_used = 0;
+ int bytes;
+ int heavy_loop = 100000;
+ int got;
+ char p[256];
+ int i;
+
+ /* Prepare output callback with expected result */
+ cb_data.cb = cb_count_msgpack;
+ cb_data.data = &not_used;
+
+ /* Create test context */
+ ctx = filter_test_create((void *) &cb_data);
+ if (!ctx) {
+ exit(EXIT_FAILURE);
+ }
+ clear_output_num();
+ /* Configure filter */
+ ret = flb_filter_set(ctx->flb, ctx->f_ffd,
+ "Rule", "$key ^(rewrite)$ updated false",
+ "Emitter_Mem_Buf_Limit", "1kb",
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ /* Configure output */
+ ret = flb_output_set(ctx->flb, ctx->o_ffd,
+ "Match", "updated",
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ /* Suppress emitter log. error registering chunk with tag: updated */
+ ret = flb_service_set(ctx->flb, "Log_Level", "Off", NULL);
+ TEST_CHECK(ret == 0);
+
+ /* Start the engine */
+ ret = flb_start(ctx->flb);
+ TEST_CHECK(ret == 0);
+
+ for (i = 0; i < heavy_loop; i++) {
+ memset(p, '\0', sizeof(p));
+ snprintf(p, sizeof(p), "[%d, {\"val\": \"%d\",\"key\": \"rewrite\"}]", i, i);
+ bytes = flb_lib_push(ctx->flb, ctx->i_ffd, p, strlen(p));
+ TEST_CHECK(bytes == strlen(p));
+ }
+
+ flb_time_msleep(1500); /* waiting flush */
+ got = get_output_num();
+
+ if (!TEST_CHECK(got != 0)) {
+ TEST_MSG("callback is not invoked");
+ }
+
+ /* Input should be paused since Mem_Buf_Limit is small size.
+ * So got is less than heavy_loop.
+ */
+ if(!TEST_CHECK(heavy_loop > got)) {
+ TEST_MSG("expect: %d got: %d", heavy_loop, got);
+ }
+
+ filter_test_destroy(ctx);
+}
+
+static void flb_test_issue_4793()
+{
+ struct flb_lib_out_cb cb_data;
+ struct filter_test *ctx;
+ int ret;
+ int not_used = 0;
+ int loop_max = 4;
+ int bytes;
+ int got;
+ char p[256];
+ int i;
+
+ /* Prepare output callback with expected result */
+ cb_data.cb = cb_count_msgpack;
+ cb_data.data = &not_used;
+
+ /* Create test context */
+ ctx = filter_test_create((void *) &cb_data);
+ if (!ctx) {
+ exit(EXIT_FAILURE);
+ }
+ clear_output_num();
+ /* Configure filter */
+ ret = flb_filter_set(ctx->flb, ctx->f_ffd,
+ "Rule", "$destination ^(server)$ updated false",
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ /* Configure output */
+ ret = flb_output_set(ctx->flb, ctx->o_ffd, "Match", "*", NULL);
+ TEST_CHECK(ret == 0);
+
+ /* Start the engine */
+ ret = flb_start(ctx->flb);
+ TEST_CHECK(ret == 0);
+
+
+ /* emit (loop_max * 2) records */
+ for (i = 0; i < loop_max; i++) {
+ /* "destination": "server" */
+ memset(p, '\0', sizeof(p));
+ snprintf(p, sizeof(p), "[%d, {\"val\": \"%d\",\"destination\": \"server\"}]", i, i);
+ bytes = flb_lib_push(ctx->flb, ctx->i_ffd, p, strlen(p));
+ TEST_CHECK(bytes == strlen(p));
+
+ /* "destination": "other" */
+ memset(p, '\0', sizeof(p));
+ snprintf(p, sizeof(p), "[%d, {\"val\": \"%d\",\"destination\": \"other\"}]", i+1, i+1);
+ bytes = flb_lib_push(ctx->flb, ctx->i_ffd, p, strlen(p));
+ TEST_CHECK(bytes == strlen(p));
+ }
+
+ flb_time_msleep(1500); /* waiting flush */
+ got = get_output_num();
+
+ if (!TEST_CHECK(got != 0)) {
+ TEST_MSG("callback is not invoked");
+ }
+
+ if(!TEST_CHECK(2*loop_max == got)) {
+ TEST_MSG("expect: %d got: %d", 2 * loop_max, got);
+ }
+
+ filter_test_destroy(ctx);
+}
+
+static void flb_test_issue_4518()
+{
+ struct flb_lib_out_cb cb_data;
+ struct filter_test *ctx;
+ int ret;
+ int not_used = 0;
+ int loop_max = 2;
+ int bytes;
+ int got;
+ char p[256];
+ int i;
+ int f_ffd;
+
+ /* Prepare output callback with expected result */
+ cb_data.cb = cb_count_msgpack;
+ cb_data.data = &not_used;
+
+ /* Create test context */
+ ctx = filter_test_create((void *) &cb_data);
+ if (!ctx) {
+ exit(EXIT_FAILURE);
+ }
+ clear_output_num();
+
+ /* Configure output */
+ ret = flb_output_set(ctx->flb, ctx->o_ffd,
+ "Match", "*",
+ NULL);
+
+ /* create 2nd filter */
+ f_ffd = flb_filter(ctx->flb, (char *) "rewrite_tag", NULL);
+ TEST_CHECK(f_ffd >= 0);
+ flb_filter_set(ctx->flb, f_ffd, "match", "rewrite", NULL);
+ /* Configure filter */
+ ret = flb_filter_set(ctx->flb, f_ffd,
+ "Rule", "$test3 ^(true)$ updated true",
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ /* Configure 1st filter */
+ ret = flb_filter_set(ctx->flb, ctx->f_ffd,
+ "Rule", "$test2 ^(true)$ updated true",
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ /* Start the engine */
+ ret = flb_start(ctx->flb);
+ TEST_CHECK(ret == 0);
+
+ for (i = 0; i < loop_max; i++) {
+ memset(p, '\0', sizeof(p));
+ /* 1st filter duplicates below record. */
+ snprintf(p, sizeof(p), "[%d, {\"msg\":\"DEBUG\", \"val\": \"%d\",\"test2\": \"true\"}]", i, i);
+ bytes = flb_lib_push(ctx->flb, ctx->i_ffd, p, strlen(p));
+ TEST_CHECK(bytes == strlen(p));
+
+ /* 2nd filter duplicates below record. */
+ memset(p, '\0', sizeof(p));
+ snprintf(p, sizeof(p), "[%d, {\"msg\":\"ERROR\", \"val\": \"%d\",\"test3\": \"true\"}]", i, i);
+ bytes = flb_lib_push(ctx->flb, ctx->i_ffd, p, strlen(p));
+ TEST_CHECK(bytes == strlen(p));
+ }
+
+ flb_time_msleep(1500); /* waiting flush */
+ got = get_output_num();
+
+ if (!TEST_CHECK(got != 0)) {
+ TEST_MSG("callback is not invoked");
+ }
+
+ /* Output should be 4 * loop_max.
+ 1st filter appends 1 record and 2nd filter also appends 1 record.
+ Original 2 records + 1 record(1st filter) + 1 record(2nd filter) = 4 records.
+ */
+ if(!TEST_CHECK(4*loop_max == got)) {
+ TEST_MSG("expect: %d got: %d", 4 * loop_max, got);
+ }
+
+ filter_test_destroy(ctx);
+}
+
+/* $TAG as a key of rule causes SIGSEGV */
+static void flb_test_issue_5846()
+{
+ struct flb_lib_out_cb cb_data;
+ struct filter_test *ctx;
+ int ret;
+ int not_used = 0;
+ int bytes;
+ char *p = "[0, {\"key\":\"rewrite\"}]";
+
+ /* Prepare output callback with expected result */
+ cb_data.cb = cb_count_msgpack;
+ cb_data.data = &not_used;
+
+ /* Create test context */
+ ctx = filter_test_create((void *) &cb_data);
+ if (!ctx) {
+ exit(EXIT_FAILURE);
+ }
+ clear_output_num();
+ /* Configure filter */
+ ret = flb_filter_set(ctx->flb, ctx->f_ffd,
+ "Rule", "$TAG ^(rewrite)$ updated false",
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ /* Configure output */
+ ret = flb_output_set(ctx->flb, ctx->o_ffd,
+ "Match", "updated",
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ /* Start the engine */
+ ret = flb_start(ctx->flb);
+ TEST_CHECK(ret == 0);
+
+ /* ingest record */
+ bytes = flb_lib_push(ctx->flb, ctx->i_ffd, p, strlen(p));
+ TEST_CHECK(bytes == strlen(p));
+
+ flb_time_msleep(1500); /* waiting flush */
+
+ /* It is OK, if there is no SIGSEGV. */
+
+ filter_test_destroy(ctx);
+}
+
+TEST_LIST = {
+ {"matched", flb_test_matched},
+ {"not_matched", flb_test_not_matched},
+ {"keep_true", flb_test_keep_true},
+ {"heavy_input_pause_emitter", flb_test_heavy_input_pause_emitter},
+ {"issue_4518", flb_test_issue_4518},
+ {"issue_4793", flb_test_issue_4793},
+ {"sigsegv_issue_5846", flb_test_issue_5846},
+ {NULL, NULL}
+};
diff --git a/fluent-bit/tests/runtime/filter_stdout.c b/fluent-bit/tests/runtime/filter_stdout.c
new file mode 100644
index 00000000..835ad8ed
--- /dev/null
+++ b/fluent-bit/tests/runtime/filter_stdout.c
@@ -0,0 +1,84 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+#include <fluent-bit.h>
+#include "flb_tests_runtime.h"
+
+/* Test data */
+
+/* Test functions */
+void flb_test_filter_stdout_json_multiple(void);
+void flb_test_filter_stdout_case_insensitive(void);
+
+/* Test list */
+TEST_LIST = {
+ {"json_multiple", flb_test_filter_stdout_json_multiple },
+ {"case_insensitive_name", flb_test_filter_stdout_case_insensitive},
+ {NULL, NULL}
+};
+
+/*
+ * This test case is to check if fluent-bit allows case-insensitive plugin name.
+ * This test is not unique to filter_stdout, but we test here :) ,
+ */
+
+void flb_test_filter_stdout_case_insensitive(void)
+{
+ int filter_ffd;
+ char filter_name[] = "stDoUt";
+ flb_ctx_t *ctx;
+
+ ctx = flb_create();
+
+ filter_ffd = flb_filter(ctx, (char *) filter_name, NULL);
+ if(!TEST_CHECK(filter_ffd >= 0)) {
+ TEST_MSG("%s should be valid\n", filter_name);
+ }
+
+ /* Initialize thread local storage (FLB_TLS) properly when without calling flb_start().
+ * Then, FLB_TLS_GET working on macOS.
+ * In general, macOS requests surely initialization for pthread stuffs.
+ */
+ flb_init_env();
+ flb_destroy(ctx);
+}
+
+void flb_test_filter_stdout_json_multiple(void)
+{
+ int i;
+ int ret;
+ int bytes;
+ char p[100];
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+ int filter_ffd;
+
+ ctx = flb_create();
+
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ TEST_CHECK(in_ffd >= 0);
+ flb_input_set(ctx, in_ffd, "tag", "test", NULL);
+
+ out_ffd = flb_output(ctx, (char *) "null", NULL);
+ TEST_CHECK(out_ffd >= 0);
+ flb_output_set(ctx, out_ffd, "match", "test", NULL);
+
+ filter_ffd = flb_filter(ctx, (char *) "stdout", NULL);
+ TEST_CHECK(filter_ffd >= 0);
+ flb_filter_set(ctx, filter_ffd, "match", "test", NULL);
+
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ for (i = 0; i < 256; i++) {
+ memset(p, '\0', sizeof(p));
+ snprintf(p, sizeof(p), "[%d, {\"val\": %d,\"END_KEY\": \"JSON_END\"}]", i, (i * i));
+ bytes = flb_lib_push(ctx, in_ffd, p, strlen(p));
+ TEST_CHECK(bytes == strlen(p));
+ }
+
+ sleep(1); /* waiting flush */
+
+ flb_stop(ctx);
+ flb_destroy(ctx);
+}
diff --git a/fluent-bit/tests/runtime/filter_throttle.c b/fluent-bit/tests/runtime/filter_throttle.c
new file mode 100644
index 00000000..7654e6fe
--- /dev/null
+++ b/fluent-bit/tests/runtime/filter_throttle.c
@@ -0,0 +1,105 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+#include <fluent-bit.h>
+#include "flb_tests_runtime.h"
+
+/* Test data */
+
+/* Utility functions */
+pthread_mutex_t result_mutex = PTHREAD_MUTEX_INITIALIZER;
+
+/* Test functions */
+void flb_test_filter_throttle(void);
+void flb_test_filter_window_0(void);
+
+/* Test list */
+TEST_LIST = {
+ {"throttle", flb_test_filter_throttle },
+ {"window_0", flb_test_filter_window_0 },
+ {NULL, NULL}
+};
+
+
+void flb_test_filter_throttle(void)
+{
+ int i;
+ int ret;
+ int bytes;
+ char p[100];
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+ int filter_ffd;
+
+ ctx = flb_create();
+
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ TEST_CHECK(in_ffd >= 0);
+ flb_input_set(ctx, in_ffd, "tag", "test", NULL);
+
+ out_ffd = flb_output(ctx, (char *) "stdout", NULL);
+ TEST_CHECK(out_ffd >= 0);
+ flb_output_set(ctx, out_ffd, "match", "test", NULL);
+
+ filter_ffd = flb_filter(ctx, (char *) "throttle", NULL);
+ TEST_CHECK(filter_ffd >= 0);
+ ret = flb_filter_set(ctx, filter_ffd, "match", "*", NULL);
+ TEST_CHECK(ret == 0);
+ ret = flb_filter_set(ctx, filter_ffd, "rate", "9", NULL);
+ TEST_CHECK(ret == 0);
+ ret = flb_filter_set(ctx, filter_ffd, "window", "3", NULL);
+ TEST_CHECK(ret == 0);
+ ret = flb_filter_set(ctx, filter_ffd, "interval", "3s", NULL);
+ TEST_CHECK(ret == 0);
+ ret = flb_filter_set(ctx, filter_ffd, "print_status", "true", NULL);
+ TEST_CHECK(ret == 0);
+
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ /* Send log messages all should go through */
+ for (i = 0; i < 256; i++) {
+ memset(p, '\0', sizeof(p));
+ snprintf(p, sizeof(p), "[%d, {\"val\": \"%d\",\"END_KEY\": \"JSON_END\"}]", i, i);
+ bytes = flb_lib_push(ctx, in_ffd, p, strlen(p));
+ TEST_CHECK(bytes == strlen(p));
+ }
+
+ sleep(1); /* waiting flush */
+
+ flb_stop(ctx);
+ flb_destroy(ctx);
+}
+
+void flb_test_filter_window_0(void)
+{
+ int ret;
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+ int filter_ffd;
+
+ ctx = flb_create();
+
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ TEST_CHECK(in_ffd >= 0);
+ flb_input_set(ctx, in_ffd, "tag", "test", NULL);
+
+ out_ffd = flb_output(ctx, (char *) "stdout", NULL);
+ TEST_CHECK(out_ffd >= 0);
+ flb_output_set(ctx, out_ffd, "match", "test", NULL);
+
+ filter_ffd = flb_filter(ctx, (char *) "throttle", NULL);
+ TEST_CHECK(filter_ffd >= 0);
+ ret = flb_filter_set(ctx, filter_ffd, "match", "*",
+ "window", "0",
+ NULL);
+
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ sleep(1); /* waiting flush */
+
+ flb_stop(ctx);
+ flb_destroy(ctx);
+}
diff --git a/fluent-bit/tests/runtime/filter_throttle_size.c b/fluent-bit/tests/runtime/filter_throttle_size.c
new file mode 100644
index 00000000..b3f7c3c8
--- /dev/null
+++ b/fluent-bit/tests/runtime/filter_throttle_size.c
@@ -0,0 +1,608 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+#include <fluent-bit.h>
+#include "flb_tests_runtime.h"
+#include <stdio.h>
+
+#define FLUSH_DURATION 1
+#define WAIT_FOR_FLUSH sleep(FLUSH_DURATION);
+
+/* Test data */
+static const char *simple_log =
+ "[1519233660.000000, {\"log\":\"%s\", \"stream\":\"%s\", \"time\":\"2018-02-21T17:26:53.360920913Z\", \"value\":\"%d\"}]";
+static const char *simple_log_with_missing_log_key =
+ "[1519233660.000000, {\"msg\":\"%s\", \"stream\":\"%s\", \"time\":\"2018-02-21T17:26:53.360920913Z\", \"value\":\"%d\"}]";
+static const char *simple_log_with_missing_stream_key =
+ "[1519233660.000000, {\"log\":\"%s\", \"source\":\"%s\", \"time\":\"2018-02-21T17:26:53.360920913Z\", \"value\":\"%d\"}]";
+static const char *nested_log =
+ "[1519234013.360921, {\"logwrapper\":{\"log\":\"%s\", \"extra\":\"field\"}, \"stream\":\"stdout\", \"time\":\"2018-02-21T17:26:53.360920913Z\", \"kubernetes\":{\"pod_name\":\"%s\", \"namespace_name\":\"default\", \"pod_id\":\"64f7da23-172c-11e8-bfad-080027749cbc\", \"labels\":{\"run\":\"apache-logs\"}, \"host\":\"minikube\", \"container_name\":\"apache-logs\", \"docker_id\":\"ac6095b6c715d823d732dcc9067f75b1299de5cc69a012b08d616a6058bdc0ad\"}, \"va\":\"%d\"}]";
+static const char *swap_log_field =
+ "[1519234013.360921, {\"log\":{\"logwrapper\":\"%s\", \"extra\":\"field\"}, \"stream\":\"stdout\", \"time\":\"2018-02-21T17:26:53.360920913Z\", \"kubernetes\":{\"pod_name\":\"%s\", \"namespace_name\":\"default\", \"pod_id\":\"64f7da23-172c-11e8-bfad-080027749cbc\", \"labels\":{\"run\":\"apache-logs\"}, \"host\":\"minikube\", \"container_name\":\"apache-logs\", \"docker_id\":\"ac6095b6c715d823d732dcc9067f75b1299de5cc69a012b08d616a6058bdc0ad\"}, \"va\":\"%d\"}]";
+static const char *swap_name_field =
+ "[1519234013.360921, {\"logwrapper\":{\"log\":\"%s\", \"extra\":\"field\"}, \"stream\":\"stdout\", \"time\":\"2018-02-21T17:26:53.360920913Z\", \"pod_name\":{\"kubernetes\":\"%s\", \"namespace_name\":\"default\", \"pod_id\":\"64f7da23-172c-11e8-bfad-080027749cbc\", \"labels\":{\"run\":\"apache-logs\"}, \"host\":\"minikube\", \"container_name\":\"apache-logs\", \"docker_id\":\"ac6095b6c715d823d732dcc9067f75b1299de5cc69a012b08d616a6058bdc0ad\"}, \"va\":\"%d\"}]";
+static const char *nested_log_with_missing_log_field =
+ "[1519234013.360921, {\"logwrapper\":{\"msg\":\"%s\", \"extra\":\"field\"}, \"stream\":\"stdout\", \"time\":\"2018-02-21T17:26:53.360920913Z\", \"kubernetes\":{\"pod_name\":\"%s\", \"namespace_name\":\"default\", \"pod_id\":\"64f7da23-172c-11e8-bfad-080027749cbc\", \"labels\":{\"run\":\"apache-logs\"}, \"host\":\"minikube\", \"container_name\":\"apache-logs\", \"docker_id\":\"ac6095b6c715d823d732dcc9067f75b1299de5cc69a012b08d616a6058bdc0ad\"}, \"va\":\"%d\"}]";
+static const char *nested_log_with_missing_name_field =
+ "[1519234013.360921, {\"logwrapper\":{\"log\":\"%s\", \"extra\":\"field\"}, \"stream\":\"stdout\", \"time\":\"2018-02-21T17:26:53.360920913Z\", \"kubernetes\":{\"pod_alias\":\"%s\", \"namespace_name\":\"default\", \"pod_id\":\"64f7da23-172c-11e8-bfad-080027749cbc\", \"labels\":{\"run\":\"apache-logs\"}, \"host\":\"minikube\", \"container_name\":\"apache-logs\", \"docker_id\":\"ac6095b6c715d823d732dcc9067f75b1299de5cc69a012b08d616a6058bdc0ad\"}, \"va\":\"%d\"}]";
+static const char *_32_bytes_msg = "This meeesage is 32 symbols long";
+static const char *_11_bytes_msg = "I will pass";
+static const char *_6_bytes_msg = "I pass";
+static const char *_180_bytes_msg =
+ "This message is 180 bytes long, so it will be used where we are sure that this message will pass every time or not at all. So used it carefully and at the proper place. Understand?";
+static const char *stdout_str = "stdout";
+static const char *stderr_str = "stderr";
+static const char *apiserver = "kube-apiserver";
+static const char *alertmanager = "alertmanager";
+
+/* Utility functions */
+char *push_data_to_engine_and_take_output(flb_ctx_t * ctx, int in_ffd,
+ char *message);
+void check_if_message_pass_through_engine(flb_ctx_t * ctx, int in_ffd,
+ char *message);
+void check_if_message_doesnt_pass_through_engine(flb_ctx_t * ctx, int in_ffd,
+ char *message);
+pthread_mutex_t result_mutex = PTHREAD_MUTEX_INITIALIZER;
+char *output = NULL;
+
+
+
+/* Test functions */
+
+void flb_test_simple_log(void);
+void test_nestest_name_fields(void);
+void test_default_name_field(void);
+void test_default_log_field(void);
+
+
+/* Test list */
+TEST_LIST = {
+ {
+ "throttle_size", flb_test_simple_log}, {
+ "throttle_size2", test_nestest_name_fields}, {
+ "throttle_size3", test_default_name_field}, {
+ "throttle_size4", test_default_log_field}, {
+ NULL, NULL}
+};
+
+void set_output(char *val)
+{
+ pthread_mutex_lock(&result_mutex);
+ output = val;
+ pthread_mutex_unlock(&result_mutex);
+}
+
+char *get_output(void)
+{
+ char *val;
+
+ pthread_mutex_lock(&result_mutex);
+ val = output;
+ output = NULL;
+ pthread_mutex_unlock(&result_mutex);
+
+ return val;
+}
+
+int callback_test(void *data, size_t size, void *cb_data)
+{
+ if (size > 0) {
+ flb_debug("[test_filter_throttle_size] received message: %s", data);
+ set_output(data); /* success */
+ }
+ return 0;
+}
+
+void flb_test_simple_log(void)
+{
+ int i;
+ int ret;
+ char p[1000];
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+ int filter_ffd;
+
+
+ struct flb_lib_out_cb cb;
+ cb.cb = callback_test;
+ cb.data = NULL;
+
+ ctx = flb_create();
+
+ /* Configure service */
+ flb_service_set(ctx, "Flush", "1", "Grace" "1", "Log_Level", "debug",
+ NULL);
+
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ TEST_CHECK(in_ffd >= 0);
+ flb_input_set(ctx, in_ffd, "tag", "test", NULL);
+
+ /* Output */
+ out_ffd = flb_output(ctx, (char *) "lib", &cb);
+ TEST_CHECK(out_ffd >= 0);
+ flb_output_set(ctx, out_ffd, "Match", "*", "format", "json", NULL);
+
+ filter_ffd = flb_filter(ctx, (char *) "throttle_size", NULL);
+ TEST_CHECK(filter_ffd >= 0);
+ ret = flb_filter_set(ctx, filter_ffd, "match", "*", NULL);
+ TEST_CHECK(ret == 0);
+ ret = flb_filter_set(ctx, filter_ffd, "rate", "10", NULL);
+ TEST_CHECK(ret == 0);
+ ret = flb_filter_set(ctx, filter_ffd, "window", "30", NULL);
+ TEST_CHECK(ret == 0);
+ ret = flb_filter_set(ctx, filter_ffd, "interval", "3s", NULL);
+ TEST_CHECK(ret == 0);
+ ret = flb_filter_set(ctx, filter_ffd, "print_status", "true", NULL);
+ TEST_CHECK(ret == 0);
+ ret = flb_filter_set(ctx, filter_ffd, "log_field", "log", NULL);
+ TEST_CHECK(ret == 0);
+ ret = flb_filter_set(ctx, filter_ffd, "name_field", "stream", NULL);
+ TEST_CHECK(ret == 0);
+ ret =
+ flb_filter_set(ctx, filter_ffd, "window_time_duration", "10s", NULL);
+ TEST_CHECK(ret == 0);
+
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ /* Verify that the size throttle plugin differentiates logs by a non nested name_field.
+ We put 9 logs 32 bytes long which is 288 bytes of total or rate of 9.6.
+ If all logs passed this means that the the plugin sees them as two seperates tipes or
+ does now work at all.Or each logs is seen as different group of logs. */
+ for (i = 0; i < 9; i++) {
+ /*Make message with sream: stdout */
+ memset(p, '\0', sizeof(p));
+ snprintf(p, sizeof(p), simple_log, _32_bytes_msg, stdout_str, i);
+ check_if_message_pass_through_engine(ctx, in_ffd, p);
+
+ /*Make message with sream: stderr */
+ memset(p, '\0', sizeof(p));
+ snprintf(p, sizeof(p), simple_log, _32_bytes_msg, stderr_str, i);
+ check_if_message_pass_through_engine(ctx, in_ffd, p);
+ }
+
+ /* Verify that the plugin cut logs wen rate exceeds 10.
+ By add next message which is 32 bytes log the total must become 320 which
+ makes the rate 10.66. If the messege is dropped this means that the plugin
+ works properly. */
+ memset(p, '\0', sizeof(p));
+ snprintf(p, sizeof(p), simple_log, _32_bytes_msg, stdout_str, 9);
+ check_if_message_doesnt_pass_through_engine(ctx, in_ffd, p);
+
+
+ memset(p, '\0', sizeof(p));
+ snprintf(p, sizeof(p), simple_log, _32_bytes_msg, stderr_str, 9);
+ check_if_message_doesnt_pass_through_engine(ctx, in_ffd, p);
+
+ /*Now we will pass two messages with 11 bytes of lenght an they will make the
+ rate 9.96 which is less than 10 and they must pass */
+ memset(p, '\0', sizeof(p));
+ snprintf(p, sizeof(p), simple_log, _11_bytes_msg, stdout_str, 10);
+ check_if_message_pass_through_engine(ctx, in_ffd, p);
+
+ memset(p, '\0', sizeof(p));
+ snprintf(p, sizeof(p), simple_log, _11_bytes_msg, stderr_str, 10);
+ check_if_message_pass_through_engine(ctx, in_ffd, p);
+
+ /*check that if log field is missing then the messages will pass throughout the engine */
+ for (i = 0; i < 2; i++) {
+ /*Make message with sream: stdout */
+ memset(p, '\0', sizeof(p));
+ snprintf(p, sizeof(p), simple_log_with_missing_log_key,
+ _180_bytes_msg, stdout_str, i + 11);
+ check_if_message_pass_through_engine(ctx, in_ffd, p);
+
+ /*Make message with sream: stderr */
+ memset(p, '\0', sizeof(p));
+ snprintf(p, sizeof(p), simple_log_with_missing_log_key,
+ _180_bytes_msg, stderr_str, i + 11);
+ check_if_message_pass_through_engine(ctx, in_ffd, p);
+ }
+
+ /*check that if stream field is missing then the messages will pass throughout the engine */
+ for (i = 0; i < 2; i++) {
+ /*Make message with sream: stdout */
+ memset(p, '\0', sizeof(p));
+ snprintf(p, sizeof(p), simple_log_with_missing_stream_key,
+ _180_bytes_msg, stdout_str, i + 13);
+ check_if_message_pass_through_engine(ctx, in_ffd, p);
+
+ /*Make message with sream: stderr */
+ memset(p, '\0', sizeof(p));
+ snprintf(p, sizeof(p), simple_log_with_missing_stream_key,
+ _180_bytes_msg, stderr_str, i + 13);
+ check_if_message_pass_through_engine(ctx, in_ffd, p);
+ }
+
+ flb_stop(ctx);
+ flb_destroy(ctx);
+}
+
+void test_nestest_name_fields(void)
+{
+ int i;
+ int ret;
+ char p[1000];
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+ int filter_ffd;
+
+
+ struct flb_lib_out_cb cb;
+ cb.cb = callback_test;
+ cb.data = NULL;
+
+ ctx = flb_create();
+
+ /* Configure service */
+ flb_service_set(ctx, "Flush", "1", "Grace" "1", "Log_Level", "debug",
+ NULL);
+
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ TEST_CHECK(in_ffd >= 0);
+ flb_input_set(ctx, in_ffd, "tag", "test", NULL);
+
+ /* Output */
+ out_ffd = flb_output(ctx, (char *) "lib", &cb);
+ TEST_CHECK(out_ffd >= 0);
+ flb_output_set(ctx, out_ffd, "Match", "*", "format", "json", NULL);
+
+ filter_ffd = flb_filter(ctx, (char *) "throttle_size", NULL);
+ TEST_CHECK(filter_ffd >= 0);
+ ret = flb_filter_set(ctx, filter_ffd, "match", "*", NULL);
+ TEST_CHECK(ret == 0);
+ ret = flb_filter_set(ctx, filter_ffd, "rate", "10", NULL);
+ TEST_CHECK(ret == 0);
+ ret = flb_filter_set(ctx, filter_ffd, "window", "30", NULL);
+ TEST_CHECK(ret == 0);
+ ret = flb_filter_set(ctx, filter_ffd, "interval", "3s", NULL);
+ TEST_CHECK(ret == 0);
+ ret = flb_filter_set(ctx, filter_ffd, "print_status", "false", NULL);
+ TEST_CHECK(ret == 0);
+ ret =
+ flb_filter_set(ctx, filter_ffd, "log_field", "logwrapper|log", NULL);
+ TEST_CHECK(ret == 0);
+ ret =
+ flb_filter_set(ctx, filter_ffd, "name_field", "kubernetes|pod_name",
+ NULL);
+ TEST_CHECK(ret == 0);
+ ret =
+ flb_filter_set(ctx, filter_ffd, "window_time_duration", "10s", NULL);
+ TEST_CHECK(ret == 0);
+
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ /* Verify that the size throttle plugin differentiates logs by a nested name_field and nested log_field.
+ We put 9 logs 32 bytes long which is 288 bytes of total or rate of 9.6.
+ If all logs passed this means that the the plugin sees them as two seperates tipes or
+ does now work at all.Or each logs is seen as different group of logs. */
+ for (i = 0; i < 9; i++) {
+ /*Make message with sream: stdout */
+ memset(p, '\0', sizeof(p));
+ snprintf(p, sizeof(p), nested_log, _32_bytes_msg, apiserver, i);
+ check_if_message_pass_through_engine(ctx, in_ffd, p);
+
+ /*Make message with kubernetes.pod_name: alermanager */
+ memset(p, '\0', sizeof(p));
+ snprintf(p, sizeof(p), nested_log, _32_bytes_msg, alertmanager, i);
+ check_if_message_pass_through_engine(ctx, in_ffd, p);
+ }
+
+ /* Verify that the plugin cut logs when rate exceeds 10.
+ By add next message which is 32 bytes log the total must become 320 which
+ makes the rate 10.66. If the messege is dropped this means that the plugin
+ works properly. */
+ memset(p, '\0', sizeof(p));
+ snprintf(p, sizeof(p), nested_log, _32_bytes_msg, apiserver, 9);
+ check_if_message_doesnt_pass_through_engine(ctx, in_ffd, p);
+
+
+ memset(p, '\0', sizeof(p));
+ snprintf(p, sizeof(p), nested_log, _32_bytes_msg, alertmanager, 9);
+ check_if_message_doesnt_pass_through_engine(ctx, in_ffd, p);
+
+ /*Now we will pass two messages with 11 bytes of lenght an they will make the
+ rate 9.96 which is less than 10 and they must pass */
+ memset(p, '\0', sizeof(p));
+ snprintf(p, sizeof(p), nested_log, _11_bytes_msg, apiserver, 10);
+ check_if_message_pass_through_engine(ctx, in_ffd, p);
+
+ memset(p, '\0', sizeof(p));
+ snprintf(p, sizeof(p), nested_log, _11_bytes_msg, alertmanager, 10);
+ check_if_message_pass_through_engine(ctx, in_ffd, p);
+
+ /*check that if log field is missing then the messages will pass throughout the engine */
+ for (i = 0; i < 2; i++) {
+ /*Make message with kubernetes.pod_name: kube-apiserver */
+ memset(p, '\0', sizeof(p));
+ snprintf(p, sizeof(p), nested_log_with_missing_log_field,
+ _180_bytes_msg, apiserver, i + 11);
+ check_if_message_pass_through_engine(ctx, in_ffd, p);
+
+ /*Make message with kubernetes.pod_name: alertmanager */
+ memset(p, '\0', sizeof(p));
+ snprintf(p, sizeof(p), nested_log_with_missing_log_field,
+ _180_bytes_msg, alertmanager, i + 11);
+ check_if_message_pass_through_engine(ctx, in_ffd, p);
+ }
+
+ /*check that if pod_name field is missing then the messages will pass throughout the engine */
+ for (i = 0; i < 2; i++) {
+ /*Make message with kubernetes.pod_name: kube-apiserver */
+ memset(p, '\0', sizeof(p));
+ snprintf(p, sizeof(p), nested_log_with_missing_name_field,
+ _180_bytes_msg, apiserver, i + 13);
+ check_if_message_pass_through_engine(ctx, in_ffd, p);
+
+ /*Make message with kubernetes.pod_name: alermanager */
+ memset(p, '\0', sizeof(p));
+ snprintf(p, sizeof(p), nested_log_with_missing_name_field,
+ _180_bytes_msg, alertmanager, i + 13);
+ check_if_message_pass_through_engine(ctx, in_ffd, p);
+ }
+
+ /*check that if pod_name is not in the right order then the messages will pass throughout the engine */
+ for (i = 0; i < 2; i++) {
+ /*Make message with kubernetes.pod_name: kube-apiserver */
+ memset(p, '\0', sizeof(p));
+ snprintf(p, sizeof(p), swap_name_field, _180_bytes_msg, apiserver,
+ i + 15);
+ check_if_message_pass_through_engine(ctx, in_ffd, p);
+
+ /*Make message with kubernets.pod_name: alertmanager */
+ memset(p, '\0', sizeof(p));
+ snprintf(p, sizeof(p), swap_name_field, _180_bytes_msg, alertmanager,
+ i + 15);
+ check_if_message_pass_through_engine(ctx, in_ffd, p);
+ }
+
+ /*check that if log field is wrong order then the messages will pass throughout the engine */
+ for (i = 0; i < 2; i++) {
+ /*Make message with kubernetes.pod_name: kube-apiserver */
+ memset(p, '\0', sizeof(p));
+ snprintf(p, sizeof(p), swap_log_field, _180_bytes_msg, apiserver,
+ i + 17);
+ check_if_message_pass_through_engine(ctx, in_ffd, p);
+
+ /*Make message with kubernetes: alertmanager */
+ memset(p, '\0', sizeof(p));
+ snprintf(p, sizeof(p), swap_log_field, _180_bytes_msg, alertmanager,
+ i + 17);
+ check_if_message_pass_through_engine(ctx, in_ffd, p);
+ }
+
+ flb_stop(ctx);
+ flb_destroy(ctx);
+}
+
+void test_default_name_field(void)
+{
+ int i;
+ int ret;
+ char p[200];
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+ int filter_ffd;
+
+ struct flb_lib_out_cb cb;
+ cb.cb = callback_test;
+ cb.data = NULL;
+
+ ctx = flb_create();
+
+ /* Configure service */
+ flb_service_set(ctx, "Flush", "1", "Grace" "1", "Log_Level", "debug",
+ NULL);
+
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ TEST_CHECK(in_ffd >= 0);
+ flb_input_set(ctx, in_ffd, "tag", "test", NULL);
+
+ /* Output */
+ out_ffd = flb_output(ctx, (char *) "lib", &cb);
+ TEST_CHECK(out_ffd >= 0);
+ flb_output_set(ctx, out_ffd, "Match", "*", "format", "json", NULL);
+
+ filter_ffd = flb_filter(ctx, (char *) "throttle_size", NULL);
+ TEST_CHECK(filter_ffd >= 0);
+ ret = flb_filter_set(ctx, filter_ffd, "match", "*", NULL);
+ TEST_CHECK(ret == 0);
+ ret = flb_filter_set(ctx, filter_ffd, "rate", "10", NULL);
+ TEST_CHECK(ret == 0);
+ ret = flb_filter_set(ctx, filter_ffd, "window", "30", NULL);
+ TEST_CHECK(ret == 0);
+ ret = flb_filter_set(ctx, filter_ffd, "interval", "3s", NULL);
+ TEST_CHECK(ret == 0);
+ ret = flb_filter_set(ctx, filter_ffd, "print_status", "false", NULL);
+ TEST_CHECK(ret == 0);
+ ret = flb_filter_set(ctx, filter_ffd, "log_field", "log", NULL);
+ TEST_CHECK(ret == 0);
+ ret =
+ flb_filter_set(ctx, filter_ffd, "window_time_duration", "10s", NULL);
+ TEST_CHECK(ret == 0);
+
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ /* Verify that the size throttle plugin do not differentiates logs by name_field.
+ We put 8 logs 32 bytes long which is 256 bytes of total or rate of 8.53.
+ If all logs passed this means that the the plugin sees them as one or
+ does now work at all.Or each logs is seen as different group of logs. */
+ for (i = 0; i < 4; i++) {
+ /*Make message with sream: stdout */
+ memset(p, '\0', sizeof(p));
+ snprintf(p, sizeof(p), simple_log, _32_bytes_msg, stdout_str, i);
+ check_if_message_pass_through_engine(ctx, in_ffd, p);
+
+ /*Make message with sream: stderr */
+ memset(p, '\0', sizeof(p));
+ snprintf(p, sizeof(p), simple_log, _32_bytes_msg, stderr_str, i);
+ check_if_message_pass_through_engine(ctx, in_ffd, p);
+ }
+
+ /*Add one exra message with lenght of 32 bytes to make the total 288 or rate of 9.6 */
+ memset(p, '\0', sizeof(p));
+ snprintf(p, sizeof(p), simple_log, _32_bytes_msg, stdout_str, 9);
+ check_if_message_pass_through_engine(ctx, in_ffd, p);
+
+ /* Verify that the plugin cut logs when rate exceeds 10.
+ By add next message which is 32 bytes log the total must become 320 which
+ makes the rate 10.66. If the messege is dropped this means that the plugin
+ works properly. */
+ memset(p, '\0', sizeof(p));
+ snprintf(p, sizeof(p), simple_log, _32_bytes_msg, stdout_str, 9);
+ check_if_message_doesnt_pass_through_engine(ctx, in_ffd, p);
+ /* Verify that the plugin cut logs when rate exceeds 10.
+ By add next message which is 32 bytes log the total must become 320 which
+ makes the rate 10.66. If the messege is dropped this means that the plugin
+ works properly. */
+ memset(p, '\0', sizeof(p));
+ snprintf(p, sizeof(p), simple_log, _32_bytes_msg, stderr_str, 9);
+ check_if_message_doesnt_pass_through_engine(ctx, in_ffd, p);
+
+
+ /*Now we will pass two messages with 6 bytes of lenght an they will make the
+ rate 10 whch is the limit and the message must pass. */
+ memset(p, '\0', sizeof(p));
+ snprintf(p, sizeof(p), simple_log, _6_bytes_msg, stdout_str, 10);
+ check_if_message_pass_through_engine(ctx, in_ffd, p);
+
+ memset(p, '\0', sizeof(p));
+ snprintf(p, sizeof(p), simple_log, _6_bytes_msg, stderr_str, 10);
+ check_if_message_pass_through_engine(ctx, in_ffd, p);
+
+
+ flb_stop(ctx);
+ flb_destroy(ctx);
+}
+
+
+void test_default_log_field(void)
+{
+ int i;
+ int ret;
+ char p[1000];
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+ int filter_ffd;
+
+
+ struct flb_lib_out_cb cb;
+ cb.cb = callback_test;
+ cb.data = NULL;
+
+ ctx = flb_create();
+
+ /* Configure service */
+ flb_service_set(ctx, "Flush", "1", "Grace" "1", "Log_Level", "debug",
+ NULL);
+
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ TEST_CHECK(in_ffd >= 0);
+ flb_input_set(ctx, in_ffd, "tag", "test", NULL);
+
+ /* Output */
+ out_ffd = flb_output(ctx, (char *) "lib", &cb);
+ TEST_CHECK(out_ffd >= 0);
+ flb_output_set(ctx, out_ffd, "Match", "*", "format", "json", NULL);
+
+ filter_ffd = flb_filter(ctx, (char *) "throttle_size", NULL);
+ TEST_CHECK(filter_ffd >= 0);
+ ret = flb_filter_set(ctx, filter_ffd, "match", "*", NULL);
+ TEST_CHECK(ret == 0);
+ ret = flb_filter_set(ctx, filter_ffd, "rate", "43", NULL);
+ TEST_CHECK(ret == 0);
+ ret = flb_filter_set(ctx, filter_ffd, "window", "30", NULL);
+ TEST_CHECK(ret == 0);
+ ret = flb_filter_set(ctx, filter_ffd, "interval", "3s", NULL);
+ TEST_CHECK(ret == 0);
+ ret =
+ flb_filter_set(ctx, filter_ffd, "name_field", "kubernetes|pod_name",
+ NULL);
+ TEST_CHECK(ret == 0);
+ ret = flb_filter_set(ctx, filter_ffd, "print_status", "false", NULL);
+ TEST_CHECK(ret == 0);
+ ret =
+ flb_filter_set(ctx, filter_ffd, "window_time_duration", "10s", NULL);
+ TEST_CHECK(ret == 0);
+
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ /* Verify that fluent-bit take in account all of the message payload when log_field is missing.
+ We shall put two messages with differen kubernetes.podname which will pass.
+ One message is about 463 bytes long and two makes the rate about 30.87 */
+ for (i = 0; i < 2; i++) {
+ /*Make message with kubernetes.pod_name:kube-apiserver */
+ memset(p, '\0', sizeof(p));
+ snprintf(p, sizeof(p), nested_log, _180_bytes_msg, apiserver, 1);
+ check_if_message_pass_through_engine(ctx, in_ffd, p);
+
+ /*Make message with kubernetes.pod_name:alermanager */
+ memset(p, '\0', sizeof(p));
+ snprintf(p, sizeof(p), nested_log, _180_bytes_msg, alertmanager, 1);
+ check_if_message_pass_through_engine(ctx, in_ffd, p);
+ }
+
+ /* Verify that the plugin cut logs when rate exceeds 37.
+ We shall add again two messages with size 463 and they will
+ fail passing. */
+ memset(p, '\0', sizeof(p));
+ snprintf(p, sizeof(p), nested_log, _180_bytes_msg, apiserver, 1);
+ check_if_message_doesnt_pass_through_engine(ctx, in_ffd, p);
+
+ /*Make message with kubernetes.pod_name:alermanager */
+ memset(p, '\0', sizeof(p));
+ snprintf(p, sizeof(p), nested_log, _180_bytes_msg, alertmanager, 1);
+ check_if_message_doesnt_pass_through_engine(ctx, in_ffd, p);
+
+ /*The next two must pass */
+ /*Make message with kubernetes.pod_name:kube-apiserver */
+ memset(p, '\0', sizeof(p));
+ snprintf(p, sizeof(p), nested_log, _32_bytes_msg, apiserver, 1);
+ check_if_message_pass_through_engine(ctx, in_ffd, p);
+
+ /*Make message with kubernetes.pod_name:alermanager */
+ memset(p, '\0', sizeof(p));
+ snprintf(p, sizeof(p), nested_log, _32_bytes_msg, alertmanager, 1);
+ check_if_message_pass_through_engine(ctx, in_ffd, p);
+
+
+ flb_stop(ctx);
+ flb_destroy(ctx);
+}
+
+char *push_data_to_engine_and_take_output(flb_ctx_t * ctx, int in_ffd,
+ char *message)
+{
+ char *result = NULL;
+ int bytes;
+ /*Push the message into the engine */
+ bytes = flb_lib_push(ctx, in_ffd, (void *) message, strlen(message));
+ WAIT_FOR_FLUSH /*wait the output data to be flushed */
+ result = get_output(); /*get the output message */
+ TEST_CHECK(bytes == strlen(message)); /*Chech if all of the message was proceesed */
+ return result;
+}
+
+void check_if_message_pass_through_engine(flb_ctx_t * ctx, int in_ffd,
+ char *message)
+{
+ char *result;
+ result = push_data_to_engine_and_take_output(ctx, in_ffd, message);
+ /*Check that the message go throught engine without modification */
+ TEST_CHECK(strncmp(result, message, strlen(result)) == 0);
+}
+
+void check_if_message_doesnt_pass_through_engine(flb_ctx_t * ctx, int in_ffd,
+ char *message)
+{
+ char *result;
+ result = push_data_to_engine_and_take_output(ctx, in_ffd, message);
+ /*Check that the message didn't throught engine */
+ TEST_CHECK(result == NULL);
+}
diff --git a/fluent-bit/tests/runtime/filter_type_converter.c b/fluent-bit/tests/runtime/filter_type_converter.c
new file mode 100644
index 00000000..da2556e7
--- /dev/null
+++ b/fluent-bit/tests/runtime/filter_type_converter.c
@@ -0,0 +1,389 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/* Fluent Bit
+ * ==========
+ * Copyright (C) 2019-2021 The Fluent Bit Authors
+ * Copyright (C) 2015-2018 Treasure Data Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <fluent-bit.h>
+#include <fluent-bit/flb_time.h>
+#include "flb_tests_runtime.h"
+
+#define JSON_TEST_DATA "[12345678, {\"numstr\":\"123.456\", \"int\":123, \"float\":123.456, \"hexstr\":\"0xff\"}]"
+#define JSON_NEST_DATA "[12345678, {\"nest\":{\"numstr\":\"123.456\", \"float\":123.456}}]"
+
+struct filter_test {
+ flb_ctx_t *flb; /* Fluent Bit library context */
+ int i_ffd; /* Input fd */
+ int f_ffd; /* Filter fd */
+};
+
+/* Callback to check expected results */
+static int cb_check_result(void *record, size_t size, void *data)
+{
+ char *p;
+ char *expected;
+ char *result;
+
+ expected = (char *) data;
+ result = (char *) record;
+
+ p = strstr(result, expected);
+ TEST_CHECK(p != NULL);
+
+ if (p==NULL) {
+ flb_error("Expected to find: '%s' in result '%s'",
+ expected, result);
+ }
+ /*
+ * If you want to debug your test
+ *
+ * printf("Expect: '%s' in result '%s'", expected, result);
+ */
+ flb_free(record);
+ return 0;
+}
+
+static struct filter_test *filter_test_create(struct flb_lib_out_cb *data)
+{
+ int i_ffd;
+ int f_ffd;
+ int o_ffd;
+ struct filter_test *ctx;
+
+ ctx = flb_malloc(sizeof(struct filter_test));
+ if (!ctx) {
+ flb_errno();
+ return NULL;
+ }
+
+ /* Service config */
+ ctx->flb = flb_create();
+ flb_service_set(ctx->flb,
+ "Flush", "0.200000000",
+ "Grace", "1",
+ "Log_Level", "error",
+ NULL);
+
+ /* Input */
+ i_ffd = flb_input(ctx->flb, (char *) "lib", NULL);
+ TEST_CHECK(i_ffd >= 0);
+ flb_input_set(ctx->flb, i_ffd, "tag", "test", NULL);
+ ctx->i_ffd = i_ffd;
+
+ /* Filter configuration */
+ f_ffd = flb_filter(ctx->flb, (char *) "type_converter", NULL);
+ TEST_CHECK(f_ffd >= 0);
+ flb_filter_set(ctx->flb, f_ffd, "match", "*", NULL);
+ ctx->f_ffd = f_ffd;
+
+ /* Output */
+ o_ffd = flb_output(ctx->flb, (char *) "lib", (void *) data);
+ TEST_CHECK(o_ffd >= 0);
+ flb_output_set(ctx->flb, o_ffd,
+ "match", "test",
+ "format", "json",
+ NULL);
+
+ return ctx;
+}
+
+static void filter_test_destroy(struct filter_test *ctx)
+{
+ sleep(1);
+ flb_stop(ctx->flb);
+ flb_destroy(ctx->flb);
+ flb_free(ctx);
+}
+
+void flb_test_str_to_int()
+{
+ struct flb_lib_out_cb cb_data;
+ struct filter_test *ctx;
+ int len;
+ int ret;
+ int bytes;
+ char *p = JSON_TEST_DATA;
+
+ /* Prepare output callback with expected result */
+ cb_data.cb = cb_check_result;
+ cb_data.data = "\"new\":123";
+
+ /* Create test context */
+ ctx = filter_test_create((void *) &cb_data);
+ if (!TEST_CHECK(ctx != NULL)) {
+ TEST_MSG("failed to create context");
+ exit(EXIT_FAILURE);
+ }
+
+ /* Configure filter */
+ ret = flb_filter_set(ctx->flb, ctx->f_ffd,
+ "str_key", "numstr new int",
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ /* Start the engine */
+ ret = flb_start(ctx->flb);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data samples */
+ len = strlen(p);
+ bytes = flb_lib_push(ctx->flb, ctx->i_ffd, p, len);
+ TEST_CHECK(bytes == len);
+
+ filter_test_destroy(ctx);
+}
+
+void flb_test_str_to_hex()
+{
+ struct flb_lib_out_cb cb_data;
+ struct filter_test *ctx;
+ int len;
+ int ret;
+ int bytes;
+ char *p = JSON_TEST_DATA;
+
+ /* Prepare output callback with expected result */
+ cb_data.cb = cb_check_result;
+ cb_data.data = "\"new\":255";
+
+ /* Create test context */
+ ctx = filter_test_create((void *) &cb_data);
+ if (!TEST_CHECK(ctx != NULL)) {
+ TEST_MSG("failed to create context");
+ exit(EXIT_FAILURE);
+ }
+
+ /* Configure filter */
+ ret = flb_filter_set(ctx->flb, ctx->f_ffd,
+ "str_key", "hexstr new hex",
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ /* Start the engine */
+ ret = flb_start(ctx->flb);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data samples */
+ len = strlen(p);
+ bytes = flb_lib_push(ctx->flb, ctx->i_ffd, p, len);
+ TEST_CHECK(bytes == len);
+
+ filter_test_destroy(ctx);
+}
+
+void flb_test_str_to_float()
+{
+ struct flb_lib_out_cb cb_data;
+ struct filter_test *ctx;
+ int len;
+ int ret;
+ int bytes;
+ char *p = JSON_TEST_DATA;
+
+ /* Prepare output callback with expected result */
+ cb_data.cb = cb_check_result;
+ cb_data.data = "\"new\":123.456";
+
+ /* Create test context */
+ ctx = filter_test_create((void *) &cb_data);
+ if (!TEST_CHECK(ctx != NULL)) {
+ TEST_MSG("failed to create context");
+ exit(EXIT_FAILURE);
+ }
+
+ /* Configure filter */
+ ret = flb_filter_set(ctx->flb, ctx->f_ffd,
+ "str_key", "numstr new float",
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ /* Start the engine */
+ ret = flb_start(ctx->flb);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data samples */
+ len = strlen(p);
+ bytes = flb_lib_push(ctx->flb, ctx->i_ffd, p, len);
+ TEST_CHECK(bytes == len);
+
+ filter_test_destroy(ctx);
+}
+
+void flb_test_int_to_str()
+{
+ struct flb_lib_out_cb cb_data;
+ struct filter_test *ctx;
+ int len;
+ int ret;
+ int bytes;
+ char *p = JSON_TEST_DATA;
+
+ /* Prepare output callback with expected result */
+ cb_data.cb = cb_check_result;
+ cb_data.data = "\"new\":\"123\"";
+
+ /* Create test context */
+ ctx = filter_test_create((void *) &cb_data);
+ if (!TEST_CHECK(ctx != NULL)) {
+ TEST_MSG("failed to create context");
+ exit(EXIT_FAILURE);
+ }
+
+ /* Configure filter */
+ ret = flb_filter_set(ctx->flb, ctx->f_ffd,
+ "int_key", "int new str",
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ /* Start the engine */
+ ret = flb_start(ctx->flb);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data samples */
+ len = strlen(p);
+ bytes = flb_lib_push(ctx->flb, ctx->i_ffd, p, len);
+ TEST_CHECK(bytes == len);
+
+ filter_test_destroy(ctx);
+}
+
+void flb_test_int_to_float()
+{
+ struct flb_lib_out_cb cb_data;
+ struct filter_test *ctx;
+ int len;
+ int ret;
+ int bytes;
+ char *p = JSON_TEST_DATA;
+
+ /* Prepare output callback with expected result */
+ cb_data.cb = cb_check_result;
+ cb_data.data = "\"new\":123.";
+
+ /* Create test context */
+ ctx = filter_test_create((void *) &cb_data);
+ if (!TEST_CHECK(ctx != NULL)) {
+ TEST_MSG("failed to create context");
+ exit(EXIT_FAILURE);
+ }
+
+ /* Configure filter */
+ ret = flb_filter_set(ctx->flb, ctx->f_ffd,
+ "int_key", "int new float",
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ /* Start the engine */
+ ret = flb_start(ctx->flb);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data samples */
+ len = strlen(p);
+ bytes = flb_lib_push(ctx->flb, ctx->i_ffd, p, len);
+ TEST_CHECK(bytes == len);
+
+ filter_test_destroy(ctx);
+}
+
+void flb_test_str_to_int_and_int_to_str()
+{
+ struct flb_lib_out_cb cb_data;
+ struct filter_test *ctx;
+ int len;
+ int ret;
+ int bytes;
+ char *p = JSON_TEST_DATA;
+
+ /* Prepare output callback with expected result */
+ cb_data.cb = cb_check_result;
+ cb_data.data = "\"new1\":123,\"new2\":\"123\"";
+
+ /* Create test context */
+ ctx = filter_test_create((void *) &cb_data);
+ if (!TEST_CHECK(ctx != NULL)) {
+ TEST_MSG("failed to create context");
+ exit(EXIT_FAILURE);
+ }
+
+ /* Configure filter */
+ ret = flb_filter_set(ctx->flb, ctx->f_ffd,
+ "str_key", "numstr new1 int",
+ "int_key", "int new2 str",
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ /* Start the engine */
+ ret = flb_start(ctx->flb);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data samples */
+ len = strlen(p);
+ bytes = flb_lib_push(ctx->flb, ctx->i_ffd, p, len);
+ TEST_CHECK(bytes == len);
+
+ filter_test_destroy(ctx);
+}
+
+void flb_test_nest_key()
+{
+ struct flb_lib_out_cb cb_data;
+ struct filter_test *ctx;
+ int len;
+ int ret;
+ int bytes;
+ char *p = JSON_NEST_DATA;
+
+ /* Prepare output callback with expected result */
+ cb_data.cb = cb_check_result;
+ cb_data.data = "\"new\":123";
+
+ /* Create test context */
+ ctx = filter_test_create((void *) &cb_data);
+ if (!TEST_CHECK(ctx != NULL)) {
+ TEST_MSG("failed to create context");
+ exit(EXIT_FAILURE);
+ }
+
+ /* Configure filter */
+ ret = flb_filter_set(ctx->flb, ctx->f_ffd,
+ "str_key", "$nest['numstr'] new int",
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ /* Start the engine */
+ ret = flb_start(ctx->flb);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data samples */
+ len = strlen(p);
+ bytes = flb_lib_push(ctx->flb, ctx->i_ffd, p, len);
+ TEST_CHECK(bytes == len);
+
+ filter_test_destroy(ctx);
+}
+
+/* Test list */
+TEST_LIST = {
+ {"str_to_int" , flb_test_str_to_int },
+ {"str_to_float" , flb_test_str_to_float },
+ {"str_to_hex" , flb_test_str_to_hex },
+ {"int_to_str" , flb_test_int_to_str },
+ {"int_to_float" , flb_test_int_to_float },
+ {"str<->int" , flb_test_str_to_int_and_int_to_str },
+ {"nest_key" , flb_test_nest_key},
+ {NULL, NULL}
+};
diff --git a/fluent-bit/tests/runtime/filter_wasm.c b/fluent-bit/tests/runtime/filter_wasm.c
new file mode 100644
index 00000000..4cc1afcb
--- /dev/null
+++ b/fluent-bit/tests/runtime/filter_wasm.c
@@ -0,0 +1,468 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/* Fluent Bit
+ * ==========
+ * Copyright (C) 2022 The Fluent Bit Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <fluent-bit.h>
+#include <fluent-bit/flb_time.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include "flb_tests_runtime.h"
+
+#define DPATH_WASM FLB_TESTS_DATA_PATH "/data/wasm"
+#define FLUSH_INTERVAL "1.0"
+#ifdef _WIN32
+ #define TIME_EPSILON_MS 30
+#else
+ #define TIME_EPSILON_MS 10
+#endif
+
+pthread_mutex_t result_mutex = PTHREAD_MUTEX_INITIALIZER;
+char *output = NULL;
+int num_output = 0;
+
+void set_output(char *val)
+{
+ pthread_mutex_lock(&result_mutex);
+ output = val;
+ pthread_mutex_unlock(&result_mutex);
+}
+
+char *get_output(void)
+{
+ char *val;
+
+ pthread_mutex_lock(&result_mutex);
+ val = output;
+ pthread_mutex_unlock(&result_mutex);
+
+ return val;
+}
+
+static void clear_output()
+{
+ pthread_mutex_lock(&result_mutex);
+ output = NULL;
+ pthread_mutex_unlock(&result_mutex);
+}
+
+static void clear_output_num()
+{
+ pthread_mutex_lock(&result_mutex);
+ num_output = 0;
+ pthread_mutex_unlock(&result_mutex);
+}
+
+static int get_output_num()
+{
+ int ret;
+ pthread_mutex_lock(&result_mutex);
+ ret = num_output;
+ pthread_mutex_unlock(&result_mutex);
+
+ return ret;
+}
+
+static int cb_count_msgpack_events(void *record, size_t size, void *data)
+{
+ msgpack_unpacked result;
+ size_t off = 0;
+
+ if (!TEST_CHECK(data != NULL)) {
+ flb_error("data is NULL");
+ }
+
+ /* Iterate each item array and apply rules */
+ msgpack_unpacked_init(&result);
+ while (msgpack_unpack_next(&result, record, size, &off) == MSGPACK_UNPACK_SUCCESS) {
+ pthread_mutex_lock(&result_mutex);
+ num_output++;
+ pthread_mutex_unlock(&result_mutex);
+ }
+ msgpack_unpacked_destroy(&result);
+
+ flb_free(record);
+ return 0;
+}
+
+int callback_test(void* data, size_t size, void* cb_data)
+{
+ if (size > 0) {
+ flb_debug("[test_filter_wasm] received message: %s", (char*)data);
+ set_output(data); /* success */
+ }
+ return 0;
+}
+
+void wait_with_timeout(uint32_t timeout_ms, char **out_result)
+{
+ struct flb_time start_time;
+ struct flb_time end_time;
+ struct flb_time diff_time;
+ uint64_t elapsed_time_flb = 0;
+ char *output = NULL;
+
+ flb_time_get(&start_time);
+
+ while (true) {
+ output = get_output();
+
+ if (output != NULL) {
+ *out_result = output;
+ break;
+ }
+
+ flb_time_msleep(100);
+ flb_time_get(&end_time);
+ flb_time_diff(&end_time, &start_time, &diff_time);
+ elapsed_time_flb = flb_time_to_nanosec(&diff_time) / 1000000;
+
+ if (elapsed_time_flb > timeout_ms - TIME_EPSILON_MS) {
+ flb_warn("[timeout] elapsed_time: %ld", elapsed_time_flb);
+ // Reached timeout.
+ break;
+ }
+ }
+}
+
+
+void flb_test_append_tag(void)
+{
+ int ret;
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+ int filter_ffd;
+ char *output = NULL;
+ char *input = "[0, {\"key\":\"val\"}]";
+ char *result;
+ struct flb_lib_out_cb cb_data;
+
+ /* clear previous output */
+ clear_output();
+
+ /* Create context, flush every second (some checks omitted here) */
+ ctx = flb_create();
+ flb_service_set(ctx, "flush", FLUSH_INTERVAL, "grace", "1", NULL);
+
+ /* Prepare output callback context*/
+ cb_data.cb = callback_test;
+ cb_data.data = NULL;
+
+ /* Filter */
+ filter_ffd = flb_filter(ctx, (char *) "wasm", NULL);
+ TEST_CHECK(filter_ffd >= 0);
+ ret = flb_filter_set(ctx, filter_ffd,
+ "Match", "*",
+ "wasm_path", DPATH_WASM "/append_tag.wasm",
+ "function_name", "filter_append_tag",
+ NULL);
+
+ /* Input */
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ flb_input_set(ctx, in_ffd, "tag", "test.wasm", NULL);
+ TEST_CHECK(in_ffd >= 0);
+
+ /* Lib output */
+ out_ffd = flb_output(ctx, (char *) "lib", (void *)&cb_data);
+ TEST_CHECK(out_ffd >= 0);
+ flb_output_set(ctx, out_ffd,
+ "match", "test.wasm",
+ "format", "json",
+ NULL);
+
+ ret = flb_start(ctx);
+ TEST_CHECK(ret==0);
+
+ flb_lib_push(ctx, in_ffd, input, strlen(input));
+ wait_with_timeout(2000, &output);
+ result = strstr(output, "\"tag\":\"test.wasm\"");
+ TEST_CHECK(result != NULL);
+
+ /* clean up */
+ flb_lib_free(output);
+
+ flb_stop(ctx);
+ flb_destroy(ctx);
+}
+
+void flb_test_helloworld(void)
+{
+ int ret;
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+ int filter_ffd;
+
+ /* Create context, flush every second (some checks omitted here) */
+ ctx = flb_create();
+ flb_service_set(ctx, "flush", FLUSH_INTERVAL, "grace", "1", NULL);
+
+ /* Filter */
+ filter_ffd = flb_filter(ctx, (char *) "wasm", NULL);
+ TEST_CHECK(filter_ffd >= 0);
+ ret = flb_filter_set(ctx, filter_ffd,
+ "Match", "*",
+ "wasm_path", DPATH_WASM "/say_hello.wasm",
+ "function_name", "filter_say_hello",
+ NULL);
+ /* Input */
+ in_ffd = flb_input(ctx, (char *) "dummy", NULL);
+ flb_input_set(ctx, in_ffd, "tag", "test", NULL);
+ TEST_CHECK(in_ffd >= 0);
+
+ /* Lib output */
+ out_ffd = flb_output(ctx, (char *) "stdout", NULL);
+ TEST_CHECK(out_ffd >= 0);
+ flb_output_set(ctx, out_ffd,
+ "match", "test",
+ NULL);
+
+ ret = flb_start(ctx);
+ TEST_CHECK(ret==0);
+
+ flb_stop(ctx);
+ flb_destroy(ctx);
+}
+
+void flb_test_numerics_records(void)
+{
+ int ret;
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+ int filter_ffd;
+ char *output = NULL;
+ char *input = "[0, {\"key\":\"val\"}]";
+ char *result;
+ struct flb_lib_out_cb cb_data;
+
+ /* clear previous output */
+ clear_output();
+
+ /* Create context, flush every second (some checks omitted here) */
+ ctx = flb_create();
+ flb_service_set(ctx, "flush", FLUSH_INTERVAL, "grace", "1", NULL);
+
+ /* Prepare output callback context*/
+ cb_data.cb = callback_test;
+ cb_data.data = NULL;
+
+ /* Filter */
+ filter_ffd = flb_filter(ctx, (char *) "wasm", NULL);
+ TEST_CHECK(filter_ffd >= 0);
+ ret = flb_filter_set(ctx, filter_ffd,
+ "Match", "*",
+ "wasm_path", DPATH_WASM "/numeric_records.wasm",
+ "function_name", "filter_numeric_records",
+ NULL);
+
+ /* Input */
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ flb_input_set(ctx, in_ffd, "tag", "test.wasm", NULL);
+ TEST_CHECK(in_ffd >= 0);
+
+ /* Lib output */
+ out_ffd = flb_output(ctx, (char *) "lib", (void *)&cb_data);
+ TEST_CHECK(out_ffd >= 0);
+ flb_output_set(ctx, out_ffd,
+ "match", "test.wasm",
+ "format", "json",
+ NULL);
+
+ ret = flb_start(ctx);
+ TEST_CHECK(ret==0);
+
+ flb_lib_push(ctx, in_ffd, input, strlen(input));
+ wait_with_timeout(2000, &output);
+
+ /* check if float (for int keys)*/
+ result = strstr(output, "\"wasm_int1\":10.");
+ if (!TEST_CHECK(result == NULL)) {
+ TEST_MSG("output:%s\n", output);
+ }
+ result = strstr(output, "\"wasm_int2\":100.");
+ if (!TEST_CHECK(result == NULL)) {
+ TEST_MSG("output:%s\n", output);
+ }
+
+ /* check if float (for float keys)*/
+ result = strstr(output, "\"wasm_float1\":10.5");
+ if (!TEST_CHECK(result != NULL)) {
+ TEST_MSG("output:%s\n", output);
+ }
+ result = strstr(output, "\"wasm_float2\":100.5");
+ if (!TEST_CHECK(result != NULL)) {
+ TEST_MSG("output:%s\n", output);
+ }
+
+ /* check if float (for exp style float key)*/
+ result = strstr(output, "\"wasm_exp_float\":0.00354");
+ if (!TEST_CHECK(result != NULL)) {
+ TEST_MSG("output:%s\n", output);
+ }
+
+ /* check if float (for truncated float key)*/
+ result = strstr(output, "\"wasm_truncate_float\":120");
+ if (!TEST_CHECK(result != NULL)) {
+ TEST_MSG("output:%s\n", output);
+ }
+ result = strstr(output, "\"wasm_truncate_float\":120.");
+ if (!TEST_CHECK(result == NULL)) {
+ TEST_MSG("output:%s\n", output);
+ }
+
+ /* clean up */
+ flb_lib_free(output);
+
+ flb_stop(ctx);
+ flb_destroy(ctx);
+}
+
+void flb_test_array_contains_null(void)
+{
+ int ret;
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+ int filter_ffd;
+ char *output = NULL;
+ char *input = "[0, {\"hello\": [1, null, \"world\"]}]";
+ char *result;
+ struct flb_lib_out_cb cb_data;
+
+ /* clear previous output */
+ clear_output();
+
+ /* Create context, flush every second (some checks omitted here) */
+ ctx = flb_create();
+ flb_service_set(ctx, "flush", FLUSH_INTERVAL, "grace", "1", NULL);
+
+ /* Prepare output callback context*/
+ cb_data.cb = callback_test;
+ cb_data.data = NULL;
+
+ /* Filter */
+ filter_ffd = flb_filter(ctx, (char *) "wasm", NULL);
+ TEST_CHECK(filter_ffd >= 0);
+ ret = flb_filter_set(ctx, filter_ffd,
+ "Match", "*",
+ "wasm_path", DPATH_WASM "/modify_record.wasm",
+ "function_name", "filter_modify_record",
+ NULL);
+
+ /* Input */
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ flb_input_set(ctx, in_ffd, "tag", "test.wasm", NULL);
+ TEST_CHECK(in_ffd >= 0);
+
+ /* Lib output */
+ out_ffd = flb_output(ctx, (char *) "lib", (void *)&cb_data);
+ TEST_CHECK(out_ffd >= 0);
+ flb_output_set(ctx, out_ffd,
+ "match", "test.wasm",
+ "format", "json",
+ NULL);
+
+ ret = flb_start(ctx);
+ TEST_CHECK(ret==0);
+
+ flb_lib_push(ctx, in_ffd, input, strlen(input));
+ wait_with_timeout(2000, &output);
+
+ result = strstr(output, "[1,null,\"world\"]");
+ if(!TEST_CHECK(result != NULL)) {
+ TEST_MSG("output:%s\n", output);
+ }
+ result = strstr(output, "\"modify\":\"yes\"");
+ if(!TEST_CHECK(result != NULL)) {
+ TEST_MSG("output:%s\n", output);
+ }
+
+ /* clean up */
+ flb_lib_free(output);
+
+ flb_stop(ctx);
+ flb_destroy(ctx);
+}
+
+void flb_test_drop_all_records(void)
+{
+ int ret;
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+ int filter_ffd;
+ char *output = NULL;
+ char *input = "[0, {\"key\":\"val\"}]";
+ struct flb_lib_out_cb cb_data;
+
+ clear_output_num();
+
+ /* Create context, flush every second (some checks omitted here) */
+ ctx = flb_create();
+ flb_service_set(ctx, "flush", FLUSH_INTERVAL, "grace", "1", NULL);
+
+ /* Prepare output callback context*/
+ cb_data.cb = cb_count_msgpack_events;
+ cb_data.data = NULL;
+
+ /* Filter */
+ filter_ffd = flb_filter(ctx, (char *) "wasm", NULL);
+ TEST_CHECK(filter_ffd >= 0);
+ ret = flb_filter_set(ctx, filter_ffd,
+ "Match", "*",
+ "wasm_path", DPATH_WASM "/drop_record.wasm",
+ "function_name", "filter_drop_record",
+ NULL);
+ /* Input */
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ flb_input_set(ctx, in_ffd, "tag", "test", NULL);
+ TEST_CHECK(in_ffd >= 0);
+
+ /* Lib output */
+ out_ffd = flb_output(ctx, (char *) "lib", (void *)&cb_data);
+ TEST_CHECK(out_ffd >= 0);
+ flb_output_set(ctx, out_ffd,
+ "match", "test",
+ NULL);
+
+ ret = flb_start(ctx);
+ TEST_CHECK(ret==0);
+
+ flb_lib_push(ctx, in_ffd, input, strlen(input));
+ wait_with_timeout(2000, &output); /* waiting flush */
+
+ ret = get_output_num();
+ if (!TEST_CHECK(ret == 0)) {
+ TEST_MSG("error. got %d expect 0", ret);
+ }
+
+ flb_stop(ctx);
+ flb_destroy(ctx);
+}
+
+
+TEST_LIST = {
+ {"hello_world", flb_test_helloworld},
+ {"append_tag", flb_test_append_tag},
+ {"numeric_records", flb_test_numerics_records},
+ {"array_contains_null", flb_test_array_contains_null},
+ {"drop_all_records", flb_test_drop_all_records},
+ {NULL, NULL}
+};
diff --git a/fluent-bit/tests/runtime/flb_tests_runtime.h.in b/fluent-bit/tests/runtime/flb_tests_runtime.h.in
new file mode 100644
index 00000000..085fe6f1
--- /dev/null
+++ b/fluent-bit/tests/runtime/flb_tests_runtime.h.in
@@ -0,0 +1,52 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/* Fluent Bit
+ * ==========
+ * Copyright (C) 2019-2020 The Fluent Bit Authors
+ * Copyright (C) 2015-2018 Treasure Data Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef FLB_TESTS_RUNTIME_H
+#define FLB_TESTS_RUNTIME_H
+
+#include <sys/stat.h>
+
+#include "../lib/acutest/acutest.h"
+#define FLB_TESTS_DATA_PATH "@FLB_TESTS_DATA_PATH@"
+
+static inline int wait_for_file(char *path,
+ size_t minimum_size,
+ int time_limit)
+{
+ int elapsed_time;
+ struct stat file_info;
+ int result;
+
+ for (elapsed_time = 0 ; elapsed_time < time_limit ; elapsed_time++) {
+ result = stat(path, &file_info);
+
+ if (result == 0) {
+ if (file_info.st_size >= minimum_size) {
+ break;
+ }
+ }
+
+ sleep(1);
+ }
+
+ return result;
+}
+
+#endif
diff --git a/fluent-bit/tests/runtime/gen_data.py b/fluent-bit/tests/runtime/gen_data.py
new file mode 100755
index 00000000..dd145309
--- /dev/null
+++ b/fluent-bit/tests/runtime/gen_data.py
@@ -0,0 +1,63 @@
+#!/usr/bin/python
+
+# This script generate samples at data/ directory
+
+def write_header(handle, name):
+ handle.write("#define %s\t\"[\"\t\t\\\n" % name)
+ handle.write("\t\"1448403340,\"\t\t\t\\\n")
+ handle.write("\t\"{\"\t\t\t\t\\\n")
+
+def write_footer(handle):
+ handle.write("\t\"\\\"END_KEY\\\": \\\"JSON_END\\\"\"\t\t\\\n")
+ handle.write("\t\"}]\"\n")
+ handle.write("\n")
+
+def write_entry(handle, key, string, num_bool, eof=False):
+ if string:
+ handle.write(("\t\"\\\"%s\\\": \\\"%s\\\"" % (key, string)))
+ else:
+ handle.write(("\t\"\\\"%s\\\": %s" % (key, num_bool)))
+
+ handle.write(",\"\t\t\\\n")
+
+# Invalid JSON
+f = open("data/common/json_invalid.h", 'w')
+write_header(f, "JSON_INVALID")
+f.write("\t\"{{{{{{{{\"")
+write_footer(f)
+f.close()
+
+# A small JSON
+f = open("data/common/json_small.h", 'w')
+write_header(f, "JSON_SMALL")
+for i in range(0, 250):
+ write_entry(f, "key_%i" % i, None, "false", True)
+write_footer(f)
+f.close()
+
+# Long JSON
+f = open("data/common/json_long.h", 'w')
+write_header(f, "JSON_LONG")
+for i in range(0, 1000):
+ write_entry(f, "key_%i" % i, "val_%i" % i, None)
+write_footer(f)
+f.close()
+
+# Long JSON for TD
+f = open("data/td/json_td.h", 'w')
+write_header(f, "JSON_TD")
+for i in range(0, 500):
+ write_entry(f, "key_%i" % i, "val_%i" % i, None)
+write_footer(f)
+f.close()
+
+# JSON for ES
+f = open("data/es/json_es.h", 'w')
+write_header(f, "JSON_ES")
+write_entry(f, "key_0", None, "false")
+write_entry(f, "key_1", None, "true")
+write_entry(f, "key_2", "some string", None)
+write_entry(f, "key_3", None, 0.12345678)
+write_entry(f, "key_4", None, 5000)
+write_footer(f)
+f.close()
diff --git a/fluent-bit/tests/runtime/http_callbacks.c b/fluent-bit/tests/runtime/http_callbacks.c
new file mode 100644
index 00000000..76e1c5f3
--- /dev/null
+++ b/fluent-bit/tests/runtime/http_callbacks.c
@@ -0,0 +1,58 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+#include <fluent-bit.h>
+#include <fluent-bit/flb_http_client.h>
+
+#include "flb_tests_runtime.h"
+
+static void debug_cb_request_headers(char *name, void *p1, void *p2)
+{
+ struct flb_http_client *c = p1;
+
+ fprintf(stderr, "[http] request headers\n%s", c->header_buf);
+}
+
+void flb_test_http_callbacks()
+{
+ int ret;
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+
+ ctx = flb_create();
+ TEST_CHECK(ctx != NULL);
+
+ flb_service_set(ctx,
+ "Flush", "1",
+ "Grace", "5",
+ "Daemon", "false",
+ NULL);
+
+ in_ffd = flb_input(ctx, (char *) "dummy", NULL);
+ flb_input_set(ctx, in_ffd, "samples", "1", NULL);
+
+ out_ffd = flb_output(ctx, (char *) "http", NULL);
+ flb_output_set(ctx, out_ffd,
+ "match", "*",
+ "host", "google.com",
+ "port", "80",
+ "uri" , "/",
+ NULL);
+
+ flb_output_set_callback(ctx, out_ffd, "_debug.http.request_headers",
+ debug_cb_request_headers);
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ sleep(5);
+
+ flb_stop(ctx);
+ flb_destroy(ctx);
+
+}
+
+/* Test list */
+TEST_LIST = {
+ {"http_callbacks", flb_test_http_callbacks },
+ {NULL, NULL}
+};
diff --git a/fluent-bit/tests/runtime/in_cpu.c b/fluent-bit/tests/runtime/in_cpu.c
new file mode 120000
index 00000000..b194a853
--- /dev/null
+++ b/fluent-bit/tests/runtime/in_cpu.c
@@ -0,0 +1 @@
+in_simple_systems.c \ No newline at end of file
diff --git a/fluent-bit/tests/runtime/in_disk.c b/fluent-bit/tests/runtime/in_disk.c
new file mode 120000
index 00000000..b194a853
--- /dev/null
+++ b/fluent-bit/tests/runtime/in_disk.c
@@ -0,0 +1 @@
+in_simple_systems.c \ No newline at end of file
diff --git a/fluent-bit/tests/runtime/in_dummy.c b/fluent-bit/tests/runtime/in_dummy.c
new file mode 120000
index 00000000..b194a853
--- /dev/null
+++ b/fluent-bit/tests/runtime/in_dummy.c
@@ -0,0 +1 @@
+in_simple_systems.c \ No newline at end of file
diff --git a/fluent-bit/tests/runtime/in_elasticsearch.c b/fluent-bit/tests/runtime/in_elasticsearch.c
new file mode 100644
index 00000000..b373d285
--- /dev/null
+++ b/fluent-bit/tests/runtime/in_elasticsearch.c
@@ -0,0 +1,899 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/* Fluent Bit
+ * ==========
+ * Copyright (C) 2019-2023 The Fluent Bit Authors
+ * Copyright (C) 2015-2018 Treasure Data Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdlib.h>
+#include <fluent-bit.h>
+#include <fluent-bit/flb_compat.h>
+#include <fluent-bit/flb_time.h>
+#include <fluent-bit/flb_pack.h>
+#include <fluent-bit/flb_http_client.h>
+#include <fluent-bit/flb_gzip.h>
+#include <monkey/mk_core.h>
+#include "flb_tests_runtime.h"
+
+/* Test data */
+#include "data/in_elasticsearch/json_bulk.h" /* NDBULK_JSON */
+
+#define NDJSON_CONTENT_TYPE "application/x-ndjson"
+
+struct in_elasticsearch_client_ctx {
+ struct flb_upstream *u;
+ struct flb_connection *u_conn;
+ struct flb_config *config;
+ struct mk_event_loop *evl;
+};
+
+struct test_ctx {
+ flb_ctx_t *flb; /* Fluent Bit library context */
+ int i_ffd; /* Input fd */
+ int f_ffd; /* Filter fd (unused) */
+ int o_ffd; /* Output fd */
+ struct in_elasticsearch_client_ctx *httpc;
+};
+
+
+pthread_mutex_t result_mutex = PTHREAD_MUTEX_INITIALIZER;
+int num_output = 0;
+static int get_output_num()
+{
+ int ret;
+ pthread_mutex_lock(&result_mutex);
+ ret = num_output;
+ pthread_mutex_unlock(&result_mutex);
+
+ return ret;
+}
+
+static void set_output_num(int num)
+{
+ pthread_mutex_lock(&result_mutex);
+ num_output = num;
+ pthread_mutex_unlock(&result_mutex);
+}
+
+static void clear_output_num()
+{
+ set_output_num(0);
+}
+
+/* Callback to check expected results */
+static int cb_check_result_json(void *record, size_t size, void *data)
+{
+ char *p;
+ char *expected;
+ char *result;
+ int num = get_output_num();
+
+ set_output_num(num+1);
+
+ expected = (char *) data;
+ result = (char *) record;
+
+ p = strstr(result, expected);
+ TEST_CHECK(p != NULL);
+
+ if (p==NULL) {
+ flb_error("Expected to find: '%s' in result '%s'",
+ expected, result);
+ }
+ /*
+ * If you want to debug your test
+ *
+ * printf("Expect: '%s' in result '%s'", expected, result);
+ */
+ flb_free(record);
+ return 0;
+}
+
+struct in_elasticsearch_client_ctx* in_elasticsearch_client_ctx_create(int port)
+{
+ struct in_elasticsearch_client_ctx *ret_ctx = NULL;
+ struct mk_event_loop *evl = NULL;
+
+ ret_ctx = flb_calloc(1, sizeof(struct in_elasticsearch_client_ctx));
+ if (!TEST_CHECK(ret_ctx != NULL)) {
+ flb_errno();
+ TEST_MSG("flb_calloc(in_elasticsearch_client_ctx) failed");
+ return NULL;
+ }
+
+ evl = mk_event_loop_create(16);
+ if (!TEST_CHECK(evl != NULL)) {
+ TEST_MSG("mk_event_loop failed");
+ flb_free(ret_ctx);
+ return NULL;
+ }
+ ret_ctx->evl = evl;
+ flb_engine_evl_init();
+ flb_engine_evl_set(evl);
+
+ ret_ctx->config = flb_config_init();
+ if(!TEST_CHECK(ret_ctx->config != NULL)) {
+ TEST_MSG("flb_config_init failed");
+ mk_event_loop_destroy(evl);
+ flb_free(ret_ctx);
+ return NULL;
+ }
+
+ ret_ctx->u = flb_upstream_create(ret_ctx->config, "127.0.0.1", port, 0, NULL);
+ if (!TEST_CHECK(ret_ctx->u != NULL)) {
+ TEST_MSG("flb_upstream_create failed");
+ flb_config_exit(ret_ctx->config);
+ mk_event_loop_destroy(evl);
+ flb_free(ret_ctx);
+ return NULL;
+ }
+
+ ret_ctx->u_conn = flb_upstream_conn_get(ret_ctx->u);
+ TEST_CHECK(ret_ctx->u_conn != NULL);
+
+ ret_ctx->u_conn->upstream = ret_ctx->u;
+
+ return ret_ctx;
+}
+
+static struct test_ctx *test_ctx_create(struct flb_lib_out_cb *data)
+{
+ int i_ffd;
+ int o_ffd;
+ struct test_ctx *ctx = NULL;
+
+ ctx = flb_calloc(1, sizeof(struct test_ctx));
+ if (!TEST_CHECK(ctx != NULL)) {
+ TEST_MSG("flb_calloc failed");
+ flb_errno();
+ return NULL;
+ }
+
+ /* Service config */
+ ctx->flb = flb_create();
+ flb_service_set(ctx->flb,
+ "Flush", "0.200000000",
+ "Grace", "1",
+ "Log_Level", "error",
+ NULL);
+
+ /* Input */
+ i_ffd = flb_input(ctx->flb, (char *) "elasticsearch", NULL);
+ TEST_CHECK(i_ffd >= 0);
+ ctx->i_ffd = i_ffd;
+
+ /* Output */
+ o_ffd = flb_output(ctx->flb, (char *) "lib", (void *) data);
+ ctx->o_ffd = o_ffd;
+
+ return ctx;
+}
+
+int in_elasticsearch_client_ctx_destroy(struct in_elasticsearch_client_ctx* ctx)
+{
+ if (!TEST_CHECK(ctx != NULL)) {
+ return -1;
+ }
+ if (ctx->u) {
+ flb_upstream_destroy(ctx->u);
+ }
+ if (ctx->config) {
+ flb_config_exit(ctx->config);
+ }
+ if (ctx->evl) {
+ mk_event_loop_destroy(ctx->evl);
+ }
+
+ flb_free(ctx);
+ return 0;
+}
+
+static void test_ctx_destroy(struct test_ctx *ctx)
+{
+ TEST_CHECK(ctx != NULL);
+ if (ctx->httpc) {
+ in_elasticsearch_client_ctx_destroy(ctx->httpc);
+ }
+
+ sleep(1);
+ flb_stop(ctx->flb);
+ flb_destroy(ctx->flb);
+ flb_free(ctx);
+}
+
+void flb_test_in_elasticsearch_version()
+{
+ struct flb_lib_out_cb cb_data;
+ struct test_ctx *ctx;
+ struct flb_http_client *c;
+ int ret;
+ size_t b_sent;
+ char *expected = "\"version\":{\"number\":\"8.0.0\",\"build_flavor\"";
+ char *buf = NULL;
+ int port = 9201;
+ char sport[16];
+
+ snprintf(sport, 16, "%d", port);
+
+ ctx = test_ctx_create(&cb_data);
+ if (!TEST_CHECK(ctx != NULL)) {
+ TEST_MSG("test_ctx_create failed");
+ exit(EXIT_FAILURE);
+ }
+
+ ret = flb_input_set(ctx->flb, ctx->i_ffd,
+ "port", sport,
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ ret = flb_output_set(ctx->flb, ctx->o_ffd,
+ "match", "*",
+ "format", "json",
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ /* Start the engine */
+ ret = flb_start(ctx->flb);
+ TEST_CHECK(ret == 0);
+
+ ctx->httpc = in_elasticsearch_client_ctx_create(port);
+ TEST_CHECK(ctx->httpc != NULL);
+
+ c = flb_http_client(ctx->httpc->u_conn, FLB_HTTP_GET, "/", NULL, 0,
+ "127.0.0.1", port, NULL, 0);
+ if (!TEST_CHECK(c != NULL)) {
+ TEST_MSG("in_elasticsearch_client failed");
+ exit(EXIT_FAILURE);
+ }
+
+ ret = flb_http_do(c, &b_sent);
+ if (!TEST_CHECK(ret == 0)) {
+ TEST_MSG("ret error. ret=%d\n", ret);
+ }
+ else if (!TEST_CHECK(b_sent > 0)){
+ TEST_MSG("b_sent size error. b_sent = %lu\n", b_sent);
+ }
+ else if (!TEST_CHECK(c->resp.status == 200)) {
+ TEST_MSG("http response code error. expect: 200, got: %d\n", c->resp.status);
+ }
+
+ /* waiting to flush */
+ flb_time_msleep(1500);
+
+ buf = strstr(c->resp.payload, expected);
+ if (!TEST_CHECK(buf != NULL)) {
+ TEST_MSG("http request for version info failed");
+ }
+ flb_http_client_destroy(c);
+ flb_upstream_conn_release(ctx->httpc->u_conn);
+ test_ctx_destroy(ctx);
+}
+
+void flb_test_in_elasticsearch_version_configured()
+{
+ struct flb_lib_out_cb cb_data;
+ struct test_ctx *ctx;
+ struct flb_http_client *c;
+ int ret;
+ size_t b_sent;
+ char *expected = "\"version\":{\"number\":\"8.1.2\",\"build_flavor\"";
+ char *buf = NULL;
+ int port = 9202;
+ char sport[16];
+
+ snprintf(sport, 16, "%d", port);
+
+ ctx = test_ctx_create(&cb_data);
+ if (!TEST_CHECK(ctx != NULL)) {
+ TEST_MSG("test_ctx_create failed");
+ exit(EXIT_FAILURE);
+ }
+
+ ret = flb_input_set(ctx->flb, ctx->i_ffd,
+ "port", sport,
+ "version", "8.1.2",
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ ret = flb_output_set(ctx->flb, ctx->o_ffd,
+ "match", "*",
+ "format", "json",
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ /* Start the engine */
+ ret = flb_start(ctx->flb);
+ TEST_CHECK(ret == 0);
+
+ ctx->httpc = in_elasticsearch_client_ctx_create(port);
+ TEST_CHECK(ctx->httpc != NULL);
+
+ c = flb_http_client(ctx->httpc->u_conn, FLB_HTTP_GET, "/", NULL, 0,
+ "127.0.0.1", port, NULL, 0);
+ if (!TEST_CHECK(c != NULL)) {
+ TEST_MSG("in_elasticsearch_client failed");
+ exit(EXIT_FAILURE);
+ }
+
+ ret = flb_http_do(c, &b_sent);
+ if (!TEST_CHECK(ret == 0)) {
+ TEST_MSG("ret error. ret=%d\n", ret);
+ }
+ else if (!TEST_CHECK(b_sent > 0)){
+ TEST_MSG("b_sent size error. b_sent = %lu\n", b_sent);
+ }
+ else if (!TEST_CHECK(c->resp.status == 200)) {
+ TEST_MSG("http response code error. expect: 200, got: %d\n", c->resp.status);
+ }
+
+ /* waiting to flush */
+ flb_time_msleep(1500);
+
+ buf = strstr(c->resp.payload, expected);
+ if (!TEST_CHECK(buf != NULL)) {
+ TEST_MSG("http request for version info failed");
+ }
+ flb_http_client_destroy(c);
+ flb_upstream_conn_release(ctx->httpc->u_conn);
+ test_ctx_destroy(ctx);
+}
+
+void flb_test_in_elasticsearch(char *write_op, int port, char *tag)
+{
+ struct flb_lib_out_cb cb_data;
+ struct test_ctx *ctx;
+ struct flb_http_client *c;
+ int ret;
+ int num;
+ size_t b_sent;
+ char buf[64];
+ char expected[64];
+ char sport[16];
+
+ snprintf(buf, 64, "{\"%s\":{\"_index\":\"fluent-bit\",\"_id\":1}}\n{\"test\":\"msg\"}\n", write_op);
+ snprintf(expected, 64, "\"@meta\":{\"%s\":{\"_index\":\"fluent-bit\",\"_id\":1}},\"test\":\"msg\"", write_op);
+
+ snprintf(sport, 16, "%d", port);
+
+ clear_output_num();
+
+ cb_data.cb = cb_check_result_json;
+ cb_data.data = expected;
+
+ ctx = test_ctx_create(&cb_data);
+ if (!TEST_CHECK(ctx != NULL)) {
+ TEST_MSG("test_ctx_create failed");
+ exit(EXIT_FAILURE);
+ }
+
+ ret = flb_input_set(ctx->flb, ctx->i_ffd,
+ "port", sport,
+ NULL);
+ TEST_CHECK(ret == 0);
+ if (tag != NULL) {
+ ret = flb_input_set(ctx->flb, ctx->i_ffd,
+ "tag", tag,
+ NULL);
+ TEST_CHECK(ret == 0);
+ }
+
+ if (tag != NULL) {
+ ret = flb_output_set(ctx->flb, ctx->o_ffd,
+ "match", tag,
+ "format", "json",
+ NULL);
+ TEST_CHECK(ret == 0);
+ }
+ else {
+ ret = flb_output_set(ctx->flb, ctx->o_ffd,
+ "match", "*",
+ "format", "json",
+ NULL);
+ TEST_CHECK(ret == 0);
+ }
+
+ /* Start the engine */
+ ret = flb_start(ctx->flb);
+ TEST_CHECK(ret == 0);
+
+ ctx->httpc = in_elasticsearch_client_ctx_create(port);
+ TEST_CHECK(ctx->httpc != NULL);
+
+ c = flb_http_client(ctx->httpc->u_conn, FLB_HTTP_POST, "/_bulk", buf, strlen(buf),
+ "127.0.0.1", port, NULL, 0);
+ ret = flb_http_add_header(c, FLB_HTTP_HEADER_CONTENT_TYPE, strlen(FLB_HTTP_HEADER_CONTENT_TYPE),
+ NDJSON_CONTENT_TYPE, strlen(NDJSON_CONTENT_TYPE));
+ TEST_CHECK(ret == 0);
+ if (!TEST_CHECK(c != NULL)) {
+ TEST_MSG("in_elasticsearch_client failed");
+ exit(EXIT_FAILURE);
+ }
+
+ ret = flb_http_do(c, &b_sent);
+ if (!TEST_CHECK(ret == 0)) {
+ TEST_MSG("ret error. ret=%d\n", ret);
+ }
+ else if (!TEST_CHECK(b_sent > 0)){
+ TEST_MSG("b_sent size error. b_sent = %lu\n", b_sent);
+ }
+ else if (!TEST_CHECK(c->resp.status == 200)) {
+ TEST_MSG("http response code error. expect: 200, got: %d\n", c->resp.status);
+ }
+
+ /* waiting to flush */
+ flb_time_msleep(1500);
+
+ num = get_output_num();
+ if (!TEST_CHECK(num > 0)) {
+ TEST_MSG("no outputs");
+ }
+ flb_http_client_destroy(c);
+ flb_upstream_conn_release(ctx->httpc->u_conn);
+ test_ctx_destroy(ctx);
+}
+
+void flb_test_in_elasticsearch_index_op()
+{
+ flb_test_in_elasticsearch("index", 9203, NULL);
+}
+
+void flb_test_in_elasticsearch_create_op()
+{
+ flb_test_in_elasticsearch("create", 9204, NULL);
+}
+
+void flb_test_in_elasticsearch_invalid(char *write_op, int status, char *expected_op, int port)
+{
+ struct flb_lib_out_cb cb_data;
+ struct test_ctx *ctx;
+ struct flb_http_client *c;
+ int ret;
+ int num;
+ size_t b_sent;
+ char buf[64];
+ char expected[64];
+ char *ret_buf = NULL;
+ char sport[16];
+
+ snprintf(buf, 64, "{\"%s\":{\"_index\":\"fluent-bit\",\"_id\":1}}\n{\"test\":\"msg\"}\n", write_op);
+ snprintf(expected, 64, "{\"%s\":{\"status\":%d", expected_op, status);
+
+ snprintf(sport, 16, "%d", port);
+
+ clear_output_num();
+
+ cb_data.cb = NULL;
+ cb_data.data = NULL;
+
+ ctx = test_ctx_create(&cb_data);
+ if (!TEST_CHECK(ctx != NULL)) {
+ TEST_MSG("test_ctx_create failed");
+ exit(EXIT_FAILURE);
+ }
+
+ ret = flb_input_set(ctx->flb, ctx->i_ffd,
+ "port", sport,
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ ret = flb_output_set(ctx->flb, ctx->o_ffd,
+ "match", "*",
+ "format", "json",
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ /* Start the engine */
+ ret = flb_start(ctx->flb);
+ TEST_CHECK(ret == 0);
+
+ ctx->httpc = in_elasticsearch_client_ctx_create(port);
+ TEST_CHECK(ctx->httpc != NULL);
+
+ c = flb_http_client(ctx->httpc->u_conn, FLB_HTTP_POST, "/_bulk", buf, strlen(buf),
+ "127.0.0.1", port, NULL, 0);
+ ret = flb_http_add_header(c, FLB_HTTP_HEADER_CONTENT_TYPE, strlen(FLB_HTTP_HEADER_CONTENT_TYPE),
+ NDJSON_CONTENT_TYPE, strlen(NDJSON_CONTENT_TYPE));
+ TEST_CHECK(ret == 0);
+ if (!TEST_CHECK(c != NULL)) {
+ TEST_MSG("in_elasticsearch_client failed");
+ exit(EXIT_FAILURE);
+ }
+
+ ret = flb_http_do(c, &b_sent);
+ if (!TEST_CHECK(ret == 0)) {
+ TEST_MSG("ret error. ret=%d\n", ret);
+ }
+ else if (!TEST_CHECK(b_sent > 0)){
+ TEST_MSG("b_sent size error. b_sent = %lu\n", b_sent);
+ }
+ else if (!TEST_CHECK(c->resp.status == 200)) {
+ TEST_MSG("http response code error. expect: 200, got: %d\n", c->resp.status);
+ }
+
+ /* waiting to flush */
+ flb_time_msleep(1500);
+
+ num = get_output_num();
+ if (!TEST_CHECK(num == 0)) {
+ TEST_MSG("invalid ingested requests");
+ }
+ ret_buf = strstr(c->resp.payload, expected);
+ if (!TEST_CHECK(ret_buf != NULL)) {
+ TEST_MSG("http request for bulk failed");
+ }
+
+ flb_http_client_destroy(c);
+ flb_upstream_conn_release(ctx->httpc->u_conn);
+ test_ctx_destroy(ctx);
+}
+
+void flb_test_in_elasticsearch_update_op()
+{
+ flb_test_in_elasticsearch_invalid("update", 403, "update", 9205);
+}
+
+void flb_test_in_elasticsearch_delete_op()
+{
+ flb_test_in_elasticsearch_invalid("delete", 404, "delete", 9206);
+}
+
+void flb_test_in_elasticsearch_nonexistent_op()
+{
+ flb_test_in_elasticsearch_invalid("nonexistent", 400, "unknown", 9207);
+}
+
+void flb_test_in_elasticsearch_multi_ops()
+{
+ struct flb_lib_out_cb cb_data;
+ struct test_ctx *ctx;
+ struct flb_http_client *c;
+ int ret;
+ int num;
+ int port = 9208;
+ char sport[16];
+ size_t b_sent;
+ char *buf = NDJSON_BULK;
+ char *expected = ":{\"_index\":\"test\",\"_id\":";
+ char *ret_buf = NULL;
+ char *ret_expected = "{\"errors\":true,\"items\":[";
+
+ snprintf(sport, 16, "%d", port);
+
+ clear_output_num();
+
+ cb_data.cb = cb_check_result_json;
+ cb_data.data = expected;
+
+ ctx = test_ctx_create(&cb_data);
+ if (!TEST_CHECK(ctx != NULL)) {
+ TEST_MSG("test_ctx_create failed");
+ exit(EXIT_FAILURE);
+ }
+
+ ret = flb_input_set(ctx->flb, ctx->i_ffd,
+ "port", sport,
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ ret = flb_output_set(ctx->flb, ctx->o_ffd,
+ "match", "*",
+ "format", "json",
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ /* Start the engine */
+ ret = flb_start(ctx->flb);
+ TEST_CHECK(ret == 0);
+
+ ctx->httpc = in_elasticsearch_client_ctx_create(port);
+ TEST_CHECK(ctx->httpc != NULL);
+
+ c = flb_http_client(ctx->httpc->u_conn, FLB_HTTP_POST, "/_bulk", buf, strlen(buf),
+ "127.0.0.1", port, NULL, 0);
+ ret = flb_http_add_header(c, FLB_HTTP_HEADER_CONTENT_TYPE, strlen(FLB_HTTP_HEADER_CONTENT_TYPE),
+ NDJSON_CONTENT_TYPE, strlen(NDJSON_CONTENT_TYPE));
+ TEST_CHECK(ret == 0);
+ if (!TEST_CHECK(c != NULL)) {
+ TEST_MSG("in_elasticsearch_client failed");
+ exit(EXIT_FAILURE);
+ }
+
+ ret = flb_http_do(c, &b_sent);
+ if (!TEST_CHECK(ret == 0)) {
+ TEST_MSG("ret error. ret=%d\n", ret);
+ }
+ else if (!TEST_CHECK(b_sent > 0)){
+ TEST_MSG("b_sent size error. b_sent = %lu\n", b_sent);
+ }
+ else if (!TEST_CHECK(c->resp.status == 200)) {
+ TEST_MSG("http response code error. expect: 200, got: %d\n", c->resp.status);
+ }
+
+ /* waiting to flush */
+ flb_time_msleep(1500);
+
+ num = get_output_num();
+ if (!TEST_CHECK(num > 0)) {
+ TEST_MSG("no outputs");
+ }
+ ret_buf = strstr(c->resp.payload, ret_expected);
+ if (!TEST_CHECK(ret_buf != NULL)) {
+ TEST_MSG("bulk request for multi write ops failed");
+ }
+ flb_http_client_destroy(c);
+ flb_upstream_conn_release(ctx->httpc->u_conn);
+ test_ctx_destroy(ctx);
+}
+
+void flb_test_in_elasticsearch_multi_ops_gzip()
+{
+ struct flb_lib_out_cb cb_data;
+ struct test_ctx *ctx;
+ struct flb_http_client *c;
+ int ret;
+ int num;
+ int port = 9209;
+ char sport[16];
+ size_t b_sent;
+ char *buf = NDJSON_BULK;
+ char *expected = ":{\"_index\":\"test\",\"_id\":";
+ char *ret_buf = NULL;
+ char *ret_expected = "{\"errors\":true,\"items\":[";
+ void *final_data;
+ size_t final_bytes;
+
+ snprintf(sport, 16, "%d", port);
+
+ clear_output_num();
+
+ cb_data.cb = cb_check_result_json;
+ cb_data.data = expected;
+
+ ctx = test_ctx_create(&cb_data);
+ if (!TEST_CHECK(ctx != NULL)) {
+ TEST_MSG("test_ctx_create failed");
+ exit(EXIT_FAILURE);
+ }
+
+ ret = flb_input_set(ctx->flb, ctx->i_ffd,
+ "port", sport,
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ ret = flb_output_set(ctx->flb, ctx->o_ffd,
+ "match", "*",
+ "format", "json",
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ /* Start the engine */
+ ret = flb_start(ctx->flb);
+ TEST_CHECK(ret == 0);
+
+ ctx->httpc = in_elasticsearch_client_ctx_create(port);
+ TEST_CHECK(ctx->httpc != NULL);
+
+ ret = flb_gzip_compress((void *) buf, strlen(buf), &final_data, &final_bytes);
+ TEST_CHECK(ret != -1);
+
+ c = flb_http_client(ctx->httpc->u_conn, FLB_HTTP_POST, "/_bulk", final_data, final_bytes,
+ "127.0.0.1", port, NULL, 0);
+ ret = flb_http_add_header(c, FLB_HTTP_HEADER_CONTENT_TYPE, strlen(FLB_HTTP_HEADER_CONTENT_TYPE),
+ NDJSON_CONTENT_TYPE, strlen(NDJSON_CONTENT_TYPE));
+ TEST_CHECK(ret == 0);
+ /* Add Content-Encoding: gzip */
+ ret = flb_http_add_header(c, "Content-Encoding", 16, "gzip", 4);
+ TEST_CHECK(ret == 0);
+
+ if (!TEST_CHECK(c != NULL)) {
+ TEST_MSG("in_elasticsearch_client failed");
+ exit(EXIT_FAILURE);
+ }
+
+ ret = flb_http_do(c, &b_sent);
+ if (!TEST_CHECK(ret == 0)) {
+ TEST_MSG("ret error. ret=%d\n", ret);
+ }
+ else if (!TEST_CHECK(b_sent > 0)){
+ TEST_MSG("b_sent size error. b_sent = %lu\n", b_sent);
+ }
+ else if (!TEST_CHECK(c->resp.status == 200)) {
+ TEST_MSG("http response code error. expect: 200, got: %d\n", c->resp.status);
+ }
+ flb_free(final_data);
+
+ /* waiting to flush */
+ flb_time_msleep(1500);
+
+ num = get_output_num();
+ if (!TEST_CHECK(num > 0)) {
+ TEST_MSG("no outputs");
+ }
+ ret_buf = strstr(c->resp.payload, ret_expected);
+ if (!TEST_CHECK(ret_buf != NULL)) {
+ TEST_MSG("bulk request for multi write ops failed");
+ }
+ flb_http_client_destroy(c);
+ flb_upstream_conn_release(ctx->httpc->u_conn);
+ test_ctx_destroy(ctx);
+}
+
+void flb_test_in_elasticsearch_node_info()
+{
+ struct flb_lib_out_cb cb_data;
+ struct test_ctx *ctx;
+ struct flb_http_client *c;
+ int ret;
+ int port = 9210;
+ char sport[16];
+ size_t b_sent;
+ char *expected = "{\"_nodes\":{\"total\":1,\"successful\":1,\"failed\":0},\"nodes\":{\"";
+ char *buf = NULL;
+
+ snprintf(sport, 16, "%d", port);
+
+ ctx = test_ctx_create(&cb_data);
+ if (!TEST_CHECK(ctx != NULL)) {
+ TEST_MSG("test_ctx_create failed");
+ exit(EXIT_FAILURE);
+ }
+
+ ret = flb_input_set(ctx->flb, ctx->i_ffd,
+ "port", sport,
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ ret = flb_output_set(ctx->flb, ctx->o_ffd,
+ "match", "*",
+ "format", "json",
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ /* Start the engine */
+ ret = flb_start(ctx->flb);
+ TEST_CHECK(ret == 0);
+
+ ctx->httpc = in_elasticsearch_client_ctx_create(port);
+ TEST_CHECK(ctx->httpc != NULL);
+
+ c = flb_http_client(ctx->httpc->u_conn, FLB_HTTP_GET, "/_nodes/http", NULL, 0,
+ "127.0.0.1", port, NULL, 0);
+ if (!TEST_CHECK(c != NULL)) {
+ TEST_MSG("in_elasticsearch_client failed");
+ exit(EXIT_FAILURE);
+ }
+
+ ret = flb_http_do(c, &b_sent);
+ if (!TEST_CHECK(ret == 0)) {
+ TEST_MSG("ret error. ret=%d\n", ret);
+ }
+ else if (!TEST_CHECK(b_sent > 0)){
+ TEST_MSG("b_sent size error. b_sent = %lu\n", b_sent);
+ }
+ else if (!TEST_CHECK(c->resp.status == 200)) {
+ TEST_MSG("http response code error. expect: 200, got: %d\n", c->resp.status);
+ }
+
+ /* waiting to flush */
+ flb_time_msleep(1500);
+
+ buf = strstr(c->resp.payload, expected);
+ if (!TEST_CHECK(buf != NULL)) {
+ TEST_MSG("http request for version info failed");
+ }
+ flb_http_client_destroy(c);
+ flb_upstream_conn_release(ctx->httpc->u_conn);
+ test_ctx_destroy(ctx);
+}
+
+void flb_test_in_elasticsearch_tag_key()
+{
+ struct flb_lib_out_cb cb_data;
+ struct test_ctx *ctx;
+ struct flb_http_client *c;
+ int ret;
+ int num;
+ size_t b_sent;
+ int port = 9211;
+ char sport[16];
+
+ char *buf = "{\"index\":{\"_index\":\"fluent-bit\"}}\n{\"test\":\"msg\",\"tag\":\"new_tag\"}\n";
+
+ snprintf(sport, 16, "%d", port);
+
+ clear_output_num();
+
+ cb_data.cb = cb_check_result_json;
+ cb_data.data = "\"@meta\":{\"index\":{\"_index\":\"fluent-bit\"}},\"test\":\"msg\",\"tag\":\"new_tag\"";
+
+ ctx = test_ctx_create(&cb_data);
+ if (!TEST_CHECK(ctx != NULL)) {
+ TEST_MSG("test_ctx_create failed");
+ exit(EXIT_FAILURE);
+ }
+
+ ret = flb_input_set(ctx->flb, ctx->i_ffd,
+ "port", sport,
+ "tag_key", "tag",
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ ret = flb_output_set(ctx->flb, ctx->o_ffd,
+ "match", "new_tag",
+ "format", "json",
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ /* Start the engine */
+ ret = flb_start(ctx->flb);
+ TEST_CHECK(ret == 0);
+
+ ctx->httpc = in_elasticsearch_client_ctx_create(port);
+ TEST_CHECK(ctx->httpc != NULL);
+
+ c = flb_http_client(ctx->httpc->u_conn, FLB_HTTP_POST, "/_bulk", buf, strlen(buf),
+ "127.0.0.1", port, NULL, 0);
+ ret = flb_http_add_header(c, FLB_HTTP_HEADER_CONTENT_TYPE, strlen(FLB_HTTP_HEADER_CONTENT_TYPE),
+ NDJSON_CONTENT_TYPE, strlen(NDJSON_CONTENT_TYPE));
+ TEST_CHECK(ret == 0);
+ if (!TEST_CHECK(c != NULL)) {
+ TEST_MSG("in_elasticsearch_client failed");
+ exit(EXIT_FAILURE);
+ }
+
+ ret = flb_http_do(c, &b_sent);
+ if (!TEST_CHECK(ret == 0)) {
+ TEST_MSG("ret error. ret=%d\n", ret);
+ }
+ else if (!TEST_CHECK(b_sent > 0)){
+ TEST_MSG("b_sent size error. b_sent = %lu\n", b_sent);
+ }
+ else if (!TEST_CHECK(c->resp.status == 200)) {
+ TEST_MSG("http response code error. expect: 200, got: %d\n", c->resp.status);
+ }
+
+ /* waiting to flush */
+ flb_time_msleep(1500);
+
+ num = get_output_num();
+ if (!TEST_CHECK(num > 0)) {
+ TEST_MSG("no outputs");
+ }
+ flb_http_client_destroy(c);
+ flb_upstream_conn_release(ctx->httpc->u_conn);
+ test_ctx_destroy(ctx);
+}
+
+void flb_test_in_elasticsearch_index_op_with_plugin_tag()
+{
+ flb_test_in_elasticsearch("index", 9210, "es.index");
+}
+
+TEST_LIST = {
+ {"version", flb_test_in_elasticsearch_version},
+ {"configured_version", flb_test_in_elasticsearch_version_configured},
+ {"index_op", flb_test_in_elasticsearch_index_op},
+ {"create_op", flb_test_in_elasticsearch_create_op},
+ {"update_op", flb_test_in_elasticsearch_update_op},
+ {"delete_op", flb_test_in_elasticsearch_delete_op},
+ {"nonexistent_op", flb_test_in_elasticsearch_nonexistent_op},
+ {"multi_ops", flb_test_in_elasticsearch_multi_ops},
+ {"multi_ops_gzip", flb_test_in_elasticsearch_multi_ops_gzip},
+ {"index_op_with_plugin_tag", flb_test_in_elasticsearch_index_op_with_plugin_tag},
+ {"node_info", flb_test_in_elasticsearch_node_info},
+ {"tag_key", flb_test_in_elasticsearch_tag_key},
+ {NULL, NULL}
+};
diff --git a/fluent-bit/tests/runtime/in_event_test.c b/fluent-bit/tests/runtime/in_event_test.c
new file mode 100644
index 00000000..1169d8fd
--- /dev/null
+++ b/fluent-bit/tests/runtime/in_event_test.c
@@ -0,0 +1,35 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+#include <fluent-bit.h>
+#include "flb_tests_runtime.h"
+
+void flb_test_input_event()
+{
+ int ret;
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+
+ ctx = flb_create();
+
+ in_ffd = flb_input(ctx, (char *) "event_test", NULL);
+ TEST_CHECK(in_ffd >= 0);
+
+ out_ffd = flb_output(ctx, (char *) "null", NULL);
+ TEST_CHECK(out_ffd >= 0);
+ flb_output_set(ctx, out_ffd, "match", "test", NULL);
+
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ sleep(8);
+
+ flb_stop(ctx);
+ flb_destroy(ctx);
+}
+
+/* Test list */
+TEST_LIST = {
+ {"event_test", flb_test_input_event},
+ {NULL, NULL}
+};
diff --git a/fluent-bit/tests/runtime/in_fluentbit_metrics.c b/fluent-bit/tests/runtime/in_fluentbit_metrics.c
new file mode 100644
index 00000000..cb82ec2d
--- /dev/null
+++ b/fluent-bit/tests/runtime/in_fluentbit_metrics.c
@@ -0,0 +1,226 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/* Fluent Bit
+ * ==========
+ * Copyright (C) 2019-2022 The Fluent Bit Authors
+ * Copyright (C) 2015-2018 Treasure Data Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <fluent-bit.h>
+#include <fluent-bit/flb_compat.h>
+#include <fluent-bit/flb_time.h>
+#include <float.h>
+#include "flb_tests_runtime.h"
+
+struct test_ctx {
+ flb_ctx_t *flb; /* Fluent Bit library context */
+ int i_ffd; /* Input fd */
+ int f_ffd; /* Filter fd (unused) */
+ int o_ffd; /* Output fd */
+};
+
+
+pthread_mutex_t result_mutex = PTHREAD_MUTEX_INITIALIZER;
+int num_output = 0;
+static int get_output_num()
+{
+ int ret;
+ pthread_mutex_lock(&result_mutex);
+ ret = num_output;
+ pthread_mutex_unlock(&result_mutex);
+
+ return ret;
+}
+
+static void set_output_num(int num)
+{
+ pthread_mutex_lock(&result_mutex);
+ num_output = num;
+ pthread_mutex_unlock(&result_mutex);
+}
+
+static void clear_output_num()
+{
+ set_output_num(0);
+}
+
+struct str_list {
+ size_t size;
+ char **lists;
+};
+
+void wait_with_timeout(uint32_t timeout_ms, int *output_num)
+{
+ struct flb_time start_time;
+ struct flb_time end_time;
+ struct flb_time diff_time;
+ uint64_t elapsed_time_flb = 0;
+
+ flb_time_get(&start_time);
+
+ while (true) {
+ *output_num = get_output_num();
+
+ if (*output_num > 0) {
+ break;
+ }
+
+ flb_time_msleep(100);
+ flb_time_get(&end_time);
+ flb_time_diff(&end_time, &start_time, &diff_time);
+ elapsed_time_flb = flb_time_to_nanosec(&diff_time) / 1000000;
+
+ if (elapsed_time_flb > timeout_ms) {
+ flb_warn("[timeout] elapsed_time: %ld", elapsed_time_flb);
+ // Reached timeout.
+ break;
+ }
+ }
+}
+
+/* Callback to check expected results */
+static int cb_check_json_str_list(void *record, size_t size, void *data)
+{
+ char *p;
+ char *result;
+ int num = get_output_num();
+ size_t i;
+ struct str_list *l = (struct str_list*)data;
+
+ if (!TEST_CHECK(l != NULL)) {
+ TEST_MSG("Data is NULL");
+ flb_free(record);
+ return 0;
+ }
+
+ set_output_num(num+1);
+
+ result = (char *) record;
+
+ for (i=0; i<l->size; i++) {
+ p = strstr(result, l->lists[i]);
+ if(!TEST_CHECK(p != NULL)) {
+ TEST_MSG("Expected to find: '%s' in result '%s'",
+ l->lists[i], result);
+ }
+ }
+
+ /*
+ * If you want to debug your test
+ *
+ * printf("Expect: '%s' in result '%s'", expected, result);
+ */
+ flb_free(record);
+ return 0;
+}
+
+static struct test_ctx *test_ctx_create(struct flb_lib_out_cb *data)
+{
+ int o_ffd;
+ struct test_ctx *ctx = NULL;
+
+ ctx = flb_malloc(sizeof(struct test_ctx));
+ if (!TEST_CHECK(ctx != NULL)) {
+ TEST_MSG("malloc failed");
+ flb_errno();
+ return NULL;
+ }
+
+ /* Service config */
+ ctx->flb = flb_create();
+ flb_service_set(ctx->flb,
+ "Flush", "0.200000000",
+ "Grace", "1",
+ "Log_Level", "error",
+ NULL);
+
+ ctx->i_ffd = flb_input(ctx->flb, (char *) "fluentbit_metrics", NULL);
+ TEST_CHECK(ctx->i_ffd >= 0);
+
+ /* Output */
+ o_ffd = flb_output(ctx->flb, (char *) "lib", (void *) data);
+ ctx->o_ffd = o_ffd;
+ TEST_CHECK(ctx->o_ffd >= 0);
+
+ return ctx;
+}
+
+static void test_ctx_destroy(struct test_ctx *ctx)
+{
+ TEST_CHECK(ctx != NULL);
+
+ sleep(1);
+ flb_stop(ctx->flb);
+ flb_destroy(ctx->flb);
+ flb_free(ctx);
+}
+
+#ifdef FLB_HAVE_METRICS
+char *basic_expected_strs[] = {"\"uptime\"", "\"records_total\"", "\"bytes_total\"", "\"proc_records_total\"", "\"proc_bytes_total\"", "\"errors_total\"", "\"retries_total\"", "\"retries_failed_total\"", "\"dropped_records_total\"", "\"retried_records_total\""};
+
+static void test_basic(void)
+{
+ struct flb_lib_out_cb cb_data;
+ struct test_ctx *ctx;
+ int ret;
+ int num;
+
+ struct str_list expected = {
+ .size = sizeof(basic_expected_strs)/sizeof(char*),
+ .lists = &basic_expected_strs[0],
+ };
+
+ clear_output_num();
+
+ cb_data.cb = cb_check_json_str_list;
+ cb_data.data = &expected;
+
+ ctx = test_ctx_create(&cb_data);
+ if (!TEST_CHECK(ctx != NULL)) {
+ TEST_MSG("test_ctx_create failed");
+ exit(EXIT_FAILURE);
+ }
+ /* Input */
+ ret = flb_input_set(ctx->flb, ctx->i_ffd,
+ "scrape_interval", "1",
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ ret = flb_output_set(ctx->flb, ctx->o_ffd,
+ "format", "json",
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ /* Start the engine */
+ ret = flb_start(ctx->flb);
+ TEST_CHECK(ret == 0);
+
+ /* waiting to flush */
+ wait_with_timeout(3000, &num);
+
+ if (!TEST_CHECK(num > 0)) {
+ TEST_MSG("no outputs");
+ }
+
+ test_ctx_destroy(ctx);
+}
+#endif
+
+TEST_LIST = {
+#ifdef FLB_HAVE_METRICS
+ {"basic", test_basic},
+#endif
+ {NULL, NULL}
+};
diff --git a/fluent-bit/tests/runtime/in_forward.c b/fluent-bit/tests/runtime/in_forward.c
new file mode 100644
index 00000000..6cabfa94
--- /dev/null
+++ b/fluent-bit/tests/runtime/in_forward.c
@@ -0,0 +1,579 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/* Fluent Bit
+ * ==========
+ * Copyright (C) 2019-2022 The Fluent Bit Authors
+ * Copyright (C) 2015-2018 Treasure Data Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <fluent-bit.h>
+#include <fluent-bit/flb_compat.h>
+#include <fluent-bit/flb_time.h>
+#include <fluent-bit/flb_pack.h>
+#include <fluent-bit/flb_socket.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#ifdef FLB_HAVE_UNIX_SOCKET
+#include <sys/socket.h>
+#include <sys/un.h>
+#endif
+#include <fcntl.h>
+#include "flb_tests_runtime.h"
+
+struct test_ctx {
+ flb_ctx_t *flb; /* Fluent Bit library context */
+ int i_ffd; /* Input fd */
+ int f_ffd; /* Filter fd (unused) */
+ int o_ffd; /* Output fd */
+};
+
+
+pthread_mutex_t result_mutex = PTHREAD_MUTEX_INITIALIZER;
+int num_output = 0;
+static int get_output_num()
+{
+ int ret;
+ pthread_mutex_lock(&result_mutex);
+ ret = num_output;
+ pthread_mutex_unlock(&result_mutex);
+
+ return ret;
+}
+
+static void set_output_num(int num)
+{
+ pthread_mutex_lock(&result_mutex);
+ num_output = num;
+ pthread_mutex_unlock(&result_mutex);
+}
+
+static void clear_output_num()
+{
+ set_output_num(0);
+}
+
+static int create_simple_json(char **out_buf, size_t *size)
+{
+ int root_type;
+ int ret;
+ char json[] = "[\"test\", 1234567890,{\"test\":\"msg\"} ]";
+
+ ret = flb_pack_json(&json[0], strlen(json), out_buf, size, &root_type, NULL);
+ TEST_CHECK(ret==0);
+
+ return ret;
+}
+
+
+/* Callback to check expected results */
+static int cb_check_result_json(void *record, size_t size, void *data)
+{
+ char *p;
+ char *expected;
+ char *result;
+ int num = get_output_num();
+
+ set_output_num(num+1);
+
+ expected = (char *) data;
+ result = (char *) record;
+
+ p = strstr(result, expected);
+ TEST_CHECK(p != NULL);
+
+ if (p==NULL) {
+ flb_error("Expected to find: '%s' in result '%s'",
+ expected, result);
+ }
+ /*
+ * If you want to debug your test
+ *
+ * printf("Expect: '%s' in result '%s'", expected, result);
+ */
+ flb_free(record);
+ return 0;
+}
+
+static struct test_ctx *test_ctx_create(struct flb_lib_out_cb *data)
+{
+ int i_ffd;
+ int o_ffd;
+ struct test_ctx *ctx = NULL;
+
+ ctx = flb_malloc(sizeof(struct test_ctx));
+ if (!TEST_CHECK(ctx != NULL)) {
+ TEST_MSG("malloc failed");
+ flb_errno();
+ return NULL;
+ }
+
+ /* Service config */
+ ctx->flb = flb_create();
+ flb_service_set(ctx->flb,
+ "Flush", "0.200000000",
+ "Grace", "1",
+ "Log_Level", "error",
+ NULL);
+
+ /* Input */
+ i_ffd = flb_input(ctx->flb, (char *) "forward", NULL);
+ TEST_CHECK(i_ffd >= 0);
+ ctx->i_ffd = i_ffd;
+
+ /* Output */
+ o_ffd = flb_output(ctx->flb, (char *) "lib", (void *) data);
+ ctx->o_ffd = o_ffd;
+
+ return ctx;
+}
+
+static void test_ctx_destroy(struct test_ctx *ctx)
+{
+ TEST_CHECK(ctx != NULL);
+
+ sleep(1);
+ flb_stop(ctx->flb);
+ flb_destroy(ctx->flb);
+ flb_free(ctx);
+}
+
+#define DEFAULT_HOST "127.0.0.1"
+#define DEFAULT_PORT 24224
+static flb_sockfd_t connect_tcp(char *in_host, int in_port)
+{
+ int port = in_port;
+ char *host = in_host;
+ flb_sockfd_t fd;
+ int ret;
+ struct sockaddr_in addr;
+
+ if (host == NULL) {
+ host = DEFAULT_HOST;
+ }
+ if (port < 0) {
+ port = DEFAULT_PORT;
+ }
+
+ memset(&addr, 0, sizeof(addr));
+ fd = socket(PF_INET, SOCK_STREAM, 0);
+ if (!TEST_CHECK(fd >= 0)) {
+ TEST_MSG("failed to socket. host=%s port=%d errno=%d", host, port, errno);
+ return -1;
+ }
+
+ addr.sin_family = AF_INET;
+ addr.sin_addr.s_addr = inet_addr(host);
+ addr.sin_port = htons(port);
+
+ ret = connect(fd, (const struct sockaddr *)&addr, sizeof(addr));
+ if (!TEST_CHECK(ret >= 0)) {
+ TEST_MSG("failed to connect. host=%s port=%d errno=%d", host, port, errno);
+ flb_socket_close(fd);
+ return -1;
+ }
+ return fd;
+}
+
+void flb_test_forward()
+{
+ struct flb_lib_out_cb cb_data;
+ struct test_ctx *ctx;
+ flb_sockfd_t fd;
+ int ret;
+ int num;
+ ssize_t w_size;
+
+ char *buf;
+ size_t size;
+
+ clear_output_num();
+
+ cb_data.cb = cb_check_result_json;
+ cb_data.data = "\"test\":\"msg\"";
+
+ ctx = test_ctx_create(&cb_data);
+ if (!TEST_CHECK(ctx != NULL)) {
+ TEST_MSG("test_ctx_create failed");
+ exit(EXIT_FAILURE);
+ }
+
+ ret = flb_output_set(ctx->flb, ctx->o_ffd,
+ "match", "test",
+ "format", "json",
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ /* Start the engine */
+ ret = flb_start(ctx->flb);
+ TEST_CHECK(ret == 0);
+
+ /* use default host/port */
+ fd = connect_tcp(NULL, -1);
+ if (!TEST_CHECK(fd >= 0)) {
+ exit(EXIT_FAILURE);
+ }
+ create_simple_json(&buf, &size);
+ w_size = send(fd, buf, size, 0);
+ flb_free(buf);
+ if (!TEST_CHECK(w_size == size)) {
+ TEST_MSG("failed to send, errno=%d", errno);
+ flb_socket_close(fd);
+ exit(EXIT_FAILURE);
+ }
+
+ /* waiting to flush */
+ flb_time_msleep(1500);
+
+ num = get_output_num();
+ if (!TEST_CHECK(num > 0)) {
+ TEST_MSG("no outputs");
+ }
+
+ flb_socket_close(fd);
+ test_ctx_destroy(ctx);
+}
+
+void flb_test_forward_port()
+{
+ struct flb_lib_out_cb cb_data;
+ struct test_ctx *ctx;
+
+ flb_sockfd_t fd;
+ int ret;
+ int num;
+ ssize_t w_size;
+ char *port = "24000";
+
+ char *buf;
+ size_t size;
+
+ clear_output_num();
+
+ cb_data.cb = cb_check_result_json;
+ cb_data.data = "\"test\":\"msg\"";
+
+ ctx = test_ctx_create(&cb_data);
+ if (!TEST_CHECK(ctx != NULL)) {
+ TEST_MSG("test_ctx_create failed");
+ exit(EXIT_FAILURE);
+ }
+
+ ret = flb_input_set(ctx->flb, ctx->i_ffd,
+ "port", port,
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ ret = flb_output_set(ctx->flb, ctx->o_ffd,
+ "match", "test",
+ "format", "json",
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ /* Start the engine */
+ ret = flb_start(ctx->flb);
+ TEST_CHECK(ret == 0);
+
+ /* use default host */
+ fd = connect_tcp(NULL, atoi(port));
+ if (!TEST_CHECK(fd >= 0)) {
+ exit(EXIT_FAILURE);
+ }
+
+ create_simple_json(&buf, &size);
+ w_size = send(fd, buf, size, 0);
+ flb_free(buf);
+ if (!TEST_CHECK(w_size == size)) {
+ TEST_MSG("failed to send, errno=%d", errno);
+ flb_socket_close(fd);
+ exit(EXIT_FAILURE);
+ }
+
+ /* waiting to flush */
+ flb_time_msleep(1500);
+
+ num = get_output_num();
+ if (!TEST_CHECK(num > 0)) {
+ TEST_MSG("no outputs");
+ }
+
+ flb_socket_close(fd);
+ test_ctx_destroy(ctx);
+}
+
+void flb_test_tag_prefix()
+{
+ struct flb_lib_out_cb cb_data;
+ struct test_ctx *ctx;
+ char *tag_prefix = "tag_";
+ flb_sockfd_t fd;
+ int ret;
+ int num;
+ ssize_t w_size;
+
+ char *buf;
+ size_t size;
+
+ clear_output_num();
+
+ cb_data.cb = cb_check_result_json;
+ cb_data.data = "\"test\":\"msg\"";
+
+ ctx = test_ctx_create(&cb_data);
+ if (!TEST_CHECK(ctx != NULL)) {
+ TEST_MSG("test_ctx_create failed");
+ exit(EXIT_FAILURE);
+ }
+ ret = flb_input_set(ctx->flb, ctx->i_ffd,
+ "tag_prefix", tag_prefix,
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ ret = flb_output_set(ctx->flb, ctx->o_ffd,
+ "match", "tag_test", /*tag_prefix + "test"*/
+ "format", "json",
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ /* Start the engine */
+ ret = flb_start(ctx->flb);
+ TEST_CHECK(ret == 0);
+
+ /* use default host/port */
+ fd = connect_tcp(NULL, -1);
+ if (!TEST_CHECK(fd >= 0)) {
+ exit(EXIT_FAILURE);
+ }
+
+ create_simple_json(&buf, &size);
+ w_size = send(fd, buf, size, 0);
+ flb_free(buf);
+ if (!TEST_CHECK(w_size == size)) {
+ TEST_MSG("failed to send, errno=%d", errno);
+ flb_socket_close(fd);
+ exit(EXIT_FAILURE);
+ }
+
+ /* waiting to flush */
+ flb_time_msleep(1500);
+
+ num = get_output_num();
+ if (!TEST_CHECK(num > 0)) {
+ TEST_MSG("no outputs");
+ }
+
+ flb_socket_close(fd);
+ test_ctx_destroy(ctx);
+}
+
+#ifdef FLB_HAVE_UNIX_SOCKET
+void flb_test_unix_path()
+{
+ struct flb_lib_out_cb cb_data;
+ struct test_ctx *ctx;
+ struct sockaddr_un sun;
+ flb_sockfd_t fd;
+ int ret;
+ int num;
+ ssize_t w_size;
+ char *unix_path = "in_forward_unix";
+
+ char *buf;
+ size_t size;
+
+ clear_output_num();
+
+ cb_data.cb = cb_check_result_json;
+ cb_data.data = "\"test\":\"msg\"";
+
+ ctx = test_ctx_create(&cb_data);
+ if (!TEST_CHECK(ctx != NULL)) {
+ TEST_MSG("test_ctx_create failed");
+ exit(EXIT_FAILURE);
+ }
+
+ ret = flb_input_set(ctx->flb, ctx->i_ffd,
+ "unix_path", unix_path,
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ ret = flb_output_set(ctx->flb, ctx->o_ffd,
+ "match", "test",
+ "format", "json",
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ /* Start the engine */
+ ret = flb_start(ctx->flb);
+ TEST_CHECK(ret == 0);
+
+ /* waiting to create socket */
+ flb_time_msleep(200);
+
+ memset(&sun, 0, sizeof(sun));
+ fd = socket(AF_LOCAL, SOCK_STREAM, 0);
+ if (!TEST_CHECK(fd >= 0)) {
+ TEST_MSG("failed to socket %s, errno=%d", unix_path, errno);
+ unlink(unix_path);
+ exit(EXIT_FAILURE);
+ }
+
+ sun.sun_family = AF_LOCAL;
+ strcpy(sun.sun_path, unix_path);
+ ret = connect(fd, (const struct sockaddr *)&sun, sizeof(sun));
+ if (!TEST_CHECK(ret >= 0)) {
+ TEST_MSG("failed to connect, errno=%d", errno);
+ flb_socket_close(fd);
+ unlink(unix_path);
+ exit(EXIT_FAILURE);
+ }
+ create_simple_json(&buf, &size);
+ w_size = send(fd, buf, size, 0);
+ flb_free(buf);
+ if (!TEST_CHECK(w_size == size)) {
+ TEST_MSG("failed to write to %s", unix_path);
+ flb_socket_close(fd);
+ unlink(unix_path);
+ exit(EXIT_FAILURE);
+ }
+
+ /* waiting to flush */
+ flb_time_msleep(1500);
+
+ num = get_output_num();
+ if (!TEST_CHECK(num > 0)) {
+ TEST_MSG("no outputs");
+ }
+
+ flb_socket_close(fd);
+ test_ctx_destroy(ctx);
+}
+
+
+void flb_test_unix_perm()
+{
+ struct flb_lib_out_cb cb_data;
+ struct test_ctx *ctx;
+ struct sockaddr_un sun;
+ flb_sockfd_t fd;
+ int ret;
+ int num;
+ ssize_t w_size;
+ char *unix_path = "in_forward_unix";
+ struct stat sb;
+
+ char *buf;
+ size_t size;
+
+ clear_output_num();
+
+ cb_data.cb = cb_check_result_json;
+ cb_data.data = "\"test\":\"msg\"";
+
+ ctx = test_ctx_create(&cb_data);
+ if (!TEST_CHECK(ctx != NULL)) {
+ TEST_MSG("test_ctx_create failed");
+ exit(EXIT_FAILURE);
+ }
+
+ ret = flb_input_set(ctx->flb, ctx->i_ffd,
+ "unix_path", unix_path,
+ "unix_perm", "0600",
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ ret = flb_output_set(ctx->flb, ctx->o_ffd,
+ "match", "test",
+ "format", "json",
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ /* Start the engine */
+ ret = flb_start(ctx->flb);
+ TEST_CHECK(ret == 0);
+
+ /* waiting to create socket */
+ flb_time_msleep(200);
+
+ memset(&sun, 0, sizeof(sun));
+ fd = socket(AF_LOCAL, SOCK_STREAM, 0);
+ if (!TEST_CHECK(fd >= 0)) {
+ TEST_MSG("failed to socket %s, errno=%d", unix_path, errno);
+ unlink(unix_path);
+ exit(EXIT_FAILURE);
+ }
+
+ sun.sun_family = AF_LOCAL;
+ strcpy(sun.sun_path, unix_path);
+ ret = connect(fd, (const struct sockaddr *)&sun, sizeof(sun));
+ if (!TEST_CHECK(ret >= 0)) {
+ TEST_MSG("failed to connect, errno=%d", errno);
+ flb_socket_close(fd);
+ unlink(unix_path);
+ exit(EXIT_FAILURE);
+ }
+ create_simple_json(&buf, &size);
+ w_size = send(fd, buf, size, 0);
+ flb_free(buf);
+ if (!TEST_CHECK(w_size == size)) {
+ TEST_MSG("failed to write to %s", unix_path);
+ flb_socket_close(fd);
+ unlink(unix_path);
+ exit(EXIT_FAILURE);
+ }
+
+ /* waiting to flush */
+ flb_time_msleep(1500);
+
+ num = get_output_num();
+ if (!TEST_CHECK(num > 0)) {
+ TEST_MSG("no outputs");
+ }
+
+
+ /* File permission */
+ ret = stat(unix_path, &sb);
+ if (!TEST_CHECK(ret == 0)) {
+ TEST_MSG("stat failed. errno=%d", errno);
+ test_ctx_destroy(ctx);
+ exit(EXIT_FAILURE);
+ }
+
+ if (!TEST_CHECK((sb.st_mode & S_IRWXO) == 0)) {
+ TEST_MSG("Permssion(others) error. val=0x%x",sb.st_mode & S_IRWXO);
+ }
+ if (!TEST_CHECK((sb.st_mode & S_IRWXG) == 0)) {
+ TEST_MSG("Permssion(group) error. val=0x%x",sb.st_mode & S_IRWXG);
+ }
+ if (!TEST_CHECK((sb.st_mode & S_IRWXU) == (S_IRUSR | S_IWUSR))) {
+ TEST_MSG("Permssion(user) error. val=0x%x",sb.st_mode & S_IRWXU);
+ }
+
+ flb_socket_close(fd);
+ test_ctx_destroy(ctx);
+}
+#endif /* FLB_HAVE_UNIX_SOCKET */
+
+
+TEST_LIST = {
+ {"forward", flb_test_forward},
+ {"forward_port", flb_test_forward_port},
+ {"tag_prefix", flb_test_tag_prefix},
+#ifdef FLB_HAVE_UNIX_SOCKET
+ {"unix_path", flb_test_unix_path},
+ {"unix_perm", flb_test_unix_perm},
+#endif
+ {NULL, NULL}
+};
+
diff --git a/fluent-bit/tests/runtime/in_head.c b/fluent-bit/tests/runtime/in_head.c
new file mode 120000
index 00000000..b194a853
--- /dev/null
+++ b/fluent-bit/tests/runtime/in_head.c
@@ -0,0 +1 @@
+in_simple_systems.c \ No newline at end of file
diff --git a/fluent-bit/tests/runtime/in_http.c b/fluent-bit/tests/runtime/in_http.c
new file mode 100644
index 00000000..700e581f
--- /dev/null
+++ b/fluent-bit/tests/runtime/in_http.c
@@ -0,0 +1,444 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/* Fluent Bit
+ * ==========
+ * Copyright (C) 2019-2022 The Fluent Bit Authors
+ * Copyright (C) 2015-2018 Treasure Data Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdlib.h>
+#include <fluent-bit.h>
+#include <fluent-bit/flb_compat.h>
+#include <fluent-bit/flb_time.h>
+#include <fluent-bit/flb_pack.h>
+#include <fluent-bit/flb_http_client.h>
+#include <monkey/mk_core.h>
+#include "flb_tests_runtime.h"
+
+#define JSON_CONTENT_TYPE "application/json"
+
+struct http_client_ctx {
+ struct flb_upstream *u;
+ struct flb_connection *u_conn;
+ struct flb_config *config;
+ struct mk_event_loop *evl;
+};
+
+struct test_ctx {
+ flb_ctx_t *flb; /* Fluent Bit library context */
+ int i_ffd; /* Input fd */
+ int f_ffd; /* Filter fd (unused) */
+ int o_ffd; /* Output fd */
+ struct http_client_ctx *httpc;
+};
+
+
+pthread_mutex_t result_mutex = PTHREAD_MUTEX_INITIALIZER;
+int num_output = 0;
+static int get_output_num()
+{
+ int ret;
+ pthread_mutex_lock(&result_mutex);
+ ret = num_output;
+ pthread_mutex_unlock(&result_mutex);
+
+ return ret;
+}
+
+static void set_output_num(int num)
+{
+ pthread_mutex_lock(&result_mutex);
+ num_output = num;
+ pthread_mutex_unlock(&result_mutex);
+}
+
+static void clear_output_num()
+{
+ set_output_num(0);
+}
+
+/* Callback to check expected results */
+static int cb_check_result_json(void *record, size_t size, void *data)
+{
+ char *p;
+ char *expected;
+ char *result;
+ int num = get_output_num();
+
+ set_output_num(num+1);
+
+ expected = (char *) data;
+ result = (char *) record;
+
+ p = strstr(result, expected);
+ TEST_CHECK(p != NULL);
+
+ if (p==NULL) {
+ flb_error("Expected to find: '%s' in result '%s'",
+ expected, result);
+ }
+ /*
+ * If you want to debug your test
+ *
+ * printf("Expect: '%s' in result '%s'", expected, result);
+ */
+ flb_free(record);
+ return 0;
+}
+
+struct http_client_ctx* http_client_ctx_create()
+{
+ struct http_client_ctx *ret_ctx = NULL;
+ struct mk_event_loop *evl = NULL;
+
+ ret_ctx = flb_calloc(1, sizeof(struct http_client_ctx));
+ if (!TEST_CHECK(ret_ctx != NULL)) {
+ flb_errno();
+ TEST_MSG("flb_calloc(http_client_ctx) failed");
+ return NULL;
+ }
+
+ evl = mk_event_loop_create(16);
+ if (!TEST_CHECK(evl != NULL)) {
+ TEST_MSG("mk_event_loop failed");
+ flb_free(ret_ctx);
+ return NULL;
+ }
+ ret_ctx->evl = evl;
+ flb_engine_evl_init();
+ flb_engine_evl_set(evl);
+
+ ret_ctx->config = flb_config_init();
+ if(!TEST_CHECK(ret_ctx->config != NULL)) {
+ TEST_MSG("flb_config_init failed");
+ mk_event_loop_destroy(evl);
+ flb_free(ret_ctx);
+ return NULL;
+ }
+
+ ret_ctx->u = flb_upstream_create(ret_ctx->config, "127.0.0.1", 9880, 0, NULL);
+ if (!TEST_CHECK(ret_ctx->u != NULL)) {
+ TEST_MSG("flb_upstream_create failed");
+ flb_config_exit(ret_ctx->config);
+ mk_event_loop_destroy(evl);
+ flb_free(ret_ctx);
+ return NULL;
+ }
+
+ ret_ctx->u_conn = flb_upstream_conn_get(ret_ctx->u);
+ TEST_CHECK(ret_ctx->u_conn != NULL);
+
+ ret_ctx->u_conn->upstream = ret_ctx->u;
+
+ return ret_ctx;
+}
+
+static struct test_ctx *test_ctx_create(struct flb_lib_out_cb *data)
+{
+ int i_ffd;
+ int o_ffd;
+ struct test_ctx *ctx = NULL;
+
+ ctx = flb_calloc(1, sizeof(struct test_ctx));
+ if (!TEST_CHECK(ctx != NULL)) {
+ TEST_MSG("flb_calloc failed");
+ flb_errno();
+ return NULL;
+ }
+
+ /* Service config */
+ ctx->flb = flb_create();
+ flb_service_set(ctx->flb,
+ "Flush", "0.200000000",
+ "Grace", "1",
+ "Log_Level", "error",
+ NULL);
+
+ /* Input */
+ i_ffd = flb_input(ctx->flb, (char *) "http", NULL);
+ TEST_CHECK(i_ffd >= 0);
+ ctx->i_ffd = i_ffd;
+
+ /* Output */
+ o_ffd = flb_output(ctx->flb, (char *) "lib", (void *) data);
+ ctx->o_ffd = o_ffd;
+
+ return ctx;
+}
+
+int http_client_ctx_destroy(struct http_client_ctx* ctx)
+{
+ if (!TEST_CHECK(ctx != NULL)) {
+ return -1;
+ }
+ if (ctx->u) {
+ flb_upstream_destroy(ctx->u);
+ }
+ if (ctx->config) {
+ flb_config_exit(ctx->config);
+ }
+ if (ctx->evl) {
+ mk_event_loop_destroy(ctx->evl);
+ }
+
+ flb_free(ctx);
+ return 0;
+}
+
+static void test_ctx_destroy(struct test_ctx *ctx)
+{
+ TEST_CHECK(ctx != NULL);
+ if (ctx->httpc) {
+ http_client_ctx_destroy(ctx->httpc);
+ }
+
+ sleep(1);
+ flb_stop(ctx->flb);
+ flb_destroy(ctx->flb);
+ flb_free(ctx);
+}
+
+void flb_test_http()
+{
+ struct flb_lib_out_cb cb_data;
+ struct test_ctx *ctx;
+ struct flb_http_client *c;
+ int ret;
+ int num;
+ size_t b_sent;
+
+ char *buf = "{\"test\":\"msg\"}";
+
+ clear_output_num();
+
+ cb_data.cb = cb_check_result_json;
+ cb_data.data = "\"test\":\"msg\"";
+
+ ctx = test_ctx_create(&cb_data);
+ if (!TEST_CHECK(ctx != NULL)) {
+ TEST_MSG("test_ctx_create failed");
+ exit(EXIT_FAILURE);
+ }
+
+ ret = flb_output_set(ctx->flb, ctx->o_ffd,
+ "match", "*",
+ "format", "json",
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ /* Start the engine */
+ ret = flb_start(ctx->flb);
+ TEST_CHECK(ret == 0);
+
+ ctx->httpc = http_client_ctx_create();
+ TEST_CHECK(ctx->httpc != NULL);
+
+ c = flb_http_client(ctx->httpc->u_conn, FLB_HTTP_POST, "/", buf, strlen(buf),
+ "127.0.0.1", 9880, NULL, 0);
+ ret = flb_http_add_header(c, FLB_HTTP_HEADER_CONTENT_TYPE, strlen(FLB_HTTP_HEADER_CONTENT_TYPE),
+ JSON_CONTENT_TYPE, strlen(JSON_CONTENT_TYPE));
+ TEST_CHECK(ret == 0);
+ if (!TEST_CHECK(c != NULL)) {
+ TEST_MSG("http_client failed");
+ exit(EXIT_FAILURE);
+ }
+
+ ret = flb_http_do(c, &b_sent);
+ if (!TEST_CHECK(ret == 0)) {
+ TEST_MSG("ret error. ret=%d\n", ret);
+ }
+ else if (!TEST_CHECK(b_sent > 0)){
+ TEST_MSG("b_sent size error. b_sent = %lu\n", b_sent);
+ }
+ else if (!TEST_CHECK(c->resp.status == 201)) {
+ TEST_MSG("http response code error. expect: 201, got: %d\n", c->resp.status);
+ }
+
+ /* waiting to flush */
+ flb_time_msleep(1500);
+
+ num = get_output_num();
+ if (!TEST_CHECK(num > 0)) {
+ TEST_MSG("no outputs");
+ }
+ flb_http_client_destroy(c);
+ flb_upstream_conn_release(ctx->httpc->u_conn);
+ test_ctx_destroy(ctx);
+}
+
+void flb_test_http_successful_response_code(char *response_code)
+{
+ struct flb_lib_out_cb cb_data;
+ struct test_ctx *ctx;
+ struct flb_http_client *c;
+ int ret;
+ int num;
+ size_t b_sent;
+
+ char *buf = "{\"test\":\"msg\"}";
+
+ clear_output_num();
+
+ cb_data.cb = cb_check_result_json;
+ cb_data.data = "\"test\":\"msg\"";
+
+ ctx = test_ctx_create(&cb_data);
+ if (!TEST_CHECK(ctx != NULL)) {
+ TEST_MSG("test_ctx_create failed");
+ exit(EXIT_FAILURE);
+ }
+
+ ret = flb_input_set(ctx->flb, ctx->i_ffd,
+ "successful_response_code", response_code,
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ ret = flb_output_set(ctx->flb, ctx->o_ffd,
+ "match", "*",
+ "format", "json",
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ /* Start the engine */
+ ret = flb_start(ctx->flb);
+ TEST_CHECK(ret == 0);
+
+ ctx->httpc = http_client_ctx_create();
+ TEST_CHECK(ctx->httpc != NULL);
+
+ c = flb_http_client(ctx->httpc->u_conn, FLB_HTTP_POST, "/", buf, strlen(buf),
+ "127.0.0.1", 9880, NULL, 0);
+ ret = flb_http_add_header(c, FLB_HTTP_HEADER_CONTENT_TYPE, strlen(FLB_HTTP_HEADER_CONTENT_TYPE),
+ JSON_CONTENT_TYPE, strlen(JSON_CONTENT_TYPE));
+ TEST_CHECK(ret == 0);
+ if (!TEST_CHECK(c != NULL)) {
+ TEST_MSG("http_client failed");
+ exit(EXIT_FAILURE);
+ }
+
+ ret = flb_http_do(c, &b_sent);
+ if (!TEST_CHECK(ret == 0)) {
+ TEST_MSG("ret error. ret=%d\n", ret);
+ }
+ else if (!TEST_CHECK(b_sent > 0)){
+ TEST_MSG("b_sent size error. b_sent = %lu\n", b_sent);
+ }
+ else if (!TEST_CHECK(c->resp.status == atoi(response_code))) {
+ TEST_MSG("http response code error. expect: %d, got: %d\n", atoi(response_code), c->resp.status);
+ }
+
+ /* waiting to flush */
+ flb_time_msleep(1500);
+
+ num = get_output_num();
+ if (!TEST_CHECK(num > 0)) {
+ TEST_MSG("no outputs");
+ }
+ flb_http_client_destroy(c);
+ flb_upstream_conn_release(ctx->httpc->u_conn);
+ test_ctx_destroy(ctx);
+}
+
+void flb_test_http_successful_response_code_200()
+{
+ flb_test_http_successful_response_code("200");
+}
+
+void flb_test_http_successful_response_code_204()
+{
+ flb_test_http_successful_response_code("204");
+}
+
+void flb_test_http_tag_key()
+{
+ struct flb_lib_out_cb cb_data;
+ struct test_ctx *ctx;
+ struct flb_http_client *c;
+ int ret;
+ int num;
+ size_t b_sent;
+
+ char *buf = "{\"test\":\"msg\", \"tag\":\"new_tag\"}";
+
+ clear_output_num();
+
+ cb_data.cb = cb_check_result_json;
+ cb_data.data = "\"test\":\"msg\"";
+
+ ctx = test_ctx_create(&cb_data);
+ if (!TEST_CHECK(ctx != NULL)) {
+ TEST_MSG("test_ctx_create failed");
+ exit(EXIT_FAILURE);
+ }
+
+ ret = flb_input_set(ctx->flb, ctx->i_ffd,
+ "tag_key", "tag",
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ ret = flb_output_set(ctx->flb, ctx->o_ffd,
+ "match", "new_tag",
+ "format", "json",
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ /* Start the engine */
+ ret = flb_start(ctx->flb);
+ TEST_CHECK(ret == 0);
+
+ ctx->httpc = http_client_ctx_create();
+ TEST_CHECK(ctx->httpc != NULL);
+
+ c = flb_http_client(ctx->httpc->u_conn, FLB_HTTP_POST, "/", buf, strlen(buf),
+ "127.0.0.1", 9880, NULL, 0);
+ ret = flb_http_add_header(c, FLB_HTTP_HEADER_CONTENT_TYPE, strlen(FLB_HTTP_HEADER_CONTENT_TYPE),
+ JSON_CONTENT_TYPE, strlen(JSON_CONTENT_TYPE));
+ TEST_CHECK(ret == 0);
+ if (!TEST_CHECK(c != NULL)) {
+ TEST_MSG("http_client failed");
+ exit(EXIT_FAILURE);
+ }
+
+ ret = flb_http_do(c, &b_sent);
+ if (!TEST_CHECK(ret == 0)) {
+ TEST_MSG("ret error. ret=%d\n", ret);
+ }
+ else if (!TEST_CHECK(b_sent > 0)){
+ TEST_MSG("b_sent size error. b_sent = %lu\n", b_sent);
+ }
+ else if (!TEST_CHECK(c->resp.status == 201)) {
+ TEST_MSG("http response code error. expect: 201, got: %d\n", c->resp.status);
+ }
+
+ /* waiting to flush */
+ flb_time_msleep(1500);
+
+ num = get_output_num();
+ if (!TEST_CHECK(num > 0)) {
+ TEST_MSG("no outputs");
+ }
+ flb_http_client_destroy(c);
+ flb_upstream_conn_release(ctx->httpc->u_conn);
+ test_ctx_destroy(ctx);
+}
+
+TEST_LIST = {
+ {"http", flb_test_http},
+ {"successful_response_code_200", flb_test_http_successful_response_code_200},
+ {"successful_response_code_204", flb_test_http_successful_response_code_204},
+ {"tag_key", flb_test_http_tag_key},
+ {NULL, NULL}
+};
+
diff --git a/fluent-bit/tests/runtime/in_mem.c b/fluent-bit/tests/runtime/in_mem.c
new file mode 120000
index 00000000..b194a853
--- /dev/null
+++ b/fluent-bit/tests/runtime/in_mem.c
@@ -0,0 +1 @@
+in_simple_systems.c \ No newline at end of file
diff --git a/fluent-bit/tests/runtime/in_mqtt.c b/fluent-bit/tests/runtime/in_mqtt.c
new file mode 100644
index 00000000..0e38d0c5
--- /dev/null
+++ b/fluent-bit/tests/runtime/in_mqtt.c
@@ -0,0 +1,396 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/* Fluent Bit
+ * ==========
+ * Copyright (C) 2019-2023 The Fluent Bit Authors
+ * Copyright (C) 2015-2018 Treasure Data Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <fluent-bit.h>
+#include <fluent-bit/flb_compat.h>
+#include <fluent-bit/flb_time.h>
+#include <fluent-bit/flb_socket.h>
+#include "flb_tests_runtime.h"
+
+struct test_ctx {
+ flb_ctx_t *flb; /* Fluent Bit library context */
+ int i_ffd; /* Input fd */
+ int f_ffd; /* Filter fd (unused) */
+ int o_ffd; /* Output fd */
+};
+
+pthread_mutex_t result_mutex = PTHREAD_MUTEX_INITIALIZER;
+int num_output = 0;
+static int get_output_num()
+{
+ int ret;
+ pthread_mutex_lock(&result_mutex);
+ ret = num_output;
+ pthread_mutex_unlock(&result_mutex);
+
+ return ret;
+}
+
+static void set_output_num(int num)
+{
+ pthread_mutex_lock(&result_mutex);
+ num_output = num;
+ pthread_mutex_unlock(&result_mutex);
+}
+
+static void clear_output_num()
+{
+ set_output_num(0);
+}
+
+struct str_list {
+ size_t size;
+ char **lists;
+};
+
+/* Callback to check expected results */
+static int cb_check_json_str_list(void *record, size_t size, void *data)
+{
+ char *p;
+ char *result;
+ int num = get_output_num();
+ size_t i;
+ struct str_list *l = (struct str_list*)data;
+
+ if (!TEST_CHECK(l != NULL)) {
+ flb_error("Data is NULL");
+ flb_free(record);
+ return 0;
+ }
+ set_output_num(num+1);
+
+ result = (char *) record;
+
+ for (i=0; i<l->size; i++) {
+ p = strstr(result, l->lists[i]);
+ if(!TEST_CHECK(p != NULL)) {
+ flb_error("Expected to find: '%s' in result '%s'",
+ l->lists[i], result);
+ }
+ }
+ flb_free(record);
+ return 0;
+}
+
+static struct test_ctx *test_ctx_create(struct flb_lib_out_cb *data)
+{
+ int i_ffd;
+ int o_ffd;
+ int ret;
+ struct test_ctx *ctx = NULL;
+
+ ctx = flb_malloc(sizeof(struct test_ctx));
+ if (!TEST_CHECK(ctx != NULL)) {
+ TEST_MSG("malloc failed");
+ flb_errno();
+ return NULL;
+ }
+
+ /* Service config */
+ ctx->flb = flb_create();
+ flb_service_set(ctx->flb,
+ "Flush", "0.200000000",
+ "Grace", "1",
+ "Log_Level", "error",
+ NULL);
+
+ /* Input */
+ i_ffd = flb_input(ctx->flb, (char *) "mqtt", NULL);
+ TEST_CHECK(i_ffd >= 0);
+ ctx->i_ffd = i_ffd;
+
+ /* Output */
+ o_ffd = flb_output(ctx->flb, (char *) "lib", (void *) data);
+ ctx->o_ffd = o_ffd;
+ ret = flb_output_set(ctx->flb, ctx->o_ffd,
+ "format", "json",
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ return ctx;
+}
+
+static void test_ctx_destroy(struct test_ctx *ctx)
+{
+ TEST_CHECK(ctx != NULL);
+
+ sleep(1);
+ flb_stop(ctx->flb);
+ flb_destroy(ctx->flb);
+ flb_free(ctx);
+}
+
+#define DEFAULT_HOST "127.0.0.1"
+#define DEFAULT_PORT 1883
+static flb_sockfd_t connect_tcp(char *in_host, int in_port)
+{
+ int port = in_port;
+ char *host = in_host;
+ flb_sockfd_t fd;
+ int ret;
+ struct sockaddr_in addr;
+
+ if (host == NULL) {
+ host = DEFAULT_HOST;
+ }
+ if (port < 0) {
+ port = DEFAULT_PORT;
+ }
+
+ memset(&addr, 0, sizeof(addr));
+ fd = socket(PF_INET, SOCK_STREAM, 0);
+ if (!TEST_CHECK(fd >= 0)) {
+ TEST_MSG("failed to socket. host=%s port=%d errno=%d", host, port, errno);
+ return -1;
+ }
+
+ addr.sin_family = AF_INET;
+ addr.sin_addr.s_addr = inet_addr(host);
+ addr.sin_port = htons(port);
+
+ ret = connect(fd, (const struct sockaddr *)&addr, sizeof(addr));
+ if (!TEST_CHECK(ret >= 0)) {
+ TEST_MSG("failed to connect. host=%s port=%d errno=%d", host, port, errno);
+ flb_socket_close(fd);
+ return -1;
+ }
+ return fd;
+}
+
+/* http://docs.oasis-open.org/mqtt/mqtt/v3.1.1/os/mqtt-v3.1.1-os.html#_Toc398718028 */
+int send_CONNECT(flb_sockfd_t fd)
+{
+ ssize_t w_size;
+ char buf[] = {0x10, 0xa,
+ 0x0,0x4, 'M', 'Q', 'T', 'T', 0x4, 0xce, 0x0, 0xa
+ };
+
+ w_size = send(fd, buf, sizeof(buf), 0);
+ if (!TEST_CHECK(w_size == sizeof(buf))) {
+ TEST_MSG("failed to send CONNECT, errno=%d", errno);
+ return -1;
+ }
+ return 0;
+}
+
+/* http://docs.oasis-open.org/mqtt/mqtt/v3.1.1/os/mqtt-v3.1.1-os.html#_Toc398718033 */
+int recv_CONNACK(flb_sockfd_t fd)
+{
+ ssize_t r_size;
+ char buf[1024] = {0};
+
+ r_size = recv(fd, &buf[0], sizeof(buf), 0);
+ if (!TEST_CHECK(r_size != -1)) {
+ TEST_MSG("failed to recv CONNACK, errno=%d", errno);
+ return -1;
+ }
+ return 0;
+}
+
+/* http://docs.oasis-open.org/mqtt/mqtt/v3.1.1/os/mqtt-v3.1.1-os.html#_Toc398718037 */
+int send_PUBLISH(flb_sockfd_t fd, const char *payload, size_t payload_size)
+{
+ ssize_t w_size;
+ char buf[1024] = {0};
+
+ buf[0] = 0x30;
+
+ buf[2] = 0x0;
+ buf[3] = 0x3;
+ buf[4] = 'a';
+ buf[5] = '/';
+ buf[6] = 'b';
+ /* No packet id since QoS Level is 0 */
+ buf[1] = 5 + (char)payload_size;
+
+ if (!TEST_CHECK(sizeof(buf) >= buf[1])) {
+ TEST_MSG("payload is too long");
+ return -1;
+ }
+
+ strncpy(&buf[7], payload, payload_size);
+
+ w_size = send(fd, buf, sizeof(buf), 0);
+ if (!TEST_CHECK(w_size == sizeof(buf))) {
+ TEST_MSG("failed to send PUBLISH, errno=%d", errno);
+ return -1;
+ }
+ return 0;
+}
+
+void flb_test_mqtt()
+{
+ struct flb_lib_out_cb cb_data;
+ struct test_ctx *ctx;
+ flb_sockfd_t fd;
+ int ret;
+ int num;
+
+ char *expected_strs[] = {"\"key\":\"val\""};
+ struct str_list expected = {
+ .size = sizeof(expected_strs)/sizeof(char*),
+ .lists = &expected_strs[0],
+ };
+ const char *payload = "{\"key\":\"val\"}";
+ size_t payload_size = strlen(payload);
+
+ clear_output_num();
+
+ cb_data.cb = cb_check_json_str_list;
+ cb_data.data = &expected;
+
+ ctx = test_ctx_create(&cb_data);
+ if (!TEST_CHECK(ctx != NULL)) {
+ TEST_MSG("test_ctx_create failed");
+ exit(EXIT_FAILURE);
+ }
+
+ /* Start the engine */
+ ret = flb_start(ctx->flb);
+ TEST_CHECK(ret == 0);
+
+ /* use default host/port */
+ fd = connect_tcp(NULL, -1);
+ if (!TEST_CHECK(fd >= 0)) {
+ test_ctx_destroy(ctx);
+ exit(EXIT_FAILURE);
+ }
+
+ ret = send_CONNECT(fd);
+ if (!TEST_CHECK(ret == 0)) {
+ TEST_MSG("failed to send, errno=%d", errno);
+ flb_socket_close(fd);
+ test_ctx_destroy(ctx);
+ exit(EXIT_FAILURE);
+ }
+
+ ret = recv_CONNACK(fd);
+ if (!TEST_CHECK(ret == 0)) {
+ TEST_MSG("failed to recv, errno=%d", errno);
+ flb_socket_close(fd);
+ test_ctx_destroy(ctx);
+ exit(EXIT_FAILURE);
+ }
+
+ ret = send_PUBLISH(fd, payload, payload_size);
+ if (!TEST_CHECK(ret == 0)) {
+ TEST_MSG("failed to send, errno=%d", errno);
+ flb_socket_close(fd);
+ test_ctx_destroy(ctx);
+ exit(EXIT_FAILURE);
+ }
+
+ /* waiting to flush */
+ flb_time_msleep(500);
+
+ num = get_output_num();
+ if (!TEST_CHECK(num > 0)) {
+ TEST_MSG("no outputs");
+ }
+
+ flb_socket_close(fd);
+ test_ctx_destroy(ctx);
+}
+
+void flb_test_payload_key()
+{
+ struct flb_lib_out_cb cb_data;
+ struct test_ctx *ctx;
+ flb_sockfd_t fd;
+ int ret;
+ int num;
+
+ char *expected_strs[] = {"\"payload_k\":{\"key\":\"val\"}"};
+ struct str_list expected = {
+ .size = sizeof(expected_strs)/sizeof(char*),
+ .lists = &expected_strs[0],
+ };
+ const char *payload = "{\"key\":\"val\"}";
+ size_t payload_size = strlen(payload);
+
+ clear_output_num();
+
+ cb_data.cb = cb_check_json_str_list;
+ cb_data.data = &expected;
+
+ ctx = test_ctx_create(&cb_data);
+ if (!TEST_CHECK(ctx != NULL)) {
+ TEST_MSG("test_ctx_create failed");
+ exit(EXIT_FAILURE);
+ }
+
+ ret = flb_input_set(ctx->flb, ctx->i_ffd,
+ "payload_key", "payload_k",
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ /* Start the engine */
+ ret = flb_start(ctx->flb);
+ TEST_CHECK(ret == 0);
+
+ /* use default host/port */
+ fd = connect_tcp(NULL, -1);
+ if (!TEST_CHECK(fd >= 0)) {
+ test_ctx_destroy(ctx);
+ exit(EXIT_FAILURE);
+ }
+
+ ret = send_CONNECT(fd);
+ if (!TEST_CHECK(ret == 0)) {
+ TEST_MSG("failed to send, errno=%d", errno);
+ flb_socket_close(fd);
+ test_ctx_destroy(ctx);
+ exit(EXIT_FAILURE);
+ }
+
+ ret = recv_CONNACK(fd);
+ if (!TEST_CHECK(ret == 0)) {
+ TEST_MSG("failed to recv, errno=%d", errno);
+ flb_socket_close(fd);
+ test_ctx_destroy(ctx);
+ exit(EXIT_FAILURE);
+ }
+
+ ret = send_PUBLISH(fd, payload, payload_size);
+ if (!TEST_CHECK(ret == 0)) {
+ TEST_MSG("failed to send, errno=%d", errno);
+ flb_socket_close(fd);
+ test_ctx_destroy(ctx);
+ exit(EXIT_FAILURE);
+ }
+
+ /* waiting to flush */
+ flb_time_msleep(500);
+
+ num = get_output_num();
+ if (!TEST_CHECK(num > 0)) {
+ TEST_MSG("no outputs");
+ }
+
+ flb_socket_close(fd);
+ test_ctx_destroy(ctx);
+}
+
+TEST_LIST = {
+ {"mqtt", flb_test_mqtt},
+ {"payload_key", flb_test_payload_key},
+ {NULL, NULL}
+};
+
diff --git a/fluent-bit/tests/runtime/in_netif.c b/fluent-bit/tests/runtime/in_netif.c
new file mode 100644
index 00000000..ff66d9e1
--- /dev/null
+++ b/fluent-bit/tests/runtime/in_netif.c
@@ -0,0 +1,350 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/* Fluent Bit
+ * ==========
+ * Copyright (C) 2019-2022 The Fluent Bit Authors
+ * Copyright (C) 2015-2018 Treasure Data Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <fluent-bit.h>
+#include <fluent-bit/flb_sds.h>
+#include <fluent-bit/flb_time.h>
+#include <sys/types.h>
+#include <ifaddrs.h>
+#include <errno.h>
+#include "flb_tests_runtime.h"
+
+struct test_ctx {
+ flb_ctx_t *flb; /* Fluent Bit library context */
+ int i_ffd; /* Input fd */
+ int f_ffd; /* Filter fd (unused) */
+ int o_ffd; /* Output fd */
+};
+
+pthread_mutex_t result_mutex = PTHREAD_MUTEX_INITIALIZER;
+int num_output = 0;
+
+static struct test_ctx *test_ctx_create(struct flb_lib_out_cb *data)
+{
+ int i_ffd;
+ int o_ffd;
+ struct test_ctx *ctx = NULL;
+
+ ctx = flb_malloc(sizeof(struct test_ctx));
+ if (!TEST_CHECK(ctx != NULL)) {
+ TEST_MSG("malloc failed");
+ flb_errno();
+ return NULL;
+ }
+
+ /* Service config */
+ ctx->flb = flb_create();
+ flb_service_set(ctx->flb,
+ "Flush", "0.200000000",
+ "Grace", "1",
+ "Log_Level", "error",
+ NULL);
+
+ /* Input */
+ i_ffd = flb_input(ctx->flb, (char *) "netif", NULL);
+ TEST_CHECK(i_ffd >= 0);
+ ctx->i_ffd = i_ffd;
+
+ /* Output */
+ o_ffd = flb_output(ctx->flb, (char *) "lib", (void *) data);
+ ctx->o_ffd = o_ffd;
+
+ return ctx;
+}
+
+static void test_ctx_destroy(struct test_ctx *ctx)
+{
+ TEST_CHECK(ctx != NULL);
+
+ sleep(1);
+ flb_stop(ctx->flb);
+ flb_destroy(ctx->flb);
+ flb_free(ctx);
+}
+
+static void clear_output_num()
+{
+ pthread_mutex_lock(&result_mutex);
+ num_output = 0;
+ pthread_mutex_unlock(&result_mutex);
+}
+
+static int get_output_num()
+{
+ int ret;
+ pthread_mutex_lock(&result_mutex);
+ ret = num_output;
+ pthread_mutex_unlock(&result_mutex);
+
+ return ret;
+}
+
+static flb_sds_t get_interface_str()
+{
+ struct ifaddrs *ifap = NULL;
+ flb_sds_t ret_str;
+ int ret = 0;
+
+ ret = getifaddrs(&ifap);
+ if (!TEST_CHECK(ret == 0)) {
+ TEST_MSG("getifaddrs failed errno=%d", errno);
+ return NULL;
+ }
+
+ if (!TEST_CHECK(ifap != NULL)) {
+ TEST_MSG("failed to get ifaddrs");
+ return NULL;
+ }
+ ret_str = flb_sds_create(ifap->ifa_name);
+ freeifaddrs(ifap);
+
+ /* printf("ret:%s\n", ret_str); */
+
+ return ret_str;
+}
+
+static int cb_count_msgpack_map_size(void *record, size_t size, void *data)
+{
+ msgpack_unpacked result;
+ msgpack_object *obj;
+ struct flb_time tm;
+ size_t off = 0;
+ int map_size;
+
+ if (!TEST_CHECK(data != NULL)) {
+ flb_error("data is NULL");
+ }
+
+ /* Iterate each item array and apply rules */
+ msgpack_unpacked_init(&result);
+ while (msgpack_unpack_next(&result, record, size, &off) == MSGPACK_UNPACK_SUCCESS) {
+ if (!TEST_CHECK(result.data.type == MSGPACK_OBJECT_ARRAY)) {
+ continue;
+ }
+ flb_time_pop_from_msgpack(&tm, &result, &obj);
+
+ if (!TEST_CHECK(obj->type == MSGPACK_OBJECT_MAP)) {
+ continue;
+ }
+ map_size = get_output_num();
+ if (obj->via.map.size > map_size) {
+ pthread_mutex_lock(&result_mutex);
+ num_output = obj->via.map.size;
+ pthread_mutex_unlock(&result_mutex);
+ }
+ }
+ msgpack_unpacked_destroy(&result);
+
+ flb_free(record);
+ return 0;
+}
+
+static int cb_count_msgpack_events(void *record, size_t size, void *data)
+{
+ msgpack_unpacked result;
+ size_t off = 0;
+
+ if (!TEST_CHECK(data != NULL)) {
+ flb_error("data is NULL");
+ }
+
+ /* Iterate each item array and apply rules */
+ msgpack_unpacked_init(&result);
+ while (msgpack_unpack_next(&result, record, size, &off) == MSGPACK_UNPACK_SUCCESS) {
+ pthread_mutex_lock(&result_mutex);
+ num_output++;
+ pthread_mutex_unlock(&result_mutex);
+ }
+ msgpack_unpacked_destroy(&result);
+
+ flb_free(record);
+ return 0;
+}
+
+static void flb_test_normal()
+{
+ int ret;
+ int got;
+ int unused = 0;
+ flb_sds_t ifname;
+ struct test_ctx *ctx;
+ struct flb_lib_out_cb cb_data;
+
+ cb_data.cb = cb_count_msgpack_events;
+ cb_data.data = &unused;
+
+ ctx = test_ctx_create(&cb_data);
+ if (!TEST_CHECK(ctx != NULL)) {
+ TEST_MSG("test_ctx_create failed");
+ exit(EXIT_FAILURE);
+ }
+
+ ifname = get_interface_str();
+ if (!TEST_CHECK(ifname != NULL)) {
+ TEST_MSG("can't get interface name");
+ flb_free(ctx);
+ exit(EXIT_FAILURE);
+ }
+
+ ret = flb_input_set(ctx->flb, ctx->i_ffd,
+ "interface", ifname,
+ "interval_sec", "0",
+ "interval_nsec", "500000000", /* 500 ms */
+ "test_at_init", "true",
+ NULL);
+ TEST_CHECK(ret==0);
+
+ flb_sds_destroy(ifname);
+
+ clear_output_num();
+
+ /* Start the engine */
+ ret = flb_start(ctx->flb);
+ TEST_CHECK(ret == 0);
+
+ flb_time_msleep(1500); /* waiting flush */
+ got = get_output_num();
+
+ if (!TEST_CHECK(got != 0)) {
+ TEST_MSG("expect: >=1 got: %d", got);
+ }
+
+ test_ctx_destroy(ctx);
+}
+
+static void flb_test_no_interface()
+{
+ int ret;
+ int unused = 0;
+ struct test_ctx *ctx;
+ struct flb_lib_out_cb cb_data;
+
+ cb_data.cb = cb_count_msgpack_events;
+ cb_data.data = &unused;
+
+ ctx = test_ctx_create(&cb_data);
+ if (!TEST_CHECK(ctx != NULL)) {
+ TEST_MSG("test_ctx_create failed");
+ exit(EXIT_FAILURE);
+ }
+
+ clear_output_num();
+
+ /* It should be error */
+ ret = flb_start(ctx->flb);
+ TEST_CHECK(ret != 0);
+
+ test_ctx_destroy(ctx);
+}
+
+static void flb_test_invalid_interface()
+{
+ int ret;
+ int unused = 0;
+ char *ifname = "\t\n";
+ struct test_ctx *ctx;
+ struct flb_lib_out_cb cb_data;
+
+
+ cb_data.cb = cb_count_msgpack_events;
+ cb_data.data = &unused;
+ ctx = test_ctx_create(&cb_data);
+ if (!TEST_CHECK(ctx != NULL)) {
+ TEST_MSG("test_ctx_create failed");
+ exit(EXIT_FAILURE);
+ }
+
+ ret = flb_input_set(ctx->flb, ctx->i_ffd,
+ "interface", ifname,
+ "interval_sec", "0",
+ "interval_nsec", "500000000", /* 500 ms */
+ "test_at_init", "true",
+ NULL);
+ TEST_CHECK(ret==0);
+
+ clear_output_num();
+
+ /* It should be error */
+ ret = flb_start(ctx->flb);
+ TEST_CHECK(ret != 0);
+
+ test_ctx_destroy(ctx);
+}
+
+static void flb_test_verbose()
+{
+ int ret;
+ int got;
+ int unused = 0;
+ int expect = 10;
+ flb_sds_t ifname;
+ struct test_ctx *ctx;
+ struct flb_lib_out_cb cb_data;
+
+ cb_data.cb = cb_count_msgpack_map_size;
+ cb_data.data = &unused;
+
+ ctx = test_ctx_create(&cb_data);
+ if (!TEST_CHECK(ctx != NULL)) {
+ TEST_MSG("test_ctx_create failed");
+ exit(EXIT_FAILURE);
+ }
+
+ ifname = get_interface_str();
+ if (!TEST_CHECK(ifname != NULL)) {
+ TEST_MSG("can't get interface name");
+ flb_free(ctx);
+ exit(EXIT_FAILURE);
+ }
+
+ ret = flb_input_set(ctx->flb, ctx->i_ffd,
+ "interface", ifname,
+ "interval_sec", "0",
+ "interval_nsec", "500000000", /* 500 ms */
+ "test_at_init", "true",
+ "verbose", "true",
+ NULL);
+ TEST_CHECK(ret==0);
+
+ flb_sds_destroy(ifname);
+
+ clear_output_num();
+
+ /* Start the engine */
+ ret = flb_start(ctx->flb);
+ TEST_CHECK(ret == 0);
+
+ flb_time_msleep(1500); /* waiting flush */
+ got = get_output_num();
+
+ if (!TEST_CHECK(got > expect)) {
+ TEST_MSG("expect: >10 got: %d", got);
+ }
+
+ test_ctx_destroy(ctx);
+}
+
+TEST_LIST = {
+ {"normal case", flb_test_normal},
+ {"no interface", flb_test_no_interface},
+ {"invalid interface", flb_test_invalid_interface},
+ {"verbose", flb_test_verbose},
+ {NULL, NULL}
+};
diff --git a/fluent-bit/tests/runtime/in_opentelemetry.c b/fluent-bit/tests/runtime/in_opentelemetry.c
new file mode 100644
index 00000000..6f721cbe
--- /dev/null
+++ b/fluent-bit/tests/runtime/in_opentelemetry.c
@@ -0,0 +1,376 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/* Fluent Bit
+ * ==========
+ * Copyright (C) 2019-2022 The Fluent Bit Authors
+ * Copyright (C) 2015-2018 Treasure Data Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdlib.h>
+#include <fluent-bit.h>
+#include <fluent-bit/flb_compat.h>
+#include <fluent-bit/flb_time.h>
+#include <fluent-bit/flb_pack.h>
+#include <fluent-bit/flb_http_client.h>
+#include <monkey/mk_core.h>
+#include "flb_tests_runtime.h"
+
+#define JSON_CONTENT_TYPE "application/json"
+
+#define PORT_OTEL 4318
+#define TEST_MSG_OTEL_LOGS "{\"resourceLogs\":[{\"resource\":{},\"scopeLogs\":[{\"scope\":{},\"logRecords\":[{\"timeUnixNano\":\"1660296023390371588\",\"body\":{\"stringValue\":\"{\\\"message\\\":\\\"test\\\"}\"}}]}]}]}"
+#define TEST_CB_MSG_OTEL_LOGS "[1660296024.698112,{\"log\":\"{\\\"message\\\":\\\"test\\\"}\"}]"
+
+#define V1_ENDPOINT_LOGS "/v1/logs"
+
+struct http_client_ctx {
+ struct flb_upstream *u;
+ struct flb_connection *u_conn;
+ struct flb_config *config;
+ struct mk_event_loop *evl;
+};
+
+struct test_ctx {
+ flb_ctx_t *flb; /* Fluent Bit library context */
+ int i_ffd; /* Input fd */
+ int f_ffd; /* Filter fd (unused) */
+ int o_ffd; /* Output fd */
+ struct http_client_ctx *httpc;
+};
+
+
+pthread_mutex_t result_mutex = PTHREAD_MUTEX_INITIALIZER;
+int num_output = 0;
+static int get_output_num()
+{
+ int ret;
+ pthread_mutex_lock(&result_mutex);
+ ret = num_output;
+ pthread_mutex_unlock(&result_mutex);
+
+ return ret;
+}
+
+static void set_output_num(int num)
+{
+ pthread_mutex_lock(&result_mutex);
+ num_output = num;
+ pthread_mutex_unlock(&result_mutex);
+}
+
+static void clear_output_num()
+{
+ set_output_num(0);
+}
+
+/* Callback to check expected results */
+static int cb_check_result_json(void *record, size_t size, void *data)
+{
+ char *p;
+ char *expected;
+ char *result;
+ int num = get_output_num();
+
+ set_output_num(num+1);
+
+ expected = (char *) data;
+ result = (char *) record;
+
+ p = strstr(result, expected);
+ TEST_CHECK(p != NULL);
+
+ if (p==NULL) {
+ flb_error("Expected to find: '%s' in result '%s'",
+ expected, result);
+ }
+ /*
+ * If you want to debug your test
+ *
+ * printf("Expect: '%s' in result '%s'", expected, result);
+ */
+ flb_free(record);
+ return 0;
+}
+
+struct http_client_ctx* http_client_ctx_create()
+{
+ struct http_client_ctx *ret_ctx = NULL;
+ struct mk_event_loop *evl = NULL;
+
+ ret_ctx = flb_calloc(1, sizeof(struct http_client_ctx));
+ if (!TEST_CHECK(ret_ctx != NULL)) {
+ flb_errno();
+ TEST_MSG("flb_calloc(http_client_ctx) failed");
+ return NULL;
+ }
+
+ evl = mk_event_loop_create(16);
+ if (!TEST_CHECK(evl != NULL)) {
+ TEST_MSG("mk_event_loop failed");
+ flb_free(ret_ctx);
+ return NULL;
+ }
+ ret_ctx->evl = evl;
+ flb_engine_evl_init();
+ flb_engine_evl_set(evl);
+
+ ret_ctx->config = flb_config_init();
+ if(!TEST_CHECK(ret_ctx->config != NULL)) {
+ TEST_MSG("flb_config_init failed");
+ mk_event_loop_destroy(evl);
+ flb_free(ret_ctx);
+ return NULL;
+ }
+
+ ret_ctx->u = flb_upstream_create(ret_ctx->config, "127.0.0.1", PORT_OTEL, 0, NULL);
+ if (!TEST_CHECK(ret_ctx->u != NULL)) {
+ TEST_MSG("flb_upstream_create failed");
+ flb_config_exit(ret_ctx->config);
+ mk_event_loop_destroy(evl);
+ flb_free(ret_ctx);
+ return NULL;
+ }
+
+ ret_ctx->u_conn = flb_upstream_conn_get(ret_ctx->u);
+ TEST_CHECK(ret_ctx->u_conn != NULL);
+
+ ret_ctx->u_conn->upstream = ret_ctx->u;
+
+ return ret_ctx;
+}
+
+static struct test_ctx *test_ctx_create(struct flb_lib_out_cb *data)
+{
+ int i_ffd;
+ int o_ffd;
+ struct test_ctx *ctx = NULL;
+
+ ctx = flb_calloc(1, sizeof(struct test_ctx));
+ if (!TEST_CHECK(ctx != NULL)) {
+ TEST_MSG("flb_calloc failed");
+ flb_errno();
+ return NULL;
+ }
+
+ /* Service config */
+ ctx->flb = flb_create();
+ flb_service_set(ctx->flb,
+ "Flush", "0.200000000",
+ "Grace", "1",
+ "Log_Level", "error",
+ NULL);
+
+ /* Input */
+ i_ffd = flb_input(ctx->flb, (char *) "opentelemetry", NULL);
+ TEST_CHECK(i_ffd >= 0);
+ ctx->i_ffd = i_ffd;
+
+ /* Output */
+ o_ffd = flb_output(ctx->flb, (char *) "lib", (void *) data);
+ ctx->o_ffd = o_ffd;
+
+ return ctx;
+}
+
+int http_client_ctx_destroy(struct http_client_ctx* ctx)
+{
+ if (!TEST_CHECK(ctx != NULL)) {
+ return -1;
+ }
+ if (ctx->u) {
+ flb_upstream_destroy(ctx->u);
+ }
+ if (ctx->config) {
+ flb_config_exit(ctx->config);
+ }
+ if (ctx->evl) {
+ mk_event_loop_destroy(ctx->evl);
+ }
+
+ flb_free(ctx);
+ return 0;
+}
+
+static void test_ctx_destroy(struct test_ctx *ctx)
+{
+ TEST_CHECK(ctx != NULL);
+ if (ctx->httpc) {
+ http_client_ctx_destroy(ctx->httpc);
+ }
+
+ sleep(1);
+ flb_stop(ctx->flb);
+ flb_destroy(ctx->flb);
+ flb_free(ctx);
+}
+
+void flb_test_otel_logs()
+{
+ struct flb_lib_out_cb cb_data;
+ struct test_ctx *ctx;
+ struct flb_http_client *c;
+ int ret;
+ int num;
+ size_t b_sent;
+
+ char *buf = TEST_MSG_OTEL_LOGS;
+
+ clear_output_num();
+
+ cb_data.cb = cb_check_result_json;
+ cb_data.data = TEST_CB_MSG_OTEL_LOGS;
+
+ ctx = test_ctx_create(&cb_data);
+ if (!TEST_CHECK(ctx != NULL)) {
+ TEST_MSG("test_ctx_create failed");
+ exit(EXIT_FAILURE);
+ }
+
+ ret = flb_output_set(ctx->flb, ctx->o_ffd,
+ "match", "*",
+ "format", "json",
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ /* Start the engine */
+ ret = flb_start(ctx->flb);
+ TEST_CHECK(ret == 0);
+
+ ctx->httpc = http_client_ctx_create();
+ TEST_CHECK(ctx->httpc != NULL);
+
+ c = flb_http_client(ctx->httpc->u_conn, FLB_HTTP_POST, V1_ENDPOINT_LOGS, buf, strlen(buf),
+ "127.0.0.1", PORT_OTEL, NULL, 0);
+ ret = flb_http_add_header(c, FLB_HTTP_HEADER_CONTENT_TYPE, strlen(FLB_HTTP_HEADER_CONTENT_TYPE),
+ JSON_CONTENT_TYPE, strlen(JSON_CONTENT_TYPE));
+ TEST_CHECK(ret == 0);
+ if (!TEST_CHECK(c != NULL)) {
+ TEST_MSG("http_client failed");
+ exit(EXIT_FAILURE);
+ }
+
+ ret = flb_http_do(c, &b_sent);
+ if (!TEST_CHECK(ret == 0)) {
+ TEST_MSG("ret error. ret=%d\n", ret);
+ }
+ else if (!TEST_CHECK(b_sent > 0)){
+ TEST_MSG("b_sent size error. b_sent = %lu\n", b_sent);
+ }
+ else if (!TEST_CHECK(c->resp.status == 201)) {
+ TEST_MSG("http response code error. expect: 201, got: %d\n", c->resp.status);
+ }
+
+ /* waiting to flush */
+ flb_time_msleep(1500);
+
+ num = get_output_num();
+ if (!TEST_CHECK(num > 0)) {
+ TEST_MSG("no outputs");
+ }
+ flb_http_client_destroy(c);
+ flb_upstream_conn_release(ctx->httpc->u_conn);
+ test_ctx_destroy(ctx);
+}
+
+void flb_test_otel_successful_response_code(char *response_code)
+{
+ struct flb_lib_out_cb cb_data;
+ struct test_ctx *ctx;
+ struct flb_http_client *c;
+ int ret;
+ int num;
+ size_t b_sent;
+
+ char *buf = TEST_MSG_OTEL_LOGS;
+
+ clear_output_num();
+
+ cb_data.cb = cb_check_result_json;
+ cb_data.data = TEST_CB_MSG_OTEL_LOGS;
+
+ ctx = test_ctx_create(&cb_data);
+ if (!TEST_CHECK(ctx != NULL)) {
+ TEST_MSG("test_ctx_create failed");
+ exit(EXIT_FAILURE);
+ }
+
+ ret = flb_input_set(ctx->flb, ctx->i_ffd,
+ "successful_response_code", response_code,
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ ret = flb_output_set(ctx->flb, ctx->o_ffd,
+ "match", "*",
+ "format", "json",
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ /* Start the engine */
+ ret = flb_start(ctx->flb);
+ TEST_CHECK(ret == 0);
+
+ ctx->httpc = http_client_ctx_create();
+ TEST_CHECK(ctx->httpc != NULL);
+
+ c = flb_http_client(ctx->httpc->u_conn, FLB_HTTP_POST, V1_ENDPOINT_LOGS, buf, strlen(buf),
+ "127.0.0.1", PORT_OTEL, NULL, 0);
+ ret = flb_http_add_header(c, FLB_HTTP_HEADER_CONTENT_TYPE, strlen(FLB_HTTP_HEADER_CONTENT_TYPE),
+ JSON_CONTENT_TYPE, strlen(JSON_CONTENT_TYPE));
+ TEST_CHECK(ret == 0);
+ if (!TEST_CHECK(c != NULL)) {
+ TEST_MSG("http_client failed");
+ exit(EXIT_FAILURE);
+ }
+
+ ret = flb_http_do(c, &b_sent);
+ if (!TEST_CHECK(ret == 0)) {
+ TEST_MSG("ret error. ret=%d\n", ret);
+ }
+ else if (!TEST_CHECK(b_sent > 0)){
+ TEST_MSG("b_sent size error. b_sent = %lu\n", b_sent);
+ }
+ else if (!TEST_CHECK(c->resp.status == atoi(response_code))) {
+ TEST_MSG("http response code error. expect: %d, got: %d\n", atoi(response_code), c->resp.status);
+ }
+
+ /* waiting to flush */
+ flb_time_msleep(1500);
+
+ num = get_output_num();
+ if (!TEST_CHECK(num > 0)) {
+ TEST_MSG("no outputs");
+ }
+ flb_http_client_destroy(c);
+ flb_upstream_conn_release(ctx->httpc->u_conn);
+ test_ctx_destroy(ctx);
+}
+
+void flb_test_otel_successful_response_code_200()
+{
+ flb_test_otel_successful_response_code("200");
+}
+
+void flb_test_otel_successful_response_code_204()
+{
+ flb_test_otel_successful_response_code("204");
+}
+
+TEST_LIST = {
+ {"otel_logs", flb_test_otel_logs},
+ {"successful_response_code_200", flb_test_otel_successful_response_code_200},
+ {"successful_response_code_204", flb_test_otel_successful_response_code_204},
+ {NULL, NULL}
+};
+
diff --git a/fluent-bit/tests/runtime/in_podman_metrics.c b/fluent-bit/tests/runtime/in_podman_metrics.c
new file mode 100644
index 00000000..5ea0f852
--- /dev/null
+++ b/fluent-bit/tests/runtime/in_podman_metrics.c
@@ -0,0 +1,222 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/* Fluent Bit
+ * ==========
+ * Copyright (C) 2015-2016 Treasure Data Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <fluent-bit.h>
+#include "flb_tests_runtime.h"
+
+#define DPATH_PODMAN_REGULAR FLB_TESTS_DATA_PATH "/data/podman/regular"
+#define DPATH_PODMAN_REVERSED FLB_TESTS_DATA_PATH "/data/podman/reversed"
+#define DPATH_PODMAN_NO_CONFIG FLB_TESTS_DATA_PATH "/data/podman/no_config"
+#define DPATH_PODMAN_GARBAGE_CONFIG FLB_TESTS_DATA_PATH "/data/podman/garbage_config"
+#define DPATH_PODMAN_NO_SYSFS FLB_TESTS_DATA_PATH "/data/podman/no_sysfs"
+#define DPATH_PODMAN_NO_PROC FLB_TESTS_DATA_PATH "/data/podman/no_proc"
+#define DPATH_PODMAN_GARBAGE FLB_TESTS_DATA_PATH "/data/podman/garbage"
+#define DPATH_PODMAN_CGROUP_V2 FLB_TESTS_DATA_PATH "/data/podman/cgroupv2"
+
+
+int check_metric(flb_ctx_t *ctx, flb_sds_t *name) {
+ struct mk_list *tmp;
+ struct mk_list *head;
+ struct mk_list *inner_tmp;
+ struct mk_list *inner_head;
+
+ struct flb_input_instance *i_ins;
+ struct cmt_counter *counter;
+
+ int number_of_metrics=0;
+
+ mk_list_foreach_safe(head, tmp, &ctx->config->inputs) {
+ i_ins = mk_list_entry(head, struct flb_input_instance, _head);
+ mk_list_foreach_safe(inner_head, inner_tmp, &i_ins->cmt->counters) {
+ counter = mk_list_entry(inner_head, struct cmt_counter, _head);
+
+ if (strlen(name) != 0 && strcmp(name, counter->opts.name) == 0)
+ {
+ return 0;
+ }
+ number_of_metrics++;
+ }
+ }
+ return number_of_metrics;
+
+}
+
+void do_create(flb_ctx_t *ctx, char *system, ...)
+{
+ int64_t ret;
+ int in_ffd;
+ int out_ffd;
+ va_list va;
+ char *key;
+ char *value;
+ struct flb_lib_out_cb cb;
+ cb.data = NULL;
+
+ in_ffd = flb_input(ctx, (char *) system, NULL);
+
+ va_start(va, system);
+ while ((key = va_arg(va, char *))) {
+ value = va_arg(va, char *);
+ TEST_CHECK(value != NULL);
+ TEST_CHECK(flb_input_set(ctx, in_ffd, key, value, NULL) == 0);
+ }
+ va_end(va);
+
+ TEST_CHECK(flb_service_set(ctx, "Flush", "0.5",
+ "Grace", "1",
+ NULL) == 0);
+}
+
+void do_destroy(flb_ctx_t *ctx) {
+ flb_stop(ctx);
+ flb_destroy(ctx);
+}
+
+void flb_test_ipm_regular() {
+ flb_ctx_t *ctx = flb_create();
+ do_create(ctx,
+ "podman_metrics",
+ "path.config", DPATH_PODMAN_REGULAR "/config.json",
+ "scrape_on_start", "true",
+ "path.sysfs", DPATH_PODMAN_REGULAR,
+ "path.procfs", DPATH_PODMAN_REGULAR,
+ NULL);
+ TEST_CHECK(flb_start(ctx) == 0);
+ sleep(1);
+ TEST_CHECK(check_metric(ctx, "usage_bytes") == 0);
+ TEST_CHECK(check_metric(ctx, "receive_bytes_total") == 0);
+ do_destroy(ctx);
+}
+
+void flb_test_ipm_reversed() {
+ flb_ctx_t *ctx = flb_create();
+ do_create(ctx,
+ "podman_metrics",
+ "path.config", DPATH_PODMAN_REVERSED "/config.json",
+ "scrape_on_start", "true",
+ "path.sysfs", DPATH_PODMAN_REVERSED,
+ "path.procfs", DPATH_PODMAN_REVERSED,
+ NULL);
+ TEST_CHECK(flb_start(ctx) == 0);
+ sleep(1);
+ TEST_CHECK(check_metric(ctx, "usage_bytes") == 0);
+ TEST_CHECK(check_metric(ctx, "receive_bytes_total") == 0);
+ do_destroy(ctx);
+}
+
+void flb_test_ipm_garbage_config() {
+ flb_ctx_t *ctx = flb_create();
+ do_create(ctx,
+ "podman_metrics",
+ "path.config", DPATH_PODMAN_GARBAGE_CONFIG "/config.json",
+ "scrape_on_start", "true",
+ "path.sysfs", DPATH_PODMAN_GARBAGE_CONFIG,
+ "path.procfs", DPATH_PODMAN_GARBAGE_CONFIG,
+ NULL);
+ TEST_CHECK(flb_start(ctx) != 0);
+ do_destroy(ctx);
+}
+
+void flb_test_ipm_no_config() {
+ flb_ctx_t *ctx = flb_create();
+ do_create(ctx,
+ "podman_metrics",
+ "path.config", DPATH_PODMAN_NO_CONFIG "/config.json",
+ "scrape_on_start", "true",
+ "path.sysfs", DPATH_PODMAN_NO_CONFIG,
+ "path.procfs", DPATH_PODMAN_NO_CONFIG,
+ NULL);
+ TEST_CHECK(flb_start(ctx) != 0);
+ do_destroy(ctx);
+}
+
+void flb_test_ipm_no_sysfs() {
+ flb_ctx_t *ctx = flb_create();
+ do_create(ctx,
+ "podman_metrics",
+ "path.config", DPATH_PODMAN_NO_SYSFS "/config.json",
+ "scrape_on_start", "true",
+ "path.sysfs", DPATH_PODMAN_NO_SYSFS,
+ "path.procfs", DPATH_PODMAN_NO_SYSFS,
+ NULL);
+ TEST_CHECK(flb_start(ctx) == 0);
+ sleep(1);
+ TEST_CHECK(check_metric(ctx, "usage_bytes") != 0);
+ TEST_CHECK(check_metric(ctx, "receive_bytes_total") != 0);
+ do_destroy(ctx);
+}
+
+void flb_test_ipm_no_proc() {
+ flb_ctx_t *ctx = flb_create();
+ do_create(ctx,
+ "podman_metrics",
+ "path.config", DPATH_PODMAN_NO_PROC "/config.json",
+ "scrape_on_start", "true",
+ "path.sysfs", DPATH_PODMAN_NO_PROC,
+ "path.procfs", DPATH_PODMAN_NO_PROC,
+ NULL);
+ TEST_CHECK(flb_start(ctx) == 0);
+ sleep(1);
+ TEST_CHECK(check_metric(ctx, "usage_bytes") == 0);
+ TEST_CHECK(check_metric(ctx, "receive_bytes_total") != 0);
+ do_destroy(ctx);
+}
+
+void flb_test_ipm_garbage() {
+ flb_ctx_t *ctx = flb_create();
+ do_create(ctx,
+ "podman_metrics",
+ "path.config", DPATH_PODMAN_GARBAGE "/config.json",
+ "scrape_on_start", "true",
+ "path.sysfs", DPATH_PODMAN_GARBAGE,
+ "path.procfs", DPATH_PODMAN_GARBAGE,
+ NULL);
+ TEST_CHECK(flb_start(ctx) == 0);
+ sleep(1);
+ TEST_CHECK(check_metric(ctx, "usage_bytes") != 0);
+ TEST_CHECK(check_metric(ctx, "receive_bytes_total") != 0);
+ do_destroy(ctx);
+}
+
+void flb_test_ipm_cgroupv2() {
+ flb_ctx_t *ctx = flb_create();
+ do_create(ctx,
+ "podman_metrics",
+ "path.config", DPATH_PODMAN_CGROUP_V2 "/config.json",
+ "scrape_on_start", "true",
+ "path.sysfs", DPATH_PODMAN_CGROUP_V2,
+ "path.procfs", DPATH_PODMAN_CGROUP_V2,
+ NULL);
+ TEST_CHECK(flb_start(ctx) == 0);
+ sleep(1);
+ TEST_CHECK(check_metric(ctx, "usage_bytes") == 0);
+ TEST_CHECK(check_metric(ctx, "receive_bytes_total") == 0);
+ do_destroy(ctx);
+}
+
+TEST_LIST = {
+ {"regular", flb_test_ipm_regular},
+ {"reversed", flb_test_ipm_reversed},
+ {"no_config", flb_test_ipm_no_config},
+ {"garbage_config", flb_test_ipm_garbage_config},
+ {"no_sysfs_data", flb_test_ipm_no_sysfs},
+ {"no_proc_data", flb_test_ipm_no_proc},
+ {"garbage_data", flb_test_ipm_garbage},
+ {"cgroup_v2", flb_test_ipm_cgroupv2},
+ {NULL, NULL}};
diff --git a/fluent-bit/tests/runtime/in_proc.c b/fluent-bit/tests/runtime/in_proc.c
new file mode 120000
index 00000000..b194a853
--- /dev/null
+++ b/fluent-bit/tests/runtime/in_proc.c
@@ -0,0 +1 @@
+in_simple_systems.c \ No newline at end of file
diff --git a/fluent-bit/tests/runtime/in_random.c b/fluent-bit/tests/runtime/in_random.c
new file mode 120000
index 00000000..b194a853
--- /dev/null
+++ b/fluent-bit/tests/runtime/in_random.c
@@ -0,0 +1 @@
+in_simple_systems.c \ No newline at end of file
diff --git a/fluent-bit/tests/runtime/in_simple_systems.c b/fluent-bit/tests/runtime/in_simple_systems.c
new file mode 100644
index 00000000..7020c4f4
--- /dev/null
+++ b/fluent-bit/tests/runtime/in_simple_systems.c
@@ -0,0 +1,576 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/* Fluent Bit
+ * ==========
+ * Copyright (C) 2015-2016 Treasure Data Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <fluent-bit.h>
+#include <fluent-bit/flb_time.h>
+#include <pthread.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include "flb_tests_runtime.h"
+
+
+int64_t result_time;
+static inline int64_t set_result(int64_t v)
+{
+ int64_t old = __sync_lock_test_and_set(&result_time, v);
+ return old;
+}
+
+static inline int64_t get_result(void)
+{
+ int64_t old = __sync_fetch_and_add(&result_time, 0);
+
+ return old;
+}
+
+static inline int64_t time_in_ms()
+{
+ int ms;
+ struct timespec s;
+ TEST_CHECK(clock_gettime(CLOCK_MONOTONIC, &s) == 0);
+ ms = s.tv_nsec / 1.0e6;
+ if (ms >= 1000) {
+ ms = 0;
+ }
+ return 1000 * s.tv_sec + ms;
+}
+
+int callback_test(void* data, size_t size, void* cb_data)
+{
+ if (size > 0) {
+ flb_info("[test] flush triggered");
+ flb_lib_free(data);
+ set_result(time_in_ms()); /* success */
+ }
+ return 0;
+}
+
+struct callback_record {
+ void *data;
+ size_t size;
+};
+
+struct callback_records {
+ int num_records;
+ struct callback_record *records;
+};
+
+int callback_add_record(void* data, size_t size, void* cb_data)
+{
+ struct callback_records *ctx = (struct callback_records *)cb_data;
+
+ if (size > 0) {
+ flb_info("[test] flush record");
+ if (ctx->records == NULL) {
+ ctx->records = (struct callback_record *)
+ flb_calloc(1, sizeof(struct callback_record));
+ } else {
+ ctx->records = (struct callback_record *)
+ flb_realloc(ctx->records,
+ (ctx->num_records+1)*sizeof(struct callback_record));
+ }
+ if (ctx->records == NULL) {
+ return -1;
+ }
+ ctx->records[ctx->num_records].size = size;
+ ctx->records[ctx->num_records].data = data;
+ ctx->num_records++;
+ }
+ return 0;
+}
+
+void do_test(char *system, ...)
+{
+ int64_t ret;
+ flb_ctx_t *ctx = NULL;
+ int in_ffd;
+ int out_ffd;
+ va_list va;
+ char *key;
+ char *value;
+ struct flb_lib_out_cb cb;
+
+ cb.cb = callback_test;
+ cb.data = NULL;
+
+ /* initialize */
+ set_result(0);
+
+ ctx = flb_create();
+
+ in_ffd = flb_input(ctx, (char *) system, NULL);
+ TEST_CHECK(in_ffd >= 0);
+ TEST_CHECK(flb_input_set(ctx, in_ffd, "tag", "test", NULL) == 0);
+
+ va_start(va, system);
+ while ((key = va_arg(va, char *))) {
+ value = va_arg(va, char *);
+ TEST_CHECK(value != NULL);
+ TEST_CHECK(flb_input_set(ctx, in_ffd, key, value, NULL) == 0);
+ }
+ va_end(va);
+
+ out_ffd = flb_output(ctx, (char *) "lib", &cb);
+ TEST_CHECK(out_ffd >= 0);
+ TEST_CHECK(flb_output_set(ctx, out_ffd, "match", "test", NULL) == 0);
+
+ TEST_CHECK(flb_service_set(ctx, "Flush", "0.5",
+ "Grace", "1",
+ NULL) == 0);
+
+ /* The following test tries to check if an input plugin generates
+ * data in a timely manner.
+ *
+ * 0 1 2 3 4 (sec)
+ * |--F--F--F--C--F--F--F--C--|
+ *
+ * F ... Flush (0.5 sec interval)
+ * C ... Condition checks
+ *
+ * Since CI servers can be sometimes very slow, we wait slightly a
+ * little more before checking the condition.
+ */
+
+ /* Start test */
+ TEST_CHECK(flb_start(ctx) == 0);
+
+ /* 2 sec passed. It must have flushed */
+ sleep(2);
+ flb_info("[test] check status 1");
+ ret = get_result();
+ TEST_CHECK(ret > 0);
+
+ /* 4 sec passed. It must have flushed */
+ sleep(2);
+ flb_info("[test] check status 2");
+ ret = get_result();
+ TEST_CHECK(ret > 0);
+
+ flb_stop(ctx);
+ flb_destroy(ctx);
+}
+
+void do_test_records(char *system, void (*records_cb)(struct callback_records *), ...)
+{
+ flb_ctx_t *ctx = NULL;
+ int in_ffd;
+ int out_ffd;
+ va_list va;
+ char *key;
+ char *value;
+ int i;
+ struct flb_lib_out_cb cb;
+ struct callback_records *records;
+
+ records = flb_calloc(1, sizeof(struct callback_records));
+ records->num_records = 0;
+ records->records = NULL;
+ cb.cb = callback_add_record;
+ cb.data = (void *)records;
+
+ /* initialize */
+ set_result(0);
+
+ ctx = flb_create();
+
+ in_ffd = flb_input(ctx, (char *) system, NULL);
+ TEST_CHECK(in_ffd >= 0);
+ TEST_CHECK(flb_input_set(ctx, in_ffd, "tag", "test", NULL) == 0);
+
+ va_start(va, records_cb);
+ while ((key = va_arg(va, char *))) {
+ value = va_arg(va, char *);
+ TEST_CHECK(value != NULL);
+ TEST_CHECK(flb_input_set(ctx, in_ffd, key, value, NULL) == 0);
+ }
+ va_end(va);
+
+ out_ffd = flb_output(ctx, (char *) "lib", &cb);
+ TEST_CHECK(out_ffd >= 0);
+ TEST_CHECK(flb_output_set(ctx, out_ffd, "match", "test", NULL) == 0);
+
+ TEST_CHECK(flb_service_set(ctx, "Flush", "1",
+ "Grace", "1",
+ NULL) == 0);
+
+ /* Start test */
+ TEST_CHECK(flb_start(ctx) == 0);
+
+ /* 4 sec passed. It must have flushed */
+ sleep(5);
+
+ records_cb(records);
+
+ flb_stop(ctx);
+
+ for (i = 0; i < records->num_records; i++) {
+ flb_lib_free(records->records[i].data);
+ }
+ flb_free(records->records);
+ flb_free(records);
+
+ flb_destroy(ctx);
+}
+
+void do_test_records_single(char *system, void (*records_cb)(struct callback_records *), ...)
+{
+ flb_ctx_t *ctx = NULL;
+ int in_ffd;
+ int out_ffd;
+ int exit_ffd;
+ va_list va;
+ char *key;
+ char *value;
+ int i;
+ struct flb_lib_out_cb cb;
+ struct callback_records *records;
+
+ records = flb_calloc(1, sizeof(struct callback_records));
+ records->num_records = 0;
+ records->records = NULL;
+ cb.cb = callback_add_record;
+ cb.data = (void *)records;
+
+ /* initialize */
+ set_result(0);
+
+ ctx = flb_create();
+
+ in_ffd = flb_input(ctx, (char *) system, NULL);
+ TEST_CHECK(in_ffd >= 0);
+ TEST_CHECK(flb_input_set(ctx, in_ffd, "tag", "test", NULL) == 0);
+
+ va_start(va, records_cb);
+ while ((key = va_arg(va, char *))) {
+ value = va_arg(va, char *);
+ TEST_CHECK(value != NULL);
+ TEST_CHECK(flb_input_set(ctx, in_ffd, key, value, NULL) == 0);
+ }
+ va_end(va);
+
+ out_ffd = flb_output(ctx, (char *) "lib", &cb);
+ TEST_CHECK(out_ffd >= 0);
+ TEST_CHECK(flb_output_set(ctx, out_ffd, "match", "test", NULL) == 0);
+
+ exit_ffd = flb_output(ctx, (char *)"exit", &cb);
+ TEST_CHECK(flb_output_set(ctx, exit_ffd, "match", "test", NULL) == 0);
+
+ TEST_CHECK(flb_service_set(ctx, "Flush", "1",
+ "Grace", "1",
+ NULL) == 0);
+
+ /* Start test */
+ TEST_CHECK(flb_start(ctx) == 0);
+
+ /* 4 sec passed. It must have flushed */
+ sleep(5);
+
+ records_cb(records);
+
+ flb_stop(ctx);
+
+ for (i = 0; i < records->num_records; i++) {
+ flb_lib_free(records->records[i].data);
+ }
+ flb_free(records->records);
+ flb_free(records);
+
+ flb_destroy(ctx);
+}
+
+void flb_test_in_disk_flush()
+{
+ do_test("disk",
+ "interval_sec", "0",
+ "interval_nsec", "500000000",
+ NULL);
+}
+void flb_test_in_proc_flush()
+{
+ do_test("proc",
+ "interval_sec", "0",
+ "interval_nsec", "500000000",
+ "proc_name", "flb_test_in_proc",
+ "alert", "true",
+ "mem", "on",
+ "fd", "on",
+ NULL);
+}
+void flb_test_in_head_flush()
+{
+ do_test("head",
+ "interval_sec", "0",
+ "interval_nsec", "500000000",
+ "File", "/dev/urandom",
+ NULL);
+}
+void flb_test_in_cpu_flush()
+{
+ do_test("cpu", NULL);
+}
+void flb_test_in_random_flush()
+{
+ do_test("random", NULL);
+}
+
+void flb_test_dummy_records_1234(struct callback_records *records)
+{
+ int i;
+ msgpack_unpacked result;
+ msgpack_object *obj;
+ size_t off = 0;
+ struct flb_time ftm;
+
+ TEST_CHECK(records->num_records > 0);
+ for (i = 0; i < records->num_records; i++) {
+ msgpack_unpacked_init(&result);
+
+ while (msgpack_unpack_next(&result, records->records[i].data,
+ records->records[i].size, &off) == MSGPACK_UNPACK_SUCCESS) {
+ flb_time_pop_from_msgpack(&ftm, &result, &obj);
+
+ TEST_CHECK(ftm.tm.tv_sec == 1234);
+ TEST_CHECK(ftm.tm.tv_nsec == 1234);
+ }
+ msgpack_unpacked_destroy(&result);
+ }
+}
+
+void flb_test_dummy_records_1999(struct callback_records *records)
+{
+ int i;
+ msgpack_unpacked result;
+ msgpack_object *obj;
+ size_t off = 0;
+ struct flb_time ftm;
+
+ TEST_CHECK(records->num_records > 0);
+ for (i = 0; i < records->num_records; i++) {
+ msgpack_unpacked_init(&result);
+
+ while (msgpack_unpack_next(&result, records->records[i].data,
+ records->records[i].size, &off) == MSGPACK_UNPACK_SUCCESS) {
+ flb_time_pop_from_msgpack(&ftm, &result, &obj);
+ TEST_CHECK(ftm.tm.tv_sec == 1999);
+ TEST_CHECK(ftm.tm.tv_nsec == 1999);
+ }
+ msgpack_unpacked_destroy(&result);
+ }
+}
+
+void flb_test_dummy_records_today(struct callback_records *records)
+{
+ int i;
+ msgpack_unpacked result;
+ msgpack_object *obj;
+ size_t off = 0;
+ struct flb_time ftm;
+ struct flb_time now;
+
+ flb_time_get(&now);
+ /* set 5 minutes in the past since this is invoked after the test began */
+ now.tm.tv_sec -= (5 * 60);
+
+ TEST_CHECK(records->num_records > 0);
+ for (i = 0; i < records->num_records; i++) {
+ msgpack_unpacked_init(&result);
+
+ while (msgpack_unpack_next(&result, records->records[i].data,
+ records->records[i].size, &off) == MSGPACK_UNPACK_SUCCESS) {
+ flb_time_pop_from_msgpack(&ftm, &result, &obj);
+ TEST_CHECK(ftm.tm.tv_sec >= now.tm.tv_sec);
+ }
+ msgpack_unpacked_destroy(&result);
+ }
+}
+
+void flb_test_dummy_records_message(struct callback_records *records)
+{
+ int i;
+ msgpack_unpacked result;
+ msgpack_object *obj;
+ size_t off = 0;
+ struct flb_time ftm;
+
+ TEST_CHECK(records->num_records > 0);
+ for (i = 0; i < records->num_records; i++) {
+ msgpack_unpacked_init(&result);
+
+ while (msgpack_unpack_next(&result, records->records[i].data,
+ records->records[i].size, &off) == MSGPACK_UNPACK_SUCCESS) {
+ flb_time_pop_from_msgpack(&ftm, &result, &obj);
+ TEST_CHECK(obj->type == MSGPACK_OBJECT_MAP);
+ TEST_CHECK(strncmp("new_key",
+ obj->via.map.ptr[0].key.via.str.ptr,
+ obj->via.map.ptr[0].key.via.str.size) == 0);
+ TEST_CHECK(strncmp("new_value",
+ obj->via.map.ptr[0].val.via.str.ptr,
+ obj->via.map.ptr[0].val.via.str.size) == 0);
+ }
+ msgpack_unpacked_destroy(&result);
+ }
+}
+
+void flb_test_dummy_records_message_default(struct callback_records *records)
+{
+ int i;
+ msgpack_unpacked result;
+ msgpack_object *obj;
+ size_t off = 0;
+ struct flb_time ftm;
+
+ TEST_CHECK(records->num_records > 0);
+ for (i = 0; i < records->num_records; i++) {
+ msgpack_unpacked_init(&result);
+
+ while (msgpack_unpack_next(&result, records->records[i].data,
+ records->records[i].size, &off) == MSGPACK_UNPACK_SUCCESS) {
+ flb_time_pop_from_msgpack(&ftm, &result, &obj);
+ TEST_CHECK(obj->type == MSGPACK_OBJECT_MAP);
+ TEST_CHECK(strncmp("message",
+ obj->via.map.ptr[0].key.via.str.ptr,
+ obj->via.map.ptr[0].key.via.str.size) == 0);
+ TEST_CHECK(strncmp("dummy",
+ obj->via.map.ptr[0].val.via.str.ptr,
+ obj->via.map.ptr[0].val.via.str.size) == 0);
+ }
+ msgpack_unpacked_destroy(&result);
+ }
+}
+
+// We check for a minimum of messages because of the non-deterministic
+// nature of flushes as well as chunking.
+void flb_test_dummy_records_message_copies_1(struct callback_records *records)
+{
+ TEST_CHECK(records->num_records >= 1);
+}
+
+void flb_test_dummy_records_message_copies_5(struct callback_records *records)
+{
+ TEST_CHECK(records->num_records >= 5);
+}
+
+void flb_test_dummy_records_message_copies_100(struct callback_records *records)
+{
+ TEST_CHECK(records->num_records >= 100);
+}
+
+void flb_test_in_dummy_flush()
+{
+ do_test("dummy", NULL);
+ do_test_records("dummy", flb_test_dummy_records_message_default, NULL);
+ do_test_records("dummy", flb_test_dummy_records_today, NULL);
+ do_test_records("dummy", flb_test_dummy_records_message,
+ "dummy", "{\"new_key\": \"new_value\"}",
+ NULL);
+ do_test_records("dummy", flb_test_dummy_records_message_default,
+ "dummy", "{\"bad_json}",
+ NULL);
+ do_test_records("dummy", flb_test_dummy_records_1234,
+ "start_time_sec", "1234",
+ "start_time_nsec", "1234",
+ "fixed_timestamp", "on",
+ NULL);
+ do_test_records("dummy", flb_test_dummy_records_1999,
+ "start_time_sec", "1999",
+ "start_time_nsec", "1999",
+ "fixed_timestamp", "on",
+ NULL);
+ do_test_records_single("dummy", flb_test_dummy_records_message_copies_1,
+ "copies", "1",
+ NULL);
+ do_test_records_single("dummy", flb_test_dummy_records_message_copies_5,
+ "copies", "5",
+ NULL);
+ do_test_records_single("dummy", flb_test_dummy_records_message_copies_100,
+ "copies", "100",
+ NULL);
+}
+
+void flb_test_in_dummy_thread_flush()
+{
+ do_test("dummy_thread", NULL);
+}
+
+void flb_test_in_mem_flush()
+{
+ do_test("mem", NULL);
+}
+
+#ifdef in_proc
+void flb_test_in_proc_absent_process(void)
+{
+ int ret;
+ flb_ctx_t *ctx = NULL;
+ int in_ffd;
+ int out_ffd;
+
+ struct flb_lib_out_cb cb;
+ cb.cb = callback_test;
+ cb.data = NULL;
+
+ ctx = flb_create();
+
+ in_ffd = flb_input(ctx, (char *) "proc", NULL);
+ TEST_CHECK(in_ffd >= 0);
+ flb_input_set(ctx, in_ffd, "tag", "test",
+ "interval_sec", "1", "proc_name", "-",
+ "alert", "true", "mem", "on", "fd", "on", NULL);
+
+ out_ffd = flb_output(ctx, (char *) "lib", &cb);
+ TEST_CHECK(out_ffd >= 0);
+ flb_output_set(ctx, out_ffd, "match", "test", NULL);
+
+ flb_service_set(ctx, "Flush", "2", "Grace", "1", NULL);
+
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0); // error occurs but return value is true
+
+ flb_stop(ctx);
+ flb_destroy(ctx);
+}
+#endif
+
+/* Test list */
+TEST_LIST = {
+#ifdef in_disk
+ {"disk_flush", flb_test_in_disk_flush},
+#endif
+#ifdef in_proc
+ {"proc_flush", flb_test_in_proc_flush},
+ {"proc_absent_process", flb_test_in_proc_absent_process},
+#endif
+#ifdef in_head
+ {"head_flush", flb_test_in_head_flush},
+#endif
+#ifdef in_cpu
+ {"cpu_flush", flb_test_in_cpu_flush},
+#endif
+#ifdef in_random
+ {"random_flush", flb_test_in_random_flush},
+#endif
+#ifdef in_dummy
+ {"dummy_flush", flb_test_in_dummy_flush},
+#endif
+#ifdef in_mem
+ {"mem_flush", flb_test_in_mem_flush},
+#endif
+ {NULL, NULL}
+};
+
diff --git a/fluent-bit/tests/runtime/in_splunk.c b/fluent-bit/tests/runtime/in_splunk.c
new file mode 100644
index 00000000..bba4977c
--- /dev/null
+++ b/fluent-bit/tests/runtime/in_splunk.c
@@ -0,0 +1,824 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/* Fluent Bit
+ * ==========
+ * Copyright (C) 2019-2023 The Fluent Bit Authors
+ * Copyright (C) 2015-2018 Treasure Data Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdlib.h>
+#include <fluent-bit.h>
+#include <fluent-bit/flb_compat.h>
+#include <fluent-bit/flb_time.h>
+#include <fluent-bit/flb_pack.h>
+#include <fluent-bit/flb_http_client.h>
+#include <fluent-bit/flb_gzip.h>
+#include <monkey/mk_core.h>
+#include "flb_tests_runtime.h"
+
+#define JSON_CONTENT_TYPE "application/json"
+
+struct in_splunk_client_ctx {
+ struct flb_upstream *u;
+ struct flb_connection *u_conn;
+ struct flb_config *config;
+ struct mk_event_loop *evl;
+};
+
+struct test_ctx {
+ flb_ctx_t *flb; /* Fluent Bit library context */
+ int i_ffd; /* Input fd */
+ int f_ffd; /* Filter fd (unused) */
+ int o_ffd; /* Output fd */
+ struct in_splunk_client_ctx *httpc;
+};
+
+
+pthread_mutex_t result_mutex = PTHREAD_MUTEX_INITIALIZER;
+int num_output = 0;
+static int get_output_num()
+{
+ int ret;
+ pthread_mutex_lock(&result_mutex);
+ ret = num_output;
+ pthread_mutex_unlock(&result_mutex);
+
+ return ret;
+}
+
+static void set_output_num(int num)
+{
+ pthread_mutex_lock(&result_mutex);
+ num_output = num;
+ pthread_mutex_unlock(&result_mutex);
+}
+
+static void clear_output_num()
+{
+ set_output_num(0);
+}
+
+/* Callback to check expected results */
+static int cb_check_result_json(void *record, size_t size, void *data)
+{
+ char *p;
+ char *expected;
+ char *result;
+ int num = get_output_num();
+
+ set_output_num(num+1);
+
+ expected = (char *) data;
+ result = (char *) record;
+
+ p = strstr(result, expected);
+ TEST_CHECK(p != NULL);
+
+ if (p==NULL) {
+ flb_error("Expected to find: '%s' in result '%s'",
+ expected, result);
+ }
+ /*
+ * If you want to debug your test
+ *
+ * printf("Expect: '%s' in result '%s'", expected, result);
+ */
+ flb_free(record);
+ return 0;
+}
+
+struct in_splunk_client_ctx* splunk_client_ctx_create(int port)
+{
+ struct in_splunk_client_ctx *ret_ctx = NULL;
+ struct mk_event_loop *evl = NULL;
+
+ ret_ctx = flb_calloc(1, sizeof(struct in_splunk_client_ctx));
+ if (!TEST_CHECK(ret_ctx != NULL)) {
+ flb_errno();
+ TEST_MSG("flb_calloc(splunk_client_ctx) failed");
+ return NULL;
+ }
+
+ evl = mk_event_loop_create(16);
+ if (!TEST_CHECK(evl != NULL)) {
+ TEST_MSG("mk_event_loop failed");
+ flb_free(ret_ctx);
+ return NULL;
+ }
+ ret_ctx->evl = evl;
+ flb_engine_evl_init();
+ flb_engine_evl_set(evl);
+
+ ret_ctx->config = flb_config_init();
+ if(!TEST_CHECK(ret_ctx->config != NULL)) {
+ TEST_MSG("flb_config_init failed");
+ mk_event_loop_destroy(evl);
+ flb_free(ret_ctx);
+ return NULL;
+ }
+
+ ret_ctx->u = flb_upstream_create(ret_ctx->config, "127.0.0.1", port, 0, NULL);
+ if (!TEST_CHECK(ret_ctx->u != NULL)) {
+ TEST_MSG("flb_upstream_create failed");
+ flb_config_exit(ret_ctx->config);
+ mk_event_loop_destroy(evl);
+ flb_free(ret_ctx);
+ return NULL;
+ }
+
+ ret_ctx->u_conn = flb_upstream_conn_get(ret_ctx->u);
+ TEST_CHECK(ret_ctx->u_conn != NULL);
+
+ ret_ctx->u_conn->upstream = ret_ctx->u;
+
+ return ret_ctx;
+}
+
+static struct test_ctx *test_ctx_create(struct flb_lib_out_cb *data)
+{
+ int i_ffd;
+ int o_ffd;
+ struct test_ctx *ctx = NULL;
+
+ ctx = flb_calloc(1, sizeof(struct test_ctx));
+ if (!TEST_CHECK(ctx != NULL)) {
+ TEST_MSG("flb_calloc failed");
+ flb_errno();
+ return NULL;
+ }
+
+ /* Service config */
+ ctx->flb = flb_create();
+ flb_service_set(ctx->flb,
+ "Flush", "0.200000000",
+ "Grace", "1",
+ "Log_Level", "error",
+ NULL);
+
+ /* Input */
+ i_ffd = flb_input(ctx->flb, (char *) "splunk", NULL);
+ TEST_CHECK(i_ffd >= 0);
+ ctx->i_ffd = i_ffd;
+
+ /* Output */
+ o_ffd = flb_output(ctx->flb, (char *) "lib", (void *) data);
+ ctx->o_ffd = o_ffd;
+
+ return ctx;
+}
+
+int splunk_client_ctx_destroy(struct in_splunk_client_ctx* ctx)
+{
+ if (!TEST_CHECK(ctx != NULL)) {
+ return -1;
+ }
+ if (ctx->u) {
+ flb_upstream_destroy(ctx->u);
+ }
+ if (ctx->config) {
+ flb_config_exit(ctx->config);
+ }
+ if (ctx->evl) {
+ mk_event_loop_destroy(ctx->evl);
+ }
+
+ flb_free(ctx);
+ return 0;
+}
+
+static void test_ctx_destroy(struct test_ctx *ctx)
+{
+ TEST_CHECK(ctx != NULL);
+ if (ctx->httpc) {
+ splunk_client_ctx_destroy(ctx->httpc);
+ }
+
+ sleep(1);
+ flb_stop(ctx->flb);
+ flb_destroy(ctx->flb);
+ flb_free(ctx);
+}
+
+void flb_test_splunk_health()
+{
+ struct flb_lib_out_cb cb_data;
+ struct test_ctx *ctx;
+ struct flb_http_client *c;
+ int ret;
+ size_t b_sent;
+ char *expected = "{\"text\":\"Success\",\"code\":200}";
+ char *buf = NULL;
+ int port = 8808;
+ char sport[16];
+
+ snprintf(sport, 16, "%d", port);
+
+ ctx = test_ctx_create(&cb_data);
+ if (!TEST_CHECK(ctx != NULL)) {
+ TEST_MSG("test_ctx_create failed");
+ exit(EXIT_FAILURE);
+ }
+
+ ret = flb_input_set(ctx->flb, ctx->i_ffd,
+ "port", sport,
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ ret = flb_output_set(ctx->flb, ctx->o_ffd,
+ "match", "*",
+ "format", "json",
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ /* Start the engine */
+ ret = flb_start(ctx->flb);
+ TEST_CHECK(ret == 0);
+
+ ctx->httpc = splunk_client_ctx_create(port);
+ TEST_CHECK(ctx->httpc != NULL);
+
+ c = flb_http_client(ctx->httpc->u_conn, FLB_HTTP_GET, "/services/collector/health", NULL, 0,
+ "127.0.0.1", port, NULL, 0);
+ if (!TEST_CHECK(c != NULL)) {
+ TEST_MSG("splunk_client failed");
+ exit(EXIT_FAILURE);
+ }
+
+ ret = flb_http_do(c, &b_sent);
+ if (!TEST_CHECK(ret == 0)) {
+ TEST_MSG("ret error. ret=%d\n", ret);
+ }
+ else if (!TEST_CHECK(b_sent > 0)){
+ TEST_MSG("b_sent size error. b_sent = %lu\n", b_sent);
+ }
+ else if (!TEST_CHECK(c->resp.status == 200)) {
+ TEST_MSG("http response code error. expect: 200, got: %d\n", c->resp.status);
+ }
+
+ /* waiting to flush */
+ flb_time_msleep(1500);
+
+ buf = strstr(c->resp.payload, expected);
+ if (!TEST_CHECK(buf != NULL)) {
+ TEST_MSG("http request for version info failed");
+ }
+ flb_http_client_destroy(c);
+ flb_upstream_conn_release(ctx->httpc->u_conn);
+ test_ctx_destroy(ctx);
+}
+
+void flb_test_splunk(int port, char *endpoint)
+{
+ struct flb_lib_out_cb cb_data;
+ struct test_ctx *ctx;
+ struct flb_http_client *c;
+ int ret;
+ int num;
+ size_t b_sent;
+ char *buf = "{\"event\": \"Pony 1 has left the barn\"}{\"event\": \"Pony 2 has left the barn\"}{\"event\": \"Pony 3 has left the barn\", \"nested\": {\"key1\": \"value1\"}}";
+ char *expected = "\"event\":";
+ char sport[16];
+ flb_sds_t target;
+
+ target = flb_sds_create_size(64);
+ flb_sds_cat(target, endpoint, strlen(endpoint));
+
+ snprintf(sport, 16, "%d", port);
+
+ clear_output_num();
+
+ cb_data.cb = cb_check_result_json;
+ cb_data.data = expected;
+
+ ctx = test_ctx_create(&cb_data);
+ if (!TEST_CHECK(ctx != NULL)) {
+ TEST_MSG("test_ctx_create failed");
+ exit(EXIT_FAILURE);
+ }
+
+ ret = flb_input_set(ctx->flb, ctx->i_ffd,
+ "port", sport,
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ ret = flb_output_set(ctx->flb, ctx->o_ffd,
+ "match", "*",
+ "format", "json",
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ /* Start the engine */
+ ret = flb_start(ctx->flb);
+ TEST_CHECK(ret == 0);
+
+ ctx->httpc = splunk_client_ctx_create(port);
+ TEST_CHECK(ctx->httpc != NULL);
+
+ c = flb_http_client(ctx->httpc->u_conn, FLB_HTTP_POST, target, buf, strlen(buf),
+ "127.0.0.1", port, NULL, 0);
+ ret = flb_http_add_header(c, FLB_HTTP_HEADER_CONTENT_TYPE, strlen(FLB_HTTP_HEADER_CONTENT_TYPE),
+ JSON_CONTENT_TYPE, strlen(JSON_CONTENT_TYPE));
+ TEST_CHECK(ret == 0);
+ if (!TEST_CHECK(c != NULL)) {
+ TEST_MSG("splunk_client failed");
+ exit(EXIT_FAILURE);
+ }
+
+ ret = flb_http_do(c, &b_sent);
+ if (!TEST_CHECK(ret == 0)) {
+ TEST_MSG("ret error. ret=%d\n", ret);
+ }
+ else if (!TEST_CHECK(b_sent > 0)){
+ TEST_MSG("b_sent size error. b_sent = %lu\n", b_sent);
+ }
+ else if (!TEST_CHECK(c->resp.status == 200)) {
+ TEST_MSG("http response code error. expect: 200, got: %d\n", c->resp.status);
+ }
+
+ /* waiting to flush */
+ flb_time_msleep(1500);
+
+ num = get_output_num();
+ if (!TEST_CHECK(num > 0)) {
+ TEST_MSG("no outputs");
+ }
+ flb_sds_destroy(target);
+ flb_http_client_destroy(c);
+ flb_upstream_conn_release(ctx->httpc->u_conn);
+ test_ctx_destroy(ctx);
+}
+
+void flb_test_splunk_collector()
+{
+ flb_test_splunk(8809, "/services/collector");
+}
+
+void flb_test_splunk_collector_event()
+{
+ flb_test_splunk(8810, "/services/collector/event");
+}
+
+void flb_test_splunk_raw(int port)
+{
+ struct flb_lib_out_cb cb_data;
+ struct test_ctx *ctx;
+ struct flb_http_client *c;
+ int ret;
+ int num;
+ size_t b_sent;
+ char *buf = "1, 2, 3... Hello, world!";
+ char *expected = "\"log\":";
+ char sport[16];
+
+ snprintf(sport, 16, "%d", port);
+
+ clear_output_num();
+
+ cb_data.cb = cb_check_result_json;
+ cb_data.data = expected;
+
+ ctx = test_ctx_create(&cb_data);
+ if (!TEST_CHECK(ctx != NULL)) {
+ TEST_MSG("test_ctx_create failed");
+ exit(EXIT_FAILURE);
+ }
+
+ ret = flb_input_set(ctx->flb, ctx->i_ffd,
+ "port", sport,
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ ret = flb_output_set(ctx->flb, ctx->o_ffd,
+ "match", "*",
+ "format", "json",
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ /* Start the engine */
+ ret = flb_start(ctx->flb);
+ TEST_CHECK(ret == 0);
+
+ ctx->httpc = splunk_client_ctx_create(port);
+ TEST_CHECK(ctx->httpc != NULL);
+
+ c = flb_http_client(ctx->httpc->u_conn, FLB_HTTP_POST, "/services/collector/raw", buf, strlen(buf),
+ "127.0.0.1", port, NULL, 0);
+ ret = flb_http_add_header(c, FLB_HTTP_HEADER_CONTENT_TYPE, strlen(FLB_HTTP_HEADER_CONTENT_TYPE),
+ JSON_CONTENT_TYPE, strlen(JSON_CONTENT_TYPE));
+ TEST_CHECK(ret == 0);
+ if (!TEST_CHECK(c != NULL)) {
+ TEST_MSG("splunk_client failed");
+ exit(EXIT_FAILURE);
+ }
+
+ ret = flb_http_do(c, &b_sent);
+ if (!TEST_CHECK(ret == 0)) {
+ TEST_MSG("ret error. ret=%d\n", ret);
+ }
+ else if (!TEST_CHECK(b_sent > 0)){
+ TEST_MSG("b_sent size error. b_sent = %lu\n", b_sent);
+ }
+ else if (!TEST_CHECK(c->resp.status == 200)) {
+ TEST_MSG("http response code error. expect: 200, got: %d\n", c->resp.status);
+ }
+
+ /* waiting to flush */
+ flb_time_msleep(1500);
+
+ num = get_output_num();
+ if (!TEST_CHECK(num > 0)) {
+ TEST_MSG("no outputs");
+ }
+ flb_http_client_destroy(c);
+ flb_upstream_conn_release(ctx->httpc->u_conn);
+ test_ctx_destroy(ctx);
+}
+
+void flb_test_splunk_collector_raw()
+{
+ flb_test_splunk_raw(8811);
+}
+
+void flb_test_splunk_raw_multilines(int port)
+{
+ struct flb_lib_out_cb cb_data;
+ struct test_ctx *ctx;
+ struct flb_http_client *c;
+ int ret;
+ int num;
+ size_t b_sent;
+ char *buf = "127.0.0.1 - admin [28/Sep/2016:09:05:26.875 -0700] \"GET /servicesNS/admin/launcher/data/ui/views?count=-1 HTTP/1.0\" 200 126721 - - - 6ms" \
+ "127.0.0.1 - admin [28/Sep/2016:09:05:26.917 -0700] \"GET /servicesNS/admin/launcher/data/ui/nav/default HTTP/1.0\" 200 4367 - - - 6ms" \
+ "127.0.0.1 - admin [28/Sep/2016:09:05:26.941 -0700] \"GET /services/apps/local?search=disabled%3Dfalse&count=-1 HTTP/1.0\" 200 31930 - - - 4ms" \
+ "127.0.0.1 - admin [28/Sep/2016:09:05:26.954 -0700] \"GET /services/apps/local?search=disabled%3Dfalse&count=-1 HTTP/1.0\" 200 31930 - - - 3ms" \
+ "127.0.0.1 - admin [28/Sep/2016:09:05:26.968 -0700] \"GET /servicesNS/admin/launcher/data/ui/views?digest=1&count=-1 HTTP/1.0\" 200 58672 - - - 5ms";
+ char *expected = "\"log\":";
+ char sport[16];
+
+ snprintf(sport, 16, "%d", port);
+
+ clear_output_num();
+
+ cb_data.cb = cb_check_result_json;
+ cb_data.data = expected;
+
+ ctx = test_ctx_create(&cb_data);
+ if (!TEST_CHECK(ctx != NULL)) {
+ TEST_MSG("test_ctx_create failed");
+ exit(EXIT_FAILURE);
+ }
+
+ ret = flb_input_set(ctx->flb, ctx->i_ffd,
+ "port", sport,
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ ret = flb_output_set(ctx->flb, ctx->o_ffd,
+ "match", "*",
+ "format", "json",
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ /* Start the engine */
+ ret = flb_start(ctx->flb);
+ TEST_CHECK(ret == 0);
+
+ ctx->httpc = splunk_client_ctx_create(port);
+ TEST_CHECK(ctx->httpc != NULL);
+
+ c = flb_http_client(ctx->httpc->u_conn, FLB_HTTP_POST, "/services/collector/raw", buf, strlen(buf),
+ "127.0.0.1", port, NULL, 0);
+ ret = flb_http_add_header(c, FLB_HTTP_HEADER_CONTENT_TYPE, strlen(FLB_HTTP_HEADER_CONTENT_TYPE),
+ JSON_CONTENT_TYPE, strlen(JSON_CONTENT_TYPE));
+ TEST_CHECK(ret == 0);
+ if (!TEST_CHECK(c != NULL)) {
+ TEST_MSG("splunk_client failed");
+ exit(EXIT_FAILURE);
+ }
+
+ ret = flb_http_do(c, &b_sent);
+ if (!TEST_CHECK(ret == 0)) {
+ TEST_MSG("ret error. ret=%d\n", ret);
+ }
+ else if (!TEST_CHECK(b_sent > 0)){
+ TEST_MSG("b_sent size error. b_sent = %lu\n", b_sent);
+ }
+ else if (!TEST_CHECK(c->resp.status == 200)) {
+ TEST_MSG("http response code error. expect: 200, got: %d\n", c->resp.status);
+ }
+
+ /* waiting to flush */
+ flb_time_msleep(1500);
+
+ num = get_output_num();
+ if (!TEST_CHECK(num > 0)) {
+ TEST_MSG("no outputs");
+ }
+ flb_http_client_destroy(c);
+ flb_upstream_conn_release(ctx->httpc->u_conn);
+ test_ctx_destroy(ctx);
+}
+
+void flb_test_splunk_collector_raw_multilines()
+{
+ flb_test_splunk_raw_multilines(8812);
+}
+
+void flb_test_splunk_tag_key()
+{
+ struct flb_lib_out_cb cb_data;
+ struct test_ctx *ctx;
+ struct flb_http_client *c;
+ int ret;
+ int num;
+ size_t b_sent;
+ int port = 8812;
+ char sport[16];
+
+ char *buf = "{\"event\": \"Pony 1 has left the barn\",\"tag\":\"new_tag\"}";
+
+ snprintf(sport, 16, "%d", port);
+
+ clear_output_num();
+
+ cb_data.cb = cb_check_result_json;
+ cb_data.data = "\"event\":\"Pony 1 has left the barn\",\"tag\":\"new_tag\"";
+
+ ctx = test_ctx_create(&cb_data);
+ if (!TEST_CHECK(ctx != NULL)) {
+ TEST_MSG("test_ctx_create failed");
+ exit(EXIT_FAILURE);
+ }
+
+ ret = flb_input_set(ctx->flb, ctx->i_ffd,
+ "port", sport,
+ "tag_key", "tag",
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ ret = flb_output_set(ctx->flb, ctx->o_ffd,
+ "match", "new_tag",
+ "format", "json",
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ /* Start the engine */
+ ret = flb_start(ctx->flb);
+ TEST_CHECK(ret == 0);
+
+ ctx->httpc = splunk_client_ctx_create(port);
+ TEST_CHECK(ctx->httpc != NULL);
+
+ c = flb_http_client(ctx->httpc->u_conn, FLB_HTTP_POST, "/services/collector", buf, strlen(buf),
+ "127.0.0.1", port, NULL, 0);
+ ret = flb_http_add_header(c, FLB_HTTP_HEADER_CONTENT_TYPE, strlen(FLB_HTTP_HEADER_CONTENT_TYPE),
+ JSON_CONTENT_TYPE, strlen(JSON_CONTENT_TYPE));
+ TEST_CHECK(ret == 0);
+ if (!TEST_CHECK(c != NULL)) {
+ TEST_MSG("splunk_client failed");
+ exit(EXIT_FAILURE);
+ }
+
+ ret = flb_http_do(c, &b_sent);
+ if (!TEST_CHECK(ret == 0)) {
+ TEST_MSG("ret error. ret=%d\n", ret);
+ }
+ else if (!TEST_CHECK(b_sent > 0)){
+ TEST_MSG("b_sent size error. b_sent = %lu\n", b_sent);
+ }
+ else if (!TEST_CHECK(c->resp.status == 200)) {
+ TEST_MSG("http response code error. expect: 200, got: %d\n", c->resp.status);
+ }
+
+ /* waiting to flush */
+ flb_time_msleep(1500);
+
+ num = get_output_num();
+ if (!TEST_CHECK(num > 0)) {
+ TEST_MSG("no outputs");
+ }
+ flb_http_client_destroy(c);
+ flb_upstream_conn_release(ctx->httpc->u_conn);
+ test_ctx_destroy(ctx);
+}
+
+void flb_test_splunk_gzip(int port, char *endpoint)
+{
+ struct flb_lib_out_cb cb_data;
+ struct test_ctx *ctx;
+ struct flb_http_client *c;
+ int ret;
+ int num;
+ size_t b_sent;
+ char *buf = "{\"event\": \"Pony 1 has left the barn\"}{\"event\": \"Pony 2 has left the barn\"}{\"event\": \"Pony 3 has left the barn\", \"nested\": {\"key1\": \"value1\"}}";
+ char *expected = "\"event\":";
+ char sport[16];
+ flb_sds_t target;
+ void *final_data;
+ size_t final_bytes;
+
+ target = flb_sds_create_size(64);
+ flb_sds_cat(target, endpoint, strlen(endpoint));
+
+ snprintf(sport, 16, "%d", port);
+
+ clear_output_num();
+
+ cb_data.cb = cb_check_result_json;
+ cb_data.data = expected;
+
+ ctx = test_ctx_create(&cb_data);
+ if (!TEST_CHECK(ctx != NULL)) {
+ TEST_MSG("test_ctx_create failed");
+ exit(EXIT_FAILURE);
+ }
+
+ ret = flb_input_set(ctx->flb, ctx->i_ffd,
+ "port", sport,
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ ret = flb_output_set(ctx->flb, ctx->o_ffd,
+ "match", "*",
+ "format", "json",
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ /* Start the engine */
+ ret = flb_start(ctx->flb);
+ TEST_CHECK(ret == 0);
+
+ ctx->httpc = splunk_client_ctx_create(port);
+ TEST_CHECK(ctx->httpc != NULL);
+
+ ret = flb_gzip_compress((void *) buf, strlen(buf), &final_data, &final_bytes);
+ TEST_CHECK(ret != -1);
+
+ c = flb_http_client(ctx->httpc->u_conn, FLB_HTTP_POST, target, final_data, final_bytes,
+ "127.0.0.1", port, NULL, 0);
+ ret = flb_http_add_header(c, FLB_HTTP_HEADER_CONTENT_TYPE, strlen(FLB_HTTP_HEADER_CONTENT_TYPE),
+ JSON_CONTENT_TYPE, strlen(JSON_CONTENT_TYPE));
+ TEST_CHECK(ret == 0);
+ /* Add Content-Encoding: gzip */
+ ret = flb_http_add_header(c, "Content-Encoding", 16, "gzip", 4);
+ TEST_CHECK(ret == 0);
+
+ if (!TEST_CHECK(c != NULL)) {
+ TEST_MSG("splunk_client failed");
+ exit(EXIT_FAILURE);
+ }
+
+ ret = flb_http_do(c, &b_sent);
+ if (!TEST_CHECK(ret == 0)) {
+ TEST_MSG("ret error. ret=%d\n", ret);
+ }
+ else if (!TEST_CHECK(b_sent > 0)){
+ TEST_MSG("b_sent size error. b_sent = %lu\n", b_sent);
+ }
+ else if (!TEST_CHECK(c->resp.status == 200)) {
+ TEST_MSG("http response code error. expect: 200, got: %d\n", c->resp.status);
+ }
+ flb_free(final_data);
+
+ /* waiting to flush */
+ flb_time_msleep(1500);
+
+ num = get_output_num();
+ if (!TEST_CHECK(num > 0)) {
+ TEST_MSG("no outputs");
+ }
+ flb_sds_destroy(target);
+ flb_http_client_destroy(c);
+ flb_upstream_conn_release(ctx->httpc->u_conn);
+ test_ctx_destroy(ctx);
+}
+
+void flb_test_splunk_collector_gzip()
+{
+ flb_test_splunk_gzip(8813, "/services/collector");
+}
+
+void flb_test_splunk_collector_event_gzip()
+{
+ flb_test_splunk_gzip(8814, "/services/collector/event");
+}
+
+void flb_test_splunk_raw_multilines_gzip(int port)
+{
+ struct flb_lib_out_cb cb_data;
+ struct test_ctx *ctx;
+ struct flb_http_client *c;
+ int ret;
+ int num;
+ size_t b_sent;
+ char *buf = "127.0.0.1 - admin [28/Sep/2016:09:05:26.875 -0700] \"GET /servicesNS/admin/launcher/data/ui/views?count=-1 HTTP/1.0\" 200 126721 - - - 6ms" \
+ "127.0.0.1 - admin [28/Sep/2016:09:05:26.917 -0700] \"GET /servicesNS/admin/launcher/data/ui/nav/default HTTP/1.0\" 200 4367 - - - 6ms" \
+ "127.0.0.1 - admin [28/Sep/2016:09:05:26.941 -0700] \"GET /services/apps/local?search=disabled%3Dfalse&count=-1 HTTP/1.0\" 200 31930 - - - 4ms" \
+ "127.0.0.1 - admin [28/Sep/2016:09:05:26.954 -0700] \"GET /services/apps/local?search=disabled%3Dfalse&count=-1 HTTP/1.0\" 200 31930 - - - 3ms" \
+ "127.0.0.1 - admin [28/Sep/2016:09:05:26.968 -0700] \"GET /servicesNS/admin/launcher/data/ui/views?digest=1&count=-1 HTTP/1.0\" 200 58672 - - - 5ms";
+ char *expected = "\"log\":";
+ char sport[16];
+ void *final_data;
+ size_t final_bytes;
+
+ snprintf(sport, 16, "%d", port);
+
+ clear_output_num();
+
+ cb_data.cb = cb_check_result_json;
+ cb_data.data = expected;
+
+ ctx = test_ctx_create(&cb_data);
+ if (!TEST_CHECK(ctx != NULL)) {
+ TEST_MSG("test_ctx_create failed");
+ exit(EXIT_FAILURE);
+ }
+
+ ret = flb_input_set(ctx->flb, ctx->i_ffd,
+ "port", sport,
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ ret = flb_output_set(ctx->flb, ctx->o_ffd,
+ "match", "*",
+ "format", "json",
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ /* Start the engine */
+ ret = flb_start(ctx->flb);
+ TEST_CHECK(ret == 0);
+
+ ctx->httpc = splunk_client_ctx_create(port);
+ TEST_CHECK(ctx->httpc != NULL);
+
+ ret = flb_gzip_compress((void *) buf, strlen(buf), &final_data, &final_bytes);
+ TEST_CHECK(ret != -1);
+
+ c = flb_http_client(ctx->httpc->u_conn, FLB_HTTP_POST, "/services/collector/raw", final_data, final_bytes,
+ "127.0.0.1", port, NULL, 0);
+ ret = flb_http_add_header(c, FLB_HTTP_HEADER_CONTENT_TYPE, strlen(FLB_HTTP_HEADER_CONTENT_TYPE),
+ JSON_CONTENT_TYPE, strlen(JSON_CONTENT_TYPE));
+ /* Add Content-Encoding: gzip */
+ ret = flb_http_add_header(c, "Content-Encoding", 16, "gzip", 4);
+ TEST_CHECK(ret == 0);
+
+ TEST_CHECK(ret == 0);
+ if (!TEST_CHECK(c != NULL)) {
+ TEST_MSG("splunk_client failed");
+ exit(EXIT_FAILURE);
+ }
+
+ ret = flb_http_do(c, &b_sent);
+ if (!TEST_CHECK(ret == 0)) {
+ TEST_MSG("ret error. ret=%d\n", ret);
+ }
+ else if (!TEST_CHECK(b_sent > 0)){
+ TEST_MSG("b_sent size error. b_sent = %lu\n", b_sent);
+ }
+ else if (!TEST_CHECK(c->resp.status == 200)) {
+ TEST_MSG("http response code error. expect: 200, got: %d\n", c->resp.status);
+ }
+ flb_free(final_data);
+
+ /* waiting to flush */
+ flb_time_msleep(1500);
+
+ num = get_output_num();
+ if (!TEST_CHECK(num > 0)) {
+ TEST_MSG("no outputs");
+ }
+ flb_http_client_destroy(c);
+ flb_upstream_conn_release(ctx->httpc->u_conn);
+ test_ctx_destroy(ctx);
+}
+
+void flb_test_splunk_collector_raw_multilines_gzip()
+{
+ flb_test_splunk_raw_multilines_gzip(8815);
+}
+
+TEST_LIST = {
+ {"health", flb_test_splunk_health},
+ {"collector", flb_test_splunk_collector},
+ {"collector_event", flb_test_splunk_collector_event},
+ {"collector_raw", flb_test_splunk_collector_raw},
+ {"collector_raw_multilines", flb_test_splunk_collector_raw_multilines},
+ {"collector_gzip", flb_test_splunk_collector_gzip},
+ {"collector_event_gzip", flb_test_splunk_collector_event_gzip},
+ {"collector_raw_multilines_gzip", flb_test_splunk_collector_raw_multilines_gzip},
+ {"tag_key", flb_test_splunk_tag_key},
+ {NULL, NULL}
+};
diff --git a/fluent-bit/tests/runtime/in_statsd.c b/fluent-bit/tests/runtime/in_statsd.c
new file mode 100644
index 00000000..1ae9d6cf
--- /dev/null
+++ b/fluent-bit/tests/runtime/in_statsd.c
@@ -0,0 +1,324 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/* Fluent Bit
+ * ==========
+ * Copyright (C) 2019-2022 The Fluent Bit Authors
+ * Copyright (C) 2015-2018 Treasure Data Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <fluent-bit.h>
+#include <fluent-bit/flb_compat.h>
+#include <fluent-bit/flb_time.h>
+#include <fluent-bit/flb_socket.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include "flb_tests_runtime.h"
+
+struct test_ctx {
+ flb_ctx_t *flb; /* Fluent Bit library context */
+ int i_ffd; /* Input fd */
+ int f_ffd; /* Filter fd (unused) */
+ int o_ffd; /* Output fd */
+};
+
+
+pthread_mutex_t result_mutex = PTHREAD_MUTEX_INITIALIZER;
+int num_output = 0;
+static int get_output_num()
+{
+ int ret;
+ pthread_mutex_lock(&result_mutex);
+ ret = num_output;
+ pthread_mutex_unlock(&result_mutex);
+
+ return ret;
+}
+
+static void set_output_num(int num)
+{
+ pthread_mutex_lock(&result_mutex);
+ num_output = num;
+ pthread_mutex_unlock(&result_mutex);
+}
+
+static void clear_output_num()
+{
+ set_output_num(0);
+}
+
+struct str_list {
+ size_t size;
+ char **lists;
+};
+
+/* Callback to check expected results */
+static int cb_check_json_str_list(void *record, size_t size, void *data)
+{
+ char *p;
+ char *result;
+ int num = get_output_num();
+ size_t i;
+ struct str_list *l = (struct str_list*)data;
+
+ if (!TEST_CHECK(l != NULL)) {
+ flb_error("Data is NULL");
+ flb_free(record);
+ return 0;
+ }
+
+
+ set_output_num(num+1);
+
+ result = (char *) record;
+
+ for (i=0; i<l->size; i++) {
+ p = strstr(result, l->lists[i]);
+ if(!TEST_CHECK(p != NULL)) {
+ flb_error("Expected to find: '%s' in result '%s'",
+ l->lists[i], result);
+ }
+ }
+
+ /*
+ * If you want to debug your test
+ *
+ * printf("Expect: '%s' in result '%s'", expected, result);
+ */
+ flb_free(record);
+ return 0;
+}
+
+static struct test_ctx *test_ctx_create(struct flb_lib_out_cb *data)
+{
+ int i_ffd;
+ int o_ffd;
+ struct test_ctx *ctx = NULL;
+
+ ctx = flb_malloc(sizeof(struct test_ctx));
+ if (!TEST_CHECK(ctx != NULL)) {
+ TEST_MSG("malloc failed");
+ flb_errno();
+ return NULL;
+ }
+
+ /* Service config */
+ ctx->flb = flb_create();
+ flb_service_set(ctx->flb,
+ "Flush", "0.200000000",
+ "Grace", "1",
+ "Log_Level", "error",
+ NULL);
+
+ /* Input */
+ i_ffd = flb_input(ctx->flb, (char *) "statsd", NULL);
+ TEST_CHECK(i_ffd >= 0);
+ ctx->i_ffd = i_ffd;
+
+ /* Output */
+ o_ffd = flb_output(ctx->flb, (char *) "lib", (void *) data);
+ ctx->o_ffd = o_ffd;
+
+ return ctx;
+}
+
+static void test_ctx_destroy(struct test_ctx *ctx)
+{
+ TEST_CHECK(ctx != NULL);
+
+ sleep(1);
+ flb_stop(ctx->flb);
+ flb_destroy(ctx->flb);
+ flb_free(ctx);
+}
+
+#define DEFAULT_HOST "127.0.0.1"
+#define DEFAULT_PORT 8125
+static int init_udp(char *in_host, int in_port, struct sockaddr_in *addr)
+{
+ int port = in_port;
+ char *host = in_host;
+ flb_sockfd_t fd;
+
+ if (host == NULL) {
+ host = DEFAULT_HOST;
+ }
+ if (port < 0) {
+ port = DEFAULT_PORT;
+ }
+
+ memset(addr, 0, sizeof(struct sockaddr_in));
+ fd = socket(AF_INET, SOCK_DGRAM, 0);
+ if (!TEST_CHECK(fd >= 0)) {
+ TEST_MSG("failed to socket. host=%s port=%d errno=%d", host, port, errno);
+ return -1;
+ }
+
+ addr->sin_family = AF_INET;
+ addr->sin_addr.s_addr = inet_addr(host);
+ addr->sin_port = htons(port);
+
+ return fd;
+}
+
+static int test_normal(char *payload, struct str_list *expected)
+{
+ struct flb_lib_out_cb cb_data;
+ struct test_ctx *ctx;
+ struct sockaddr_in addr;
+ int fd;
+ int ret;
+ int num;
+ ssize_t w_size;
+
+ size_t size;
+
+ if (!TEST_CHECK(payload != NULL && expected != NULL)) {
+ TEST_MSG("input is NULL");
+ return -1;
+ }
+ size = strlen(payload);
+
+ clear_output_num();
+
+ cb_data.cb = cb_check_json_str_list;
+ cb_data.data = expected;
+
+ ctx = test_ctx_create(&cb_data);
+ if (!TEST_CHECK(ctx != NULL)) {
+ TEST_MSG("test_ctx_create failed");
+ exit(EXIT_FAILURE);
+ }
+
+ ret = flb_output_set(ctx->flb, ctx->o_ffd,
+ "format", "json",
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ /* Start the engine */
+ ret = flb_start(ctx->flb);
+ TEST_CHECK(ret == 0);
+
+ /* use default host/port */
+ fd = init_udp(NULL, -1, &addr);
+ if (!TEST_CHECK(fd >= 0)) {
+ exit(EXIT_FAILURE);
+ }
+
+ w_size = sendto(fd, payload, size, 0, (const struct sockaddr *)&addr, sizeof(addr));
+ if (!TEST_CHECK(w_size == size)) {
+ TEST_MSG("failed to send, errno=%d", errno);
+ flb_socket_close(fd);
+ exit(EXIT_FAILURE);
+ }
+
+ /* waiting to flush */
+ flb_time_msleep(500);
+
+ num = get_output_num();
+ if (!TEST_CHECK(num > 0)) {
+ TEST_MSG("no outputs");
+ }
+
+ flb_socket_close(fd);
+ test_ctx_destroy(ctx);
+
+ return 0;
+}
+
+void flb_test_statsd_count()
+{
+ char *expected_strs[] = {"\"bucket\":\"gorets\"", "\"type\":\"counter\"", "\"value\":1"};
+ struct str_list expected = {
+ .size = sizeof(expected_strs)/sizeof(char*),
+ .lists = &expected_strs[0],
+ };
+
+ char *buf = "gorets:1|c";
+ int ret;
+
+ ret = test_normal(buf, &expected);
+ if (!TEST_CHECK(ret == 0)) {
+ TEST_MSG("test failed");
+ exit(EXIT_FAILURE);
+ }
+
+}
+
+void flb_test_statsd_sample()
+{
+ char *expected_strs[] = {"\"bucket\":\"gorets\"", "\"type\":\"counter\"",
+ "\"value\":1", "\"sample_rate\":0.1"};
+ struct str_list expected = {
+ .size = sizeof(expected_strs)/sizeof(char*),
+ .lists = &expected_strs[0],
+ };
+
+ char *buf = "gorets:1|c|@0.1";
+ int ret;
+
+ ret = test_normal(buf, &expected);
+ if (!TEST_CHECK(ret == 0)) {
+ TEST_MSG("test failed");
+ exit(EXIT_FAILURE);
+ }
+}
+
+void flb_test_statsd_gauge()
+{
+ char *expected_strs[] = {"\"type\":\"gauge\"","\"bucket\":\"gaugor\"",
+ "\"value\":333"};
+ struct str_list expected = {
+ .size = sizeof(expected_strs)/sizeof(char*),
+ .lists = &expected_strs[0],
+ };
+
+ char *buf = "gaugor:333|g";
+ int ret;
+
+ ret = test_normal(buf, &expected);
+ if (!TEST_CHECK(ret == 0)) {
+ TEST_MSG("test failed");
+ exit(EXIT_FAILURE);
+ }
+}
+
+void flb_test_statsd_set()
+{
+ char *expected_strs[] = {"\"bucket\":\"uniques\"", "\"type\":\"set\"",
+ "\"value\":\"765\""};
+ struct str_list expected = {
+ .size = sizeof(expected_strs)/sizeof(char*),
+ .lists = &expected_strs[0],
+ };
+
+ char *buf = "uniques:765|s";
+ int ret;
+
+ ret = test_normal(buf, &expected);
+ if (!TEST_CHECK(ret == 0)) {
+ TEST_MSG("test failed");
+ exit(EXIT_FAILURE);
+ }
+}
+
+TEST_LIST = {
+ {"count", flb_test_statsd_count},
+ {"sample", flb_test_statsd_sample},
+ {"gauge", flb_test_statsd_gauge},
+ {"set", flb_test_statsd_set},
+ {NULL, NULL}
+};
+
diff --git a/fluent-bit/tests/runtime/in_syslog.c b/fluent-bit/tests/runtime/in_syslog.c
new file mode 100644
index 00000000..6935c7a3
--- /dev/null
+++ b/fluent-bit/tests/runtime/in_syslog.c
@@ -0,0 +1,1023 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/* Fluent Bit
+ * ==========
+ * Copyright (C) 2019-2022 The Fluent Bit Authors
+ * Copyright (C) 2015-2018 Treasure Data Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <fluent-bit.h>
+#include <fluent-bit/flb_compat.h>
+#include <fluent-bit/flb_time.h>
+#include <fluent-bit/flb_socket.h>
+#include <fluent-bit/flb_parser.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#ifdef FLB_HAVE_UNIX_SOCKET
+#include <sys/socket.h>
+#include <sys/un.h>
+#endif
+#include <fcntl.h>
+#include "flb_tests_runtime.h"
+
+#define DPATH FLB_TESTS_DATA_PATH "/data/common"
+
+/* Examples from RFCs */
+#define RFC5424_EXAMPLE_1 "<34>1 2003-10-11T22:14:15.003Z mymachine.example.com su - ID47 - 'su root' failed for lonvick on /dev/pts/8\n"
+#define RFC3164_EXAMPLE_1 "<34>Oct 11 22:14:15 mymachine su: 'su root' failed for lonvick on /dev/pts/8\n"
+
+char *RFC5424_EXPECTED_STRS_1[] = {"\"pri\":\"34\"", "\"message\":\"'su root' failed for lonvick on /dev/pts/8\"",
+ "\"host\":\"mymachine.example.com\"", "\"msgid\":\"ID47\"","\"time\":\"2003-10-11T22:14:15.003Z\"",
+ "\"ident\":\"su\""
+};
+
+char *RFC5424_EXPECTED_STRS_TCP[] = {"\"pri\":\"34\"", "\"message\":\"'su root' failed for lonvick on /dev/pts/8\"",
+ "\"host\":\"mymachine.example.com\"", "\"msgid\":\"ID47\"","\"time\":\"2003-10-11T22:14:15.003Z\"",
+ "\"ident\":\"su\"",
+ "\"source_host\":\"tcp://"
+};
+
+char *RFC5424_EXPECTED_STRS_UDP[] = {"\"pri\":\"34\"", "\"message\":\"'su root' failed for lonvick on /dev/pts/8\"",
+ "\"host\":\"mymachine.example.com\"", "\"msgid\":\"ID47\"","\"time\":\"2003-10-11T22:14:15.003Z\"",
+ "\"ident\":\"su\"",
+ "\"source_host\":\"udp://"
+};
+
+char *RFC3164_EXPECTED_STRS_1[] = {"\"pri\":\"34\"", "\"message\":\"'su root' failed for lonvick on /dev/pts/8\"",
+ "\"host\":\"mymachine\"", "\"time\":\"Oct 11 22:14:15\"", "\"ident\":\"su\""
+};
+
+
+struct test_ctx {
+ flb_ctx_t *flb; /* Fluent Bit library context */
+ int i_ffd; /* Input fd */
+ int f_ffd; /* Filter fd (unused) */
+ int o_ffd; /* Output fd */
+};
+
+
+pthread_mutex_t result_mutex = PTHREAD_MUTEX_INITIALIZER;
+int num_output = 0;
+static int get_output_num()
+{
+ int ret;
+ pthread_mutex_lock(&result_mutex);
+ ret = num_output;
+ pthread_mutex_unlock(&result_mutex);
+
+ return ret;
+}
+
+static void set_output_num(int num)
+{
+ pthread_mutex_lock(&result_mutex);
+ num_output = num;
+ pthread_mutex_unlock(&result_mutex);
+}
+
+static void clear_output_num()
+{
+ set_output_num(0);
+}
+
+struct str_list {
+ size_t size;
+ char **lists;
+};
+
+/* Callback to check expected results */
+static int cb_check_json_str_list(void *record, size_t size, void *data)
+{
+ char *p;
+ char *result;
+ int num = get_output_num();
+ size_t i;
+ struct str_list *l = (struct str_list*)data;
+
+ if (!TEST_CHECK(l != NULL)) {
+ flb_error("Data is NULL");
+ flb_free(record);
+ return 0;
+ }
+ set_output_num(num+1);
+
+ result = (char *) record;
+
+ for (i=0; i<l->size; i++) {
+ p = strstr(result, l->lists[i]);
+ if(!TEST_CHECK(p != NULL)) {
+ flb_error("Expected to find: '%s' in result '%s'",
+ l->lists[i], result);
+ }
+ }
+ flb_free(record);
+ return 0;
+}
+
+static struct test_ctx *test_ctx_create(struct flb_lib_out_cb *data)
+{
+ int i_ffd;
+ int o_ffd;
+ int ret;
+ struct test_ctx *ctx = NULL;
+
+ ctx = flb_malloc(sizeof(struct test_ctx));
+ if (!TEST_CHECK(ctx != NULL)) {
+ TEST_MSG("malloc failed");
+ flb_errno();
+ return NULL;
+ }
+
+ /* Service config */
+ ctx->flb = flb_create();
+ flb_service_set(ctx->flb,
+ "Flush", "0.200000000",
+ "Grace", "1",
+ "Log_Level", "error",
+ "Parsers_File", DPATH "/parsers.conf",
+ NULL);
+
+ /* Input */
+ i_ffd = flb_input(ctx->flb, (char *) "syslog", NULL);
+ TEST_CHECK(i_ffd >= 0);
+ ctx->i_ffd = i_ffd;
+
+ /* Output */
+ o_ffd = flb_output(ctx->flb, (char *) "lib", (void *) data);
+ ctx->o_ffd = o_ffd;
+ ret = flb_output_set(ctx->flb, ctx->o_ffd,
+ "format", "json",
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ return ctx;
+}
+
+#define PARSER_NAME_RFC5424 "syslog-rfc5424"
+#define PARSER_NAME_RFC3164 "syslog-rfc3164"
+
+static void test_ctx_destroy(struct test_ctx *ctx)
+{
+ TEST_CHECK(ctx != NULL);
+
+ sleep(1);
+ flb_stop(ctx->flb);
+ flb_destroy(ctx->flb);
+ flb_free(ctx);
+}
+
+#define DEFAULT_HOST "127.0.0.1"
+#define DEFAULT_PORT 5140
+static flb_sockfd_t connect_tcp(char *in_host, int in_port)
+{
+ int port = in_port;
+ char *host = in_host;
+ flb_sockfd_t fd;
+ int ret;
+ struct sockaddr_in addr;
+
+ if (host == NULL) {
+ host = DEFAULT_HOST;
+ }
+ if (port < 0) {
+ port = DEFAULT_PORT;
+ }
+
+ memset(&addr, 0, sizeof(addr));
+ fd = socket(PF_INET, SOCK_STREAM, 0);
+ if (!TEST_CHECK(fd >= 0)) {
+ TEST_MSG("failed to socket. host=%s port=%d errno=%d", host, port, errno);
+ return -1;
+ }
+
+ addr.sin_family = AF_INET;
+ addr.sin_addr.s_addr = inet_addr(host);
+ addr.sin_port = htons(port);
+
+ ret = connect(fd, (const struct sockaddr *)&addr, sizeof(addr));
+ if (!TEST_CHECK(ret >= 0)) {
+ TEST_MSG("failed to connect. host=%s port=%d errno=%d", host, port, errno);
+ flb_socket_close(fd);
+ return -1;
+ }
+ return fd;
+}
+
+#ifdef FLB_HAVE_UNIX_SOCKET
+static flb_sockfd_t connect_tcp_unix(char *path)
+{
+ flb_sockfd_t fd;
+ struct sockaddr_un sun;
+ int ret;
+
+ if (!TEST_CHECK(path != NULL)) {
+ TEST_MSG("path is NULL");
+ return -1;
+ }
+
+ memset(&sun, 0, sizeof(sun));
+ fd = socket(AF_LOCAL, SOCK_STREAM, 0);
+ if (!TEST_CHECK(fd >= 0)) {
+ TEST_MSG("failed to socket. path=%s errno=%d", path, errno);
+ return -1;
+ }
+
+ sun.sun_family = AF_LOCAL;
+ strcpy(sun.sun_path, path);
+ ret = connect(fd, (const struct sockaddr *)&sun, sizeof(sun));
+ if (!TEST_CHECK(ret >= 0)) {
+ TEST_MSG("failed to connect. path=%s errno=%d", path, errno);
+ flb_socket_close(fd);
+ return -1;
+ }
+ return fd;
+}
+
+static flb_sockfd_t init_udp_unix(char *path, struct sockaddr_un *sun)
+{
+ flb_sockfd_t fd;
+ int ret;
+
+ if (!TEST_CHECK(path != NULL)) {
+ TEST_MSG("path is NULL");
+ return -1;
+ }
+ if (!TEST_CHECK(sun != NULL)) {
+ TEST_MSG("sun is NULL");
+ return -1;
+ }
+
+ memset(sun, 0, sizeof(struct sockaddr_un));
+ fd = socket(AF_LOCAL, SOCK_DGRAM, 0);
+ if (!TEST_CHECK(fd >= 0)) {
+ TEST_MSG("failed to socket. path=%s errno=%d", path, errno);
+ return -1;
+ }
+
+ sun->sun_family = AF_LOCAL;
+ strcpy(sun->sun_path, path);
+ ret = connect(fd, (const struct sockaddr *)sun, sizeof(struct sockaddr_un));
+ if (!TEST_CHECK(ret >= 0)) {
+ TEST_MSG("failed to connect. path=%s errno=%d", path, errno);
+ flb_socket_close(fd);
+ return -1;
+ }
+ return fd;
+}
+#endif
+
+static int init_udp(char *in_host, int in_port, struct sockaddr_in *addr)
+{
+ int port = in_port;
+ char *host = in_host;
+ flb_sockfd_t fd;
+
+ if (host == NULL) {
+ host = DEFAULT_HOST;
+ }
+ if (port < 0) {
+ port = DEFAULT_PORT;
+ }
+
+ memset(addr, 0, sizeof(struct sockaddr_in));
+ fd = socket(AF_INET, SOCK_DGRAM, 0);
+ if (!TEST_CHECK(fd >= 0)) {
+ TEST_MSG("failed to socket. host=%s port=%d errno=%d", host, port, errno);
+ return -1;
+ }
+
+ addr->sin_family = AF_INET;
+ addr->sin_addr.s_addr = inet_addr(host);
+ addr->sin_port = htons(port);
+
+ return fd;
+}
+
+void flb_test_syslog_tcp()
+{
+ struct flb_lib_out_cb cb_data;
+ struct test_ctx *ctx;
+ flb_sockfd_t fd;
+ int ret;
+ int num;
+ ssize_t w_size;
+
+ struct str_list expected = {
+ .size = sizeof(RFC5424_EXPECTED_STRS_1)/sizeof(char*),
+ .lists = &RFC5424_EXPECTED_STRS_1[0],
+ };
+
+ char *buf = RFC5424_EXAMPLE_1;
+ size_t size = strlen(buf);
+
+ clear_output_num();
+
+ cb_data.cb = cb_check_json_str_list;
+ cb_data.data = &expected;
+
+ ctx = test_ctx_create(&cb_data);
+ if (!TEST_CHECK(ctx != NULL)) {
+ TEST_MSG("test_ctx_create failed");
+ exit(EXIT_FAILURE);
+ }
+
+ ret = flb_input_set(ctx->flb, ctx->i_ffd,
+ "mode", "tcp",
+ "parser", PARSER_NAME_RFC5424,
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ /* Start the engine */
+ ret = flb_start(ctx->flb);
+ TEST_CHECK(ret == 0);
+
+ /* use default host/port */
+ fd = connect_tcp(NULL, -1);
+ if (!TEST_CHECK(fd >= 0)) {
+ test_ctx_destroy(ctx);
+ exit(EXIT_FAILURE);
+ }
+
+ w_size = send(fd, buf, size, 0);
+ if (!TEST_CHECK(w_size == size)) {
+ TEST_MSG("failed to send, errno=%d", errno);
+ flb_socket_close(fd);
+ test_ctx_destroy(ctx);
+ exit(EXIT_FAILURE);
+ }
+
+ /* waiting to flush */
+ flb_time_msleep(500);
+
+ num = get_output_num();
+ if (!TEST_CHECK(num > 0)) {
+ TEST_MSG("no outputs");
+ }
+
+ flb_socket_close(fd);
+ test_ctx_destroy(ctx);
+}
+
+void flb_test_syslog_tcp_port()
+{
+ struct flb_lib_out_cb cb_data;
+ struct test_ctx *ctx;
+ flb_sockfd_t fd;
+ int ret;
+ int num;
+ ssize_t w_size;
+ char *port = "15140";
+
+ struct str_list expected = {
+ .size = sizeof(RFC5424_EXPECTED_STRS_1)/sizeof(char*),
+ .lists = &RFC5424_EXPECTED_STRS_1[0],
+ };
+
+ char *buf = RFC5424_EXAMPLE_1;
+ size_t size = strlen(buf);
+
+ clear_output_num();
+
+ cb_data.cb = cb_check_json_str_list;
+ cb_data.data = &expected;
+
+ ctx = test_ctx_create(&cb_data);
+ if (!TEST_CHECK(ctx != NULL)) {
+ TEST_MSG("test_ctx_create failed");
+ exit(EXIT_FAILURE);
+ }
+
+ ret = flb_input_set(ctx->flb, ctx->i_ffd,
+ "mode", "tcp",
+ "Port", port,
+ "parser", PARSER_NAME_RFC5424,
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ /* Start the engine */
+ ret = flb_start(ctx->flb);
+ TEST_CHECK(ret == 0);
+
+ /* use default host/port */
+ fd = connect_tcp(NULL, atoi(port));
+ if (!TEST_CHECK(fd >= 0)) {
+ test_ctx_destroy(ctx);
+ exit(EXIT_FAILURE);
+ }
+
+ w_size = send(fd, buf, size, 0);
+ if (!TEST_CHECK(w_size == size)) {
+ TEST_MSG("failed to send, errno=%d", errno);
+ flb_socket_close(fd);
+ test_ctx_destroy(ctx);
+ exit(EXIT_FAILURE);
+ }
+
+ /* waiting to flush */
+ flb_time_msleep(500);
+
+ num = get_output_num();
+ if (!TEST_CHECK(num > 0)) {
+ TEST_MSG("no outputs");
+ }
+
+ flb_socket_close(fd);
+ test_ctx_destroy(ctx);
+}
+
+void flb_test_syslog_tcp_source_address()
+{
+ struct flb_lib_out_cb cb_data;
+ struct test_ctx *ctx;
+ flb_sockfd_t fd;
+ int ret;
+ int num;
+ ssize_t w_size;
+
+ struct str_list expected = {
+ .size = sizeof(RFC5424_EXPECTED_STRS_TCP)/sizeof(char*),
+ .lists = &RFC5424_EXPECTED_STRS_TCP[0],
+ };
+
+ char *buf = RFC5424_EXAMPLE_1;
+ size_t size = strlen(buf);
+
+ clear_output_num();
+
+ cb_data.cb = cb_check_json_str_list;
+ cb_data.data = &expected;
+
+ ctx = test_ctx_create(&cb_data);
+ if (!TEST_CHECK(ctx != NULL)) {
+ TEST_MSG("test_ctx_create failed");
+ exit(EXIT_FAILURE);
+ }
+
+ ret = flb_input_set(ctx->flb, ctx->i_ffd,
+ "mode", "tcp",
+ "source_address_key", "source_host",
+ "parser", PARSER_NAME_RFC5424,
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ /* Start the engine */
+ ret = flb_start(ctx->flb);
+ TEST_CHECK(ret == 0);
+
+ /* use default host/port */
+ fd = connect_tcp(NULL, -1);
+ if (!TEST_CHECK(fd >= 0)) {
+ test_ctx_destroy(ctx);
+ exit(EXIT_FAILURE);
+ }
+
+ w_size = send(fd, buf, size, 0);
+ if (!TEST_CHECK(w_size == size)) {
+ TEST_MSG("failed to send, errno=%d", errno);
+ flb_socket_close(fd);
+ test_ctx_destroy(ctx);
+ exit(EXIT_FAILURE);
+ }
+
+ /* waiting to flush */
+ flb_time_msleep(500);
+
+ num = get_output_num();
+ if (!TEST_CHECK(num > 0)) {
+ TEST_MSG("no outputs");
+ }
+
+ flb_socket_close(fd);
+ test_ctx_destroy(ctx);
+}
+
+void flb_test_syslog_unknown_mode()
+{
+ struct flb_lib_out_cb cb_data;
+ struct test_ctx *ctx;
+ int ret;
+
+ struct str_list expected = {
+ .size = sizeof(RFC5424_EXPECTED_STRS_1)/sizeof(char*),
+ .lists = &RFC5424_EXPECTED_STRS_1[0],
+ };
+
+
+ clear_output_num();
+
+ cb_data.cb = cb_check_json_str_list;
+ cb_data.data = &expected;
+
+ ctx = test_ctx_create(&cb_data);
+ if (!TEST_CHECK(ctx != NULL)) {
+ TEST_MSG("test_ctx_create failed");
+ exit(EXIT_FAILURE);
+ }
+
+ ret = flb_input_set(ctx->flb, ctx->i_ffd,
+ "mode", "UNKNOWN",
+ "parser", PARSER_NAME_RFC5424,
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ /* Start the engine */
+ ret = flb_start(ctx->flb);
+ if(!TEST_CHECK(ret != 0)) {
+ TEST_MSG("flb_start should be failed");
+ test_ctx_destroy(ctx);
+ exit(EXIT_FAILURE);
+ }
+
+ test_ctx_destroy(ctx);
+}
+
+void flb_test_syslog_unix_perm()
+{
+ struct flb_lib_out_cb cb_data;
+ struct test_ctx *ctx;
+ int ret;
+ char *unix_path = "in_syslog_unix";
+ struct stat sb;
+ struct str_list expected = {
+ .size = sizeof(RFC5424_EXPECTED_STRS_1)/sizeof(char*),
+ .lists = &RFC5424_EXPECTED_STRS_1[0],
+ };
+
+
+ clear_output_num();
+
+ cb_data.cb = cb_check_json_str_list;
+ cb_data.data = &expected;
+
+ ctx = test_ctx_create(&cb_data);
+ if (!TEST_CHECK(ctx != NULL)) {
+ TEST_MSG("test_ctx_create failed");
+ exit(EXIT_FAILURE);
+ }
+
+ ret = flb_input_set(ctx->flb, ctx->i_ffd,
+ "path", unix_path,
+ "unix_perm", "0600",
+ "parser", PARSER_NAME_RFC5424,
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ /* Start the engine */
+ ret = flb_start(ctx->flb);
+ TEST_CHECK(ret == 0);
+
+ ret = stat(unix_path, &sb);
+ if (!TEST_CHECK(ret == 0)) {
+ TEST_MSG("stat failed. errno=%d", errno);
+ test_ctx_destroy(ctx);
+ exit(EXIT_FAILURE);
+ }
+
+ if (!TEST_CHECK((sb.st_mode & S_IRWXO) == 0)) {
+ TEST_MSG("Permssion(others) error. val=0x%x",sb.st_mode & S_IRWXO);
+ }
+ if (!TEST_CHECK((sb.st_mode & S_IRWXG) == 0)) {
+ TEST_MSG("Permssion(group) error. val=0x%x",sb.st_mode & S_IRWXG);
+ }
+ if (!TEST_CHECK((sb.st_mode & S_IRWXU) == (S_IRUSR | S_IWUSR))) {
+ TEST_MSG("Permssion(user) error. val=0x%x",sb.st_mode & S_IRWXU);
+ }
+
+ test_ctx_destroy(ctx);
+}
+
+void flb_test_syslog_udp()
+{
+ struct flb_lib_out_cb cb_data;
+ struct test_ctx *ctx;
+ struct sockaddr_in addr;
+ flb_sockfd_t fd;
+ int ret;
+ int num;
+ ssize_t w_size;
+
+ struct str_list expected = {
+ .size = sizeof(RFC5424_EXPECTED_STRS_1)/sizeof(char*),
+ .lists = &RFC5424_EXPECTED_STRS_1[0],
+ };
+
+ char *buf = RFC5424_EXAMPLE_1;
+ size_t size = strlen(buf);
+
+ clear_output_num();
+
+ cb_data.cb = cb_check_json_str_list;
+ cb_data.data = &expected;
+
+ ctx = test_ctx_create(&cb_data);
+ if (!TEST_CHECK(ctx != NULL)) {
+ TEST_MSG("test_ctx_create failed");
+ exit(EXIT_FAILURE);
+ }
+
+ ret = flb_input_set(ctx->flb, ctx->i_ffd,
+ "mode", "udp",
+ "parser", PARSER_NAME_RFC5424,
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ /* Start the engine */
+ ret = flb_start(ctx->flb);
+ TEST_CHECK(ret == 0);
+
+ /* use default host/port */
+ fd = init_udp(NULL, -1, &addr);
+ if (!TEST_CHECK(fd >= 0)) {
+ test_ctx_destroy(ctx);
+ exit(EXIT_FAILURE);
+ }
+
+ w_size = sendto(fd, buf, size, 0, (const struct sockaddr *)&addr, sizeof(addr));
+ if (!TEST_CHECK(w_size == size)) {
+ TEST_MSG("failed to send, errno=%d", errno);
+ flb_socket_close(fd);
+ test_ctx_destroy(ctx);
+ exit(EXIT_FAILURE);
+ }
+
+ /* waiting to flush */
+ flb_time_msleep(500);
+
+ num = get_output_num();
+ if (!TEST_CHECK(num > 0)) {
+ TEST_MSG("no outputs");
+ }
+
+ flb_socket_close(fd);
+ test_ctx_destroy(ctx);
+}
+
+void flb_test_syslog_udp_port()
+{
+ struct flb_lib_out_cb cb_data;
+ struct test_ctx *ctx;
+ struct sockaddr_in addr;
+ flb_sockfd_t fd;
+ int ret;
+ int num;
+ ssize_t w_size;
+ char *port = "15140";
+
+ struct str_list expected = {
+ .size = sizeof(RFC5424_EXPECTED_STRS_1)/sizeof(char*),
+ .lists = &RFC5424_EXPECTED_STRS_1[0],
+ };
+
+ char *buf = RFC5424_EXAMPLE_1;
+ size_t size = strlen(buf);
+
+ clear_output_num();
+
+ cb_data.cb = cb_check_json_str_list;
+ cb_data.data = &expected;
+
+ ctx = test_ctx_create(&cb_data);
+ if (!TEST_CHECK(ctx != NULL)) {
+ TEST_MSG("test_ctx_create failed");
+ exit(EXIT_FAILURE);
+ }
+
+ ret = flb_input_set(ctx->flb, ctx->i_ffd,
+ "mode", "udp",
+ "Port", port,
+ "parser", PARSER_NAME_RFC5424,
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ /* Start the engine */
+ ret = flb_start(ctx->flb);
+ TEST_CHECK(ret == 0);
+
+ /* use default host/port */
+ fd = init_udp(NULL, atoi(port), &addr);
+ if (!TEST_CHECK(fd >= 0)) {
+ test_ctx_destroy(ctx);
+ exit(EXIT_FAILURE);
+ }
+
+ w_size = sendto(fd, buf, size, 0, (const struct sockaddr *)&addr, sizeof(addr));
+ if (!TEST_CHECK(w_size == size)) {
+ TEST_MSG("failed to send, errno=%d", errno);
+ flb_socket_close(fd);
+ test_ctx_destroy(ctx);
+ exit(EXIT_FAILURE);
+ }
+
+ /* waiting to flush */
+ flb_time_msleep(500);
+
+ num = get_output_num();
+ if (!TEST_CHECK(num > 0)) {
+ TEST_MSG("no outputs");
+ }
+
+ flb_socket_close(fd);
+ test_ctx_destroy(ctx);
+}
+
+void flb_test_syslog_udp_source_address()
+{
+ struct flb_lib_out_cb cb_data;
+ struct test_ctx *ctx;
+ struct sockaddr_in addr;
+ flb_sockfd_t fd;
+ int ret;
+ int num;
+ ssize_t w_size;
+
+ struct str_list expected = {
+ .size = sizeof(RFC5424_EXPECTED_STRS_UDP)/sizeof(char*),
+ .lists = &RFC5424_EXPECTED_STRS_UDP[0],
+ };
+
+ char *buf = RFC5424_EXAMPLE_1;
+ size_t size = strlen(buf);
+
+ clear_output_num();
+
+ cb_data.cb = cb_check_json_str_list;
+ cb_data.data = &expected;
+
+ ctx = test_ctx_create(&cb_data);
+ if (!TEST_CHECK(ctx != NULL)) {
+ TEST_MSG("test_ctx_create failed");
+ exit(EXIT_FAILURE);
+ }
+
+ ret = flb_input_set(ctx->flb, ctx->i_ffd,
+ "mode", "udp",
+ "source_address_key", "source_host",
+ "parser", PARSER_NAME_RFC5424,
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ /* Start the engine */
+ ret = flb_start(ctx->flb);
+ TEST_CHECK(ret == 0);
+
+ /* use default host/port */
+ fd = init_udp(NULL, -1, &addr);
+ if (!TEST_CHECK(fd >= 0)) {
+ test_ctx_destroy(ctx);
+ exit(EXIT_FAILURE);
+ }
+
+ w_size = sendto(fd, buf, size, 0, (const struct sockaddr *)&addr, sizeof(addr));
+ if (!TEST_CHECK(w_size == size)) {
+ TEST_MSG("failed to send, errno=%d", errno);
+ flb_socket_close(fd);
+ test_ctx_destroy(ctx);
+ exit(EXIT_FAILURE);
+ }
+
+ /* waiting to flush */
+ flb_time_msleep(500);
+
+ num = get_output_num();
+ if (!TEST_CHECK(num > 0)) {
+ TEST_MSG("no outputs");
+ }
+
+ flb_socket_close(fd);
+ test_ctx_destroy(ctx);
+}
+
+#ifdef FLB_HAVE_UNIX_SOCKET
+void flb_test_syslog_tcp_unix()
+{
+ struct flb_lib_out_cb cb_data;
+ struct test_ctx *ctx;
+ flb_sockfd_t fd;
+ int ret;
+ int num;
+ ssize_t w_size;
+ char *unix_path = "in_syslog_unix";
+
+ struct str_list expected = {
+ .size = sizeof(RFC5424_EXPECTED_STRS_1)/sizeof(char*),
+ .lists = &RFC5424_EXPECTED_STRS_1[0],
+ };
+
+ char *buf = RFC5424_EXAMPLE_1;
+ size_t size = strlen(buf);
+
+ clear_output_num();
+
+ cb_data.cb = cb_check_json_str_list;
+ cb_data.data = &expected;
+
+ ctx = test_ctx_create(&cb_data);
+ if (!TEST_CHECK(ctx != NULL)) {
+ TEST_MSG("test_ctx_create failed");
+ exit(EXIT_FAILURE);
+ }
+
+ ret = flb_input_set(ctx->flb, ctx->i_ffd,
+ "mode", "unix_tcp",
+ "path", unix_path,
+ "parser", PARSER_NAME_RFC5424,
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ /* Start the engine */
+ ret = flb_start(ctx->flb);
+ TEST_CHECK(ret == 0);
+
+ /* use default host/port */
+ fd = connect_tcp_unix(unix_path);
+ if (!TEST_CHECK(fd >= 0)) {
+ test_ctx_destroy(ctx);
+ exit(EXIT_FAILURE);
+ }
+
+ w_size = send(fd, buf, size, 0);
+ if (!TEST_CHECK(w_size == size)) {
+ TEST_MSG("failed to send, errno=%d", errno);
+ flb_socket_close(fd);
+ test_ctx_destroy(ctx);
+ exit(EXIT_FAILURE);
+ }
+
+ /* waiting to flush */
+ flb_time_msleep(500);
+
+ num = get_output_num();
+ if (!TEST_CHECK(num > 0)) {
+ TEST_MSG("no outputs");
+ }
+
+ flb_socket_close(fd);
+ test_ctx_destroy(ctx);
+}
+
+void flb_test_syslog_udp_unix()
+{
+ struct flb_lib_out_cb cb_data;
+ struct test_ctx *ctx;
+ struct sockaddr_un sun;
+ flb_sockfd_t fd;
+ int ret;
+ int num;
+ ssize_t w_size;
+ char *unix_path = "in_syslog_unix";
+
+ struct str_list expected = {
+ .size = sizeof(RFC5424_EXPECTED_STRS_1)/sizeof(char*),
+ .lists = &RFC5424_EXPECTED_STRS_1[0],
+ };
+
+ char *buf = RFC5424_EXAMPLE_1;
+ size_t size = strlen(buf);
+
+ clear_output_num();
+
+ cb_data.cb = cb_check_json_str_list;
+ cb_data.data = &expected;
+
+ ctx = test_ctx_create(&cb_data);
+ if (!TEST_CHECK(ctx != NULL)) {
+ TEST_MSG("test_ctx_create failed");
+ exit(EXIT_FAILURE);
+ }
+
+ ret = flb_input_set(ctx->flb, ctx->i_ffd,
+ "mode", "unix_udp",
+ "path", unix_path,
+ "parser", PARSER_NAME_RFC5424,
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ /* Start the engine */
+ ret = flb_start(ctx->flb);
+ TEST_CHECK(ret == 0);
+
+ fd = init_udp_unix(unix_path, &sun);
+ if (!TEST_CHECK(fd >= 0)) {
+ test_ctx_destroy(ctx);
+ exit(EXIT_FAILURE);
+ }
+
+ w_size = sendto(fd, buf, size, 0, (const struct sockaddr *)&sun, sizeof(sun));
+ if (!TEST_CHECK(w_size == size)) {
+ TEST_MSG("failed to send, errno=%d", errno);
+ flb_socket_close(fd);
+ test_ctx_destroy(ctx);
+ exit(EXIT_FAILURE);
+ }
+
+ /* waiting to flush */
+ flb_time_msleep(500);
+
+ num = get_output_num();
+ if (!TEST_CHECK(num > 0)) {
+ TEST_MSG("no outputs");
+ }
+
+ flb_socket_close(fd);
+ test_ctx_destroy(ctx);
+}
+#endif
+
+void flb_test_syslog_rfc3164()
+{
+ struct flb_lib_out_cb cb_data;
+ struct test_ctx *ctx;
+ flb_sockfd_t fd;
+ int ret;
+ int num;
+ ssize_t w_size;
+
+ struct str_list expected = {
+ .size = sizeof(RFC3164_EXPECTED_STRS_1)/sizeof(char*),
+ .lists = &RFC3164_EXPECTED_STRS_1[0],
+ };
+
+ char *buf = RFC3164_EXAMPLE_1;
+ size_t size = strlen(buf);
+
+ clear_output_num();
+
+ cb_data.cb = cb_check_json_str_list;
+ cb_data.data = &expected;
+
+ ctx = test_ctx_create(&cb_data);
+ if (!TEST_CHECK(ctx != NULL)) {
+ TEST_MSG("test_ctx_create failed");
+ exit(EXIT_FAILURE);
+ }
+
+ ret = flb_input_set(ctx->flb, ctx->i_ffd,
+ "mode", "tcp",
+ "parser", PARSER_NAME_RFC3164,
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ /* Start the engine */
+ ret = flb_start(ctx->flb);
+ TEST_CHECK(ret == 0);
+
+ /* use default host/port */
+ fd = connect_tcp(NULL, -1);
+ if (!TEST_CHECK(fd >= 0)) {
+ test_ctx_destroy(ctx);
+ exit(EXIT_FAILURE);
+ }
+
+ w_size = send(fd, buf, size, 0);
+ if (!TEST_CHECK(w_size == size)) {
+ TEST_MSG("failed to send, errno=%d", errno);
+ flb_socket_close(fd);
+ test_ctx_destroy(ctx);
+ exit(EXIT_FAILURE);
+ }
+
+ /* waiting to flush */
+ flb_time_msleep(500);
+
+ num = get_output_num();
+ if (!TEST_CHECK(num > 0)) {
+ TEST_MSG("no outputs");
+ }
+
+ flb_socket_close(fd);
+ test_ctx_destroy(ctx);
+}
+
+TEST_LIST = {
+ {"syslog_tcp", flb_test_syslog_tcp},
+ {"syslog_udp", flb_test_syslog_udp},
+ {"syslog_tcp_port", flb_test_syslog_tcp_port},
+ {"syslog_tcp_source_address", flb_test_syslog_tcp_source_address},
+ {"syslog_udp_port", flb_test_syslog_udp_port},
+ {"syslog_udp_source_address", flb_test_syslog_udp_source_address},
+ {"syslog_unknown_mode", flb_test_syslog_unknown_mode},
+#ifdef FLB_HAVE_UNIX_SOCKET
+ {"syslog_unix_perm", flb_test_syslog_unix_perm},
+#endif
+ {"syslog_rfc3164", flb_test_syslog_rfc3164},
+#ifdef FLB_HAVE_UNIX_SOCKET
+ {"syslog_tcp_unix", flb_test_syslog_tcp_unix},
+#ifndef FLB_SYSTEM_MACOS
+ {"syslog_udp_unix", flb_test_syslog_udp_unix},
+#endif
+#endif
+ {NULL, NULL}
+};
+
diff --git a/fluent-bit/tests/runtime/in_tail.c b/fluent-bit/tests/runtime/in_tail.c
new file mode 100644
index 00000000..ee5fba88
--- /dev/null
+++ b/fluent-bit/tests/runtime/in_tail.c
@@ -0,0 +1,1583 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/* Fluent Bit
+ * ==========
+ * Copyright (C) 2019-2022 The Fluent Bit Authors
+ * Copyright (C) 2015-2018 Treasure Data Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+Approach for this tests is basing on filter_kubernetes tests
+*/
+
+#include <fluent-bit.h>
+#include <fluent-bit/flb_time.h>
+#include <fluent-bit/flb_pthread.h>
+#include <fluent-bit/flb_compat.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <string.h>
+#include "flb_tests_runtime.h"
+
+#define NEW_LINE "\n"
+#define PATH_SEPARATOR "/"
+
+#define DPATH_COMMON FLB_TESTS_DATA_PATH "/data/common"
+
+#ifdef _WIN32
+ #define TIME_EPSILON_MS 30
+#else
+ #define TIME_EPSILON_MS 10
+#endif
+
+struct test_tail_ctx {
+ flb_ctx_t *flb; /* Fluent Bit library context */
+ int i_ffd; /* Input fd */
+ int o_ffd; /* Output fd */
+ char **filepaths;
+ int *fds;
+ int fd_num;
+};
+
+pthread_mutex_t result_mutex = PTHREAD_MUTEX_INITIALIZER;
+int num_output = 0;
+static int get_output_num()
+{
+ int ret;
+ pthread_mutex_lock(&result_mutex);
+ ret = num_output;
+ pthread_mutex_unlock(&result_mutex);
+
+ return ret;
+}
+
+static void set_output_num(int num)
+{
+ pthread_mutex_lock(&result_mutex);
+ num_output = num;
+ pthread_mutex_unlock(&result_mutex);
+}
+
+static void clear_output_num()
+{
+ set_output_num(0);
+}
+
+static int cb_count_msgpack(void *record, size_t size, void *data)
+{
+ msgpack_unpacked result;
+ size_t off = 0;
+
+ /* Iterate each item array and apply rules */
+ msgpack_unpacked_init(&result);
+ while (msgpack_unpack_next(&result, record, size, &off) == MSGPACK_UNPACK_SUCCESS) {
+ pthread_mutex_lock(&result_mutex);
+ num_output++;
+ /*
+ msgpack_object_print(stdout, result.data);
+ puts(NEW_LINE);
+ */
+ pthread_mutex_unlock(&result_mutex);
+ }
+ msgpack_unpacked_destroy(&result);
+
+ flb_free(record);
+ return 0;
+}
+
+struct str_list {
+ size_t size;
+ char **lists;
+};
+
+/* Callback to check expected results */
+static int cb_check_json_str_list(void *record, size_t size, void *data)
+{
+ char *p;
+ char *result;
+ int num = get_output_num();
+ size_t i;
+ struct str_list *l = (struct str_list*)data;
+
+ if (!TEST_CHECK(l != NULL)) {
+ TEST_MSG("Data is NULL");
+ flb_free(record);
+ return 0;
+ }
+
+
+ set_output_num(num+1);
+
+ result = (char *) record;
+
+ for (i=0; i<l->size; i++) {
+ p = strstr(result, l->lists[i]);
+ if(!TEST_CHECK(p != NULL)) {
+ TEST_MSG("Expected to find: '%s' in result '%s'",
+ l->lists[i], result);
+ }
+ }
+
+ flb_free(record);
+ return 0;
+}
+
+static struct test_tail_ctx *test_tail_ctx_create(struct flb_lib_out_cb *data,
+ char **paths, int path_num, int override)
+{
+ int i_ffd;
+ int o_ffd;
+ int i;
+ int j;
+ int fd;
+ int o_flags;
+ struct test_tail_ctx *ctx = NULL;
+
+ if (!TEST_CHECK(data != NULL)){
+ TEST_MSG("data is NULL");
+ return NULL;
+ }
+
+ ctx = flb_malloc(sizeof(struct test_tail_ctx));
+ if (!TEST_CHECK(ctx != NULL)) {
+ TEST_MSG("malloc failed");
+ flb_errno();
+ return NULL;
+ }
+ ctx->fds = NULL;
+ ctx->filepaths = NULL;
+ ctx->fd_num = path_num;
+
+ /* Service config */
+ ctx->flb = flb_create();
+ flb_service_set(ctx->flb,
+ "Flush", "0.200000000",
+ "Grace", "1",
+ "Log_Level", "info",
+ "Parsers_File", DPATH_COMMON "/parsers.conf",
+ NULL);
+
+ /* Input */
+ i_ffd = flb_input(ctx->flb, (char *) "tail", NULL);
+ TEST_CHECK(i_ffd >= 0);
+ ctx->i_ffd = i_ffd;
+
+ /* Output */
+ o_ffd = flb_output(ctx->flb, (char *) "lib", (void *) data);
+ ctx->o_ffd = o_ffd;
+
+ /* open() flags */
+ o_flags = O_RDWR | O_CREAT;
+
+ if (paths != NULL) {
+ ctx->fds = flb_malloc(sizeof(int) * path_num);
+ ctx->filepaths = paths;
+ if (!TEST_CHECK(ctx->fds != NULL)) {
+ TEST_MSG("malloc failed");
+ flb_destroy(ctx->flb);
+ flb_free(ctx);
+ flb_errno();
+ return NULL;
+ }
+
+ for (i=0; i<path_num; i++) {
+ if (override) {
+ unlink(paths[i]);
+ }
+ else {
+ o_flags |= O_APPEND;
+ }
+
+ fd = open(paths[i], o_flags, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP);
+ if (!TEST_CHECK(fd >= 0)) {
+ TEST_MSG("open failed. errno=%d path[%d]=%s", errno, i, paths[i]);
+ flb_destroy(ctx->flb);
+ for (j=0; j<i; j++) {
+ close(ctx->fds[j]);
+ }
+ flb_free(ctx->fds);
+ flb_free(ctx);
+ flb_errno();
+ return NULL;
+ }
+ ctx->fds[i] = fd;
+ }
+ }
+
+ return ctx;
+}
+
+static void test_tail_ctx_destroy(struct test_tail_ctx *ctx)
+{
+ int i;
+ TEST_CHECK(ctx != NULL);
+
+ if (ctx->fds != NULL) {
+ for (i=0; i <ctx->fd_num; i++) {
+ close(ctx->fds[i]);
+ unlink(ctx->filepaths[i]);
+ }
+ flb_free(ctx->fds);
+ }
+
+ sleep(1);
+ flb_stop(ctx->flb);
+ flb_destroy(ctx->flb);
+ flb_free(ctx);
+}
+
+static ssize_t write_msg(struct test_tail_ctx *ctx, char *msg, size_t msg_len)
+{
+ int i;
+ ssize_t w_byte;
+
+ for (i = 0; i <ctx->fd_num; i++) {
+ flb_time_msleep(100);
+ w_byte = write(ctx->fds[i], msg, msg_len);
+ if (!TEST_CHECK(w_byte == msg_len)) {
+ TEST_MSG("write failed ret=%ld", w_byte);
+ return -1;
+ }
+ /* new line */
+ w_byte = write(ctx->fds[i], NEW_LINE, strlen(NEW_LINE));
+ if (!TEST_CHECK(w_byte == strlen(NEW_LINE))) {
+ TEST_MSG("write failed ret=%ld", w_byte);
+ return -1;
+ }
+ fsync(ctx->fds[i]);
+ flb_time_msleep(100);
+ }
+ return w_byte;
+}
+
+
+#define DPATH FLB_TESTS_DATA_PATH "/data/tail"
+#define MAX_LINES 32
+
+int64_t result_time;
+struct tail_test_result {
+ const char *target;
+ int nMatched;
+ int nNotMatched;
+ int nLines;
+};
+
+struct tail_file_lines {
+ char *lines[MAX_LINES];
+ int lines_c;
+};
+
+void wait_with_timeout(uint32_t timeout_ms, struct tail_test_result *result, int nExpected)
+{
+ struct flb_time start_time;
+ struct flb_time end_time;
+ struct flb_time diff_time;
+ uint64_t elapsed_time_flb = 0;
+
+ flb_time_get(&start_time);
+
+ while (true) {
+ if (result->nMatched == nExpected) {
+ break;
+ }
+
+ flb_time_msleep(100);
+ flb_time_get(&end_time);
+ flb_time_diff(&end_time, &start_time, &diff_time);
+ elapsed_time_flb = flb_time_to_nanosec(&diff_time) / 1000000;
+
+ if (elapsed_time_flb > timeout_ms - TIME_EPSILON_MS) {
+ flb_warn("[timeout] elapsed_time: %ld", elapsed_time_flb);
+ // Reached timeout.
+ break;
+ }
+ }
+}
+
+static inline int64_t set_result(int64_t v)
+{
+ int64_t old = __sync_lock_test_and_set(&result_time, v);
+ return old;
+}
+
+
+static int file_to_buf(const char *path, char **out_buf, size_t *out_size)
+{
+ int ret;
+ long bytes;
+ char *buf;
+ FILE *fp;
+ struct stat st;
+
+ ret = stat(path, &st);
+ if (ret == -1) {
+ return -1;
+ }
+
+ fp = fopen(path, "r");
+ if (!fp) {
+ return -1;
+ }
+
+ buf = flb_malloc(st.st_size+1);
+ if (!buf) {
+ flb_errno();
+ fclose(fp);
+ return -1;
+ }
+
+ bytes = fread(buf, st.st_size, 1, fp);
+ if (bytes != 1) {
+ flb_errno();
+ flb_free(buf);
+ fclose(fp);
+ return -1;
+ }
+
+ fclose(fp);
+ buf[st.st_size] = '\0';
+ *out_buf = buf;
+ *out_size = st.st_size;
+
+ return 0;
+}
+
+/* Given a target, lookup the .out file and return it content in a tail_file_lines structure */
+static struct tail_file_lines *get_out_file_content(const char *target)
+{
+ int ret;
+ char file[PATH_MAX];
+ char *p;
+ char *out_buf;
+ size_t out_size;
+ struct tail_file_lines *file_lines = flb_malloc(sizeof (struct tail_file_lines));
+ file_lines->lines_c = 0;
+
+ snprintf(file, sizeof(file) - 1, DPATH "/out/%s.out", target);
+
+ ret = file_to_buf(file, &out_buf, &out_size);
+ TEST_CHECK_(ret == 0, "getting output file content: %s", file);
+ if (ret != 0) {
+ file_lines->lines_c = 0;
+ return file_lines;
+ }
+
+ file_lines->lines[file_lines->lines_c++] = out_buf;
+
+ int i;
+ for (i=0; i<out_size; i++) {
+ // Nullify \n and \r characters
+ p = (char *)(out_buf + i);
+ if (*p == '\n' || *p == '\r') {
+ *p = '\0';
+
+ if (i == out_size - 1) {
+ break;
+ }
+
+ if (*++p != '\0' && *p != '\n' && *p != '\r' && file_lines->lines_c < MAX_LINES) {
+ file_lines->lines[file_lines->lines_c++] = p;
+ }
+ }
+ }
+
+ return file_lines;
+}
+
+static int cb_check_result(void *record, size_t size, void *data)
+{
+ struct tail_test_result *result;
+ struct tail_file_lines *out;
+
+ result = (struct tail_test_result *) data;
+
+ char *check;
+
+ out = get_out_file_content(result->target);
+ if (!out->lines_c) {
+ goto exit;
+ }
+ /*
+ * Our validation is: check that the one of the output lines
+ * in the output record.
+ */
+ int i;
+ result->nLines = out->lines_c;
+ for (i=0; i<out->lines_c; i++) {
+ check = strstr(record, out->lines[i]);
+ if (check != NULL) {
+ result->nMatched++;
+ goto exit;
+ }
+ }
+ result->nNotMatched++;
+exit:
+ if (size > 0) {
+ flb_free(record);
+ }
+ if (out->lines_c) {
+ flb_free(out->lines[0]);
+ flb_free(out);
+ }
+ return 0;
+}
+
+void do_test(char *system, const char *target, int tExpected, int nExpected, ...)
+{
+ int64_t ret;
+ flb_ctx_t *ctx = NULL;
+ int in_ffd;
+ int out_ffd;
+ va_list va;
+ char *key;
+ char *value;
+ char path[PATH_MAX];
+ struct tail_test_result result = {0};
+
+ result.nMatched = 0;
+ result.target = target;
+
+ struct flb_lib_out_cb cb;
+ cb.cb = cb_check_result;
+ cb.data = &result;
+
+ /* initialize */
+ set_result(0);
+
+ ctx = flb_create();
+
+ ret = flb_service_set(ctx,
+ "Log_Level", "error",
+ "Parsers_File", DPATH "/parsers.conf",
+ NULL);
+ TEST_CHECK_(ret == 0, "setting service options");
+
+ in_ffd = flb_input(ctx, (char *) system, NULL);
+ TEST_CHECK(in_ffd >= 0);
+ TEST_CHECK(flb_input_set(ctx, in_ffd, "tag", "test", NULL) == 0);
+
+ /* Compose path based on target */
+ snprintf(path, sizeof(path) - 1, DPATH "/log/%s.log", target);
+ TEST_CHECK_(access(path, R_OK) == 0, "accessing log file: %s", path);
+
+ TEST_CHECK(flb_input_set(ctx, in_ffd,
+ "path" , path,
+ "docker_mode" , "on",
+ "parser" , "docker",
+ "read_from_head", "true",
+ NULL) == 0);
+
+ va_start(va, nExpected);
+ while ((key = va_arg(va, char *))) {
+ value = va_arg(va, char *);
+ TEST_CHECK(value != NULL);
+ TEST_CHECK(flb_input_set(ctx, in_ffd, key, value, NULL) == 0);
+ }
+ va_end(va);
+
+ out_ffd = flb_output(ctx, (char *) "lib", &cb);
+ TEST_CHECK(out_ffd >= 0);
+ TEST_CHECK(flb_output_set(ctx, out_ffd,
+ "match", "test",
+ "format", "json",
+ NULL) == 0);
+
+ TEST_CHECK(flb_service_set(ctx, "Flush", "0.5",
+ "Grace", "1",
+ NULL) == 0);
+
+ /* Start test */
+ /* Start the engine */
+ ret = flb_start(ctx);
+ TEST_CHECK_(ret == 0, "starting engine");
+
+ /* Poll for up to 5 seconds or until we got a match */
+ for (ret = 0; ret < tExpected && result.nMatched < nExpected; ret++) {
+ usleep(1000);
+ }
+
+ /* Wait until matching nExpected results */
+ wait_with_timeout(5000, &result, nExpected);
+
+ TEST_CHECK(result.nMatched == nExpected);
+ TEST_MSG("result.nMatched: %i\nnExpected: %i", result.nMatched, nExpected);
+
+ ret = flb_stop(ctx);
+ TEST_CHECK_(ret == 0, "stopping engine");
+
+ if (ctx) {
+ flb_destroy(ctx);
+ }
+}
+
+void flb_test_in_tail_dockermode()
+{
+ do_test("tail", "dockermode", 20000, 3,
+ NULL);
+}
+
+void flb_test_in_tail_dockermode_splitted_line()
+{
+ do_test("tail", "dockermode_splitted_line", 20000, 2,
+ NULL);
+}
+
+void flb_test_in_tail_dockermode_multiple_lines()
+{
+ do_test("tail", "dockermode_multiple_lines", 20000, 2,
+ "Docker_Mode_Parser", "docker_multiline",
+ NULL);
+}
+
+void flb_test_in_tail_dockermode_splitted_multiple_lines()
+{
+ do_test("tail", "dockermode_splitted_multiple_lines", 20000, 2,
+ "Docker_Mode_Parser", "docker_multiline",
+ NULL);
+}
+
+void flb_test_in_tail_dockermode_firstline_detection()
+{
+ do_test("tail", "dockermode_firstline_detection", 20000, 5,
+ "Docker_Mode_Parser", "docker_multiline",
+ NULL);
+}
+
+int write_long_lines(int fd) {
+ ssize_t ret;
+ int i;
+ const char* data = "0123456789abcdef" "0123456789abcdef";
+ size_t len = strlen(data);
+
+ for (i=0; i<1024; i++) {
+ ret = write(fd, data, strlen(data));
+ if (ret < 0) {
+ flb_errno();
+ return -1;
+ }
+ else if(ret != len) {
+ write(fd, &data[ret], len-ret);
+ }
+ }
+
+ write(fd, "\n", 1);
+ return 0;
+}
+
+void flb_test_in_tail_skip_long_lines()
+{
+ int64_t ret;
+ flb_ctx_t *ctx = NULL;
+ int in_ffd;
+ int out_ffd;
+ char path[PATH_MAX];
+ struct tail_test_result result = {0};
+ int fd;
+
+ char *target = "skip_long_lines";
+ int nExpected = 2;
+ int nExpectedNotMatched = 0;
+ int nExpectedLines = 2;
+
+ result.nMatched = 0;
+ result.target = target;
+
+ struct flb_lib_out_cb cb;
+ cb.cb = cb_check_result;
+ cb.data = &result;
+
+ /* initialize */
+ set_result(0);
+
+ ctx = flb_create();
+
+ ret = flb_service_set(ctx,
+ "Log_Level", "error",
+ NULL);
+ TEST_CHECK_(ret == 0, "setting service options");
+
+ in_ffd = flb_input(ctx, "tail", NULL);
+ TEST_CHECK(in_ffd >= 0);
+ TEST_CHECK(flb_input_set(ctx, in_ffd, "tag", "test", NULL) == 0);
+
+ /* Compose path based on target */
+ snprintf(path, sizeof(path) - 1, DPATH "/log/%s.log", target);
+ fd = creat(path, S_IRWXU | S_IRGRP);
+ TEST_CHECK(fd >= 0);
+
+ /* Write log
+ =======
+ before_long_line
+ (long line which should be skipped)
+ after_long_line
+ =======
+
+ Output should be "before_long_line" and "after_long_line"
+ */
+ write(fd, "before_long_line\n", strlen("before_long_line\n"));
+ write_long_lines(fd);
+ write(fd, "after_long_line\n", strlen("after_long_line\n"));
+ close(fd);
+
+ TEST_CHECK_(access(path, R_OK) == 0, "accessing log file: %s", path);
+
+ TEST_CHECK(flb_input_set(ctx, in_ffd,
+ "path" , path,
+ "read_from_head", "true",
+ "skip_long_lines", "on",
+ NULL) == 0);
+
+ out_ffd = flb_output(ctx, (char *) "lib", &cb);
+ TEST_CHECK(out_ffd >= 0);
+ TEST_CHECK(flb_output_set(ctx, out_ffd,
+ "match", "test",
+ "format", "json",
+ NULL) == 0);
+
+ TEST_CHECK(flb_service_set(ctx, "Flush", "0.5",
+ "Grace", "1",
+ NULL) == 0);
+
+ /* Start test */
+ /* Start the engine */
+ ret = flb_start(ctx);
+ TEST_CHECK_(ret == 0, "starting engine");
+
+ wait_with_timeout(5000, &result, nExpected);
+
+ TEST_CHECK(result.nMatched == nExpected);
+ TEST_MSG("result.nMatched: %i\nnExpected: %i", result.nMatched, nExpected);
+ TEST_CHECK(result.nNotMatched == nExpectedNotMatched);
+ TEST_MSG("result.nNotMatched: %i\nnExpectedNotMatched: %i", result.nNotMatched, nExpectedNotMatched);
+ TEST_CHECK(result.nLines == nExpectedLines);
+ TEST_MSG("result.nLines: %i\nnExpectedLines: %i", result.nLines, nExpectedLines);
+
+ ret = flb_stop(ctx);
+ TEST_CHECK_(ret == 0, "stopping engine");
+
+ if (ctx) {
+ flb_destroy(ctx);
+ }
+
+ unlink(path);
+}
+
+/*
+ * test case for https://github.com/fluent/fluent-bit/issues/3943
+ *
+ * test to read the lines "CRLF + empty_line + LF"
+ */
+void flb_test_in_tail_issue_3943()
+{
+ int64_t ret;
+ flb_ctx_t *ctx = NULL;
+ int in_ffd;
+ int out_ffd;
+ char path[PATH_MAX];
+ struct tail_test_result result = {0};
+
+ char *target = "3943";
+ int nExpected = 2;
+ int nExpectedNotMatched = 0;
+ int nExpectedLines = 2;
+
+ result.nMatched = 0;
+ result.target = target;
+
+ struct flb_lib_out_cb cb;
+ cb.cb = cb_check_result;
+ cb.data = &result;
+
+ /* initialize */
+ set_result(0);
+
+ ctx = flb_create();
+
+ ret = flb_service_set(ctx,
+ "Log_Level", "error",
+ NULL);
+ TEST_CHECK_(ret == 0, "setting service options");
+
+ in_ffd = flb_input(ctx, "tail", NULL);
+ TEST_CHECK(in_ffd >= 0);
+ TEST_CHECK(flb_input_set(ctx, in_ffd, "tag", "test", NULL) == 0);
+
+ snprintf(path, sizeof(path) - 1, DPATH "/log/%s.log", target);
+ TEST_CHECK_(access(path, R_OK) == 0, "accessing log file: %s", path);
+
+ TEST_CHECK(flb_input_set(ctx, in_ffd,
+ "path" , path,
+ "read_from_head", "true",
+ NULL) == 0);
+
+ out_ffd = flb_output(ctx, (char *) "lib", &cb);
+ TEST_CHECK(out_ffd >= 0);
+ TEST_CHECK(flb_output_set(ctx, out_ffd,
+ "match", "test",
+ "format", "json",
+ NULL) == 0);
+
+ TEST_CHECK(flb_service_set(ctx, "Flush", "0.5",
+ "Grace", "1",
+ NULL) == 0);
+
+ /* Start test */
+ /* Start the engine */
+ ret = flb_start(ctx);
+ TEST_CHECK_(ret == 0, "starting engine");
+
+ wait_with_timeout(3000, &result, nExpected);
+
+ TEST_CHECK(result.nMatched == nExpected);
+ TEST_MSG("result.nMatched: %i\nnExpected: %i", result.nMatched, nExpected);
+ TEST_CHECK(result.nNotMatched == nExpectedNotMatched);
+ TEST_MSG("result.nNotMatched: %i\nnExpectedNotMatched: %i", result.nNotMatched, nExpectedNotMatched);
+ TEST_CHECK(result.nLines == nExpectedLines);
+ TEST_MSG("result.nLines: %i\nnExpectedLines: %i", result.nLines, nExpectedLines);
+
+ ret = flb_stop(ctx);
+ TEST_CHECK_(ret == 0, "stopping engine");
+
+ if (ctx) {
+ flb_destroy(ctx);
+ }
+}
+
+void flb_test_in_tail_multiline_json_and_regex()
+{
+ int64_t ret;
+ int in_ffd;
+ int out_ffd;
+ int n_expected;
+ int t_expected;
+ char *target;
+ char path[PATH_MAX];
+ struct tail_test_result result = {0};
+ flb_ctx_t *ctx;
+
+ target = "multiline_001";
+ result.nMatched = 0;
+ result.target = target;
+
+ struct flb_lib_out_cb cb;
+ cb.cb = cb_check_result;
+ cb.data = &result;
+
+ /* initialize */
+ set_result(0);
+
+ ctx = flb_create();
+
+ TEST_CHECK(flb_service_set(ctx, "Flush", "0.5",
+ "Grace", "5",
+ NULL) == 0);
+
+ ret = flb_service_set(ctx,
+ "Log_Level", "info",
+ "Parsers_File", DPATH "/parsers_multiline_json.conf",
+ NULL);
+ TEST_CHECK_(ret == 0, "setting service options");
+
+ in_ffd = flb_input(ctx, (char *) "tail", NULL);
+ TEST_CHECK(in_ffd >= 0);
+ TEST_CHECK(flb_input_set(ctx, in_ffd, "tag", "test", NULL) == 0);
+
+ /* Compose path based on target */
+ snprintf(path, sizeof(path) - 1, DPATH "/log/%s.log", target);
+ TEST_CHECK_(access(path, R_OK) == 0, "accessing log file: %s", path);
+
+ TEST_CHECK(flb_input_set(ctx, in_ffd,
+ "path" , path,
+ "read_from_head" , "true",
+ "multiline.parser", "multiline-json-regex",
+ NULL) == 0);
+
+
+ out_ffd = flb_output(ctx, (char *) "lib", &cb);
+ TEST_CHECK(out_ffd >= 0);
+ TEST_CHECK(flb_output_set(ctx, out_ffd,
+ "match", "test",
+ "format", "json",
+ NULL) == 0);
+
+ /* Start test */
+ /* Start the engine */
+ ret = flb_start(ctx);
+ TEST_CHECK_(ret == 0, "starting engine");
+
+ /* Expect 1 final record */
+ n_expected = 1;
+ t_expected = 5000;
+
+ /* Poll for up to 5 seconds or until we got a match */
+ for (ret = 0; ret < t_expected && result.nMatched < n_expected; ret++) {
+ usleep(1000);
+ }
+ wait_with_timeout(5000, &result, n_expected);
+
+ TEST_CHECK(result.nMatched == n_expected);
+ TEST_MSG("result.nMatched: %i\nnExpected: %i", result.nMatched, n_expected);
+
+ ret = flb_stop(ctx);
+ TEST_CHECK_(ret == 0, "stopping engine");
+
+ if (ctx) {
+ flb_destroy(ctx);
+ }
+}
+
+void flb_test_path_comma()
+{
+ struct flb_lib_out_cb cb_data;
+ struct test_tail_ctx *ctx;
+ char *file[] = {"a.log", "b.log", "c.log", "d.log"};
+ char *path = "a.log, b.log, c.log, d.log";
+ char *msg = "hello world";
+ int ret;
+ int num;
+ int unused;
+
+ clear_output_num();
+
+ cb_data.cb = cb_count_msgpack;
+ cb_data.data = &unused;
+
+ ctx = test_tail_ctx_create(&cb_data, &file[0], sizeof(file)/sizeof(char*), FLB_TRUE);
+ if (!TEST_CHECK(ctx != NULL)) {
+ TEST_MSG("test_ctx_create failed");
+ exit(EXIT_FAILURE);
+ }
+
+ ret = flb_input_set(ctx->flb, ctx->o_ffd,
+ "path", path,
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ ret = flb_output_set(ctx->flb, ctx->o_ffd,
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ /* Start the engine */
+ ret = flb_start(ctx->flb);
+ TEST_CHECK(ret == 0);
+
+ ret = write_msg(ctx, msg, strlen(msg));
+ if (!TEST_CHECK(ret > 0)) {
+ test_tail_ctx_destroy(ctx);
+ exit(EXIT_FAILURE);
+ }
+
+ /* waiting to flush */
+ flb_time_msleep(500);
+
+ num = get_output_num();
+ if (!TEST_CHECK(num == sizeof(file)/sizeof(char*))) {
+ TEST_MSG("output num error. expect=%lu got=%d", sizeof(file)/sizeof(char*), num);
+ }
+
+ test_tail_ctx_destroy(ctx);
+}
+
+void flb_test_path_key()
+{
+ struct flb_lib_out_cb cb_data;
+ struct test_tail_ctx *ctx;
+ char *file[] = {"path_key.log"};
+ char *path_key = "path_key_is";
+ char *msg = "hello world";
+ int ret;
+ int num;
+
+ char *expected_strs[] = {path_key, msg, file[0]};
+ struct str_list expected = {
+ .size = sizeof(expected_strs)/sizeof(char*),
+ .lists = &expected_strs[0],
+ };
+
+ clear_output_num();
+
+ cb_data.cb = cb_check_json_str_list;
+ cb_data.data = &expected;
+
+ ctx = test_tail_ctx_create(&cb_data, &file[0], sizeof(file)/sizeof(char*), FLB_TRUE);
+ if (!TEST_CHECK(ctx != NULL)) {
+ TEST_MSG("test_ctx_create failed");
+ exit(EXIT_FAILURE);
+ }
+
+ ret = flb_input_set(ctx->flb, ctx->o_ffd,
+ "path", file[0],
+ "path_key", path_key,
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ ret = flb_output_set(ctx->flb, ctx->o_ffd,
+ "format", "json",
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ /* Start the engine */
+ ret = flb_start(ctx->flb);
+ TEST_CHECK(ret == 0);
+
+ ret = write_msg(ctx, msg, strlen(msg));
+ if (!TEST_CHECK(ret > 0)) {
+ test_tail_ctx_destroy(ctx);
+ exit(EXIT_FAILURE);
+ }
+
+ /* waiting to flush */
+ flb_time_msleep(500);
+
+ num = get_output_num();
+ if (!TEST_CHECK(num > 0)) {
+ TEST_MSG("no outputs");
+ }
+
+ test_tail_ctx_destroy(ctx);
+}
+
+void flb_test_exclude_path()
+{
+ struct flb_lib_out_cb cb_data;
+ struct test_tail_ctx *ctx;
+ char *exclude_path = "ep_ignore*.txt";
+ char *path = "ep_*.txt";
+ char *file[] = {"ep_ignore_1.txt", "ep_ignore_2.txt", "ep_file1.txt", "ep_file2.txt", "ep_file3.txt"};
+ char *msg = "hello world";
+ int unused;
+ int ret;
+ int num;
+
+ clear_output_num();
+
+ cb_data.cb = cb_count_msgpack;
+ cb_data.data = &unused;
+
+ ctx = test_tail_ctx_create(&cb_data, &file[0], sizeof(file)/sizeof(char*), FLB_TRUE);
+ if (!TEST_CHECK(ctx != NULL)) {
+ TEST_MSG("test_ctx_create failed");
+ exit(EXIT_FAILURE);
+ }
+
+ ret = flb_input_set(ctx->flb, ctx->o_ffd,
+ "path", path,
+ "exclude_path", exclude_path,
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ /* Start the engine */
+ ret = flb_start(ctx->flb);
+ TEST_CHECK(ret == 0);
+
+ ret = write_msg(ctx, msg, strlen(msg));
+ if (!TEST_CHECK(ret > 0)) {
+ test_tail_ctx_destroy(ctx);
+ exit(EXIT_FAILURE);
+ }
+
+ /* waiting to flush */
+ flb_time_msleep(500);
+
+ num = get_output_num();
+ if (!TEST_CHECK(num == 3 /* 3files. "ep_file1.txt", "ep_file2.txt", "ep_file3.txt" */)) {
+ TEST_MSG("output num error. expect=3 got=%d", num);
+ }
+
+ test_tail_ctx_destroy(ctx);
+}
+
+void flb_test_offset_key()
+{
+ struct flb_lib_out_cb cb_data;
+ struct test_tail_ctx *ctx;
+ char *file[] = {"offset_key.log"};
+ char *offset_key = "OffsetKey";
+ char *msg_before_tail = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
+ char *msg_after_tail = "test test";
+ char expected_msg[1024] = {0};
+ int ret;
+ int num;
+
+ char *expected_strs[] = {msg_after_tail, &expected_msg[0]};
+ struct str_list expected = {
+ .size = sizeof(expected_strs)/sizeof(char*),
+ .lists = &expected_strs[0],
+ };
+
+ clear_output_num();
+
+ cb_data.cb = cb_check_json_str_list;
+ cb_data.data = &expected;
+
+ ret = snprintf(&expected_msg[0], sizeof(expected_msg), "\"%s\":%ld", offset_key, strlen(msg_before_tail)+strlen(NEW_LINE));
+ if(!TEST_CHECK(ret >= 0)) {
+ TEST_MSG("snprintf failed");
+ exit(EXIT_FAILURE);
+ }
+
+
+ ctx = test_tail_ctx_create(&cb_data, &file[0], sizeof(file)/sizeof(char *), FLB_TRUE);
+ if (!TEST_CHECK(ctx != NULL)) {
+ TEST_MSG("test_ctx_create failed");
+ exit(EXIT_FAILURE);
+ }
+
+ ret = flb_input_set(ctx->flb, ctx->o_ffd,
+ "path", file[0],
+ "offset_key", offset_key,
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ ret = flb_output_set(ctx->flb, ctx->o_ffd,
+ "format", "json",
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ ret = write_msg(ctx, msg_before_tail, strlen(msg_before_tail));
+ if (!TEST_CHECK(ret > 0)) {
+ test_tail_ctx_destroy(ctx);
+ exit(EXIT_FAILURE);
+ }
+
+ /* Start the engine */
+ ret = flb_start(ctx->flb);
+ TEST_CHECK(ret == 0);
+
+ ret = write_msg(ctx, msg_after_tail, strlen(msg_after_tail));
+ if (!TEST_CHECK(ret > 0)) {
+ test_tail_ctx_destroy(ctx);
+ exit(EXIT_FAILURE);
+ }
+
+ /* waiting to flush */
+ flb_time_msleep(500);
+
+ num = get_output_num();
+ if (!TEST_CHECK(num > 0)) {
+ TEST_MSG("no outputs");
+ }
+
+ test_tail_ctx_destroy(ctx);
+}
+
+void flb_test_skip_empty_lines()
+{
+ struct flb_lib_out_cb cb_data;
+ struct test_tail_ctx *ctx;
+ char *file[] = {"skip_empty_lines.log"};
+ char *empty_lines[] = {NEW_LINE, NEW_LINE};
+ char *msg = "lalala";
+ int ret;
+ int num;
+ int i;
+
+ char *expected_strs[] = {msg};
+ struct str_list expected = {
+ .size = sizeof(expected_strs)/sizeof(char*),
+ .lists = &expected_strs[0],
+ };
+
+ clear_output_num();
+
+ cb_data.cb = cb_check_json_str_list;
+ cb_data.data = &expected;
+
+ ctx = test_tail_ctx_create(&cb_data, &file[0], sizeof(file)/sizeof(char *), FLB_TRUE);
+ if (!TEST_CHECK(ctx != NULL)) {
+ TEST_MSG("test_ctx_create failed");
+ exit(EXIT_FAILURE);
+ }
+
+ ret = flb_input_set(ctx->flb, ctx->o_ffd,
+ "path", file[0],
+ "skip_empty_lines", "true",
+ "Read_From_Head", "true",
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ ret = flb_output_set(ctx->flb, ctx->o_ffd,
+ "format", "json",
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ ret = write_msg(ctx, msg, strlen(msg));
+ if (!TEST_CHECK(ret > 0)) {
+ test_tail_ctx_destroy(ctx);
+ exit(EXIT_FAILURE);
+ }
+
+ for (i=0; i<sizeof(empty_lines)/sizeof(char*); i++) {
+ ret = write_msg(ctx, empty_lines[i], strlen(empty_lines[i]));
+ if (!TEST_CHECK(ret > 0)) {
+ test_tail_ctx_destroy(ctx);
+ exit(EXIT_FAILURE);
+ }
+ }
+
+ /* Start the engine */
+ ret = flb_start(ctx->flb);
+ TEST_CHECK(ret == 0);
+
+ /* waiting to flush */
+ flb_time_msleep(500);
+
+ num = get_output_num();
+ if (!TEST_CHECK(num == 1)) {
+ TEST_MSG("output error: expect=1 got=%d", num);
+ }
+
+ test_tail_ctx_destroy(ctx);
+}
+
+static int ignore_older(int expected, char *ignore_older)
+{
+ struct flb_lib_out_cb cb_data;
+ struct test_tail_ctx *ctx;
+ struct timespec times[2];
+ struct flb_time tm;
+ char *file[] = {"time_now.log", "time_30m.log", "time_3h.log", "time_3d.log"};
+ char *path = "time_*.log";
+ char *msg = "hello world";
+ int ret;
+ int num;
+ int unused;
+
+ clear_output_num();
+
+ cb_data.cb = cb_count_msgpack;
+ cb_data.data = &unused;
+
+ ctx = test_tail_ctx_create(&cb_data, &file[0], sizeof(file)/sizeof(char *), FLB_TRUE);
+ if (!TEST_CHECK(ctx != NULL)) {
+ TEST_MSG("test_ctx_create failed");
+ return -1;
+ }
+
+ times[0].tv_nsec = 0;
+ times[1].tv_nsec = 0;
+
+ flb_time_get(&tm);
+ times[0].tv_sec = tm.tm.tv_sec - 3 * 24 * 60 * 60;
+ times[1].tv_sec = tm.tm.tv_sec - 3 * 24 * 60 * 60;
+ ret = utimensat(AT_FDCWD, file[3], times, 0);
+ if (!TEST_CHECK(ret == 0)) {
+ TEST_MSG("utimensat failed. errno=%d file=%s", errno, file[3]);
+ return -1;
+ }
+
+ times[0].tv_sec = tm.tm.tv_sec - 3 * 60 * 60;
+ times[1].tv_sec = tm.tm.tv_sec - 3 * 60 * 60;
+ ret = utimensat(AT_FDCWD, file[2], times, 0);
+ if (!TEST_CHECK(ret == 0)) {
+ TEST_MSG("utimensat failed. errno=%d file=%s", errno, file[2]);
+ return -1;
+ }
+
+ times[0].tv_sec = tm.tm.tv_sec - 30 * 60;
+ times[1].tv_sec = tm.tm.tv_sec - 30 * 60;
+ ret = utimensat(AT_FDCWD, file[1], times, 0);
+ if (!TEST_CHECK(ret == 0)) {
+ TEST_MSG("utimensat failed. errno=%d file=%s", errno, file[1]);
+ return -1;
+ }
+
+ ret = flb_input_set(ctx->flb, ctx->o_ffd,
+ "path", path,
+ "ignore_older", ignore_older,
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ /* Start the engine */
+ ret = flb_start(ctx->flb);
+ TEST_CHECK(ret == 0);
+
+ ret = write_msg(ctx, msg, strlen(msg));
+ if (!TEST_CHECK(ret > 0)) {
+ test_tail_ctx_destroy(ctx);
+ return -1;
+ }
+
+ /* waiting to flush */
+ flb_time_msleep(500);
+
+ num = get_output_num();
+ if (!TEST_CHECK(num == expected)) {
+ TEST_MSG("output num error. expect=%d got=%d", expected, num);
+ return -1;
+ }
+
+ test_tail_ctx_destroy(ctx);
+ return 0;
+}
+
+void flb_test_ignore_older()
+{
+ int ret;
+ char *ignore_olders[] = {"10m", "40m", "4h", "4d"};
+ int expecteds[] = {1/*10m*/, 2/*10m, 40m*/, 3/*10m, 40m, 4h*/, 4 /*all*/};
+ int i;
+
+ TEST_CHECK(sizeof(ignore_olders)/sizeof(char*) == sizeof(expecteds)/sizeof(int));
+
+ for (i=0; i<sizeof(expecteds)/sizeof(int); i++) {
+ ret = ignore_older(expecteds[i], ignore_olders[i]);
+ if (!TEST_CHECK(ret == 0)) {
+ TEST_MSG("case %d failed. ignore_older=%s", i, ignore_olders[i]);
+ exit(EXIT_FAILURE);
+ }
+ }
+}
+
+void flb_test_inotify_watcher_false()
+{
+ struct flb_lib_out_cb cb_data;
+ struct test_tail_ctx *ctx;
+ char *file[] = {"inotify_watcher_false.log"};
+ char *msg = "hello world";
+ int ret;
+ int num;
+
+ char *expected_strs[] = {msg};
+ struct str_list expected = {
+ .size = sizeof(expected_strs)/sizeof(char*),
+ .lists = &expected_strs[0],
+ };
+
+ clear_output_num();
+
+ cb_data.cb = cb_check_json_str_list;
+ cb_data.data = &expected;
+
+ ctx = test_tail_ctx_create(&cb_data, &file[0], sizeof(file)/sizeof(char *), FLB_TRUE);
+ if (!TEST_CHECK(ctx != NULL)) {
+ TEST_MSG("test_ctx_create failed");
+ exit(EXIT_FAILURE);
+ }
+
+ ret = flb_input_set(ctx->flb, ctx->o_ffd,
+ "path", file[0],
+ "inotify_watcher", "false",
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ ret = flb_output_set(ctx->flb, ctx->o_ffd,
+ "format", "json",
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ /* Start the engine */
+ ret = flb_start(ctx->flb);
+ TEST_CHECK(ret == 0);
+
+ ret = write_msg(ctx, msg, strlen(msg));
+ if (!TEST_CHECK(ret > 0)) {
+ test_tail_ctx_destroy(ctx);
+ exit(EXIT_FAILURE);
+ }
+
+ /* waiting to flush */
+ flb_time_msleep(1500);
+
+ num = get_output_num();
+ if (!TEST_CHECK(num > 0)) {
+ TEST_MSG("no output");
+ }
+
+ test_tail_ctx_destroy(ctx);
+}
+
+#ifdef FLB_HAVE_REGEX
+void flb_test_parser()
+{
+ struct flb_lib_out_cb cb_data;
+ struct test_tail_ctx *ctx;
+ char *file[] = {"parser.log"};
+ /* https://httpd.apache.org/docs/2.4/en/logs.html */
+ char *msg = "127.0.0.1 - frank [10/Oct/2000:13:55:36 -0700] \"GET /apache_pb.gif HTTP/1.0\" 200 2326";
+ int ret;
+ int num;
+
+ char *expected_strs[] = {"\"method\":\"GET\"", "\"host\":\"127.0.0.1\"","\"user\":\"frank\"",
+ "\"path\":\"/apache_pb.gif\"","\"code\":\"200\"","\"size\":\"2326\""};
+ struct str_list expected = {
+ .size = sizeof(expected_strs)/sizeof(char*),
+ .lists = &expected_strs[0],
+ };
+
+ clear_output_num();
+
+ cb_data.cb = cb_check_json_str_list;
+ cb_data.data = &expected;
+
+ ctx = test_tail_ctx_create(&cb_data, &file[0], sizeof(file)/sizeof(char *), FLB_TRUE);
+ if (!TEST_CHECK(ctx != NULL)) {
+ TEST_MSG("test_ctx_create failed");
+ exit(EXIT_FAILURE);
+ }
+
+ ret = flb_input_set(ctx->flb, ctx->o_ffd,
+ "path", file[0],
+ "parser", "apache2",
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ ret = flb_output_set(ctx->flb, ctx->o_ffd,
+ "format", "json",
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ /* Start the engine */
+ ret = flb_start(ctx->flb);
+ TEST_CHECK(ret == 0);
+
+ ret = write_msg(ctx, msg, strlen(msg));
+ if (!TEST_CHECK(ret > 0)) {
+ test_tail_ctx_destroy(ctx);
+ exit(EXIT_FAILURE);
+ }
+
+ /* waiting to flush */
+ flb_time_msleep(500);
+
+ num = get_output_num();
+ if (!TEST_CHECK(num > 0)) {
+ TEST_MSG("no outputs");
+ }
+
+ test_tail_ctx_destroy(ctx);
+}
+
+void flb_test_tag_regex()
+{
+ struct flb_lib_out_cb cb_data;
+ struct test_tail_ctx *ctx;
+ char *file[] = {"aa_bb_cc.log"};
+ char *tag_regex = "(?<first>[a-z]+)_(?<second>[a-z]+)_(?<third>[a-z]+)\\.log";
+ char *tag = "<first>.<second>.<third>"; /* tag will be "aa.bb.cc" */
+ char *msg = "hello world";
+ int ret;
+ int num;
+
+ char *expected_strs[] = {msg};
+ struct str_list expected = {
+ .size = sizeof(expected_strs)/sizeof(char*),
+ .lists = &expected_strs[0],
+ };
+
+ clear_output_num();
+
+ cb_data.cb = cb_check_json_str_list;
+ cb_data.data = &expected;
+
+ ctx = test_tail_ctx_create(&cb_data, &file[0], sizeof(file)/sizeof(char *), FLB_TRUE);
+ if (!TEST_CHECK(ctx != NULL)) {
+ TEST_MSG("test_ctx_create failed");
+ exit(EXIT_FAILURE);
+ }
+
+ ret = flb_input_set(ctx->flb, ctx->o_ffd,
+ "path", file[0],
+ "tag", tag,
+ "tag_regex", tag_regex,
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ ret = flb_output_set(ctx->flb, ctx->o_ffd,
+ "match", "aa.bb.cc",
+ "format", "json",
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ /* Start the engine */
+ ret = flb_start(ctx->flb);
+ TEST_CHECK(ret == 0);
+
+ ret = write_msg(ctx, msg, strlen(msg));
+ if (!TEST_CHECK(ret > 0)) {
+ test_tail_ctx_destroy(ctx);
+ exit(EXIT_FAILURE);
+ }
+
+ /* waiting to flush */
+ flb_time_msleep(500);
+
+ num = get_output_num();
+ if (!TEST_CHECK(num > 0)) {
+ TEST_MSG("no outputs");
+ }
+
+ test_tail_ctx_destroy(ctx);
+}
+#endif /* FLB_HAVE_REGEX */
+
+#ifdef FLB_HAVE_SQLDB
+void flb_test_db()
+{
+ struct flb_lib_out_cb cb_data;
+ struct test_tail_ctx *ctx;
+ char *file[] = {"test_db.log"};
+ char *db = "test_db.db";
+ char *msg_init = "hello world";
+ char *msg = "hello db";
+ char *msg_end = "hello db end";
+ int i;
+ int ret;
+ int num;
+ int unused;
+
+ unlink(db);
+
+ clear_output_num();
+
+ cb_data.cb = cb_count_msgpack;
+ cb_data.data = &unused;
+
+ ctx = test_tail_ctx_create(&cb_data, &file[0], sizeof(file)/sizeof(char *), FLB_FALSE);
+ if (!TEST_CHECK(ctx != NULL)) {
+ TEST_MSG("test_ctx_create failed");
+ exit(EXIT_FAILURE);
+ }
+
+ ret = flb_input_set(ctx->flb, ctx->o_ffd,
+ "path", file[0],
+ "db", db,
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ ret = flb_output_set(ctx->flb, ctx->o_ffd,
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ /* Start the engine */
+ ret = flb_start(ctx->flb);
+ TEST_CHECK(ret == 0);
+
+ ret = write_msg(ctx, msg_init, strlen(msg_init));
+ if (!TEST_CHECK(ret > 0)) {
+ test_tail_ctx_destroy(ctx);
+ unlink(db);
+ exit(EXIT_FAILURE);
+ }
+
+ /* waiting to flush */
+ flb_time_msleep(500);
+
+ num = get_output_num();
+ if (!TEST_CHECK(num > 0)) {
+ TEST_MSG("no output");
+ }
+
+ if (ctx->fds != NULL) {
+ for (i=0; i<ctx->fd_num; i++) {
+ close(ctx->fds[i]);
+ }
+ flb_free(ctx->fds);
+ }
+ flb_stop(ctx->flb);
+ flb_destroy(ctx->flb);
+ flb_free(ctx);
+
+ /* re-init to use db */
+ clear_output_num();
+
+ cb_data.cb = cb_count_msgpack;
+ cb_data.data = &unused;
+
+ ctx = test_tail_ctx_create(&cb_data, &file[0], sizeof(file)/sizeof(char *), FLB_FALSE);
+ if (!TEST_CHECK(ctx != NULL)) {
+ TEST_MSG("test_ctx_create failed");
+ unlink(db);
+ exit(EXIT_FAILURE);
+ }
+
+ ret = flb_input_set(ctx->flb, ctx->o_ffd,
+ "path", file[0],
+ "db", db,
+ "db.sync", "full",
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ ret = write_msg(ctx, msg, strlen(msg));
+ if (!TEST_CHECK(ret > 0)) {
+ test_tail_ctx_destroy(ctx);
+ unlink(db);
+ exit(EXIT_FAILURE);
+ }
+
+ /* Start the engine */
+ ret = flb_start(ctx->flb);
+ TEST_CHECK(ret == 0);
+
+ /* waiting to flush */
+ flb_time_msleep(500);
+
+ ret = write_msg(ctx, msg_end, strlen(msg_end));
+ if (!TEST_CHECK(ret > 0)) {
+ test_tail_ctx_destroy(ctx);
+ unlink(db);
+ exit(EXIT_FAILURE);
+ }
+
+ /* waiting to flush */
+ flb_time_msleep(500);
+
+ num = get_output_num();
+ if (!TEST_CHECK(num == 2)) {
+ /* 2 = msg + msg_end */
+ TEST_MSG("num error. expect=2 got=%d", num);
+ }
+
+ test_tail_ctx_destroy(ctx);
+ unlink(db);
+}
+#endif /* FLB_HAVE_SQLDB */
+
+/* Test list */
+TEST_LIST = {
+ {"issue_3943", flb_test_in_tail_issue_3943},
+ /* Properties */
+ {"skip_long_lines", flb_test_in_tail_skip_long_lines},
+ {"path_comma", flb_test_path_comma},
+ {"path_key", flb_test_path_key},
+ {"exclude_path", flb_test_exclude_path},
+ {"offset_key", flb_test_offset_key},
+ {"skip_empty_lines", flb_test_skip_empty_lines},
+ {"ignore_older", flb_test_ignore_older},
+#ifdef FLB_HAVE_INOTIFY
+ {"inotify_watcher_false", flb_test_inotify_watcher_false},
+#endif /* FLB_HAVE_INOTIFY */
+
+#ifdef FLB_HAVE_REGEX
+ {"parser", flb_test_parser},
+ {"tag_regex", flb_test_tag_regex},
+#endif /* FLB_HAVE_INOTIFY */
+
+#ifdef FLB_HAVE_SQLDB
+ {"db", flb_test_db},
+#endif
+
+#ifdef in_tail
+ {"in_tail_dockermode", flb_test_in_tail_dockermode},
+ {"in_tail_dockermode_splitted_line", flb_test_in_tail_dockermode_splitted_line},
+ {"in_tail_dockermode_multiple_lines", flb_test_in_tail_dockermode_multiple_lines},
+ {"in_tail_dockermode_splitted_multiple_lines", flb_test_in_tail_dockermode_splitted_multiple_lines},
+ {"in_tail_dockermode_firstline_detection", flb_test_in_tail_dockermode_firstline_detection},
+ {"in_tail_multiline_json_and_regex", flb_test_in_tail_multiline_json_and_regex},
+#endif
+ {NULL, NULL}
+};
diff --git a/fluent-bit/tests/runtime/in_tcp.c b/fluent-bit/tests/runtime/in_tcp.c
new file mode 100644
index 00000000..eee20bd6
--- /dev/null
+++ b/fluent-bit/tests/runtime/in_tcp.c
@@ -0,0 +1,561 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/* Fluent Bit
+ * ==========
+ * Copyright (C) 2019-2022 The Fluent Bit Authors
+ * Copyright (C) 2015-2018 Treasure Data Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <fluent-bit.h>
+#include <fluent-bit/flb_compat.h>
+#include <fluent-bit/flb_time.h>
+#include <fluent-bit/flb_pack.h>
+#include <fluent-bit/flb_socket.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include "flb_tests_runtime.h"
+
+#define DEFAULT_IO_TIMEOUT 10
+#define DEFAULT_HOST "127.0.0.1"
+#define DEFAULT_PORT 5170
+
+#define TLS_CERTIFICATE_HOSTNAME "leo.vcap.me"
+#define TLS_CERTIFICATE_FILENAME FLB_TESTS_DATA_PATH "/data/tls/certificate.pem"
+#define TLS_PRIVATE_KEY_FILENAME FLB_TESTS_DATA_PATH "/data/tls/private_key.pem"
+
+struct test_ctx {
+ flb_ctx_t *flb; /* Fluent Bit library context */
+ int i_ffd; /* Input fd */
+ int f_ffd; /* Filter fd (unused) */
+ int o_ffd; /* Output fd */
+};
+
+
+pthread_mutex_t result_mutex = PTHREAD_MUTEX_INITIALIZER;
+int num_output = 0;
+static int get_output_num()
+{
+ int ret;
+ pthread_mutex_lock(&result_mutex);
+ ret = num_output;
+ pthread_mutex_unlock(&result_mutex);
+
+ return ret;
+}
+
+static void set_output_num(int num)
+{
+ pthread_mutex_lock(&result_mutex);
+ num_output = num;
+ pthread_mutex_unlock(&result_mutex);
+}
+
+static void clear_output_num()
+{
+ set_output_num(0);
+}
+
+static int cb_count_msgpack(void *record, size_t size, void *data)
+{
+ msgpack_unpacked result;
+ size_t off = 0;
+
+ if (!TEST_CHECK(data != NULL)) {
+ flb_error("data is NULL");
+ }
+
+ /* Iterate each item array and apply rules */
+ msgpack_unpacked_init(&result);
+ while (msgpack_unpack_next(&result, record, size, &off) == MSGPACK_UNPACK_SUCCESS) {
+ pthread_mutex_lock(&result_mutex);
+ num_output++;
+ pthread_mutex_unlock(&result_mutex);
+ }
+ msgpack_unpacked_destroy(&result);
+
+ flb_free(record);
+ return 0;
+}
+
+/* Callback to check expected results */
+static int cb_check_result_json(void *record, size_t size, void *data)
+{
+ char *p;
+ char *expected;
+ char *result;
+ int num = get_output_num();
+
+ set_output_num(num+1);
+
+ expected = (char *) data;
+ result = (char *) record;
+
+ p = strstr(result, expected);
+ TEST_CHECK(p != NULL);
+
+ if (p==NULL) {
+ flb_error("Expected to find: '%s' in result '%s'",
+ expected, result);
+ }
+ /*
+ * If you want to debug your test
+ *
+ * printf("Expect: '%s' in result '%s'", expected, result);
+ */
+ flb_free(record);
+ return 0;
+}
+
+static struct test_ctx *test_ctx_create(struct flb_lib_out_cb *data)
+{
+ int i_ffd;
+ int o_ffd;
+ struct test_ctx *ctx = NULL;
+
+ ctx = flb_malloc(sizeof(struct test_ctx));
+ if (!TEST_CHECK(ctx != NULL)) {
+ TEST_MSG("malloc failed");
+ flb_errno();
+ return NULL;
+ }
+
+ /* Service config */
+ ctx->flb = flb_create();
+ flb_service_set(ctx->flb,
+ "Flush", "0.200000000",
+ "Grace", "1",
+ "Log_Level", "error",
+ NULL);
+
+ /* Input */
+ i_ffd = flb_input(ctx->flb, (char *) "tcp", NULL);
+ TEST_CHECK(i_ffd >= 0);
+ ctx->i_ffd = i_ffd;
+
+ /* Output */
+ o_ffd = flb_output(ctx->flb, (char *) "lib", (void *) data);
+ ctx->o_ffd = o_ffd;
+
+ return ctx;
+}
+
+static void test_ctx_destroy(struct test_ctx *ctx)
+{
+ TEST_CHECK(ctx != NULL);
+
+ sleep(1);
+ flb_stop(ctx->flb);
+ flb_destroy(ctx->flb);
+ flb_free(ctx);
+}
+
+static flb_sockfd_t connect_tcp(char *in_host, int in_port)
+{
+ int port = in_port;
+ char *host = in_host;
+ flb_sockfd_t fd;
+ int ret;
+ struct sockaddr_in addr;
+
+ if (host == NULL) {
+ host = DEFAULT_HOST;
+ }
+ if (port < 0) {
+ port = DEFAULT_PORT;
+ }
+
+ memset(&addr, 0, sizeof(addr));
+ fd = socket(PF_INET, SOCK_STREAM, 0);
+ if (!TEST_CHECK(fd >= 0)) {
+ TEST_MSG("failed to socket. host=%s port=%d errno=%d", host, port, errno);
+ return -1;
+ }
+
+ addr.sin_family = AF_INET;
+ addr.sin_addr.s_addr = inet_addr(host);
+ addr.sin_port = htons(port);
+
+ ret = connect(fd, (const struct sockaddr *)&addr, sizeof(addr));
+ if (!TEST_CHECK(ret >= 0)) {
+ TEST_MSG("failed to connect. host=%s port=%d errno=%d", host, port, errno);
+ flb_socket_close(fd);
+ return -1;
+ }
+ return fd;
+}
+
+void flb_test_tcp()
+{
+ struct flb_lib_out_cb cb_data;
+ struct test_ctx *ctx;
+ flb_sockfd_t fd;
+ int ret;
+ int num;
+ ssize_t w_size;
+
+ char *buf = "{\"test\":\"msg\"}";
+ size_t size = strlen(buf);
+
+ clear_output_num();
+
+ cb_data.cb = cb_check_result_json;
+ cb_data.data = "\"test\":\"msg\"";
+
+ ctx = test_ctx_create(&cb_data);
+ if (!TEST_CHECK(ctx != NULL)) {
+ TEST_MSG("test_ctx_create failed");
+ exit(EXIT_FAILURE);
+ }
+
+ ret = flb_output_set(ctx->flb, ctx->o_ffd,
+ "match", "*",
+ "format", "json",
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ /* Start the engine */
+ ret = flb_start(ctx->flb);
+ TEST_CHECK(ret == 0);
+
+ /* use default host/port */
+ fd = connect_tcp(NULL, -1);
+ if (!TEST_CHECK(fd >= 0)) {
+ exit(EXIT_FAILURE);
+ }
+
+ w_size = send(fd, buf, size, 0);
+ if (!TEST_CHECK(w_size == size)) {
+ TEST_MSG("failed to send, errno=%d", errno);
+ flb_socket_close(fd);
+ exit(EXIT_FAILURE);
+ }
+
+ /* waiting to flush */
+ flb_time_msleep(1500);
+
+ num = get_output_num();
+ if (!TEST_CHECK(num > 0)) {
+ TEST_MSG("no outputs");
+ }
+
+ flb_socket_close(fd);
+ test_ctx_destroy(ctx);
+}
+
+void flb_test_tcp_with_tls()
+{
+ struct flb_connection *client_connection;
+ struct flb_upstream *upstream;
+ struct flb_lib_out_cb cb_data;
+ size_t sent;
+ struct test_ctx *ctx;
+ int ret;
+ int num;
+ struct flb_tls *tls;
+
+ char *buf = "{\"test\":\"msg\"}";
+ size_t size = strlen(buf);
+
+ clear_output_num();
+
+ cb_data.cb = cb_check_result_json;
+ cb_data.data = "\"test\":\"msg\"";
+
+ ctx = test_ctx_create(&cb_data);
+ if (!TEST_CHECK(ctx != NULL)) {
+ TEST_MSG("test_ctx_create failed");
+ exit(EXIT_FAILURE);
+ }
+
+ ret = flb_input_set(ctx->flb, ctx->i_ffd,
+ "tls", "on",
+ "tls.verify", "no",
+ "tls.vhost", TLS_CERTIFICATE_HOSTNAME,
+ "tls.crt_file", TLS_CERTIFICATE_FILENAME,
+ "tls.key_file", TLS_PRIVATE_KEY_FILENAME,
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ ret = flb_output_set(ctx->flb, ctx->o_ffd,
+ "match", "*",
+ "format", "json",
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ /* Start the engine */
+ ret = flb_start(ctx->flb);
+ TEST_CHECK(ret == 0);
+
+ ret = flb_tls_init();
+ TEST_CHECK(ret == 0);
+
+ tls = flb_tls_create(FLB_TLS_CLIENT_MODE,
+ FLB_FALSE,
+ FLB_TRUE,
+ TLS_CERTIFICATE_HOSTNAME,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL);
+
+ TEST_CHECK(tls != NULL);
+
+ upstream = flb_upstream_create(ctx->flb->config,
+ DEFAULT_HOST,
+ DEFAULT_PORT,
+ FLB_IO_TCP | FLB_IO_TLS,
+ tls);
+
+ TEST_CHECK(upstream != NULL);
+
+ flb_stream_disable_async_mode(&upstream->base);
+
+ upstream->base.net.io_timeout = DEFAULT_IO_TIMEOUT;
+
+ client_connection = flb_upstream_conn_get(upstream);
+
+ TEST_CHECK(client_connection != NULL);
+
+ if (client_connection != NULL) {
+ ret = flb_io_net_write(client_connection,
+ (void *) buf,
+ size,
+ &sent);
+
+ TEST_CHECK(ret > 0);
+
+ /* waiting to flush */
+ flb_time_msleep(1500);
+ }
+
+ num = get_output_num();
+
+ if (!TEST_CHECK(num > 0)) {
+ TEST_MSG("no outputs");
+ }
+
+ sleep(1);
+
+ flb_stop(ctx->flb);
+ flb_upstream_destroy(upstream);
+ flb_tls_destroy(tls);
+ flb_destroy(ctx->flb);
+ flb_free(ctx);
+}
+
+void flb_test_format_none()
+{
+ struct flb_lib_out_cb cb_data;
+ struct test_ctx *ctx;
+ flb_sockfd_t fd;
+ int ret;
+ int num;
+ ssize_t w_size;
+
+ char *buf = "message\n";
+ size_t size = strlen(buf);
+
+ clear_output_num();
+
+ cb_data.cb = cb_check_result_json;
+ cb_data.data = "\"log\":\"message\"";
+
+ ctx = test_ctx_create(&cb_data);
+ if (!TEST_CHECK(ctx != NULL)) {
+ TEST_MSG("test_ctx_create failed");
+ exit(EXIT_FAILURE);
+ }
+
+ ret = flb_output_set(ctx->flb, ctx->o_ffd,
+ "match", "*",
+ "format", "json",
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ ret = flb_input_set(ctx->flb, ctx->i_ffd,
+ "format", "none",
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ /* Start the engine */
+ ret = flb_start(ctx->flb);
+ TEST_CHECK(ret == 0);
+
+ /* use default host/port */
+ fd = connect_tcp(NULL, -1);
+ if (!TEST_CHECK(fd >= 0)) {
+ exit(EXIT_FAILURE);
+ }
+
+ w_size = send(fd, buf, size, 0);
+ if (!TEST_CHECK(w_size == size)) {
+ TEST_MSG("failed to send, errno=%d", errno);
+ flb_socket_close(fd);
+ exit(EXIT_FAILURE);
+ }
+
+ /* waiting to flush */
+ flb_time_msleep(1500);
+
+ num = get_output_num();
+ if (!TEST_CHECK(num > 0)) {
+ TEST_MSG("no outputs");
+ }
+
+ flb_socket_close(fd);
+ test_ctx_destroy(ctx);
+}
+
+void flb_test_format_none_separator()
+{
+ struct flb_lib_out_cb cb_data;
+ struct test_ctx *ctx;
+ flb_sockfd_t fd;
+ int ret;
+ int num;
+ ssize_t w_size;
+
+ char *buf = "message:message:";
+ size_t size = strlen(buf);
+
+ clear_output_num();
+
+ cb_data.cb = cb_check_result_json;
+ cb_data.data = "\"log\":\"message\"";
+
+ ctx = test_ctx_create(&cb_data);
+ if (!TEST_CHECK(ctx != NULL)) {
+ TEST_MSG("test_ctx_create failed");
+ exit(EXIT_FAILURE);
+ }
+
+ ret = flb_output_set(ctx->flb, ctx->o_ffd,
+ "match", "*",
+ "format", "json",
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ ret = flb_input_set(ctx->flb, ctx->i_ffd,
+ "format", "none",
+ "separator", ":",
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ /* Start the engine */
+ ret = flb_start(ctx->flb);
+ TEST_CHECK(ret == 0);
+
+ /* use default host/port */
+ fd = connect_tcp(NULL, -1);
+ if (!TEST_CHECK(fd >= 0)) {
+ exit(EXIT_FAILURE);
+ }
+
+ w_size = send(fd, buf, size, 0);
+ if (!TEST_CHECK(w_size == size)) {
+ TEST_MSG("failed to send, errno=%d", errno);
+ flb_socket_close(fd);
+ exit(EXIT_FAILURE);
+ }
+
+ /* waiting to flush */
+ flb_time_msleep(1500);
+
+ num = get_output_num();
+ if (!TEST_CHECK(num == 2)) {
+ TEST_MSG("no outputs");
+ }
+
+ flb_socket_close(fd);
+ test_ctx_destroy(ctx);
+}
+
+/*
+ * Ingest 64k records.
+ * https://github.com/fluent/fluent-bit/issues/5336
+ */
+void flb_test_issue_5336()
+{
+ struct flb_lib_out_cb cb_data;
+ struct test_ctx *ctx;
+ flb_sockfd_t fd;
+ int ret;
+ int num;
+ ssize_t w_size;
+ int not_used;
+ int i;
+ int count = 65535;
+
+ char *buf = "{\"test\":\"msg\"}";
+ size_t size = strlen(buf);
+
+ clear_output_num();
+
+ cb_data.cb = cb_count_msgpack;
+ cb_data.data = &not_used;
+
+ ctx = test_ctx_create(&cb_data);
+ if (!TEST_CHECK(ctx != NULL)) {
+ TEST_MSG("test_ctx_create failed");
+ exit(EXIT_FAILURE);
+ }
+
+ ret = flb_output_set(ctx->flb, ctx->o_ffd,
+ "match", "*",
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ /* Start the engine */
+ ret = flb_start(ctx->flb);
+ TEST_CHECK(ret == 0);
+
+ /* use default host/port */
+ fd = connect_tcp(NULL, -1);
+ if (!TEST_CHECK(fd >= 0)) {
+ exit(EXIT_FAILURE);
+ }
+
+ for (i=0; i<count; i++) {
+ w_size = send(fd, buf, size, 0);
+ if (!TEST_CHECK(w_size == size)) {
+ TEST_MSG("failed to send, count=%d errno=%d",i, errno);
+ flb_socket_close(fd);
+ exit(EXIT_FAILURE);
+ }
+ }
+
+ /* waiting to flush */
+ flb_time_msleep(2500);
+
+ num = get_output_num();
+ if (!TEST_CHECK(num == count)) {
+ TEST_MSG("got %d, expected: %d", num, count);
+ }
+
+ flb_socket_close(fd);
+ test_ctx_destroy(ctx);
+}
+
+TEST_LIST = {
+ {"tcp", flb_test_tcp},
+ {"tcp_with_tls", flb_test_tcp_with_tls},
+ {"format_none", flb_test_format_none},
+ {"format_none_separator", flb_test_format_none_separator},
+ {"65535_records_issue_5336", flb_test_issue_5336},
+ {NULL, NULL}
+};
+
diff --git a/fluent-bit/tests/runtime/in_udp.c b/fluent-bit/tests/runtime/in_udp.c
new file mode 100644
index 00000000..df60c923
--- /dev/null
+++ b/fluent-bit/tests/runtime/in_udp.c
@@ -0,0 +1,440 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/* Fluent Bit
+ * ==========
+ * Copyright (C) 2019-2022 The Fluent Bit Authors
+ * Copyright (C) 2015-2018 Treasure Data Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <fluent-bit.h>
+#include <fluent-bit/flb_compat.h>
+#include <fluent-bit/flb_time.h>
+#include <fluent-bit/flb_pack.h>
+#include <fluent-bit/flb_socket.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include "flb_tests_runtime.h"
+
+#define DEFAULT_IO_TIMEOUT 10
+#define DEFAULT_HOST "127.0.0.1"
+#define DEFAULT_PORT 5180
+
+#define TLS_CERTIFICATE_HOSTNAME "leo.vcap.me"
+#define TLS_CERTIFICATE_FILENAME FLB_TESTS_DATA_PATH "/data/tls/certificate.pem"
+#define TLS_PRIVATE_KEY_FILENAME FLB_TESTS_DATA_PATH "/data/tls/private_key.pem"
+
+struct test_ctx {
+ flb_ctx_t *flb; /* Fluent Bit library context */
+ int i_ffd; /* Input fd */
+ int f_ffd; /* Filter fd (unused) */
+ int o_ffd; /* Output fd */
+};
+
+pthread_mutex_t result_mutex = PTHREAD_MUTEX_INITIALIZER;
+int num_output = 0;
+static int get_output_num()
+{
+ int ret;
+ pthread_mutex_lock(&result_mutex);
+ ret = num_output;
+ pthread_mutex_unlock(&result_mutex);
+
+ return ret;
+}
+
+static void set_output_num(int num)
+{
+ pthread_mutex_lock(&result_mutex);
+ num_output = num;
+ pthread_mutex_unlock(&result_mutex);
+}
+
+static void clear_output_num()
+{
+ set_output_num(0);
+}
+
+/* Callback to check expected results */
+static int cb_check_result_json(void *record, size_t size, void *data)
+{
+ char *p;
+ char *expected;
+ char *result;
+ int num = get_output_num();
+
+ set_output_num(num+1);
+
+ expected = (char *) data;
+ result = (char *) record;
+
+ p = strstr(result, expected);
+ TEST_CHECK(p != NULL);
+
+ if (p==NULL) {
+ flb_error("Expected to find: '%s' in result '%s'",
+ expected, result);
+ }
+ /*
+ * If you want to debug your test
+ *
+ * printf("Expect: '%s' in result '%s'", expected, result);
+ */
+ flb_free(record);
+ return 0;
+}
+
+static struct test_ctx *test_ctx_create(struct flb_lib_out_cb *data)
+{
+ int ret;
+ int i_ffd;
+ int o_ffd;
+ struct test_ctx *ctx = NULL;
+ char sport[8];
+
+ ctx = flb_malloc(sizeof(struct test_ctx));
+ if (!TEST_CHECK(ctx != NULL)) {
+ TEST_MSG("malloc failed");
+ flb_errno();
+ return NULL;
+ }
+
+ /* Service config */
+ ctx->flb = flb_create();
+ flb_service_set(ctx->flb,
+ "Flush", "0.200000000",
+ "Grace", "1",
+ "Log_Level", "error",
+ NULL);
+
+ /* Input */
+ i_ffd = flb_input(ctx->flb, (char *) "udp", NULL);
+ TEST_CHECK(i_ffd >= 0);
+ ctx->i_ffd = i_ffd;
+
+ /* Avoid to port collisions for default port of in_tcp */
+ snprintf(sport, 8, "%d", DEFAULT_PORT);
+ ret = flb_input_set(ctx->flb, ctx->i_ffd,
+ "port", sport,
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ /* Output */
+ o_ffd = flb_output(ctx->flb, (char *) "lib", (void *) data);
+ ctx->o_ffd = o_ffd;
+
+ return ctx;
+}
+
+static void test_ctx_destroy(struct test_ctx *ctx)
+{
+ TEST_CHECK(ctx != NULL);
+
+ sleep(1);
+ flb_stop(ctx->flb);
+ flb_destroy(ctx->flb);
+ flb_free(ctx);
+}
+
+static int init_udp(char *in_host, int in_port, struct sockaddr_in *addr)
+{
+ int port = in_port;
+ char *host = in_host;
+ flb_sockfd_t fd;
+
+ if (host == NULL) {
+ host = DEFAULT_HOST;
+ }
+ if (port < 0) {
+ port = DEFAULT_PORT;
+ }
+
+ memset(addr, 0, sizeof(struct sockaddr_in));
+ fd = socket(AF_INET, SOCK_DGRAM, 0);
+ if (!TEST_CHECK(fd >= 0)) {
+ TEST_MSG("failed to socket. host=%s port=%d errno=%d", host, port, errno);
+ return -1;
+ }
+
+ addr->sin_family = AF_INET;
+ addr->sin_addr.s_addr = inet_addr(host);
+ addr->sin_port = htons(port);
+
+ return fd;
+}
+
+void flb_test_udp()
+{
+ struct flb_lib_out_cb cb_data;
+ struct test_ctx *ctx;
+ struct sockaddr_in addr;
+ flb_sockfd_t fd;
+ int ret;
+ int num;
+ ssize_t w_size;
+
+ char *buf = "{\"test\":\"msg\"}";
+ size_t size = strlen(buf);
+
+ clear_output_num();
+
+ cb_data.cb = cb_check_result_json;
+ cb_data.data = "\"test\":\"msg\"";
+
+ ctx = test_ctx_create(&cb_data);
+ if (!TEST_CHECK(ctx != NULL)) {
+ TEST_MSG("test_ctx_create failed");
+ exit(EXIT_FAILURE);
+ }
+
+ ret = flb_output_set(ctx->flb, ctx->o_ffd,
+ "match", "*",
+ "format", "json",
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ /* Start the engine */
+ ret = flb_start(ctx->flb);
+ TEST_CHECK(ret == 0);
+
+ /* use default host/port */
+ fd = init_udp(NULL, -1, &addr);
+ if (!TEST_CHECK(fd >= 0)) {
+ exit(EXIT_FAILURE);
+ }
+
+ w_size = sendto(fd, buf, size, 0, (const struct sockaddr *)&addr, sizeof(addr));
+ if (!TEST_CHECK(w_size == size)) {
+ TEST_MSG("failed to send, errno=%d", errno);
+ flb_socket_close(fd);
+ test_ctx_destroy(ctx);
+ exit(EXIT_FAILURE);
+ }
+
+ /* waiting to flush */
+ flb_time_msleep(1500);
+
+ num = get_output_num();
+ if (!TEST_CHECK(num > 0)) {
+ TEST_MSG("no outputs");
+ }
+
+ flb_socket_close(fd);
+ test_ctx_destroy(ctx);
+}
+
+void flb_test_udp_with_source_address()
+{
+ struct flb_lib_out_cb cb_data;
+ struct test_ctx *ctx;
+ struct sockaddr_in addr;
+ flb_sockfd_t fd;
+ int ret;
+ int num;
+ ssize_t w_size;
+
+ char *buf = "{\"test\":\"msg\"}";
+ size_t size = strlen(buf);
+
+ clear_output_num();
+
+ cb_data.cb = cb_check_result_json;
+ cb_data.data = "\"test\":\"msg\",\"source_host\":\"udp://";
+
+ ctx = test_ctx_create(&cb_data);
+ if (!TEST_CHECK(ctx != NULL)) {
+ TEST_MSG("test_ctx_create failed");
+ exit(EXIT_FAILURE);
+ }
+
+ ret = flb_input_set(ctx->flb, ctx->i_ffd,
+ "source_address_key", "source_host",
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ ret = flb_output_set(ctx->flb, ctx->o_ffd,
+ "match", "*",
+ "format", "json",
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ /* Start the engine */
+ ret = flb_start(ctx->flb);
+ TEST_CHECK(ret == 0);
+
+ /* use default host/port */
+ fd = init_udp(NULL, -1, &addr);
+ if (!TEST_CHECK(fd >= 0)) {
+ exit(EXIT_FAILURE);
+ }
+
+ w_size = sendto(fd, buf, size, 0, (const struct sockaddr *)&addr, sizeof(addr));
+ if (!TEST_CHECK(w_size == size)) {
+ TEST_MSG("failed to send, errno=%d", errno);
+ flb_socket_close(fd);
+ test_ctx_destroy(ctx);
+ exit(EXIT_FAILURE);
+ }
+
+ /* waiting to flush */
+ flb_time_msleep(1500);
+
+ num = get_output_num();
+ if (!TEST_CHECK(num > 0)) {
+ TEST_MSG("no outputs");
+ }
+
+ flb_socket_close(fd);
+ test_ctx_destroy(ctx);
+}
+
+void flb_test_format_none()
+{
+ struct flb_lib_out_cb cb_data;
+ struct test_ctx *ctx;
+ struct sockaddr_in addr;
+ flb_sockfd_t fd;
+ int ret;
+ int num;
+ ssize_t w_size;
+
+ char *buf = "message\n";
+ size_t size = strlen(buf);
+
+ clear_output_num();
+
+ cb_data.cb = cb_check_result_json;
+ cb_data.data = "\"log\":\"message\"";
+
+ ctx = test_ctx_create(&cb_data);
+ if (!TEST_CHECK(ctx != NULL)) {
+ TEST_MSG("test_ctx_create failed");
+ exit(EXIT_FAILURE);
+ }
+
+ ret = flb_output_set(ctx->flb, ctx->o_ffd,
+ "match", "*",
+ "format", "json",
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ ret = flb_input_set(ctx->flb, ctx->i_ffd,
+ "format", "none",
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ /* Start the engine */
+ ret = flb_start(ctx->flb);
+ TEST_CHECK(ret == 0);
+
+ /* use default host/port */
+ fd = init_udp(NULL, -1, &addr);
+ if (!TEST_CHECK(fd >= 0)) {
+ exit(EXIT_FAILURE);
+ }
+
+ w_size = sendto(fd, buf, size, 0, (const struct sockaddr *)&addr, sizeof(addr));
+ if (!TEST_CHECK(w_size == size)) {
+ TEST_MSG("failed to send, errno=%d", errno);
+ flb_socket_close(fd);
+ test_ctx_destroy(ctx);
+ exit(EXIT_FAILURE);
+ }
+
+ /* waiting to flush */
+ flb_time_msleep(1500);
+
+ num = get_output_num();
+ if (!TEST_CHECK(num > 0)) {
+ TEST_MSG("no outputs");
+ }
+
+ flb_socket_close(fd);
+ test_ctx_destroy(ctx);
+}
+
+void flb_test_format_none_separator()
+{
+ struct flb_lib_out_cb cb_data;
+ struct test_ctx *ctx;
+ struct sockaddr_in addr;
+ flb_sockfd_t fd;
+ int ret;
+ int num;
+ ssize_t w_size;
+
+ char *buf = "message:message:";
+ size_t size = strlen(buf);
+
+ clear_output_num();
+
+ cb_data.cb = cb_check_result_json;
+ cb_data.data = "\"log\":\"message\"";
+
+ ctx = test_ctx_create(&cb_data);
+ if (!TEST_CHECK(ctx != NULL)) {
+ TEST_MSG("test_ctx_create failed");
+ exit(EXIT_FAILURE);
+ }
+
+ ret = flb_output_set(ctx->flb, ctx->o_ffd,
+ "match", "*",
+ "format", "json",
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ ret = flb_input_set(ctx->flb, ctx->i_ffd,
+ "format", "none",
+ "separator", ":",
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ /* Start the engine */
+ ret = flb_start(ctx->flb);
+ TEST_CHECK(ret == 0);
+
+ /* use default host/port */
+ fd = init_udp(NULL, -1, &addr);
+ if (!TEST_CHECK(fd >= 0)) {
+ exit(EXIT_FAILURE);
+ }
+
+ w_size = sendto(fd, buf, size, 0, (const struct sockaddr *)&addr, sizeof(addr));
+ if (!TEST_CHECK(w_size == size)) {
+ TEST_MSG("failed to send, errno=%d", errno);
+ flb_socket_close(fd);
+ test_ctx_destroy(ctx);
+ exit(EXIT_FAILURE);
+ }
+
+ /* waiting to flush */
+ flb_time_msleep(1500);
+
+ num = get_output_num();
+ if (!TEST_CHECK(num == 2)) {
+ TEST_MSG("no outputs");
+ }
+
+ flb_socket_close(fd);
+ test_ctx_destroy(ctx);
+}
+
+TEST_LIST = {
+ {"udp", flb_test_udp},
+ {"udp_with_source_address", flb_test_udp_with_source_address},
+ {"format_none", flb_test_format_none},
+ {"format_none_separator", flb_test_format_none_separator},
+ {NULL, NULL}
+};
diff --git a/fluent-bit/tests/runtime/out_cloudwatch.c b/fluent-bit/tests/runtime/out_cloudwatch.c
new file mode 100644
index 00000000..fc54e4c3
--- /dev/null
+++ b/fluent-bit/tests/runtime/out_cloudwatch.c
@@ -0,0 +1,362 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+#include <fluent-bit.h>
+#include "flb_tests_runtime.h"
+
+/* Test data */
+#include "data/td/json_td.h" /* JSON_TD */
+
+#define ERROR_ALREADY_EXISTS "{\"__type\":\"ResourceAlreadyExistsException\"}"
+/* not a real error code, but tests that the code can respond to any error */
+#define ERROR_UNKNOWN "{\"__type\":\"UNKNOWN\"}"
+
+/* It writes a big JSON message (copied from TD test) */
+void flb_test_cloudwatch_success(void)
+{
+ int ret;
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+
+ /* mocks calls- signals that we are in test mode */
+ setenv("FLB_CLOUDWATCH_PLUGIN_UNDER_TEST", "true", 1);
+
+ ctx = flb_create();
+
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ TEST_CHECK(in_ffd >= 0);
+ flb_input_set(ctx,in_ffd, "tag", "test", NULL);
+
+ out_ffd = flb_output(ctx, (char *) "cloudwatch_logs", NULL);
+ TEST_CHECK(out_ffd >= 0);
+ flb_output_set(ctx, out_ffd,"match", "test", NULL);
+ flb_output_set(ctx, out_ffd,"region", "us-west-2", NULL);
+ flb_output_set(ctx, out_ffd,"log_group_name", "fluent", NULL);
+ flb_output_set(ctx, out_ffd,"log_stream_prefix", "from-fluent-", NULL);
+ flb_output_set(ctx, out_ffd,"auto_create_group", "On", NULL);
+ flb_output_set(ctx, out_ffd,"net.keepalive", "Off", NULL);
+ flb_output_set(ctx, out_ffd,"Retry_Limit", "1", NULL);
+
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ flb_lib_push(ctx, in_ffd, (char *) JSON_TD , (int) sizeof(JSON_TD) - 1);
+
+ sleep(2);
+ flb_stop(ctx);
+ flb_destroy(ctx);
+}
+
+void flb_test_cloudwatch_already_exists_create_group(void)
+{
+ int ret;
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+
+ /* mocks calls- signals that we are in test mode */
+ setenv("FLB_CLOUDWATCH_PLUGIN_UNDER_TEST", "true", 1);
+ setenv("TEST_CREATE_LOG_GROUP_ERROR", ERROR_ALREADY_EXISTS, 1);
+
+ ctx = flb_create();
+
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ TEST_CHECK(in_ffd >= 0);
+ flb_input_set(ctx,in_ffd, "tag", "test", NULL);
+
+ out_ffd = flb_output(ctx, (char *) "cloudwatch_logs", NULL);
+ TEST_CHECK(out_ffd >= 0);
+ flb_output_set(ctx, out_ffd,"match", "test", NULL);
+ flb_output_set(ctx, out_ffd,"region", "us-west-2", NULL);
+ flb_output_set(ctx, out_ffd,"log_group_name", "fluent", NULL);
+ flb_output_set(ctx, out_ffd,"log_stream_prefix", "from-fluent-", NULL);
+ flb_output_set(ctx, out_ffd,"auto_create_group", "On", NULL);
+ flb_output_set(ctx, out_ffd,"net.keepalive", "Off", NULL);
+ flb_output_set(ctx, out_ffd,"Retry_Limit", "1", NULL);
+
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ flb_lib_push(ctx, in_ffd, (char *) JSON_TD , (int) sizeof(JSON_TD) - 1);
+
+ sleep(2);
+ flb_stop(ctx);
+ flb_destroy(ctx);
+}
+
+void flb_test_cloudwatch_already_exists_create_stream(void)
+{
+ int ret;
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+
+ /* mocks calls- signals that we are in test mode */
+ setenv("FLB_CLOUDWATCH_PLUGIN_UNDER_TEST", "true", 1);
+ setenv("TEST_CREATE_LOG_STREAM_ERROR", ERROR_ALREADY_EXISTS, 1);
+
+ ctx = flb_create();
+
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ TEST_CHECK(in_ffd >= 0);
+ flb_input_set(ctx,in_ffd, "tag", "test", NULL);
+
+ out_ffd = flb_output(ctx, (char *) "cloudwatch_logs", NULL);
+ TEST_CHECK(out_ffd >= 0);
+ flb_output_set(ctx, out_ffd,"match", "test", NULL);
+ flb_output_set(ctx, out_ffd,"region", "us-west-2", NULL);
+ flb_output_set(ctx, out_ffd,"log_group_name", "fluent", NULL);
+ flb_output_set(ctx, out_ffd,"log_stream_prefix", "from-fluent-", NULL);
+ flb_output_set(ctx, out_ffd,"auto_create_group", "On", NULL);
+ flb_output_set(ctx, out_ffd,"net.keepalive", "Off", NULL);
+ flb_output_set(ctx, out_ffd,"Retry_Limit", "1", NULL);
+
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ flb_lib_push(ctx, in_ffd, (char *) JSON_TD , (int) sizeof(JSON_TD) - 1);
+
+ sleep(2);
+ flb_stop(ctx);
+ flb_destroy(ctx);
+}
+
+void flb_test_cloudwatch_error_create_group(void)
+{
+ int ret;
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+
+ /* mocks calls- signals that we are in test mode */
+ setenv("FLB_CLOUDWATCH_PLUGIN_UNDER_TEST", "true", 1);
+ setenv("TEST_CREATE_LOG_GROUP_ERROR", ERROR_UNKNOWN, 1);
+
+ ctx = flb_create();
+
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ TEST_CHECK(in_ffd >= 0);
+ flb_input_set(ctx,in_ffd, "tag", "test", NULL);
+
+ out_ffd = flb_output(ctx, (char *) "cloudwatch_logs", NULL);
+ TEST_CHECK(out_ffd >= 0);
+ flb_output_set(ctx, out_ffd,"match", "test", NULL);
+ flb_output_set(ctx, out_ffd,"region", "us-west-2", NULL);
+ flb_output_set(ctx, out_ffd,"log_group_name", "fluent", NULL);
+ flb_output_set(ctx, out_ffd,"log_stream_prefix", "from-fluent-", NULL);
+ flb_output_set(ctx, out_ffd,"auto_create_group", "On", NULL);
+ flb_output_set(ctx, out_ffd,"net.keepalive", "Off", NULL);
+ flb_output_set(ctx, out_ffd,"Retry_Limit", "1", NULL);
+
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ flb_lib_push(ctx, in_ffd, (char *) JSON_TD , (int) sizeof(JSON_TD) - 1);
+
+ sleep(2);
+ flb_stop(ctx);
+ flb_destroy(ctx);
+}
+
+void flb_test_cloudwatch_error_create_stream(void)
+{
+ int ret;
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+
+ /* mocks calls- signals that we are in test mode */
+ setenv("FLB_CLOUDWATCH_PLUGIN_UNDER_TEST", "true", 1);
+ setenv("TEST_CREATE_LOG_STREAM_ERROR", ERROR_UNKNOWN, 1);
+
+ ctx = flb_create();
+
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ TEST_CHECK(in_ffd >= 0);
+ flb_input_set(ctx,in_ffd, "tag", "test", NULL);
+
+ out_ffd = flb_output(ctx, (char *) "cloudwatch_logs", NULL);
+ TEST_CHECK(out_ffd >= 0);
+ flb_output_set(ctx, out_ffd,"match", "test", NULL);
+ flb_output_set(ctx, out_ffd,"region", "us-west-2", NULL);
+ flb_output_set(ctx, out_ffd,"log_group_name", "fluent", NULL);
+ flb_output_set(ctx, out_ffd,"log_stream_prefix", "from-fluent-", NULL);
+ flb_output_set(ctx, out_ffd,"auto_create_group", "On", NULL);
+ flb_output_set(ctx, out_ffd,"net.keepalive", "Off", NULL);
+ flb_output_set(ctx, out_ffd,"Retry_Limit", "1", NULL);
+
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ flb_lib_push(ctx, in_ffd, (char *) JSON_TD , (int) sizeof(JSON_TD) - 1);
+
+ sleep(2);
+ flb_stop(ctx);
+ flb_destroy(ctx);
+}
+
+void flb_test_cloudwatch_error_put_log_events(void)
+{
+ int ret;
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+
+ /* mocks calls- signals that we are in test mode */
+ setenv("FLB_CLOUDWATCH_PLUGIN_UNDER_TEST", "true", 1);
+ setenv("TEST_PUT_LOG_EVENTS_ERROR", ERROR_UNKNOWN, 1);
+
+ ctx = flb_create();
+
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ TEST_CHECK(in_ffd >= 0);
+ flb_input_set(ctx,in_ffd, "tag", "test", NULL);
+
+ out_ffd = flb_output(ctx, (char *) "cloudwatch_logs", NULL);
+ TEST_CHECK(out_ffd >= 0);
+ flb_output_set(ctx, out_ffd,"match", "test", NULL);
+ flb_output_set(ctx, out_ffd,"region", "us-west-2", NULL);
+ flb_output_set(ctx, out_ffd,"log_group_name", "fluent", NULL);
+ flb_output_set(ctx, out_ffd,"log_stream_prefix", "from-fluent-", NULL);
+ flb_output_set(ctx, out_ffd,"auto_create_group", "On", NULL);
+ flb_output_set(ctx, out_ffd,"net.keepalive", "Off", NULL);
+ flb_output_set(ctx, out_ffd,"Retry_Limit", "1", NULL);
+
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ flb_lib_push(ctx, in_ffd, (char *) JSON_TD , (int) sizeof(JSON_TD) - 1);
+
+ sleep(2);
+ flb_stop(ctx);
+ flb_destroy(ctx);
+}
+
+void flb_test_cloudwatch_put_retention_policy_success(void)
+{
+ int ret;
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+
+ /* mocks calls- signals that we are in test mode */
+ setenv("FLB_CLOUDWATCH_PLUGIN_UNDER_TEST", "true", 1);
+
+ ctx = flb_create();
+
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ TEST_CHECK(in_ffd >= 0);
+ flb_input_set(ctx,in_ffd, "tag", "test", NULL);
+
+ out_ffd = flb_output(ctx, (char *) "cloudwatch_logs", NULL);
+ TEST_CHECK(out_ffd >= 0);
+ flb_output_set(ctx, out_ffd,"match", "test", NULL);
+ flb_output_set(ctx, out_ffd,"region", "us-west-2", NULL);
+ flb_output_set(ctx, out_ffd,"log_group_name", "fluent", NULL);
+ flb_output_set(ctx, out_ffd,"log_stream_prefix", "from-fluent-", NULL);
+ flb_output_set(ctx, out_ffd,"auto_create_group", "On", NULL);
+ flb_output_set(ctx, out_ffd,"log_retention_days", "14", NULL);
+ flb_output_set(ctx, out_ffd,"net.keepalive", "Off", NULL);
+ flb_output_set(ctx, out_ffd,"Retry_Limit", "1", NULL);
+
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ flb_lib_push(ctx, in_ffd, (char *) JSON_TD , (int) sizeof(JSON_TD) - 1);
+
+ sleep(2);
+ flb_stop(ctx);
+ flb_destroy(ctx);
+}
+
+void flb_test_cloudwatch_already_exists_create_group_put_retention_policy(void)
+{
+ int ret;
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+
+ /* mocks calls- signals that we are in test mode */
+ setenv("FLB_CLOUDWATCH_PLUGIN_UNDER_TEST", "true", 1);
+ setenv("TEST_CREATE_LOG_GROUP_ERROR", ERROR_ALREADY_EXISTS, 1);
+
+ /* PutRetentionPolicy is not called if the group already exists */
+ setenv("TEST_PUT_RETENTION_POLICY_ERROR", ERROR_UNKNOWN, 1);
+
+ ctx = flb_create();
+
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ TEST_CHECK(in_ffd >= 0);
+ flb_input_set(ctx,in_ffd, "tag", "test", NULL);
+
+ out_ffd = flb_output(ctx, (char *) "cloudwatch_logs", NULL);
+ TEST_CHECK(out_ffd >= 0);
+ flb_output_set(ctx, out_ffd,"match", "test", NULL);
+ flb_output_set(ctx, out_ffd,"region", "us-west-2", NULL);
+ flb_output_set(ctx, out_ffd,"log_group_name", "fluent", NULL);
+ flb_output_set(ctx, out_ffd,"log_stream_prefix", "from-fluent-", NULL);
+ flb_output_set(ctx, out_ffd,"auto_create_group", "On", NULL);
+ flb_output_set(ctx, out_ffd,"log_retention_days", "14", NULL);
+ flb_output_set(ctx, out_ffd,"net.keepalive", "Off", NULL);
+ flb_output_set(ctx, out_ffd,"Retry_Limit", "1", NULL);
+
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ flb_lib_push(ctx, in_ffd, (char *) JSON_TD , (int) sizeof(JSON_TD) - 1);
+
+ sleep(2);
+ flb_stop(ctx);
+ flb_destroy(ctx);
+}
+
+void flb_test_cloudwatch_error_put_retention_policy(void)
+{
+ int ret;
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+
+ /* mocks calls- signals that we are in test mode */
+ setenv("FLB_CLOUDWATCH_PLUGIN_UNDER_TEST", "true", 1);
+ setenv("TEST_PUT_RETENTION_POLICY_ERROR", ERROR_UNKNOWN, 1);
+
+ ctx = flb_create();
+
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ TEST_CHECK(in_ffd >= 0);
+ flb_input_set(ctx,in_ffd, "tag", "test", NULL);
+
+ out_ffd = flb_output(ctx, (char *) "cloudwatch_logs", NULL);
+ TEST_CHECK(out_ffd >= 0);
+ flb_output_set(ctx, out_ffd,"match", "test", NULL);
+ flb_output_set(ctx, out_ffd,"region", "us-west-2", NULL);
+ flb_output_set(ctx, out_ffd,"log_group_name", "fluent", NULL);
+ flb_output_set(ctx, out_ffd,"log_stream_prefix", "from-fluent-", NULL);
+ flb_output_set(ctx, out_ffd,"auto_create_group", "On", NULL);
+ flb_output_set(ctx, out_ffd,"log_retention_days", "14", NULL);
+ flb_output_set(ctx, out_ffd,"net.keepalive", "Off", NULL);
+ flb_output_set(ctx, out_ffd,"Retry_Limit", "1", NULL);
+
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ flb_lib_push(ctx, in_ffd, (char *) JSON_TD , (int) sizeof(JSON_TD) - 1);
+
+ sleep(2);
+ flb_stop(ctx);
+ flb_destroy(ctx);
+}
+
+/* Test list */
+TEST_LIST = {
+ {"success", flb_test_cloudwatch_success },
+ {"group_already_exists", flb_test_cloudwatch_already_exists_create_group },
+ {"stream_already_exists", flb_test_cloudwatch_already_exists_create_stream },
+ {"create_group_error", flb_test_cloudwatch_error_create_group },
+ {"create_stream_error", flb_test_cloudwatch_error_create_stream },
+ {"put_log_events_error", flb_test_cloudwatch_error_put_log_events },
+ {"put_retention_policy_success", flb_test_cloudwatch_put_retention_policy_success },
+ {"already_exists_create_group_put_retention_policy", flb_test_cloudwatch_already_exists_create_group_put_retention_policy },
+ {"error_put_retention_policy", flb_test_cloudwatch_error_put_retention_policy },
+ {NULL, NULL}
+};
diff --git a/fluent-bit/tests/runtime/out_counter.c b/fluent-bit/tests/runtime/out_counter.c
new file mode 100644
index 00000000..525b78f4
--- /dev/null
+++ b/fluent-bit/tests/runtime/out_counter.c
@@ -0,0 +1,126 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+#include <fluent-bit.h>
+#include "flb_tests_runtime.h"
+
+/* Test data */
+#include "data/common/json_invalid.h" /* JSON_INVALID */
+#include "data/common/json_long.h" /* JSON_LONG */
+#include "data/common/json_small.h" /* JSON_SMALL */
+
+/* Test functions */
+void flb_test_counter_json_invalid(void);
+void flb_test_counter_json_long(void);
+void flb_test_counter_json_small(void);
+
+/* Test list */
+TEST_LIST = {
+ {"json_invalid", flb_test_counter_json_invalid },
+ {"json_long", flb_test_counter_json_long },
+ {"json_small", flb_test_counter_json_small },
+ {NULL, NULL}
+};
+
+void flb_test_counter_json_invalid(void)
+{
+ int i;
+ int ret;
+ int bytes;
+ char *p = (char *) JSON_INVALID;
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+
+ ctx = flb_create();
+ flb_service_set(ctx, "Flush", "1", "Grace", "1", "Log_Level", "error", NULL);
+
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ TEST_CHECK(in_ffd >= 0);
+ flb_input_set(ctx, in_ffd, "tag", "test", NULL);
+
+ out_ffd = flb_output(ctx, (char *) "counter", NULL);
+ TEST_CHECK(out_ffd >= 0);
+ flb_output_set(ctx, out_ffd, "match", "test", NULL);
+
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ for (i = 0; i < (int) sizeof(JSON_INVALID) - 1; i++) {
+ bytes = flb_lib_push(ctx, in_ffd, p + i, 1);
+ TEST_CHECK(bytes == 1);
+ }
+
+ flb_stop(ctx);
+ flb_destroy(ctx);
+}
+
+/* It writes a very long JSON map (> 100KB) byte by byte */
+void flb_test_counter_json_long(void)
+{
+ int i;
+ int ret;
+ int bytes;
+ char *p = (char *) JSON_LONG;
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+
+ ctx = flb_create();
+ flb_service_set(ctx, "Flush", "1", "Grace", "1", "Log_Level", "error", NULL);
+
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ TEST_CHECK(in_ffd >= 0);
+ flb_input_set(ctx, in_ffd, "tag", "test", NULL);
+
+ out_ffd = flb_output(ctx, (char *) "counter", NULL);
+ TEST_CHECK(out_ffd >= 0);
+ flb_output_set(ctx, out_ffd, "match", "test", NULL);
+
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ for (i = 0; i < (int) sizeof(JSON_LONG) - 1; i++) {
+ bytes = flb_lib_push(ctx, in_ffd, p + i, 1);
+ TEST_CHECK(bytes == 1);
+ }
+
+ sleep(1); /* waiting flush */
+
+ flb_stop(ctx);
+ flb_destroy(ctx);
+}
+
+void flb_test_counter_json_small(void)
+{
+ int i;
+ int ret;
+ int bytes;
+ char *p = (char *) JSON_SMALL;
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+
+ ctx = flb_create();
+ flb_service_set(ctx, "Flush", "1", "Grace", "1", "Log_Level", "error", NULL);
+
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ TEST_CHECK(in_ffd >= 0);
+ flb_input_set(ctx, in_ffd, "tag", "test", NULL);
+
+ out_ffd = flb_output(ctx, (char *) "counter", NULL);
+ TEST_CHECK(out_ffd >= 0);
+ flb_output_set(ctx, out_ffd, "match", "test", NULL);
+
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ for (i = 0; i < (int) sizeof(JSON_SMALL) - 1; i++) {
+ bytes = flb_lib_push(ctx, in_ffd, p + i, 1);
+ TEST_CHECK(bytes == 1);
+ }
+
+ sleep(1); /* waiting flush */
+
+ flb_stop(ctx);
+ flb_destroy(ctx);
+}
diff --git a/fluent-bit/tests/runtime/out_datadog.c b/fluent-bit/tests/runtime/out_datadog.c
new file mode 100644
index 00000000..26cd81fb
--- /dev/null
+++ b/fluent-bit/tests/runtime/out_datadog.c
@@ -0,0 +1,173 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/* Fluent Bit
+ * ==========
+ * Copyright (C) 2019-2020 The Fluent Bit Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <fluent-bit.h>
+#include <fluent-bit/flb_pack.h>
+#include <fluent-bit/flb_record_accessor.h>
+#include <fluent-bit/flb_ra_key.h>
+
+#include "flb_tests_runtime.h"
+
+/* JSON payload example */
+#include "data/datadog/json.h"
+
+/*
+ * Fluent Bit Datadog plugin, always set as payload a JSON strings contained in a
+ * 'sds'. Since we want to validate specific keys and it values we expose here some
+ * helper functions to make testing easier.
+ *
+ * The approach is:
+ *
+ * - Convert formatter JSON to msgpack
+ * - use the helper function to check keys and values
+ *
+ * it returns FLB_TRUE if expected 'key/val' matches or FLB_FALSE if 'key' no exists
+ * or if there is a mismatch.
+ */
+static int mp_kv_cmp(char *json_data, size_t json_len, char *key_accessor, char *val)
+{
+ int ret;
+ int type;
+ char *mp_buf = NULL;
+ size_t mp_size;
+ size_t off = 0;
+ msgpack_object map;
+ msgpack_unpacked result;
+ struct flb_ra_value *rval = NULL;
+ struct flb_record_accessor *ra = NULL;
+
+ /* Convert JSON to msgpack */
+ ret = flb_pack_json((const char *) json_data, json_len, &mp_buf, &mp_size,
+ &type, NULL);
+ TEST_CHECK(ret != -1);
+
+ /* Set return status */
+ ret = FLB_FALSE;
+
+ /* Unpack msgpack and reference the main 'map' */
+ msgpack_unpacked_init(&result);
+ ret = msgpack_unpack_next(&result, mp_buf, mp_size, &off);
+ TEST_CHECK(ret == MSGPACK_UNPACK_SUCCESS);
+ map = result.data;
+
+ /* Create a record_accessor context */
+ ra = flb_ra_create(key_accessor, FLB_TRUE);
+ if (!ra) {
+ flb_error("invalid record accessor key, aborting test");
+ goto out;
+ }
+
+ rval = flb_ra_get_value_object(ra, map);
+ TEST_CHECK(rval != NULL);
+ msgpack_unpacked_destroy(&result);
+ if (!rval) {
+ goto out;
+ }
+
+ /* We only validate strings, feel free to expand it as needed */
+ TEST_CHECK(rval->type == FLB_RA_STRING);
+ if (strcmp(rval->val.string, val) == 0) {
+ ret = FLB_TRUE;
+ }
+
+ out:
+ if (rval) {
+ flb_ra_key_value_destroy(rval);
+ }
+ if (ra) {
+ flb_ra_destroy(ra);
+ }
+ if (mp_buf) {
+ flb_free(mp_buf);
+ }
+ return ret;
+}
+
+static void cb_check_dd_properties(void *ctx, int ffd,
+ int res_ret, void *res_data, size_t res_size,
+ void *data)
+{
+ int ret;
+
+ /* source */
+ ret = mp_kv_cmp((char *) res_data + 1, res_size - 2, "$ddsource", "my-app");
+ TEST_CHECK(ret == FLB_TRUE);
+
+ /* service */
+ ret = mp_kv_cmp((char *) res_data + 1, res_size - 2, "$service", "my-app-service");
+ TEST_CHECK(ret == FLB_TRUE);
+
+ /* ddtags */
+ ret = mp_kv_cmp((char *) res_data + 1, res_size - 2, "$ddtags", "project:dev");
+ TEST_CHECK(ret == FLB_TRUE);
+
+ flb_sds_destroy(res_data);
+}
+
+void flb_test_dd_properties()
+{
+ int ret;
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+
+ /* Create context, flush every second (some checks omitted here) */
+ ctx = flb_create();
+ flb_service_set(ctx, "flush", "1", "grace", "1", NULL);
+
+ /* Lib input mode */
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ flb_input_set(ctx, in_ffd, "tag", "test", NULL);
+
+ /* Datadog output */
+ out_ffd = flb_output(ctx, (char *) "datadog", NULL);
+ flb_output_set(ctx, out_ffd,
+ "match", "test",
+ "provider", "ecs",
+ "dd_service", "my-app-service",
+ "dd_source", "my-app",
+ "dd_tags", "project:dev",
+ "apikey", "abc",
+ NULL);
+
+ /* Enable test mode */
+ ret = flb_output_set_test(ctx, out_ffd, "formatter",
+ cb_check_dd_properties,
+ NULL, NULL);
+
+ /* Start */
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ sleep(1);
+
+ /* Ingest data sample */
+ flb_lib_push(ctx, in_ffd, (char *) JSON, sizeof(JSON) - 1);
+
+ sleep(2);
+
+ flb_stop(ctx);
+ flb_destroy(ctx);
+}
+
+/* Test list */
+TEST_LIST = {
+ { "dd_properties", flb_test_dd_properties },
+ { NULL, NULL}
+};
diff --git a/fluent-bit/tests/runtime/out_elasticsearch.c b/fluent-bit/tests/runtime/out_elasticsearch.c
new file mode 100644
index 00000000..9efe7610
--- /dev/null
+++ b/fluent-bit/tests/runtime/out_elasticsearch.c
@@ -0,0 +1,818 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+#include <fluent-bit.h>
+#include "flb_tests_runtime.h"
+
+/* Test data */
+#include "data/es/json_es.h" /* JSON_ES */
+
+
+static void cb_check_write_op_index(void *ctx, int ffd,
+ int res_ret, void *res_data,
+ size_t res_size, void *data)
+{
+ char *p;
+ char *out_js = res_data;
+ char *index_line = "{\"index\":{";
+
+ p = strstr(out_js, index_line);
+ TEST_CHECK(p == out_js);
+
+ flb_free(res_data);
+}
+
+static void cb_check_write_op_create(void *ctx, int ffd,
+ int res_ret, void *res_data,
+ size_t res_size, void *data)
+{
+ char *p;
+ char *out_js = res_data;
+ char *index_line = "{\"create\":{";
+
+ p = strstr(out_js, index_line);
+ TEST_CHECK(p == out_js);
+
+ flb_free(res_data);
+}
+
+static void cb_check_write_op_update(void *ctx, int ffd,
+ int res_ret, void *res_data,
+ size_t res_size, void *data)
+{
+ char *p;
+ char *b;
+ char *out_js = res_data;
+ char *index_line = "{\"update\":{";
+ char *body = "{\"doc\":";
+
+ p = strstr(out_js, index_line);
+ TEST_CHECK(p == out_js);
+ b = strstr(out_js, body);
+ TEST_CHECK(b != NULL);
+
+ flb_free(res_data);
+}
+
+static void cb_check_write_op_upsert(void *ctx, int ffd,
+ int res_ret, void *res_data,
+ size_t res_size, void *data)
+{
+ char *p;
+ char *b;
+ char *out_js = res_data;
+ char *index_line = "{\"update\":{";
+ char *body = "{\"doc_as_upsert\":true,\"doc\":";
+
+ p = strstr(out_js, index_line);
+ TEST_CHECK(p == out_js);
+ b = strstr(out_js, body);
+ TEST_CHECK(b != NULL);
+
+ flb_free(res_data);
+}
+
+static void cb_check_index_type(void *ctx, int ffd,
+ int res_ret, void *res_data, size_t res_size,
+ void *data)
+{
+ char *p;
+ char *out_js = res_data;
+ char *index_line = "{\"create\":{\"_index\":\"index_test\",\"_type\":\"type_test\"}";
+
+ p = strstr(out_js, index_line);
+ TEST_CHECK(p != NULL);
+
+ flb_free(res_data);
+}
+
+static void cb_check_logstash_format(void *ctx, int ffd,
+ int res_ret, void *res_data, size_t res_size,
+ void *data)
+{
+ char *p;
+ char *out_js = res_data;
+ char *index_line = "{\"create\":{\"_index\":\"prefix-2015-11-24\",\"_type\":\"_doc\"}";
+
+ p = strstr(out_js, index_line);
+ if(!TEST_CHECK(p != NULL)) {
+ TEST_MSG("Got: %s", out_js);
+ }
+ flb_free(res_data);
+}
+
+static void cb_check_logstash_prefix_separator(void *ctx, int ffd,
+ int res_ret, void *res_data, size_t res_size,
+ void *data)
+{
+ char *p;
+ char *out_js = res_data;
+ char *index_line = "{\"create\":{\"_index\":\"prefixSEP2015-11-24\",\"_type\":\"_doc\"}";
+
+ p = strstr(out_js, index_line);
+ if(!TEST_CHECK(p != NULL)) {
+ TEST_MSG("Got: %s", out_js);
+ }
+ flb_free(res_data);
+}
+
+static void cb_check_logstash_format_nanos(void *ctx, int ffd,
+ int res_ret, void *res_data, size_t res_size,
+ void *data)
+{
+ char *p;
+ char *out_js = res_data;
+ char *index_line = "\"@timestamp\":\"2015-11-24T22:15:40.000000000Z\"";
+
+ p = strstr(out_js, index_line);
+ TEST_CHECK(p != NULL);
+ flb_free(res_data);
+}
+
+static void cb_check_tag_key(void *ctx, int ffd,
+ int res_ret, void *res_data, size_t res_size,
+ void *data)
+{
+ char *p;
+ char *out_js = res_data;
+ char *record = "\"mytag\":\"test\"";
+
+ p = strstr(out_js, record);
+ TEST_CHECK(p != NULL);
+ flb_free(res_data);
+}
+
+static void cb_check_replace_dots(void *ctx, int ffd,
+ int res_ret, void *res_data, size_t res_size,
+ void *data)
+{
+ char *p;
+ char *out_js = res_data;
+ char *record = "\"_o_k\":[{\"_b_ar\"";
+
+ p = strstr(out_js, record);
+ TEST_CHECK(p != NULL);
+ flb_free(res_data);
+}
+
+static void cb_check_id_key(void *ctx, int ffd,
+ int res_ret, void *res_data, size_t res_size,
+ void *data)
+{
+ char *p;
+ char *out_js = res_data;
+ char *record = "\"_id\":\"some string\""; // see data/es/json_es.h
+
+ p = strstr(out_js, record);
+ TEST_CHECK(p != NULL);
+ flb_free(res_data);
+}
+
+void flb_test_write_operation_index()
+{
+ int ret;
+ int size = sizeof(JSON_ES) - 1;
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+
+ /* Create context, flush every second (some checks omitted here) */
+ ctx = flb_create();
+ flb_service_set(ctx, "flush", "1", "grace", "1", NULL);
+
+ /* Lib input mode */
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ flb_input_set(ctx, in_ffd, "tag", "test", NULL);
+
+ /* Elasticsearch output */
+ out_ffd = flb_output(ctx, (char *) "es", NULL);
+ flb_output_set(ctx, out_ffd,
+ "match", "test",
+ NULL);
+
+ /* Override defaults of index and type */
+ flb_output_set(ctx, out_ffd,
+ "write_operation", "index",
+ NULL);
+
+ /* Enable test mode */
+ ret = flb_output_set_test(ctx, out_ffd, "formatter",
+ cb_check_write_op_index,
+ NULL, NULL);
+
+ /* Start */
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data sample */
+ flb_lib_push(ctx, in_ffd, (char *) JSON_ES, size);
+
+ sleep(2);
+ flb_stop(ctx);
+ flb_destroy(ctx);
+}
+
+void flb_test_write_operation_create()
+{
+ int ret;
+ int size = sizeof(JSON_ES) - 1;
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+
+ /* Create context, flush every second (some checks omitted here) */
+ ctx = flb_create();
+ flb_service_set(ctx, "flush", "1", "grace", "1", NULL);
+
+ /* Lib input mode */
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ flb_input_set(ctx, in_ffd, "tag", "test", NULL);
+
+ /* Elasticsearch output */
+ out_ffd = flb_output(ctx, (char *) "es", NULL);
+ flb_output_set(ctx, out_ffd,
+ "match", "test",
+ NULL);
+
+ /* Override defaults of index and type */
+ flb_output_set(ctx, out_ffd,
+ "write_operation", "create",
+ NULL);
+
+ /* Enable test mode */
+ ret = flb_output_set_test(ctx, out_ffd, "formatter",
+ cb_check_write_op_create,
+ NULL, NULL);
+
+ /* Start */
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data sample */
+ flb_lib_push(ctx, in_ffd, (char *) JSON_ES, size);
+
+ sleep(2);
+ flb_stop(ctx);
+ flb_destroy(ctx);
+}
+
+
+void flb_test_write_operation_update()
+{
+ int ret;
+ int size = sizeof(JSON_ES) - 1;
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+
+ /* Create context, flush every second (some checks omitted here) */
+ ctx = flb_create();
+ flb_service_set(ctx, "flush", "1", "grace", "1", NULL);
+
+ /* Lib input mode */
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ flb_input_set(ctx, in_ffd, "tag", "test", NULL);
+
+ /* Elasticsearch output */
+ out_ffd = flb_output(ctx, (char *) "es", NULL);
+ flb_output_set(ctx, out_ffd,
+ "match", "test",
+ NULL);
+
+ /* Override defaults of index and type */
+ flb_output_set(ctx, out_ffd,
+ "Write_Operation", "Update",
+ "Generate_Id", "True",
+ NULL);
+
+ /* Enable test mode */
+ ret = flb_output_set_test(ctx, out_ffd, "formatter",
+ cb_check_write_op_update,
+ NULL, NULL);
+
+ /* Start */
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data sample */
+ flb_lib_push(ctx, in_ffd, (char *) JSON_ES, size);
+
+ sleep(2);
+ flb_stop(ctx);
+ flb_destroy(ctx);
+}
+
+
+void flb_test_write_operation_upsert()
+{
+ int ret;
+ int size = sizeof(JSON_ES) - 1;
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+
+ /* Create context, flush every second (some checks omitted here) */
+ ctx = flb_create();
+ flb_service_set(ctx, "flush", "1", "grace", "1", NULL);
+
+ /* Lib input mode */
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ flb_input_set(ctx, in_ffd, "tag", "test", NULL);
+
+ /* Elasticsearch output */
+ out_ffd = flb_output(ctx, (char *) "es", NULL);
+ flb_output_set(ctx, out_ffd,
+ "match", "test",
+ NULL);
+
+ /* Override defaults of index and type */
+ flb_output_set(ctx, out_ffd,
+ "Write_Operation", "Upsert",
+ "Generate_Id", "True",
+ NULL);
+
+ /* Enable test mode */
+ ret = flb_output_set_test(ctx, out_ffd, "formatter",
+ cb_check_write_op_upsert,
+ NULL, NULL);
+
+ /* Start */
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data sample */
+ flb_lib_push(ctx, in_ffd, (char *) JSON_ES, size);
+
+ sleep(2);
+ flb_stop(ctx);
+ flb_destroy(ctx);
+}
+
+void flb_test_index_type()
+{
+ int ret;
+ int size = sizeof(JSON_ES) - 1;
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+
+ /* Create context, flush every second (some checks omitted here) */
+ ctx = flb_create();
+ flb_service_set(ctx, "flush", "1", "grace", "1", NULL);
+
+ /* Lib input mode */
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ flb_input_set(ctx, in_ffd, "tag", "test", NULL);
+
+ /* Elasticsearch output */
+ out_ffd = flb_output(ctx, (char *) "es", NULL);
+ flb_output_set(ctx, out_ffd,
+ "match", "test",
+ NULL);
+
+ /* Override defaults of index and type */
+ flb_output_set(ctx, out_ffd,
+ "index", "index_test",
+ "type", "type_test",
+ NULL);
+
+ /* Enable test mode */
+ ret = flb_output_set_test(ctx, out_ffd, "formatter",
+ cb_check_index_type,
+ NULL, NULL);
+
+ /* Start */
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data sample */
+ flb_lib_push(ctx, in_ffd, (char *) JSON_ES, size);
+
+ sleep(2);
+ flb_stop(ctx);
+ flb_destroy(ctx);
+}
+
+void flb_test_logstash_format()
+{
+ int ret;
+ int size = sizeof(JSON_ES) - 1;
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+
+ /* Create context, flush every second (some checks omitted here) */
+ ctx = flb_create();
+ flb_service_set(ctx, "flush", "1", "grace", "1", NULL);
+
+ /* Lib input mode */
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ flb_input_set(ctx, in_ffd, "tag", "test", NULL);
+
+ /* Elasticsearch output */
+ out_ffd = flb_output(ctx, (char *) "es", NULL);
+ flb_output_set(ctx, out_ffd,
+ "match", "test",
+ NULL);
+
+ /* Override defaults of index and type */
+ flb_output_set(ctx, out_ffd,
+ "logstash_format", "on",
+ "logstash_prefix", "prefix",
+ "logstash_dateformat", "%Y-%m-%d",
+ NULL);
+
+ /* Enable test mode */
+ ret = flb_output_set_test(ctx, out_ffd, "formatter",
+ cb_check_logstash_format,
+ NULL, NULL);
+
+ /* Start */
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data sample */
+ flb_lib_push(ctx, in_ffd, (char *) JSON_ES, size);
+
+ sleep(2);
+ flb_stop(ctx);
+ flb_destroy(ctx);
+}
+
+void flb_test_logstash_format_nanos()
+{
+ int ret;
+ int size = sizeof(JSON_ES) - 1;
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+
+ /* Create context, flush every second (some checks omitted here) */
+ ctx = flb_create();
+ flb_service_set(ctx, "flush", "1", "grace", "1", NULL);
+
+ /* Lib input mode */
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ flb_input_set(ctx, in_ffd, "tag", "test", NULL);
+
+ /* Elasticsearch output */
+ out_ffd = flb_output(ctx, (char *) "es", NULL);
+ flb_output_set(ctx, out_ffd,
+ "match", "test",
+ NULL);
+
+ /* Override defaults of index and type */
+ flb_output_set(ctx, out_ffd,
+ "logstash_format", "on",
+ "logstash_prefix", "prefix",
+ "logstash_dateformat", "%Y-%m-%d",
+ "time_key_nanos", "on",
+ NULL);
+
+ /* Enable test mode */
+ ret = flb_output_set_test(ctx, out_ffd, "formatter",
+ cb_check_logstash_format_nanos,
+ NULL, NULL);
+
+ /* Start */
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data sample */
+ flb_lib_push(ctx, in_ffd, (char *) JSON_ES, size);
+
+ sleep(2);
+ flb_stop(ctx);
+ flb_destroy(ctx);
+}
+
+void flb_test_tag_key()
+{
+ int ret;
+ int size = sizeof(JSON_ES) - 1;
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+
+ /* Create context, flush every second (some checks omitted here) */
+ ctx = flb_create();
+ flb_service_set(ctx, "flush", "1", "grace", "1", NULL);
+
+ /* Lib input mode */
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ flb_input_set(ctx, in_ffd, "tag", "test", NULL);
+
+ /* Elasticsearch output */
+ out_ffd = flb_output(ctx, (char *) "es", NULL);
+ flb_output_set(ctx, out_ffd,
+ "match", "test",
+ NULL);
+
+ /* Override defaults of index and type */
+ flb_output_set(ctx, out_ffd,
+ "include_tag_key", "on",
+ "tag_key", "mytag",
+ NULL);
+
+ /* Enable test mode */
+ ret = flb_output_set_test(ctx, out_ffd, "formatter",
+ cb_check_tag_key,
+ NULL, NULL);
+
+ /* Start */
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data sample */
+ flb_lib_push(ctx, in_ffd, (char *) JSON_ES, size);
+
+ sleep(2);
+ flb_stop(ctx);
+ flb_destroy(ctx);
+}
+
+void flb_test_replace_dots()
+{
+ int ret;
+ int size = sizeof(JSON_DOTS) - 1;
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+
+ /* Create context, flush every second (some checks omitted here) */
+ ctx = flb_create();
+ flb_service_set(ctx, "flush", "1", "grace", "1", NULL);
+
+ /* Lib input mode */
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ flb_input_set(ctx, in_ffd, "tag", "test", NULL);
+
+ /* Elasticsearch output */
+ out_ffd = flb_output(ctx, (char *) "es", NULL);
+ flb_output_set(ctx, out_ffd,
+ "match", "test",
+ NULL);
+
+ /* Override defaults of index and type */
+ flb_output_set(ctx, out_ffd,
+ "replace_dots", "on",
+ NULL);
+
+ /* Enable test mode */
+ ret = flb_output_set_test(ctx, out_ffd, "formatter",
+ cb_check_replace_dots,
+ NULL, NULL);
+
+ /* Start */
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data sample */
+ flb_lib_push(ctx, in_ffd, (char *) JSON_DOTS, size);
+
+ sleep(2);
+ flb_stop(ctx);
+ flb_destroy(ctx);
+}
+
+void flb_test_id_key()
+{
+ int ret;
+ int size = sizeof(JSON_ES) - 1;
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+
+ /* Create context, flush every second (some checks omitted here) */
+ ctx = flb_create();
+ flb_service_set(ctx, "flush", "1", "grace", "1", NULL);
+
+ /* Lib input mode */
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ flb_input_set(ctx, in_ffd, "tag", "test", NULL);
+
+ /* Elasticsearch output */
+ out_ffd = flb_output(ctx, (char *) "es", NULL);
+ flb_output_set(ctx, out_ffd,
+ "match", "test",
+ NULL);
+
+ /* Override defaults of index and type */
+ flb_output_set(ctx, out_ffd,
+ "id_key", "key_2",
+ NULL);
+
+ /* Enable test mode */
+ ret = flb_output_set_test(ctx, out_ffd, "formatter",
+ cb_check_id_key,
+ NULL, NULL);
+
+ /* Start */
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data sample */
+ flb_lib_push(ctx, in_ffd, (char *) JSON_ES, size);
+
+ sleep(2);
+ flb_stop(ctx);
+ flb_destroy(ctx);
+}
+
+/* no check */
+static void cb_check_nothing(void *ctx, int ffd,
+ int res_ret, void *res_data, size_t res_size,
+ void *data)
+{
+ flb_free(res_data);
+}
+
+/* https://github.com/fluent/fluent-bit/issues/3905 */
+void flb_test_div0()
+{
+ int ret;
+ char record[8000];
+ char record_header[] = "[1448403340,{\"key\":\"";
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+ int i;
+
+ /* Create context, flush every second (some checks omitted here) */
+ ctx = flb_create();
+ flb_service_set(ctx, "flush", "1", "grace", "1", NULL);
+
+ /* Lib input mode */
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ flb_input_set(ctx, in_ffd, "tag", "test", NULL);
+
+ /* Elasticsearch output */
+ out_ffd = flb_output(ctx, (char *) "es", NULL);
+ flb_output_set(ctx, out_ffd,
+ "match", "test",
+ NULL);
+
+ /* Override defaults of index and type */
+ flb_output_set(ctx, out_ffd,
+ NULL);
+
+ /* Enable test mode */
+ ret = flb_output_set_test(ctx, out_ffd, "formatter",
+ cb_check_nothing,
+ NULL, NULL);
+
+ /* Start */
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ /* create json */
+ strncpy(&record[0], &record_header[0], strlen(record_header));
+ for(i=strlen(record_header); i<sizeof(record)-4; i++) {
+ record[i] = 'a';
+ }
+ record[sizeof(record)-4] = '"';
+ record[sizeof(record)-3] = '}';
+ record[sizeof(record)-2] = ']';
+ record[sizeof(record)-1] = '\0';
+ /* Ingest data sample */
+ flb_lib_push(ctx, in_ffd, (char *) &record[0], strlen(record));
+
+ sleep(2);
+ flb_stop(ctx);
+ flb_destroy(ctx);
+}
+
+
+static void cb_check_long_index(void *ctx, int ffd,
+ int res_ret, void *res_data, size_t res_size,
+ void *data)
+{
+ char *p;
+ char *out_js = res_data;
+ char long_index[256] = {0};
+ int i;
+
+ for (i=0; i<sizeof(long_index)-1; i++) {
+ long_index[i] = '0' + (i%10);
+ }
+
+ p = strstr(out_js, &long_index[0]);
+ TEST_CHECK(p != NULL);
+ flb_free(res_data);
+}
+
+/* https://github.com/fluent/fluent-bit/issues/4311 */
+void flb_test_long_index()
+{
+ int ret;
+ int size = sizeof(JSON_ES) -1;
+ char long_index[256] = {0};
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+ int i;
+
+ for (i=0; i<sizeof(long_index)-1; i++) {
+ long_index[i] = '0' + (i%10);
+ }
+
+ /* Create context, flush every second (some checks omitted here) */
+ ctx = flb_create();
+ flb_service_set(ctx, "flush", "1", "grace", "1", NULL);
+
+ /* Lib input mode */
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ flb_input_set(ctx, in_ffd, "tag", "test", NULL);
+
+ /* Elasticsearch output */
+ out_ffd = flb_output(ctx, (char *) "es", NULL);
+ flb_output_set(ctx, out_ffd,
+ "match", "test",
+ "generate_id", "true",
+ "index", &long_index[0],
+ NULL);
+
+ /* Override defaults of index and type */
+ flb_output_set(ctx, out_ffd,
+ NULL);
+
+ /* Enable test mode */
+ ret = flb_output_set_test(ctx, out_ffd, "formatter",
+ cb_check_long_index,
+ NULL, NULL);
+
+ /* Start */
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data sample */
+ flb_lib_push(ctx, in_ffd, (char *)JSON_ES, size);
+
+ sleep(2);
+ flb_stop(ctx);
+ flb_destroy(ctx);
+}
+
+void flb_test_logstash_prefix_separator()
+{
+ int ret;
+ int size = sizeof(JSON_ES) - 1;
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+
+ /* Create context, flush every second (some checks omitted here) */
+ ctx = flb_create();
+ flb_service_set(ctx, "flush", "1", "grace", "1", NULL);
+
+ /* Lib input mode */
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ flb_input_set(ctx, in_ffd, "tag", "test", NULL);
+
+ /* Elasticsearch output */
+ out_ffd = flb_output(ctx, (char *) "es", NULL);
+ flb_output_set(ctx, out_ffd,
+ "match", "test",
+ NULL);
+
+ /* Override defaults of index and type */
+ flb_output_set(ctx, out_ffd,
+ "logstash_format", "on",
+ "logstash_prefix", "prefix",
+ "logstash_prefix_separator", "SEP",
+ "logstash_dateformat", "%Y-%m-%d",
+ NULL);
+
+ /* Enable test mode */
+ ret = flb_output_set_test(ctx, out_ffd, "formatter",
+ cb_check_logstash_prefix_separator,
+ NULL, NULL);
+
+ /* Start */
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data sample */
+ flb_lib_push(ctx, in_ffd, (char *) JSON_ES, size);
+
+ sleep(2);
+ flb_stop(ctx);
+ flb_destroy(ctx);
+}
+
+/* Test list */
+TEST_LIST = {
+ {"long_index" , flb_test_long_index },
+ {"div0_error" , flb_test_div0 },
+ {"write_operation_index" , flb_test_write_operation_index },
+ {"write_operation_create", flb_test_write_operation_create },
+ {"write_operation_update", flb_test_write_operation_update },
+ {"write_operation_upsert", flb_test_write_operation_upsert },
+ {"index_type" , flb_test_index_type },
+ {"logstash_format" , flb_test_logstash_format },
+ {"logstash_format_nanos" , flb_test_logstash_format_nanos },
+ {"tag_key" , flb_test_tag_key },
+ {"replace_dots" , flb_test_replace_dots },
+ {"id_key" , flb_test_id_key },
+ {"logstash_prefix_separator" , flb_test_logstash_prefix_separator },
+ {NULL, NULL}
+};
diff --git a/fluent-bit/tests/runtime/out_exit.c b/fluent-bit/tests/runtime/out_exit.c
new file mode 100644
index 00000000..7ddd70de
--- /dev/null
+++ b/fluent-bit/tests/runtime/out_exit.c
@@ -0,0 +1,210 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+#include <fluent-bit.h>
+#include "flb_tests_runtime.h"
+
+/* Test data */
+#include "data/common/json_invalid.h" /* JSON_INVALID */
+#include "data/common/json_long.h" /* JSON_LONG */
+#include "data/common/json_small.h" /* JSON_SMALL */
+
+/* Test functions */
+void flb_test_exit_json_invalid(void);
+void flb_test_exit_json_long(void);
+void flb_test_exit_json_small(void);
+void flb_test_exit_keep_alive(void);
+void flb_test_exit_clean_shutdown(void);
+
+/* Test list */
+TEST_LIST = {
+ {"json_invalid", flb_test_exit_json_invalid },
+ {"json_long", flb_test_exit_json_long },
+ {"json_small", flb_test_exit_json_small },
+ {"keep_alive", flb_test_exit_keep_alive },
+ {"clean_shutdown", flb_test_exit_clean_shutdown},
+ {NULL, NULL}
+};
+
+
+#define WAIT_STOP (5+1) /* pause in flb_engine_stop and buffer period */
+
+void flb_test_exit_json_invalid(void)
+{
+ int i;
+ int ret;
+ int bytes;
+ char *p = (char *) JSON_INVALID;
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+
+ ctx = flb_create();
+ flb_service_set(ctx, "Flush", "1", "Grace", "1", "Log_Level", "error", NULL);
+
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ TEST_CHECK(in_ffd >= 0);
+ flb_input_set(ctx, in_ffd, "tag", "test", NULL);
+
+ out_ffd = flb_output(ctx, (char *) "exit", NULL);
+ TEST_CHECK(out_ffd >= 0);
+ flb_output_set(ctx, out_ffd, "match", "test", NULL);
+
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ for (i = 0; i < (int) sizeof(JSON_INVALID) - 1; i++) {
+ bytes = flb_lib_push(ctx, in_ffd, p + i, 1);
+ TEST_CHECK(bytes == 1);
+ }
+
+ sleep(WAIT_STOP); /* waiting stop automatically */
+
+ /* On invalid case, flb_stop() is not called from out_exit plugin.
+ * To shutdown normally and cleanly, it needs to call flb_stop() here. */
+ flb_stop(ctx);
+ flb_destroy(ctx);
+}
+
+/* It writes a very long JSON map (> 100KB) byte by byte */
+void flb_test_exit_json_long(void)
+{
+ int i;
+ int ret;
+ int bytes;
+ char *p = (char *) JSON_LONG;
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+
+ ctx = flb_create();
+ flb_service_set(ctx, "Flush", "1", "Grace", "1", "Log_Level", "error", NULL);
+
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ TEST_CHECK(in_ffd >= 0);
+ flb_input_set(ctx, in_ffd, "tag", "test", NULL);
+
+ out_ffd = flb_output(ctx, (char *) "exit", NULL);
+ TEST_CHECK(out_ffd >= 0);
+ flb_output_set(ctx, out_ffd, "match", "test", NULL);
+
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ for (i = 0; i < (int) sizeof(JSON_LONG) - 1; i++) {
+ bytes = flb_lib_push(ctx, in_ffd, p + i, 1);
+ TEST_CHECK(bytes == 1);
+ }
+
+ sleep(WAIT_STOP); /* waiting stop automatically */
+
+ /* call flb_stop() from out_exit plugin */
+ flb_destroy(ctx);
+}
+
+void flb_test_exit_json_small(void)
+{
+ int i;
+ int ret;
+ int bytes;
+ char *p = (char *) JSON_SMALL;
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+
+ ctx = flb_create();
+ flb_service_set(ctx, "Flush", "1", "Grace", "1", "Log_Level", "error", NULL);
+
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ TEST_CHECK(in_ffd >= 0);
+ flb_input_set(ctx, in_ffd, "tag", "test", NULL);
+
+ out_ffd = flb_output(ctx, (char *) "exit", NULL);
+ TEST_CHECK(out_ffd >= 0);
+ flb_output_set(ctx, out_ffd, "match", "test", NULL);
+
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ for (i = 0; i < (int) sizeof(JSON_SMALL) - 1; i++) {
+ bytes = flb_lib_push(ctx, in_ffd, p + i, 1);
+ TEST_CHECK(bytes == 1);
+ }
+
+ sleep(WAIT_STOP); /* waiting stop automatically */
+
+ /* call flb_stop() from out_exit plugin */
+ flb_destroy(ctx);
+}
+
+void flb_test_exit_keep_alive(void)
+{
+ int i;
+ int ret;
+ int bytes;
+ char *p = (char *) JSON_SMALL;
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+
+ ctx = flb_create();
+ flb_service_set(ctx, "Flush", "1", "Grace", "1", "Log_Level", "error", NULL);
+
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ TEST_CHECK(in_ffd >= 0);
+ flb_input_set(ctx, in_ffd, "tag", "test", NULL);
+
+ out_ffd = flb_output(ctx, (char *) "exit", NULL);
+ TEST_CHECK(out_ffd >= 0);
+ flb_output_set(ctx, out_ffd, "match", "test", NULL);
+ flb_output_set(ctx, out_ffd, "flush_count", "10", NULL);
+
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ for (i = 0; i < (int) sizeof(JSON_SMALL) - 1; i++) {
+ bytes = flb_lib_push(ctx, in_ffd, p + i, 1);
+ TEST_CHECK(bytes == 1);
+ }
+
+ sleep(WAIT_STOP); /* waiting flush */
+
+ flb_stop(ctx);
+ flb_destroy(ctx);
+}
+
+void flb_test_exit_clean_shutdown(void)
+{
+ int i;
+ int ret;
+ int bytes;
+ char *p = (char *) JSON_SMALL;
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+
+ ctx = flb_create();
+ flb_service_set(ctx, "Flush", "1", "Grace", "-1", "Log_Level", "error", NULL);
+
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ TEST_CHECK(in_ffd >= 0);
+ flb_input_set(ctx, in_ffd, "tag", "test", NULL);
+
+ out_ffd = flb_output(ctx, (char *) "exit", NULL);
+ TEST_CHECK(out_ffd >= 0);
+ flb_output_set(ctx, out_ffd, "match", "test", NULL);
+ flb_output_set(ctx, out_ffd, "flush_count", "10", NULL);
+
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ for (i = 0; i < (int) sizeof(JSON_SMALL) - 1; i++) {
+ bytes = flb_lib_push(ctx, in_ffd, p + i, 1);
+ TEST_CHECK(bytes == 1);
+ }
+
+ sleep(WAIT_STOP); /* waiting flush */
+
+ flb_stop(ctx);
+ flb_destroy(ctx);
+}
+
diff --git a/fluent-bit/tests/runtime/out_file.c b/fluent-bit/tests/runtime/out_file.c
new file mode 100644
index 00000000..33b1440d
--- /dev/null
+++ b/fluent-bit/tests/runtime/out_file.c
@@ -0,0 +1,824 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+#include <fluent-bit.h>
+#include <fluent-bit/flb_sds.h>
+#include "flb_tests_runtime.h"
+#include <sys/stat.h>
+#include <sys/types.h>
+
+/* Test data */
+#include "data/common/json_invalid.h" /* JSON_INVALID */
+#include "data/common/json_long.h" /* JSON_LONG */
+#include "data/common/json_small.h" /* JSON_SMALL */
+
+/* Test functions */
+void flb_test_file_json_invalid(void);
+void flb_test_file_json_long(void);
+void flb_test_file_json_small(void);
+void flb_test_file_format_csv(void);
+void flb_test_file_format_ltsv(void);
+void flb_test_file_format_invalid(void);
+void flb_test_file_format_out_file(void);
+void flb_test_file_path_file(void);
+void flb_test_file_path(void);
+void flb_test_file_delim_csv(void);
+void flb_test_file_delim_ltsv(void);
+void flb_test_file_label_delim(void);
+void flb_test_file_template(void);
+void flb_test_file_mkdir(void);
+
+/* Test list */
+TEST_LIST = {
+ {"path", flb_test_file_path},
+ {"path_file", flb_test_file_path_file},
+ {"mkdir", flb_test_file_mkdir},
+ {"template", flb_test_file_template},
+ {"delimiter_ltsv", flb_test_file_delim_ltsv},
+ {"delimiter_csv", flb_test_file_delim_csv},
+ {"label_delimiter", flb_test_file_label_delim},
+ {"json_invalid", flb_test_file_json_invalid },
+ {"json_long", flb_test_file_json_long },
+ {"json_small", flb_test_file_json_small },
+ {"format_csv", flb_test_file_format_csv },
+ {"format_ltsv", flb_test_file_format_ltsv },
+ {"format_invalid", flb_test_file_format_invalid },
+ {"format_out_file", flb_test_file_format_out_file},
+
+ {NULL, NULL}
+};
+
+
+#define TEST_LOGFILE "flb_test_file_dummy.log"
+#define TEST_LOGPATH "out_file"
+#define TEST_TIMEOUT 5
+
+void flb_test_file_json_invalid(void)
+{
+ int i;
+ int ret;
+ int bytes;
+ char *p = (char *) JSON_INVALID;
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+ FILE *fp;
+
+ remove(TEST_LOGFILE);
+
+ ctx = flb_create();
+ flb_service_set(ctx, "Flush", "1", "Grace", "1", "Log_Level", "error", NULL);
+
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ TEST_CHECK(in_ffd >= 0);
+ flb_input_set(ctx, in_ffd, "tag", "test", NULL);
+
+ out_ffd = flb_output(ctx, (char *) "file", NULL);
+ TEST_CHECK(out_ffd >= 0);
+ flb_output_set(ctx, out_ffd, "match", "test", NULL);
+ flb_output_set(ctx, out_ffd, "file", TEST_LOGFILE, NULL);
+
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ for (i = 0; i < (int) sizeof(JSON_INVALID) - 1; i++) {
+ bytes = flb_lib_push(ctx, in_ffd, p + i, 1);
+ TEST_CHECK(bytes == 1);
+ }
+
+ flb_stop(ctx);
+ flb_destroy(ctx);
+
+ fp = fopen(TEST_LOGFILE, "r");
+ TEST_CHECK(fp == NULL);
+ if (fp != NULL) {
+ fclose(fp);
+ remove(TEST_LOGFILE);
+ }
+}
+
+/* It writes a very long JSON map (> 100KB) byte by byte */
+void flb_test_file_json_long(void)
+{
+ int i;
+ int ret;
+ int bytes;
+ char *p = (char *) JSON_LONG;
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+ FILE *fp;
+
+ remove(TEST_LOGFILE);
+
+ ctx = flb_create();
+ flb_service_set(ctx, "Flush", "1", "Grace", "1", "Log_Level", "error", NULL);
+
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ TEST_CHECK(in_ffd >= 0);
+ flb_input_set(ctx, in_ffd, "tag", "test", NULL);
+
+ out_ffd = flb_output(ctx, (char *) "file", NULL);
+ TEST_CHECK(out_ffd >= 0);
+ flb_output_set(ctx, out_ffd, "match", "test", NULL);
+ flb_output_set(ctx, out_ffd, "file", TEST_LOGFILE, NULL);
+
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ for (i = 0; i < (int) sizeof(JSON_LONG) - 1; i++) {
+ bytes = flb_lib_push(ctx, in_ffd, p + i, 1);
+ TEST_CHECK(bytes == 1);
+ }
+
+ ret = wait_for_file(TEST_LOGFILE, 1, TEST_TIMEOUT);
+ TEST_CHECK(ret == 0);
+
+ flb_stop(ctx);
+ flb_destroy(ctx);
+
+ fp = fopen(TEST_LOGFILE, "r");
+ TEST_CHECK(fp != NULL);
+ if (fp != NULL) {
+ fclose(fp);
+ remove(TEST_LOGFILE);
+ }
+}
+
+void flb_test_file_json_small(void)
+{
+ int i;
+ int ret;
+ int bytes;
+ char *p = (char *) JSON_SMALL;
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+ FILE *fp;
+
+ remove(TEST_LOGFILE);
+
+ ctx = flb_create();
+ flb_service_set(ctx, "Flush", "1", "Grace", "1", "Log_Level", "error", NULL);
+
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ TEST_CHECK(in_ffd >= 0);
+ flb_input_set(ctx, in_ffd, "tag", "test", NULL);
+
+ out_ffd = flb_output(ctx, (char *) "file", NULL);
+ TEST_CHECK(out_ffd >= 0);
+ flb_output_set(ctx, out_ffd, "match", "test", NULL);
+ flb_output_set(ctx, out_ffd, "file", TEST_LOGFILE, NULL);
+
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ for (i = 0; i < (int) sizeof(JSON_SMALL) - 1; i++) {
+ bytes = flb_lib_push(ctx, in_ffd, p + i, 1);
+ TEST_CHECK(bytes == 1);
+ }
+
+ ret = wait_for_file(TEST_LOGFILE, 1, TEST_TIMEOUT);
+ TEST_CHECK(ret == 0);
+
+ flb_stop(ctx);
+ flb_destroy(ctx);
+
+ fp = fopen(TEST_LOGFILE, "r");
+ TEST_CHECK(fp != NULL);
+ if (fp != NULL) {
+ fclose(fp);
+ remove(TEST_LOGFILE);
+ }
+}
+
+void flb_test_file_format_csv(void)
+{
+ int i;
+ int ret;
+ int bytes;
+ char *p = (char *) JSON_SMALL;
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+ FILE *fp;
+
+ remove(TEST_LOGFILE);
+
+ ctx = flb_create();
+ flb_service_set(ctx, "Flush", "1", "Grace", "1", "Log_Level", "error", NULL);
+
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ TEST_CHECK(in_ffd >= 0);
+ flb_input_set(ctx, in_ffd, "tag", "test", NULL);
+
+ out_ffd = flb_output(ctx, (char *) "file", NULL);
+ TEST_CHECK(out_ffd >= 0);
+ flb_output_set(ctx, out_ffd, "match", "test", NULL);
+ flb_output_set(ctx, out_ffd, "file", TEST_LOGFILE, NULL);
+ flb_output_set(ctx, out_ffd, "format", "csv", NULL);
+ flb_output_set(ctx, out_ffd, "delimiter", "comma", NULL);
+
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ for (i = 0; i < (int) sizeof(JSON_SMALL) - 1; i++) {
+ bytes = flb_lib_push(ctx, in_ffd, p + i, 1);
+ TEST_CHECK(bytes == 1);
+ }
+
+ ret = wait_for_file(TEST_LOGFILE, 1, TEST_TIMEOUT);
+ TEST_CHECK(ret == 0);
+
+ flb_stop(ctx);
+ flb_destroy(ctx);
+
+ fp = fopen(TEST_LOGFILE, "r");
+ TEST_CHECK(fp != NULL);
+ if (fp != NULL) {
+ fclose(fp);
+ remove(TEST_LOGFILE);
+ }
+}
+
+void flb_test_file_format_ltsv(void)
+{
+ int i;
+ int ret;
+ int bytes;
+ char *p = (char *) JSON_SMALL;
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+ FILE *fp;
+
+ remove(TEST_LOGFILE);
+
+ ctx = flb_create();
+ flb_service_set(ctx, "Flush", "1", "Grace", "1", "Log_Level", "error", NULL);
+
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ TEST_CHECK(in_ffd >= 0);
+ flb_input_set(ctx, in_ffd, "tag", "test", NULL);
+
+ out_ffd = flb_output(ctx, (char *) "file", NULL);
+ TEST_CHECK(out_ffd >= 0);
+ flb_output_set(ctx, out_ffd, "match", "test", NULL);
+ flb_output_set(ctx, out_ffd, "file", TEST_LOGFILE, NULL);
+ flb_output_set(ctx, out_ffd, "format", "ltsv", NULL);
+ flb_output_set(ctx, out_ffd, "delimiter", "tab", NULL);
+ flb_output_set(ctx, out_ffd, "label_delimiter", "comma", NULL);
+
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ for (i = 0; i < (int) sizeof(JSON_SMALL) - 1; i++) {
+ bytes = flb_lib_push(ctx, in_ffd, p + i, 1);
+ TEST_CHECK(bytes == 1);
+ }
+
+ ret = wait_for_file(TEST_LOGFILE, 1, TEST_TIMEOUT);
+ TEST_CHECK(ret == 0);
+
+ flb_stop(ctx);
+ flb_destroy(ctx);
+
+ fp = fopen(TEST_LOGFILE, "r");
+ TEST_CHECK(fp != NULL);
+ if (fp != NULL) {
+ fclose(fp);
+ remove(TEST_LOGFILE);
+ }
+}
+
+/* https://github.com/fluent/fluent-bit/issues/4152 */
+void flb_test_file_format_out_file(void)
+{
+ int i;
+ int ret;
+ int bytes;
+ char *p = (char *) JSON_SMALL;
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+ FILE *fp;
+
+ remove(TEST_LOGFILE);
+
+ ctx = flb_create();
+ flb_service_set(ctx, "Flush", "1", "Grace", "1", "Log_Level", "error", NULL);
+
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ TEST_CHECK(in_ffd >= 0);
+ flb_input_set(ctx, in_ffd, "tag", "test", NULL);
+
+ out_ffd = flb_output(ctx, (char *) "file", NULL);
+ TEST_CHECK(out_ffd >= 0);
+ flb_output_set(ctx, out_ffd, "match", "test", NULL);
+ flb_output_set(ctx, out_ffd, "file", TEST_LOGFILE, NULL);
+ flb_output_set(ctx, out_ffd, "format", "out_file", NULL);
+
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ for (i = 0; i < (int) sizeof(JSON_SMALL) - 1; i++) {
+ bytes = flb_lib_push(ctx, in_ffd, p + i, 1);
+ TEST_CHECK(bytes == 1);
+ }
+
+ ret = wait_for_file(TEST_LOGFILE, 1, TEST_TIMEOUT);
+ TEST_CHECK(ret == 0);
+
+ flb_stop(ctx);
+ flb_destroy(ctx);
+
+ fp = fopen(TEST_LOGFILE, "r");
+ TEST_CHECK(fp != NULL);
+ if (fp != NULL) {
+ fclose(fp);
+ remove(TEST_LOGFILE);
+ }
+}
+
+void flb_test_file_format_invalid(void)
+{
+ int ret;
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+ FILE *fp;
+
+ remove(TEST_LOGFILE);
+
+ ctx = flb_create();
+ flb_service_set(ctx, "Flush", "1", "Grace", "1", "Log_Level", "off", NULL);
+
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ TEST_CHECK(in_ffd >= 0);
+ flb_input_set(ctx, in_ffd, "tag", "test", NULL);
+
+ out_ffd = flb_output(ctx, (char *) "file", NULL);
+ TEST_CHECK(out_ffd >= 0);
+ flb_output_set(ctx, out_ffd, "match", "test", NULL);
+ flb_output_set(ctx, out_ffd, "file", TEST_LOGFILE, NULL);
+ flb_output_set(ctx, out_ffd, "format", "xxx", NULL);
+ flb_output_set(ctx, out_ffd, "delimiter", "yyy", NULL);
+ flb_output_set(ctx, out_ffd, "label_delimiter", "zzz", NULL);
+
+ ret = flb_start(ctx);
+ if (!TEST_CHECK(ret != 0)) {
+ TEST_MSG("invalid format should be error");
+
+ flb_stop(ctx);
+ flb_destroy(ctx);
+ fp = fopen(TEST_LOGFILE, "r");
+ TEST_CHECK(fp != NULL);
+ if (fp != NULL) {
+ fclose(fp);
+ remove(TEST_LOGFILE);
+ }
+ }
+ else {
+ flb_destroy(ctx);
+ }
+}
+
+void flb_test_file_path(void)
+{
+ int i;
+ int ret;
+ int bytes;
+ char *p = (char *) JSON_LONG;
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+ FILE *fp;
+ flb_sds_t path;
+ flb_sds_t file;
+
+ file = flb_sds_create("test");
+ if (!TEST_CHECK(file != NULL)) {
+ TEST_MSG("flb_sds_create failed");
+ return;
+ }
+
+ path = flb_sds_create_size(256);
+ if (!TEST_CHECK(path != NULL)) {
+ TEST_MSG("flb_sds_create_size failed");
+ flb_sds_destroy(file);
+ return;
+ }
+ flb_sds_printf(&path, "%s/%s", TEST_LOGPATH, file);
+
+ remove(path);
+ remove(TEST_LOGPATH);
+ ret = mkdir(TEST_LOGPATH, S_IRUSR | S_IWUSR | S_IXUSR);
+ if (!TEST_CHECK(ret == 0)) {
+ TEST_MSG("mkdir failed:path=%s errno=%d",TEST_LOGPATH, errno);
+ flb_sds_destroy(path);
+ flb_sds_destroy(file);
+ return;
+ }
+
+ ctx = flb_create();
+ flb_service_set(ctx, "Flush", "1", "Grace", "1", "Log_Level", "error", NULL);
+
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ TEST_CHECK(in_ffd >= 0);
+ flb_input_set(ctx, in_ffd, "tag", file, NULL);
+
+ out_ffd = flb_output(ctx, (char *) "file", NULL);
+ TEST_CHECK(out_ffd >= 0);
+ flb_output_set(ctx, out_ffd, "match", "test", NULL);
+ flb_output_set(ctx, out_ffd, "path", TEST_LOGPATH, NULL);
+
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ for (i = 0; i < (int) sizeof(JSON_LONG) - 1; i++) {
+ bytes = flb_lib_push(ctx, in_ffd, p + i, 1);
+ TEST_CHECK(bytes == 1);
+ }
+
+ ret = wait_for_file(path, 1, TEST_TIMEOUT);
+ TEST_CHECK(ret == 0);
+
+ flb_stop(ctx);
+ flb_destroy(ctx);
+
+ fp = fopen(path, "r");
+ TEST_CHECK(fp != NULL);
+ if (fp != NULL) {
+ fclose(fp);
+ remove(path);
+ }
+ flb_sds_destroy(path);
+ flb_sds_destroy(file);
+ remove(TEST_LOGPATH);
+}
+
+void flb_test_file_path_file(void)
+{
+ int i;
+ int ret;
+ int bytes;
+ char *p = (char *) JSON_LONG;
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+ FILE *fp;
+ flb_sds_t path;
+
+ path = flb_sds_create_size(256);
+ if (!TEST_CHECK(path != NULL)) {
+ TEST_MSG("flb_sds_create_size failed");
+ return;
+ }
+ flb_sds_printf(&path, "%s/%s", TEST_LOGPATH, TEST_LOGFILE);
+
+ remove(path);
+ remove(TEST_LOGPATH);
+ ret = mkdir(TEST_LOGPATH, S_IRUSR | S_IWUSR | S_IXUSR);
+ if (!TEST_CHECK(ret == 0)) {
+ TEST_MSG("mkdir failed:path=%s errno=%d",TEST_LOGPATH, errno);
+ flb_sds_destroy(path);
+ return;
+ }
+
+ ctx = flb_create();
+ flb_service_set(ctx, "Flush", "1", "Grace", "1", "Log_Level", "error", NULL);
+
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ TEST_CHECK(in_ffd >= 0);
+ flb_input_set(ctx, in_ffd, "tag", "test", NULL);
+
+ out_ffd = flb_output(ctx, (char *) "file", NULL);
+ TEST_CHECK(out_ffd >= 0);
+ flb_output_set(ctx, out_ffd, "match", "test", NULL);
+ flb_output_set(ctx, out_ffd, "file", TEST_LOGFILE, NULL);
+ flb_output_set(ctx, out_ffd, "path", TEST_LOGPATH, NULL);
+
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ for (i = 0; i < (int) sizeof(JSON_LONG) - 1; i++) {
+ bytes = flb_lib_push(ctx, in_ffd, p + i, 1);
+ TEST_CHECK(bytes == 1);
+ }
+
+ ret = wait_for_file(path, 1, TEST_TIMEOUT);
+ TEST_CHECK(ret == 0);
+
+ flb_stop(ctx);
+ flb_destroy(ctx);
+
+ fp = fopen(path, "r");
+ TEST_CHECK(fp != NULL);
+ if (fp != NULL) {
+ fclose(fp);
+ remove(path);
+ }
+ flb_sds_destroy(path);
+ remove(TEST_LOGPATH);
+}
+
+#define JSON_BASIC "[1448403340,{\"key1\":\"val1\", \"key2\":\"val2\"}]"
+void flb_test_file_delim_csv(void)
+{
+ int ret;
+ int bytes;
+ char *p = JSON_BASIC;
+ char output[256] = {0};
+ char *expect = "1448403340.000000000 \"val1\" \"val2\"";
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+ FILE *fp;
+
+ remove(TEST_LOGFILE);
+
+ ctx = flb_create();
+ flb_service_set(ctx, "Flush", "1", "Grace", "1", "Log_Level", "error", NULL);
+
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ TEST_CHECK(in_ffd >= 0);
+ flb_input_set(ctx, in_ffd, "tag", "test", NULL);
+
+ out_ffd = flb_output(ctx, (char *) "file", NULL);
+ TEST_CHECK(out_ffd >= 0);
+ flb_output_set(ctx, out_ffd, "match", "test", NULL);
+ flb_output_set(ctx, out_ffd, "file", TEST_LOGFILE, NULL);
+ flb_output_set(ctx, out_ffd, "format", "csv", NULL);
+ flb_output_set(ctx, out_ffd, "delimiter", "space", NULL);
+
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ bytes = flb_lib_push(ctx, in_ffd, p, strlen(p));
+ TEST_CHECK(bytes == strlen(p));
+
+ ret = wait_for_file(TEST_LOGFILE, 1, TEST_TIMEOUT);
+ TEST_CHECK(ret == 0);
+
+ flb_stop(ctx);
+ flb_destroy(ctx);
+
+ fp = fopen(TEST_LOGFILE, "r");
+ TEST_CHECK(fp != NULL);
+ if (fp != NULL) {
+ bytes = fread(&output[0], sizeof(output), 1, fp);
+ if(!TEST_CHECK(bytes > 0 || feof(fp))) {
+ TEST_MSG("fread error bytes=%d", bytes);
+ }
+ if (!TEST_CHECK(strncmp(expect, &output[0], strlen(expect)) == 0)) {
+ TEST_MSG("format error\n");
+ TEST_MSG("expect: %s\n", expect);
+ TEST_MSG("got : %s",output);
+ }
+
+ fclose(fp);
+ remove(TEST_LOGFILE);
+ }
+}
+
+void flb_test_file_delim_ltsv(void)
+{
+ int ret;
+ int bytes;
+ char *p = JSON_BASIC;
+ char output[256] = {0};
+ char *expect = "\"time\":1448403340.000000 \"key1\":\"val1\" \"key2\":\"val2\"";
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+ FILE *fp;
+
+ remove(TEST_LOGFILE);
+
+ ctx = flb_create();
+ flb_service_set(ctx, "Flush", "1", "Grace", "1", "Log_Level", "error", NULL);
+
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ TEST_CHECK(in_ffd >= 0);
+ flb_input_set(ctx, in_ffd, "tag", "test", NULL);
+
+ out_ffd = flb_output(ctx, (char *) "file", NULL);
+ TEST_CHECK(out_ffd >= 0);
+ flb_output_set(ctx, out_ffd, "match", "test", NULL);
+ flb_output_set(ctx, out_ffd, "file", TEST_LOGFILE, NULL);
+ flb_output_set(ctx, out_ffd, "format", "ltsv", NULL);
+ flb_output_set(ctx, out_ffd, "delimiter", "space", NULL);
+
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ bytes = flb_lib_push(ctx, in_ffd, p, strlen(p));
+ TEST_CHECK(bytes == strlen(p));
+
+ ret = wait_for_file(TEST_LOGFILE, 1, TEST_TIMEOUT);
+ TEST_CHECK(ret == 0);
+
+ flb_stop(ctx);
+ flb_destroy(ctx);
+
+ fp = fopen(TEST_LOGFILE, "r");
+ TEST_CHECK(fp != NULL);
+ if (fp != NULL) {
+ bytes = fread(&output[0], sizeof(output), 1, fp);
+ if(!TEST_CHECK(bytes > 0 || feof(fp))) {
+ TEST_MSG("fread error bytes=%d", bytes);
+ }
+ if (!TEST_CHECK(strncmp(expect, &output[0], strlen(expect)) == 0)) {
+ TEST_MSG("format error\n");
+ TEST_MSG("expect: %s\n", expect);
+ TEST_MSG("got : %s",output);
+ }
+
+ fclose(fp);
+ remove(TEST_LOGFILE);
+ }
+}
+
+void flb_test_file_label_delim(void)
+{
+ int ret;
+ int bytes;
+ char *p = JSON_BASIC;
+ char output[256] = {0};
+ char *expect = "\"time\" 1448403340.000000 \"key1\" \"val1\" \"key2\" \"val2\"";
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+ FILE *fp;
+
+ remove(TEST_LOGFILE);
+
+ ctx = flb_create();
+ flb_service_set(ctx, "Flush", "1", "Grace", "1", "Log_Level", "error", NULL);
+
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ TEST_CHECK(in_ffd >= 0);
+ flb_input_set(ctx, in_ffd, "tag", "test", NULL);
+
+ out_ffd = flb_output(ctx, (char *) "file", NULL);
+ TEST_CHECK(out_ffd >= 0);
+ flb_output_set(ctx, out_ffd, "match", "test", NULL);
+ flb_output_set(ctx, out_ffd, "file", TEST_LOGFILE, NULL);
+ flb_output_set(ctx, out_ffd, "format", "ltsv", NULL);
+ flb_output_set(ctx, out_ffd, "delimiter", "space", NULL);
+ flb_output_set(ctx, out_ffd, "label_delimiter", "space", NULL);
+
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ bytes = flb_lib_push(ctx, in_ffd, p, strlen(p));
+ TEST_CHECK(bytes == strlen(p));
+
+ ret = wait_for_file(TEST_LOGFILE, 1, TEST_TIMEOUT);
+ TEST_CHECK(ret == 0);
+
+ flb_stop(ctx);
+ flb_destroy(ctx);
+
+ fp = fopen(TEST_LOGFILE, "r");
+ TEST_CHECK(fp != NULL);
+ if (fp != NULL) {
+ bytes = fread(&output[0], sizeof(output), 1, fp);
+ if(!TEST_CHECK(bytes > 0 || feof(fp))) {
+ TEST_MSG("fread error bytes=%d", bytes);
+ }
+ if (!TEST_CHECK(strncmp(expect, &output[0], strlen(expect)) == 0)) {
+ TEST_MSG("format error\n");
+ TEST_MSG("expect: %s\n", expect);
+ TEST_MSG("got : %s",output);
+ }
+
+ fclose(fp);
+ remove(TEST_LOGFILE);
+ }
+}
+
+void flb_test_file_template(void)
+{
+ int ret;
+ int bytes;
+ char *p = JSON_BASIC;
+ char output[256] = {0};
+ char *expect = "1448403340.000000 KEY1=val1 KEY2=val2";
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+ FILE *fp;
+
+ remove(TEST_LOGFILE);
+
+ ctx = flb_create();
+ flb_service_set(ctx, "Flush", "1", "Grace", "1", "Log_Level", "error", NULL);
+
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ TEST_CHECK(in_ffd >= 0);
+ flb_input_set(ctx, in_ffd, "tag", "test", NULL);
+
+ out_ffd = flb_output(ctx, (char *) "file", NULL);
+ TEST_CHECK(out_ffd >= 0);
+ flb_output_set(ctx, out_ffd, "match", "test", NULL);
+ flb_output_set(ctx, out_ffd, "file", TEST_LOGFILE, NULL);
+ flb_output_set(ctx, out_ffd, "format", "template", NULL);
+ flb_output_set(ctx, out_ffd, "template", "{time} KEY1={key1} KEY2={key2}", NULL);
+
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ bytes = flb_lib_push(ctx, in_ffd, p, strlen(p));
+ TEST_CHECK(bytes == strlen(p));
+
+ ret = wait_for_file(TEST_LOGFILE, 1, TEST_TIMEOUT);
+ TEST_CHECK(ret == 0);
+
+ flb_stop(ctx);
+ flb_destroy(ctx);
+
+ fp = fopen(TEST_LOGFILE, "r");
+ TEST_CHECK(fp != NULL);
+ if (fp != NULL) {
+ bytes = fread(&output[0], sizeof(output), 1, fp);
+ if(!TEST_CHECK(bytes > 0 || feof(fp))) {
+ TEST_MSG("fread error bytes=%d", bytes);
+ }
+ if (!TEST_CHECK(strncmp(expect, &output[0], strlen(expect)) == 0)) {
+ TEST_MSG("format error\n");
+ TEST_MSG("expect: %s\n", expect);
+ TEST_MSG("got : %s",output);
+ }
+
+ fclose(fp);
+ remove(TEST_LOGFILE);
+ }
+}
+
+void flb_test_file_mkdir(void)
+{
+ int i;
+ int ret;
+ int bytes;
+ char *p = (char *) JSON_LONG;
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+ FILE *fp;
+ flb_sds_t path;
+ flb_sds_t file;
+
+ file = flb_sds_create("test");
+ if (!TEST_CHECK(file != NULL)) {
+ TEST_MSG("flb_sds_create failed");
+ return;
+ }
+
+ path = flb_sds_create_size(256);
+ if (!TEST_CHECK(path != NULL)) {
+ TEST_MSG("flb_sds_create_size failed");
+ flb_sds_destroy(file);
+ return;
+ }
+ flb_sds_printf(&path, "%s/%s", TEST_LOGPATH, file);
+
+ remove(path);
+ remove(TEST_LOGPATH);
+
+ ctx = flb_create();
+ flb_service_set(ctx, "Flush", "1", "Grace", "1", "Log_Level", "error", NULL);
+
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ TEST_CHECK(in_ffd >= 0);
+ flb_input_set(ctx, in_ffd, "tag", file, NULL);
+
+ out_ffd = flb_output(ctx, (char *) "file", NULL);
+ TEST_CHECK(out_ffd >= 0);
+ flb_output_set(ctx, out_ffd, "match", "test", NULL);
+ flb_output_set(ctx, out_ffd, "path", TEST_LOGPATH, NULL);
+ flb_output_set(ctx, out_ffd, "mkdir", "true", NULL);
+
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ for (i = 0; i < (int) sizeof(JSON_LONG) - 1; i++) {
+ bytes = flb_lib_push(ctx, in_ffd, p + i, 1);
+ TEST_CHECK(bytes == 1);
+ }
+
+ ret = wait_for_file(path, 1, TEST_TIMEOUT);
+ TEST_CHECK(ret == 0);
+
+ flb_stop(ctx);
+ flb_destroy(ctx);
+
+ fp = fopen(path, "r");
+ TEST_CHECK(fp != NULL);
+ if (fp != NULL) {
+ fclose(fp);
+ remove(path);
+ }
+ flb_sds_destroy(path);
+ flb_sds_destroy(file);
+ remove(TEST_LOGPATH);
+}
diff --git a/fluent-bit/tests/runtime/out_firehose.c b/fluent-bit/tests/runtime/out_firehose.c
new file mode 100644
index 00000000..cead6b3c
--- /dev/null
+++ b/fluent-bit/tests/runtime/out_firehose.c
@@ -0,0 +1,200 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+#include <fluent-bit.h>
+#include "flb_tests_runtime.h"
+
+/* Test data */
+#include "data/td/json_td.h" /* JSON_TD */
+
+#define ERROR_THROUGHPUT "{\"__type\":\"ServiceUnavailableException\"}"
+/* not a real error code, but tests that the code can respond to any error */
+#define ERROR_UNKNOWN "{\"__type\":\"UNKNOWN\"}"
+
+/* It writes a big JSON message (copied from TD test) */
+void flb_test_firehose_success(void)
+{
+ int ret;
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+
+ /* mocks calls- signals that we are in test mode */
+ setenv("FLB_FIREHOSE_PLUGIN_UNDER_TEST", "true", 1);
+
+ ctx = flb_create();
+
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ TEST_CHECK(in_ffd >= 0);
+ flb_input_set(ctx,in_ffd, "tag", "test", NULL);
+
+ out_ffd = flb_output(ctx, (char *) "kinesis_firehose", NULL);
+ TEST_CHECK(out_ffd >= 0);
+ flb_output_set(ctx, out_ffd,"match", "*", NULL);
+ flb_output_set(ctx, out_ffd,"region", "us-west-2", NULL);
+ flb_output_set(ctx, out_ffd,"delivery_stream", "fluent", NULL);
+ flb_output_set(ctx, out_ffd,"time_key", "time", NULL);
+ flb_output_set(ctx, out_ffd,"Retry_Limit", "1", NULL);
+
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ flb_lib_push(ctx, in_ffd, (char *) JSON_TD , (int) sizeof(JSON_TD) - 1);
+
+ sleep(2);
+ flb_stop(ctx);
+ flb_destroy(ctx);
+}
+
+void flb_test_firehose_partial_success(void)
+{
+ int ret;
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+
+ /* mocks calls- signals that we are in test mode */
+ setenv("FLB_FIREHOSE_PLUGIN_UNDER_TEST", "true", 1);
+ setenv("PARTIAL_SUCCESS_CASE", "true", 1);
+
+ ctx = flb_create();
+
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ TEST_CHECK(in_ffd >= 0);
+ flb_input_set(ctx,in_ffd, "tag", "test", NULL);
+
+ out_ffd = flb_output(ctx, (char *) "kinesis_firehose", NULL);
+ TEST_CHECK(out_ffd >= 0);
+ flb_output_set(ctx, out_ffd,"match", "*", NULL);
+ flb_output_set(ctx, out_ffd,"region", "us-west-2", NULL);
+ flb_output_set(ctx, out_ffd,"delivery_stream", "fluent", NULL);
+ flb_output_set(ctx, out_ffd,"time_key", "time", NULL);
+ flb_output_set(ctx, out_ffd,"Retry_Limit", "1", NULL);
+
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ flb_lib_push(ctx, in_ffd, (char *) JSON_TD , (int) sizeof(JSON_TD) - 1);
+
+ sleep(2);
+ flb_stop(ctx);
+ flb_destroy(ctx);
+ unsetenv("PARTIAL_SUCCESS_CASE");
+}
+
+void flb_test_firehose_throughput_error(void)
+{
+ int ret;
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+
+ /* mocks calls- signals that we are in test mode */
+ setenv("FLB_FIREHOSE_PLUGIN_UNDER_TEST", "true", 1);
+ setenv("TEST_PUT_RECORD_BATCH_ERROR", ERROR_THROUGHPUT, 1);
+
+ ctx = flb_create();
+
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ TEST_CHECK(in_ffd >= 0);
+ flb_input_set(ctx,in_ffd, "tag", "test", NULL);
+
+ out_ffd = flb_output(ctx, (char *) "kinesis_firehose", NULL);
+ TEST_CHECK(out_ffd >= 0);
+ flb_output_set(ctx, out_ffd,"match", "*", NULL);
+ flb_output_set(ctx, out_ffd,"region", "us-west-2", NULL);
+ flb_output_set(ctx, out_ffd,"delivery_stream", "fluent", NULL);
+ flb_output_set(ctx, out_ffd,"time_key", "time", NULL);
+ flb_output_set(ctx, out_ffd,"Retry_Limit", "1", NULL);
+
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ flb_lib_push(ctx, in_ffd, (char *) JSON_TD , (int) sizeof(JSON_TD) - 1);
+
+ sleep(2);
+ flb_stop(ctx);
+ flb_destroy(ctx);
+ unsetenv("TEST_PUT_RECORD_BATCH_ERROR");
+}
+
+void flb_test_firehose_error_unknown(void)
+{
+ int ret;
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+
+ /* mocks calls- signals that we are in test mode */
+ setenv("FLB_FIREHOSE_PLUGIN_UNDER_TEST", "true", 1);
+ setenv("TEST_PUT_RECORD_BATCH_ERROR", ERROR_UNKNOWN, 1);
+
+ ctx = flb_create();
+
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ TEST_CHECK(in_ffd >= 0);
+ flb_input_set(ctx,in_ffd, "tag", "test", NULL);
+
+ out_ffd = flb_output(ctx, (char *) "kinesis_firehose", NULL);
+ TEST_CHECK(out_ffd >= 0);
+ flb_output_set(ctx, out_ffd,"match", "*", NULL);
+ flb_output_set(ctx, out_ffd,"region", "us-west-2", NULL);
+ flb_output_set(ctx, out_ffd,"delivery_stream", "fluent", NULL);
+ flb_output_set(ctx, out_ffd,"time_key", "time", NULL);
+ flb_output_set(ctx, out_ffd,"Retry_Limit", "1", NULL);
+
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ flb_lib_push(ctx, in_ffd, (char *) JSON_TD , (int) sizeof(JSON_TD) - 1);
+
+ sleep(2);
+ flb_stop(ctx);
+ flb_destroy(ctx);
+ unsetenv("TEST_PUT_RECORD_BATCH_ERROR");
+}
+
+void flb_test_firehose_nonsense_error(void)
+{
+ int ret;
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+
+ /* mocks calls- signals that we are in test mode */
+ setenv("FLB_FIREHOSE_PLUGIN_UNDER_TEST", "true", 1);
+ setenv("TEST_PUT_RECORD_BATCH_ERROR", "\tbadresponse\nnotparsable{}", 1);
+
+ ctx = flb_create();
+
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ TEST_CHECK(in_ffd >= 0);
+ flb_input_set(ctx,in_ffd, "tag", "test", NULL);
+
+ out_ffd = flb_output(ctx, (char *) "kinesis_firehose", NULL);
+ TEST_CHECK(out_ffd >= 0);
+ flb_output_set(ctx, out_ffd,"match", "*", NULL);
+ flb_output_set(ctx, out_ffd,"region", "us-west-2", NULL);
+ flb_output_set(ctx, out_ffd,"delivery_stream", "fluent", NULL);
+ flb_output_set(ctx, out_ffd,"time_key", "time", NULL);
+ flb_output_set(ctx, out_ffd,"Retry_Limit", "1", NULL);
+
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ flb_lib_push(ctx, in_ffd, (char *) JSON_TD , (int) sizeof(JSON_TD) - 1);
+
+ sleep(2);
+ flb_stop(ctx);
+ flb_destroy(ctx);
+ unsetenv("TEST_PUT_RECORD_BATCH_ERROR");
+}
+
+
+/* Test list */
+TEST_LIST = {
+ {"success", flb_test_firehose_success },
+ {"partial_success", flb_test_firehose_partial_success },
+ {"throughput_error", flb_test_firehose_throughput_error },
+ {"unknown_error", flb_test_firehose_error_unknown },
+ {"nonsense_error", flb_test_firehose_nonsense_error },
+ {NULL, NULL}
+};
diff --git a/fluent-bit/tests/runtime/out_flowcounter.c b/fluent-bit/tests/runtime/out_flowcounter.c
new file mode 100644
index 00000000..81479369
--- /dev/null
+++ b/fluent-bit/tests/runtime/out_flowcounter.c
@@ -0,0 +1,317 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+#include <fluent-bit.h>
+#include "flb_tests_runtime.h"
+
+/* Test data */
+#include "data/common/json_invalid.h" /* JSON_INVALID */
+#include "data/common/json_long.h" /* JSON_LONG */
+#include "data/common/json_small.h" /* JSON_SMALL */
+
+/* Test functions */
+void flb_test_flowcounter_json_invalid(void);
+void flb_test_flowcounter_json_long(void);
+void flb_test_flowcounter_json_small(void);
+void flb_test_flowcounter_unit_second(void);
+void flb_test_flowcounter_unit_minute(void);
+void flb_test_flowcounter_unit_hour(void);
+void flb_test_flowcounter_unit_day(void);
+void flb_test_flowcounter_unit_invalid(void);
+
+/* Test list */
+TEST_LIST = {
+ {"json_invalid", flb_test_flowcounter_json_invalid },
+ {"json_long", flb_test_flowcounter_json_long },
+ {"json_small", flb_test_flowcounter_json_small },
+ {"unit_second", flb_test_flowcounter_unit_second },
+ {"unit_minute", flb_test_flowcounter_unit_minute },
+ {"unit_hour", flb_test_flowcounter_unit_hour },
+ {"unit_day", flb_test_flowcounter_unit_day },
+ {"unit_invalid", flb_test_flowcounter_unit_invalid },
+ {NULL, NULL}
+};
+
+
+void flb_test_flowcounter_json_invalid(void)
+{
+ int i;
+ int ret;
+ int bytes;
+ char *p = (char *) JSON_INVALID;
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+
+ ctx = flb_create();
+ flb_service_set(ctx, "Flush", "1", "Grace", "1", "Log_Level", "error", NULL);
+
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ TEST_CHECK(in_ffd >= 0);
+ flb_input_set(ctx, in_ffd, "tag", "test", NULL);
+
+ out_ffd = flb_output(ctx, (char *) "flowcounter", NULL);
+ TEST_CHECK(out_ffd >= 0);
+ flb_output_set(ctx, out_ffd, "match", "test", NULL);
+
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ for (i = 0; i < (int) sizeof(JSON_INVALID) - 1; i++) {
+ bytes = flb_lib_push(ctx, in_ffd, p + i, 1);
+ TEST_CHECK(bytes == 1);
+ }
+
+ flb_stop(ctx);
+ flb_destroy(ctx);
+}
+
+/* It writes a very long JSON map (> 100KB) byte by byte */
+void flb_test_flowcounter_json_long(void)
+{
+ int i;
+ int ret;
+ int bytes;
+ char *p = (char *) JSON_LONG;
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+
+ ctx = flb_create();
+ flb_service_set(ctx, "Flush", "1", "Grace", "1", "Log_Level", "error", NULL);
+
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ TEST_CHECK(in_ffd >= 0);
+ flb_input_set(ctx, in_ffd, "tag", "test", NULL);
+
+ out_ffd = flb_output(ctx, (char *) "flowcounter", NULL);
+ TEST_CHECK(out_ffd >= 0);
+ flb_output_set(ctx, out_ffd, "match", "test", NULL);
+
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ for (i = 0; i < (int) sizeof(JSON_LONG) - 1; i++) {
+ bytes = flb_lib_push(ctx, in_ffd, p + i, 1);
+ TEST_CHECK(bytes == 1);
+ }
+
+ sleep(1); /* waiting flush */
+
+ flb_stop(ctx);
+ flb_destroy(ctx);
+}
+
+void flb_test_flowcounter_json_small(void)
+{
+ int i;
+ int ret;
+ int bytes;
+ char *p = (char *) JSON_SMALL;
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+
+ ctx = flb_create();
+ flb_service_set(ctx, "Flush", "1", "Grace", "1", "Log_Level", "error", NULL);
+
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ TEST_CHECK(in_ffd >= 0);
+ flb_input_set(ctx, in_ffd, "tag", "test", NULL);
+
+ out_ffd = flb_output(ctx, (char *) "flowcounter", NULL);
+ TEST_CHECK(out_ffd >= 0);
+ flb_output_set(ctx, out_ffd, "match", "test", NULL);
+
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ for (i = 0; i < (int) sizeof(JSON_SMALL) - 1; i++) {
+ bytes = flb_lib_push(ctx, in_ffd, p + i, 1);
+ TEST_CHECK(bytes == 1);
+ }
+
+ sleep(1); /* waiting flush */
+
+ flb_stop(ctx);
+ flb_destroy(ctx);
+}
+
+void flb_test_flowcounter_unit_second(void)
+{
+ int i;
+ int ret;
+ int bytes;
+ char *p = (char *) JSON_SMALL;
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+
+ ctx = flb_create();
+ flb_service_set(ctx, "Flush", "1", "Grace", "1", "Log_Level", "error", NULL);
+
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ TEST_CHECK(in_ffd >= 0);
+ flb_input_set(ctx, in_ffd, "tag", "test", NULL);
+
+ out_ffd = flb_output(ctx, (char *) "flowcounter", NULL);
+ TEST_CHECK(out_ffd >= 0);
+ flb_output_set(ctx, out_ffd, "match", "test", NULL);
+ flb_output_set(ctx, out_ffd, "Unit", "second", NULL);
+
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ for (i = 0; i < (int) sizeof(JSON_SMALL) - 1; i++) {
+ bytes = flb_lib_push(ctx, in_ffd, p + i, 1);
+ TEST_CHECK(bytes == 1);
+ }
+
+ sleep(1); /* waiting flush */
+
+ flb_stop(ctx);
+ flb_destroy(ctx);
+}
+
+void flb_test_flowcounter_unit_minute(void)
+{
+ int i;
+ int ret;
+ int bytes;
+ char *p = (char *) JSON_SMALL;
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+
+ ctx = flb_create();
+ flb_service_set(ctx, "Flush", "1", "Grace", "1", "Log_Level", "error", NULL);
+
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ TEST_CHECK(in_ffd >= 0);
+ flb_input_set(ctx, in_ffd, "tag", "test", NULL);
+
+ out_ffd = flb_output(ctx, (char *) "flowcounter", NULL);
+ TEST_CHECK(out_ffd >= 0);
+ flb_output_set(ctx, out_ffd, "match", "test", NULL);
+ flb_output_set(ctx, out_ffd, "Unit", "minute", NULL);
+
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ for (i = 0; i < (int) sizeof(JSON_SMALL) - 1; i++) {
+ bytes = flb_lib_push(ctx, in_ffd, p + i, 1);
+ TEST_CHECK(bytes == 1);
+ }
+
+ sleep(1); /* waiting flush */
+
+ flb_stop(ctx);
+ flb_destroy(ctx);
+}
+
+void flb_test_flowcounter_unit_hour(void)
+{
+ int i;
+ int ret;
+ int bytes;
+ char *p = (char *) JSON_SMALL;
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+
+ ctx = flb_create();
+ flb_service_set(ctx, "Flush", "1", "Grace", "1", "Log_Level", "error", NULL);
+
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ TEST_CHECK(in_ffd >= 0);
+ flb_input_set(ctx, in_ffd, "tag", "test", NULL);
+
+ out_ffd = flb_output(ctx, (char *) "flowcounter", NULL);
+ TEST_CHECK(out_ffd >= 0);
+ flb_output_set(ctx, out_ffd, "match", "test", NULL);
+ flb_output_set(ctx, out_ffd, "Unit", "hour", NULL);
+
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ for (i = 0; i < (int) sizeof(JSON_SMALL) - 1; i++) {
+ bytes = flb_lib_push(ctx, in_ffd, p + i, 1);
+ TEST_CHECK(bytes == 1);
+ }
+
+ sleep(1); /* waiting flush */
+
+ flb_stop(ctx);
+ flb_destroy(ctx);
+}
+
+void flb_test_flowcounter_unit_day(void)
+{
+ int i;
+ int ret;
+ int bytes;
+ char *p = (char *) JSON_SMALL;
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+
+ ctx = flb_create();
+ flb_service_set(ctx, "Flush", "1", "Grace", "1", "Log_Level", "error", NULL);
+
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ TEST_CHECK(in_ffd >= 0);
+ flb_input_set(ctx, in_ffd, "tag", "test", NULL);
+
+ out_ffd = flb_output(ctx, (char *) "flowcounter", NULL);
+ TEST_CHECK(out_ffd >= 0);
+ flb_output_set(ctx, out_ffd, "match", "test", NULL);
+ flb_output_set(ctx, out_ffd, "Unit", "day", NULL);
+
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ for (i = 0; i < (int) sizeof(JSON_SMALL) - 1; i++) {
+ bytes = flb_lib_push(ctx, in_ffd, p + i, 1);
+ TEST_CHECK(bytes == 1);
+ }
+
+ sleep(1); /* waiting flush */
+
+ flb_stop(ctx);
+ flb_destroy(ctx);
+}
+
+void flb_test_flowcounter_unit_invalid(void)
+{
+ int i;
+ int ret;
+ int bytes;
+ char *p = (char *) JSON_SMALL;
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+
+ ctx = flb_create();
+ flb_service_set(ctx, "Flush", "1", "Grace", "1", "Log_Level", "error", NULL);
+
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ TEST_CHECK(in_ffd >= 0);
+ flb_input_set(ctx, in_ffd, "tag", "test", NULL);
+
+ out_ffd = flb_output(ctx, (char *) "flowcounter", NULL);
+ TEST_CHECK(out_ffd >= 0);
+ flb_output_set(ctx, out_ffd, "match", "test", NULL);
+ flb_output_set(ctx, out_ffd, "Unit", "xxx", NULL);
+
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ for (i = 0; i < (int) sizeof(JSON_SMALL) - 1; i++) {
+ bytes = flb_lib_push(ctx, in_ffd, p + i, 1);
+ TEST_CHECK(bytes == 1);
+ }
+
+ sleep(1); /* waiting flush */
+
+ flb_stop(ctx);
+ flb_destroy(ctx);
+}
diff --git a/fluent-bit/tests/runtime/out_forward.c b/fluent-bit/tests/runtime/out_forward.c
new file mode 100644
index 00000000..7e3b90ae
--- /dev/null
+++ b/fluent-bit/tests/runtime/out_forward.c
@@ -0,0 +1,364 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/* Fluent Bit
+ * ==========
+ * Copyright (C) 2015-2022 The Fluent Bit Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <fluent-bit.h>
+#include <fluent-bit/flb_pack.h>
+#include <fluent-bit/flb_record_accessor.h>
+
+#include "flb_tests_runtime.h"
+
+/* Include plugin header to get the flush_ctx structure definition */
+#include "../../plugins/out_forward/forward.h"
+
+static void cb_check_message_mode(void *ctx, int ffd,
+ int res_ret, void *res_data, size_t res_size,
+ void *data)
+{
+ int ret;
+ size_t off = 0;
+ msgpack_object tag;
+ msgpack_object ts;
+ msgpack_object record;
+ msgpack_object root;
+ msgpack_unpacked result;
+ struct flb_time time = {0};
+
+ msgpack_unpacked_init(&result);
+ ret = msgpack_unpack_next(&result, res_data, res_size, &off);
+ root = result.data;
+
+ TEST_CHECK(ret == MSGPACK_UNPACK_SUCCESS);
+ TEST_CHECK(root.type == MSGPACK_OBJECT_ARRAY);
+ TEST_CHECK(root.via.array.size == 4);
+
+ /* Tag */
+ tag = root.via.array.ptr[0];
+ TEST_CHECK(tag.type == MSGPACK_OBJECT_STR);
+ ret = strncmp(tag.via.str.ptr, "new.tag.fluent", tag.via.str.size);
+ TEST_CHECK(ret == 0);
+
+ /* Timestamp */
+ ts = root.via.array.ptr[1];
+ TEST_CHECK(ts.type == MSGPACK_OBJECT_EXT);
+
+ ret = flb_time_msgpack_to_time(&time, &ts);
+ TEST_CHECK(ret == 0);
+ TEST_CHECK(time.tm.tv_nsec != 0);
+
+ /* Record */
+ record = root.via.array.ptr[2];
+ TEST_CHECK(record.type == MSGPACK_OBJECT_MAP);
+ TEST_CHECK(record.via.map.size == 2);
+
+ msgpack_unpacked_destroy(&result);
+ flb_free(res_data);
+}
+
+static void cb_check_message_compat_mode(void *ctx, int ffd,
+ int res_ret, void *res_data, size_t res_size,
+ void *data)
+{
+ int ret;
+ size_t off = 0;
+ msgpack_object tag;
+ msgpack_object ts;
+ msgpack_object record;
+ msgpack_object root;
+ msgpack_unpacked result;
+ struct flb_time time = {0};
+
+ msgpack_unpacked_init(&result);
+ ret = msgpack_unpack_next(&result, res_data, res_size, &off);
+ root = result.data;
+
+ TEST_CHECK(ret == MSGPACK_UNPACK_SUCCESS);
+ TEST_CHECK(root.type == MSGPACK_OBJECT_ARRAY);
+ TEST_CHECK(root.via.array.size == 4);
+
+ /* Tag */
+ tag = root.via.array.ptr[0];
+ TEST_CHECK(tag.type == MSGPACK_OBJECT_STR);
+ ret = strncmp(tag.via.str.ptr, "new.tag.fluent", tag.via.str.size);
+ TEST_CHECK(ret == 0);
+
+ /* Timestamp */
+ ts = root.via.array.ptr[1];
+ TEST_CHECK(ts.type == MSGPACK_OBJECT_POSITIVE_INTEGER);
+
+ ret = flb_time_msgpack_to_time(&time, &ts);
+ TEST_CHECK(ret == 0);
+ TEST_CHECK(time.tm.tv_nsec == 0);
+
+ /* Record */
+ record = root.via.array.ptr[2];
+ TEST_CHECK(record.type == MSGPACK_OBJECT_MAP);
+ TEST_CHECK(record.via.map.size == 2);
+
+ msgpack_unpacked_destroy(&result);
+ flb_free(res_data);
+}
+
+static void cb_check_forward_mode(void *ctx, int ffd,
+ int res_ret, void *res_data, size_t res_size,
+ void *data)
+{
+ int ret;
+ size_t off = 0;
+ msgpack_object key;
+ msgpack_object val;
+ msgpack_object root;
+ msgpack_unpacked result;
+
+ /*
+ * the check for forward mode is a bit special, since no data is formatted, instead the formatter callback
+ * will return the "options" map that will be send after the records chunk. The options are set because the
+ * caller specified 'send_options true'.
+ */
+ TEST_CHECK(res_ret == MODE_FORWARD);
+
+ msgpack_unpacked_init(&result);
+ ret = msgpack_unpack_next(&result, res_data, res_size, &off);
+ root = result.data;
+
+ TEST_CHECK(ret == MSGPACK_UNPACK_SUCCESS);
+ TEST_CHECK(root.type == MSGPACK_OBJECT_MAP);
+
+ /* fluent_signal and size */
+ TEST_CHECK(root.via.map.size == 2);
+
+ /* Record */
+ key = root.via.map.ptr[1].key;
+ val = root.via.map.ptr[1].val;
+
+ ret = strncmp(key.via.str.ptr, "fluent_signal", 13);
+ TEST_CHECK(ret == 0);
+ TEST_CHECK(val.type == MSGPACK_OBJECT_POSITIVE_INTEGER);
+ TEST_CHECK(val.via.u64 == 0);
+
+ msgpack_unpacked_destroy(&result);
+ flb_free(res_data);
+}
+
+static void cb_check_forward_compat_mode(void *ctx, int ffd,
+ int res_ret, void *res_data, size_t res_size,
+ void *data)
+{
+ int ret;
+ size_t off = 0;
+ msgpack_object root;
+ msgpack_object records;
+ msgpack_object entry;
+ msgpack_unpacked result;
+
+ TEST_CHECK(res_ret == MODE_FORWARD_COMPAT);
+
+ msgpack_unpacked_init(&result);
+ ret = msgpack_unpack_next(&result, res_data, res_size, &off);
+ root = result.data;
+ records = root.via.array.ptr[1];
+
+ TEST_CHECK(ret == MSGPACK_UNPACK_SUCCESS);
+ TEST_CHECK(root.type == MSGPACK_OBJECT_ARRAY);
+
+ /* Record */
+ entry = records.via.array.ptr[0];
+ TEST_CHECK(entry.type == MSGPACK_OBJECT_ARRAY);
+ TEST_CHECK(entry.via.array.ptr[0].type == MSGPACK_OBJECT_POSITIVE_INTEGER);
+
+ msgpack_unpacked_destroy(&result);
+ flb_free(res_data);
+}
+
+void flb_test_message_mode()
+{
+ int ret;
+ int in_ffd;
+ int out_ffd;
+ flb_ctx_t *ctx;
+
+ /* Create context, flush every second (some checks omitted here) */
+ ctx = flb_create();
+ flb_service_set(ctx, "flush", "2", "grace", "1", NULL);
+
+ /* Lib input mode */
+ in_ffd = flb_input(ctx, (char *) "dummy", NULL);
+ flb_input_set(ctx, in_ffd,
+ "tag", "test",
+ "samples", "1",
+ "dummy", "{\"key1\": 123, \"key2\": {\"s1\": \"fluent\"}}",
+ NULL);
+
+
+ /* Forward output */
+ out_ffd = flb_output(ctx, (char *) "forward", NULL);
+ flb_output_set(ctx, out_ffd,
+ "match", "test",
+ "tag", "new.tag.$key2['s1']",
+ NULL);
+
+ /* Enable test mode */
+ ret = flb_output_set_test(ctx, out_ffd, "formatter",
+ cb_check_message_mode,
+ NULL, NULL);
+
+ /* Start */
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ sleep(2);
+ flb_stop(ctx);
+ flb_destroy(ctx);
+}
+
+void flb_test_message_compat_mode()
+{
+ int ret;
+ int in_ffd;
+ int out_ffd;
+ flb_ctx_t *ctx;
+
+ /* Create context, flush every second (some checks omitted here) */
+ ctx = flb_create();
+ flb_service_set(ctx, "flush", "2", "grace", "1", NULL);
+
+ /* Lib input mode */
+ in_ffd = flb_input(ctx, (char *) "dummy", NULL);
+ flb_input_set(ctx, in_ffd,
+ "tag", "test",
+ "samples", "1",
+ "dummy", "{\"key1\": 123, \"key2\": {\"s1\": \"fluent\"}}",
+ NULL);
+
+
+ /* Forward output with timestamp in integer mode (compat) */
+ out_ffd = flb_output(ctx, (char *) "forward", NULL);
+ flb_output_set(ctx, out_ffd,
+ "match", "test",
+ "time_as_integer", "true",
+ "tag", "new.tag.$key2['s1']",
+ NULL);
+
+ /* Enable test mode */
+ ret = flb_output_set_test(ctx, out_ffd, "formatter",
+ cb_check_message_compat_mode,
+ NULL, NULL);
+
+ /* Start */
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ sleep(2);
+ flb_stop(ctx);
+ flb_destroy(ctx);
+}
+
+void flb_test_forward_mode()
+{
+ int ret;
+ int in_ffd;
+ int out_ffd;
+ flb_ctx_t *ctx;
+
+ /* Create context, flush every second (some checks omitted here) */
+ ctx = flb_create();
+ flb_service_set(ctx, "flush", "2", "grace", "1", NULL);
+
+ /* Lib input mode */
+ in_ffd = flb_input(ctx, (char *) "dummy", NULL);
+ flb_input_set(ctx, in_ffd,
+ "tag", "test",
+ "samples", "1",
+ "dummy", "{\"key1\": 123, \"key2\": {\"s1\": \"fluent\"}}",
+ NULL);
+
+
+ /* Forward output: without a tag key access, forward mode is used */
+ out_ffd = flb_output(ctx, (char *) "forward", NULL);
+ flb_output_set(ctx, out_ffd,
+ "match", "test",
+ "tag", "new.tag",
+ "send_options", "true",
+ NULL);
+
+ /* Enable test mode */
+ ret = flb_output_set_test(ctx, out_ffd, "formatter",
+ cb_check_forward_mode,
+ NULL, NULL);
+
+ /* Start */
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ sleep(2);
+ flb_stop(ctx);
+ flb_destroy(ctx);
+}
+
+void flb_test_forward_compat_mode()
+{
+ int ret;
+ int in_ffd;
+ int out_ffd;
+ flb_ctx_t *ctx;
+
+ /* Create context, flush every second (some checks omitted here) */
+ ctx = flb_create();
+ flb_service_set(ctx, "flush", "3", "grace", "1", NULL);
+
+ /* Lib input mode */
+ in_ffd = flb_input(ctx, (char *) "dummy", NULL);
+ flb_input_set(ctx, in_ffd,
+ "tag", "test",
+ "samples", "2",
+ "dummy", "{\"key1\": 123, \"key2\": {\"s1\": \"fluent\"}}",
+ NULL);
+
+
+ /* Forward output: without a tag key access, forward mode is used */
+ out_ffd = flb_output(ctx, (char *) "forward", NULL);
+ flb_output_set(ctx, out_ffd,
+ "match", "test",
+ "tag", "new.tag",
+ "time_as_integer", "true",
+ NULL);
+
+ /* Enable test mode */
+ ret = flb_output_set_test(ctx, out_ffd, "formatter",
+ cb_check_forward_compat_mode,
+ NULL, NULL);
+
+ /* Start */
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ sleep(2);
+ flb_stop(ctx);
+ flb_destroy(ctx);
+}
+
+/* Test list */
+TEST_LIST = {
+#ifdef FLB_HAVE_RECORD_ACCESSOR
+ {"message_mode" , flb_test_message_mode },
+ {"message_compat_mode", flb_test_message_compat_mode },
+#endif
+ {"forward_mode" , flb_test_forward_mode },
+ {"forward_compat_mode", flb_test_forward_compat_mode },
+ {NULL, NULL}
+};
diff --git a/fluent-bit/tests/runtime/out_http.c b/fluent-bit/tests/runtime/out_http.c
new file mode 100644
index 00000000..c898693c
--- /dev/null
+++ b/fluent-bit/tests/runtime/out_http.c
@@ -0,0 +1,1060 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/* Fluent Bit
+ * ==========
+ * Copyright (C) 2019-2022 The Fluent Bit Authors
+ * Copyright (C) 2015-2018 Treasure Data Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <fluent-bit.h>
+#include <fluent-bit/flb_sds.h>
+#include <fluent-bit/flb_time.h>
+#include <float.h>
+#include <math.h>
+#include <msgpack.h>
+#include "flb_tests_runtime.h"
+
+struct test_ctx {
+ flb_ctx_t *flb; /* Fluent Bit library context */
+ int i_ffd; /* Input fd */
+ int f_ffd; /* Filter fd (unused) */
+ int o_ffd; /* Output fd */
+};
+
+
+pthread_mutex_t result_mutex = PTHREAD_MUTEX_INITIALIZER;
+int num_output = 0;
+static int get_output_num()
+{
+ int ret;
+ pthread_mutex_lock(&result_mutex);
+ ret = num_output;
+ pthread_mutex_unlock(&result_mutex);
+
+ return ret;
+}
+
+static void set_output_num(int num)
+{
+ pthread_mutex_lock(&result_mutex);
+ num_output = num;
+ pthread_mutex_unlock(&result_mutex);
+}
+
+static void clear_output_num()
+{
+ set_output_num(0);
+}
+
+struct str_list {
+ size_t size;
+ char **lists;
+};
+
+/* Callback to check expected results */
+static void cb_check_str_list(void *ctx, int ffd, int res_ret,
+ void *res_data, size_t res_size, void *data)
+{
+ char *p;
+ flb_sds_t out_line = res_data;
+ int num = get_output_num();
+ size_t i;
+ struct str_list *l = (struct str_list *)data;
+
+ if (!TEST_CHECK(res_data != NULL)) {
+ TEST_MSG("res_data is NULL");
+ return;
+ }
+
+ if (!TEST_CHECK(l != NULL)) {
+ TEST_MSG("l is NULL");
+ flb_sds_destroy(out_line);
+ return;
+ }
+
+ if(!TEST_CHECK(res_ret == 0)) {
+ TEST_MSG("callback ret=%d", res_ret);
+ }
+ if (!TEST_CHECK(res_data != NULL)) {
+ TEST_MSG("res_data is NULL");
+ flb_sds_destroy(out_line);
+ return;
+ }
+
+ for (i=0; i<l->size; i++) {
+ p = strstr(out_line, l->lists[i]);
+ if (!TEST_CHECK(p != NULL)) {
+ TEST_MSG(" Got :%s\n expect:%s", out_line, l->lists[i]);
+ }
+ }
+ set_output_num(num+1);
+
+ flb_sds_destroy(out_line);
+}
+
+static int msgpack_strncmp(char* str, size_t str_len, msgpack_object obj)
+{
+ int ret = -1;
+
+ if (str == NULL) {
+ flb_error("str is NULL");
+ return -1;
+ }
+
+ switch (obj.type) {
+ case MSGPACK_OBJECT_STR:
+ if (obj.via.str.size != str_len) {
+ return -1;
+ }
+ ret = strncmp(str, obj.via.str.ptr, str_len);
+ break;
+ case MSGPACK_OBJECT_POSITIVE_INTEGER:
+ {
+ unsigned long val = strtoul(str, NULL, 10);
+ if (val == (unsigned long)obj.via.u64) {
+ ret = 0;
+ }
+ }
+ break;
+ case MSGPACK_OBJECT_NEGATIVE_INTEGER:
+ {
+ long long val = strtoll(str, NULL, 10);
+ if (val == (unsigned long)obj.via.i64) {
+ ret = 0;
+ }
+ }
+ break;
+ case MSGPACK_OBJECT_FLOAT32:
+ case MSGPACK_OBJECT_FLOAT64:
+ {
+ double val = strtod(str, NULL);
+ if (fabs(val - obj.via.f64) < DBL_EPSILON) {
+ ret = 0;
+ }
+ }
+ break;
+ case MSGPACK_OBJECT_BOOLEAN:
+ if (obj.via.boolean) {
+ if (str_len != 4 /*true*/) {
+ return -1;
+ }
+ ret = strncasecmp(str, "true", 4);
+ }
+ else {
+ if (str_len != 5 /*false*/) {
+ return -1;
+ }
+ ret = strncasecmp(str, "false", 5);
+ }
+ break;
+ default:
+ flb_error("not supported");
+ }
+
+ return ret;
+}
+
+/* Callback to check expected results */
+static void cb_check_msgpack_kv(void *ctx, int ffd, int res_ret,
+ void *res_data, size_t res_size, void *data)
+{
+ msgpack_unpacked result;
+ msgpack_object obj;
+ size_t off = 0;
+ struct str_list *l = (struct str_list *)data;
+ int i_map;
+ int map_size;
+ int i_list;
+ int num = get_output_num();
+
+ if (!TEST_CHECK(res_data != NULL)) {
+ TEST_MSG("res_data is NULL");
+ return;
+ }
+
+ if (!TEST_CHECK(data != NULL)) {
+ flb_error("data is NULL");
+ return;
+ }
+
+ /* Iterate each item array and apply rules */
+ msgpack_unpacked_init(&result);
+ while (msgpack_unpack_next(&result, res_data, res_size, &off) == MSGPACK_UNPACK_SUCCESS) {
+ obj = result.data;
+ /*
+ msgpack_object_print(stdout, obj);
+ */
+ if (obj.type != MSGPACK_OBJECT_ARRAY || obj.via.array.size != 2) {
+ flb_error("array error. type = %d", obj.type);
+ continue;
+ }
+ obj = obj.via.array.ptr[1];
+ if (obj.type != MSGPACK_OBJECT_MAP) {
+ flb_error("map error. type = %d", obj.type);
+ continue;
+ }
+ map_size = obj.via.map.size;
+ for (i_map=0; i_map<map_size; i_map++) {
+ if (obj.via.map.ptr[i_map].key.type != MSGPACK_OBJECT_STR) {
+ flb_error("key is not string. type =%d", obj.via.map.ptr[i_map].key.type);
+ continue;
+ }
+ for (i_list=0; i_list< l->size/2; i_list++) {
+ if (msgpack_strncmp(l->lists[i_list*2], strlen(l->lists[i_list*2]),
+ obj.via.map.ptr[i_map].key) == 0 &&
+ msgpack_strncmp(l->lists[i_list*2+1], strlen(l->lists[i_list*2+1]),
+ obj.via.map.ptr[i_map].val) == 0) {
+ num++;
+ }
+ }
+ }
+ }
+ set_output_num(num);
+
+ msgpack_unpacked_destroy(&result);
+
+ return ;
+}
+
+static struct test_ctx *test_ctx_create()
+{
+ int i_ffd;
+ int o_ffd;
+ struct test_ctx *ctx = NULL;
+
+ ctx = flb_malloc(sizeof(struct test_ctx));
+ if (!TEST_CHECK(ctx != NULL)) {
+ TEST_MSG("malloc failed");
+ flb_errno();
+ return NULL;
+ }
+
+ /* Service config */
+ ctx->flb = flb_create();
+ flb_service_set(ctx->flb,
+ "Flush", "0.200000000",
+ "Grace", "1",
+ "Log_Level", "error",
+ NULL);
+
+ /* Input */
+ i_ffd = flb_input(ctx->flb, (char *) "lib", NULL);
+ TEST_CHECK(i_ffd >= 0);
+ ctx->i_ffd = i_ffd;
+
+ /* Output */
+ o_ffd = flb_output(ctx->flb, (char *) "http", NULL);
+ ctx->o_ffd = o_ffd;
+
+ return ctx;
+}
+
+static void test_ctx_destroy(struct test_ctx *ctx)
+{
+ TEST_CHECK(ctx != NULL);
+
+ sleep(1);
+ flb_stop(ctx->flb);
+ flb_destroy(ctx->flb);
+ flb_free(ctx);
+}
+
+void flb_test_format_msgpack()
+{
+ struct test_ctx *ctx;
+ int ret;
+ int num;
+
+ char *buf1 = "[1, {\"msg\":\"hello world\", \"val\":1000, \"nval\":-10000, \"bool\":true, \"float\":1.234}]";
+ size_t size1 = strlen(buf1);
+
+ char *expected_strs[] = {"msg", "hello world", "val", "1000", "nval", "-10000", "bool", "true", "float", "1.234"};
+ struct str_list expected = {
+ .size = sizeof(expected_strs)/sizeof(char*),
+ .lists = &expected_strs[0],
+ };
+
+ clear_output_num();
+
+ ctx = test_ctx_create();
+ if (!TEST_CHECK(ctx != NULL)) {
+ TEST_MSG("test_ctx_create failed");
+ exit(EXIT_FAILURE);
+ }
+
+ ret = flb_output_set(ctx->flb, ctx->o_ffd,
+ "match", "*",
+ "format", "msgpack",
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ ret = flb_output_set_test(ctx->flb, ctx->o_ffd,
+ "formatter", cb_check_msgpack_kv,
+ &expected, NULL);
+ TEST_CHECK(ret == 0);
+
+ /* Start the engine */
+ ret = flb_start(ctx->flb);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data sample */
+ ret = flb_lib_push(ctx->flb, ctx->i_ffd, (char *) buf1, size1);
+ TEST_CHECK(ret >= 0);
+
+ /* waiting to flush */
+ flb_time_msleep(500);
+
+ num = get_output_num();
+ if (!TEST_CHECK(num == expected.size / 2)) {
+ TEST_MSG("got %d, expected %lu", num, expected.size/2);
+ }
+
+ test_ctx_destroy(ctx);
+}
+
+void flb_test_format_json()
+{
+ struct test_ctx *ctx;
+ int ret;
+ int num;
+
+ char *buf1 = "[1, {\"msg\":\"hello world\"}]";
+ size_t size1 = strlen(buf1);
+ char *buf2 = "[2, {\"msg\":\"hello world\"}]";
+ size_t size2 = strlen(buf2);
+
+ char *expected_strs[] = {"[{\"date\":1.0,\"msg\":\"hello world\"},{\"date\":2.0,\"msg\":\"hello world\"}]"};
+ struct str_list expected = {
+ .size = sizeof(expected_strs)/sizeof(char*),
+ .lists = &expected_strs[0],
+ };
+
+ clear_output_num();
+
+ ctx = test_ctx_create();
+ if (!TEST_CHECK(ctx != NULL)) {
+ TEST_MSG("test_ctx_create failed");
+ exit(EXIT_FAILURE);
+ }
+
+ ret = flb_output_set(ctx->flb, ctx->o_ffd,
+ "match", "*",
+ "format", "json",
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ ret = flb_output_set_test(ctx->flb, ctx->o_ffd,
+ "formatter", cb_check_str_list,
+ &expected, NULL);
+ TEST_CHECK(ret == 0);
+
+ /* Start the engine */
+ ret = flb_start(ctx->flb);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data sample */
+ ret = flb_lib_push(ctx->flb, ctx->i_ffd, (char *) buf1, size1);
+ TEST_CHECK(ret >= 0);
+ ret = flb_lib_push(ctx->flb, ctx->i_ffd, (char *) buf2, size2);
+ TEST_CHECK(ret >= 0);
+
+ /* waiting to flush */
+ flb_time_msleep(500);
+
+ num = get_output_num();
+ if (!TEST_CHECK(num > 0)) {
+ TEST_MSG("no outputs");
+ }
+
+ test_ctx_destroy(ctx);
+}
+
+void flb_test_format_json_stream()
+{
+ struct test_ctx *ctx;
+ int ret;
+ int num;
+
+ char *buf1 = "[1, {\"msg\":\"hello world\"}]";
+ size_t size1 = strlen(buf1);
+ char *buf2 = "[2, {\"msg\":\"hello world\"}]";
+ size_t size2 = strlen(buf2);
+
+ char *expected_strs[] = {"{\"date\":1.0,\"msg\":\"hello world\"}{\"date\":2.0,\"msg\":\"hello world\"}"};
+ struct str_list expected = {
+ .size = sizeof(expected_strs)/sizeof(char*),
+ .lists = &expected_strs[0],
+ };
+
+ clear_output_num();
+
+ ctx = test_ctx_create();
+ if (!TEST_CHECK(ctx != NULL)) {
+ TEST_MSG("test_ctx_create failed");
+ exit(EXIT_FAILURE);
+ }
+
+ ret = flb_output_set(ctx->flb, ctx->o_ffd,
+ "match", "*",
+ "format", "json_stream",
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ ret = flb_output_set_test(ctx->flb, ctx->o_ffd,
+ "formatter", cb_check_str_list,
+ &expected, NULL);
+ TEST_CHECK(ret == 0);
+
+ /* Start the engine */
+ ret = flb_start(ctx->flb);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data sample */
+ ret = flb_lib_push(ctx->flb, ctx->i_ffd, (char *) buf1, size1);
+ TEST_CHECK(ret >= 0);
+ ret = flb_lib_push(ctx->flb, ctx->i_ffd, (char *) buf2, size2);
+ TEST_CHECK(ret >= 0);
+
+ /* waiting to flush */
+ flb_time_msleep(500);
+
+ num = get_output_num();
+ if (!TEST_CHECK(num > 0)) {
+ TEST_MSG("no outputs");
+ }
+
+ test_ctx_destroy(ctx);
+}
+
+void flb_test_format_json_lines()
+{
+ struct test_ctx *ctx;
+ int ret;
+ int num;
+
+ char *buf1 = "[1, {\"msg\":\"hello world\"}]";
+ size_t size1 = strlen(buf1);
+ char *buf2 = "[2, {\"msg\":\"hello world\"}]";
+ size_t size2 = strlen(buf2);
+
+ char *expected_strs[] = {"{\"date\":1.0,\"msg\":\"hello world\"}\n{\"date\":2.0,\"msg\":\"hello world\"}"};
+ struct str_list expected = {
+ .size = sizeof(expected_strs)/sizeof(char*),
+ .lists = &expected_strs[0],
+ };
+
+ clear_output_num();
+
+ ctx = test_ctx_create();
+ if (!TEST_CHECK(ctx != NULL)) {
+ TEST_MSG("test_ctx_create failed");
+ exit(EXIT_FAILURE);
+ }
+
+ ret = flb_output_set(ctx->flb, ctx->o_ffd,
+ "match", "*",
+ "format", "json_lines",
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ ret = flb_output_set_test(ctx->flb, ctx->o_ffd,
+ "formatter", cb_check_str_list,
+ &expected, NULL);
+ TEST_CHECK(ret == 0);
+
+ /* Start the engine */
+ ret = flb_start(ctx->flb);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data sample */
+ ret = flb_lib_push(ctx->flb, ctx->i_ffd, (char *) buf1, size1);
+ TEST_CHECK(ret >= 0);
+ ret = flb_lib_push(ctx->flb, ctx->i_ffd, (char *) buf2, size2);
+ TEST_CHECK(ret >= 0);
+
+ /* waiting to flush */
+ flb_time_msleep(500);
+
+ num = get_output_num();
+ if (!TEST_CHECK(num > 0)) {
+ TEST_MSG("no outputs");
+ }
+
+ test_ctx_destroy(ctx);
+}
+
+void flb_test_format_gelf()
+{
+ struct test_ctx *ctx;
+ int ret;
+ int num;
+
+ char *buf1 = "[1, {\"msg\":\"hello world\"}]";
+ size_t size1 = strlen(buf1);
+
+ char *expected_strs[] = {"\"short_message\":\"hello world\"", "\"timestamp\":1.000"};
+ struct str_list expected = {
+ .size = sizeof(expected_strs)/sizeof(char*),
+ .lists = &expected_strs[0],
+ };
+
+ clear_output_num();
+
+ ctx = test_ctx_create();
+ if (!TEST_CHECK(ctx != NULL)) {
+ TEST_MSG("test_ctx_create failed");
+ exit(EXIT_FAILURE);
+ }
+
+ ret = flb_output_set(ctx->flb, ctx->o_ffd,
+ "match", "*",
+ "format", "gelf",
+ "gelf_short_message_key", "msg",
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ ret = flb_output_set_test(ctx->flb, ctx->o_ffd,
+ "formatter", cb_check_str_list,
+ &expected, NULL);
+ TEST_CHECK(ret == 0);
+
+ /* Start the engine */
+ ret = flb_start(ctx->flb);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data sample */
+ ret = flb_lib_push(ctx->flb, ctx->i_ffd, (char *) buf1, size1);
+ TEST_CHECK(ret >= 0);
+
+ /* waiting to flush */
+ flb_time_msleep(500);
+
+ num = get_output_num();
+ if (!TEST_CHECK(num > 0)) {
+ TEST_MSG("no outputs");
+ }
+
+ test_ctx_destroy(ctx);
+}
+
+
+void flb_test_format_gelf_host_key()
+{
+ struct test_ctx *ctx;
+ int ret;
+ int num;
+
+ char *buf1 = "[1, {\"msg\":\"hello world\", \"h_key\":\"localhost\"}]";
+ size_t size1 = strlen(buf1);
+
+ char *expected_strs[] = {"\"short_message\":\"hello world\"", "\"timestamp\":1.000", "\"host\":\"localhost\""};
+ struct str_list expected = {
+ .size = sizeof(expected_strs)/sizeof(char*),
+ .lists = &expected_strs[0],
+ };
+
+ clear_output_num();
+
+ ctx = test_ctx_create();
+ if (!TEST_CHECK(ctx != NULL)) {
+ TEST_MSG("test_ctx_create failed");
+ exit(EXIT_FAILURE);
+ }
+
+ ret = flb_output_set(ctx->flb, ctx->o_ffd,
+ "match", "*",
+ "format", "gelf",
+ "gelf_short_message_key", "msg",
+ "gelf_host_key", "h_key",
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ ret = flb_output_set_test(ctx->flb, ctx->o_ffd,
+ "formatter", cb_check_str_list,
+ &expected, NULL);
+ TEST_CHECK(ret == 0);
+
+ /* Start the engine */
+ ret = flb_start(ctx->flb);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data sample */
+ ret = flb_lib_push(ctx->flb, ctx->i_ffd, (char *) buf1, size1);
+ TEST_CHECK(ret >= 0);
+
+ /* waiting to flush */
+ flb_time_msleep(500);
+
+ num = get_output_num();
+ if (!TEST_CHECK(num > 0)) {
+ TEST_MSG("no outputs");
+ }
+
+ test_ctx_destroy(ctx);
+}
+
+void flb_test_format_gelf_timestamp_key()
+{
+ struct test_ctx *ctx;
+ int ret;
+ int num;
+
+ char *buf1 = "[1, {\"msg\":\"hello world\", \"t_key\":\"2018-05-30T09:39:52.000681Z\"}]";
+ size_t size1 = strlen(buf1);
+
+ char *expected_strs[] = {"\"short_message\":\"hello world\"", "\"timestamp\":\"2018-05-30T09:39:52.000681Z\""};
+ struct str_list expected = {
+ .size = sizeof(expected_strs)/sizeof(char*),
+ .lists = &expected_strs[0],
+ };
+
+ clear_output_num();
+
+ ctx = test_ctx_create();
+ if (!TEST_CHECK(ctx != NULL)) {
+ TEST_MSG("test_ctx_create failed");
+ exit(EXIT_FAILURE);
+ }
+
+ ret = flb_output_set(ctx->flb, ctx->o_ffd,
+ "match", "*",
+ "format", "gelf",
+ "gelf_short_message_key", "msg",
+ "gelf_timestamp_key", "t_key",
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ ret = flb_output_set_test(ctx->flb, ctx->o_ffd,
+ "formatter", cb_check_str_list,
+ &expected, NULL);
+ TEST_CHECK(ret == 0);
+
+ /* Start the engine */
+ ret = flb_start(ctx->flb);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data sample */
+ ret = flb_lib_push(ctx->flb, ctx->i_ffd, (char *) buf1, size1);
+ TEST_CHECK(ret >= 0);
+
+ /* waiting to flush */
+ flb_time_msleep(500);
+
+ num = get_output_num();
+ if (!TEST_CHECK(num > 0)) {
+ TEST_MSG("no outputs");
+ }
+
+ test_ctx_destroy(ctx);
+}
+
+void flb_test_format_gelf_full_message_key()
+{
+ struct test_ctx *ctx;
+ int ret;
+ int num;
+
+ char *buf1 = "[1, {\"msg\":\"hello world\", \"f_msg\":\"full message\"}]";
+ size_t size1 = strlen(buf1);
+
+ char *expected_strs[] = {"\"short_message\":\"hello world\"", "\"timestamp\":1.000","\"full_message\":\"full message\""};
+ struct str_list expected = {
+ .size = sizeof(expected_strs)/sizeof(char*),
+ .lists = &expected_strs[0],
+ };
+
+ clear_output_num();
+
+ ctx = test_ctx_create();
+ if (!TEST_CHECK(ctx != NULL)) {
+ TEST_MSG("test_ctx_create failed");
+ exit(EXIT_FAILURE);
+ }
+
+ ret = flb_output_set(ctx->flb, ctx->o_ffd,
+ "match", "*",
+ "format", "gelf",
+ "gelf_short_message_key", "msg",
+ "gelf_full_message_key", "f_msg",
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ ret = flb_output_set_test(ctx->flb, ctx->o_ffd,
+ "formatter", cb_check_str_list,
+ &expected, NULL);
+ TEST_CHECK(ret == 0);
+
+ /* Start the engine */
+ ret = flb_start(ctx->flb);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data sample */
+ ret = flb_lib_push(ctx->flb, ctx->i_ffd, (char *) buf1, size1);
+ TEST_CHECK(ret >= 0);
+
+ /* waiting to flush */
+ flb_time_msleep(500);
+
+ num = get_output_num();
+ if (!TEST_CHECK(num > 0)) {
+ TEST_MSG("no outputs");
+ }
+
+ test_ctx_destroy(ctx);
+}
+
+void flb_test_format_gelf_level_key()
+{
+ struct test_ctx *ctx;
+ int ret;
+ int num;
+
+ char *buf1 = "[1, {\"msg\":\"hello world\", \"l_msg\":6\"}]";
+ size_t size1 = strlen(buf1);
+
+ char *expected_strs[] = {"\"short_message\":\"hello world\"", "\"timestamp\":1.000","\"level\":6"};
+ struct str_list expected = {
+ .size = sizeof(expected_strs)/sizeof(char*),
+ .lists = &expected_strs[0],
+ };
+
+ clear_output_num();
+
+ ctx = test_ctx_create();
+ if (!TEST_CHECK(ctx != NULL)) {
+ TEST_MSG("test_ctx_create failed");
+ exit(EXIT_FAILURE);
+ }
+
+ ret = flb_output_set(ctx->flb, ctx->o_ffd,
+ "match", "*",
+ "format", "gelf",
+ "gelf_short_message_key", "msg",
+ "gelf_level_key", "l_msg",
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ ret = flb_output_set_test(ctx->flb, ctx->o_ffd,
+ "formatter", cb_check_str_list,
+ &expected, NULL);
+ TEST_CHECK(ret == 0);
+
+ /* Start the engine */
+ ret = flb_start(ctx->flb);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data sample */
+ ret = flb_lib_push(ctx->flb, ctx->i_ffd, (char *) buf1, size1);
+ TEST_CHECK(ret >= 0);
+
+ /* waiting to flush */
+ flb_time_msleep(500);
+
+ num = get_output_num();
+ if (!TEST_CHECK(num > 0)) {
+ TEST_MSG("no outputs");
+ }
+
+ test_ctx_destroy(ctx);
+}
+
+void flb_test_set_json_date_key()
+{
+ struct test_ctx *ctx;
+ int ret;
+ int num;
+
+ char *buf1 = "[1, {\"msg\":\"hello world\"}]";
+ size_t size1 = strlen(buf1);
+
+ char *expected_strs[] = {"{\"timestamp\":1.0,\"msg\":\"hello world\"}"};
+ struct str_list expected = {
+ .size = sizeof(expected_strs)/sizeof(char*),
+ .lists = &expected_strs[0],
+ };
+
+ clear_output_num();
+
+ ctx = test_ctx_create();
+ if (!TEST_CHECK(ctx != NULL)) {
+ TEST_MSG("test_ctx_create failed");
+ exit(EXIT_FAILURE);
+ }
+
+ ret = flb_output_set(ctx->flb, ctx->o_ffd,
+ "match", "*",
+ "format", "json",
+ "json_date_key", "timestamp",
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ ret = flb_output_set_test(ctx->flb, ctx->o_ffd,
+ "formatter", cb_check_str_list,
+ &expected, NULL);
+ TEST_CHECK(ret == 0);
+
+ /* Start the engine */
+ ret = flb_start(ctx->flb);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data sample */
+ ret = flb_lib_push(ctx->flb, ctx->i_ffd, (char *) buf1, size1);
+ TEST_CHECK(ret >= 0);
+
+ /* waiting to flush */
+ flb_time_msleep(500);
+
+ num = get_output_num();
+ if (!TEST_CHECK(num > 0)) {
+ TEST_MSG("no outputs");
+ }
+
+ test_ctx_destroy(ctx);
+}
+
+void flb_test_disable_json_date_key()
+{
+ struct test_ctx *ctx;
+ int ret;
+ int num;
+
+ char *buf1 = "[1, {\"msg\":\"hello world\"}]";
+ size_t size1 = strlen(buf1);
+
+ char *expected_strs[] = {"{\"msg\":\"hello world\"}"};
+ struct str_list expected = {
+ .size = sizeof(expected_strs)/sizeof(char*),
+ .lists = &expected_strs[0],
+ };
+
+ clear_output_num();
+
+ ctx = test_ctx_create();
+ if (!TEST_CHECK(ctx != NULL)) {
+ TEST_MSG("test_ctx_create failed");
+ exit(EXIT_FAILURE);
+ }
+
+ ret = flb_output_set(ctx->flb, ctx->o_ffd,
+ "match", "*",
+ "format", "json",
+ "json_date_key", "false",
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ ret = flb_output_set_test(ctx->flb, ctx->o_ffd,
+ "formatter", cb_check_str_list,
+ &expected, NULL);
+ TEST_CHECK(ret == 0);
+
+ /* Start the engine */
+ ret = flb_start(ctx->flb);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data sample */
+ ret = flb_lib_push(ctx->flb, ctx->i_ffd, (char *) buf1, size1);
+ TEST_CHECK(ret >= 0);
+
+ /* waiting to flush */
+ flb_time_msleep(500);
+
+ num = get_output_num();
+ if (!TEST_CHECK(num > 0)) {
+ TEST_MSG("no outputs");
+ }
+
+ test_ctx_destroy(ctx);
+}
+
+void flb_test_json_date_format_epoch()
+{
+ struct test_ctx *ctx;
+ int ret;
+ int num;
+
+ char *buf1 = "[1, {\"msg\":\"hello world\"}]";
+ size_t size1 = strlen(buf1);
+
+ char *expected_strs[] = {"{\"date\":1,\"msg\":\"hello world\"}"};
+ struct str_list expected = {
+ .size = sizeof(expected_strs)/sizeof(char*),
+ .lists = &expected_strs[0],
+ };
+
+ clear_output_num();
+
+ ctx = test_ctx_create();
+ if (!TEST_CHECK(ctx != NULL)) {
+ TEST_MSG("test_ctx_create failed");
+ exit(EXIT_FAILURE);
+ }
+
+ ret = flb_output_set(ctx->flb, ctx->o_ffd,
+ "match", "*",
+ "format", "json",
+ "json_date_format", "epoch",
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ ret = flb_output_set_test(ctx->flb, ctx->o_ffd,
+ "formatter", cb_check_str_list,
+ &expected, NULL);
+ TEST_CHECK(ret == 0);
+
+ /* Start the engine */
+ ret = flb_start(ctx->flb);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data sample */
+ ret = flb_lib_push(ctx->flb, ctx->i_ffd, (char *) buf1, size1);
+ TEST_CHECK(ret >= 0);
+
+ /* waiting to flush */
+ flb_time_msleep(500);
+
+ num = get_output_num();
+ if (!TEST_CHECK(num > 0)) {
+ TEST_MSG("no outputs");
+ }
+
+ test_ctx_destroy(ctx);
+}
+
+void flb_test_json_date_format_iso8601()
+{
+ struct test_ctx *ctx;
+ int ret;
+ int num;
+
+ char *buf1 = "[1, {\"msg\":\"hello world\"}]";
+ size_t size1 = strlen(buf1);
+
+ char *expected_strs[] = {"\"msg\":\"hello world\"", "\"date\":\"1970-01-01T00:00:01.000000Z\""};
+ struct str_list expected = {
+ .size = sizeof(expected_strs)/sizeof(char*),
+ .lists = &expected_strs[0],
+ };
+
+ clear_output_num();
+
+ ctx = test_ctx_create();
+ if (!TEST_CHECK(ctx != NULL)) {
+ TEST_MSG("test_ctx_create failed");
+ exit(EXIT_FAILURE);
+ }
+
+ ret = flb_output_set(ctx->flb, ctx->o_ffd,
+ "match", "*",
+ "format", "json",
+ "json_date_format", "iso8601",
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ ret = flb_output_set_test(ctx->flb, ctx->o_ffd,
+ "formatter", cb_check_str_list,
+ &expected, NULL);
+ TEST_CHECK(ret == 0);
+
+ /* Start the engine */
+ ret = flb_start(ctx->flb);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data sample */
+ ret = flb_lib_push(ctx->flb, ctx->i_ffd, (char *) buf1, size1);
+ TEST_CHECK(ret >= 0);
+
+ /* waiting to flush */
+ flb_time_msleep(500);
+
+ num = get_output_num();
+ if (!TEST_CHECK(num > 0)) {
+ TEST_MSG("no outputs");
+ }
+
+ test_ctx_destroy(ctx);
+}
+
+void flb_test_json_date_format_java_sql_timestamp()
+{
+ struct test_ctx *ctx;
+ int ret;
+ int num;
+
+ char *buf1 = "[1, {\"msg\":\"hello world\"}]";
+ size_t size1 = strlen(buf1);
+
+ char *expected_strs[] = {"\"msg\":\"hello world\"", "\"date\":\"1970-01-01 00:00:01.000000\""};
+ struct str_list expected = {
+ .size = sizeof(expected_strs)/sizeof(char*),
+ .lists = &expected_strs[0],
+ };
+
+ clear_output_num();
+
+ ctx = test_ctx_create();
+ if (!TEST_CHECK(ctx != NULL)) {
+ TEST_MSG("test_ctx_create failed");
+ exit(EXIT_FAILURE);
+ }
+
+ ret = flb_output_set(ctx->flb, ctx->o_ffd,
+ "match", "*",
+ "format", "json",
+ "json_date_format", "java_sql_timestamp",
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ ret = flb_output_set_test(ctx->flb, ctx->o_ffd,
+ "formatter", cb_check_str_list,
+ &expected, NULL);
+ TEST_CHECK(ret == 0);
+
+ /* Start the engine */
+ ret = flb_start(ctx->flb);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data sample */
+ ret = flb_lib_push(ctx->flb, ctx->i_ffd, (char *) buf1, size1);
+ TEST_CHECK(ret >= 0);
+
+ /* waiting to flush */
+ flb_time_msleep(500);
+
+ num = get_output_num();
+ if (!TEST_CHECK(num > 0)) {
+ TEST_MSG("no outputs");
+ }
+
+ test_ctx_destroy(ctx);
+}
+
+/* Test list */
+TEST_LIST = {
+ {"format_msgpack" , flb_test_format_msgpack},
+ {"format_json" , flb_test_format_json},
+ {"format_json_stream" , flb_test_format_json_stream},
+ {"format_json_lines" , flb_test_format_json_lines},
+ {"format_gelf" , flb_test_format_gelf},
+ {"format_gelf_host_key" , flb_test_format_gelf_host_key},
+ {"format_gelf_timestamp_key" , flb_test_format_gelf_timestamp_key},
+ {"format_gelf_full_message_key" , flb_test_format_gelf_full_message_key},
+ {"format_gelf_level_key" , flb_test_format_gelf_level_key},
+ {"set_json_date_key" , flb_test_set_json_date_key},
+ {"disable_json_date_key" , flb_test_disable_json_date_key},
+ {"json_date_format_epoch" , flb_test_json_date_format_epoch},
+ {"json_date_format_iso8601" , flb_test_json_date_format_iso8601},
+ {"json_date_format_java_sql_timestamp" , flb_test_json_date_format_java_sql_timestamp},
+ {NULL, NULL}
+};
diff --git a/fluent-bit/tests/runtime/out_kinesis.c b/fluent-bit/tests/runtime/out_kinesis.c
new file mode 100644
index 00000000..da3e925a
--- /dev/null
+++ b/fluent-bit/tests/runtime/out_kinesis.c
@@ -0,0 +1,200 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+#include <fluent-bit.h>
+#include "flb_tests_runtime.h"
+
+/* Test data */
+#include "data/td/json_td.h" /* JSON_TD */
+
+#define ERROR_THROUGHPUT "{\"__type\":\"ServiceUnavailableException\"}"
+/* not a real error code, but tests that the code can respond to any error */
+#define ERROR_UNKNOWN "{\"__type\":\"UNKNOWN\"}"
+
+/* It writes a big JSON message (copied from TD test) */
+void flb_test_firehose_success(void)
+{
+ int ret;
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+
+ /* mocks calls- signals that we are in test mode */
+ setenv("FLB_KINESIS_PLUGIN_UNDER_TEST", "true", 1);
+
+ ctx = flb_create();
+
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ TEST_CHECK(in_ffd >= 0);
+ flb_input_set(ctx,in_ffd, "tag", "test", NULL);
+
+ out_ffd = flb_output(ctx, (char *) "kinesis_streams", NULL);
+ TEST_CHECK(out_ffd >= 0);
+ flb_output_set(ctx, out_ffd,"match", "*", NULL);
+ flb_output_set(ctx, out_ffd,"region", "us-west-2", NULL);
+ flb_output_set(ctx, out_ffd,"stream", "fluent", NULL);
+ flb_output_set(ctx, out_ffd,"time_key", "time", NULL);
+ flb_output_set(ctx, out_ffd,"Retry_Limit", "1", NULL);
+
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ flb_lib_push(ctx, in_ffd, (char *) JSON_TD , (int) sizeof(JSON_TD) - 1);
+
+ sleep(2);
+ flb_stop(ctx);
+ flb_destroy(ctx);
+}
+
+void flb_test_firehose_partial_success(void)
+{
+ int ret;
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+
+ /* mocks calls- signals that we are in test mode */
+ setenv("FLB_KINESIS_PLUGIN_UNDER_TEST", "true", 1);
+ setenv("PARTIAL_SUCCESS_CASE", "true", 1);
+
+ ctx = flb_create();
+
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ TEST_CHECK(in_ffd >= 0);
+ flb_input_set(ctx,in_ffd, "tag", "test", NULL);
+
+ out_ffd = flb_output(ctx, (char *) "kinesis_streams", NULL);
+ TEST_CHECK(out_ffd >= 0);
+ flb_output_set(ctx, out_ffd,"match", "*", NULL);
+ flb_output_set(ctx, out_ffd,"region", "us-west-2", NULL);
+ flb_output_set(ctx, out_ffd,"stream", "fluent", NULL);
+ flb_output_set(ctx, out_ffd,"time_key", "time", NULL);
+ flb_output_set(ctx, out_ffd,"Retry_Limit", "1", NULL);
+
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ flb_lib_push(ctx, in_ffd, (char *) JSON_TD , (int) sizeof(JSON_TD) - 1);
+
+ sleep(2);
+ flb_stop(ctx);
+ flb_destroy(ctx);
+ unsetenv("PARTIAL_SUCCESS_CASE");
+}
+
+void flb_test_firehose_throughput_error(void)
+{
+ int ret;
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+
+ /* mocks calls- signals that we are in test mode */
+ setenv("FLB_KINESIS_PLUGIN_UNDER_TEST", "true", 1);
+ setenv("TEST_PUT_RECORDS_ERROR", ERROR_THROUGHPUT, 1);
+
+ ctx = flb_create();
+
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ TEST_CHECK(in_ffd >= 0);
+ flb_input_set(ctx,in_ffd, "tag", "test", NULL);
+
+ out_ffd = flb_output(ctx, (char *) "kinesis_streams", NULL);
+ TEST_CHECK(out_ffd >= 0);
+ flb_output_set(ctx, out_ffd,"match", "*", NULL);
+ flb_output_set(ctx, out_ffd,"region", "us-west-2", NULL);
+ flb_output_set(ctx, out_ffd,"stream", "fluent", NULL);
+ flb_output_set(ctx, out_ffd,"time_key", "time", NULL);
+ flb_output_set(ctx, out_ffd,"Retry_Limit", "1", NULL);
+
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ flb_lib_push(ctx, in_ffd, (char *) JSON_TD , (int) sizeof(JSON_TD) - 1);
+
+ sleep(2);
+ flb_stop(ctx);
+ flb_destroy(ctx);
+ unsetenv("TEST_PUT_RECORDS_ERROR");
+}
+
+void flb_test_firehose_error_unknown(void)
+{
+ int ret;
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+
+ /* mocks calls- signals that we are in test mode */
+ setenv("FLB_KINESIS_PLUGIN_UNDER_TEST", "true", 1);
+ setenv("TEST_PUT_RECORDS_ERROR", ERROR_UNKNOWN, 1);
+
+ ctx = flb_create();
+
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ TEST_CHECK(in_ffd >= 0);
+ flb_input_set(ctx,in_ffd, "tag", "test", NULL);
+
+ out_ffd = flb_output(ctx, (char *) "kinesis_streams", NULL);
+ TEST_CHECK(out_ffd >= 0);
+ flb_output_set(ctx, out_ffd,"match", "*", NULL);
+ flb_output_set(ctx, out_ffd,"region", "us-west-2", NULL);
+ flb_output_set(ctx, out_ffd,"stream", "fluent", NULL);
+ flb_output_set(ctx, out_ffd,"time_key", "time", NULL);
+ flb_output_set(ctx, out_ffd,"Retry_Limit", "1", NULL);
+
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ flb_lib_push(ctx, in_ffd, (char *) JSON_TD , (int) sizeof(JSON_TD) - 1);
+
+ sleep(2);
+ flb_stop(ctx);
+ flb_destroy(ctx);
+ unsetenv("TEST_PUT_RECORDS_ERROR");
+}
+
+void flb_test_firehose_nonsense_error(void)
+{
+ int ret;
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+
+ /* mocks calls- signals that we are in test mode */
+ setenv("FLB_KINESIS_PLUGIN_UNDER_TEST", "true", 1);
+ setenv("TEST_PUT_RECORDS_ERROR", "\tbadresponse\nnotparsable{}", 1);
+
+ ctx = flb_create();
+
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ TEST_CHECK(in_ffd >= 0);
+ flb_input_set(ctx,in_ffd, "tag", "test", NULL);
+
+ out_ffd = flb_output(ctx, (char *) "kinesis_streams", NULL);
+ TEST_CHECK(out_ffd >= 0);
+ flb_output_set(ctx, out_ffd,"match", "*", NULL);
+ flb_output_set(ctx, out_ffd,"region", "us-west-2", NULL);
+ flb_output_set(ctx, out_ffd,"stream", "fluent", NULL);
+ flb_output_set(ctx, out_ffd,"time_key", "time", NULL);
+ flb_output_set(ctx, out_ffd,"Retry_Limit", "1", NULL);
+
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ flb_lib_push(ctx, in_ffd, (char *) JSON_TD , (int) sizeof(JSON_TD) - 1);
+
+ sleep(2);
+ flb_stop(ctx);
+ flb_destroy(ctx);
+ unsetenv("TEST_PUT_RECORDS_ERROR");
+}
+
+
+/* Test list */
+TEST_LIST = {
+ {"success", flb_test_firehose_success },
+ {"partial_success", flb_test_firehose_partial_success },
+ {"throughput_error", flb_test_firehose_throughput_error },
+ {"unknown_error", flb_test_firehose_error_unknown },
+ {"nonsense_error", flb_test_firehose_nonsense_error },
+ {NULL, NULL}
+};
diff --git a/fluent-bit/tests/runtime/out_lib.c b/fluent-bit/tests/runtime/out_lib.c
new file mode 100644
index 00000000..580a480f
--- /dev/null
+++ b/fluent-bit/tests/runtime/out_lib.c
@@ -0,0 +1,587 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/* Fluent Bit
+ * ==========
+ * Copyright (C) 2019-2022 The Fluent Bit Authors
+ * Copyright (C) 2015-2018 Treasure Data Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <fluent-bit.h>
+#include <fluent-bit/flb_compat.h>
+#include <fluent-bit/flb_time.h>
+#include <float.h>
+#include <math.h>
+#include "flb_tests_runtime.h"
+
+struct test_ctx {
+ flb_ctx_t *flb; /* Fluent Bit library context */
+ int i_ffd; /* Input fd */
+ int f_ffd; /* Filter fd (unused) */
+ int o_ffd; /* Output fd */
+};
+
+
+pthread_mutex_t result_mutex = PTHREAD_MUTEX_INITIALIZER;
+int num_output = 0;
+static int get_output_num()
+{
+ int ret;
+ pthread_mutex_lock(&result_mutex);
+ ret = num_output;
+ pthread_mutex_unlock(&result_mutex);
+
+ return ret;
+}
+
+static void set_output_num(int num)
+{
+ pthread_mutex_lock(&result_mutex);
+ num_output = num;
+ pthread_mutex_unlock(&result_mutex);
+}
+
+static void clear_output_num()
+{
+ set_output_num(0);
+}
+
+void wait_with_timeout(uint32_t timeout_ms, int *output_num)
+{
+ struct flb_time start_time;
+ struct flb_time end_time;
+ struct flb_time diff_time;
+ uint64_t elapsed_time_flb = 0;
+
+ flb_time_get(&start_time);
+
+ while (true) {
+ *output_num = get_output_num();
+
+ if (*output_num > 0) {
+ break;
+ }
+
+ flb_time_msleep(100);
+ flb_time_get(&end_time);
+ flb_time_diff(&end_time, &start_time, &diff_time);
+ elapsed_time_flb = flb_time_to_nanosec(&diff_time) / 1000000;
+
+ if (elapsed_time_flb > timeout_ms) {
+ flb_warn("[timeout] elapsed_time: %ld", elapsed_time_flb);
+ // Reached timeout.
+ break;
+ }
+ }
+}
+
+struct str_list {
+ size_t size;
+ char **lists;
+};
+
+/* Callback to check expected results */
+static int cb_check_json_str_list(void *record, size_t size, void *data)
+{
+ char *p;
+ char *result;
+ int num = get_output_num();
+ size_t i;
+ struct str_list *l = (struct str_list*)data;
+
+ if (!TEST_CHECK(l != NULL)) {
+ TEST_MSG("Data is NULL");
+ flb_free(record);
+ return 0;
+ }
+
+ set_output_num(num+1);
+
+ result = (char *) record;
+
+ for (i=0; i<l->size; i++) {
+ p = strstr(result, l->lists[i]);
+ if(!TEST_CHECK(p != NULL)) {
+ TEST_MSG("Expected to find: '%s' in result '%s'",
+ l->lists[i], result);
+ }
+ }
+
+ /*
+ * If you want to debug your test
+ *
+ * printf("Expect: '%s' in result '%s'", expected, result);
+ */
+ flb_free(record);
+ return 0;
+}
+
+static int msgpack_strncmp(char* str, size_t str_len, msgpack_object obj)
+{
+ int ret = -1;
+
+ if (str == NULL) {
+ flb_error("str is NULL");
+ return -1;
+ }
+
+ switch (obj.type) {
+ case MSGPACK_OBJECT_STR:
+ if (obj.via.str.size != str_len) {
+ return -1;
+ }
+ ret = strncmp(str, obj.via.str.ptr, str_len);
+ break;
+ case MSGPACK_OBJECT_POSITIVE_INTEGER:
+ {
+ unsigned long val = strtoul(str, NULL, 10);
+ if (val == (unsigned long)obj.via.u64) {
+ ret = 0;
+ }
+ }
+ break;
+ case MSGPACK_OBJECT_NEGATIVE_INTEGER:
+ {
+ long long val = strtoll(str, NULL, 10);
+ if (val == (unsigned long)obj.via.i64) {
+ ret = 0;
+ }
+ }
+ break;
+ case MSGPACK_OBJECT_FLOAT32:
+ case MSGPACK_OBJECT_FLOAT64:
+ {
+ double val = strtod(str, NULL);
+ if (fabs(val - obj.via.f64) < DBL_EPSILON) {
+ ret = 0;
+ }
+ }
+ break;
+ case MSGPACK_OBJECT_BOOLEAN:
+ if (obj.via.boolean) {
+ if (str_len != 4 /*true*/) {
+ return -1;
+ }
+ ret = strncasecmp(str, "true", 4);
+ }
+ else {
+ if (str_len != 5 /*false*/) {
+ return -1;
+ }
+ ret = strncasecmp(str, "false", 5);
+ }
+ break;
+ default:
+ flb_error("not supported");
+ }
+
+ return ret;
+}
+
+/* Callback to check expected results */
+static int cb_check_msgpack_kv(void *record, size_t size, void *data)
+
+ /*void *ctx, int ffd, int res_ret,
+ void *res_data, size_t res_size, void *data)*/
+{
+ msgpack_unpacked result;
+ msgpack_object obj;
+ size_t off = 0;
+ struct str_list *l = (struct str_list *)data;
+ int i_map;
+ int map_size;
+ int i_list;
+ int num = get_output_num();
+
+ if (!TEST_CHECK(record != NULL)) {
+ TEST_MSG("record is NULL");
+ return -1;
+ }
+
+ if (!TEST_CHECK(data != NULL)) {
+ TEST_MSG("data is NULL");
+ flb_free(record);
+ return -1;
+ }
+
+ /* Iterate each item array and apply rules */
+ msgpack_unpacked_init(&result);
+ while (msgpack_unpack_next(&result, record, size, &off) == MSGPACK_UNPACK_SUCCESS) {
+ obj = result.data;
+ /*
+ msgpack_object_print(stdout, obj);
+ */
+ if (obj.type != MSGPACK_OBJECT_ARRAY || obj.via.array.size != 2) {
+ flb_error("array error. type = %d", obj.type);
+ continue;
+ }
+ obj = obj.via.array.ptr[1];
+ if (obj.type != MSGPACK_OBJECT_MAP) {
+ flb_error("map error. type = %d", obj.type);
+ continue;
+ }
+ map_size = obj.via.map.size;
+ for (i_map=0; i_map<map_size; i_map++) {
+ if (obj.via.map.ptr[i_map].key.type != MSGPACK_OBJECT_STR) {
+ flb_error("key is not string. type =%d", obj.via.map.ptr[i_map].key.type);
+ continue;
+ }
+ for (i_list=0; i_list< l->size/2; i_list++) {
+ if (msgpack_strncmp(l->lists[i_list*2], strlen(l->lists[i_list*2]),
+ obj.via.map.ptr[i_map].key) == 0 &&
+ msgpack_strncmp(l->lists[i_list*2+1], strlen(l->lists[i_list*2+1]),
+ obj.via.map.ptr[i_map].val) == 0) {
+ num++;
+ }
+ }
+ }
+ }
+ set_output_num(num);
+
+ msgpack_unpacked_destroy(&result);
+ flb_free(record);
+
+ return 0;
+}
+
+
+static int cb_count_msgpack(void *record, size_t size, void *data)
+{
+ msgpack_unpacked result;
+ size_t off = 0;
+
+ if (!TEST_CHECK(data != NULL)) {
+ TEST_MSG("data is NULL");
+ }
+
+ /* Iterate each item array and apply rules */
+ msgpack_unpacked_init(&result);
+ while (msgpack_unpack_next(&result, record, size, &off) == MSGPACK_UNPACK_SUCCESS) {
+ pthread_mutex_lock(&result_mutex);
+ num_output++;
+ /*
+ msgpack_object_print(stdout, result.data);
+ */
+ pthread_mutex_unlock(&result_mutex);
+ }
+ msgpack_unpacked_destroy(&result);
+
+ flb_free(record);
+ return 0;
+}
+
+static int cb_count(void *record, size_t size, void *data)
+{
+ if (!TEST_CHECK(data != NULL)) {
+ TEST_MSG("data is NULL");
+ }
+ pthread_mutex_lock(&result_mutex);
+ num_output++;
+ pthread_mutex_unlock(&result_mutex);
+
+ flb_free(record);
+ return 0;
+}
+
+static struct test_ctx *test_ctx_create(struct flb_lib_out_cb *data)
+{
+ int o_ffd;
+ struct test_ctx *ctx = NULL;
+
+ ctx = flb_malloc(sizeof(struct test_ctx));
+ if (!TEST_CHECK(ctx != NULL)) {
+ TEST_MSG("malloc failed");
+ flb_errno();
+ return NULL;
+ }
+
+ /* Service config */
+ ctx->flb = flb_create();
+ flb_service_set(ctx->flb,
+ "Flush", "0.200000000",
+ "Grace", "1",
+ "Log_Level", "error",
+ NULL);
+
+ /* Output */
+ o_ffd = flb_output(ctx->flb, (char *) "lib", (void *) data);
+ ctx->o_ffd = o_ffd;
+
+ return ctx;
+}
+
+static void test_ctx_destroy(struct test_ctx *ctx)
+{
+ TEST_CHECK(ctx != NULL);
+
+ sleep(1);
+ flb_stop(ctx->flb);
+ flb_destroy(ctx->flb);
+ flb_free(ctx);
+}
+
+static void test_format_json(void)
+{
+ struct flb_lib_out_cb cb_data;
+ struct test_ctx *ctx;
+ char *input_json = "[1,{\"hoge\":\"moge\", \"bool\":true, \"int\":100, \"float\":-2.0}]";
+ int size = strlen(input_json);
+ int ret;
+ int num;
+
+ char *expected_strs[] = {"\"hoge\"", "\"moge\"", "\"bool\"", "true", "\"int\"", "100", "\"float\"", "-2."};
+ struct str_list expected = {
+ .size = sizeof(expected_strs)/sizeof(char*),
+ .lists = &expected_strs[0],
+ };
+
+ clear_output_num();
+
+ cb_data.cb = cb_check_json_str_list;
+ cb_data.data = &expected;
+
+ ctx = test_ctx_create(&cb_data);
+ if (!TEST_CHECK(ctx != NULL)) {
+ TEST_MSG("test_ctx_create failed");
+ exit(EXIT_FAILURE);
+ }
+ /* Input */
+ ctx->i_ffd = flb_input(ctx->flb, (char *) "lib", NULL);
+ TEST_CHECK(ctx->i_ffd >= 0);
+
+ ret = flb_output_set(ctx->flb, ctx->o_ffd,
+ "format", "json",
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ /* Start the engine */
+ ret = flb_start(ctx->flb);
+ TEST_CHECK(ret == 0);
+
+ ret = flb_lib_push(ctx->flb, ctx->i_ffd, input_json,size);
+ TEST_CHECK(ret >= 0);
+
+ /* waiting to flush */
+ wait_with_timeout(2000, &num);
+
+ if (!TEST_CHECK(num > 0)) {
+ TEST_MSG("no outputs");
+ }
+
+ test_ctx_destroy(ctx);
+}
+
+static void test_format_msgpack(void)
+{
+ struct flb_lib_out_cb cb_data;
+ struct test_ctx *ctx;
+ int ret;
+ int num;
+ char *input_json = "[1,{\"hoge\":\"moge\", \"bool\":true, \"int\":100, \"float\":-2.0}]";
+ int size = strlen(input_json);
+
+ char *expected_strs[] = {"hoge", "moge", "bool", "true", "int", "100", "float", "-2.0"};
+ struct str_list expected = {
+ .size = sizeof(expected_strs)/sizeof(char*),
+ .lists = &expected_strs[0],
+ };
+
+ clear_output_num();
+
+ cb_data.cb = cb_check_msgpack_kv;
+ cb_data.data = &expected;
+
+ ctx = test_ctx_create(&cb_data);
+ if (!TEST_CHECK(ctx != NULL)) {
+ TEST_MSG("test_ctx_create failed");
+ exit(EXIT_FAILURE);
+ }
+ /* Input */
+ ctx->i_ffd = flb_input(ctx->flb, (char *) "lib", NULL);
+ TEST_CHECK(ctx->i_ffd >= 0);
+
+ ret = flb_output_set(ctx->flb, ctx->o_ffd,
+ "format", "msgpack",
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ /* Start the engine */
+ ret = flb_start(ctx->flb);
+ TEST_CHECK(ret == 0);
+
+ ret = flb_lib_push(ctx->flb, ctx->i_ffd, input_json,size);
+ TEST_CHECK(ret >= 0);
+
+ /* waiting to flush */
+ wait_with_timeout(2000, &num);
+
+ if (!TEST_CHECK(num > 0)) {
+ TEST_MSG("no outputs");
+ }
+
+ test_ctx_destroy(ctx);
+}
+
+static void test_max_records(void)
+{
+ struct flb_lib_out_cb cb_data;
+ struct test_ctx *ctx;
+ int ret;
+ int num;
+ char *input_json = "[1,{\"hoge\":\"moge\", \"bool\":true, \"int\":100, \"float\":-2.0}]";
+ int size = strlen(input_json);
+ int i;
+ int unused;
+ int expected = 5 /* max_records */;
+
+ clear_output_num();
+
+ cb_data.cb = cb_count_msgpack;
+ cb_data.data = &unused;
+
+ ctx = test_ctx_create(&cb_data);
+ if (!TEST_CHECK(ctx != NULL)) {
+ TEST_MSG("test_ctx_create failed");
+ exit(EXIT_FAILURE);
+ }
+ /* Input */
+ ctx->i_ffd = flb_input(ctx->flb, (char *) "lib", NULL);
+ TEST_CHECK(ctx->i_ffd >= 0);
+
+ ret = flb_output_set(ctx->flb, ctx->o_ffd,
+ "format", "msgpack",
+ "max_records", "5",
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ /* Start the engine */
+ ret = flb_start(ctx->flb);
+ TEST_CHECK(ret == 0);
+
+ for (i=0; i<100; i++) {
+ ret = flb_lib_push(ctx->flb, ctx->i_ffd, input_json,size);
+ TEST_CHECK(ret >= 0);
+ }
+
+ /* waiting to flush */
+ wait_with_timeout(1000, &num);
+
+ if (!TEST_CHECK(num == expected /* max_records */)) {
+ TEST_MSG("max_records error. got=%d, expected=%d", num, expected);
+ }
+
+ test_ctx_destroy(ctx);
+}
+#ifdef FLB_HAVE_METRICS
+static void test_metrics_msgpack(void)
+{
+ struct flb_lib_out_cb cb_data;
+ struct test_ctx *ctx;
+ int ret;
+ int num;
+ int unused;
+
+ clear_output_num();
+
+ cb_data.cb = cb_count_msgpack;
+ cb_data.data = &unused;
+
+ ctx = test_ctx_create(&cb_data);
+ if (!TEST_CHECK(ctx != NULL)) {
+ TEST_MSG("test_ctx_create failed");
+ exit(EXIT_FAILURE);
+ }
+ /* Input */
+ ctx->i_ffd = flb_input(ctx->flb, (char *) "fluentbit_metrics", NULL);
+ TEST_CHECK(ctx->i_ffd >= 0);
+ ret = flb_input_set(ctx->flb, ctx->i_ffd,
+ "scrape_interval", "1",
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ ret = flb_output_set(ctx->flb, ctx->o_ffd,
+ "format", "msgpack",
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ /* Start the engine */
+ ret = flb_start(ctx->flb);
+ TEST_CHECK(ret == 0);
+
+ /* waiting to flush */
+ wait_with_timeout(5000, &num);
+
+ if (!TEST_CHECK(num > 0)) {
+ TEST_MSG("no outputs");
+ }
+
+ test_ctx_destroy(ctx);
+}
+
+static void test_metrics_json(void)
+{
+ struct flb_lib_out_cb cb_data;
+ struct test_ctx *ctx;
+ int ret;
+ int num;
+ int unused;
+
+ clear_output_num();
+
+ cb_data.cb = cb_count;
+ cb_data.data = &unused;
+
+ ctx = test_ctx_create(&cb_data);
+ if (!TEST_CHECK(ctx != NULL)) {
+ TEST_MSG("test_ctx_create failed");
+ exit(EXIT_FAILURE);
+ }
+ /* Input */
+ ctx->i_ffd = flb_input(ctx->flb, (char *) "fluentbit_metrics", NULL);
+ TEST_CHECK(ctx->i_ffd >= 0);
+ ret = flb_input_set(ctx->flb, ctx->i_ffd,
+ "scrape_interval", "1",
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ ret = flb_output_set(ctx->flb, ctx->o_ffd,
+ "format", "json",
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ /* Start the engine */
+ ret = flb_start(ctx->flb);
+ TEST_CHECK(ret == 0);
+
+ /* waiting to flush */
+ wait_with_timeout(5000, &num);
+
+ if (!TEST_CHECK(num > 0)) {
+ TEST_MSG("no outputs");
+ }
+
+ test_ctx_destroy(ctx);
+}
+#endif
+
+TEST_LIST = {
+#ifdef FLB_HAVE_METRICS
+ {"metrics_msgpack", test_metrics_msgpack},
+ {"metrics_json", test_metrics_json},
+#endif
+ {"format_json", test_format_json},
+ {"format_msgpack", test_format_msgpack},
+ {"max_records", test_max_records},
+ {NULL, NULL}
+};
diff --git a/fluent-bit/tests/runtime/out_loki.c b/fluent-bit/tests/runtime/out_loki.c
new file mode 100644
index 00000000..48805d2c
--- /dev/null
+++ b/fluent-bit/tests/runtime/out_loki.c
@@ -0,0 +1,623 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/* Fluent Bit
+ * ==========
+ * Copyright (C) 2019-2021 The Fluent Bit Authors
+ * Copyright (C) 2015-2018 Treasure Data Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <fluent-bit.h>
+#include <fluent-bit/flb_sds.h>
+#include "flb_tests_runtime.h"
+
+#define DPATH_LOKI FLB_TESTS_DATA_PATH "/data/loki"
+
+pthread_mutex_t result_mutex = PTHREAD_MUTEX_INITIALIZER;
+int num_output = 0;
+static int get_output_num()
+{
+ int ret;
+ pthread_mutex_lock(&result_mutex);
+ ret = num_output;
+ pthread_mutex_unlock(&result_mutex);
+
+ return ret;
+}
+
+static void set_output_num(int num)
+{
+ pthread_mutex_lock(&result_mutex);
+ num_output = num;
+ pthread_mutex_unlock(&result_mutex);
+}
+
+static void clear_output_num()
+{
+ set_output_num(0);
+}
+
+#define JSON_BASIC "[12345678, {\"key\":\"value\"}]"
+static void cb_check_basic(void *ctx, int ffd,
+ int res_ret, void *res_data, size_t res_size,
+ void *data)
+{
+ char *p;
+ flb_sds_t out_js = res_data;
+ char *index_line = "{\\\"key\\\":\\\"value\\\"}";
+
+ p = strstr(out_js, index_line);
+ if (!TEST_CHECK(p != NULL)) {
+ TEST_MSG("Given:%s", out_js);
+ }
+
+ flb_sds_destroy(out_js);
+}
+
+void flb_test_basic()
+{
+ int ret;
+ int size = sizeof(JSON_BASIC) - 1;
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+
+ /* Create context, flush every second (some checks omitted here) */
+ ctx = flb_create();
+ flb_service_set(ctx, "flush", "1", "grace", "1",
+ "log_level", "error",
+ NULL);
+
+ /* Lib input mode */
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ flb_input_set(ctx, in_ffd, "tag", "test", NULL);
+
+ /* Elasticsearch output */
+ out_ffd = flb_output(ctx, (char *) "loki", NULL);
+ flb_output_set(ctx, out_ffd,
+ "match", "test",
+ NULL);
+
+ /* Enable test mode */
+ ret = flb_output_set_test(ctx, out_ffd, "formatter",
+ cb_check_basic,
+ NULL, NULL);
+
+ /* Start */
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data sample */
+ ret = flb_lib_push(ctx, in_ffd, (char *) JSON_BASIC, size);
+ TEST_CHECK(ret >= 0);
+
+ sleep(2);
+ flb_stop(ctx);
+ flb_destroy(ctx);
+}
+
+static void cb_check_labels(void *ctx, int ffd,
+ int res_ret, void *res_data, size_t res_size,
+ void *data)
+{
+ char *p;
+ flb_sds_t out_js = res_data;
+ char *index_line = "\"stream\":{\"a\":\"b\"}";
+
+ p = strstr(out_js, index_line);
+ if (!TEST_CHECK(p != NULL)) {
+ TEST_MSG("Given:%s", out_js);
+ }
+
+ flb_sds_destroy(out_js);
+}
+
+void flb_test_labels()
+{
+ int ret;
+ int size = sizeof(JSON_BASIC) - 1;
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+
+ /* Create context, flush every second (some checks omitted here) */
+ ctx = flb_create();
+ flb_service_set(ctx, "flush", "1", "grace", "1",
+ "log_level", "error",
+ NULL);
+
+ /* Lib input mode */
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ flb_input_set(ctx, in_ffd, "tag", "test", NULL);
+
+ /* Elasticsearch output */
+ out_ffd = flb_output(ctx, (char *) "loki", NULL);
+ flb_output_set(ctx, out_ffd,
+ "match", "test",
+ "labels", "a=b", /* "stream":{"a":"b"} */
+ NULL);
+
+ /* Enable test mode */
+ ret = flb_output_set_test(ctx, out_ffd, "formatter",
+ cb_check_labels,
+ NULL, NULL);
+
+ /* Start */
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data sample */
+ ret = flb_lib_push(ctx, in_ffd, (char *) JSON_BASIC, size);
+ TEST_CHECK(ret >= 0);
+
+ sleep(2);
+ flb_stop(ctx);
+ flb_destroy(ctx);
+}
+
+static void cb_check_label_keys(void *ctx, int ffd,
+ int res_ret, void *res_data, size_t res_size,
+ void *data)
+{
+ char *p;
+ flb_sds_t out_js = res_data;
+ char *index_line = "{\"stream\":{\"data_l_key\":\"test\"}";
+
+ p = strstr(out_js, index_line);
+ if (!TEST_CHECK(p != NULL)) {
+ TEST_MSG("Given:%s", out_js);
+ }
+
+ flb_sds_destroy(out_js);
+}
+
+#define JSON_LABEL_KEYS "[12345678, {\"key\":\"value\",\"foo\":\"bar\", \"data\":{\"l_key\":\"test\"}}]"
+void flb_test_label_keys()
+{
+ int ret;
+ int size = sizeof(JSON_LABEL_KEYS) - 1;
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+
+ /* Create context, flush every second (some checks omitted here) */
+ ctx = flb_create();
+ flb_service_set(ctx, "flush", "1", "grace", "1",
+ "log_level", "error",
+ NULL);
+
+ /* Lib input mode */
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ flb_input_set(ctx, in_ffd, "tag", "test", NULL);
+
+ /* Elasticsearch output */
+ out_ffd = flb_output(ctx, (char *) "loki", NULL);
+ flb_output_set(ctx, out_ffd,
+ "match", "test",
+ "label_keys", "$data['l_key']", /* {"stream":{"data_l_key":"test"} */
+ NULL);
+
+ /* Enable test mode */
+ ret = flb_output_set_test(ctx, out_ffd, "formatter",
+ cb_check_label_keys,
+ NULL, NULL);
+
+ /* Start */
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data sample */
+ ret = flb_lib_push(ctx, in_ffd, (char *) JSON_LABEL_KEYS, size);
+ TEST_CHECK(ret >= 0);
+
+ sleep(2);
+ flb_stop(ctx);
+ flb_destroy(ctx);
+}
+
+static void cb_check_line_format(void *ctx, int ffd,
+ int res_ret, void *res_data, size_t res_size,
+ void *data)
+{
+ char *p;
+ flb_sds_t out_js = res_data;
+ char *index_line = "key=\\\"value\\\"";
+
+ p = strstr(out_js, index_line);
+ if (!TEST_CHECK(p != NULL)) {
+ TEST_MSG("Given:%s", out_js);
+ }
+
+ flb_sds_destroy(out_js);
+}
+
+void flb_test_line_format()
+{
+ int ret;
+ int size = sizeof(JSON_BASIC) - 1;
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+
+ /* Create context, flush every second (some checks omitted here) */
+ ctx = flb_create();
+ flb_service_set(ctx, "flush", "1", "grace", "1",
+ "log_level", "error",
+ NULL);
+
+ /* Lib input mode */
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ flb_input_set(ctx, in_ffd, "tag", "test", NULL);
+
+ /* Elasticsearch output */
+ out_ffd = flb_output(ctx, (char *) "loki", NULL);
+ flb_output_set(ctx, out_ffd,
+ "match", "test",
+ "line_format", "key_value",
+ NULL);
+
+ /* Enable test mode */
+ ret = flb_output_set_test(ctx, out_ffd, "formatter",
+ cb_check_line_format,
+ NULL, NULL);
+
+ /* Start */
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data sample */
+ ret = flb_lib_push(ctx, in_ffd, (char *) JSON_BASIC, size);
+ TEST_CHECK(ret >= 0);
+
+ sleep(2);
+ flb_stop(ctx);
+ flb_destroy(ctx);
+}
+
+
+static void cb_check_line_format_remove_keys(void *ctx, int ffd,
+ int res_ret, void *res_data,
+ size_t res_size, void *data)
+{
+ char *p;
+ flb_sds_t out_js = res_data;
+ char *index_line = "value_nested";
+
+ /* p == NULL is expected since it should be removed.*/
+ p = strstr(out_js, index_line);
+ if (!TEST_CHECK(p == NULL)) {
+ TEST_MSG("Given:%s", out_js);
+ }
+
+ flb_sds_destroy(out_js);
+}
+#define JSON_BASIC_NEST "[12345678, {\"key\": {\"nest\":\"value_nested\"}} ]"
+/* https://github.com/fluent/fluent-bit/issues/3875 */
+void flb_test_remove_map()
+{
+ int ret;
+ int size = sizeof(JSON_BASIC_NEST) - 1;
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+
+ /* Create context, flush every second (some checks omitted here) */
+ ctx = flb_create();
+ flb_service_set(ctx, "flush", "1", "grace", "1",
+ "log_level", "error",
+ NULL);
+
+ /* Lib input mode */
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ flb_input_set(ctx, in_ffd, "tag", "test", NULL);
+
+ /* Elasticsearch output */
+ out_ffd = flb_output(ctx, (char *) "loki", NULL);
+ flb_output_set(ctx, out_ffd,
+ "match", "test",
+ "remove_keys", "key",
+ NULL);
+
+ /* Enable test mode */
+ ret = flb_output_set_test(ctx, out_ffd, "formatter",
+ cb_check_line_format_remove_keys,
+ NULL, NULL);
+
+ /* Start */
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data sample */
+ ret = flb_lib_push(ctx, in_ffd, (char *) JSON_BASIC_NEST, size);
+ TEST_CHECK(ret >= 0);
+
+ sleep(2);
+ flb_stop(ctx);
+ flb_destroy(ctx);
+}
+
+static void cb_check_labels_ra(void *ctx, int ffd,
+ int res_ret, void *res_data, size_t res_size,
+ void *data)
+{
+ char *p;
+ flb_sds_t out_js = res_data;
+ char *index_line = "\\\"data\\\":{\\\"l_key\\\":\\\"test\\\"}";
+
+ p = strstr(out_js, index_line);
+ if (!TEST_CHECK(p != NULL)) {
+ TEST_MSG("Given:%s", out_js);
+ }
+
+ flb_sds_destroy(out_js);
+}
+
+/* https://github.com/fluent/fluent-bit/issues/3867 */
+void flb_test_labels_ra()
+{
+ int ret;
+ int size = sizeof(JSON_LABEL_KEYS) - 1;
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+
+ /* Create context, flush every second (some checks omitted here) */
+ ctx = flb_create();
+ flb_service_set(ctx, "flush", "1", "grace", "1",
+ "log_level", "error",
+ NULL);
+
+ /* Lib input mode */
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ flb_input_set(ctx, in_ffd, "tag", "test", NULL);
+
+ /* Elasticsearch output */
+ out_ffd = flb_output(ctx, (char *) "loki", NULL);
+ flb_output_set(ctx, out_ffd,
+ "match", "test",
+ "labels", "$data['l_key']",
+ NULL);
+
+ /* Enable test mode */
+ ret = flb_output_set_test(ctx, out_ffd, "formatter",
+ cb_check_labels_ra,
+ NULL, NULL);
+
+ /* Start */
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data sample */
+ flb_lib_push(ctx, in_ffd, (char *) JSON_LABEL_KEYS, size);
+
+ sleep(2);
+ flb_stop(ctx);
+ flb_destroy(ctx);
+}
+
+static void cb_check_remove_keys(void *ctx, int ffd,
+ int res_ret, void *res_data, size_t res_size,
+ void *data)
+{
+ char *p;
+ flb_sds_t out_js = res_data;
+
+ p = strstr(out_js, "foo");
+ if (!TEST_CHECK(p == NULL)) {
+ TEST_MSG("Given:%s", out_js);
+ }
+
+ p = strstr(out_js, "l_key");
+ if (!TEST_CHECK(p == NULL)) {
+ TEST_MSG("Given:%s", out_js);
+ }
+
+ flb_sds_destroy(out_js);
+}
+
+void flb_test_remove_keys()
+{
+ int ret;
+ int size = sizeof(JSON_LABEL_KEYS) - 1;
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+
+ /* Create context, flush every second (some checks omitted here) */
+ ctx = flb_create();
+ flb_service_set(ctx, "flush", "1", "grace", "1",
+ "log_level", "error",
+ NULL);
+
+ /* Lib input mode */
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ flb_input_set(ctx, in_ffd, "tag", "test", NULL);
+
+ /* Elasticsearch output */
+ out_ffd = flb_output(ctx, (char *) "loki", NULL);
+ flb_output_set(ctx, out_ffd,
+ "match", "test",
+ "remove_keys", "foo, $data['l_key']",
+ NULL);
+
+ /* Enable test mode */
+ ret = flb_output_set_test(ctx, out_ffd, "formatter",
+ cb_check_remove_keys,
+ NULL, NULL);
+
+ /* Start */
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data sample */
+ flb_lib_push(ctx, in_ffd, (char *) JSON_LABEL_KEYS, size);
+
+ sleep(2);
+ flb_stop(ctx);
+ flb_destroy(ctx);
+}
+
+static void cb_check_label_map_path(void *ctx, int ffd,
+ int res_ret, void *res_data, size_t res_size,
+ void *data)
+{
+ char *p;
+ flb_sds_t out_log = res_data;
+ char *expected[] = {
+ "\"container\":\"promtail\"",
+ "\"pod\":\"promtail-xxx\"",
+ "\"namespace\":\"prod\"",
+ "\"team\":\"lalala\"",
+ NULL};
+ int i = 0;
+
+ set_output_num(1);
+
+ while(expected[i] != NULL) {
+ p = strstr(out_log, expected[i]);
+ if (!TEST_CHECK(p != NULL)) {
+ TEST_MSG("Given:%s Expect:%s", out_log, expected[i]);
+ }
+ i++;
+ }
+
+ flb_sds_destroy(out_log);
+}
+
+void flb_test_label_map_path()
+{
+ int ret;
+ char *str = "[12345678, {\"kubernetes\":{\"container_name\":\"promtail\",\"pod_name\":\"promtail-xxx\",\"namespace_name\":\"prod\",\"labels\":{\"team\": \"lalala\"}},\"log\":\"log\"}]";
+ int size = strlen(str);
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+
+ clear_output_num();
+
+ /* Create context, flush every second (some checks omitted here) */
+ ctx = flb_create();
+ flb_service_set(ctx, "flush", "1", "grace", "1",
+ "log_level", "error",
+ NULL);
+
+ /* Lib input mode */
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ flb_input_set(ctx, in_ffd, "tag", "test", NULL);
+
+ /* Elasticsearch output */
+ out_ffd = flb_output(ctx, (char *) "loki", NULL);
+ flb_output_set(ctx, out_ffd,
+ "match", "test",
+ "label_map_path", DPATH_LOKI "/labelmap.json",
+ NULL);
+
+ /* Enable test mode */
+ ret = flb_output_set_test(ctx, out_ffd, "formatter",
+ cb_check_label_map_path,
+ NULL, NULL);
+
+ /* Start */
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data sample */
+ ret = flb_lib_push(ctx, in_ffd, str, size);
+ TEST_CHECK(ret == size);
+
+ sleep(2);
+
+ ret = get_output_num();
+ if (!TEST_CHECK(ret != 0)) {
+ TEST_MSG("no output");
+ }
+
+ flb_stop(ctx);
+ flb_destroy(ctx);
+}
+
+static void cb_check_float_value(void *ctx, int ffd,
+ int res_ret, void *res_data, size_t res_size,
+ void *data)
+{
+ char *p;
+ flb_sds_t out_js = res_data;
+ char *index_line = "\"float=1.3\"";
+
+ p = strstr(out_js, index_line);
+ if (!TEST_CHECK(p != NULL)) {
+ TEST_MSG("Given:%s", out_js);
+ }
+
+ flb_sds_destroy(out_js);
+}
+
+#define JSON_FLOAT "[12345678, {\"float\":1.3}]"
+void flb_test_float_value()
+{
+ int ret;
+ int size = sizeof(JSON_FLOAT) - 1;
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+
+ /* Create context, flush every second (some checks omitted here) */
+ ctx = flb_create();
+ flb_service_set(ctx, "flush", "1", "grace", "1",
+ "log_level", "error",
+ NULL);
+
+ /* Lib input mode */
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ flb_input_set(ctx, in_ffd, "tag", "test", NULL);
+
+ /* Elasticsearch output */
+ out_ffd = flb_output(ctx, (char *) "loki", NULL);
+ flb_output_set(ctx, out_ffd,
+ "match", "test",
+ "line_format", "key_value",
+ NULL);
+
+ /* Enable test mode */
+ ret = flb_output_set_test(ctx, out_ffd, "formatter",
+ cb_check_float_value,
+ NULL, NULL);
+
+ /* Start */
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data sample */
+ ret = flb_lib_push(ctx, in_ffd, (char *) JSON_FLOAT, size);
+ TEST_CHECK(ret >= 0);
+
+ sleep(2);
+ flb_stop(ctx);
+ flb_destroy(ctx);
+}
+
+
+/* Test list */
+TEST_LIST = {
+ {"remove_keys_remove_map" , flb_test_remove_map},
+ {"labels_ra" , flb_test_labels_ra },
+ {"remove_keys" , flb_test_remove_keys },
+ {"basic" , flb_test_basic },
+ {"labels" , flb_test_labels },
+ {"label_keys" , flb_test_label_keys },
+ {"line_format" , flb_test_line_format },
+ {"label_map_path" , flb_test_label_map_path},
+ {"float_value" , flb_test_float_value},
+ {NULL, NULL}
+};
diff --git a/fluent-bit/tests/runtime/out_null.c b/fluent-bit/tests/runtime/out_null.c
new file mode 100644
index 00000000..c74e0977
--- /dev/null
+++ b/fluent-bit/tests/runtime/out_null.c
@@ -0,0 +1,127 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+#include <fluent-bit.h>
+#include "flb_tests_runtime.h"
+
+/* Test data */
+#include "data/common/json_invalid.h" /* JSON_INVALID */
+#include "data/common/json_long.h" /* JSON_LONG */
+#include "data/common/json_small.h" /* JSON_SMALL */
+
+/* Test functions */
+void flb_test_null_json_invalid(void);
+void flb_test_null_json_long(void);
+void flb_test_null_json_small(void);
+
+/* Test list */
+TEST_LIST = {
+ {"json_invalid", flb_test_null_json_invalid },
+ {"json_long", flb_test_null_json_long },
+ {"json_small", flb_test_null_json_small },
+ {NULL, NULL}
+};
+
+
+void flb_test_null_json_invalid(void)
+{
+ int i;
+ int ret;
+ int bytes;
+ char *p = (char *) JSON_INVALID;
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+
+ ctx = flb_create();
+ flb_service_set(ctx, "Flush", "1", "Grace", "1", "Log_Level", "error", NULL);
+
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ TEST_CHECK(in_ffd >= 0);
+ flb_input_set(ctx, in_ffd, "tag", "test", NULL);
+
+ out_ffd = flb_output(ctx, (char *) "null", NULL);
+ TEST_CHECK(out_ffd >= 0);
+ flb_output_set(ctx, out_ffd, "match", "test", NULL);
+
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ for (i = 0; i < (int) sizeof(JSON_INVALID) - 1; i++) {
+ bytes = flb_lib_push(ctx, in_ffd, p + i, 1);
+ TEST_CHECK(bytes == 1);
+ }
+
+ flb_stop(ctx);
+ flb_destroy(ctx);
+}
+
+/* It writes a very long JSON map (> 100KB) byte by byte */
+void flb_test_null_json_long(void)
+{
+ int i;
+ int ret;
+ int bytes;
+ char *p = (char *) JSON_LONG;
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+
+ ctx = flb_create();
+ flb_service_set(ctx, "Flush", "1", "Grace", "1", "Log_Level", "error", NULL);
+
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ TEST_CHECK(in_ffd >= 0);
+ flb_input_set(ctx, in_ffd, "tag", "test", NULL);
+
+ out_ffd = flb_output(ctx, (char *) "null", NULL);
+ TEST_CHECK(out_ffd >= 0);
+ flb_output_set(ctx, out_ffd, "match", "test", NULL);
+
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ for (i = 0; i < (int) sizeof(JSON_LONG) - 1; i++) {
+ bytes = flb_lib_push(ctx, in_ffd, p + i, 1);
+ TEST_CHECK(bytes == 1);
+ }
+
+ sleep(1); /* waiting flush */
+
+ flb_stop(ctx);
+ flb_destroy(ctx);
+}
+
+void flb_test_null_json_small(void)
+{
+ int i;
+ int ret;
+ int bytes;
+ char *p = (char *) JSON_SMALL;
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+
+ ctx = flb_create();
+ flb_service_set(ctx, "Flush", "1", "Grace", "1", "Log_Level", "error", NULL);
+
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ TEST_CHECK(in_ffd >= 0);
+ flb_input_set(ctx, in_ffd, "tag", "test", NULL);
+
+ out_ffd = flb_output(ctx, (char *) "null", NULL);
+ TEST_CHECK(out_ffd >= 0);
+ flb_output_set(ctx, out_ffd, "match", "test", NULL);
+
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ for (i = 0; i < (int) sizeof(JSON_SMALL) - 1; i++) {
+ bytes = flb_lib_push(ctx, in_ffd, p + i, 1);
+ TEST_CHECK(bytes == 1);
+ }
+
+ sleep(1); /* waiting flush */
+
+ flb_stop(ctx);
+ flb_destroy(ctx);
+}
diff --git a/fluent-bit/tests/runtime/out_opensearch.c b/fluent-bit/tests/runtime/out_opensearch.c
new file mode 100644
index 00000000..26ced9e5
--- /dev/null
+++ b/fluent-bit/tests/runtime/out_opensearch.c
@@ -0,0 +1,1077 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+#include <fluent-bit.h>
+#include "flb_tests_runtime.h"
+
+/* Test data */
+#include "data/es/json_es.h" /* JSON_ES */
+
+static void cb_check_write_op_index(void *ctx, int ffd,
+ int res_ret, void *res_data,
+ size_t res_size, void *data)
+{
+ char *p;
+ char *out_js = res_data;
+ char *index_line = "{\"index\":{";
+
+ p = strstr(out_js, index_line);
+ TEST_CHECK(p == out_js);
+
+ flb_sds_destroy(res_data);
+}
+
+static void cb_check_write_op_create(void *ctx, int ffd,
+ int res_ret, void *res_data,
+ size_t res_size, void *data)
+{
+ char *p;
+ char *out_js = res_data;
+ char *index_line = "{\"create\":{";
+
+ p = strstr(out_js, index_line);
+ TEST_CHECK(p == out_js);
+
+ flb_sds_destroy(res_data);
+}
+
+static void cb_check_write_op_update(void *ctx, int ffd,
+ int res_ret, void *res_data,
+ size_t res_size, void *data)
+{
+ char *p;
+ char *b;
+ char *out_js = res_data;
+ char *index_line = "{\"update\":{";
+ char *body = "{\"doc\":";
+
+ p = strstr(out_js, index_line);
+ TEST_CHECK(p == out_js);
+ b = strstr(out_js, body);
+ TEST_CHECK(b != NULL);
+
+ flb_sds_destroy(res_data);
+}
+
+static void cb_check_write_op_upsert(void *ctx, int ffd,
+ int res_ret, void *res_data,
+ size_t res_size, void *data)
+{
+ char *p;
+ char *b;
+ char *out_js = res_data;
+ char *index_line = "{\"update\":{";
+ char *body = "{\"doc_as_upsert\":true,\"doc\":";
+
+ p = strstr(out_js, index_line);
+ TEST_CHECK(p == out_js);
+ b = strstr(out_js, body);
+ TEST_CHECK(b != NULL);
+
+ flb_sds_destroy(res_data);
+}
+
+static void cb_check_index_type(void *ctx, int ffd,
+ int res_ret, void *res_data, size_t res_size,
+ void *data)
+{
+ char *p;
+ char *out_js = res_data;
+ char *index_line = "{\"create\":{\"_index\":\"index_test\",\"_type\":\"type_test\"}";
+
+ p = strstr(out_js, index_line);
+ TEST_CHECK(p != NULL);
+
+ flb_sds_destroy(res_data);
+}
+
+static void cb_check_index_record_accessor(void *ctx, int ffd,
+ int res_ret, void *res_data, size_t res_size,
+ void *data)
+{
+ char *p;
+ char *out_js = res_data;
+ char *index_line = "{\"create\":{\"_index\":\"abc\",\"_type\":\"def\"}";
+
+ p = strstr(out_js, index_line);
+ TEST_CHECK(p != NULL);
+
+ flb_sds_destroy(res_data);
+}
+
+static void cb_check_index_record_accessor_suppress_type(void *ctx, int ffd,
+ int res_ret, void *res_data, size_t res_size,
+ void *data)
+{
+ char *p;
+ char *out_js = res_data;
+ char *index_line = "{\"create\":{\"_index\":\"abc\"}";
+
+ p = strstr(out_js, index_line);
+ TEST_CHECK(p != NULL);
+
+ flb_sds_destroy(res_data);
+}
+
+static void cb_check_index_record_accessor_id_key(void *ctx, int ffd,
+ int res_ret, void *res_data, size_t res_size,
+ void *data)
+{
+ char *p;
+ char *out_js = res_data;
+ char *index_line = "{\"create\":{\"_index\":\"abc\",\"_type\":\"def\",\"_id\":\"something\"}}";
+
+ p = strstr(out_js, index_line);
+ TEST_CHECK(p != NULL);
+
+ flb_sds_destroy(res_data);
+}
+
+static void cb_check_index_record_accessor_generate_id(void *ctx, int ffd,
+ int res_ret, void *res_data, size_t res_size,
+ void *data)
+{
+ char *p;
+ char *out_js = res_data;
+ char *index_line = "{\"create\":{\"_index\":\"code\",\"_type\":\"def\",\"_id\":\"";
+ char *id = NULL;
+ char c;
+ int i;
+
+ // check that index is working
+ p = strstr(out_js, index_line);
+ TEST_CHECK(p != NULL);
+
+ // check that we have a UUID
+ id = p + strlen(index_line);
+ TEST_CHECK(strlen(id) > 36);
+ for (i = 0; i < strlen(id) && i < 36; i++) {
+ c = (*(id+i));
+ TEST_CHECK(
+ (c >= 'A' && c <= 'F') ||
+ (*(id+i)) == '-' ||
+ (c >= '0' && c <= '9')
+ );
+ }
+
+ flb_sds_destroy(res_data);
+}
+
+static void cb_check_logstash_format(void *ctx, int ffd,
+ int res_ret, void *res_data, size_t res_size,
+ void *data)
+{
+ char *p;
+ char *out_js = res_data;
+ char *index_line = "{\"create\":{\"_index\":\"prefix-2015-11-24\",\"_type\":\"_doc\"}";
+
+ p = strstr(out_js, index_line);
+ TEST_CHECK(p != NULL);
+
+ flb_sds_destroy(res_data);
+}
+
+static void cb_check_logstash_prefix_separator(void *ctx, int ffd,
+ int res_ret, void *res_data, size_t res_size,
+ void *data)
+{
+ char *p;
+ char *out_js = res_data;
+ char *index_line = "{\"create\":{\"_index\":\"prefixSEP2015-11-24\",\"_type\":\"_doc\"}";
+
+ p = strstr(out_js, index_line);
+ if(!TEST_CHECK(p != NULL)) {
+ TEST_MSG("Got: %s", out_js);
+ }
+ flb_sds_destroy(res_data);
+}
+
+static void cb_check_logstash_format_nanos(void *ctx, int ffd,
+ int res_ret, void *res_data, size_t res_size,
+ void *data)
+{
+ char *p;
+ char *out_js = res_data;
+ char *index_line = "\"@timestamp\":\"2015-11-24T22:15:40.000000000Z\"";
+
+ p = strstr(out_js, index_line);
+ TEST_CHECK(p != NULL);
+
+ flb_sds_destroy(res_data);
+}
+
+static void cb_check_tag_key(void *ctx, int ffd,
+ int res_ret, void *res_data, size_t res_size,
+ void *data)
+{
+ char *p;
+ char *out_js = res_data;
+ char *record = "\"mytag\":\"test\"";
+
+ p = strstr(out_js, record);
+ TEST_CHECK(p != NULL);
+ flb_sds_destroy(res_data);
+}
+
+static void cb_check_replace_dots(void *ctx, int ffd,
+ int res_ret, void *res_data, size_t res_size,
+ void *data)
+{
+ char *p;
+ char *out_js = res_data;
+ char *record = "\"_o_k\":[{\"_b_ar\"";
+
+ p = strstr(out_js, record);
+ TEST_CHECK(p != NULL);
+
+ flb_sds_destroy(res_data);
+}
+
+static void cb_check_id_key(void *ctx, int ffd,
+ int res_ret, void *res_data, size_t res_size,
+ void *data)
+{
+ char *p;
+ char *out_js = res_data;
+ char *record = "\"_id\":\"some string\""; // see data/es/json_es.h
+
+ p = strstr(out_js, record);
+ TEST_CHECK(p != NULL);
+
+ flb_sds_destroy(res_data);
+}
+
+void flb_test_write_operation_index()
+{
+ int ret;
+ int size = sizeof(JSON_ES) - 1;
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+
+ /* Create context, flush every second (some checks omitted here) */
+ ctx = flb_create();
+ flb_service_set(ctx, "flush", "1", "grace", "1", NULL);
+
+ /* Lib input mode */
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ flb_input_set(ctx, in_ffd, "tag", "test", NULL);
+
+ /* Elasticsearch output */
+ out_ffd = flb_output(ctx, (char *) "opensearch", NULL);
+ flb_output_set(ctx, out_ffd,
+ "match", "test",
+ NULL);
+
+ /* Override defaults of index and type */
+ flb_output_set(ctx, out_ffd,
+ "write_operation", "index",
+ NULL);
+
+ /* Enable test mode */
+ ret = flb_output_set_test(ctx, out_ffd, "formatter",
+ cb_check_write_op_index,
+ NULL, NULL);
+
+ /* Start */
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data sample */
+ flb_lib_push(ctx, in_ffd, (char *) JSON_ES, size);
+
+ sleep(2);
+ flb_stop(ctx);
+ flb_destroy(ctx);
+}
+
+void flb_test_write_operation_create()
+{
+ int ret;
+ int size = sizeof(JSON_ES) - 1;
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+
+ /* Create context, flush every second (some checks omitted here) */
+ ctx = flb_create();
+ flb_service_set(ctx, "flush", "1", "grace", "1", NULL);
+
+ /* Lib input mode */
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ flb_input_set(ctx, in_ffd, "tag", "test", NULL);
+
+ /* Elasticsearch output */
+ out_ffd = flb_output(ctx, (char *) "opensearch", NULL);
+ flb_output_set(ctx, out_ffd,
+ "match", "test",
+ NULL);
+
+ /* Override defaults of index and type */
+ flb_output_set(ctx, out_ffd,
+ "write_operation", "create",
+ NULL);
+
+ /* Enable test mode */
+ ret = flb_output_set_test(ctx, out_ffd, "formatter",
+ cb_check_write_op_create,
+ NULL, NULL);
+
+ /* Start */
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data sample */
+ flb_lib_push(ctx, in_ffd, (char *) JSON_ES, size);
+
+ sleep(2);
+ flb_stop(ctx);
+ flb_destroy(ctx);
+}
+
+
+void flb_test_write_operation_update()
+{
+ int ret;
+ int size = sizeof(JSON_ES) - 1;
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+
+ /* Create context, flush every second (some checks omitted here) */
+ ctx = flb_create();
+ flb_service_set(ctx, "flush", "1", "grace", "1", NULL);
+
+ /* Lib input mode */
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ flb_input_set(ctx, in_ffd, "tag", "test", NULL);
+
+ /* Elasticsearch output */
+ out_ffd = flb_output(ctx, (char *) "opensearch", NULL);
+ flb_output_set(ctx, out_ffd,
+ "match", "test",
+ NULL);
+
+ /* Override defaults of index and type */
+ flb_output_set(ctx, out_ffd,
+ "Write_Operation", "Update",
+ "Generate_Id", "True",
+ NULL);
+
+ /* Enable test mode */
+ ret = flb_output_set_test(ctx, out_ffd, "formatter",
+ cb_check_write_op_update,
+ NULL, NULL);
+
+ /* Start */
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data sample */
+ flb_lib_push(ctx, in_ffd, (char *) JSON_ES, size);
+
+ sleep(2);
+ flb_stop(ctx);
+ flb_destroy(ctx);
+}
+
+
+void flb_test_write_operation_upsert()
+{
+ int ret;
+ int size = sizeof(JSON_ES) - 1;
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+
+ /* Create context, flush every second (some checks omitted here) */
+ ctx = flb_create();
+ flb_service_set(ctx, "flush", "1", "grace", "1", NULL);
+
+ /* Lib input mode */
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ flb_input_set(ctx, in_ffd, "tag", "test", NULL);
+
+ /* Elasticsearch output */
+ out_ffd = flb_output(ctx, (char *) "opensearch", NULL);
+ flb_output_set(ctx, out_ffd,
+ "match", "test",
+ NULL);
+
+ /* Override defaults of index and type */
+ flb_output_set(ctx, out_ffd,
+ "Write_Operation", "Upsert",
+ "Generate_Id", "True",
+ NULL);
+
+ /* Enable test mode */
+ ret = flb_output_set_test(ctx, out_ffd, "formatter",
+ cb_check_write_op_upsert,
+ NULL, NULL);
+
+ /* Start */
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data sample */
+ flb_lib_push(ctx, in_ffd, (char *) JSON_ES, size);
+
+ sleep(2);
+ flb_stop(ctx);
+ flb_destroy(ctx);
+}
+
+void flb_test_index_type()
+{
+ int ret;
+ int size = sizeof(JSON_ES) - 1;
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+
+ /* Create context, flush every second (some checks omitted here) */
+ ctx = flb_create();
+ flb_service_set(ctx, "flush", "1", "grace", "1", NULL);
+
+ /* Lib input mode */
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ flb_input_set(ctx, in_ffd, "tag", "test", NULL);
+
+ /* Elasticsearch output */
+ out_ffd = flb_output(ctx, (char *) "opensearch", NULL);
+ flb_output_set(ctx, out_ffd,
+ "match", "test",
+ NULL);
+
+ /* Override defaults of index and type */
+ flb_output_set(ctx, out_ffd,
+ "index", "index_test",
+ "type", "type_test",
+ NULL);
+
+ /* Enable test mode */
+ ret = flb_output_set_test(ctx, out_ffd, "formatter",
+ cb_check_index_type,
+ NULL, NULL);
+
+ /* Start */
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data sample */
+ flb_lib_push(ctx, in_ffd, (char *) JSON_ES, size);
+
+ sleep(2);
+ flb_stop(ctx);
+ flb_destroy(ctx);
+}
+
+void flb_test_index_record_accessor()
+{
+ int ret;
+ int len;
+ int in_ffd;
+ int out_ffd;
+ char *record = "[1448403340, {\"key\": \"something\", \"myindex\": \"abc\"}]";
+
+ flb_ctx_t *ctx;
+
+ /* Create context, flush every second (some checks omitted here) */
+ ctx = flb_create();
+ flb_service_set(ctx, "flush", "1", "grace", "1", NULL);
+
+ /* Lib input mode */
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ flb_input_set(ctx, in_ffd, "tag", "test", NULL);
+
+ /* Elasticsearch output */
+ out_ffd = flb_output(ctx, (char *) "opensearch", NULL);
+
+ flb_output_set(ctx, out_ffd,
+ "match", "test",
+ "index", "$myindex",
+ "type", "def",
+ NULL);
+
+ /* Enable test mode */
+ ret = flb_output_set_test(ctx, out_ffd, "formatter",
+ cb_check_index_record_accessor,
+ NULL, NULL);
+
+ /* Start */
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data sample */
+ len = strlen(record);
+ flb_lib_push(ctx, in_ffd, record, len);
+
+ sleep(2);
+ flb_stop(ctx);
+ flb_destroy(ctx);
+}
+
+void flb_test_index_record_accessor_suppress_type()
+{
+ int ret;
+ int len;
+ int in_ffd;
+ int out_ffd;
+ char *record = "[1448403340, {\"key\": \"something\", \"myindex\": \"abc\"}]";
+
+ flb_ctx_t *ctx;
+
+ /* Create context, flush every second (some checks omitted here) */
+ ctx = flb_create();
+ flb_service_set(ctx, "flush", "1", "grace", "1", NULL);
+
+ /* Lib input mode */
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ flb_input_set(ctx, in_ffd, "tag", "test", NULL);
+
+ /* Elasticsearch output */
+ out_ffd = flb_output(ctx, (char *) "opensearch", NULL);
+
+ flb_output_set(ctx, out_ffd,
+ "match", "test",
+ "index", "$myindex",
+ "suppress_type_name", "true",
+ NULL);
+
+ /* Enable test mode */
+ ret = flb_output_set_test(ctx, out_ffd, "formatter",
+ cb_check_index_record_accessor_suppress_type,
+ NULL, NULL);
+
+ /* Start */
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data sample */
+ len = strlen(record);
+ flb_lib_push(ctx, in_ffd, record, len);
+
+ sleep(2);
+ flb_stop(ctx);
+ flb_destroy(ctx);
+}
+
+void flb_test_index_record_accessor_with_id_key()
+{
+ int ret;
+ int len;
+ int in_ffd;
+ int out_ffd;
+ char *record = "[1448403340, {\"key\": \"something\", \"myindex\": \"abc\"}]";
+
+ flb_ctx_t *ctx;
+
+ /* Create context, flush every second (some checks omitted here) */
+ ctx = flb_create();
+ flb_service_set(ctx, "flush", "1", "grace", "1", NULL);
+
+ /* Lib input mode */
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ flb_input_set(ctx, in_ffd, "tag", "test", NULL);
+
+ /* Elasticsearch output */
+ out_ffd = flb_output(ctx, (char *) "opensearch", NULL);
+
+ flb_output_set(ctx, out_ffd,
+ "match", "test",
+ "index", "$myindex",
+ "type", "def",
+ "id_key", "key",
+ NULL);
+
+ /* Enable test mode */
+ ret = flb_output_set_test(ctx, out_ffd, "formatter",
+ cb_check_index_record_accessor_id_key,
+ NULL, NULL);
+
+ /* Start */
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data sample */
+ len = strlen(record);
+ flb_lib_push(ctx, in_ffd, record, len);
+
+ sleep(2);
+ flb_stop(ctx);
+ flb_destroy(ctx);
+}
+
+void flb_test_index_record_accessor_with_generate_id()
+{
+ int ret;
+ int len;
+ int in_ffd;
+ int out_ffd;
+ char *record = "[1448403340, {\"key\": \"xul\", \"myindex\": \"code\"}]";
+
+ flb_ctx_t *ctx;
+
+ /* Create context, flush every second (some checks omitted here) */
+ ctx = flb_create();
+ flb_service_set(ctx, "flush", "1", "grace", "1", NULL);
+
+ /* Lib input mode */
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ flb_input_set(ctx, in_ffd, "tag", "test", NULL);
+
+ /* Elasticsearch output */
+ out_ffd = flb_output(ctx, (char *) "opensearch", NULL);
+
+ flb_output_set(ctx, out_ffd,
+ "match", "test",
+ "index", "$myindex",
+ "type", "def",
+ "generate_id", "true",
+ NULL);
+
+ /* Enable test mode */
+ ret = flb_output_set_test(ctx, out_ffd, "formatter",
+ cb_check_index_record_accessor_generate_id,
+ NULL, NULL);
+
+ /* Start */
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data sample */
+ len = strlen(record);
+ flb_lib_push(ctx, in_ffd, record, len);
+
+ sleep(2);
+ flb_stop(ctx);
+ flb_destroy(ctx);
+}
+
+void flb_test_logstash_format()
+{
+ int ret;
+ int size = sizeof(JSON_ES) - 1;
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+
+ /* Create context, flush every second (some checks omitted here) */
+ ctx = flb_create();
+ flb_service_set(ctx, "flush", "1", "grace", "1", NULL);
+
+ /* Lib input mode */
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ flb_input_set(ctx, in_ffd, "tag", "test", NULL);
+
+ /* Elasticsearch output */
+ out_ffd = flb_output(ctx, (char *) "opensearch", NULL);
+ flb_output_set(ctx, out_ffd,
+ "match", "test",
+ NULL);
+
+ /* Override defaults of index and type */
+ flb_output_set(ctx, out_ffd,
+ "logstash_format", "on",
+ "logstash_prefix", "prefix",
+ "logstash_dateformat", "%Y-%m-%d",
+ NULL);
+
+ /* Enable test mode */
+ ret = flb_output_set_test(ctx, out_ffd, "formatter",
+ cb_check_logstash_format,
+ NULL, NULL);
+
+ /* Start */
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data sample */
+ flb_lib_push(ctx, in_ffd, (char *) JSON_ES, size);
+
+ sleep(2);
+ flb_stop(ctx);
+ flb_destroy(ctx);
+}
+
+void flb_test_logstash_format_nanos()
+{
+ int ret;
+ int size = sizeof(JSON_ES) - 1;
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+
+ /* Create context, flush every second (some checks omitted here) */
+ ctx = flb_create();
+ flb_service_set(ctx, "flush", "1", "grace", "1", NULL);
+
+ /* Lib input mode */
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ flb_input_set(ctx, in_ffd, "tag", "test", NULL);
+
+ /* Elasticsearch output */
+ out_ffd = flb_output(ctx, (char *) "opensearch", NULL);
+ flb_output_set(ctx, out_ffd,
+ "match", "test",
+ NULL);
+
+ /* Override defaults of index and type */
+ flb_output_set(ctx, out_ffd,
+ "logstash_format", "on",
+ "logstash_prefix", "prefix",
+ "logstash_dateformat", "%Y-%m-%d",
+ "time_key_nanos", "on",
+ NULL);
+
+ /* Enable test mode */
+ ret = flb_output_set_test(ctx, out_ffd, "formatter",
+ cb_check_logstash_format_nanos,
+ NULL, NULL);
+
+ /* Start */
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data sample */
+ flb_lib_push(ctx, in_ffd, (char *) JSON_ES, size);
+
+ sleep(2);
+ flb_stop(ctx);
+ flb_destroy(ctx);
+}
+
+void flb_test_tag_key()
+{
+ int ret;
+ int size = sizeof(JSON_ES) - 1;
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+
+ /* Create context, flush every second (some checks omitted here) */
+ ctx = flb_create();
+ flb_service_set(ctx, "flush", "1", "grace", "1", NULL);
+
+ /* Lib input mode */
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ flb_input_set(ctx, in_ffd, "tag", "test", NULL);
+
+ /* Elasticsearch output */
+ out_ffd = flb_output(ctx, (char *) "opensearch", NULL);
+ flb_output_set(ctx, out_ffd,
+ "match", "test",
+ NULL);
+
+ /* Override defaults of index and type */
+ flb_output_set(ctx, out_ffd,
+ "include_tag_key", "on",
+ "tag_key", "mytag",
+ NULL);
+
+ /* Enable test mode */
+ ret = flb_output_set_test(ctx, out_ffd, "formatter",
+ cb_check_tag_key,
+ NULL, NULL);
+
+ /* Start */
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data sample */
+ flb_lib_push(ctx, in_ffd, (char *) JSON_ES, size);
+
+ sleep(2);
+ flb_stop(ctx);
+ flb_destroy(ctx);
+}
+
+void flb_test_replace_dots()
+{
+ int ret;
+ int size = sizeof(JSON_DOTS) - 1;
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+
+ /* Create context, flush every second (some checks omitted here) */
+ ctx = flb_create();
+ flb_service_set(ctx, "flush", "1", "grace", "1", NULL);
+
+ /* Lib input mode */
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ flb_input_set(ctx, in_ffd, "tag", "test", NULL);
+
+ /* Elasticsearch output */
+ out_ffd = flb_output(ctx, (char *) "opensearch", NULL);
+ flb_output_set(ctx, out_ffd,
+ "match", "test",
+ NULL);
+
+ /* Override defaults of index and type */
+ flb_output_set(ctx, out_ffd,
+ "replace_dots", "on",
+ NULL);
+
+ /* Enable test mode */
+ ret = flb_output_set_test(ctx, out_ffd, "formatter",
+ cb_check_replace_dots,
+ NULL, NULL);
+
+ /* Start */
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data sample */
+ flb_lib_push(ctx, in_ffd, (char *) JSON_DOTS, size);
+
+ sleep(2);
+ flb_stop(ctx);
+ flb_destroy(ctx);
+}
+
+void flb_test_id_key()
+{
+ int ret;
+ int size = sizeof(JSON_ES) - 1;
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+
+ /* Create context, flush every second (some checks omitted here) */
+ ctx = flb_create();
+ flb_service_set(ctx, "flush", "1", "grace", "1", NULL);
+
+ /* Lib input mode */
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ flb_input_set(ctx, in_ffd, "tag", "test", NULL);
+
+ /* Elasticsearch output */
+ out_ffd = flb_output(ctx, (char *) "opensearch", NULL);
+ flb_output_set(ctx, out_ffd,
+ "match", "test",
+ NULL);
+
+ /* Override defaults of index and type */
+ flb_output_set(ctx, out_ffd,
+ "id_key", "key_2",
+ NULL);
+
+ /* Enable test mode */
+ ret = flb_output_set_test(ctx, out_ffd, "formatter",
+ cb_check_id_key,
+ NULL, NULL);
+
+ /* Start */
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data sample */
+ flb_lib_push(ctx, in_ffd, (char *) JSON_ES, size);
+
+ sleep(2);
+ flb_stop(ctx);
+ flb_destroy(ctx);
+}
+
+/* no check */
+static void cb_check_nothing(void *ctx, int ffd,
+ int res_ret, void *res_data, size_t res_size,
+ void *data)
+{
+ flb_sds_destroy(res_data);
+}
+
+/* https://github.com/fluent/fluent-bit/issues/3905 */
+void flb_test_div0()
+{
+ int ret;
+ char record[8000];
+ char record_header[] = "[1448403340,{\"key\":\"";
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+ int i;
+
+ /* Create context, flush every second (some checks omitted here) */
+ ctx = flb_create();
+ flb_service_set(ctx, "flush", "1", "grace", "1", NULL);
+
+ /* Lib input mode */
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ flb_input_set(ctx, in_ffd, "tag", "test", NULL);
+
+ /* Elasticsearch output */
+ out_ffd = flb_output(ctx, (char *) "opensearch", NULL);
+ flb_output_set(ctx, out_ffd,
+ "match", "test",
+ NULL);
+
+ /* Override defaults of index and type */
+ flb_output_set(ctx, out_ffd,
+ NULL);
+
+ /* Enable test mode */
+ ret = flb_output_set_test(ctx, out_ffd, "formatter",
+ cb_check_nothing,
+ NULL, NULL);
+
+ /* Start */
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ /* create json */
+ strncpy(&record[0], &record_header[0], strlen(record_header));
+ for(i=strlen(record_header); i<sizeof(record)-4; i++) {
+ record[i] = 'a';
+ }
+ record[sizeof(record)-4] = '"';
+ record[sizeof(record)-3] = '}';
+ record[sizeof(record)-2] = ']';
+ record[sizeof(record)-1] = '\0';
+ /* Ingest data sample */
+ flb_lib_push(ctx, in_ffd, (char *) &record[0], strlen(record));
+
+ sleep(2);
+ flb_stop(ctx);
+ flb_destroy(ctx);
+}
+
+
+static void cb_check_long_index(void *ctx, int ffd,
+ int res_ret, void *res_data, size_t res_size,
+ void *data)
+{
+ char *p;
+ char *out_js = res_data;
+ char long_index[256] = {0};
+ int i;
+
+ for (i=0; i<sizeof(long_index)-1; i++) {
+ long_index[i] = '0' + (i%10);
+ }
+
+ p = strstr(out_js, &long_index[0]);
+ TEST_CHECK(p != NULL);
+ flb_sds_destroy(res_data);
+}
+
+/* https://github.com/fluent/fluent-bit/issues/4311 */
+void flb_test_long_index()
+{
+ int ret;
+ int size = sizeof(JSON_ES) -1;
+ char long_index[256] = {0};
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+ int i;
+
+ for (i=0; i<sizeof(long_index)-1; i++) {
+ long_index[i] = '0' + (i%10);
+ }
+
+ /* Create context, flush every second (some checks omitted here) */
+ ctx = flb_create();
+ flb_service_set(ctx, "flush", "1", "grace", "1", NULL);
+
+ /* Lib input mode */
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ flb_input_set(ctx, in_ffd, "tag", "test", NULL);
+
+ /* Elasticsearch output */
+ out_ffd = flb_output(ctx, (char *) "opensearch", NULL);
+ flb_output_set(ctx, out_ffd,
+ "match", "test",
+ "generate_id", "true",
+ "index", &long_index[0],
+ NULL);
+
+ /* Override defaults of index and type */
+ flb_output_set(ctx, out_ffd,
+ NULL);
+
+ /* Enable test mode */
+ ret = flb_output_set_test(ctx, out_ffd, "formatter",
+ cb_check_long_index,
+ NULL, NULL);
+
+ /* Start */
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data sample */
+ flb_lib_push(ctx, in_ffd, (char *)JSON_ES, size);
+
+ sleep(2);
+ flb_stop(ctx);
+ flb_destroy(ctx);
+}
+
+void flb_test_logstash_prefix_separator()
+{
+ int ret;
+ int size = sizeof(JSON_ES) - 1;
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+
+ /* Create context, flush every second (some checks omitted here) */
+ ctx = flb_create();
+ flb_service_set(ctx, "flush", "1", "grace", "1", NULL);
+
+ /* Lib input mode */
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ flb_input_set(ctx, in_ffd, "tag", "test", NULL);
+
+ /* Elasticsearch output */
+ out_ffd = flb_output(ctx, (char *) "opensearch", NULL);
+ flb_output_set(ctx, out_ffd,
+ "match", "test",
+ NULL);
+
+ /* Override defaults of index and type */
+ flb_output_set(ctx, out_ffd,
+ "logstash_format", "on",
+ "logstash_prefix", "prefix",
+ "logstash_prefix_separator", "SEP",
+ "logstash_dateformat", "%Y-%m-%d",
+ NULL);
+
+ /* Enable test mode */
+ ret = flb_output_set_test(ctx, out_ffd, "formatter",
+ cb_check_logstash_prefix_separator,
+ NULL, NULL);
+
+ /* Start */
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data sample */
+ flb_lib_push(ctx, in_ffd, (char *) JSON_ES, size);
+
+ sleep(2);
+ flb_stop(ctx);
+ flb_destroy(ctx);
+}
+
+/* Test list */
+TEST_LIST = {
+ {"long_index" , flb_test_long_index },
+ {"div0_error" , flb_test_div0 },
+ {"write_operation_index" , flb_test_write_operation_index },
+ {"write_operation_create", flb_test_write_operation_create },
+ {"write_operation_update", flb_test_write_operation_update },
+ {"write_operation_upsert", flb_test_write_operation_upsert },
+ {"index_type" , flb_test_index_type },
+ {"index_record_accessor" , flb_test_index_record_accessor},
+ {"index_record_accessor_suppress_type" , flb_test_index_record_accessor_suppress_type},
+ {"index_record_accessor_id_key", flb_test_index_record_accessor_with_id_key},
+ {"index_record_accessor_generate_id", flb_test_index_record_accessor_with_generate_id},
+ {"logstash_format" , flb_test_logstash_format },
+ {"logstash_format_nanos" , flb_test_logstash_format_nanos },
+ {"tag_key" , flb_test_tag_key },
+ {"replace_dots" , flb_test_replace_dots },
+ {"id_key" , flb_test_id_key },
+ {"logstash_prefix_separator" , flb_test_logstash_prefix_separator },
+ {NULL, NULL}
+};
diff --git a/fluent-bit/tests/runtime/out_plot.c b/fluent-bit/tests/runtime/out_plot.c
new file mode 100644
index 00000000..67c841f2
--- /dev/null
+++ b/fluent-bit/tests/runtime/out_plot.c
@@ -0,0 +1,157 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+#include <fluent-bit.h>
+#include "flb_tests_runtime.h"
+
+/* Test data */
+#include "data/common/json_invalid.h" /* JSON_INVALID */
+
+/* Test functions */
+void flb_test_plot_json_invalid(void);
+void flb_test_plot_json_multiple(void);
+void flb_test_plot_key_mismatch(void);
+
+/* Test list */
+TEST_LIST = {
+ {"json_invalid", flb_test_plot_json_invalid },
+ {"json_multiple", flb_test_plot_json_multiple },
+ {"key_mismatch", flb_test_plot_key_mismatch },
+ {NULL, NULL}
+};
+
+#define TEST_LOGFILE "flb_test_plot_dummy.log"
+#define TEST_TIMEOUT 5
+
+void flb_test_plot_json_invalid(void)
+{
+ int i;
+ int ret;
+ int bytes;
+ char *p = (char *) JSON_INVALID;
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+
+ ctx = flb_create();
+
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ TEST_CHECK(in_ffd >= 0);
+ flb_input_set(ctx, in_ffd, "tag", "test", NULL);
+
+ out_ffd = flb_output(ctx, (char *) "plot", NULL);
+ TEST_CHECK(out_ffd >= 0);
+ flb_output_set(ctx, out_ffd, "match", "test", NULL);
+
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ for (i = 0; i < (int) sizeof(JSON_INVALID) - 1; i++) {
+ bytes = flb_lib_push(ctx, in_ffd, p + i, 1);
+ TEST_CHECK(bytes == 1);
+ }
+
+ flb_stop(ctx);
+ flb_destroy(ctx);
+}
+
+void flb_test_plot_json_multiple(void)
+{
+ int i;
+ int ret;
+ int bytes;
+ char p[100];
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+ FILE *fp;
+
+ remove(TEST_LOGFILE);
+
+ ctx = flb_create();
+
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ TEST_CHECK(in_ffd >= 0);
+ flb_input_set(ctx, in_ffd, "tag", "test", NULL);
+
+ out_ffd = flb_output(ctx, (char *) "plot", NULL);
+ TEST_CHECK(out_ffd >= 0);
+ flb_output_set(ctx, out_ffd, "match", "test", NULL);
+ flb_output_set(ctx, out_ffd, "key", "val", NULL);
+ flb_output_set(ctx, out_ffd, "file", TEST_LOGFILE, NULL);
+
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ for (i = 0; i < 256; i++) {
+ memset(p, '\0', sizeof(p));
+ snprintf(p, sizeof(p), "[%d, {\"val\": %d,\"END_KEY\": \"JSON_END\"}]", i, (i * i));
+ bytes = flb_lib_push(ctx, in_ffd, p, strlen(p));
+ TEST_CHECK(bytes == strlen(p));
+ }
+
+ ret = wait_for_file(TEST_LOGFILE, 1, TEST_TIMEOUT);
+ TEST_CHECK(ret == 0);
+
+ flb_stop(ctx);
+ flb_destroy(ctx);
+
+ fp = fopen(TEST_LOGFILE, "r");
+ TEST_CHECK(fp != NULL);
+ if (fp != NULL) {
+ fseek(fp, 0L ,SEEK_END);
+ TEST_CHECK(ftell(fp) > 0); /* file_size > 0 */
+ fclose(fp);
+ remove(TEST_LOGFILE);
+ }
+}
+
+void flb_test_plot_key_mismatch(void)
+{
+ int i;
+ int ret;
+ int bytes;
+ char p[100];
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+ FILE *fp;
+
+ remove(TEST_LOGFILE);
+
+ ctx = flb_create();
+
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ TEST_CHECK(in_ffd >= 0);
+ flb_input_set(ctx, in_ffd, "tag", "test", NULL);
+
+ out_ffd = flb_output(ctx, (char *) "plot", NULL);
+ TEST_CHECK(out_ffd >= 0);
+ flb_output_set(ctx, out_ffd, "match", "test", NULL);
+ flb_output_set(ctx, out_ffd, "key", "xxx", NULL);
+ flb_output_set(ctx, out_ffd, "file", TEST_LOGFILE, NULL);
+
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ for (i = 0; i < 256; i++) {
+ memset(p, '\0', sizeof(p));
+ snprintf(p, sizeof(p), "[%d, {\"val\": %d,\"END_KEY\": \"JSON_END\"}]", i, (i * i));
+ bytes = flb_lib_push(ctx, in_ffd, p, strlen(p));
+ TEST_CHECK(bytes == strlen(p));
+ }
+
+ ret = wait_for_file(TEST_LOGFILE, 1, TEST_TIMEOUT);
+ TEST_CHECK(ret == 0);
+
+ flb_stop(ctx);
+ flb_destroy(ctx);
+
+ fp = fopen(TEST_LOGFILE, "r");
+ TEST_CHECK(fp != NULL);
+ if (fp != NULL) {
+ fseek(fp, 0L ,SEEK_END);
+ TEST_CHECK(ftell(fp) == 0); /* file_size == 0 */
+ fclose(fp);
+ remove(TEST_LOGFILE);
+ }
+}
diff --git a/fluent-bit/tests/runtime/out_retry.c b/fluent-bit/tests/runtime/out_retry.c
new file mode 100644
index 00000000..16e4710d
--- /dev/null
+++ b/fluent-bit/tests/runtime/out_retry.c
@@ -0,0 +1,88 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+#include <fluent-bit.h>
+#include "flb_tests_runtime.h"
+
+/* Test data */
+#include "data/common/json_invalid.h" /* JSON_INVALID */
+
+/* Test functions */
+void flb_test_retry_json_invalid(void);
+void flb_test_retry_normal(void);
+
+/* Test list */
+TEST_LIST = {
+ {"json_invalid", flb_test_retry_json_invalid },
+ {"normal", flb_test_retry_normal },
+ {NULL, NULL}
+};
+
+
+void flb_test_retry_json_invalid(void)
+{
+ int i;
+ int ret;
+ int bytes;
+ char *p = (char *) JSON_INVALID;
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+
+ ctx = flb_create();
+
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ TEST_CHECK(in_ffd >= 0);
+ flb_input_set(ctx, in_ffd, "tag", "test", NULL);
+
+ out_ffd = flb_output(ctx, (char *) "retry", NULL);
+ TEST_CHECK(out_ffd >= 0);
+ flb_output_set(ctx, out_ffd, "match", "test", NULL);
+
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ for (i = 0; i < (int) sizeof(JSON_INVALID) - 1; i++) {
+ bytes = flb_lib_push(ctx, in_ffd, p + i, 1);
+ TEST_CHECK(bytes == 1);
+ }
+
+ flb_stop(ctx);
+ flb_destroy(ctx);
+}
+
+void flb_test_retry_normal(void)
+{
+ int i;
+ int ret;
+ int bytes;
+ char p[100];
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+
+ ctx = flb_create();
+
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ TEST_CHECK(in_ffd >= 0);
+ flb_input_set(ctx, in_ffd, "tag", "test", NULL);
+
+ out_ffd = flb_output(ctx, (char *) "retry", NULL);
+ TEST_CHECK(out_ffd >= 0);
+ flb_output_set(ctx, out_ffd, "match", "test", NULL);
+ flb_output_set(ctx, out_ffd, "retry", "10", NULL);
+
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ for (i = 0; i < 256; i++) {
+ memset(p, '\0', sizeof(p));
+ snprintf(p, sizeof(p), "[%d, {\"val\": %d,\"END_KEY\": \"JSON_END\"}]", i, (i * i));
+ bytes = flb_lib_push(ctx, in_ffd, p, strlen(p));
+ TEST_CHECK(bytes == strlen(p));
+ }
+
+ sleep(1); /* waiting flush */
+
+ flb_stop(ctx);
+ flb_destroy(ctx);
+}
diff --git a/fluent-bit/tests/runtime/out_s3.c b/fluent-bit/tests/runtime/out_s3.c
new file mode 100644
index 00000000..5968ff12
--- /dev/null
+++ b/fluent-bit/tests/runtime/out_s3.c
@@ -0,0 +1,241 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+#include <fluent-bit.h>
+#include "flb_tests_runtime.h"
+
+/* Test data */
+#include "data/td/json_td.h" /* JSON_TD */
+
+/* not a real error code, but tests that the code can respond to any error */
+#define ERROR_ACCESS_DENIED "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\
+ <Error>\
+ <Code>AccessDenied</Code>\
+ <Message>Access Denied</Message>\
+ <RequestId>656c76696e6727732072657175657374</RequestId>\
+ <HostId>Uuag1LuByRx9e6j5Onimru9pO4ZVKnJ2Qz7/C1NPcfTWAtRPfTaOFg==</HostId>\
+ </Error>"
+
+void flb_test_s3_multipart_success(void)
+{
+ int ret;
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+
+ /* mocks calls- signals that we are in test mode */
+ setenv("FLB_S3_PLUGIN_UNDER_TEST", "true", 1);
+
+ ctx = flb_create();
+
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ TEST_CHECK(in_ffd >= 0);
+ flb_input_set(ctx,in_ffd, "tag", "test", NULL);
+
+ out_ffd = flb_output(ctx, (char *) "s3", NULL);
+ TEST_CHECK(out_ffd >= 0);
+ flb_output_set(ctx, out_ffd,"match", "*", NULL);
+ flb_output_set(ctx, out_ffd,"region", "us-west-2", NULL);
+ flb_output_set(ctx, out_ffd,"bucket", "fluent", NULL);
+ flb_output_set(ctx, out_ffd,"Retry_Limit", "1", NULL);
+
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ flb_lib_push(ctx, in_ffd, (char *) JSON_TD , (int) sizeof(JSON_TD) - 1);
+
+
+ sleep(2);
+ flb_stop(ctx);
+ flb_destroy(ctx);
+}
+
+void flb_test_s3_putobject_success(void)
+{
+ int ret;
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+
+ /* mocks calls- signals that we are in test mode */
+ setenv("FLB_S3_PLUGIN_UNDER_TEST", "true", 1);
+
+ ctx = flb_create();
+
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ TEST_CHECK(in_ffd >= 0);
+ flb_input_set(ctx,in_ffd, "tag", "test", NULL);
+
+ out_ffd = flb_output(ctx, (char *) "s3", NULL);
+ TEST_CHECK(out_ffd >= 0);
+ flb_output_set(ctx, out_ffd,"match", "*", NULL);
+ flb_output_set(ctx, out_ffd,"region", "us-west-2", NULL);
+ flb_output_set(ctx, out_ffd,"bucket", "fluent", NULL);
+ flb_output_set(ctx, out_ffd,"use_put_object", "true", NULL);
+ flb_output_set(ctx, out_ffd,"total_file_size", "5M", NULL);
+ flb_output_set(ctx, out_ffd,"Retry_Limit", "1", NULL);
+
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ flb_lib_push(ctx, in_ffd, (char *) JSON_TD , (int) sizeof(JSON_TD) - 1);
+
+
+ sleep(2);
+ flb_stop(ctx);
+ flb_destroy(ctx);
+}
+
+void flb_test_s3_putobject_error(void)
+{
+ int ret;
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+
+ /* mocks calls- signals that we are in test mode */
+ setenv("FLB_S3_PLUGIN_UNDER_TEST", "true", 1);
+ setenv("TEST_PUT_OBJECT_ERROR", ERROR_ACCESS_DENIED, 1);
+
+ ctx = flb_create();
+
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ TEST_CHECK(in_ffd >= 0);
+ flb_input_set(ctx,in_ffd, "tag", "test", NULL);
+
+ out_ffd = flb_output(ctx, (char *) "s3", NULL);
+ TEST_CHECK(out_ffd >= 0);
+ flb_output_set(ctx, out_ffd,"match", "*", NULL);
+ flb_output_set(ctx, out_ffd,"region", "us-west-2", NULL);
+ flb_output_set(ctx, out_ffd,"bucket", "fluent", NULL);
+ flb_output_set(ctx, out_ffd,"use_put_object", "true", NULL);
+ flb_output_set(ctx, out_ffd,"total_file_size", "5M", NULL);
+ flb_output_set(ctx, out_ffd,"Retry_Limit", "1", NULL);
+
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ flb_lib_push(ctx, in_ffd, (char *) JSON_TD , (int) sizeof(JSON_TD) - 1);
+
+
+ sleep(2);
+ flb_stop(ctx);
+ flb_destroy(ctx);
+ unsetenv("TEST_PUT_OBJECT_ERROR");
+
+}
+
+void flb_test_s3_create_upload_error(void)
+{
+ int ret;
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+
+ /* mocks calls- signals that we are in test mode */
+ setenv("FLB_S3_PLUGIN_UNDER_TEST", "true", 1);
+ setenv("TEST_CREATE_MULTIPART_UPLOAD_ERROR", ERROR_ACCESS_DENIED, 1);
+
+ ctx = flb_create();
+
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ TEST_CHECK(in_ffd >= 0);
+ flb_input_set(ctx,in_ffd, "tag", "test", NULL);
+
+ out_ffd = flb_output(ctx, (char *) "s3", NULL);
+ TEST_CHECK(out_ffd >= 0);
+ flb_output_set(ctx, out_ffd,"match", "*", NULL);
+ flb_output_set(ctx, out_ffd,"region", "us-west-2", NULL);
+ flb_output_set(ctx, out_ffd,"bucket", "fluent", NULL);
+ flb_output_set(ctx, out_ffd,"Retry_Limit", "1", NULL);
+
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ flb_lib_push(ctx, in_ffd, (char *) JSON_TD , (int) sizeof(JSON_TD) - 1);
+
+ sleep(2);
+ flb_stop(ctx);
+ flb_destroy(ctx);
+ unsetenv("TEST_CREATE_MULTIPART_UPLOAD_ERROR");
+}
+
+void flb_test_s3_upload_part_error(void)
+{
+ int ret;
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+
+ /* mocks calls- signals that we are in test mode */
+ setenv("FLB_S3_PLUGIN_UNDER_TEST", "true", 1);
+ setenv("TEST_UPLOAD_PART_ERROR", ERROR_ACCESS_DENIED, 1);
+
+ ctx = flb_create();
+
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ TEST_CHECK(in_ffd >= 0);
+ flb_input_set(ctx,in_ffd, "tag", "test", NULL);
+
+ out_ffd = flb_output(ctx, (char *) "s3", NULL);
+ TEST_CHECK(out_ffd >= 0);
+ flb_output_set(ctx, out_ffd,"match", "*", NULL);
+ flb_output_set(ctx, out_ffd,"region", "us-west-2", NULL);
+ flb_output_set(ctx, out_ffd,"bucket", "fluent", NULL);
+ flb_output_set(ctx, out_ffd,"Retry_Limit", "1", NULL);
+
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ flb_lib_push(ctx, in_ffd, (char *) JSON_TD , (int) sizeof(JSON_TD) - 1);
+
+ sleep(2);
+ flb_stop(ctx);
+ flb_destroy(ctx);
+ unsetenv("TEST_UPLOAD_PART_ERROR");
+}
+
+void flb_test_s3_complete_upload_error(void)
+{
+ int ret;
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+
+ /* mocks calls- signals that we are in test mode */
+ setenv("FLB_S3_PLUGIN_UNDER_TEST", "true", 1);
+ setenv("TEST_COMPLETE_MULTIPART_UPLOAD_ERROR", ERROR_ACCESS_DENIED, 1);
+
+ ctx = flb_create();
+
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ TEST_CHECK(in_ffd >= 0);
+ flb_input_set(ctx,in_ffd, "tag", "test", NULL);
+
+ out_ffd = flb_output(ctx, (char *) "s3", NULL);
+ TEST_CHECK(out_ffd >= 0);
+ flb_output_set(ctx, out_ffd,"match", "*", NULL);
+ flb_output_set(ctx, out_ffd,"region", "us-west-2", NULL);
+ flb_output_set(ctx, out_ffd,"bucket", "fluent", NULL);
+ flb_output_set(ctx, out_ffd,"Retry_Limit", "1", NULL);
+
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ flb_lib_push(ctx, in_ffd, (char *) JSON_TD , (int) sizeof(JSON_TD) - 1);
+
+ sleep(2);
+ flb_stop(ctx);
+ flb_destroy(ctx);
+ unsetenv("TEST_COMPLETE_MULTIPART_UPLOAD_ERROR");
+}
+
+
+/* Test list */
+TEST_LIST = {
+ {"multipart_success", flb_test_s3_multipart_success },
+ {"putobject_success", flb_test_s3_putobject_success },
+ {"putobject_error", flb_test_s3_putobject_error },
+ {"create_upload_error", flb_test_s3_create_upload_error },
+ {"upload_part_error", flb_test_s3_upload_part_error },
+ {"complete_upload_error", flb_test_s3_complete_upload_error },
+ {NULL, NULL}
+};
diff --git a/fluent-bit/tests/runtime/out_skywalking.c b/fluent-bit/tests/runtime/out_skywalking.c
new file mode 100644
index 00000000..b59d7086
--- /dev/null
+++ b/fluent-bit/tests/runtime/out_skywalking.c
@@ -0,0 +1,55 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/* Fluent Bit
+ * ==========
+ * Copyright (C) 2019-2020 The Fluent Bit Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <fluent-bit.h>
+
+#include "flb_tests_runtime.h"
+#include "data/td/json_td.h"
+
+void flb_test_sw_success(void) {
+ int ret;
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+
+ setenv("FLB_SW_PLUGIN_UNDER_TEST", "true", 1);
+
+ ctx = flb_create();
+
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ TEST_CHECK(in_ffd >= 0);
+ flb_input_set(ctx,in_ffd, "tag", "test", NULL);
+
+ out_ffd = flb_output(ctx, (char *) "skywalking", NULL);
+ TEST_CHECK(out_ffd >= 0);
+
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ flb_lib_push(ctx, in_ffd, (char *) JSON_TD , (int) sizeof(JSON_TD) - 1);
+
+ sleep(2);
+ flb_stop(ctx);
+ flb_destroy(ctx);
+}
+
+TEST_LIST = {
+ {"sw_success", flb_test_sw_success},
+ {NULL, NULL}
+};
diff --git a/fluent-bit/tests/runtime/out_splunk.c b/fluent-bit/tests/runtime/out_splunk.c
new file mode 100644
index 00000000..407c26b8
--- /dev/null
+++ b/fluent-bit/tests/runtime/out_splunk.c
@@ -0,0 +1,150 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/* Fluent Bit
+ * ==========
+ * Copyright (C) 2019-2021 The Fluent Bit Authors
+ * Copyright (C) 2015-2018 Treasure Data Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <fluent-bit.h>
+#include <fluent-bit/flb_sds.h>
+#include "flb_tests_runtime.h"
+
+#define JSON_BASIC "[12345678, {\"key\":\"value\"}]"
+static void cb_check_basic(void *ctx, int ffd,
+ int res_ret, void *res_data, size_t res_size,
+ void *data)
+{
+ char *p;
+ flb_sds_t out_js = res_data;
+ char *index_line = "\"event\":{\"key\":\"value\"}";
+
+ p = strstr(out_js, index_line);
+ if (!TEST_CHECK(p != NULL)) {
+ TEST_MSG("Given:%s", out_js);
+ }
+
+ flb_sds_destroy(out_js);
+}
+
+static void cb_check_send_raw(void *ctx, int ffd,
+ int res_ret, void *res_data, size_t res_size,
+ void *data)
+{
+ char *p;
+ flb_sds_t out_js = res_data;
+ char *not_match_line = "\"event\":{\"key\":\"value\"}";
+ char *match_line = "\"key\":\"value\"";
+
+ p = strstr(out_js, not_match_line);
+ if (!TEST_CHECK(p == NULL)) {
+ TEST_MSG("Given:%s", out_js);
+ }
+ p = strstr(out_js, match_line);
+ if (!TEST_CHECK(p != NULL)) {
+ TEST_MSG("Given:%s", out_js);
+ }
+
+ flb_sds_destroy(out_js);
+}
+
+// Test "Splunk_Send_Raw" property.
+void flb_test_send_raw()
+{
+ int ret;
+ int size = sizeof(JSON_BASIC) - 1;
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+
+ /* Create context, flush every second (some checks omitted here) */
+ ctx = flb_create();
+ flb_service_set(ctx, "flush", "1", "grace", "1", NULL);
+
+ /* Lib input mode */
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ flb_input_set(ctx, in_ffd, "tag", "test", NULL);
+
+ /* Elasticsearch output */
+ out_ffd = flb_output(ctx, (char *) "splunk", NULL);
+ flb_output_set(ctx, out_ffd,
+ "match", "test",
+ "http_user", "alice",
+ "splunk_send_raw", "true",
+ NULL);
+
+ /* Enable test mode */
+ ret = flb_output_set_test(ctx, out_ffd, "formatter",
+ cb_check_send_raw,
+ NULL, NULL);
+
+ /* Start */
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data sample */
+ flb_lib_push(ctx, in_ffd, (char *) JSON_BASIC, size);
+
+ sleep(2);
+ flb_stop(ctx);
+ flb_destroy(ctx);
+}
+
+void flb_test_basic()
+{
+ int ret;
+ int size = sizeof(JSON_BASIC) - 1;
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+
+ /* Create context, flush every second (some checks omitted here) */
+ ctx = flb_create();
+ flb_service_set(ctx, "flush", "1", "grace", "1", NULL);
+
+ /* Lib input mode */
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ flb_input_set(ctx, in_ffd, "tag", "test", NULL);
+
+ /* Elasticsearch output */
+ out_ffd = flb_output(ctx, (char *) "splunk", NULL);
+ flb_output_set(ctx, out_ffd,
+ "match", "test",
+ "http_user", "alice",
+ NULL);
+
+ /* Enable test mode */
+ ret = flb_output_set_test(ctx, out_ffd, "formatter",
+ cb_check_basic,
+ NULL, NULL);
+
+ /* Start */
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data sample */
+ flb_lib_push(ctx, in_ffd, (char *) JSON_BASIC, size);
+
+ sleep(2);
+ flb_stop(ctx);
+ flb_destroy(ctx);
+}
+
+/* Test list */
+TEST_LIST = {
+ {"basic" , flb_test_basic },
+ {"send_raw" , flb_test_send_raw},
+ {NULL, NULL}
+};
diff --git a/fluent-bit/tests/runtime/out_stackdriver.c b/fluent-bit/tests/runtime/out_stackdriver.c
new file mode 100644
index 00000000..f379aa2e
--- /dev/null
+++ b/fluent-bit/tests/runtime/out_stackdriver.c
@@ -0,0 +1,6252 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/* Fluent Bit
+ * ==========
+ * Copyright (C) 2019-2020 The Fluent Bit Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <fluent-bit.h>
+#include <fluent-bit/flb_pack.h>
+#include <fluent-bit/flb_record_accessor.h>
+#include <fluent-bit/flb_ra_key.h>
+
+#include "flb_tests_runtime.h"
+
+/* Local 'test' credentials file */
+#define SERVICE_CREDENTIALS \
+ FLB_TESTS_DATA_PATH "/data/stackdriver/stackdriver-credentials.json"
+#define STACKDRIVER_DATA_PATH "/data/stackdriver"
+
+/* JSON payload example */
+#include "data/stackdriver/json.h"
+#include "data/stackdriver/stackdriver_test_operation.h"
+#include "data/stackdriver/stackdriver_test_k8s_resource.h"
+#include "data/stackdriver/stackdriver_test_labels.h"
+#include "data/stackdriver/stackdriver_test_trace.h"
+#include "data/stackdriver/stackdriver_test_span_id.h"
+#include "data/stackdriver/stackdriver_test_trace_sampled.h"
+#include "data/stackdriver/stackdriver_test_log_name.h"
+#include "data/stackdriver/stackdriver_test_resource_labels.h"
+#include "data/stackdriver/stackdriver_test_insert_id.h"
+#include "data/stackdriver/stackdriver_test_source_location.h"
+#include "data/stackdriver/stackdriver_test_http_request.h"
+#include "data/stackdriver/stackdriver_test_timestamp.h"
+#include "data/stackdriver/stackdriver_test_monitored_resource.h"
+
+
+/*
+ * Fluent Bit Stackdriver plugin, always set as payload a JSON strings contained in a
+ * 'sds'. Since we want to validate specific keys and it values we expose here some
+ * helper functions to make testing easier.
+ *
+ * The approach is:
+ *
+ * - Convert formatter JSON to msgpack
+ * - use the helper function to check keys and values
+ *
+ * it returns FLB_TRUE if expected 'key/val' matches or FLB_FALSE if 'key' no exists
+ * or if there is a mismatch.
+ */
+static int mp_kv_cmp(char *json_data, size_t json_len, char *key_accessor, char *val)
+{
+ int ret;
+ int type;
+ char *mp_buf = NULL;
+ size_t mp_size;
+ size_t off = 0;
+ msgpack_object map;
+ msgpack_unpacked result;
+ struct flb_ra_value *rval = NULL;
+ struct flb_record_accessor *ra = NULL;
+
+ /* Convert JSON to msgpack */
+ ret = flb_pack_json((const char *) json_data, json_len, &mp_buf, &mp_size,
+ &type, NULL);
+ TEST_CHECK(ret != -1);
+
+ /* Set return status */
+ ret = FLB_FALSE;
+
+ /* Unpack msgpack and reference the main 'map' */
+ msgpack_unpacked_init(&result);
+ ret = msgpack_unpack_next(&result, mp_buf, mp_size, &off);
+ TEST_CHECK(ret == MSGPACK_UNPACK_SUCCESS);
+ map = result.data;
+
+ /* Create a record_accessor context */
+ ra = flb_ra_create(key_accessor, FLB_TRUE);
+ if (!ra) {
+ flb_error("invalid record accessor key, aborting test");
+ goto out;
+ }
+
+ rval = flb_ra_get_value_object(ra, map);
+ TEST_CHECK(rval != NULL);
+ msgpack_unpacked_destroy(&result);
+ if (!rval) {
+ goto out;
+ }
+
+ /* We only validate strings, feel free to expand it as needed */
+ TEST_CHECK(rval->type == FLB_RA_STRING);
+ if (strcmp(rval->val.string, val) == 0) {
+ ret = FLB_TRUE;
+ }
+
+ out:
+ if (rval) {
+ flb_ra_key_value_destroy(rval);
+ }
+ if (ra) {
+ flb_ra_destroy(ra);
+ }
+ if (mp_buf) {
+ flb_free(mp_buf);
+ }
+ return ret;
+}
+
+static int mp_kv_cmp_integer(char *json_data, size_t json_len, char *key_accessor, int64_t val)
+{
+ int ret;
+ int type;
+ char *mp_buf = NULL;
+ size_t mp_size;
+ size_t off = 0;
+ msgpack_object map;
+ msgpack_unpacked result;
+ struct flb_ra_value *rval = NULL;
+ struct flb_record_accessor *ra = NULL;
+
+ /* Convert JSON to msgpack */
+ ret = flb_pack_json((const char *) json_data, json_len, &mp_buf, &mp_size,
+ &type, NULL);
+ TEST_CHECK(ret != -1);
+
+ /* Set return status */
+ ret = FLB_FALSE;
+
+ /* Unpack msgpack and reference the main 'map' */
+ msgpack_unpacked_init(&result);
+ ret = msgpack_unpack_next(&result, mp_buf, mp_size, &off);
+ TEST_CHECK(ret == MSGPACK_UNPACK_SUCCESS);
+ map = result.data;
+
+ /* Create a record_accessor context */
+ ra = flb_ra_create(key_accessor, FLB_TRUE);
+ if (!ra) {
+ flb_error("invalid record accessor key, aborting test");
+ goto out;
+ }
+
+ rval = flb_ra_get_value_object(ra, map);
+ TEST_CHECK(rval != NULL);
+ msgpack_unpacked_destroy(&result);
+ if (!rval) {
+ goto out;
+ }
+
+ TEST_CHECK(rval->type == FLB_RA_INT);
+ if (rval->val.i64 == val) {
+ ret = FLB_TRUE;
+ }
+ else {
+ ret = FLB_FALSE;
+ }
+
+ out:
+ if (rval) {
+ flb_ra_key_value_destroy(rval);
+ }
+ if (ra) {
+ flb_ra_destroy(ra);
+ }
+ if (mp_buf) {
+ flb_free(mp_buf);
+ }
+ return ret;
+}
+
+static int mp_kv_cmp_boolean(char *json_data, size_t json_len, char *key_accessor, bool val)
+{
+ int ret;
+ int type;
+ char *mp_buf = NULL;
+ size_t mp_size;
+ size_t off = 0;
+ msgpack_object map;
+ msgpack_unpacked result;
+ struct flb_ra_value *rval = NULL;
+ struct flb_record_accessor *ra = NULL;
+
+ /* Convert JSON to msgpack */
+ ret = flb_pack_json((const char *) json_data, json_len, &mp_buf, &mp_size,
+ &type, NULL);
+ TEST_CHECK(ret != -1);
+
+ /* Set return status */
+ ret = FLB_FALSE;
+
+ /* Unpack msgpack and reference the main 'map' */
+ msgpack_unpacked_init(&result);
+ ret = msgpack_unpack_next(&result, mp_buf, mp_size, &off);
+ TEST_CHECK(ret == MSGPACK_UNPACK_SUCCESS);
+ map = result.data;
+
+ /* Create a record_accessor context */
+ ra = flb_ra_create(key_accessor, FLB_TRUE);
+ if (!ra) {
+ flb_error("invalid record accessor key, aborting test");
+ goto out;
+ }
+
+ rval = flb_ra_get_value_object(ra, map);
+ TEST_CHECK(rval != NULL);
+ msgpack_unpacked_destroy(&result);
+ if (!rval) {
+ goto out;
+ }
+
+ TEST_CHECK(rval->type == FLB_RA_BOOL);
+ if (rval->val.boolean == val) {
+ ret = FLB_TRUE;
+ }
+ else {
+ ret = FLB_FALSE;
+ }
+
+ out:
+ if (rval) {
+ flb_ra_key_value_destroy(rval);
+ }
+ if (ra) {
+ flb_ra_destroy(ra);
+ }
+ if (mp_buf) {
+ flb_free(mp_buf);
+ }
+ return ret;
+}
+
+static int mp_kv_exists(char *json_data, size_t json_len, char *key_accessor)
+{
+ int ret;
+ int type;
+ char *mp_buf = NULL;
+ size_t mp_size;
+ size_t off = 0;
+ msgpack_object map;
+ msgpack_unpacked result;
+ struct flb_ra_value *rval = NULL;
+ struct flb_record_accessor *ra = NULL;
+
+ /* Convert JSON to msgpack */
+ ret = flb_pack_json((const char *) json_data, json_len, &mp_buf, &mp_size,
+ &type, NULL);
+ TEST_CHECK(ret != -1);
+
+ /* Set return status */
+ ret = FLB_FALSE;
+
+ /* Unpack msgpack and reference the main 'map' */
+ msgpack_unpacked_init(&result);
+ ret = msgpack_unpack_next(&result, mp_buf, mp_size, &off);
+ TEST_CHECK(ret == MSGPACK_UNPACK_SUCCESS);
+ map = result.data;
+
+ /* Create a record_accessor context */
+ ra = flb_ra_create(key_accessor, FLB_TRUE);
+ if (!ra) {
+ flb_error("invalid record accessor key, aborting test");
+ goto out;
+ }
+
+ rval = flb_ra_get_value_object(ra, map);
+ msgpack_unpacked_destroy(&result);
+ if (rval) {
+ ret = FLB_TRUE;
+ }
+ else {
+ ret = FLB_FALSE;
+ }
+
+ out:
+ if (rval) {
+ flb_ra_key_value_destroy(rval);
+ }
+ if (ra) {
+ flb_ra_destroy(ra);
+ }
+ if (mp_buf) {
+ flb_free(mp_buf);
+ }
+ return ret;
+}
+
+static void cb_check_monitored_resource(void *ctx, int ffd,
+ int res_ret, void *res_data, size_t res_size,
+ void *data)
+{
+ int ret;
+
+ ret = mp_kv_cmp(res_data, res_size, "$resource['type']", "monitored_resource");
+ TEST_CHECK(ret == FLB_TRUE);
+
+ ret = mp_kv_cmp(res_data, res_size,
+ "$resource['labels']['project_id']", "monitored_resource_project_id");
+ TEST_CHECK(ret == FLB_TRUE);
+
+ ret = mp_kv_cmp(res_data, res_size,
+ "$resource['labels']['location']", "monitored_resource_location");
+ TEST_CHECK(ret == FLB_TRUE);
+
+ ret = mp_kv_cmp(res_data, res_size,
+ "$resource['labels']['testA']", "valA");
+ TEST_CHECK(ret == FLB_TRUE);
+
+ ret = mp_kv_exists(res_data, res_size,
+ "$entries[0]['jsonPayload']['logging.googleapis.com/monitored_resource']");
+ TEST_CHECK(ret == FLB_FALSE);
+
+ flb_sds_destroy(res_data);
+}
+
+static void cb_check_monitored_resource_priority_higher_than_local_resource_id(void *ctx, int ffd,
+ int res_ret, void *res_data, size_t res_size,
+ void *data)
+{
+ int ret;
+
+ ret = mp_kv_cmp(res_data, res_size, "$resource['type']", "k8s_container");
+ TEST_CHECK(ret == FLB_TRUE);
+
+ ret = mp_kv_cmp(res_data, res_size,
+ "$resource['labels']['project_id']", "monitored_resource_project_id");
+ TEST_CHECK(ret == FLB_TRUE);
+
+ ret = mp_kv_cmp(res_data, res_size,
+ "$resource['labels']['location']", "monitored_resource_location");
+ TEST_CHECK(ret == FLB_TRUE);
+
+ ret = mp_kv_cmp(res_data, res_size,
+ "$resource['labels']['cluster_name']", "monitored_resource_cluster_name");
+ TEST_CHECK(ret == FLB_TRUE);
+
+ ret = mp_kv_cmp(res_data, res_size,
+ "$resource['labels']['namespace_name']", "monitored_resource_namespace_name");
+ TEST_CHECK(ret == FLB_TRUE);
+
+ ret = mp_kv_cmp(res_data, res_size,
+ "$resource['labels']['pod_name']", "monitored_resource_pod_name");
+ TEST_CHECK(ret == FLB_TRUE);
+
+ ret = mp_kv_cmp(res_data, res_size,
+ "$resource['labels']['container_name']", "monitored_resource_container_name");
+ TEST_CHECK(ret == FLB_TRUE);
+
+ ret = mp_kv_exists(res_data, res_size,
+ "$entries[0]['jsonPayload']['logging.googleapis.com/monitored_resource']");
+ TEST_CHECK(ret == FLB_FALSE);
+
+ flb_sds_destroy(res_data);
+}
+
+static void cb_check_monitored_resource_priority_higher_than_gce_instance(void *ctx, int ffd,
+ int res_ret, void *res_data, size_t res_size,
+ void *data)
+{
+ int ret;
+
+ ret = mp_kv_cmp(res_data, res_size, "$resource['type']", "gce_instance");
+ TEST_CHECK(ret == FLB_TRUE);
+
+ ret = mp_kv_cmp(res_data, res_size,
+ "$resource['labels']['project_id']", "monitored_resource_project_id");
+ TEST_CHECK(ret == FLB_TRUE);
+
+ ret = mp_kv_cmp(res_data, res_size,
+ "$resource['labels']['zone']", "monitored_resource_zone");
+ TEST_CHECK(ret == FLB_TRUE);
+
+ ret = mp_kv_cmp(res_data, res_size,
+ "$resource['labels']['instance_id']", "monitored_resource_instance_id");
+ TEST_CHECK(ret == FLB_TRUE);
+
+ ret = mp_kv_exists(res_data, res_size,
+ "$entries[0]['jsonPayload']['logging.googleapis.com/monitored_resource']");
+ TEST_CHECK(ret == FLB_FALSE);
+
+ flb_sds_destroy(res_data);
+}
+
+static void cb_check_global_resource(void *ctx, int ffd,
+ int res_ret, void *res_data, size_t res_size,
+ void *data)
+{
+ int ret;
+
+ ret = mp_kv_cmp(res_data, res_size, "$resource['type']", "global");
+ TEST_CHECK(ret == FLB_TRUE);
+
+ flb_sds_destroy(res_data);
+}
+
+static void cb_check_generic_node_creds(void *ctx, int ffd,
+ int res_ret, void *res_data, size_t res_size,
+ void *data)
+{
+ int ret;
+
+ /* resource type */
+ ret = mp_kv_cmp(res_data, res_size, "$resource['type']", "generic_node");
+ TEST_CHECK(ret == FLB_TRUE);
+
+ /* project id */
+ ret = mp_kv_cmp(res_data, res_size,
+ "$resource['labels']['project_id']", "fluent-bit");
+ TEST_CHECK(ret == FLB_TRUE);
+
+ /* location */
+ ret = mp_kv_cmp(res_data, res_size,
+ "$resource['labels']['location']", "fluent");
+ TEST_CHECK(ret == FLB_TRUE);
+
+ /* namespace */
+ ret = mp_kv_cmp(res_data, res_size,
+ "$resource['labels']['namespace']", "test");
+ TEST_CHECK(ret == FLB_TRUE);
+
+ /* node_id */
+ ret = mp_kv_cmp(res_data, res_size,
+ "$resource['labels']['node_id']", "333222111");
+ TEST_CHECK(ret == FLB_TRUE);
+
+ flb_sds_destroy(res_data);
+}
+
+static void cb_check_generic_node_metadata(void *ctx, int ffd,
+ int res_ret, void *res_data, size_t res_size,
+ void *data)
+{
+ int ret;
+
+ /* resource type */
+ ret = mp_kv_cmp(res_data, res_size, "$resource['type']", "generic_node");
+ TEST_CHECK(ret == FLB_TRUE);
+
+ /* project id */
+ ret = mp_kv_cmp(res_data, res_size,
+ "$resource['labels']['project_id']", "fluent-bit-test");
+ TEST_CHECK(ret == FLB_TRUE);
+
+ /* location */
+ ret = mp_kv_cmp(res_data, res_size,
+ "$resource['labels']['location']", "fluent");
+ TEST_CHECK(ret == FLB_TRUE);
+
+ /* namespace */
+ ret = mp_kv_cmp(res_data, res_size,
+ "$resource['labels']['namespace']", "test");
+ TEST_CHECK(ret == FLB_TRUE);
+
+ /* node_id */
+ ret = mp_kv_cmp(res_data, res_size,
+ "$resource['labels']['node_id']", "333222111");
+ TEST_CHECK(ret == FLB_TRUE);
+
+ flb_sds_destroy(res_data);
+}
+
+static void cb_check_generic_task_creds(void *ctx, int ffd,
+ int res_ret, void *res_data, size_t res_size,
+ void *data)
+{
+ int ret;
+
+ /* resource type */
+ ret = mp_kv_cmp(res_data, res_size, "$resource['type']", "generic_task");
+ TEST_CHECK(ret == FLB_TRUE);
+
+ /* project id */
+ ret = mp_kv_cmp(res_data, res_size,
+ "$resource['labels']['project_id']", "fluent-bit");
+ TEST_CHECK(ret == FLB_TRUE);
+
+ /* location */
+ ret = mp_kv_cmp(res_data, res_size,
+ "$resource['labels']['location']", "fluent");
+ TEST_CHECK(ret == FLB_TRUE);
+
+ /* namespace */
+ ret = mp_kv_cmp(res_data, res_size,
+ "$resource['labels']['namespace']", "test");
+ TEST_CHECK(ret == FLB_TRUE);
+
+ /* job */
+ ret = mp_kv_cmp(res_data, res_size,
+ "$resource['labels']['job']", "test-job");
+ TEST_CHECK(ret == FLB_TRUE);
+
+ /* task_id */
+ ret = mp_kv_cmp(res_data, res_size,
+ "$resource['labels']['task_id']", "333222111");
+ TEST_CHECK(ret == FLB_TRUE);
+
+ flb_sds_destroy(res_data);
+}
+
+static void cb_check_generic_task_metadata(void *ctx, int ffd,
+ int res_ret, void *res_data, size_t res_size,
+ void *data)
+{
+ int ret;
+
+ /* resource type */
+ ret = mp_kv_cmp(res_data, res_size, "$resource['type']", "generic_task");
+ TEST_CHECK(ret == FLB_TRUE);
+
+ /* project id */
+ ret = mp_kv_cmp(res_data, res_size,
+ "$resource['labels']['project_id']", "fluent-bit-test");
+ TEST_CHECK(ret == FLB_TRUE);
+
+ /* location */
+ ret = mp_kv_cmp(res_data, res_size,
+ "$resource['labels']['location']", "fluent");
+ TEST_CHECK(ret == FLB_TRUE);
+
+ /* namespace */
+ ret = mp_kv_cmp(res_data, res_size,
+ "$resource['labels']['namespace']", "test");
+ TEST_CHECK(ret == FLB_TRUE);
+
+ /* job */
+ ret = mp_kv_cmp(res_data, res_size,
+ "$resource['labels']['job']", "test-job");
+ TEST_CHECK(ret == FLB_TRUE);
+
+ /* task_id */
+ ret = mp_kv_cmp(res_data, res_size,
+ "$resource['labels']['task_id']", "333222111");
+ TEST_CHECK(ret == FLB_TRUE);
+
+ flb_sds_destroy(res_data);
+}
+
+static void cb_check_gce_instance(void *ctx, int ffd,
+ int res_ret, void *res_data, size_t res_size,
+ void *data)
+{
+ int ret;
+
+ /* resource type */
+ ret = mp_kv_cmp(res_data, res_size, "$resource['type']", "gce_instance");
+ TEST_CHECK(ret == FLB_TRUE);
+
+ /* project id */
+ ret = mp_kv_cmp(res_data, res_size,
+ "$resource['labels']['project_id']", "fluent-bit-test");
+ TEST_CHECK(ret == FLB_TRUE);
+
+ /* zone */
+ ret = mp_kv_cmp(res_data, res_size,
+ "$resource['labels']['zone']", "fluent");
+ TEST_CHECK(ret == FLB_TRUE);
+
+ /* instance_id */
+ ret = mp_kv_cmp(res_data, res_size,
+ "$resource['labels']['instance_id']", "333222111");
+ TEST_CHECK(ret == FLB_TRUE);
+
+ flb_sds_destroy(res_data);
+}
+
+static void cb_check_k8s_container_resource(void *ctx, int ffd,
+ int res_ret, void *res_data, size_t res_size,
+ void *data)
+{
+ int ret;
+
+ /* resource type */
+ ret = mp_kv_cmp(res_data, res_size, "$resource['type']", "k8s_container");
+ TEST_CHECK(ret == FLB_TRUE);
+
+ /* project id */
+ ret = mp_kv_cmp(res_data, res_size,
+ "$resource['labels']['project_id']", "fluent-bit");
+ TEST_CHECK(ret == FLB_TRUE);
+
+ /* location */
+ ret = mp_kv_cmp(res_data, res_size,
+ "$resource['labels']['location']", "test_cluster_location");
+ TEST_CHECK(ret == FLB_TRUE);
+
+ /* cluster name */
+ ret = mp_kv_cmp(res_data, res_size,
+ "$resource['labels']['cluster_name']", "test_cluster_name");
+ TEST_CHECK(ret == FLB_TRUE);
+
+ /* namespace name */
+ ret = mp_kv_cmp(res_data, res_size,
+ "$resource['labels']['namespace_name']", "testnamespace");
+ TEST_CHECK(ret == FLB_TRUE);
+
+ /* pod name */
+ ret = mp_kv_cmp(res_data, res_size,
+ "$resource['labels']['pod_name']", "testpod");
+ TEST_CHECK(ret == FLB_TRUE);
+
+ /* container name */
+ ret = mp_kv_cmp(res_data, res_size,
+ "$resource['labels']['container_name']", "testctr");
+ TEST_CHECK(ret == FLB_TRUE);
+
+ /* check `local_resource_id` has been removed from jsonPayload */
+ ret = mp_kv_exists(res_data, res_size,
+ "$entries[0]['jsonPayload']['logging.googleapis.com/local_resource_id']");
+ TEST_CHECK(ret == FLB_FALSE);
+
+ flb_sds_destroy(res_data);
+}
+
+static void cb_check_k8s_container_resource_diff_tag(void *ctx, int ffd,
+ int res_ret, void *res_data, size_t res_size,
+ void *data)
+{
+ int ret;
+
+ /* resource type */
+ ret = mp_kv_cmp(res_data, res_size, "$resource['type']", "k8s_container");
+ TEST_CHECK(ret == FLB_TRUE);
+
+ /* project id */
+ ret = mp_kv_cmp(res_data, res_size,
+ "$resource['labels']['project_id']", "fluent-bit");
+ TEST_CHECK(ret == FLB_TRUE);
+
+ /* location */
+ ret = mp_kv_cmp(res_data, res_size,
+ "$resource['labels']['location']", "test_cluster_location");
+ TEST_CHECK(ret == FLB_TRUE);
+
+ /* cluster name */
+ ret = mp_kv_cmp(res_data, res_size,
+ "$resource['labels']['cluster_name']", "test_cluster_name");
+ TEST_CHECK(ret == FLB_TRUE);
+
+ /* namespace name */
+ ret = mp_kv_cmp(res_data, res_size,
+ "$resource['labels']['namespace_name']", "diffnamespace");
+ TEST_CHECK(ret == FLB_TRUE);
+
+ /* pod name */
+ ret = mp_kv_cmp(res_data, res_size,
+ "$resource['labels']['pod_name']", "diffpod");
+ TEST_CHECK(ret == FLB_TRUE);
+
+ /* container name */
+ ret = mp_kv_cmp(res_data, res_size,
+ "$resource['labels']['container_name']", "diffctr");
+ TEST_CHECK(ret == FLB_TRUE);
+
+ /* check `local_resource_id` has been removed from jsonPayload */
+ ret = mp_kv_exists(res_data, res_size,
+ "$entries[0]['jsonPayload']['logging.googleapis.com/local_resource_id']");
+ TEST_CHECK(ret == FLB_FALSE);
+
+ flb_sds_destroy(res_data);
+}
+
+static void cb_check_k8s_container_resource_default_regex(void *ctx, int ffd,
+ int res_ret, void *res_data, size_t res_size,
+ void *data)
+{
+ int ret;
+
+ /* resource type */
+ ret = mp_kv_cmp(res_data, res_size, "$resource['type']", "k8s_container");
+ TEST_CHECK(ret == FLB_TRUE);
+
+ /* project id */
+ ret = mp_kv_cmp(res_data, res_size,
+ "$resource['labels']['project_id']", "fluent-bit");
+ TEST_CHECK(ret == FLB_TRUE);
+
+ /* location */
+ ret = mp_kv_cmp(res_data, res_size,
+ "$resource['labels']['location']", "test_cluster_location");
+ TEST_CHECK(ret == FLB_TRUE);
+
+ /* cluster name */
+ ret = mp_kv_cmp(res_data, res_size,
+ "$resource['labels']['cluster_name']", "test_cluster_name");
+ TEST_CHECK(ret == FLB_TRUE);
+
+ /* namespace name */
+ ret = mp_kv_cmp(res_data, res_size,
+ "$resource['labels']['namespace_name']", "default");
+ TEST_CHECK(ret == FLB_TRUE);
+
+ /* pod name */
+ ret = mp_kv_cmp(res_data, res_size,
+ "$resource['labels']['pod_name']", "apache-logs-annotated");
+ TEST_CHECK(ret == FLB_TRUE);
+
+ /* container name */
+ ret = mp_kv_cmp(res_data, res_size,
+ "$resource['labels']['container_name']", "apache");
+ TEST_CHECK(ret == FLB_TRUE);
+
+ /* check `local_resource_id` has been removed from jsonPayload */
+ ret = mp_kv_exists(res_data, res_size,
+ "$entries[0]['jsonPayload']['logging.googleapis.com/local_resource_id']");
+ TEST_CHECK(ret == FLB_FALSE);
+
+ flb_sds_destroy(res_data);
+}
+
+static void cb_check_trace_no_autoformat(void *ctx, int ffd,
+ int res_ret, void *res_data, size_t res_size,
+ void *data)
+{
+ int ret;
+
+ /* trace in the entries */
+ ret = mp_kv_cmp(res_data, res_size, "$entries[0]['trace']", "test-trace-id-xyz");
+ TEST_CHECK(ret == FLB_TRUE);
+
+ /* trace has been removed from jsonPayload */
+ ret = mp_kv_exists(res_data, res_size, "$entries[0]['jsonPayload']['trace']");
+ TEST_CHECK(ret == FLB_FALSE);
+
+ flb_sds_destroy(res_data);
+}
+
+static void cb_check_trace_stackdriver_autoformat(void *ctx, int ffd,
+ int res_ret, void *res_data, size_t res_size,
+ void *data)
+{
+ int ret;
+
+ /* trace in the entries */
+ ret = mp_kv_cmp(
+ res_data,
+ res_size,
+ "$entries[0]['trace']",
+ "projects/fluent-bit-test/traces/test-trace-id-xyz");
+ TEST_CHECK(ret == FLB_TRUE);
+
+ /* trace has been removed from jsonPayload */
+ ret = mp_kv_exists(res_data, res_size, "$entries[0]['jsonPayload']['trace']");
+ TEST_CHECK(ret == FLB_FALSE);
+
+ flb_sds_destroy(res_data);
+}
+
+static void cb_check_span_id(void *ctx, int ffd,
+ int res_ret, void *res_data, size_t res_size,
+ void *data)
+{
+ int ret;
+
+ /* span id in the entries */
+ ret = mp_kv_cmp(res_data, res_size, "$entries[0]['spanId']", "000000000000004a");
+ TEST_CHECK(ret == FLB_TRUE);
+
+ /* span id has been removed from jsonPayload */
+ ret = mp_kv_exists(res_data, res_size,
+ "$entries[0]['jsonPayload']['logging.googleapis.com/spanId']");
+ TEST_CHECK(ret == FLB_FALSE);
+
+ flb_sds_destroy(res_data);
+}
+
+static void cb_check_trace_sampled_true(void *ctx, int ffd,
+ int res_ret, void *res_data, size_t res_size,
+ void *data)
+{
+ int ret;
+
+ /* trace sampled in the entries */
+ ret = mp_kv_cmp_boolean(res_data, res_size, "$entries[0]['traceSampled']", true);
+ TEST_CHECK(ret == FLB_TRUE);
+
+ /* trace sampled has been removed from jsonPayload */
+ ret = mp_kv_exists(res_data, res_size,
+ "$entries[0]['jsonPayload']['logging.googleapis.com/traceSampled']");
+ TEST_CHECK(ret == FLB_FALSE);
+
+ flb_sds_destroy(res_data);
+}
+
+static void cb_check_trace_sampled_false(void *ctx, int ffd,
+ int res_ret, void *res_data, size_t res_size,
+ void *data)
+{
+ int ret;
+
+ /* trace sampled in the entries */
+ ret = mp_kv_cmp_boolean(res_data, res_size, "$entries[0]['traceSampled']", false);
+ TEST_CHECK(ret == FLB_TRUE);
+
+ /* trace sampled has been removed from jsonPayload */
+ ret = mp_kv_exists(res_data, res_size,
+ "$entries[0]['jsonPayload']['logging.googleapis.com/traceSampled']");
+ TEST_CHECK(ret == FLB_FALSE);
+
+ flb_sds_destroy(res_data);
+}
+
+static void cb_check_log_name_override(void *ctx, int ffd,
+ int res_ret, void *res_data, size_t res_size,
+ void *data)
+{
+ int ret;
+
+ /* logName in the entries is created using the value under log_name_key */
+ ret = mp_kv_cmp(
+ res_data, res_size, "$entries[0]['logName']", "projects/fluent-bit-test/logs/custom_log_name");
+ TEST_CHECK(ret == FLB_TRUE);
+
+ /* log_name_key has been removed from jsonPayload */
+ ret = mp_kv_exists(res_data, res_size, "$entries[0]['jsonPayload']['custom_log_name_key']");
+ TEST_CHECK(ret == FLB_FALSE);
+
+ flb_sds_destroy(res_data);
+}
+
+static void cb_check_log_name_no_override(void *ctx, int ffd,
+ int res_ret, void *res_data, size_t res_size,
+ void *data)
+{
+ int ret;
+
+ /* logName in the entries is created using the tag */
+ ret = mp_kv_cmp(
+ res_data, res_size, "$entries[0]['logName']", "projects/fluent-bit-test/logs/test");
+ TEST_CHECK(ret == FLB_TRUE);
+
+ flb_sds_destroy(res_data);
+}
+
+static void cb_check_k8s_node_resource(void *ctx, int ffd,
+ int res_ret, void *res_data, size_t res_size,
+ void *data)
+{
+ int ret;
+
+ /* resource type */
+ ret = mp_kv_cmp(res_data, res_size, "$resource['type']", "k8s_node");
+ TEST_CHECK(ret == FLB_TRUE);
+
+ /* project id */
+ ret = mp_kv_cmp(res_data, res_size,
+ "$resource['labels']['project_id']", "fluent-bit");
+ TEST_CHECK(ret == FLB_TRUE);
+
+ /* location */
+ ret = mp_kv_cmp(res_data, res_size,
+ "$resource['labels']['location']", "test_cluster_location");
+ TEST_CHECK(ret == FLB_TRUE);
+
+ /* cluster name */
+ ret = mp_kv_cmp(res_data, res_size,
+ "$resource['labels']['cluster_name']", "test_cluster_name");
+ TEST_CHECK(ret == FLB_TRUE);
+
+ /* node name */
+ ret = mp_kv_cmp(res_data, res_size,
+ "$resource['labels']['node_name']", "testnode");
+ TEST_CHECK(ret == FLB_TRUE);
+
+ /* check `local_resource_id` has been removed from jsonPayload */
+ ret = mp_kv_exists(res_data, res_size,
+ "$entries[0]['jsonPayload']['logging.googleapis.com/local_resource_id']");
+ TEST_CHECK(ret == FLB_FALSE);
+
+ flb_sds_destroy(res_data);
+}
+
+static void cb_check_k8s_node_custom_k8s_regex(void *ctx, int ffd,
+ int res_ret, void *res_data, size_t res_size,
+ void *data)
+{
+ int ret;
+
+ /* resource type */
+ ret = mp_kv_cmp(res_data, res_size, "$resource['type']", "k8s_node");
+ TEST_CHECK(ret == FLB_TRUE);
+
+ /* project id */
+ ret = mp_kv_cmp(res_data, res_size,
+ "$resource['labels']['project_id']", "fluent-bit");
+ TEST_CHECK(ret == FLB_TRUE);
+
+ /* location */
+ ret = mp_kv_cmp(res_data, res_size,
+ "$resource['labels']['location']", "test_cluster_location");
+ TEST_CHECK(ret == FLB_TRUE);
+
+ /* cluster name */
+ ret = mp_kv_cmp(res_data, res_size,
+ "$resource['labels']['cluster_name']", "test_cluster_name");
+ TEST_CHECK(ret == FLB_TRUE);
+
+ /* node name */
+ ret = mp_kv_cmp(res_data, res_size,
+ "$resource['labels']['node_name']", "testnode.withdot.dot");
+ TEST_CHECK(ret == FLB_TRUE);
+
+ /* check `local_resource_id` has been removed from jsonPayload */
+ ret = mp_kv_exists(res_data, res_size,
+ "$entries[0]['jsonPayload']['logging.googleapis.com/local_resource_id']");
+ TEST_CHECK(ret == FLB_FALSE);
+
+ flb_sds_destroy(res_data);
+}
+
+static void cb_check_k8s_pod_resource(void *ctx, int ffd,
+ int res_ret, void *res_data, size_t res_size,
+ void *data)
+{
+ int ret;
+
+ /* resource type */
+ ret = mp_kv_cmp(res_data, res_size, "$resource['type']", "k8s_pod");
+ TEST_CHECK(ret == FLB_TRUE);
+
+ /* project id */
+ ret = mp_kv_cmp(res_data, res_size,
+ "$resource['labels']['project_id']", "fluent-bit");
+ TEST_CHECK(ret == FLB_TRUE);
+
+ /* location */
+ ret = mp_kv_cmp(res_data, res_size,
+ "$resource['labels']['location']", "test_cluster_location");
+ TEST_CHECK(ret == FLB_TRUE);
+
+ /* cluster name */
+ ret = mp_kv_cmp(res_data, res_size,
+ "$resource['labels']['cluster_name']", "test_cluster_name");
+ TEST_CHECK(ret == FLB_TRUE);
+
+ /* namespace name */
+ ret = mp_kv_cmp(res_data, res_size,
+ "$resource['labels']['namespace_name']", "testnamespace");
+ TEST_CHECK(ret == FLB_TRUE);
+
+ /* pod name */
+ ret = mp_kv_cmp(res_data, res_size,
+ "$resource['labels']['pod_name']", "testpod");
+ TEST_CHECK(ret == FLB_TRUE);
+
+ /* check `local_resource_id` has been removed from jsonPayload */
+ ret = mp_kv_exists(res_data, res_size,
+ "$entries[0]['jsonPayload']['logging.googleapis.com/local_resource_id']");
+ TEST_CHECK(ret == FLB_FALSE);
+
+ flb_sds_destroy(res_data);
+}
+
+static void cb_check_insert_id_common_case(void *ctx, int ffd,
+ int res_ret, void *res_data, size_t res_size,
+ void *data)
+{
+ int ret;
+
+ /* insertId in the entries */
+ ret = mp_kv_cmp(res_data, res_size, "$entries[0]['insertId']", "test_insertId");
+ TEST_CHECK(ret == FLB_TRUE);
+
+ /* insertId has been removed from jsonPayload */
+ ret = mp_kv_exists(res_data, res_size, "$entries[0]['jsonPayload']['logging.googleapis.com/insertId']");
+ TEST_CHECK(ret == FLB_FALSE);
+
+ flb_sds_destroy(res_data);
+}
+
+static void cb_check_empty_insert_id(void *ctx, int ffd,
+ int res_ret, void *res_data, size_t res_size,
+ void *data)
+{
+ TEST_CHECK(res_size == 0);
+
+ flb_sds_destroy(res_data);
+}
+
+static void cb_check_insert_id_incorrect_type(void *ctx, int ffd,
+ int res_ret, void *res_data, size_t res_size,
+ void *data)
+{
+ TEST_CHECK(res_size == 0);
+
+ flb_sds_destroy(res_data);
+}
+
+static void cb_check_operation_common_case(void *ctx, int ffd,
+ int res_ret, void *res_data, size_t res_size,
+ void *data)
+{
+ int ret;
+
+ /* operation_id */
+ ret = mp_kv_cmp(res_data, res_size, "$entries[0]['operation']['id']", "test_id");
+ TEST_CHECK(ret == FLB_TRUE);
+
+ /* operation_producer */
+ ret = mp_kv_cmp(res_data, res_size, "$entries[0]['operation']['producer']", "test_producer");
+ TEST_CHECK(ret == FLB_TRUE);
+
+ /* operation_first */
+ ret = mp_kv_cmp_boolean(res_data, res_size, "$entries[0]['operation']['first']", true);
+ TEST_CHECK(ret == FLB_TRUE);
+
+ /* operation_last */
+ ret = mp_kv_cmp_boolean(res_data, res_size, "$entries[0]['operation']['last']", true);
+ TEST_CHECK(ret == FLB_TRUE);
+
+ /* check `operation` has been removed from jsonPayload */
+ ret = mp_kv_exists(res_data, res_size, "$entries[0]['jsonPayload']['logging.googleapis.com/operation']");
+ TEST_CHECK(ret == FLB_FALSE);
+
+ flb_sds_destroy(res_data);
+}
+
+static void cb_check_empty_operation(void *ctx, int ffd,
+ int res_ret, void *res_data, size_t res_size,
+ void *data)
+{
+ int ret;
+
+ /* operation_id */
+ ret = mp_kv_cmp(res_data, res_size, "$entries[0]['operation']['id']", "");
+ TEST_CHECK(ret == FLB_TRUE);
+
+ /* operation_producer */
+ ret = mp_kv_cmp(res_data, res_size, "$entries[0]['operation']['producer']", "");
+ TEST_CHECK(ret == FLB_TRUE);
+
+ /* operation_first */
+ ret = mp_kv_cmp_boolean(res_data, res_size, "$entries[0]['operation']['first']", false);
+ TEST_CHECK(ret == FLB_TRUE);
+
+ /* operation_last */
+ ret = mp_kv_cmp_boolean(res_data, res_size, "$entries[0]['operation']['last']", false);
+ TEST_CHECK(ret == FLB_TRUE);
+
+ /* check `operation` has been removed from jsonPayload */
+ ret = mp_kv_exists(res_data, res_size, "$entries[0]['jsonPayload']['logging.googleapis.com/operation']");
+ TEST_CHECK(ret == FLB_FALSE);
+
+ flb_sds_destroy(res_data);
+}
+
+static void cb_check_operation_in_string(void *ctx, int ffd,
+ int res_ret, void *res_data, size_t res_size,
+ void *data)
+{
+ int ret;
+
+ /* 'operation' is not a map, won't be extracted from jsonPayload */
+ ret = mp_kv_cmp(res_data, res_size, "$entries[0]['jsonPayload']['logging.googleapis.com/operation']", "some string");
+ TEST_CHECK(ret == FLB_TRUE);
+
+ ret = mp_kv_exists(res_data, res_size, "$entries[0]['operation']");
+ TEST_CHECK(ret == FLB_FALSE);
+
+ flb_sds_destroy(res_data);
+}
+
+static void cb_check_operation_partial_subfields(void *ctx, int ffd,
+ int res_ret, void *res_data, size_t res_size,
+ void *data)
+{
+ int ret;
+
+ /* operation_id */
+ ret = mp_kv_cmp(res_data, res_size, "$entries[0]['operation']['id']", "");
+ TEST_CHECK(ret == FLB_TRUE);
+
+ /* operation_producer */
+ ret = mp_kv_cmp(res_data, res_size, "$entries[0]['operation']['producer']", "");
+ TEST_CHECK(ret == FLB_TRUE);
+
+ /* operation_first */
+ ret = mp_kv_cmp_boolean(res_data, res_size, "$entries[0]['operation']['first']", false);
+ TEST_CHECK(ret == FLB_TRUE);
+
+ /* operation_last */
+ ret = mp_kv_cmp_boolean(res_data, res_size, "$entries[0]['operation']['last']", false);
+ TEST_CHECK(ret == FLB_TRUE);
+
+ /* check `operation` has been removed from jsonPayload */
+ ret = mp_kv_exists(res_data, res_size, "$entries[0]['jsonPayload']['logging.googleapis.com/operation']");
+ TEST_CHECK(ret == FLB_FALSE);
+
+ flb_sds_destroy(res_data);
+}
+
+static void cb_check_operation_incorrect_type_subfields(void *ctx, int ffd,
+ int res_ret, void *res_data, size_t res_size,
+ void *data)
+{
+ int ret;
+
+ /* operation_id */
+ ret = mp_kv_cmp(res_data, res_size, "$entries[0]['operation']['id']", "");
+ TEST_CHECK(ret == FLB_TRUE);
+
+ /* operation_producer */
+ ret = mp_kv_cmp(res_data, res_size, "$entries[0]['operation']['producer']", "");
+ TEST_CHECK(ret == FLB_TRUE);
+
+ /* operation_first */
+ ret = mp_kv_cmp_boolean(res_data, res_size, "$entries[0]['operation']['first']", false);
+ TEST_CHECK(ret == FLB_TRUE);
+
+ /* operation_last */
+ ret = mp_kv_cmp_boolean(res_data, res_size, "$entries[0]['operation']['last']", false);
+ TEST_CHECK(ret == FLB_TRUE);
+
+ /* check `operation` has been removed from jsonPayload */
+ ret = mp_kv_exists(res_data, res_size, "$entries[0]['jsonPayload']['logging.googleapis.com/operation']");
+ TEST_CHECK(ret == FLB_FALSE);
+
+ flb_sds_destroy(res_data);
+}
+
+static void cb_check_operation_extra_subfields(void *ctx, int ffd,
+ int res_ret, void *res_data, size_t res_size,
+ void *data)
+{
+ int ret;
+
+ /* operation_id */
+ ret = mp_kv_cmp(res_data, res_size, "$entries[0]['operation']['id']", "test_id");
+ TEST_CHECK(ret == FLB_TRUE);
+
+ /* operation_producer */
+ ret = mp_kv_cmp(res_data, res_size, "$entries[0]['operation']['producer']", "test_producer");
+ TEST_CHECK(ret == FLB_TRUE);
+
+ /* operation_first */
+ ret = mp_kv_cmp_boolean(res_data, res_size, "$entries[0]['operation']['first']", true);
+ TEST_CHECK(ret == FLB_TRUE);
+
+ /* operation_last */
+ ret = mp_kv_cmp_boolean(res_data, res_size, "$entries[0]['operation']['last']", true);
+ TEST_CHECK(ret == FLB_TRUE);
+
+ /* Preserve extra subfields inside jsonPayload */
+ ret = mp_kv_cmp(res_data, res_size, "$entries[0]['jsonPayload']['logging.googleapis.com/operation']['extra_key1']", "extra_val1");
+ TEST_CHECK(ret == FLB_TRUE);
+
+ ret = mp_kv_cmp_integer(res_data, res_size, "$entries[0]['jsonPayload']['logging.googleapis.com/operation']['extra_key2']", 123);
+ TEST_CHECK(ret == FLB_TRUE);
+
+ ret = mp_kv_cmp_boolean(res_data, res_size, "$entries[0]['jsonPayload']['logging.googleapis.com/operation']['extra_key3']", true);
+ TEST_CHECK(ret == FLB_TRUE);
+
+ flb_sds_destroy(res_data);
+}
+
+static void cb_check_default_labels(void *ctx, int ffd,
+ int res_ret, void *res_data, size_t res_size,
+ void *data)
+{
+ int ret;
+
+ /* check 'labels' field has been added to root-level of log entry */
+ ret = mp_kv_exists(res_data, res_size, "$entries[0]['labels']");
+ TEST_CHECK(ret == FLB_TRUE);
+
+ /* check fields inside 'labels' field */
+ ret = mp_kv_exists(res_data, res_size, "$entries[0]['labels']['testA']");
+ TEST_CHECK(ret == FLB_TRUE);
+
+ /* check field inside 'labels' field */
+ ret = mp_kv_exists(res_data, res_size, "$entries[0]['labels']['testB']");
+ TEST_CHECK(ret == FLB_TRUE);
+
+ /* check `labels_key` has been removed from jsonPayload */
+ ret = mp_kv_exists(res_data, res_size, "$entries[0]['jsonPayload']['logging.googleapis.com/labels']");
+ TEST_CHECK(ret == FLB_FALSE);
+
+ flb_sds_destroy(res_data);
+}
+
+static void cb_check_custom_labels(void *ctx, int ffd,
+ int res_ret, void *res_data, size_t res_size,
+ void *data)
+{
+ int ret;
+
+ /* check 'labels' field has been added to root-level of log entry */
+ ret = mp_kv_exists(res_data, res_size, "$entries[0]['labels']");
+ TEST_CHECK(ret == FLB_TRUE);
+
+ /* check fields inside 'labels' field */
+ ret = mp_kv_exists(res_data, res_size, "$entries[0]['labels']['testA']");
+ TEST_CHECK(ret == FLB_TRUE);
+
+ /* check field inside 'labels' field */
+ ret = mp_kv_exists(res_data, res_size, "$entries[0]['labels']['testB']");
+ TEST_CHECK(ret == FLB_TRUE);
+
+ /* check `labels_key` has been removed from jsonPayload */
+ ret = mp_kv_exists(res_data, res_size, "$entries[0]['jsonPayload']['logging.googleapis.com/customlabels']");
+ TEST_CHECK(ret == FLB_FALSE);
+
+ flb_sds_destroy(res_data);
+}
+
+static void cb_check_config_labels_no_conflict(void *ctx, int ffd,
+ int res_ret, void *res_data, size_t res_size,
+ void *data)
+{
+ int ret;
+
+ /* check 'labels' field has been added to root-level of log entry */
+ ret = mp_kv_exists(res_data, res_size, "$entries[0]['labels']");
+ TEST_CHECK(ret == FLB_TRUE);
+
+ /* check fields inside 'labels' field */
+ ret = mp_kv_exists(res_data, res_size, "$entries[0]['labels']['testA']");
+ TEST_CHECK(ret == FLB_TRUE);
+
+ /* check field inside 'labels' field */
+ ret = mp_kv_exists(res_data, res_size, "$entries[0]['labels']['testB']");
+ TEST_CHECK(ret == FLB_TRUE);
+
+ /* check field inside 'labels' field */
+ ret = mp_kv_exists(res_data, res_size, "$entries[0]['labels']['testC']");
+ TEST_CHECK(ret == FLB_TRUE);
+
+ /* check `labels_key` has been removed from jsonPayload */
+ ret = mp_kv_exists(res_data, res_size, "$entries[0]['jsonPayload']['logging.googleapis.com/labels']");
+ TEST_CHECK(ret == FLB_FALSE);
+
+ flb_sds_destroy(res_data);
+}
+
+static void cb_check_config_labels_conflict(void *ctx, int ffd,
+ int res_ret, void *res_data, size_t res_size,
+ void *data)
+{
+ int ret;
+
+ /* check 'labels' field has been added to root-level of log entry */
+ ret = mp_kv_exists(res_data, res_size, "$entries[0]['labels']");
+ TEST_CHECK(ret == FLB_TRUE);
+
+ /* check fields inside 'labels' field */
+ ret = mp_kv_exists(res_data, res_size, "$entries[0]['labels']['testA']");
+ TEST_CHECK(ret == FLB_TRUE);
+
+ /* check field inside 'labels' field */
+ ret = mp_kv_exists(res_data, res_size, "$entries[0]['labels']['testB']");
+ TEST_CHECK(ret == FLB_TRUE);
+
+ /* check static 'labels' override value */
+ ret = mp_kv_cmp(res_data, res_size, "$entries[0]['labels']['testB']", "valC");
+ TEST_CHECK(ret == FLB_TRUE);
+
+
+ /* check `labels_key` has been removed from jsonPayload */
+ ret = mp_kv_exists(res_data, res_size, "$entries[0]['jsonPayload']['logging.googleapis.com/labels']");
+ TEST_CHECK(ret == FLB_FALSE);
+
+ flb_sds_destroy(res_data);
+}
+
+static void cb_check_default_labels_k8s_resource_type(void *ctx, int ffd,
+ int res_ret, void *res_data, size_t res_size,
+ void *data)
+{
+ int ret;
+
+ /* resource type */
+ ret = mp_kv_cmp(res_data, res_size, "$resource['type']", "k8s_container");
+ TEST_CHECK(ret == FLB_TRUE);
+
+ /* project id */
+ ret = mp_kv_cmp(res_data, res_size,
+ "$resource['labels']['project_id']", "fluent-bit");
+ TEST_CHECK(ret == FLB_TRUE);
+
+ /* location */
+ ret = mp_kv_cmp(res_data, res_size,
+ "$resource['labels']['location']", "test_cluster_location");
+ TEST_CHECK(ret == FLB_TRUE);
+
+ /* cluster name */
+ ret = mp_kv_cmp(res_data, res_size,
+ "$resource['labels']['cluster_name']", "test_cluster_name");
+ TEST_CHECK(ret == FLB_TRUE);
+
+ /* namespace name */
+ ret = mp_kv_cmp(res_data, res_size,
+ "$resource['labels']['namespace_name']", "testnamespace");
+ TEST_CHECK(ret == FLB_TRUE);
+
+ /* pod name */
+ ret = mp_kv_cmp(res_data, res_size,
+ "$resource['labels']['pod_name']", "testpod");
+ TEST_CHECK(ret == FLB_TRUE);
+
+ /* container name */
+ ret = mp_kv_cmp(res_data, res_size,
+ "$resource['labels']['container_name']", "testctr");
+ TEST_CHECK(ret == FLB_TRUE);
+
+ /* check `local_resource_id` has been removed from jsonPayload */
+ ret = mp_kv_exists(res_data, res_size,
+ "$entries[0]['jsonPayload']['logging.googleapis.com/local_resource_id']");
+ TEST_CHECK(ret == FLB_FALSE);
+
+ /* check 'labels' field has been added to root-level of log entry */
+ ret = mp_kv_exists(res_data, res_size, "$entries[0]['labels']");
+ TEST_CHECK(ret == FLB_TRUE);
+
+ /* check fields inside 'labels' field */
+ ret = mp_kv_exists(res_data, res_size, "$entries[0]['labels']['testA']");
+ TEST_CHECK(ret == FLB_TRUE);
+
+ /* check field inside 'labels' field */
+ ret = mp_kv_exists(res_data, res_size, "$entries[0]['labels']['testB']");
+ TEST_CHECK(ret == FLB_TRUE);
+
+ /* check `labels_key` has been removed from jsonPayload */
+ ret = mp_kv_exists(res_data, res_size, "$entries[0]['jsonPayload']['logging.googleapis.com/labels']");
+ TEST_CHECK(ret == FLB_FALSE);
+
+ flb_sds_destroy(res_data);
+}
+
+static void cb_check_resource_labels_one_field(void *ctx, int ffd,
+ int res_ret, void *res_data, size_t res_size, void *data)
+{
+ int ret;
+
+ ret = mp_kv_cmp(res_data, res_size, "$entries[0]['jsonPayload']['keyA']", "valA");
+ TEST_CHECK(ret == FLB_TRUE);
+
+ ret = mp_kv_cmp(res_data, res_size, "$resource['labels']['keyB']", "valA");
+ TEST_CHECK(ret == FLB_TRUE);
+
+ ret = mp_kv_cmp(res_data, res_size, "$resource['labels']['project_id']", "fluent-bit");
+ TEST_CHECK(ret == FLB_TRUE);
+
+ flb_sds_destroy(res_data);
+}
+
+static void cb_check_resource_labels_plaintext(void *ctx, int ffd,
+ int res_ret, void *res_data, size_t res_size, void *data)
+{
+ int ret;
+
+ ret = mp_kv_cmp(res_data, res_size, "$entries[0]['jsonPayload']['keyA']", "valA");
+ TEST_CHECK(ret == FLB_TRUE);
+
+ ret = mp_kv_cmp(res_data, res_size, "$resource['labels']['keyB']", "plaintext");
+ TEST_CHECK(ret == FLB_TRUE);
+
+ ret = mp_kv_cmp(res_data, res_size, "$resource['labels']['project_id']", "fluent-bit");
+ TEST_CHECK(ret == FLB_TRUE);
+
+ flb_sds_destroy(res_data);
+}
+
+static void cb_check_resource_labels_k8s(void *ctx, int ffd,
+ int res_ret, void *res_data, size_t res_size, void *data)
+{
+ int ret;
+
+ ret = mp_kv_cmp(res_data, res_size, "$resource['labels']['project_id']", "fluent-bit");
+ TEST_CHECK(ret == FLB_TRUE);
+
+ ret = mp_kv_cmp(res_data, res_size,
+ "$resource['labels']['cluster_name']", "name_from_labels");
+ TEST_CHECK(ret == FLB_TRUE);
+
+ ret = mp_kv_cmp(res_data, res_size,
+ "$resource['labels']['location']", "loc_from_labels");
+ TEST_CHECK(ret == FLB_TRUE);
+
+ flb_sds_destroy(res_data);
+}
+
+static void cb_check_nested_fields_mapped(void *ctx, int ffd,
+ int res_ret, void *res_data, size_t res_size, void *data)
+{
+ int ret;
+
+ ret = mp_kv_cmp(res_data, res_size,
+ "$entries[0]['jsonPayload']['toplevel']['keyB']", "valB");
+ TEST_CHECK(ret == FLB_TRUE);
+
+ ret = mp_kv_cmp(res_data, res_size, "$resource['labels']['keyD']", "valB");
+ TEST_CHECK(ret == FLB_TRUE);
+
+ ret = mp_kv_cmp(res_data, res_size,
+ "$entries[0]['jsonPayload']['toplevel']['keyA']", "valA");
+ TEST_CHECK(ret == FLB_TRUE);
+
+ ret = mp_kv_cmp(res_data, res_size, "$resource['labels']['keyC']", "valA");
+ TEST_CHECK(ret == FLB_TRUE);
+
+ flb_sds_destroy(res_data);
+}
+
+static void cb_check_layered_nested_fields_mapped(void *ctx, int ffd,
+ int res_ret, void *res_data, size_t res_size, void *data)
+{
+ int ret;
+
+ ret = mp_kv_cmp(res_data, res_size,
+ "$entries[0]['jsonPayload']['toplevel']['keyB']", "valB");
+ TEST_CHECK(ret == FLB_TRUE);
+
+ ret = mp_kv_cmp(res_data, res_size, "$resource['labels']['keyD']", "valB");
+ TEST_CHECK(ret == FLB_TRUE);
+
+ ret = mp_kv_cmp(res_data, res_size,
+ "$entries[0]['jsonPayload']['toplevel']['midlevel']['keyA']", "valA");
+ TEST_CHECK(ret == FLB_TRUE);
+
+ ret = mp_kv_cmp(res_data, res_size, "$resource['labels']['keyC']", "valA");
+ TEST_CHECK(ret == FLB_TRUE);
+
+ flb_sds_destroy(res_data);
+}
+
+static void cb_check_resource_labels_original_does_not_exist(void *ctx, int ffd,
+ int res_ret, void *res_data, size_t res_size, void *data)
+{
+ int ret;
+
+ ret = mp_kv_cmp(res_data, res_size, "$entries[0]['jsonPayload']['keyA']", "valA");
+ TEST_CHECK(ret == FLB_TRUE);
+
+ ret = mp_kv_exists(res_data, res_size, "$resource['labels']['keyC']");
+ TEST_CHECK(ret == FLB_FALSE);
+
+ flb_sds_destroy(res_data);
+}
+
+static void cb_check_resource_labels_nested_original_partially_exists(void *ctx, int ffd,
+ int res_ret, void *res_data, size_t res_size, void *data)
+{
+ int ret;
+
+ ret = mp_kv_exists(res_data, res_size, "$entries[0]['jsonPayload']['toplevel']");
+ TEST_CHECK(ret == FLB_TRUE);
+
+ ret = mp_kv_cmp(res_data, res_size,
+ "$entries[0]['jsonPayload']['toplevel']['keyA']", "valA");
+ TEST_CHECK(ret == FLB_TRUE);
+
+ ret = mp_kv_cmp(res_data, res_size,
+ "$entries[0]['jsonPayload']['toplevel']['keyB']", "valB");
+ TEST_CHECK(ret == FLB_TRUE);
+
+ ret = mp_kv_exists(res_data, res_size, "$resource['labels']['keyC']");
+ TEST_CHECK(ret == FLB_FALSE);
+
+ flb_sds_destroy(res_data);
+}
+
+static void cb_check_multiple_fields_mapped(void *ctx, int ffd, int res_ret,
+ void *res_data, size_t res_size, void *data)
+{
+ int ret;
+
+ ret = mp_kv_cmp(res_data, res_size, "$entries[0]['jsonPayload']['keyA']", "valA");
+ TEST_CHECK(ret == FLB_TRUE);
+
+ ret = mp_kv_cmp(res_data, res_size, "$entries[0]['jsonPayload']['keyB']", "valB");
+ TEST_CHECK(ret == FLB_TRUE);
+
+ ret = mp_kv_cmp(res_data, res_size, "$resource['labels']['keyC']", "valA");
+ TEST_CHECK(ret == FLB_TRUE);
+
+ ret = mp_kv_cmp(res_data, res_size, "$resource['labels']['keyD']", "valB");
+ TEST_CHECK(ret == FLB_TRUE);
+
+ flb_sds_destroy(res_data);
+}
+
+static void cb_check_resource_labels_duplicate_assignment(void *ctx, int ffd, int res_ret,
+ void *res_data, size_t res_size, void *data)
+{
+ int ret;
+
+ ret = mp_kv_cmp(res_data, res_size, "$entries[0]['jsonPayload']['keyA']", "valA");
+ TEST_CHECK(ret == FLB_TRUE);
+
+ ret = mp_kv_cmp(res_data, res_size, "$entries[0]['jsonPayload']['keyB']", "valB");
+ TEST_CHECK(ret == FLB_TRUE);
+
+ ret = mp_kv_cmp(res_data, res_size, "$resource['labels']['keyC']", "valB");
+ TEST_CHECK(ret == FLB_TRUE);
+
+ flb_sds_destroy(res_data);
+}
+
+
+static void cb_check_resource_labels_project_id_not_overridden(void *ctx, int ffd,
+ int res_ret, void *res_data, size_t res_size, void *data)
+{
+ int ret;
+
+ ret = mp_kv_cmp(res_data, res_size, "$resource['labels']['project_id']", "fluent-bit");
+ TEST_CHECK(ret == FLB_TRUE);
+
+ flb_sds_destroy(res_data);
+}
+
+static void cb_check_custom_labels_k8s_resource_type(void *ctx, int ffd,
+ int res_ret, void *res_data, size_t res_size,
+ void *data)
+{
+ int ret;
+
+ /* resource type */
+ ret = mp_kv_cmp(res_data, res_size, "$resource['type']", "k8s_container");
+ TEST_CHECK(ret == FLB_TRUE);
+
+ /* project id */
+ ret = mp_kv_cmp(res_data, res_size,
+ "$resource['labels']['project_id']", "fluent-bit");
+ TEST_CHECK(ret == FLB_TRUE);
+
+ /* location */
+ ret = mp_kv_cmp(res_data, res_size,
+ "$resource['labels']['location']", "test_cluster_location");
+ TEST_CHECK(ret == FLB_TRUE);
+
+ /* cluster name */
+ ret = mp_kv_cmp(res_data, res_size,
+ "$resource['labels']['cluster_name']", "test_cluster_name");
+ TEST_CHECK(ret == FLB_TRUE);
+
+ /* namespace name */
+ ret = mp_kv_cmp(res_data, res_size,
+ "$resource['labels']['namespace_name']", "testnamespace");
+ TEST_CHECK(ret == FLB_TRUE);
+
+ /* pod name */
+ ret = mp_kv_cmp(res_data, res_size,
+ "$resource['labels']['pod_name']", "testpod");
+ TEST_CHECK(ret == FLB_TRUE);
+
+ /* container name */
+ ret = mp_kv_cmp(res_data, res_size,
+ "$resource['labels']['container_name']", "testctr");
+ TEST_CHECK(ret == FLB_TRUE);
+
+ /* check `local_resource_id` has been removed from jsonPayload */
+ ret = mp_kv_exists(res_data, res_size,
+ "$entries[0]['jsonPayload']['logging.googleapis.com/local_resource_id']");
+ TEST_CHECK(ret == FLB_FALSE);
+
+ /* check 'labels' field has been added to root-level of log entry */
+ ret = mp_kv_exists(res_data, res_size, "$entries[0]['labels']");
+ TEST_CHECK(ret == FLB_TRUE);
+
+ /* check fields inside 'labels' field */
+ ret = mp_kv_exists(res_data, res_size, "$entries[0]['labels']['testA']");
+ TEST_CHECK(ret == FLB_TRUE);
+
+ /* check field inside 'labels' field */
+ ret = mp_kv_exists(res_data, res_size, "$entries[0]['labels']['testB']");
+ TEST_CHECK(ret == FLB_TRUE);
+
+ /* check `labels_key` has been removed from jsonPayload */
+ ret = mp_kv_exists(res_data, res_size, "$entries[0]['jsonPayload']['logging.googleapis.com/customlabels']");
+ TEST_CHECK(ret == FLB_FALSE);
+
+ flb_sds_destroy(res_data);
+}
+
+static void cb_check_multi_entries_severity(void *ctx, int ffd,
+ int res_ret, void *res_data, size_t res_size,
+ void *data)
+{
+ int ret;
+
+ ret = mp_kv_cmp(res_data, res_size, "$entries[0]['severity']", "INFO");
+ TEST_CHECK(ret == FLB_TRUE);
+
+ // verifies that severity is removed from jsonPayload
+ ret = mp_kv_exists(res_data, res_size, "$entries[0]['jsonPayload']['severity']");
+ TEST_CHECK(ret == FLB_FALSE);
+
+ ret = mp_kv_exists(res_data, res_size, "$entries[1]['severity']");
+ TEST_CHECK(ret == FLB_FALSE);
+
+ ret = mp_kv_cmp(res_data, res_size, "$entries[2]['severity']", "DEBUG");
+ TEST_CHECK(ret == FLB_TRUE);
+
+ // verifies that severity is removed from jsonPayload
+ ret = mp_kv_exists(res_data, res_size, "$entries[2]['jsonPayload']['severity']");
+ TEST_CHECK(ret == FLB_FALSE);
+
+ ret = mp_kv_exists(res_data, res_size, "$entries[3]['severity']");
+ TEST_CHECK(ret == FLB_FALSE);
+
+ flb_sds_destroy(res_data);
+}
+
+static void cb_check_source_location_common_case(void *ctx, int ffd,
+ int res_ret, void *res_data, size_t res_size,
+ void *data)
+{
+ int ret;
+
+ /* sourceLocation_file */
+ ret = mp_kv_cmp(res_data, res_size, "$entries[0]['sourceLocation']['file']", "test_file");
+ TEST_CHECK(ret == FLB_TRUE);
+
+ /* sourceLocation_line */
+ ret = mp_kv_cmp_integer(res_data, res_size, "$entries[0]['sourceLocation']['line']", 123);
+ TEST_CHECK(ret == FLB_TRUE);
+
+ /* sourceLocation_function */
+ ret = mp_kv_cmp(res_data, res_size, "$entries[0]['sourceLocation']['function']", "test_function");
+ TEST_CHECK(ret == FLB_TRUE);
+
+ /* check `sourceLocation` has been removed from jsonPayload */
+ ret = mp_kv_exists(res_data, res_size, "$entries[0]['jsonPayload']['logging.googleapis.com/sourceLocation']");
+ TEST_CHECK(ret == FLB_FALSE);
+
+ flb_sds_destroy(res_data);
+}
+
+static void cb_check_source_location_common_case_line_in_string(void *ctx, int ffd,
+ int res_ret, void *res_data, size_t res_size,
+ void *data)
+{
+ int ret;
+
+ /* sourceLocation_file */
+ ret = mp_kv_cmp(res_data, res_size, "$entries[0]['sourceLocation']['file']", "test_file");
+ TEST_CHECK(ret == FLB_TRUE);
+
+ /* sourceLocation_line */
+ ret = mp_kv_cmp_integer(res_data, res_size, "$entries[0]['sourceLocation']['line']", 123);
+ TEST_CHECK(ret == FLB_TRUE);
+
+ /* sourceLocation_function */
+ ret = mp_kv_cmp(res_data, res_size, "$entries[0]['sourceLocation']['function']", "test_function");
+ TEST_CHECK(ret == FLB_TRUE);
+
+ /* check `sourceLocation` has been removed from jsonPayload */
+ ret = mp_kv_exists(res_data, res_size, "$entries[0]['jsonPayload']['logging.googleapis.com/sourceLocation']");
+ TEST_CHECK(ret == FLB_FALSE);
+
+ flb_sds_destroy(res_data);
+}
+
+static void cb_check_empty_source_location(void *ctx, int ffd,
+ int res_ret, void *res_data, size_t res_size,
+ void *data)
+{
+ int ret;
+
+ /* sourceLocation_file */
+ ret = mp_kv_cmp(res_data, res_size, "$entries[0]['sourceLocation']['file']", "");
+ TEST_CHECK(ret == FLB_TRUE);
+
+ /* sourceLocation_line */
+ ret = mp_kv_cmp_integer(res_data, res_size, "$entries[0]['sourceLocation']['line']", 0);
+ TEST_CHECK(ret == FLB_TRUE);
+
+ /* sourceLocation_function */
+ ret = mp_kv_cmp(res_data, res_size, "$entries[0]['sourceLocation']['function']", "");
+ TEST_CHECK(ret == FLB_TRUE);
+
+ /* check `sourceLocation` has been removed from jsonPayload */
+ ret = mp_kv_exists(res_data, res_size, "$entries[0]['jsonPayload']['logging.googleapis.com/sourceLocation']");
+ TEST_CHECK(ret == FLB_FALSE);
+
+ flb_sds_destroy(res_data);
+}
+
+static void cb_check_source_location_in_string(void *ctx, int ffd,
+ int res_ret, void *res_data, size_t res_size,
+ void *data)
+{
+ int ret;
+
+ /* sourceLocation remains in jsonPayload */
+ ret = mp_kv_cmp(res_data, res_size, "$entries[0]['jsonPayload']['logging.googleapis.com/sourceLocation']", "some string");
+ TEST_CHECK(ret == FLB_TRUE);
+
+ ret = mp_kv_exists(res_data, res_size, "$entries[0]['sourceLocation']");
+ TEST_CHECK(ret == FLB_FALSE);
+
+ flb_sds_destroy(res_data);
+}
+
+static void cb_check_source_location_partial_subfields(void *ctx, int ffd,
+ int res_ret, void *res_data, size_t res_size,
+ void *data)
+{
+ int ret;
+
+ /* sourceLocation_file */
+ ret = mp_kv_cmp(res_data, res_size, "$entries[0]['sourceLocation']['file']", "");
+ TEST_CHECK(ret == FLB_TRUE);
+
+ /* sourceLocation_line */
+ ret = mp_kv_cmp_integer(res_data, res_size, "$entries[0]['sourceLocation']['line']", 0);
+ TEST_CHECK(ret == FLB_TRUE);
+
+ /* sourceLocation_function */
+ ret = mp_kv_cmp(res_data, res_size, "$entries[0]['sourceLocation']['function']", "test_function");
+ TEST_CHECK(ret == FLB_TRUE);
+
+
+ /* check `sourceLocation` has been removed from jsonPayload */
+ ret = mp_kv_exists(res_data, res_size, "$entries[0]['jsonPayload']['logging.googleapis.com/sourceLocation']");
+ TEST_CHECK(ret == FLB_FALSE);
+
+ flb_sds_destroy(res_data);
+}
+
+static void cb_check_source_location_incorrect_type_subfields(void *ctx, int ffd,
+ int res_ret, void *res_data, size_t res_size,
+ void *data)
+{
+ int ret;
+
+ /* sourceLocation_file */
+ ret = mp_kv_cmp(res_data, res_size, "$entries[0]['sourceLocation']['file']", "");
+ TEST_CHECK(ret == FLB_TRUE);
+
+ /* sourceLocation_line */
+ ret = mp_kv_cmp_integer(res_data, res_size, "$entries[0]['sourceLocation']['line']", 0);
+ TEST_CHECK(ret == FLB_TRUE);
+
+ /* sourceLocation_function */
+ ret = mp_kv_cmp(res_data, res_size, "$entries[0]['sourceLocation']['function']", "");
+ TEST_CHECK(ret == FLB_TRUE);
+
+
+ /* check `sourceLocation` has been removed from jsonPayload */
+ ret = mp_kv_exists(res_data, res_size, "$entries[0]['jsonPayload']['logging.googleapis.com/sourceLocation']");
+ TEST_CHECK(ret == FLB_FALSE);
+
+ flb_sds_destroy(res_data);
+}
+
+static void cb_check_source_location_extra_subfields(void *ctx, int ffd,
+ int res_ret, void *res_data, size_t res_size,
+ void *data)
+{
+ int ret;
+
+ /* sourceLocation_file */
+ ret = mp_kv_cmp(res_data, res_size, "$entries[0]['sourceLocation']['file']", "test_file");
+ TEST_CHECK(ret == FLB_TRUE);
+
+ /* sourceLocation_line */
+ ret = mp_kv_cmp_integer(res_data, res_size, "$entries[0]['sourceLocation']['line']", 123);
+ TEST_CHECK(ret == FLB_TRUE);
+
+ /* sourceLocation_function */
+ ret = mp_kv_cmp(res_data, res_size, "$entries[0]['sourceLocation']['function']", "test_function");
+ TEST_CHECK(ret == FLB_TRUE);
+
+ /* Preserve extra subfields inside jsonPayload */
+ ret = mp_kv_cmp(res_data, res_size, "$entries[0]['jsonPayload']['logging.googleapis.com/sourceLocation']['extra_key1']", "extra_val1");
+ TEST_CHECK(ret == FLB_TRUE);
+
+ ret = mp_kv_cmp_integer(res_data, res_size, "$entries[0]['jsonPayload']['logging.googleapis.com/sourceLocation']['extra_key2']", 123);
+ TEST_CHECK(ret == FLB_TRUE);
+
+ ret = mp_kv_cmp_boolean(res_data, res_size, "$entries[0]['jsonPayload']['logging.googleapis.com/sourceLocation']['extra_key3']", true);
+ TEST_CHECK(ret == FLB_TRUE);
+
+ flb_sds_destroy(res_data);
+}
+
+static void cb_check_http_request_common_case(void *ctx, int ffd,
+ int res_ret, void *res_data, size_t res_size,
+ void *data)
+{
+ int ret;
+
+ ret = mp_kv_cmp(res_data, res_size, "$entries[0]['httpRequest']['requestMethod']", "test_requestMethod");
+ TEST_CHECK(ret == FLB_TRUE);
+
+ ret = mp_kv_cmp(res_data, res_size, "$entries[0]['httpRequest']['requestUrl']", "test_requestUrl");
+ TEST_CHECK(ret == FLB_TRUE);
+
+ ret = mp_kv_cmp(res_data, res_size, "$entries[0]['httpRequest']['userAgent']", "test_userAgent");
+ TEST_CHECK(ret == FLB_TRUE);
+
+ ret = mp_kv_cmp(res_data, res_size, "$entries[0]['httpRequest']['remoteIp']", "test_remoteIp");
+ TEST_CHECK(ret == FLB_TRUE);
+
+ ret = mp_kv_cmp(res_data, res_size, "$entries[0]['httpRequest']['serverIp']", "test_serverIp");
+ TEST_CHECK(ret == FLB_TRUE);
+
+ ret = mp_kv_cmp(res_data, res_size, "$entries[0]['httpRequest']['referer']", "test_referer");
+ TEST_CHECK(ret == FLB_TRUE);
+
+ ret = mp_kv_cmp(res_data, res_size, "$entries[0]['httpRequest']['protocol']", "test_protocol");
+ TEST_CHECK(ret == FLB_TRUE);
+
+ ret = mp_kv_cmp(res_data, res_size, "$entries[0]['httpRequest']['latency']", "0s");
+ TEST_CHECK(ret == FLB_TRUE);
+
+ ret = mp_kv_cmp_integer(res_data, res_size, "$entries[0]['httpRequest']['requestSize']", 123);
+ TEST_CHECK(ret == FLB_TRUE);
+
+ ret = mp_kv_cmp_integer(res_data, res_size, "$entries[0]['httpRequest']['responseSize']", 123);
+ TEST_CHECK(ret == FLB_TRUE);
+
+ ret = mp_kv_cmp_integer(res_data, res_size, "$entries[0]['httpRequest']['status']", 200);
+ TEST_CHECK(ret == FLB_TRUE);
+
+ ret = mp_kv_cmp_integer(res_data, res_size, "$entries[0]['httpRequest']['cacheFillBytes']", 123);
+ TEST_CHECK(ret == FLB_TRUE);
+
+ ret = mp_kv_cmp_boolean(res_data, res_size, "$entries[0]['httpRequest']['cacheLookup']", true);
+ TEST_CHECK(ret == FLB_TRUE);
+
+ ret = mp_kv_cmp_boolean(res_data, res_size, "$entries[0]['httpRequest']['cacheHit']", true);
+ TEST_CHECK(ret == FLB_TRUE);
+
+ ret = mp_kv_cmp_boolean(res_data, res_size, "$entries[0]['httpRequest']['cacheValidatedWithOriginServer']", true);
+ TEST_CHECK(ret == FLB_TRUE);
+
+ /* check `httpRequest` has been removed from jsonPayload */
+ ret = mp_kv_exists(res_data, res_size, "$entries[0]['jsonPayload']['logging.googleapis.com/http_request']");
+ TEST_CHECK(ret == FLB_FALSE);
+
+ flb_sds_destroy(res_data);
+}
+
+static void cb_check_empty_http_request(void *ctx, int ffd,
+ int res_ret, void *res_data, size_t res_size,
+ void *data)
+{
+ int ret;
+
+ ret = mp_kv_cmp(res_data, res_size, "$entries[0]['httpRequest']['requestMethod']", "");
+ TEST_CHECK(ret == FLB_TRUE);
+
+ ret = mp_kv_cmp(res_data, res_size, "$entries[0]['httpRequest']['requestUrl']", "");
+ TEST_CHECK(ret == FLB_TRUE);
+
+ ret = mp_kv_cmp(res_data, res_size, "$entries[0]['httpRequest']['userAgent']", "");
+ TEST_CHECK(ret == FLB_TRUE);
+
+ ret = mp_kv_cmp(res_data, res_size, "$entries[0]['httpRequest']['remoteIp']", "");
+ TEST_CHECK(ret == FLB_TRUE);
+
+ ret = mp_kv_cmp(res_data, res_size, "$entries[0]['httpRequest']['serverIp']", "");
+ TEST_CHECK(ret == FLB_TRUE);
+
+ ret = mp_kv_cmp(res_data, res_size, "$entries[0]['httpRequest']['referer']", "");
+ TEST_CHECK(ret == FLB_TRUE);
+
+ ret = mp_kv_cmp(res_data, res_size, "$entries[0]['httpRequest']['protocol']", "");
+ TEST_CHECK(ret == FLB_TRUE);
+
+ ret = mp_kv_exists(res_data, res_size, "$entries[0]['httpRequest']['latency']");
+ TEST_CHECK(ret == FLB_FALSE);
+
+ ret = mp_kv_cmp_integer(res_data, res_size, "$entries[0]['httpRequest']['requestSize']", 0);
+ TEST_CHECK(ret == FLB_TRUE);
+
+ ret = mp_kv_cmp_integer(res_data, res_size, "$entries[0]['httpRequest']['responseSize']", 0);
+ TEST_CHECK(ret == FLB_TRUE);
+
+ ret = mp_kv_cmp_integer(res_data, res_size, "$entries[0]['httpRequest']['status']", 0);
+ TEST_CHECK(ret == FLB_TRUE);
+
+ ret = mp_kv_cmp_integer(res_data, res_size, "$entries[0]['httpRequest']['cacheFillBytes']", 0);
+ TEST_CHECK(ret == FLB_TRUE);
+
+ ret = mp_kv_cmp_boolean(res_data, res_size, "$entries[0]['httpRequest']['cacheLookup']", false);
+ TEST_CHECK(ret == FLB_TRUE);
+
+ ret = mp_kv_cmp_boolean(res_data, res_size, "$entries[0]['httpRequest']['cacheHit']", false);
+ TEST_CHECK(ret == FLB_TRUE);
+
+ ret = mp_kv_cmp_boolean(res_data, res_size, "$entries[0]['httpRequest']['cacheValidatedWithOriginServer']", false);
+ TEST_CHECK(ret == FLB_TRUE);
+
+ /* check `httpRequest` has been removed from jsonPayload */
+ ret = mp_kv_exists(res_data, res_size, "$entries[0]['jsonPayload']['logging.googleapis.com/http_request']");
+ TEST_CHECK(ret == FLB_FALSE);
+
+ flb_sds_destroy(res_data);
+}
+
+static void cb_check_http_request_in_string(void *ctx, int ffd,
+ int res_ret, void *res_data, size_t res_size,
+ void *data)
+{
+ int ret;
+
+ ret = mp_kv_cmp(res_data, res_size, "$entries[0]['jsonPayload']['logging.googleapis.com/http_request']", "some string");
+ TEST_CHECK(ret == FLB_TRUE);
+
+ ret = mp_kv_exists(res_data, res_size, "$entries[0]['httpRequest']");
+ TEST_CHECK(ret == FLB_FALSE);
+
+ flb_sds_destroy(res_data);
+}
+
+static void cb_check_http_request_partial_subfields(void *ctx, int ffd,
+ int res_ret, void *res_data, size_t res_size,
+ void *data)
+{
+ int ret;
+
+ ret = mp_kv_cmp(res_data, res_size, "$entries[0]['httpRequest']['requestMethod']", "");
+ TEST_CHECK(ret == FLB_TRUE);
+
+ ret = mp_kv_cmp(res_data, res_size, "$entries[0]['httpRequest']['requestUrl']", "");
+ TEST_CHECK(ret == FLB_TRUE);
+
+ ret = mp_kv_cmp(res_data, res_size, "$entries[0]['httpRequest']['userAgent']", "");
+ TEST_CHECK(ret == FLB_TRUE);
+
+ ret = mp_kv_cmp(res_data, res_size, "$entries[0]['httpRequest']['remoteIp']", "");
+ TEST_CHECK(ret == FLB_TRUE);
+
+ ret = mp_kv_cmp(res_data, res_size, "$entries[0]['httpRequest']['serverIp']", "");
+ TEST_CHECK(ret == FLB_TRUE);
+
+ ret = mp_kv_cmp(res_data, res_size, "$entries[0]['httpRequest']['referer']", "");
+ TEST_CHECK(ret == FLB_TRUE);
+
+ ret = mp_kv_cmp(res_data, res_size, "$entries[0]['httpRequest']['protocol']", "");
+ TEST_CHECK(ret == FLB_TRUE);
+
+ ret = mp_kv_exists(res_data, res_size, "$entries[0]['httpRequest']['latency']");
+ TEST_CHECK(ret == FLB_FALSE);
+
+ ret = mp_kv_cmp_integer(res_data, res_size, "$entries[0]['httpRequest']['requestSize']", 0);
+ TEST_CHECK(ret == FLB_TRUE);
+
+ ret = mp_kv_cmp_integer(res_data, res_size, "$entries[0]['httpRequest']['responseSize']", 0);
+ TEST_CHECK(ret == FLB_TRUE);
+
+ ret = mp_kv_cmp_integer(res_data, res_size, "$entries[0]['httpRequest']['status']", 0);
+ TEST_CHECK(ret == FLB_TRUE);
+
+ ret = mp_kv_cmp_integer(res_data, res_size, "$entries[0]['httpRequest']['cacheFillBytes']", 0);
+ TEST_CHECK(ret == FLB_TRUE);
+
+ ret = mp_kv_cmp_boolean(res_data, res_size, "$entries[0]['httpRequest']['cacheLookup']", true);
+ TEST_CHECK(ret == FLB_TRUE);
+
+ ret = mp_kv_cmp_boolean(res_data, res_size, "$entries[0]['httpRequest']['cacheHit']", true);
+ TEST_CHECK(ret == FLB_TRUE);
+
+ ret = mp_kv_cmp_boolean(res_data, res_size, "$entries[0]['httpRequest']['cacheValidatedWithOriginServer']", true);
+ TEST_CHECK(ret == FLB_TRUE);
+
+ /* check `httpRequest` has been removed from jsonPayload */
+ ret = mp_kv_exists(res_data, res_size, "$entries[0]['jsonPayload']['logging.googleapis.com/http_request']");
+ TEST_CHECK(ret == FLB_FALSE);
+
+ flb_sds_destroy(res_data);
+}
+
+static void cb_check_http_request_incorrect_type_subfields(void *ctx, int ffd,
+ int res_ret, void *res_data, size_t res_size,
+ void *data)
+{
+ int ret;
+
+ ret = mp_kv_cmp(res_data, res_size, "$entries[0]['httpRequest']['requestMethod']", "");
+ TEST_CHECK(ret == FLB_TRUE);
+
+ ret = mp_kv_cmp(res_data, res_size, "$entries[0]['httpRequest']['requestUrl']", "");
+ TEST_CHECK(ret == FLB_TRUE);
+
+ ret = mp_kv_cmp(res_data, res_size, "$entries[0]['httpRequest']['userAgent']", "");
+ TEST_CHECK(ret == FLB_TRUE);
+
+ ret = mp_kv_cmp(res_data, res_size, "$entries[0]['httpRequest']['remoteIp']", "");
+ TEST_CHECK(ret == FLB_TRUE);
+
+ ret = mp_kv_cmp(res_data, res_size, "$entries[0]['httpRequest']['serverIp']", "");
+ TEST_CHECK(ret == FLB_TRUE);
+
+ ret = mp_kv_cmp(res_data, res_size, "$entries[0]['httpRequest']['referer']", "");
+ TEST_CHECK(ret == FLB_TRUE);
+
+ ret = mp_kv_cmp(res_data, res_size, "$entries[0]['httpRequest']['protocol']", "");
+ TEST_CHECK(ret == FLB_TRUE);
+
+ ret = mp_kv_exists(res_data, res_size, "$entries[0]['httpRequest']['latency']");
+ TEST_CHECK(ret == FLB_FALSE);
+
+ ret = mp_kv_cmp_integer(res_data, res_size, "$entries[0]['httpRequest']['requestSize']", 0);
+ TEST_CHECK(ret == FLB_TRUE);
+
+ ret = mp_kv_cmp_integer(res_data, res_size, "$entries[0]['httpRequest']['responseSize']", 0);
+ TEST_CHECK(ret == FLB_TRUE);
+
+ ret = mp_kv_cmp_integer(res_data, res_size, "$entries[0]['httpRequest']['status']", 0);
+ TEST_CHECK(ret == FLB_TRUE);
+
+ ret = mp_kv_cmp_integer(res_data, res_size, "$entries[0]['httpRequest']['cacheFillBytes']", 0);
+ TEST_CHECK(ret == FLB_TRUE);
+
+ ret = mp_kv_cmp_boolean(res_data, res_size, "$entries[0]['httpRequest']['cacheLookup']", false);
+ TEST_CHECK(ret == FLB_TRUE);
+
+ ret = mp_kv_cmp_boolean(res_data, res_size, "$entries[0]['httpRequest']['cacheHit']", false);
+ TEST_CHECK(ret == FLB_TRUE);
+
+ ret = mp_kv_cmp_boolean(res_data, res_size, "$entries[0]['httpRequest']['cacheValidatedWithOriginServer']", false);
+ TEST_CHECK(ret == FLB_TRUE);
+
+ /* check `httpRequest` has been removed from jsonPayload */
+ ret = mp_kv_exists(res_data, res_size, "$entries[0]['jsonPayload']['logging.googleapis.com/http_request']");
+ TEST_CHECK(ret == FLB_FALSE);
+
+ flb_sds_destroy(res_data);
+}
+
+static void cb_check_http_request_extra_subfields(void *ctx, int ffd,
+ int res_ret, void *res_data, size_t res_size,
+ void *data)
+{
+ int ret;
+
+ ret = mp_kv_cmp(res_data, res_size, "$entries[0]['httpRequest']['requestMethod']", "test_requestMethod");
+ TEST_CHECK(ret == FLB_TRUE);
+
+ ret = mp_kv_cmp(res_data, res_size, "$entries[0]['httpRequest']['requestUrl']", "test_requestUrl");
+ TEST_CHECK(ret == FLB_TRUE);
+
+ ret = mp_kv_cmp(res_data, res_size, "$entries[0]['httpRequest']['userAgent']", "test_userAgent");
+ TEST_CHECK(ret == FLB_TRUE);
+
+ ret = mp_kv_cmp(res_data, res_size, "$entries[0]['httpRequest']['remoteIp']", "test_remoteIp");
+ TEST_CHECK(ret == FLB_TRUE);
+
+ ret = mp_kv_cmp(res_data, res_size, "$entries[0]['httpRequest']['serverIp']", "test_serverIp");
+ TEST_CHECK(ret == FLB_TRUE);
+
+ ret = mp_kv_cmp(res_data, res_size, "$entries[0]['httpRequest']['referer']", "test_referer");
+ TEST_CHECK(ret == FLB_TRUE);
+
+ ret = mp_kv_cmp(res_data, res_size, "$entries[0]['httpRequest']['protocol']", "test_protocol");
+ TEST_CHECK(ret == FLB_TRUE);
+
+ ret = mp_kv_cmp(res_data, res_size, "$entries[0]['httpRequest']['latency']", "0s");
+ TEST_CHECK(ret == FLB_TRUE);
+
+ ret = mp_kv_cmp_integer(res_data, res_size, "$entries[0]['httpRequest']['requestSize']", 123);
+ TEST_CHECK(ret == FLB_TRUE);
+
+ ret = mp_kv_cmp_integer(res_data, res_size, "$entries[0]['httpRequest']['responseSize']", 123);
+ TEST_CHECK(ret == FLB_TRUE);
+
+ ret = mp_kv_cmp_integer(res_data, res_size, "$entries[0]['httpRequest']['status']", 200);
+ TEST_CHECK(ret == FLB_TRUE);
+
+ ret = mp_kv_cmp_integer(res_data, res_size, "$entries[0]['httpRequest']['cacheFillBytes']", 123);
+ TEST_CHECK(ret == FLB_TRUE);
+
+ ret = mp_kv_cmp_boolean(res_data, res_size, "$entries[0]['httpRequest']['cacheLookup']", true);
+ TEST_CHECK(ret == FLB_TRUE);
+
+ ret = mp_kv_cmp_boolean(res_data, res_size, "$entries[0]['httpRequest']['cacheHit']", true);
+ TEST_CHECK(ret == FLB_TRUE);
+
+ ret = mp_kv_cmp_boolean(res_data, res_size, "$entries[0]['httpRequest']['cacheValidatedWithOriginServer']", true);
+ TEST_CHECK(ret == FLB_TRUE);
+
+ /* Preserve extra subfields inside jsonPayload */
+ ret = mp_kv_cmp(res_data, res_size, "$entries[0]['jsonPayload']['logging.googleapis.com/http_request']['extra_key1']", "extra_val1");
+ TEST_CHECK(ret == FLB_TRUE);
+
+ ret = mp_kv_cmp_integer(res_data, res_size, "$entries[0]['jsonPayload']['logging.googleapis.com/http_request']['extra_key2']", 123);
+ TEST_CHECK(ret == FLB_TRUE);
+
+ ret = mp_kv_cmp_boolean(res_data, res_size, "$entries[0]['jsonPayload']['logging.googleapis.com/http_request']['extra_key3']", true);
+ TEST_CHECK(ret == FLB_TRUE);
+
+ flb_sds_destroy(res_data);
+}
+
+static void cb_check_http_request_lantency_common_case(void *ctx, int ffd,
+ int res_ret, void *res_data, size_t res_size,
+ void *data)
+{
+ int ret;
+
+ ret = mp_kv_cmp(res_data, res_size, "$entries[0]['httpRequest']['latency']", "100.00s");
+ TEST_CHECK(ret == FLB_TRUE);
+
+ /* check `httpRequest` has been removed from jsonPayload */
+ ret = mp_kv_exists(res_data, res_size, "$entries[0]['jsonPayload']['logging.googleapis.com/http_request']");
+ TEST_CHECK(ret == FLB_FALSE);
+
+ flb_sds_destroy(res_data);
+}
+
+static void cb_check_http_request_latency_incorrect_format(void *ctx, int ffd,
+ int res_ret, void *res_data, size_t res_size,
+ void *data)
+{
+ int ret;
+
+ ret = mp_kv_exists(res_data, res_size, "$entries[0]['httpRequest']['latency']");
+ TEST_CHECK(ret == FLB_FALSE);
+
+ /* check `httpRequest` has been removed from jsonPayload */
+ ret = mp_kv_exists(res_data, res_size, "$entries[0]['jsonPayload']['logging.googleapis.com/http_request']");
+ TEST_CHECK(ret == FLB_FALSE);
+
+ flb_sds_destroy(res_data);
+}
+
+static void cb_check_timestamp_format_object_common_case(void *ctx, int ffd,
+ int res_ret, void *res_data, size_t res_size,
+ void *data)
+{
+ int ret;
+
+ ret = mp_kv_cmp(res_data, res_size, "$entries[0]['timestamp']", "2020-07-21T16:40:42.000012345Z");
+ TEST_CHECK(ret == FLB_TRUE);
+
+ /* check `timestamp` has been removed from jsonPayload */
+ ret = mp_kv_exists(res_data, res_size, "$entries[0]['jsonPayload']['timestamp']");
+ TEST_CHECK(ret == FLB_FALSE);
+
+ flb_sds_destroy(res_data);
+}
+
+static void cb_check_timestamp_format_object_not_a_map(void *ctx, int ffd,
+ int res_ret, void *res_data, size_t res_size,
+ void *data)
+{
+ int ret;
+
+ ret = mp_kv_cmp(res_data, res_size, "$entries[0]['timestamp']", "2020-07-21T16:40:00.000000000Z");
+ TEST_CHECK(ret == FLB_TRUE);
+
+ ret = mp_kv_cmp(res_data, res_size, "$entries[0]['jsonPayload']['timestamp']", "string");
+ TEST_CHECK(ret == FLB_TRUE);
+
+ flb_sds_destroy(res_data);
+}
+
+static void cb_check_timestamp_format_object_missing_subfield(void *ctx, int ffd,
+ int res_ret, void *res_data,
+ size_t res_size,
+ void *data)
+{
+ int ret;
+
+ ret = mp_kv_cmp(res_data, res_size, "$entries[0]['timestamp']", "2020-07-21T16:40:00.000000000Z");
+ TEST_CHECK(ret == FLB_TRUE);
+
+ ret = mp_kv_cmp(res_data, res_size, "$entries[0]['jsonPayload']['timestamp']['nanos']", "12345");
+ TEST_CHECK(ret == FLB_TRUE);
+
+ flb_sds_destroy(res_data);
+}
+
+static void cb_check_timestamp_format_object_incorrect_subfields(void *ctx, int ffd,
+ int res_ret, void *res_data,
+ size_t res_size,
+ void *data)
+{
+ int ret;
+
+ ret = mp_kv_cmp(res_data, res_size, "$entries[0]['timestamp']", "2020-07-21T16:40:00.000000000Z");
+ TEST_CHECK(ret == FLB_TRUE);
+
+ /* check `timestamp` has been removed from jsonPayload */
+ ret = mp_kv_exists(res_data, res_size, "$entries[0]['jsonPayload']['timestamp']");
+ TEST_CHECK(ret == FLB_FALSE);
+
+ flb_sds_destroy(res_data);
+}
+
+static void cb_check_timestamp_format_duo_fields_common_case(void *ctx, int ffd,
+ int res_ret, void *res_data, size_t res_size,
+ void *data)
+{
+ int ret;
+
+ ret = mp_kv_cmp(res_data, res_size, "$entries[0]['timestamp']", "2020-07-21T16:40:42.000012345Z");
+ TEST_CHECK(ret == FLB_TRUE);
+
+ /* check `timestampSeconds` has been removed from jsonPayload */
+ ret = mp_kv_exists(res_data, res_size, "$entries[0]['jsonPayload']['timestampSeconds']");
+ TEST_CHECK(ret == FLB_FALSE);
+
+ /* check `timestampNanos` has been removed from jsonPayload */
+ ret = mp_kv_exists(res_data, res_size, "$entries[0]['jsonPayload']['timestampNanos']");
+ TEST_CHECK(ret == FLB_FALSE);
+
+ flb_sds_destroy(res_data);
+}
+
+static void cb_check_timestamp_format_duo_fields_missing_nanos(void *ctx, int ffd,
+ int res_ret, void *res_data, size_t res_size,
+ void *data)
+{
+ int ret;
+
+ ret = mp_kv_cmp(res_data, res_size, "$entries[0]['timestamp']", "2020-07-21T16:40:00.000000000Z");
+ TEST_CHECK(ret == FLB_TRUE);
+
+ ret = mp_kv_cmp(res_data, res_size, "$entries[0]['jsonPayload']['timestampSeconds']", "1595349642");
+ TEST_CHECK(ret == FLB_TRUE);
+
+ flb_sds_destroy(res_data);
+}
+
+static void cb_check_timestamp_format_duo_fields_incorrect_type(void *ctx, int ffd,
+ int res_ret, void *res_data, size_t res_size,
+ void *data)
+{
+ int ret;
+
+ ret = mp_kv_cmp(res_data, res_size, "$entries[0]['timestamp']", "2020-07-21T16:40:00.000000000Z");
+ TEST_CHECK(ret == FLB_TRUE);
+
+ /* check `timestampSeconds` has been removed from jsonPayload */
+ ret = mp_kv_exists(res_data, res_size, "$entries[0]['jsonPayload']['timestampSeconds']");
+ TEST_CHECK(ret == FLB_FALSE);
+
+ /* check `timestampNanos` has been removed from jsonPayload */
+ ret = mp_kv_exists(res_data, res_size, "$entries[0]['jsonPayload']['timestampNanos']");
+ TEST_CHECK(ret == FLB_FALSE);
+
+ flb_sds_destroy(res_data);
+}
+
+void flb_test_monitored_resource_common()
+{
+ int ret;
+ int size = sizeof(MONITORED_RESOURCE_COMMON_CASE) - 1;
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+
+ /* Create context, flush every second (some checks omitted here) */
+ ctx = flb_create();
+ flb_service_set(ctx, "flush", "1", "grace", "1", NULL);
+
+ /* Lib input mode */
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ flb_input_set(ctx, in_ffd, "tag", "test", NULL);
+
+ /* Stackdriver output */
+ out_ffd = flb_output(ctx, (char *) "stackdriver", NULL);
+ flb_output_set(ctx, out_ffd,
+ "match", "test",
+ "google_service_credentials", SERVICE_CREDENTIALS,
+ "resource", "monitored_resource",
+ NULL);
+
+ /* Enable test mode */
+ ret = flb_output_set_test(ctx, out_ffd, "formatter",
+ cb_check_monitored_resource,
+ NULL, NULL);
+
+ /* Start */
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data sample */
+ flb_lib_push(ctx, in_ffd, (char *) MONITORED_RESOURCE_COMMON_CASE, size);
+
+ sleep(2);
+ flb_stop(ctx);
+ flb_destroy(ctx);
+}
+
+void flb_test_monitored_resource_priority_higher_than_local_resource_id()
+{
+ int ret;
+ int size = sizeof(MONITORED_RESOURCE_PRIORITY_HIGHER_THAN_LOCAL_RESOURCE_ID) - 1;
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+
+ /* Create context, flush every second (some checks omitted here) */
+ ctx = flb_create();
+ flb_service_set(ctx, "flush", "1", "grace", "1", NULL);
+
+ /* Lib input mode */
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ flb_input_set(ctx, in_ffd, "tag", "test", NULL);
+
+ /* Stackdriver output */
+ out_ffd = flb_output(ctx, (char *) "stackdriver", NULL);
+ flb_output_set(ctx, out_ffd,
+ "match", "test",
+ "google_service_credentials", SERVICE_CREDENTIALS,
+ "resource", "k8s_container",
+ "k8s_cluster_name", "test_cluster_name",
+ "k8s_cluster_location", "test_cluster_location",
+ NULL);
+
+ /* Enable test mode */
+ ret = flb_output_set_test(ctx, out_ffd, "formatter",
+ cb_check_monitored_resource_priority_higher_than_local_resource_id,
+ NULL, NULL);
+
+ /* Start */
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data sample */
+ flb_lib_push(ctx, in_ffd, (char *) MONITORED_RESOURCE_PRIORITY_HIGHER_THAN_LOCAL_RESOURCE_ID, size);
+
+ sleep(2);
+ flb_stop(ctx);
+ flb_destroy(ctx);
+}
+
+void flb_test_monitored_resource_priority_higher_than_gce_instance()
+{
+ int ret;
+ int size = sizeof(MONITORED_RESOURCE_PRIORITY_HIGHER_THAN_GCE_INSTANCE) - 1;
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+
+ /* Create context, flush every second (some checks omitted here) */
+ ctx = flb_create();
+ flb_service_set(ctx, "flush", "1", "grace", "1", NULL);
+
+ /* Lib input mode */
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ flb_input_set(ctx, in_ffd, "tag", "test", NULL);
+
+ /* Stackdriver output */
+ out_ffd = flb_output(ctx, (char *) "stackdriver", NULL);
+ flb_output_set(ctx, out_ffd,
+ "match", "test",
+ "google_service_credentials", SERVICE_CREDENTIALS,
+ "resource", "gce_instance",
+ NULL);
+
+ /* Enable test mode */
+ ret = flb_output_set_test(ctx, out_ffd, "formatter",
+ cb_check_monitored_resource_priority_higher_than_gce_instance,
+ NULL, NULL);
+
+ /* Start */
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data sample */
+ flb_lib_push(ctx, in_ffd, (char *) MONITORED_RESOURCE_PRIORITY_HIGHER_THAN_GCE_INSTANCE, size);
+
+ sleep(2);
+ flb_stop(ctx);
+ flb_destroy(ctx);
+}
+
+void flb_test_resource_global()
+{
+ int ret;
+ int size = sizeof(JSON) - 1;
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+
+ /* Create context, flush every second (some checks omitted here) */
+ ctx = flb_create();
+ flb_service_set(ctx, "flush", "1", "grace", "1", NULL);
+
+ /* Lib input mode */
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ flb_input_set(ctx, in_ffd, "tag", "test", NULL);
+
+ /* Stackdriver output */
+ out_ffd = flb_output(ctx, (char *) "stackdriver", NULL);
+ flb_output_set(ctx, out_ffd,
+ "match", "test",
+ "google_service_credentials", SERVICE_CREDENTIALS,
+ "resource", "global",
+ NULL);
+
+ /* Enable test mode */
+ ret = flb_output_set_test(ctx, out_ffd, "formatter",
+ cb_check_global_resource,
+ NULL, NULL);
+
+ /* Start */
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data sample */
+ flb_lib_push(ctx, in_ffd, (char *) JSON, size);
+
+ sleep(2);
+ flb_stop(ctx);
+ flb_destroy(ctx);
+}
+
+void flb_test_trace_no_autoformat()
+{
+ int ret;
+ int size = sizeof(TRACE_COMMON_CASE) - 1;
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+
+ /* Create context, flush every second (some checks omitted here) */
+ ctx = flb_create();
+ flb_service_set(ctx, "flush", "1", "grace", "1", NULL);
+
+ /* Lib input mode */
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ flb_input_set(ctx, in_ffd, "tag", "test", NULL);
+
+ /* Stackdriver output */
+ out_ffd = flb_output(ctx, (char *) "stackdriver", NULL);
+ flb_output_set(ctx, out_ffd,
+ "match", "test",
+ "resource", "gce_instance",
+ "trace_key", "trace",
+ NULL);
+
+ /* Enable test mode */
+ ret = flb_output_set_test(ctx, out_ffd, "formatter",
+ cb_check_trace_no_autoformat,
+ NULL, NULL);
+
+ /* Start */
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data sample */
+ flb_lib_push(ctx, in_ffd, (char *) TRACE_COMMON_CASE, size);
+
+ sleep(2);
+ flb_stop(ctx);
+ flb_destroy(ctx);
+}
+
+void flb_test_trace_stackdriver_autoformat()
+{
+ int ret;
+ int size = sizeof(TRACE_COMMON_CASE) - 1;
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+
+ /* Create context, flush every second (some checks omitted here) */
+ ctx = flb_create();
+ flb_service_set(ctx, "flush", "1", "grace", "1", NULL);
+
+ /* Lib input mode */
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ flb_input_set(ctx, in_ffd, "tag", "test", NULL);
+
+ /* Stackdriver output */
+ out_ffd = flb_output(ctx, (char *) "stackdriver", NULL);
+ flb_output_set(ctx, out_ffd,
+ "match", "test",
+ "resource", "gce_instance",
+ "trace_key", "trace",
+ "autoformat_stackdriver_trace", "true",
+ NULL);
+
+ /* Enable test mode */
+ ret = flb_output_set_test(ctx, out_ffd, "formatter",
+ cb_check_trace_stackdriver_autoformat,
+ NULL, NULL);
+
+ /* Start */
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data sample */
+ flb_lib_push(ctx, in_ffd, (char *) TRACE_COMMON_CASE, size);
+
+ sleep(2);
+ flb_stop(ctx);
+ flb_destroy(ctx);
+}
+
+void flb_test_span_id()
+{
+ int ret;
+ int size = sizeof(SPAN_ID_COMMON_CASE) - 1;
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+
+ /* Create context, flush every second (some checks omitted here) */
+ ctx = flb_create();
+ flb_service_set(ctx, "flush", "1", "grace", "1", NULL);
+
+ /* Lib input mode */
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ flb_input_set(ctx, in_ffd, "tag", "test", NULL);
+
+ /* Stackdriver output */
+ out_ffd = flb_output(ctx, (char *) "stackdriver", NULL);
+ flb_output_set(ctx, out_ffd,
+ "match", "test",
+ "resource", "gce_instance",
+ NULL);
+
+ /* Enable test mode */
+ ret = flb_output_set_test(ctx, out_ffd, "formatter",
+ cb_check_span_id,
+ NULL, NULL);
+
+ /* Start */
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data sample */
+ flb_lib_push(ctx, in_ffd, (char *) SPAN_ID_COMMON_CASE, size);
+
+ sleep(2);
+ flb_stop(ctx);
+ flb_destroy(ctx);
+}
+
+void flb_test_trace_sampled_true()
+{
+ int ret;
+ int size = sizeof(TRACE_SAMPLED_CASE_TRUE) - 1;
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+
+ /* Create context, flush every second (some checks omitted here) */
+ ctx = flb_create();
+ flb_service_set(ctx, "flush", "1", "grace", "1", NULL);
+
+ /* Lib input mode */
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ flb_input_set(ctx, in_ffd, "tag", "test", NULL);
+
+ /* Stackdriver output */
+ out_ffd = flb_output(ctx, (char *) "stackdriver", NULL);
+ flb_output_set(ctx, out_ffd,
+ "match", "test",
+ "resource", "gce_instance",
+ NULL);
+
+ /* Enable test mode */
+ ret = flb_output_set_test(ctx, out_ffd, "formatter",
+ cb_check_trace_sampled_true,
+ NULL, NULL);
+
+ /* Start */
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data sample */
+ flb_lib_push(ctx, in_ffd, (char *) TRACE_SAMPLED_CASE_TRUE, size);
+
+ sleep(2);
+ flb_stop(ctx);
+ flb_destroy(ctx);
+}
+
+void flb_test_trace_sampled_false()
+{
+ int ret;
+ int size = sizeof(TRACE_SAMPLED_CASE_FALSE) - 1;
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+
+ /* Create context, flush every second (some checks omitted here) */
+ ctx = flb_create();
+ flb_service_set(ctx, "flush", "1", "grace", "1", NULL);
+
+ /* Lib input mode */
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ flb_input_set(ctx, in_ffd, "tag", "test", NULL);
+
+ /* Stackdriver output */
+ out_ffd = flb_output(ctx, (char *) "stackdriver", NULL);
+ flb_output_set(ctx, out_ffd,
+ "match", "test",
+ "resource", "gce_instance",
+ NULL);
+
+ /* Enable test mode */
+ ret = flb_output_set_test(ctx, out_ffd, "formatter",
+ cb_check_trace_sampled_false,
+ NULL, NULL);
+
+ /* Start */
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data sample */
+ flb_lib_push(ctx, in_ffd, (char *) TRACE_SAMPLED_CASE_FALSE, size);
+
+ sleep(2);
+ flb_stop(ctx);
+ flb_destroy(ctx);
+}
+
+void flb_test_set_metadata_server()
+{
+ int ret;
+ int size = sizeof(JSON) - 1;
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+
+ /* Create context, flush every second (some checks omitted here) */
+ ctx = flb_create();
+ flb_service_set(ctx, "flush", "1", "grace", "1", NULL);
+
+ /* Lib input mode */
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ flb_input_set(ctx, in_ffd, "tag", "test", NULL);
+
+ /* Stackdriver output */
+ out_ffd = flb_output(ctx, (char *) "stackdriver", NULL);
+ flb_output_set(ctx, out_ffd,
+ "match", "test",
+ "resource", "gce_instance",
+ "metadata_server", "http://metadata.google.internal",
+ NULL);
+
+ /* Enable test mode */
+ ret = flb_output_set_test(ctx, out_ffd, "formatter",
+ cb_check_gce_instance,
+ NULL, NULL);
+
+ /* Start */
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data sample */
+ flb_lib_push(ctx, in_ffd, (char *) JSON, size);
+
+ sleep(2);
+ flb_stop(ctx);
+ flb_destroy(ctx);
+}
+
+void flb_test_log_name_override()
+{
+ int ret;
+ int size = sizeof(LOG_NAME_OVERRIDE) - 1;
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+
+ /* Create context, flush every second (some checks omitted here) */
+ ctx = flb_create();
+ flb_service_set(ctx, "flush", "1", "grace", "1", NULL);
+
+ /* Lib input mode */
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ flb_input_set(ctx, in_ffd, "tag", "test", NULL);
+
+ /* Stackdriver output */
+ out_ffd = flb_output(ctx, (char *) "stackdriver", NULL);
+ flb_output_set(ctx, out_ffd,
+ "match", "test",
+ "resource", "gce_instance",
+ "log_name_key", "custom_log_name_key",
+ NULL);
+
+ /* Enable test mode */
+ ret = flb_output_set_test(ctx, out_ffd, "formatter",
+ cb_check_log_name_override,
+ NULL, NULL);
+
+ /* Start */
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data sample */
+ flb_lib_push(ctx, in_ffd, (char *) LOG_NAME_OVERRIDE, size);
+
+ sleep(2);
+ flb_stop(ctx);
+ flb_destroy(ctx);
+}
+
+void flb_test_log_name_no_override()
+{
+ int ret;
+ int size = sizeof(LOG_NAME_NO_OVERRIDE) - 1;
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+
+ /* Create context, flush every second (some checks omitted here) */
+ ctx = flb_create();
+ flb_service_set(ctx, "flush", "1", "grace", "1", NULL);
+
+ /* Lib input mode */
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ flb_input_set(ctx, in_ffd, "tag", "test", NULL);
+
+ /* Stackdriver output */
+ out_ffd = flb_output(ctx, (char *) "stackdriver", NULL);
+ flb_output_set(ctx, out_ffd,
+ "match", "test",
+ "resource", "gce_instance",
+ "log_name_key", "custom_log_name_key",
+ NULL);
+
+ /* Enable test mode */
+ ret = flb_output_set_test(ctx, out_ffd, "formatter",
+ cb_check_log_name_no_override,
+ NULL, NULL);
+
+ /* Start */
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data sample */
+ flb_lib_push(ctx, in_ffd, (char *) LOG_NAME_NO_OVERRIDE, size);
+
+ sleep(2);
+ flb_stop(ctx);
+ flb_destroy(ctx);
+}
+
+void flb_test_resource_global_custom_prefix()
+{
+ /* configuring tag_prefix for non-k8s resource type should have no effect at all */
+ int ret;
+ int size = sizeof(JSON) - 1;
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+
+ /* Create context, flush every second (some checks omitted here) */
+ ctx = flb_create();
+ flb_service_set(ctx, "flush", "1", "grace", "1", NULL);
+
+ /* Lib input mode */
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ flb_input_set(ctx, in_ffd, "tag", "test", NULL);
+
+ /* Stackdriver output */
+ out_ffd = flb_output(ctx, (char *) "stackdriver", NULL);
+ flb_output_set(ctx, out_ffd,
+ "match", "test",
+ "google_service_credentials", SERVICE_CREDENTIALS,
+ "resource", "global",
+ "tag_prefix", "custom_tag.",
+ NULL);
+
+ /* Enable test mode */
+ ret = flb_output_set_test(ctx, out_ffd, "formatter",
+ cb_check_global_resource,
+ NULL, NULL);
+
+ /* Start */
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data sample */
+ flb_lib_push(ctx, in_ffd, (char *) JSON, size);
+
+ sleep(2);
+ flb_stop(ctx);
+ flb_destroy(ctx);
+}
+
+void flb_test_resource_generic_node_creds()
+{
+ int ret;
+ int size = sizeof(JSON) - 1;
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+
+ /* Create context, flush every second (some checks omitted here) */
+ ctx = flb_create();
+ flb_service_set(ctx, "flush", "1", "grace", "1", NULL);
+
+ /* Lib input mode */
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ flb_input_set(ctx, in_ffd, "tag", "test", NULL);
+
+ /* Stackdriver output */
+ out_ffd = flb_output(ctx, (char *) "stackdriver", NULL);
+ flb_output_set(ctx, out_ffd,
+ "match", "test",
+ "google_service_credentials", SERVICE_CREDENTIALS,
+ "resource", "generic_node",
+ "location", "fluent",
+ "namespace", "test",
+ "node_id", "333222111",
+ NULL);
+
+ /* Enable test mode */
+ ret = flb_output_set_test(ctx, out_ffd, "formatter",
+ cb_check_generic_node_creds,
+ NULL, NULL);
+
+ /* Start */
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data sample */
+ flb_lib_push(ctx, in_ffd, (char *) JSON, size);
+
+ sleep(2);
+ flb_stop(ctx);
+ flb_destroy(ctx);
+}
+
+void flb_test_resource_generic_node_metadata()
+{
+ int ret;
+ int size = sizeof(JSON) - 1;
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+
+ /* Create context, flush every second (some checks omitted here) */
+ ctx = flb_create();
+ flb_service_set(ctx, "flush", "1", "grace", "1", NULL);
+
+ /* Lib input mode */
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ flb_input_set(ctx, in_ffd, "tag", "test", NULL);
+
+ /* Stackdriver output */
+ out_ffd = flb_output(ctx, (char *) "stackdriver", NULL);
+ flb_output_set(ctx, out_ffd,
+ "match", "test",
+ "resource", "generic_node",
+ "location", "fluent",
+ "namespace", "test",
+ "node_id", "333222111",
+ NULL);
+
+ /* Enable test mode */
+ ret = flb_output_set_test(ctx, out_ffd, "formatter",
+ cb_check_generic_node_metadata,
+ NULL, NULL);
+
+ /* Start */
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data sample */
+ flb_lib_push(ctx, in_ffd, (char *) JSON, size);
+
+ sleep(2);
+ flb_stop(ctx);
+ flb_destroy(ctx);
+}
+
+void flb_test_resource_generic_task_creds()
+{
+ int ret;
+ int size = sizeof(JSON) - 1;
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+
+ /* Create context, flush every second (some checks omitted here) */
+ ctx = flb_create();
+ flb_service_set(ctx, "flush", "1", "grace", "1", NULL);
+
+ /* Lib input mode */
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ flb_input_set(ctx, in_ffd, "tag", "test", NULL);
+
+ /* Stackdriver output */
+ out_ffd = flb_output(ctx, (char *) "stackdriver", NULL);
+ flb_output_set(ctx, out_ffd,
+ "match", "test",
+ "google_service_credentials", SERVICE_CREDENTIALS,
+ "resource", "generic_task",
+ "location", "fluent",
+ "namespace", "test",
+ "job", "test-job",
+ "task_id", "333222111",
+ NULL);
+
+ /* Enable test mode */
+ ret = flb_output_set_test(ctx, out_ffd, "formatter",
+ cb_check_generic_task_creds,
+ NULL, NULL);
+
+ /* Start */
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data sample */
+ flb_lib_push(ctx, in_ffd, (char *) JSON, size);
+
+ sleep(2);
+ flb_stop(ctx);
+ flb_destroy(ctx);
+}
+
+void flb_test_resource_generic_task_metadata()
+{
+ int ret;
+ int size = sizeof(JSON) - 1;
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+
+ /* Create context, flush every second (some checks omitted here) */
+ ctx = flb_create();
+ flb_service_set(ctx, "flush", "1", "grace", "1", NULL);
+
+ /* Lib input mode */
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ flb_input_set(ctx, in_ffd, "tag", "test", NULL);
+
+ /* Stackdriver output */
+ out_ffd = flb_output(ctx, (char *) "stackdriver", NULL);
+ flb_output_set(ctx, out_ffd,
+ "match", "test",
+ "resource", "generic_task",
+ "location", "fluent",
+ "namespace", "test",
+ "job", "test-job",
+ "task_id", "333222111",
+ NULL);
+
+ /* Enable test mode */
+ ret = flb_output_set_test(ctx, out_ffd, "formatter",
+ cb_check_generic_task_metadata,
+ NULL, NULL);
+
+ /* Start */
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data sample */
+ flb_lib_push(ctx, in_ffd, (char *) JSON, size);
+
+ sleep(2);
+ flb_stop(ctx);
+ flb_destroy(ctx);
+}
+
+void flb_test_resource_gce_instance()
+{
+ int ret;
+ int size = sizeof(JSON) - 1;
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+
+ /* Create context, flush every second (some checks omitted here) */
+ ctx = flb_create();
+ flb_service_set(ctx, "flush", "1", "grace", "1", NULL);
+
+ /* Lib input mode */
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ flb_input_set(ctx, in_ffd, "tag", "test", NULL);
+
+ /* Stackdriver output */
+ out_ffd = flb_output(ctx, (char *) "stackdriver", NULL);
+ flb_output_set(ctx, out_ffd,
+ "match", "test",
+ "resource", "gce_instance",
+ NULL);
+
+ /* Enable test mode */
+ ret = flb_output_set_test(ctx, out_ffd, "formatter",
+ cb_check_gce_instance,
+ NULL, NULL);
+
+ /* Start */
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data sample */
+ flb_lib_push(ctx, in_ffd, (char *) JSON, size);
+
+ sleep(2);
+ flb_stop(ctx);
+ flb_destroy(ctx);
+}
+
+void flb_test_insert_id_common_case()
+{
+ int ret;
+ int size = sizeof(INSERTID_COMMON_CASE) - 1;
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+
+ /* Create context, flush every second (some checks omitted here) */
+ ctx = flb_create();
+ flb_service_set(ctx, "flush", "1", "grace", "1", NULL);
+
+ /* Lib input mode */
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ flb_input_set(ctx, in_ffd, "tag", "test", NULL);
+
+ /* Stackdriver output */
+ out_ffd = flb_output(ctx, (char *) "stackdriver", NULL);
+ flb_output_set(ctx, out_ffd,
+ "match", "test",
+ "resource", "gce_instance",
+ NULL);
+
+ /* Enable test mode */
+ ret = flb_output_set_test(ctx, out_ffd, "formatter",
+ cb_check_insert_id_common_case,
+ NULL, NULL);
+
+ /* Start */
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data sample */
+ flb_lib_push(ctx, in_ffd, (char *) INSERTID_COMMON_CASE, size);
+
+ sleep(2);
+ flb_stop(ctx);
+ flb_destroy(ctx);
+}
+
+void flb_test_empty_insert_id()
+{
+ int ret;
+ int size = sizeof(EMPTY_INSERTID) - 1;
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+
+ /* Create context, flush every second (some checks omitted here) */
+ ctx = flb_create();
+ flb_service_set(ctx, "flush", "1", "grace", "1", NULL);
+
+ /* Lib input mode */
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ flb_input_set(ctx, in_ffd, "tag", "test", NULL);
+
+ /* Stackdriver output */
+ out_ffd = flb_output(ctx, (char *) "stackdriver", NULL);
+ flb_output_set(ctx, out_ffd,
+ "match", "test",
+ "resource", "gce_instance",
+ NULL);
+
+ /* Enable test mode */
+ ret = flb_output_set_test(ctx, out_ffd, "formatter",
+ cb_check_empty_insert_id,
+ NULL, NULL);
+
+ /* Start */
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data sample */
+ flb_lib_push(ctx, in_ffd, (char *) EMPTY_INSERTID, size);
+
+ sleep(2);
+ flb_stop(ctx);
+ flb_destroy(ctx);
+}
+
+void flb_test_insert_id_incorrect_type()
+{
+ int ret;
+ int size = sizeof(INSERTID_INCORRECT_TYPE_INT) - 1;
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+
+ /* Create context, flush every second (some checks omitted here) */
+ ctx = flb_create();
+ flb_service_set(ctx, "flush", "1", "grace", "1", NULL);
+
+ /* Lib input mode */
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ flb_input_set(ctx, in_ffd, "tag", "test", NULL);
+
+ /* Stackdriver output */
+ out_ffd = flb_output(ctx, (char *) "stackdriver", NULL);
+ flb_output_set(ctx, out_ffd,
+ "match", "test",
+ "resource", "gce_instance",
+ NULL);
+
+ /* Enable test mode */
+ ret = flb_output_set_test(ctx, out_ffd, "formatter",
+ cb_check_insert_id_incorrect_type,
+ NULL, NULL);
+
+ /* Start */
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data sample */
+ flb_lib_push(ctx, in_ffd, (char *) INSERTID_INCORRECT_TYPE_INT, size);
+
+ sleep(2);
+ flb_stop(ctx);
+ flb_destroy(ctx);
+}
+
+void flb_test_operation_common()
+{
+ int ret;
+ int size = sizeof(OPERATION_COMMON_CASE) - 1;
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+
+ /* Create context, flush every second (some checks omitted here) */
+ ctx = flb_create();
+ flb_service_set(ctx, "flush", "1", "grace", "1", NULL);
+
+ /* Lib input mode */
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ flb_input_set(ctx, in_ffd, "tag", "test", NULL);
+
+ /* Stackdriver output */
+ out_ffd = flb_output(ctx, (char *) "stackdriver", NULL);
+ flb_output_set(ctx, out_ffd,
+ "match", "test",
+ "resource", "gce_instance",
+ NULL);
+
+ /* Enable test mode */
+ ret = flb_output_set_test(ctx, out_ffd, "formatter",
+ cb_check_operation_common_case,
+ NULL, NULL);
+
+ /* Start */
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data sample */
+ flb_lib_push(ctx, in_ffd, (char *) OPERATION_COMMON_CASE, size);
+
+ sleep(2);
+ flb_stop(ctx);
+ flb_destroy(ctx);
+}
+
+void flb_test_empty_operation()
+{
+ int ret;
+ int size = sizeof(EMPTY_OPERATION) - 1;
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+
+ /* Create context, flush every second (some checks omitted here) */
+ ctx = flb_create();
+ flb_service_set(ctx, "flush", "1", "grace", "1", NULL);
+
+ /* Lib input mode */
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ flb_input_set(ctx, in_ffd, "tag", "test", NULL);
+
+ /* Stackdriver output */
+ out_ffd = flb_output(ctx, (char *) "stackdriver", NULL);
+ flb_output_set(ctx, out_ffd,
+ "match", "test",
+ "resource", "gce_instance",
+ NULL);
+
+ /* Enable test mode */
+ ret = flb_output_set_test(ctx, out_ffd, "formatter",
+ cb_check_empty_operation,
+ NULL, NULL);
+
+ /* Start */
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data sample */
+ flb_lib_push(ctx, in_ffd, (char *) EMPTY_OPERATION, size);
+
+ sleep(2);
+ flb_stop(ctx);
+ flb_destroy(ctx);
+}
+
+void flb_test_operation_in_string()
+{
+ int ret;
+ int size = sizeof(OPERATION_IN_STRING) - 1;
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+
+ /* Create context, flush every second (some checks omitted here) */
+ ctx = flb_create();
+ flb_service_set(ctx, "flush", "1", "grace", "1", NULL);
+
+ /* Lib input mode */
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ flb_input_set(ctx, in_ffd, "tag", "test", NULL);
+
+ /* Stackdriver output */
+ out_ffd = flb_output(ctx, (char *) "stackdriver", NULL);
+ flb_output_set(ctx, out_ffd,
+ "match", "test",
+ "resource", "gce_instance",
+ NULL);
+
+ /* Enable test mode */
+ ret = flb_output_set_test(ctx, out_ffd, "formatter",
+ cb_check_operation_in_string,
+ NULL, NULL);
+
+ /* Start */
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data sample */
+ flb_lib_push(ctx, in_ffd, (char *) OPERATION_IN_STRING, size);
+
+ sleep(2);
+ flb_stop(ctx);
+ flb_destroy(ctx);
+}
+
+void flb_test_operation_partial_subfields()
+{
+ int ret;
+ int size = sizeof(PARTIAL_SUBFIELDS) - 1;
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+
+ /* Create context, flush every second (some checks omitted here) */
+ ctx = flb_create();
+ flb_service_set(ctx, "flush", "1", "grace", "1", NULL);
+
+ /* Lib input mode */
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ flb_input_set(ctx, in_ffd, "tag", "test", NULL);
+
+ /* Stackdriver output */
+ out_ffd = flb_output(ctx, (char *) "stackdriver", NULL);
+ flb_output_set(ctx, out_ffd,
+ "match", "test",
+ "resource", "gce_instance",
+ NULL);
+
+ /* Enable test mode */
+ ret = flb_output_set_test(ctx, out_ffd, "formatter",
+ cb_check_operation_partial_subfields,
+ NULL, NULL);
+
+ /* Start */
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data sample */
+ flb_lib_push(ctx, in_ffd, (char *) PARTIAL_SUBFIELDS, size);
+
+ sleep(2);
+ flb_stop(ctx);
+ flb_destroy(ctx);
+}
+
+void flb_test_operation_incorrect_type_subfields()
+{
+ int ret;
+ int size = sizeof(SUBFIELDS_IN_INCORRECT_TYPE) - 1;
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+
+ /* Create context, flush every second (some checks omitted here) */
+ ctx = flb_create();
+ flb_service_set(ctx, "flush", "1", "grace", "1", NULL);
+
+ /* Lib input mode */
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ flb_input_set(ctx, in_ffd, "tag", "test", NULL);
+
+ /* Stackdriver output */
+ out_ffd = flb_output(ctx, (char *) "stackdriver", NULL);
+ flb_output_set(ctx, out_ffd,
+ "match", "test",
+ "resource", "gce_instance",
+ NULL);
+
+ /* Enable test mode */
+ ret = flb_output_set_test(ctx, out_ffd, "formatter",
+ cb_check_operation_incorrect_type_subfields,
+ NULL, NULL);
+
+ /* Start */
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data sample */
+ flb_lib_push(ctx, in_ffd, (char *) SUBFIELDS_IN_INCORRECT_TYPE, size);
+
+ sleep(2);
+ flb_stop(ctx);
+ flb_destroy(ctx);
+}
+
+void flb_test_operation_extra_subfields()
+{
+ int ret;
+ int size = sizeof(EXTRA_SUBFIELDS_EXISTED) - 1;
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+
+ /* Create context, flush every second (some checks omitted here) */
+ ctx = flb_create();
+ flb_service_set(ctx, "flush", "1", "grace", "1", NULL);
+
+ /* Lib input mode */
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ flb_input_set(ctx, in_ffd, "tag", "test", NULL);
+
+ /* Stackdriver output */
+ out_ffd = flb_output(ctx, (char *) "stackdriver", NULL);
+ flb_output_set(ctx, out_ffd,
+ "match", "test",
+ "resource", "gce_instance",
+ NULL);
+
+ /* Enable test mode */
+ ret = flb_output_set_test(ctx, out_ffd, "formatter",
+ cb_check_operation_extra_subfields,
+ NULL, NULL);
+
+ /* Start */
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data sample */
+ flb_lib_push(ctx, in_ffd, (char *) EXTRA_SUBFIELDS_EXISTED, size);
+
+ sleep(2);
+ flb_stop(ctx);
+ flb_destroy(ctx);
+}
+
+void flb_test_resource_k8s_container_common()
+{
+ int ret;
+ int size = sizeof(K8S_CONTAINER_COMMON) - 1;
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+
+ /* Create context, flush every second (some checks omitted here) */
+ ctx = flb_create();
+ flb_service_set(ctx, "flush", "1", "grace", "1", NULL);
+
+ /* Lib input mode */
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ flb_input_set(ctx, in_ffd, "tag", "test", NULL);
+
+ /* Stackdriver output */
+ out_ffd = flb_output(ctx, (char *) "stackdriver", NULL);
+ flb_output_set(ctx, out_ffd,
+ "match", "test",
+ "resource", "k8s_container",
+ "google_service_credentials", SERVICE_CREDENTIALS,
+ "k8s_cluster_name", "test_cluster_name",
+ "k8s_cluster_location", "test_cluster_location",
+ NULL);
+
+ /* Enable test mode */
+ ret = flb_output_set_test(ctx, out_ffd, "formatter",
+ cb_check_k8s_container_resource,
+ NULL, NULL);
+
+ /* Start */
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data sample */
+ flb_lib_push(ctx, in_ffd, (char *) K8S_CONTAINER_COMMON, size);
+
+ sleep(2);
+ flb_stop(ctx);
+ flb_destroy(ctx);
+}
+
+void flb_test_resource_k8s_container_multi_tag_value()
+{
+ int ret;
+ int size_one = sizeof(K8S_CONTAINER_COMMON) - 1;
+ int size_two = sizeof(K8S_CONTAINER_COMMON_DIFF_TAGS) - 1;
+
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+
+ /* Create context, flush every second (some checks omitted here) */
+ ctx = flb_create();
+ flb_service_set(ctx, "flush", "1", "grace", "1", NULL);
+
+ /* Lib input mode */
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ flb_input_set(ctx, in_ffd, "tag", "test", NULL);
+
+ /* Stackdriver output */
+ out_ffd = flb_output(ctx, (char *) "stackdriver", NULL);
+ flb_output_set(ctx, out_ffd,
+ "match", "test",
+ "resource", "k8s_container",
+ "google_service_credentials", SERVICE_CREDENTIALS,
+ "k8s_cluster_name", "test_cluster_name",
+ "k8s_cluster_location", "test_cluster_location",
+ NULL);
+
+ /* Enable test mode */
+ ret = flb_output_set_test(ctx, out_ffd, "formatter",
+ cb_check_k8s_container_resource,
+ NULL, NULL);
+
+ /* Start */
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data sample */
+ flb_lib_push(ctx, in_ffd, (char *) K8S_CONTAINER_COMMON_DIFF_TAGS, size_one);
+
+ /* Enable test mode */
+ ret = flb_output_set_test(ctx, out_ffd, "formatter",
+ cb_check_k8s_container_resource_diff_tag,
+ NULL, NULL);
+
+ /* Ingest data sample */
+ flb_lib_push(ctx, in_ffd, (char *) K8S_CONTAINER_COMMON_DIFF_TAGS, size_two);
+
+
+ sleep(2);
+ flb_stop(ctx);
+ flb_destroy(ctx);
+}
+
+void flb_test_resource_k8s_container_custom_tag_prefix()
+{
+ int ret;
+ int size = sizeof(K8S_CONTAINER_NO_LOCAL_RESOURCE_ID) - 1;
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+
+ /* Create context, flush every second (some checks omitted here) */
+ ctx = flb_create();
+ flb_service_set(ctx, "flush", "1", "grace", "1", NULL);
+
+ /* Lib input mode */
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ flb_input_set(ctx, in_ffd, "tag", "kube_custom_tag.testnamespace.testpod.testctr", NULL);
+
+ /* Stackdriver output */
+ out_ffd = flb_output(ctx, (char *) "stackdriver", NULL);
+ flb_output_set(ctx, out_ffd,
+ "match", "kube_custom_tag.*",
+ "resource", "k8s_container",
+ "google_service_credentials", SERVICE_CREDENTIALS,
+ "k8s_cluster_name", "test_cluster_name",
+ "k8s_cluster_location", "test_cluster_location",
+ "tag_prefix", "kube_custom_tag.",
+ NULL);
+
+ /* Enable test mode */
+ ret = flb_output_set_test(ctx, out_ffd, "formatter",
+ cb_check_k8s_container_resource,
+ NULL, NULL);
+
+ /* Start */
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data sample */
+ flb_lib_push(ctx, in_ffd, (char *) K8S_CONTAINER_NO_LOCAL_RESOURCE_ID, size);
+
+ sleep(2);
+ flb_stop(ctx);
+ flb_destroy(ctx);
+}
+
+void flb_test_resource_k8s_container_custom_tag_prefix_with_dot()
+{
+ int ret;
+ int size = sizeof(K8S_CONTAINER_NO_LOCAL_RESOURCE_ID) - 1;
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+
+ /* Create context, flush every second (some checks omitted here) */
+ ctx = flb_create();
+ flb_service_set(ctx, "flush", "1", "grace", "1", NULL);
+
+ /* Lib input mode */
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ flb_input_set(ctx, in_ffd, "tag", "kube.custom.tag.testnamespace.testpod.testctr", NULL);
+
+ /* Stackdriver output */
+ out_ffd = flb_output(ctx, (char *) "stackdriver", NULL);
+ flb_output_set(ctx, out_ffd,
+ "match", "kube.custom.tag.*",
+ "resource", "k8s_container",
+ "google_service_credentials", SERVICE_CREDENTIALS,
+ "k8s_cluster_name", "test_cluster_name",
+ "k8s_cluster_location", "test_cluster_location",
+ "tag_prefix", "kube.custom.tag.",
+ NULL);
+
+ /* Enable test mode */
+ ret = flb_output_set_test(ctx, out_ffd, "formatter",
+ cb_check_k8s_container_resource,
+ NULL, NULL);
+
+ /* Start */
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data sample */
+ flb_lib_push(ctx, in_ffd, (char *) K8S_CONTAINER_NO_LOCAL_RESOURCE_ID, size);
+
+ sleep(2);
+ flb_stop(ctx);
+ flb_destroy(ctx);
+}
+
+void flb_test_resource_k8s_container_default_tag_regex()
+{
+ int ret;
+ int size = sizeof(K8S_CONTAINER_NO_LOCAL_RESOURCE_ID) - 1;
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+
+ /* Create context, flush every second (some checks omitted here) */
+ ctx = flb_create();
+ flb_service_set(ctx, "flush", "1", "grace", "1", NULL);
+
+ /* Lib input mode */
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ flb_input_set(ctx, in_ffd, "tag",
+ "kube.var.log.containers.apache-logs-annotated_default_apache-aeeccc7a9f00f6e4e066aeff0434cf80621215071f1b20a51e8340aa7c35eac6.log", NULL);
+
+ /* Stackdriver output */
+ out_ffd = flb_output(ctx, (char *) "stackdriver", NULL);
+ flb_output_set(ctx, out_ffd,
+ "match", "kube.custom.tag.*",
+ "resource", "k8s_container",
+ "google_service_credentials", SERVICE_CREDENTIALS,
+ "k8s_cluster_name", "test_cluster_name",
+ "k8s_cluster_location", "test_cluster_location",
+ "tag_prefix", "kube.var.log.containers.",
+ NULL);
+
+ /* Enable test mode */
+ ret = flb_output_set_test(ctx, out_ffd, "formatter",
+ cb_check_k8s_container_resource_default_regex,
+ NULL, NULL);
+
+ /* Start */
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data sample */
+ flb_lib_push(ctx, in_ffd, (char *) K8S_CONTAINER_NO_LOCAL_RESOURCE_ID, size);
+
+ sleep(2);
+ flb_stop(ctx);
+ flb_destroy(ctx);
+}
+
+void flb_test_resource_k8s_container_custom_k8s_regex()
+{
+ int ret;
+ int size = sizeof(K8S_CONTAINER_NO_LOCAL_RESOURCE_ID) - 1;
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+
+ /* Create context, flush every second (some checks omitted here) */
+ ctx = flb_create();
+ flb_service_set(ctx, "flush", "1", "grace", "1", NULL);
+
+ /* Lib input mode */
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ flb_input_set(ctx, in_ffd, "tag",
+ "k8s_container.testnamespace.testpod.testctr", NULL);
+
+ /* Stackdriver output */
+ out_ffd = flb_output(ctx, (char *) "stackdriver", NULL);
+ flb_output_set(ctx, out_ffd,
+ "match", "k8s_container.*",
+ "resource", "k8s_container",
+ "google_service_credentials", SERVICE_CREDENTIALS,
+ "k8s_cluster_name", "test_cluster_name",
+ "k8s_cluster_location", "test_cluster_location",
+ "custom_k8s_regex", "^(?<namespace_name>[^_]+)\\.(?<pod_name>[a-z0-9]([-a-z0-9]*[a-z0-9])?(\\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*)\\.(?<container_name>.+)$",
+ NULL);
+
+ /* Enable test mode */
+ ret = flb_output_set_test(ctx, out_ffd, "formatter",
+ cb_check_k8s_container_resource,
+ NULL, NULL);
+
+ /* Start */
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data sample */
+ flb_lib_push(ctx, in_ffd, (char *) K8S_CONTAINER_NO_LOCAL_RESOURCE_ID, size);
+
+ sleep(2);
+ flb_stop(ctx);
+ flb_destroy(ctx);
+}
+
+void flb_test_resource_k8s_container_custom_k8s_regex_custom_prefix()
+{
+ int ret;
+ int size = sizeof(K8S_CONTAINER_NO_LOCAL_RESOURCE_ID) - 1;
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+
+ /* Create context, flush every second (some checks omitted here) */
+ ctx = flb_create();
+ flb_service_set(ctx, "flush", "1", "grace", "1", NULL);
+
+ /* Lib input mode */
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ flb_input_set(ctx, in_ffd, "tag",
+ "kube.var.log.containers.testnamespace.testpod.testctr", NULL);
+
+ /* Stackdriver output */
+ out_ffd = flb_output(ctx, (char *) "stackdriver", NULL);
+ flb_output_set(ctx, out_ffd,
+ "match", "kube.var.log.containers.*",
+ "resource", "k8s_container",
+ "google_service_credentials", SERVICE_CREDENTIALS,
+ "k8s_cluster_name", "test_cluster_name",
+ "k8s_cluster_location", "test_cluster_location",
+ "tag_prefix", "kube.var.log.containers.",
+ "custom_k8s_regex", "^(?<namespace_name>[^_]+)\\.(?<pod_name>[a-z0-9]([-a-z0-9]*[a-z0-9])?(\\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*)\\.(?<container_name>.+)$",
+ NULL);
+
+ /* Enable test mode */
+ ret = flb_output_set_test(ctx, out_ffd, "formatter",
+ cb_check_k8s_container_resource,
+ NULL, NULL);
+
+ /* Start */
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data sample */
+ flb_lib_push(ctx, in_ffd, (char *) K8S_CONTAINER_NO_LOCAL_RESOURCE_ID, size);
+
+ sleep(2);
+ flb_stop(ctx);
+ flb_destroy(ctx);
+}
+
+void flb_test_resource_k8s_node_common()
+{
+ int ret;
+ int size = sizeof(K8S_NODE_COMMON) - 1;
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+
+ /* Create context, flush every second (some checks omitted here) */
+ ctx = flb_create();
+ flb_service_set(ctx, "flush", "1", "grace", "1", NULL);
+
+ /* Lib input mode */
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ flb_input_set(ctx, in_ffd, "tag", "test", NULL);
+
+ /* Stackdriver output */
+ out_ffd = flb_output(ctx, (char *) "stackdriver", NULL);
+ flb_output_set(ctx, out_ffd,
+ "match", "test",
+ "resource", "k8s_node",
+ "google_service_credentials", SERVICE_CREDENTIALS,
+ "k8s_cluster_name", "test_cluster_name",
+ "k8s_cluster_location", "test_cluster_location",
+ NULL);
+
+ /* Enable test mode */
+ ret = flb_output_set_test(ctx, out_ffd, "formatter",
+ cb_check_k8s_node_resource,
+ NULL, NULL);
+
+ /* Start */
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data sample */
+ flb_lib_push(ctx, in_ffd, (char *) K8S_NODE_COMMON, size);
+
+ sleep(2);
+ flb_stop(ctx);
+ flb_destroy(ctx);
+}
+
+void flb_test_resource_k8s_pod_common()
+{
+ int ret;
+ int size = sizeof(K8S_POD_COMMON) - 1;
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+
+ /* Create context, flush every second (some checks omitted here) */
+ ctx = flb_create();
+ flb_service_set(ctx, "flush", "1", "grace", "1", NULL);
+
+ /* Lib input mode */
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ flb_input_set(ctx, in_ffd, "tag", "test", NULL);
+
+ /* Stackdriver output */
+ out_ffd = flb_output(ctx, (char *) "stackdriver", NULL);
+ flb_output_set(ctx, out_ffd,
+ "match", "test",
+ "resource", "k8s_pod",
+ "google_service_credentials", SERVICE_CREDENTIALS,
+ "k8s_cluster_name", "test_cluster_name",
+ "k8s_cluster_location", "test_cluster_location",
+ NULL);
+
+ /* Enable test mode */
+ ret = flb_output_set_test(ctx, out_ffd, "formatter",
+ cb_check_k8s_pod_resource,
+ NULL, NULL);
+
+ /* Start */
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data sample */
+ flb_lib_push(ctx, in_ffd, (char *) K8S_POD_COMMON, size);
+
+ sleep(2);
+ flb_stop(ctx);
+ flb_destroy(ctx);
+}
+
+void flb_test_default_labels()
+{
+ int ret;
+ int size = sizeof(DEFAULT_LABELS) - 1;
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+
+ /* Create context, flush every second (some checks omitted here) */
+ ctx = flb_create();
+ flb_service_set(ctx, "flush", "1", "grace", "1", NULL);
+
+ /* Lib input mode */
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ flb_input_set(ctx, in_ffd, "tag", "test", NULL);
+
+ /* Stackdriver output */
+ out_ffd = flb_output(ctx, (char *) "stackdriver", NULL);
+ flb_output_set(ctx, out_ffd,
+ "match", "test",
+ "resource", "global",
+ "google_service_credentials", SERVICE_CREDENTIALS,
+ NULL);
+
+ /* Enable test mode */
+ ret = flb_output_set_test(ctx, out_ffd, "formatter",
+ cb_check_default_labels,
+ NULL, NULL);
+
+ /* Start */
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data sample */
+ flb_lib_push(ctx, in_ffd, (char *) DEFAULT_LABELS, size);
+
+ sleep(2);
+ flb_stop(ctx);
+ flb_destroy(ctx);
+}
+
+void flb_test_custom_labels()
+{
+ int ret;
+ int size = sizeof(CUSTOM_LABELS) - 1;
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+
+ /* Create context, flush every second (some checks omitted here) */
+ ctx = flb_create();
+ flb_service_set(ctx, "flush", "1", "grace", "1", NULL);
+
+ /* Lib input mode */
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ flb_input_set(ctx, in_ffd, "tag", "test", NULL);
+
+ /* Stackdriver output */
+ out_ffd = flb_output(ctx, (char *) "stackdriver", NULL);
+ flb_output_set(ctx, out_ffd,
+ "match", "test",
+ "resource", "global",
+ "google_service_credentials", SERVICE_CREDENTIALS,
+ "labels_key", "logging.googleapis.com/customlabels",
+ NULL);
+
+ /* Enable test mode */
+ ret = flb_output_set_test(ctx, out_ffd, "formatter",
+ cb_check_custom_labels,
+ NULL, NULL);
+
+ /* Start */
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data sample */
+ flb_lib_push(ctx, in_ffd, (char *) CUSTOM_LABELS, size);
+
+ sleep(2);
+ flb_stop(ctx);
+ flb_destroy(ctx);
+}
+
+void flb_test_config_labels_conflict()
+{
+ int ret;
+ int size = sizeof(DEFAULT_LABELS) - 1;
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+
+ /* Create context, flush every second (some checks omitted here) */
+ ctx = flb_create();
+ flb_service_set(ctx, "flush", "1", "grace", "1", NULL);
+
+ /* Lib input mode */
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ flb_input_set(ctx, in_ffd, "tag", "test", NULL);
+
+ /* Stackdriver output */
+ out_ffd = flb_output(ctx, (char *) "stackdriver", NULL);
+ flb_output_set(ctx, out_ffd,
+ "match", "test",
+ "resource", "global",
+ "google_service_credentials", SERVICE_CREDENTIALS,
+ "labels", "testB=valC",
+ NULL);
+
+ /* Enable test mode */
+ ret = flb_output_set_test(ctx, out_ffd, "formatter",
+ cb_check_config_labels_conflict,
+ NULL, NULL);
+
+ /* Start */
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data sample */
+ flb_lib_push(ctx, in_ffd, (char *) DEFAULT_LABELS, size);
+
+ sleep(2);
+ flb_stop(ctx);
+ flb_destroy(ctx);
+}
+
+void flb_test_config_labels_no_conflict()
+{
+ int ret;
+ int size = sizeof(DEFAULT_LABELS) - 1;
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+
+ /* Create context, flush every second (some checks omitted here) */
+ ctx = flb_create();
+ flb_service_set(ctx, "flush", "1", "grace", "1", NULL);
+
+ /* Lib input mode */
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ flb_input_set(ctx, in_ffd, "tag", "test", NULL);
+
+ /* Stackdriver output */
+ out_ffd = flb_output(ctx, (char *) "stackdriver", NULL);
+ flb_output_set(ctx, out_ffd,
+ "match", "test",
+ "resource", "global",
+ "google_service_credentials", SERVICE_CREDENTIALS,
+ "labels", "testC=valC",
+ NULL);
+
+ /* Enable test mode */
+ ret = flb_output_set_test(ctx, out_ffd, "formatter",
+ cb_check_config_labels_no_conflict,
+ NULL, NULL);
+
+ /* Start */
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data sample */
+ flb_lib_push(ctx, in_ffd, (char *) DEFAULT_LABELS, size);
+
+ sleep(2);
+ flb_stop(ctx);
+ flb_destroy(ctx);
+}
+
+void flb_test_default_labels_k8s_resource_type()
+{
+ int ret;
+ int size = sizeof(DEFAULT_LABELS_K8S_RESOURCE_TYPE) - 1;
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+
+ /* Create context, flush every second (some checks omitted here) */
+ ctx = flb_create();
+ flb_service_set(ctx, "flush", "1", "grace", "1", NULL);
+
+ /* Lib input mode */
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ flb_input_set(ctx, in_ffd, "tag", "test", NULL);
+
+ /* Stackdriver output */
+ out_ffd = flb_output(ctx, (char *) "stackdriver", NULL);
+ flb_output_set(ctx, out_ffd,
+ "match", "test",
+ "resource", "k8s_container",
+ "google_service_credentials", SERVICE_CREDENTIALS,
+ "k8s_cluster_name", "test_cluster_name",
+ "k8s_cluster_location", "test_cluster_location",
+ NULL);
+
+ /* Enable test mode */
+ ret = flb_output_set_test(ctx, out_ffd, "formatter",
+ cb_check_default_labels_k8s_resource_type,
+ NULL, NULL);
+
+ /* Start */
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data sample */
+ flb_lib_push(ctx, in_ffd, (char *) DEFAULT_LABELS_K8S_RESOURCE_TYPE, size);
+
+ sleep(2);
+ flb_stop(ctx);
+ flb_destroy(ctx);
+}
+
+void flb_test_resource_labels_one_field()
+{
+ int ret;
+ int size = sizeof(ONE_FIELD) - 1;
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+
+ /* Create context, flush every second (some checks omitted here) */
+ ctx = flb_create();
+ flb_service_set(ctx, "flush", "1", "grace", "1", NULL);
+
+ /* Lib input mode */
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ flb_input_set(ctx, in_ffd, "tag", "test", NULL);
+
+ /* Stackdriver output */
+ out_ffd = flb_output(ctx, (char *) "stackdriver", NULL);
+ flb_output_set(ctx, out_ffd,
+ "match", "test",
+ "google_service_credentials", SERVICE_CREDENTIALS,
+ "resource_labels", "keyB=$keyA",
+ NULL);
+
+ /* Enable test mode */
+ ret = flb_output_set_test(ctx, out_ffd, "formatter",
+ cb_check_resource_labels_one_field,
+ NULL, NULL);
+
+ /* Start */
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data sample */
+ flb_lib_push(ctx, in_ffd, (char *) ONE_FIELD, size);
+
+ sleep(2);
+ flb_stop(ctx);
+ flb_destroy(ctx);
+}
+
+void flb_test_resource_labels_plaintext()
+{
+ int ret;
+ int size = sizeof(ONE_FIELD) - 1;
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+
+ /* Create context, flush every second (some checks omitted here) */
+ ctx = flb_create();
+ flb_service_set(ctx, "flush", "1", "grace", "1", NULL);
+
+ /* Lib input mode */
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ flb_input_set(ctx, in_ffd, "tag", "test", NULL);
+
+ /* Stackdriver output */
+ out_ffd = flb_output(ctx, (char *) "stackdriver", NULL);
+ flb_output_set(ctx, out_ffd,
+ "match", "test",
+ "google_service_credentials", SERVICE_CREDENTIALS,
+ "resource_labels", "keyB=plaintext",
+ NULL);
+
+ /* Enable test mode */
+ ret = flb_output_set_test(ctx, out_ffd, "formatter",
+ cb_check_resource_labels_plaintext,
+ NULL, NULL);
+
+ /* Start */
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data sample */
+ flb_lib_push(ctx, in_ffd, (char *) ONE_FIELD, size);
+
+ sleep(2);
+ flb_stop(ctx);
+ flb_destroy(ctx);
+}
+
+void flb_test_resource_labels_multiple_fields()
+{
+ int ret;
+ int size = sizeof(MULTIPLE_FIELDS) - 1;
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+
+ /* Create context, flush every second (some checks omitted here) */
+ ctx = flb_create();
+ flb_service_set(ctx, "flush", "1", "grace", "1", NULL);
+
+ /* Lib input mode */
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ flb_input_set(ctx, in_ffd, "tag", "test", NULL);
+
+ /* Stackdriver output */
+ out_ffd = flb_output(ctx, (char *) "stackdriver", NULL);
+ flb_output_set(ctx, out_ffd,
+ "match", "test",
+ "google_service_credentials", SERVICE_CREDENTIALS,
+ "resource_labels", "keyD=$keyB,keyC=$keyA",
+ NULL);
+
+ /* Enable test mode */
+ ret = flb_output_set_test(ctx, out_ffd, "formatter",
+ cb_check_multiple_fields_mapped,
+ NULL, NULL);
+
+ /* Start */
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data sample */
+ flb_lib_push(ctx, in_ffd, (char *) MULTIPLE_FIELDS, size);
+
+ sleep(2);
+ flb_stop(ctx);
+ flb_destroy(ctx);
+}
+
+void flb_test_resource_labels_nested_fields()
+{
+ int ret;
+ int size = sizeof(NESTED_FIELDS) - 1;
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+
+ /* Create context, flush every second (some checks omitted here) */
+ ctx = flb_create();
+ flb_service_set(ctx, "flush", "1", "grace", "1", NULL);
+
+ /* Lib input mode */
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ flb_input_set(ctx, in_ffd, "tag", "test", NULL);
+
+ /* Stackdriver output */
+ out_ffd = flb_output(ctx, (char *) "stackdriver", NULL);
+ flb_output_set(ctx, out_ffd,
+ "match", "test",
+ "google_service_credentials", SERVICE_CREDENTIALS,
+ "resource_labels", "keyD=$toplevel['keyB'],keyC=$toplevel['keyA']",
+ NULL);
+
+ /* Enable test mode */
+ ret = flb_output_set_test(ctx, out_ffd, "formatter",
+ cb_check_nested_fields_mapped,
+ NULL, NULL);
+
+ /* Start */
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data sample */
+ flb_lib_push(ctx, in_ffd, (char *) NESTED_FIELDS, size);
+
+ sleep(2);
+ flb_stop(ctx);
+ flb_destroy(ctx);
+}
+
+void flb_test_resource_labels_layered_nested_fields()
+{
+ int ret;
+ int size = sizeof(LAYERED_NESTED_FIELDS) - 1;
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+
+ /* Create context, flush every second (some checks omitted here) */
+ ctx = flb_create();
+ flb_service_set(ctx, "flush", "1", "grace", "1", NULL);
+
+ /* Lib input mode */
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ flb_input_set(ctx, in_ffd, "tag", "test", NULL);
+
+ /* Stackdriver output */
+ out_ffd = flb_output(ctx, (char *) "stackdriver", NULL);
+ flb_output_set(ctx, out_ffd,
+ "match", "test",
+ "google_service_credentials", SERVICE_CREDENTIALS,
+ "resource_labels",
+ "keyD=$toplevel['keyB'],keyC=$toplevel['midlevel']['keyA']",
+ NULL);
+
+ /* Enable test mode */
+ ret = flb_output_set_test(ctx, out_ffd, "formatter",
+ cb_check_layered_nested_fields_mapped,
+ NULL, NULL);
+
+ /* Start */
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data sample */
+ flb_lib_push(ctx, in_ffd, (char *) LAYERED_NESTED_FIELDS, size);
+
+ sleep(2);
+ flb_stop(ctx);
+ flb_destroy(ctx);
+}
+
+void flb_test_resource_labels_original_does_not_exist()
+{
+ int ret;
+ int size = sizeof(ONE_FIELD) - 1;
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+
+ /* Create context, flush every second (some checks omitted here) */
+ ctx = flb_create();
+ flb_service_set(ctx, "flush", "1", "grace", "1", NULL);
+
+ /* Lib input mode */
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ flb_input_set(ctx, in_ffd, "tag", "test", NULL);
+
+ /* Stackdriver output */
+ out_ffd = flb_output(ctx, (char *) "stackdriver", NULL);
+ flb_output_set(ctx, out_ffd,
+ "match", "test",
+ "google_service_credentials", SERVICE_CREDENTIALS,
+ "resource_labels", "keyC=$keyB",
+ NULL);
+
+ /* Enable test mode */
+ ret = flb_output_set_test(ctx, out_ffd, "formatter",
+ cb_check_resource_labels_original_does_not_exist,
+ NULL, NULL);
+
+ /* Start */
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data sample */
+ flb_lib_push(ctx, in_ffd, (char *) ONE_FIELD, size);
+
+ sleep(2);
+ flb_stop(ctx);
+ flb_destroy(ctx);
+}
+
+void flb_test_resource_labels_nested_original_does_not_exist()
+{
+ int ret;
+ int size = sizeof(ONE_FIELD) - 1;
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+
+ /* Create context, flush every second (some checks omitted here) */
+ ctx = flb_create();
+ flb_service_set(ctx, "flush", "1", "grace", "1", NULL);
+
+ /* Lib input mode */
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ flb_input_set(ctx, in_ffd, "tag", "test", NULL);
+
+ /* Stackdriver output */
+ out_ffd = flb_output(ctx, (char *) "stackdriver", NULL);
+ flb_output_set(ctx, out_ffd,
+ "match", "test",
+ "google_service_credentials", SERVICE_CREDENTIALS,
+ "resource_labels", "keyC=$keyY['keyZ']",
+ NULL);
+
+ /* Enable test mode */
+ ret = flb_output_set_test(ctx, out_ffd, "formatter",
+ cb_check_resource_labels_original_does_not_exist,
+ NULL, NULL);
+
+ /* Start */
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data sample */
+ flb_lib_push(ctx, in_ffd, (char *) ONE_FIELD, size);
+
+ sleep(2);
+ flb_stop(ctx);
+ flb_destroy(ctx);
+}
+
+void flb_test_resource_labels_nested_original_partially_exists()
+{
+ int ret;
+ int size = sizeof(NESTED_FIELDS) - 1;
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+
+ /* Create context, flush every second (some checks omitted here) */
+ ctx = flb_create();
+ flb_service_set(ctx, "flush", "1", "grace", "1", NULL);
+
+ /* Lib input mode */
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ flb_input_set(ctx, in_ffd, "tag", "test", NULL);
+
+ /* Stackdriver output */
+ out_ffd = flb_output(ctx, (char *) "stackdriver", NULL);
+ flb_output_set(ctx, out_ffd,
+ "match", "test",
+ "google_service_credentials", SERVICE_CREDENTIALS,
+ "resource_labels", "keyC=$toplevel['keyZ']",
+ NULL);
+
+ /* Enable test mode */
+ ret = flb_output_set_test(ctx, out_ffd, "formatter",
+ cb_check_resource_labels_nested_original_partially_exists,
+ NULL, NULL);
+
+ /* Start */
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data sample */
+ flb_lib_push(ctx, in_ffd, (char *) NESTED_FIELDS, size);
+
+ sleep(2);
+ flb_stop(ctx);
+ flb_destroy(ctx);
+}
+
+void flb_test_resource_labels_one_field_with_spaces()
+{
+ int ret;
+ int size = sizeof(ONE_FIELD) - 1;
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+
+ /* Create context, flush every second (some checks omitted here) */
+ ctx = flb_create();
+ flb_service_set(ctx, "flush", "1", "grace", "1", NULL);
+
+ /* Lib input mode */
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ flb_input_set(ctx, in_ffd, "tag", "test", NULL);
+
+ /* Stackdriver output */
+ out_ffd = flb_output(ctx, (char *) "stackdriver", NULL);
+ flb_output_set(ctx, out_ffd,
+ "match", "test",
+ "google_service_credentials", SERVICE_CREDENTIALS,
+ "resource_labels", "keyB = $keyA",
+ NULL);
+
+ /* Enable test mode */
+ ret = flb_output_set_test(ctx, out_ffd, "formatter",
+ cb_check_resource_labels_one_field,
+ NULL, NULL);
+
+ /* Start */
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data sample */
+ flb_lib_push(ctx, in_ffd, (char *) ONE_FIELD, size);
+
+ sleep(2);
+ flb_stop(ctx);
+ flb_destroy(ctx);
+}
+
+void flb_test_resource_labels_multiple_fields_with_spaces()
+{
+ int ret;
+ int size = sizeof(MULTIPLE_FIELDS) - 1;
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+
+ /* Create context, flush every second (some checks omitted here) */
+ ctx = flb_create();
+ flb_service_set(ctx, "flush", "1", "grace", "1", NULL);
+
+ /* Lib input mode */
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ flb_input_set(ctx, in_ffd, "tag", "test", NULL);
+
+ /* Stackdriver output */
+ out_ffd = flb_output(ctx, (char *) "stackdriver", NULL);
+ flb_output_set(ctx, out_ffd,
+ "match", "test",
+ "google_service_credentials", SERVICE_CREDENTIALS,
+ "resource_labels", "keyD = $keyB, keyC = $keyA",
+ NULL);
+
+ /* Enable test mode */
+ ret = flb_output_set_test(ctx, out_ffd, "formatter",
+ cb_check_multiple_fields_mapped,
+ NULL, NULL);
+
+ /* Start */
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data sample */
+ flb_lib_push(ctx, in_ffd, (char *) MULTIPLE_FIELDS, size);
+
+ sleep(2);
+ flb_stop(ctx);
+ flb_destroy(ctx);
+}
+
+void flb_test_resource_labels_empty_input()
+{
+ int ret;
+ int size = sizeof(ONE_FIELD) - 1;
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+
+ /* Create context, flush every second (some checks omitted here) */
+ ctx = flb_create();
+ flb_service_set(ctx, "flush", "1", "grace", "1", NULL);
+
+ /* Lib input mode */
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ flb_input_set(ctx, in_ffd, "tag", "test", NULL);
+
+ /* Stackdriver output */
+ out_ffd = flb_output(ctx, (char *) "stackdriver", NULL);
+ flb_output_set(ctx, out_ffd,
+ "match", "test",
+ "google_service_credentials", SERVICE_CREDENTIALS,
+ "resource_labels", "",
+ NULL);
+
+ /* Enable test mode */
+ ret = flb_output_set_test(ctx, out_ffd, "formatter",
+ cb_check_resource_labels_original_does_not_exist,
+ NULL, NULL);
+
+ /* Start */
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data sample */
+ flb_lib_push(ctx, in_ffd, (char *) ONE_FIELD, size);
+
+ sleep(2);
+ flb_stop(ctx);
+ flb_destroy(ctx);
+}
+
+void flb_test_resource_labels_duplicate_assignment()
+{
+ int ret;
+ int size = sizeof(MULTIPLE_FIELDS) - 1;
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+
+ /* Create context, flush every second (some checks omitted here) */
+ ctx = flb_create();
+ flb_service_set(ctx, "flush", "1", "grace", "1", NULL);
+
+ /* Lib input mode */
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ flb_input_set(ctx, in_ffd, "tag", "test", NULL);
+
+ /* Stackdriver output */
+ out_ffd = flb_output(ctx, (char *) "stackdriver", NULL);
+ flb_output_set(ctx, out_ffd,
+ "match", "test",
+ "google_service_credentials", SERVICE_CREDENTIALS,
+ "resource_labels", "keyC=$keyA,keyC=$keyB",
+ NULL);
+
+ /* Enable test mode */
+ ret = flb_output_set_test(ctx, out_ffd, "formatter",
+ cb_check_resource_labels_duplicate_assignment,
+ NULL, NULL);
+
+ /* Start */
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data sample */
+ flb_lib_push(ctx, in_ffd, (char *) MULTIPLE_FIELDS, size);
+
+ sleep(2);
+ flb_stop(ctx);
+ flb_destroy(ctx);
+}
+
+void flb_test_resource_labels_project_id_not_overridden()
+{
+ int ret;
+ int size = sizeof(ONE_FIELD) - 1;
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+
+ /* Create context, flush every second (some checks omitted here) */
+ ctx = flb_create();
+ flb_service_set(ctx, "flush", "1", "grace", "1", NULL);
+
+ /* Lib input mode */
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ flb_input_set(ctx, in_ffd, "tag", "test", NULL);
+
+ /* Stackdriver output */
+ out_ffd = flb_output(ctx, (char *) "stackdriver", NULL);
+ flb_output_set(ctx, out_ffd,
+ "match", "test",
+ "google_service_credentials", SERVICE_CREDENTIALS,
+ "resource_labels", "project_id=$keyA",
+ NULL);
+
+ /* Enable test mode */
+ ret = flb_output_set_test(ctx, out_ffd, "formatter",
+ cb_check_resource_labels_project_id_not_overridden,
+ NULL, NULL);
+
+ /* Start */
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data sample */
+ flb_lib_push(ctx, in_ffd, (char *) ONE_FIELD, size);
+
+ sleep(2);
+ flb_stop(ctx);
+ flb_destroy(ctx);
+}
+
+void flb_test_resource_labels_has_priority()
+{
+ int ret;
+ int size = sizeof(ONE_FIELD) - 1;
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+
+ /* Create context, flush every second (some checks omitted here) */
+ ctx = flb_create();
+ flb_service_set(ctx, "flush", "1", "grace", "1", NULL);
+
+ /* Lib input mode */
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ flb_input_set(ctx, in_ffd, "tag", "test", NULL);
+
+ /* Stackdriver output */
+ out_ffd = flb_output(ctx, (char *) "stackdriver", NULL);
+ flb_output_set(ctx, out_ffd,
+ "match", "test",
+ "resource", "k8s_container",
+ "google_service_credentials", SERVICE_CREDENTIALS,
+ "k8s_cluster_name", "test_cluster_name",
+ "k8s_cluster_location", "test_cluster_location",
+ "resource_labels",
+ "cluster_name=name_from_labels,location=loc_from_labels",
+ NULL);
+
+ /* Enable test mode */
+ ret = flb_output_set_test(ctx, out_ffd, "formatter",
+ cb_check_resource_labels_k8s,
+ NULL, NULL);
+
+ /* Start */
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data sample */
+ flb_lib_push(ctx, in_ffd, (char *) ONE_FIELD, size);
+
+ sleep(2);
+ flb_stop(ctx);
+ flb_destroy(ctx);
+}
+
+void flb_test_resource_labels_fallsback_when_required_not_specified()
+{
+ int ret;
+ int size = sizeof(K8S_CONTAINER_COMMON) - 1;
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+
+ /* Create context, flush every second (some checks omitted here) */
+ ctx = flb_create();
+ flb_service_set(ctx, "flush", "1", "grace", "1", NULL);
+
+ /* Lib input mode */
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ flb_input_set(ctx, in_ffd, "tag", "test", NULL);
+
+ /* Stackdriver output */
+ out_ffd = flb_output(ctx, (char *) "stackdriver", NULL);
+ flb_output_set(ctx, out_ffd,
+ "match", "test",
+ "resource", "k8s_container",
+ "google_service_credentials", SERVICE_CREDENTIALS,
+ "k8s_cluster_name", "test_cluster_name",
+ "k8s_cluster_location", "test_cluster_location",
+ "resource_labels", "keyB=$keyA",
+ NULL);
+
+ /* Enable test mode */
+ ret = flb_output_set_test(ctx, out_ffd, "formatter",
+ cb_check_k8s_container_resource,
+ NULL, NULL);
+
+ /* Start */
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data sample */
+ flb_lib_push(ctx, in_ffd, (char *) K8S_CONTAINER_COMMON, size);
+
+ sleep(2);
+ flb_stop(ctx);
+ flb_destroy(ctx);
+}
+
+void flb_test_resource_labels_fallsback_when_required_partially_specified()
+{
+ int ret;
+ int size = sizeof(K8S_CONTAINER_COMMON) - 1;
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+
+ /* Create context, flush every second (some checks omitted here) */
+ ctx = flb_create();
+ flb_service_set(ctx, "flush", "1", "grace", "1", NULL);
+
+ /* Lib input mode */
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ flb_input_set(ctx, in_ffd, "tag", "test", NULL);
+
+ /* Stackdriver output */
+ out_ffd = flb_output(ctx, (char *) "stackdriver", NULL);
+ flb_output_set(ctx, out_ffd,
+ "match", "test",
+ "resource", "k8s_container",
+ "google_service_credentials", SERVICE_CREDENTIALS,
+ "k8s_cluster_name", "test_cluster_name",
+ "k8s_cluster_location", "test_cluster_location",
+ "resource_labels", "location=cluster_loc",
+ NULL);
+
+ /* Enable test mode */
+ ret = flb_output_set_test(ctx, out_ffd, "formatter",
+ cb_check_k8s_container_resource,
+ NULL, NULL);
+
+ /* Start */
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data sample */
+ flb_lib_push(ctx, in_ffd, (char *) K8S_CONTAINER_COMMON, size);
+
+ sleep(2);
+ flb_stop(ctx);
+ flb_destroy(ctx);
+}
+
+void flb_test_resource_labels_k8s_container()
+{
+ int ret;
+ int size = sizeof(ONE_FIELD) - 1;
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+
+ /* Create context, flush every second (some checks omitted here) */
+ ctx = flb_create();
+ flb_service_set(ctx, "flush", "1", "grace", "1", NULL);
+
+ /* Lib input mode */
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ flb_input_set(ctx, in_ffd, "tag", "test", NULL);
+
+ /* Stackdriver output */
+ out_ffd = flb_output(ctx, (char *) "stackdriver", NULL);
+ flb_output_set(ctx, out_ffd,
+ "match", "test",
+ "resource", "k8s_container",
+ "google_service_credentials", SERVICE_CREDENTIALS,
+ "resource_labels",
+ "cluster_name=name_from_labels,location=loc_from_labels",
+ NULL);
+
+ /* Enable test mode */
+ ret = flb_output_set_test(ctx, out_ffd, "formatter",
+ cb_check_resource_labels_k8s,
+ NULL, NULL);
+
+ /* Start */
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data sample */
+ flb_lib_push(ctx, in_ffd, (char *) ONE_FIELD, size);
+
+ sleep(2);
+ flb_stop(ctx);
+ flb_destroy(ctx);
+}
+
+void flb_test_resource_labels_k8s_node()
+{
+ int ret;
+ int size = sizeof(ONE_FIELD) - 1;
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+
+ /* Create context, flush every second (some checks omitted here) */
+ ctx = flb_create();
+ flb_service_set(ctx, "flush", "1", "grace", "1", NULL);
+
+ /* Lib input mode */
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ flb_input_set(ctx, in_ffd, "tag", "test", NULL);
+
+ /* Stackdriver output */
+ out_ffd = flb_output(ctx, (char *) "stackdriver", NULL);
+ flb_output_set(ctx, out_ffd,
+ "match", "test",
+ "resource", "k8s_node",
+ "google_service_credentials", SERVICE_CREDENTIALS,
+ "resource_labels",
+ "cluster_name=name_from_labels,location=loc_from_labels",
+ NULL);
+
+ /* Enable test mode */
+ ret = flb_output_set_test(ctx, out_ffd, "formatter",
+ cb_check_resource_labels_k8s,
+ NULL, NULL);
+
+ /* Start */
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data sample */
+ flb_lib_push(ctx, in_ffd, (char *) ONE_FIELD, size);
+
+ sleep(2);
+ flb_stop(ctx);
+ flb_destroy(ctx);
+}
+
+void flb_test_resource_labels_k8s_pod()
+{
+ int ret;
+ int size = sizeof(ONE_FIELD) - 1;
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+
+ /* Create context, flush every second (some checks omitted here) */
+ ctx = flb_create();
+ flb_service_set(ctx, "flush", "1", "grace", "1", NULL);
+
+ /* Lib input mode */
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ flb_input_set(ctx, in_ffd, "tag", "test", NULL);
+
+ /* Stackdriver output */
+ out_ffd = flb_output(ctx, (char *) "stackdriver", NULL);
+ flb_output_set(ctx, out_ffd,
+ "match", "test",
+ "resource", "k8s_pod",
+ "google_service_credentials", SERVICE_CREDENTIALS,
+ "resource_labels",
+ "cluster_name=name_from_labels,location=loc_from_labels",
+ NULL);
+
+ /* Enable test mode */
+ ret = flb_output_set_test(ctx, out_ffd, "formatter",
+ cb_check_resource_labels_k8s,
+ NULL, NULL);
+
+ /* Start */
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data sample */
+ flb_lib_push(ctx, in_ffd, (char *) ONE_FIELD, size);
+
+ sleep(2);
+ flb_stop(ctx);
+ flb_destroy(ctx);
+}
+
+void flb_test_resource_labels_generic_node()
+{
+ int ret;
+ int size = sizeof(ONE_FIELD) - 1;
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+
+ /* Create context, flush every second (some checks omitted here) */
+ ctx = flb_create();
+ flb_service_set(ctx, "flush", "1", "grace", "1", NULL);
+
+ /* Lib input mode */
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ flb_input_set(ctx, in_ffd, "tag", "test", NULL);
+
+ /* Stackdriver output */
+ out_ffd = flb_output(ctx, (char *) "stackdriver", NULL);
+ flb_output_set(ctx, out_ffd,
+ "match", "test",
+ "google_service_credentials", SERVICE_CREDENTIALS,
+ "resource", "generic_node",
+ "resource_labels",
+ "location=fluent,namespace=test,node_id=333222111",
+ NULL);
+
+ /* Enable test mode */
+ ret = flb_output_set_test(ctx, out_ffd, "formatter",
+ cb_check_generic_node_creds,
+ NULL, NULL);
+
+ /* Start */
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data sample */
+ flb_lib_push(ctx, in_ffd, (char *) ONE_FIELD, size);
+
+ sleep(2);
+ flb_stop(ctx);
+ flb_destroy(ctx);
+}
+
+void flb_test_resource_labels_generic_task()
+{
+ int ret;
+ int size = sizeof(ONE_FIELD) - 1;
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+
+ /* Create context, flush every second (some checks omitted here) */
+ ctx = flb_create();
+ flb_service_set(ctx, "flush", "1", "grace", "1", NULL);
+
+ /* Lib input mode */
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ flb_input_set(ctx, in_ffd, "tag", "test", NULL);
+
+ /* Stackdriver output */
+ out_ffd = flb_output(ctx, (char *) "stackdriver", NULL);
+ flb_output_set(ctx, out_ffd,
+ "match", "test",
+ "google_service_credentials", SERVICE_CREDENTIALS,
+ "resource", "generic_task",
+ "resource_labels",
+ "location=fluent,namespace=test,job=test-job,task_id=333222111",
+ NULL);
+
+ /* Enable test mode */
+ ret = flb_output_set_test(ctx, out_ffd, "formatter",
+ cb_check_generic_task_creds,
+ NULL, NULL);
+
+ /* Start */
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data sample */
+ flb_lib_push(ctx, in_ffd, (char *) ONE_FIELD, size);
+
+ sleep(2);
+ flb_stop(ctx);
+ flb_destroy(ctx);
+}
+
+void flb_test_custom_labels_k8s_resource_type()
+{
+ int ret;
+ int size = sizeof(CUSTOM_LABELS_K8S_RESOURCE_TYPE) - 1;
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+
+ /* Create context, flush every second (some checks omitted here) */
+ ctx = flb_create();
+ flb_service_set(ctx, "flush", "1", "grace", "1", NULL);
+
+ /* Lib input mode */
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ flb_input_set(ctx, in_ffd, "tag", "test", NULL);
+
+ /* Stackdriver output */
+ out_ffd = flb_output(ctx, (char *) "stackdriver", NULL);
+ flb_output_set(ctx, out_ffd,
+ "match", "test",
+ "resource", "k8s_container",
+ "google_service_credentials", SERVICE_CREDENTIALS,
+ "labels_key", "logging.googleapis.com/customlabels",
+ "k8s_cluster_name", "test_cluster_name",
+ "k8s_cluster_location", "test_cluster_location",
+ NULL);
+
+ /* Enable test mode */
+ ret = flb_output_set_test(ctx, out_ffd, "formatter",
+ cb_check_custom_labels_k8s_resource_type,
+ NULL, NULL);
+
+ /* Start */
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data sample */
+ flb_lib_push(ctx, in_ffd, (char *) CUSTOM_LABELS_K8S_RESOURCE_TYPE, size);
+
+ sleep(2);
+ flb_stop(ctx);
+ flb_destroy(ctx);
+}
+
+void flb_test_resource_k8s_container_no_local_resource_id()
+{
+ int ret;
+ int size = sizeof(K8S_CONTAINER_NO_LOCAL_RESOURCE_ID) - 1;
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+
+ /* Create context, flush every second (some checks omitted here) */
+ ctx = flb_create();
+ flb_service_set(ctx, "flush", "1", "grace", "1", NULL);
+
+ /* Lib input mode */
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ flb_input_set(ctx, in_ffd, "tag",
+ "k8s_container.testnamespace.testpod.testctr", NULL);
+
+ /* Stackdriver output */
+ out_ffd = flb_output(ctx, (char *) "stackdriver", NULL);
+ flb_output_set(ctx, out_ffd,
+ "match", "k8s_container.*",
+ "resource", "k8s_container",
+ "google_service_credentials", SERVICE_CREDENTIALS,
+ "k8s_cluster_name", "test_cluster_name",
+ "k8s_cluster_location", "test_cluster_location",
+ NULL);
+
+ /* Enable test mode */
+ ret = flb_output_set_test(ctx, out_ffd, "formatter",
+ cb_check_k8s_container_resource,
+ NULL, NULL);
+
+ /* Start */
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data sample */
+ flb_lib_push(ctx, in_ffd, (char *) K8S_CONTAINER_NO_LOCAL_RESOURCE_ID, size);
+
+ sleep(2);
+ flb_stop(ctx);
+ flb_destroy(ctx);
+}
+
+void flb_test_resource_k8s_node_no_local_resource_id()
+{
+ int ret;
+ int size = sizeof(K8S_NODE_NO_LOCAL_RESOURCE_ID) - 1;
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+
+ /* Create context, flush every second (some checks omitted here) */
+ ctx = flb_create();
+ flb_service_set(ctx, "flush", "1", "grace", "1", NULL);
+
+ /* Lib input mode */
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ flb_input_set(ctx, in_ffd, "tag", "k8s_node.testnode", NULL);
+
+ /* Stackdriver output */
+ out_ffd = flb_output(ctx, (char *) "stackdriver", NULL);
+ flb_output_set(ctx, out_ffd,
+ "match", "k8s_node.*",
+ "resource", "k8s_node",
+ "google_service_credentials", SERVICE_CREDENTIALS,
+ "k8s_cluster_name", "test_cluster_name",
+ "k8s_cluster_location", "test_cluster_location",
+ NULL);
+
+ /* Enable test mode */
+ ret = flb_output_set_test(ctx, out_ffd, "formatter",
+ cb_check_k8s_node_resource,
+ NULL, NULL);
+
+ /* Start */
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data sample */
+ flb_lib_push(ctx, in_ffd, (char *) K8S_NODE_NO_LOCAL_RESOURCE_ID, size);
+
+ sleep(2);
+ flb_stop(ctx);
+ flb_destroy(ctx);
+}
+
+void flb_test_resource_k8s_node_custom_k8s_regex_with_dot()
+{
+ int ret;
+ int size = sizeof(K8S_NODE_LOCAL_RESOURCE_ID_WITH_DOT) - 1;
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+
+ /* Create context, flush every second (some checks omitted here) */
+ ctx = flb_create();
+ flb_service_set(ctx, "flush", "1", "grace", "1", NULL);
+
+ /* Lib input mode */
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ flb_input_set(ctx, in_ffd, "tag", "test", NULL);
+
+ /* Stackdriver output */
+ out_ffd = flb_output(ctx, (char *) "stackdriver", NULL);
+ flb_output_set(ctx, out_ffd,
+ "match", "test",
+ "resource", "k8s_node",
+ "google_service_credentials", SERVICE_CREDENTIALS,
+ "k8s_cluster_name", "test_cluster_name",
+ "k8s_cluster_location", "test_cluster_location",
+ "custom_k8s_regex", "^(?<node_name>.*)$",
+ NULL);
+
+ /* Enable test mode */
+ ret = flb_output_set_test(ctx, out_ffd, "formatter",
+ cb_check_k8s_node_custom_k8s_regex,
+ NULL, NULL);
+
+ /* Start */
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data sample */
+ flb_lib_push(ctx, in_ffd, (char *) K8S_NODE_LOCAL_RESOURCE_ID_WITH_DOT, size);
+
+ sleep(2);
+ flb_stop(ctx);
+ flb_destroy(ctx);
+}
+
+void flb_test_resource_k8s_node_custom_k8s_regex_with_long_tag()
+{
+ int ret;
+ int size = sizeof(K8S_NODE_LOCAL_RESOURCE_ID_WITH_DOT) - 1;
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+
+ /* Create context, flush every second (some checks omitted here) */
+ ctx = flb_create();
+ flb_service_set(ctx, "flush", "1", "grace", "1", NULL);
+
+ /* Lib input mode */
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ flb_input_set(ctx, in_ffd, "tag", "tagWithLongLen", NULL);
+
+ /* Stackdriver output */
+ out_ffd = flb_output(ctx, (char *) "stackdriver", NULL);
+ flb_output_set(ctx, out_ffd,
+ "match", "tagWithLongLen",
+ "resource", "k8s_node",
+ "google_service_credentials", SERVICE_CREDENTIALS,
+ "k8s_cluster_name", "test_cluster_name",
+ "k8s_cluster_location", "test_cluster_location",
+ "custom_k8s_regex", "^(?<node_name>.*)$",
+ NULL);
+
+ /* Enable test mode */
+ ret = flb_output_set_test(ctx, out_ffd, "formatter",
+ cb_check_k8s_node_custom_k8s_regex,
+ NULL, NULL);
+
+ /* Start */
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data sample */
+ flb_lib_push(ctx, in_ffd, (char *) K8S_NODE_LOCAL_RESOURCE_ID_WITH_DOT, size);
+
+ sleep(2);
+ flb_stop(ctx);
+ flb_destroy(ctx);
+}
+
+void flb_test_resource_k8s_pod_no_local_resource_id()
+{
+ int ret;
+ int size = sizeof(K8S_POD_NO_LOCAL_RESOURCE_ID) - 1;
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+
+ /* Create context, flush every second (some checks omitted here) */
+ ctx = flb_create();
+ flb_service_set(ctx, "flush", "1", "grace", "1", NULL);
+
+ /* Lib input mode */
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ flb_input_set(ctx, in_ffd, "tag", "k8s_pod.testnamespace.testpod", NULL);
+
+ /* Stackdriver output */
+ out_ffd = flb_output(ctx, (char *) "stackdriver", NULL);
+ flb_output_set(ctx, out_ffd,
+ "match", "k8s_pod.*",
+ "resource", "k8s_pod",
+ "google_service_credentials", SERVICE_CREDENTIALS,
+ "k8s_cluster_name", "test_cluster_name",
+ "k8s_cluster_location", "test_cluster_location",
+ NULL);
+
+ /* Enable test mode */
+ ret = flb_output_set_test(ctx, out_ffd, "formatter",
+ cb_check_k8s_pod_resource,
+ NULL, NULL);
+
+ /* Start */
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data sample */
+ flb_lib_push(ctx, in_ffd, (char *) K8S_POD_NO_LOCAL_RESOURCE_ID, size);
+
+ sleep(2);
+ flb_stop(ctx);
+ flb_destroy(ctx);
+}
+
+void flb_test_multi_entries_severity()
+{
+ int ret;
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+
+ /* Create context, flush every second (some checks omitted here) */
+ ctx = flb_create();
+ ret = flb_service_set(ctx, "flush", "1", "grace", "1", NULL);
+ TEST_CHECK_(ret == 0, "setting service options");
+
+ /* Tail input mode */
+ in_ffd = flb_input(ctx, (char *) "tail", NULL);
+ ret = flb_input_set(ctx, in_ffd,
+ "Path", STACKDRIVER_DATA_PATH "/stackdriver_multi_entries_severity.log",
+ "tag", "test",
+ "read_from_head", "true",
+ NULL);
+ TEST_CHECK_(ret == 0, "setting input options");
+
+ /* Stackdriver output */
+ out_ffd = flb_output(ctx, (char *) "stackdriver", NULL);
+ ret = flb_output_set(ctx, out_ffd,
+ "match", "test",
+ "resource", "gce_instance",
+ "google_service_credentials", SERVICE_CREDENTIALS,
+ "severity_key", "severity",
+ NULL);
+ TEST_CHECK_(ret == 0, "setting output options");
+
+ /* Enable test mode */
+ ret = flb_output_set_test(ctx, out_ffd, "formatter",
+ cb_check_multi_entries_severity,
+ NULL, NULL);
+
+ /* Start */
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ sleep(2);
+ flb_stop(ctx);
+ flb_destroy(ctx);
+}
+
+void flb_test_source_location_common_case()
+{
+ int ret;
+ int size = sizeof(SOURCELOCATION_COMMON_CASE) - 1;
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+
+ /* Create context, flush every second (some checks omitted here) */
+ ctx = flb_create();
+ flb_service_set(ctx, "flush", "1", "grace", "1", NULL);
+
+ /* Lib input mode */
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ flb_input_set(ctx, in_ffd, "tag", "test", NULL);
+
+ /* Stackdriver output */
+ out_ffd = flb_output(ctx, (char *) "stackdriver", NULL);
+ flb_output_set(ctx, out_ffd,
+ "match", "test",
+ "resource", "gce_instance",
+ NULL);
+
+ /* Enable test mode */
+ ret = flb_output_set_test(ctx, out_ffd, "formatter",
+ cb_check_source_location_common_case,
+ NULL, NULL);
+
+ /* Start */
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data sample */
+ flb_lib_push(ctx, in_ffd, (char *) SOURCELOCATION_COMMON_CASE, size);
+
+ sleep(2);
+ flb_stop(ctx);
+ flb_destroy(ctx);
+}
+
+void flb_test_source_location_line_in_string()
+{
+ int ret;
+ int size = sizeof(SOURCELOCATION_COMMON_CASE_LINE_IN_STRING) - 1;
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+
+ /* Create context, flush every second (some checks omitted here) */
+ ctx = flb_create();
+ flb_service_set(ctx, "flush", "1", "grace", "1", NULL);
+
+ /* Lib input mode */
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ flb_input_set(ctx, in_ffd, "tag", "test", NULL);
+
+ /* Stackdriver output */
+ out_ffd = flb_output(ctx, (char *) "stackdriver", NULL);
+ flb_output_set(ctx, out_ffd,
+ "match", "test",
+ "resource", "gce_instance",
+ NULL);
+
+ /* Enable test mode */
+ ret = flb_output_set_test(ctx, out_ffd, "formatter",
+ cb_check_source_location_common_case_line_in_string,
+ NULL, NULL);
+
+ /* Start */
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data sample */
+ flb_lib_push(ctx, in_ffd, (char *) SOURCELOCATION_COMMON_CASE_LINE_IN_STRING, size);
+
+ sleep(2);
+ flb_stop(ctx);
+ flb_destroy(ctx);
+}
+
+void flb_test_empty_source_location()
+{
+ int ret;
+ int size = sizeof(EMPTY_SOURCELOCATION) - 1;
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+
+ /* Create context, flush every second (some checks omitted here) */
+ ctx = flb_create();
+ flb_service_set(ctx, "flush", "1", "grace", "1", NULL);
+
+ /* Lib input mode */
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ flb_input_set(ctx, in_ffd, "tag", "test", NULL);
+
+ /* Stackdriver output */
+ out_ffd = flb_output(ctx, (char *) "stackdriver", NULL);
+ flb_output_set(ctx, out_ffd,
+ "match", "test",
+ "resource", "gce_instance",
+ NULL);
+
+ /* Enable test mode */
+ ret = flb_output_set_test(ctx, out_ffd, "formatter",
+ cb_check_empty_source_location,
+ NULL, NULL);
+
+ /* Start */
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data sample */
+ flb_lib_push(ctx, in_ffd, (char *) EMPTY_SOURCELOCATION, size);
+
+ sleep(2);
+ flb_stop(ctx);
+ flb_destroy(ctx);
+}
+
+void flb_test_source_location_in_string()
+{
+ int ret;
+ int size = sizeof(SOURCELOCATION_IN_STRING) - 1;
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+
+ /* Create context, flush every second (some checks omitted here) */
+ ctx = flb_create();
+ flb_service_set(ctx, "flush", "1", "grace", "1", NULL);
+
+ /* Lib input mode */
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ flb_input_set(ctx, in_ffd, "tag", "test", NULL);
+
+ /* Stackdriver output */
+ out_ffd = flb_output(ctx, (char *) "stackdriver", NULL);
+ flb_output_set(ctx, out_ffd,
+ "match", "test",
+ "resource", "gce_instance",
+ NULL);
+
+ /* Enable test mode */
+ ret = flb_output_set_test(ctx, out_ffd, "formatter",
+ cb_check_source_location_in_string,
+ NULL, NULL);
+
+ /* Start */
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data sample */
+ flb_lib_push(ctx, in_ffd, (char *) SOURCELOCATION_IN_STRING, size);
+
+ sleep(2);
+ flb_stop(ctx);
+ flb_destroy(ctx);
+}
+
+void flb_test_source_location_partial_subfields()
+{
+ int ret;
+ int size = sizeof(PARTIAL_SOURCELOCATION) - 1;
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+
+ /* Create context, flush every second (some checks omitted here) */
+ ctx = flb_create();
+ flb_service_set(ctx, "flush", "1", "grace", "1", NULL);
+
+ /* Lib input mode */
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ flb_input_set(ctx, in_ffd, "tag", "test", NULL);
+
+ /* Stackdriver output */
+ out_ffd = flb_output(ctx, (char *) "stackdriver", NULL);
+ flb_output_set(ctx, out_ffd,
+ "match", "test",
+ "resource", "gce_instance",
+ NULL);
+
+ /* Enable test mode */
+ ret = flb_output_set_test(ctx, out_ffd, "formatter",
+ cb_check_source_location_partial_subfields,
+ NULL, NULL);
+
+ /* Start */
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data sample */
+ flb_lib_push(ctx, in_ffd, (char *) PARTIAL_SOURCELOCATION, size);
+
+ sleep(2);
+ flb_stop(ctx);
+ flb_destroy(ctx);
+}
+
+void flb_test_source_location_incorrect_type_subfields()
+{
+ int ret;
+ int size = sizeof(SOURCELOCATION_SUBFIELDS_IN_INCORRECT_TYPE) - 1;
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+
+ /* Create context, flush every second (some checks omitted here) */
+ ctx = flb_create();
+ flb_service_set(ctx, "flush", "1", "grace", "1", NULL);
+
+ /* Lib input mode */
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ flb_input_set(ctx, in_ffd, "tag", "test", NULL);
+
+ /* Stackdriver output */
+ out_ffd = flb_output(ctx, (char *) "stackdriver", NULL);
+ flb_output_set(ctx, out_ffd,
+ "match", "test",
+ "resource", "gce_instance",
+ NULL);
+
+ /* Enable test mode */
+ ret = flb_output_set_test(ctx, out_ffd, "formatter",
+ cb_check_source_location_incorrect_type_subfields,
+ NULL, NULL);
+
+ /* Start */
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data sample */
+ flb_lib_push(ctx, in_ffd, (char *) SOURCELOCATION_SUBFIELDS_IN_INCORRECT_TYPE, size);
+
+ sleep(2);
+ flb_stop(ctx);
+ flb_destroy(ctx);
+}
+
+void flb_test_source_location_extra_subfields()
+{
+ int ret;
+ int size = sizeof(SOURCELOCATION_EXTRA_SUBFIELDS_EXISTED) - 1;
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+
+ /* Create context, flush every second (some checks omitted here) */
+ ctx = flb_create();
+ flb_service_set(ctx, "flush", "1", "grace", "1", NULL);
+
+ /* Lib input mode */
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ flb_input_set(ctx, in_ffd, "tag", "test", NULL);
+
+ /* Stackdriver output */
+ out_ffd = flb_output(ctx, (char *) "stackdriver", NULL);
+ flb_output_set(ctx, out_ffd,
+ "match", "test",
+ "resource", "gce_instance",
+ NULL);
+
+ /* Enable test mode */
+ ret = flb_output_set_test(ctx, out_ffd, "formatter",
+ cb_check_source_location_extra_subfields,
+ NULL, NULL);
+
+ /* Start */
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data sample */
+ flb_lib_push(ctx, in_ffd, (char *) SOURCELOCATION_EXTRA_SUBFIELDS_EXISTED, size);
+
+ sleep(2);
+ flb_stop(ctx);
+ flb_destroy(ctx);
+}
+
+void flb_test_http_request_common_case()
+{
+ int ret;
+ int size = sizeof(HTTPREQUEST_COMMON_CASE) - 1;
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+
+ /* Create context, flush every second (some checks omitted here) */
+ ctx = flb_create();
+ flb_service_set(ctx, "flush", "1", "grace", "1", NULL);
+
+ /* Lib input mode */
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ flb_input_set(ctx, in_ffd, "tag", "test", NULL);
+
+ /* Stackdriver output */
+ out_ffd = flb_output(ctx, (char *) "stackdriver", NULL);
+ flb_output_set(ctx, out_ffd,
+ "match", "test",
+ "resource", "gce_instance",
+ NULL);
+
+ /* Enable test mode */
+ ret = flb_output_set_test(ctx, out_ffd, "formatter",
+ cb_check_http_request_common_case,
+ NULL, NULL);
+
+ /* Start */
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data sample */
+ flb_lib_push(ctx, in_ffd, (char *) HTTPREQUEST_COMMON_CASE, size);
+
+ sleep(2);
+ flb_stop(ctx);
+ flb_destroy(ctx);
+}
+
+void flb_test_empty_http_request()
+{
+ int ret;
+ int size = sizeof(EMPTY_HTTPREQUEST) - 1;
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+
+ /* Create context, flush every second (some checks omitted here) */
+ ctx = flb_create();
+ flb_service_set(ctx, "flush", "1", "grace", "1", NULL);
+
+ /* Lib input mode */
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ flb_input_set(ctx, in_ffd, "tag", "test", NULL);
+
+ /* Stackdriver output */
+ out_ffd = flb_output(ctx, (char *) "stackdriver", NULL);
+ flb_output_set(ctx, out_ffd,
+ "match", "test",
+ "resource", "gce_instance",
+ NULL);
+
+ /* Enable test mode */
+ ret = flb_output_set_test(ctx, out_ffd, "formatter",
+ cb_check_empty_http_request,
+ NULL, NULL);
+
+ /* Start */
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data sample */
+ flb_lib_push(ctx, in_ffd, (char *) EMPTY_HTTPREQUEST, size);
+
+ sleep(2);
+ flb_stop(ctx);
+ flb_destroy(ctx);
+}
+
+void flb_test_http_request_in_string()
+{
+ int ret;
+ int size = sizeof(HTTPREQUEST_IN_STRING) - 1;
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+
+ /* Create context, flush every second (some checks omitted here) */
+ ctx = flb_create();
+ flb_service_set(ctx, "flush", "1", "grace", "1", NULL);
+
+ /* Lib input mode */
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ flb_input_set(ctx, in_ffd, "tag", "test", NULL);
+
+ /* Stackdriver output */
+ out_ffd = flb_output(ctx, (char *) "stackdriver", NULL);
+ flb_output_set(ctx, out_ffd,
+ "match", "test",
+ "resource", "gce_instance",
+ NULL);
+
+ /* Enable test mode */
+ ret = flb_output_set_test(ctx, out_ffd, "formatter",
+ cb_check_http_request_in_string,
+ NULL, NULL);
+
+ /* Start */
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data sample */
+ flb_lib_push(ctx, in_ffd, (char *) HTTPREQUEST_IN_STRING, size);
+
+ sleep(2);
+ flb_stop(ctx);
+ flb_destroy(ctx);
+}
+
+void flb_test_http_request_partial_subfields()
+{
+ int ret;
+ int size = sizeof(PARTIAL_HTTPREQUEST) - 1;
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+
+ /* Create context, flush every second (some checks omitted here) */
+ ctx = flb_create();
+ flb_service_set(ctx, "flush", "1", "grace", "1", NULL);
+
+ /* Lib input mode */
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ flb_input_set(ctx, in_ffd, "tag", "test", NULL);
+
+ /* Stackdriver output */
+ out_ffd = flb_output(ctx, (char *) "stackdriver", NULL);
+ flb_output_set(ctx, out_ffd,
+ "match", "test",
+ "resource", "gce_instance",
+ NULL);
+
+ /* Enable test mode */
+ ret = flb_output_set_test(ctx, out_ffd, "formatter",
+ cb_check_http_request_partial_subfields,
+ NULL, NULL);
+
+ /* Start */
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data sample */
+ flb_lib_push(ctx, in_ffd, (char *) PARTIAL_HTTPREQUEST, size);
+
+ sleep(2);
+ flb_stop(ctx);
+ flb_destroy(ctx);
+}
+
+void flb_test_http_request_incorrect_type_subfields()
+{
+ int ret;
+ int size = sizeof(HTTPREQUEST_SUBFIELDS_IN_INCORRECT_TYPE) - 1;
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+
+ /* Create context, flush every second (some checks omitted here) */
+ ctx = flb_create();
+ flb_service_set(ctx, "flush", "1", "grace", "1", NULL);
+
+ /* Lib input mode */
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ flb_input_set(ctx, in_ffd, "tag", "test", NULL);
+
+ /* Stackdriver output */
+ out_ffd = flb_output(ctx, (char *) "stackdriver", NULL);
+ flb_output_set(ctx, out_ffd,
+ "match", "test",
+ "resource", "gce_instance",
+ NULL);
+
+ /* Enable test mode */
+ ret = flb_output_set_test(ctx, out_ffd, "formatter",
+ cb_check_http_request_incorrect_type_subfields,
+ NULL, NULL);
+
+ /* Start */
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data sample */
+ flb_lib_push(ctx, in_ffd, (char *) HTTPREQUEST_SUBFIELDS_IN_INCORRECT_TYPE, size);
+
+ sleep(2);
+ flb_stop(ctx);
+ flb_destroy(ctx);
+}
+
+void flb_test_http_request_extra_subfields()
+{
+ int ret;
+ int size = sizeof(HTTPREQUEST_EXTRA_SUBFIELDS_EXISTED) - 1;
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+
+ /* Create context, flush every second (some checks omitted here) */
+ ctx = flb_create();
+ flb_service_set(ctx, "flush", "1", "grace", "1", NULL);
+
+ /* Lib input mode */
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ flb_input_set(ctx, in_ffd, "tag", "test", NULL);
+
+ /* Stackdriver output */
+ out_ffd = flb_output(ctx, (char *) "stackdriver", NULL);
+ flb_output_set(ctx, out_ffd,
+ "match", "test",
+ "resource", "gce_instance",
+ NULL);
+
+ /* Enable test mode */
+ ret = flb_output_set_test(ctx, out_ffd, "formatter",
+ cb_check_http_request_extra_subfields,
+ NULL, NULL);
+
+ /* Start */
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data sample */
+ flb_lib_push(ctx, in_ffd, (char *) HTTPREQUEST_EXTRA_SUBFIELDS_EXISTED, size);
+
+ sleep(2);
+ flb_stop(ctx);
+ flb_destroy(ctx);
+}
+
+void flb_test_http_request_latency_common_case()
+{
+ int ret;
+ int size = sizeof(HTTPREQUEST_LATENCY_COMMON_CASE) - 1;
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+
+ /* Create context, flush every second (some checks omitted here) */
+ ctx = flb_create();
+ flb_service_set(ctx, "flush", "1", "grace", "1", NULL);
+
+ /* Lib input mode */
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ flb_input_set(ctx, in_ffd, "tag", "test", NULL);
+
+ /* Stackdriver output */
+ out_ffd = flb_output(ctx, (char *) "stackdriver", NULL);
+ flb_output_set(ctx, out_ffd,
+ "match", "test",
+ "resource", "gce_instance",
+ NULL);
+
+ /* Enable test mode */
+ ret = flb_output_set_test(ctx, out_ffd, "formatter",
+ cb_check_http_request_lantency_common_case,
+ NULL, NULL);
+
+ /* Start */
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data sample */
+ flb_lib_push(ctx, in_ffd, (char *) HTTPREQUEST_LATENCY_COMMON_CASE, size);
+
+ sleep(2);
+ flb_stop(ctx);
+ flb_destroy(ctx);
+}
+
+void flb_test_http_request_latency_invalid_spaces()
+{
+ int ret;
+ int size = sizeof(HTTPREQUEST_LATENCY_INVALID_SPACES) - 1;
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+
+ /* Create context, flush every second (some checks omitted here) */
+ ctx = flb_create();
+ flb_service_set(ctx, "flush", "1", "grace", "1", NULL);
+
+ /* Lib input mode */
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ flb_input_set(ctx, in_ffd, "tag", "test", NULL);
+
+ /* Stackdriver output */
+ out_ffd = flb_output(ctx, (char *) "stackdriver", NULL);
+ flb_output_set(ctx, out_ffd,
+ "match", "test",
+ "resource", "gce_instance",
+ NULL);
+
+ /* Enable test mode */
+ ret = flb_output_set_test(ctx, out_ffd, "formatter",
+ cb_check_http_request_latency_incorrect_format,
+ NULL, NULL);
+
+ /* Start */
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data sample */
+ flb_lib_push(ctx, in_ffd, (char *) HTTPREQUEST_LATENCY_INVALID_SPACES, size);
+
+ sleep(2);
+ flb_stop(ctx);
+ flb_destroy(ctx);
+}
+
+void flb_test_http_request_latency_invalid_string()
+{
+ int ret;
+ int size = sizeof(HTTPREQUEST_LATENCY_INVALID_STRING) - 1;
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+
+ /* Create context, flush every second (some checks omitted here) */
+ ctx = flb_create();
+ flb_service_set(ctx, "flush", "1", "grace", "1", NULL);
+
+ /* Lib input mode */
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ flb_input_set(ctx, in_ffd, "tag", "test", NULL);
+
+ /* Stackdriver output */
+ out_ffd = flb_output(ctx, (char *) "stackdriver", NULL);
+ flb_output_set(ctx, out_ffd,
+ "match", "test",
+ "resource", "gce_instance",
+ NULL);
+
+ /* Enable test mode */
+ ret = flb_output_set_test(ctx, out_ffd, "formatter",
+ cb_check_http_request_latency_incorrect_format,
+ NULL, NULL);
+
+ /* Start */
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data sample */
+ flb_lib_push(ctx, in_ffd, (char *) HTTPREQUEST_LATENCY_INVALID_STRING, size);
+
+ sleep(2);
+ flb_stop(ctx);
+ flb_destroy(ctx);
+}
+
+void flb_test_http_request_latency_invalid_end()
+{
+ int ret;
+ int size = sizeof(HTTPREQUEST_LATENCY_INVALID_END) - 1;
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+
+ /* Create context, flush every second (some checks omitted here) */
+ ctx = flb_create();
+ flb_service_set(ctx, "flush", "1", "grace", "1", NULL);
+
+ /* Lib input mode */
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ flb_input_set(ctx, in_ffd, "tag", "test", NULL);
+
+ /* Stackdriver output */
+ out_ffd = flb_output(ctx, (char *) "stackdriver", NULL);
+ flb_output_set(ctx, out_ffd,
+ "match", "test",
+ "resource", "gce_instance",
+ NULL);
+
+ /* Enable test mode */
+ ret = flb_output_set_test(ctx, out_ffd, "formatter",
+ cb_check_http_request_latency_incorrect_format,
+ NULL, NULL);
+
+ /* Start */
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data sample */
+ flb_lib_push(ctx, in_ffd, (char *) HTTPREQUEST_LATENCY_INVALID_END, size);
+
+ sleep(2);
+ flb_stop(ctx);
+ flb_destroy(ctx);
+}
+
+void flb_test_timestamp_format_object_common()
+{
+ int ret;
+ int size = sizeof(TIMESTAMP_FORMAT_OBJECT_COMMON_CASE) - 1;
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+
+ /* Create context, flush every second (some checks omitted here) */
+ ctx = flb_create();
+ flb_service_set(ctx, "flush", "1", "grace", "1", NULL);
+
+ /* Lib input mode */
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ flb_input_set(ctx, in_ffd, "tag", "test", NULL);
+
+ /* Stackdriver output */
+ out_ffd = flb_output(ctx, (char *) "stackdriver", NULL);
+ flb_output_set(ctx, out_ffd,
+ "match", "test",
+ "resource", "gce_instance",
+ NULL);
+
+ /* Enable test mode */
+ ret = flb_output_set_test(ctx, out_ffd, "formatter",
+ cb_check_timestamp_format_object_common_case,
+ NULL, NULL);
+
+ /* Start */
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data sample */
+ flb_lib_push(ctx, in_ffd, (char *) TIMESTAMP_FORMAT_OBJECT_COMMON_CASE, size);
+
+ sleep(2);
+ flb_stop(ctx);
+ flb_destroy(ctx);
+}
+
+void flb_test_timestamp_format_object_not_a_map()
+{
+ int ret;
+ int size = sizeof(TIMESTAMP_FORMAT_OBJECT_NOT_A_MAP) - 1;
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+
+ /* Create context, flush every second (some checks omitted here) */
+ ctx = flb_create();
+ flb_service_set(ctx, "flush", "1", "grace", "1", NULL);
+
+ /* Lib input mode */
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ flb_input_set(ctx, in_ffd, "tag", "test", NULL);
+
+ /* Stackdriver output */
+ out_ffd = flb_output(ctx, (char *) "stackdriver", NULL);
+ flb_output_set(ctx, out_ffd,
+ "match", "test",
+ "resource", "gce_instance",
+ NULL);
+
+ /* Enable test mode */
+ ret = flb_output_set_test(ctx, out_ffd, "formatter",
+ cb_check_timestamp_format_object_not_a_map,
+ NULL, NULL);
+
+ /* Start */
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data sample */
+ flb_lib_push(ctx, in_ffd, (char *) TIMESTAMP_FORMAT_OBJECT_NOT_A_MAP, size);
+
+ sleep(2);
+ flb_stop(ctx);
+ flb_destroy(ctx);
+}
+
+void flb_test_timestamp_format_object_missing_subfield()
+{
+ int ret;
+ int size = sizeof(TIMESTAMP_FORMAT_OBJECT_MISSING_SUBFIELD) - 1;
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+
+ /* Create context, flush every second (some checks omitted here) */
+ ctx = flb_create();
+ flb_service_set(ctx, "flush", "1", "grace", "1", NULL);
+
+ /* Lib input mode */
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ flb_input_set(ctx, in_ffd, "tag", "test", NULL);
+
+ /* Stackdriver output */
+ out_ffd = flb_output(ctx, (char *) "stackdriver", NULL);
+ flb_output_set(ctx, out_ffd,
+ "match", "test",
+ "resource", "gce_instance",
+ NULL);
+
+ /* Enable test mode */
+ ret = flb_output_set_test(ctx, out_ffd, "formatter",
+ cb_check_timestamp_format_object_missing_subfield,
+ NULL, NULL);
+
+ /* Start */
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data sample */
+ flb_lib_push(ctx, in_ffd, (char *) TIMESTAMP_FORMAT_OBJECT_MISSING_SUBFIELD, size);
+
+ sleep(2);
+ flb_stop(ctx);
+ flb_destroy(ctx);
+}
+
+void flb_test_timestamp_format_object_incorrect_subfields()
+{
+ int ret;
+ int size = sizeof(TIMESTAMP_FORMAT_OBJECT_INCORRECT_TYPE_SUBFIELDS) - 1;
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+
+ /* Create context, flush every second (some checks omitted here) */
+ ctx = flb_create();
+ flb_service_set(ctx, "flush", "1", "grace", "1", NULL);
+
+ /* Lib input mode */
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ flb_input_set(ctx, in_ffd, "tag", "test", NULL);
+
+ /* Stackdriver output */
+ out_ffd = flb_output(ctx, (char *) "stackdriver", NULL);
+ flb_output_set(ctx, out_ffd,
+ "match", "test",
+ "resource", "gce_instance",
+ NULL);
+
+ /* Enable test mode */
+ ret = flb_output_set_test(ctx, out_ffd, "formatter",
+ cb_check_timestamp_format_object_incorrect_subfields,
+ NULL, NULL);
+
+ /* Start */
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data sample */
+ flb_lib_push(ctx, in_ffd, (char *) TIMESTAMP_FORMAT_OBJECT_INCORRECT_TYPE_SUBFIELDS, size);
+
+ sleep(2);
+ flb_stop(ctx);
+ flb_destroy(ctx);
+}
+
+void flb_test_timestamp_format_duo_fields_common_case()
+{
+ int ret;
+ int size = sizeof(TIMESTAMP_FORMAT_DUO_FIELDS_COMMON_CASE) - 1;
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+
+ /* Create context, flush every second (some checks omitted here) */
+ ctx = flb_create();
+ flb_service_set(ctx, "flush", "1", "grace", "1", NULL);
+
+ /* Lib input mode */
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ flb_input_set(ctx, in_ffd, "tag", "test", NULL);
+
+ /* Stackdriver output */
+ out_ffd = flb_output(ctx, (char *) "stackdriver", NULL);
+ flb_output_set(ctx, out_ffd,
+ "match", "test",
+ "resource", "gce_instance",
+ NULL);
+
+ /* Enable test mode */
+ ret = flb_output_set_test(ctx, out_ffd, "formatter",
+ cb_check_timestamp_format_duo_fields_common_case,
+ NULL, NULL);
+
+ /* Start */
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data sample */
+ flb_lib_push(ctx, in_ffd, (char *) TIMESTAMP_FORMAT_DUO_FIELDS_COMMON_CASE, size);
+
+ sleep(2);
+ flb_stop(ctx);
+ flb_destroy(ctx);
+}
+
+void flb_test_timestamp_format_duo_fields_missing_nanos()
+{
+ int ret;
+ int size = sizeof(TIMESTAMP_FORMAT_DUO_FIELDS_MISSING_NANOS) - 1;
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+
+ /* Create context, flush every second (some checks omitted here) */
+ ctx = flb_create();
+ flb_service_set(ctx, "flush", "1", "grace", "1", NULL);
+
+ /* Lib input mode */
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ flb_input_set(ctx, in_ffd, "tag", "test", NULL);
+
+ /* Stackdriver output */
+ out_ffd = flb_output(ctx, (char *) "stackdriver", NULL);
+ flb_output_set(ctx, out_ffd,
+ "match", "test",
+ "resource", "gce_instance",
+ NULL);
+
+ /* Enable test mode */
+ ret = flb_output_set_test(ctx, out_ffd, "formatter",
+ cb_check_timestamp_format_duo_fields_missing_nanos,
+ NULL, NULL);
+
+ /* Start */
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data sample */
+ flb_lib_push(ctx, in_ffd, (char *) TIMESTAMP_FORMAT_DUO_FIELDS_MISSING_NANOS, size);
+
+ sleep(2);
+ flb_stop(ctx);
+ flb_destroy(ctx);
+}
+
+void flb_test_timestamp_format_duo_fields_incorrect_type()
+{
+ int ret;
+ int size = sizeof(TIMESTAMP_FORMAT_DUO_FIELDS_INCORRECT_TYPE) - 1;
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+
+ /* Create context, flush every second (some checks omitted here) */
+ ctx = flb_create();
+ flb_service_set(ctx, "flush", "1", "grace", "1", NULL);
+
+ /* Lib input mode */
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ flb_input_set(ctx, in_ffd, "tag", "test", NULL);
+
+ /* Stackdriver output */
+ out_ffd = flb_output(ctx, (char *) "stackdriver", NULL);
+ flb_output_set(ctx, out_ffd,
+ "match", "test",
+ "resource", "gce_instance",
+ NULL);
+
+ /* Enable test mode */
+ ret = flb_output_set_test(ctx, out_ffd, "formatter",
+ cb_check_timestamp_format_duo_fields_incorrect_type,
+ NULL, NULL);
+
+ /* Start */
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data sample */
+ flb_lib_push(ctx, in_ffd, (char *) TIMESTAMP_FORMAT_DUO_FIELDS_INCORRECT_TYPE, size);
+
+ sleep(2);
+ flb_stop(ctx);
+ flb_destroy(ctx);
+}
+
+/* Test list */
+TEST_LIST = {
+ {"severity_multi_entries", flb_test_multi_entries_severity },
+ {"resource_global", flb_test_resource_global },
+ {"resource_global_custom_prefix", flb_test_resource_global_custom_prefix },
+ {"resource_gce_instance", flb_test_resource_gce_instance },
+
+ /* generic resources */
+ {"resource_generic_node_creds", flb_test_resource_generic_node_creds},
+ {"resource_generic_node_metadata", flb_test_resource_generic_node_metadata},
+ {"resource_generic_task_creds", flb_test_resource_generic_task_creds},
+ {"resource_generic_task_metadata", flb_test_resource_generic_task_metadata},
+
+ /* test trace */
+ {"trace_no_autoformat", flb_test_trace_no_autoformat},
+ {"trace_stackdriver_autoformat", flb_test_trace_stackdriver_autoformat},
+
+ /* test span id */
+ {"span_id", flb_test_span_id},
+
+ /* test trace sampled */
+ {"trace_sampled_true", flb_test_trace_sampled_true},
+ {"trace_sampled_false", flb_test_trace_sampled_false},
+
+ /* test metadata server */
+ {"set_metadata_server", flb_test_set_metadata_server},
+
+ /* test log name */
+ {"log_name_override", flb_test_log_name_override},
+ {"log_name_no_override", flb_test_log_name_no_override},
+
+ /* test insertId */
+ {"insertId_common_case", flb_test_insert_id_common_case},
+ {"empty_insertId", flb_test_empty_insert_id},
+ {"insertId_incorrect_type_int", flb_test_insert_id_incorrect_type},
+
+ /* test operation */
+ {"operation_common_case", flb_test_operation_common},
+ {"empty_operation", flb_test_empty_operation},
+ {"operation_not_a_map", flb_test_operation_in_string},
+ {"operation_partial_subfields", flb_test_operation_partial_subfields},
+ {"operation_subfields_in_incorrect_type", flb_test_operation_incorrect_type_subfields},
+ {"operation_extra_subfields_exist", flb_test_operation_extra_subfields},
+
+ /* test sourceLocation */
+ {"sourceLocation_common_case", flb_test_source_location_common_case},
+ {"sourceLocation_line_in_string", flb_test_source_location_line_in_string},
+ {"empty_sourceLocation", flb_test_empty_source_location},
+ {"sourceLocation_not_a_map", flb_test_source_location_in_string},
+ {"sourceLocation_partial_subfields", flb_test_source_location_partial_subfields},
+ {"sourceLocation_subfields_in_incorrect_type", flb_test_source_location_incorrect_type_subfields},
+ {"sourceLocation_extra_subfields_exist", flb_test_source_location_extra_subfields},
+
+ /* test monitored resource */
+ {"monitored_resource_common", flb_test_monitored_resource_common},
+ {"monitored_resource_priority_higher_than_local_resource_id", flb_test_monitored_resource_priority_higher_than_local_resource_id},
+ {"monitored_resource_priority_higher_than_gce_instance", flb_test_monitored_resource_priority_higher_than_gce_instance},
+
+ /* test k8s */
+ {"resource_k8s_container_common", flb_test_resource_k8s_container_common },
+ {"resource_k8s_container_no_local_resource_id", flb_test_resource_k8s_container_no_local_resource_id },
+ {"resource_k8s_container_multi_tag_value", flb_test_resource_k8s_container_multi_tag_value } ,
+ {"resource_k8s_container_custom_tag_prefix", flb_test_resource_k8s_container_custom_tag_prefix },
+ {"resource_k8s_container_custom_tag_prefix_with_dot", flb_test_resource_k8s_container_custom_tag_prefix_with_dot },
+ {"resource_k8s_container_default_tag_regex", flb_test_resource_k8s_container_default_tag_regex },
+ {"resource_k8s_container_custom_k8s_regex", flb_test_resource_k8s_container_custom_k8s_regex },
+ {"resource_k8s_container_custom_k8s_regex_custom_prefix", flb_test_resource_k8s_container_custom_k8s_regex_custom_prefix },
+ {"resource_k8s_node_common", flb_test_resource_k8s_node_common },
+ {"resource_k8s_node_no_local_resource_id", flb_test_resource_k8s_node_no_local_resource_id },
+ {"resource_k8s_node_custom_k8s_regex_with_dot", flb_test_resource_k8s_node_custom_k8s_regex_with_dot },
+ {"resource_k8s_node_custom_k8s_regex_with_long_tag", flb_test_resource_k8s_node_custom_k8s_regex_with_long_tag },
+ {"resource_k8s_pod_common", flb_test_resource_k8s_pod_common },
+ {"resource_k8s_pod_no_local_resource_id", flb_test_resource_k8s_pod_no_local_resource_id },
+ {"default_labels", flb_test_default_labels },
+ {"custom_labels", flb_test_custom_labels },
+ {"config_labels_conflict", flb_test_config_labels_conflict },
+ {"config_labels_no_conflict", flb_test_config_labels_no_conflict },
+ {"default_labels_k8s_resource_type", flb_test_default_labels_k8s_resource_type },
+ {"custom_labels_k8s_resource_type", flb_test_custom_labels_k8s_resource_type },
+
+ /* test resource labels api */
+ {"resource_labels_one_field", flb_test_resource_labels_one_field },
+ {"resource_labels_plaintext", flb_test_resource_labels_plaintext },
+ {"resource_labels_multiple_fields", flb_test_resource_labels_multiple_fields },
+ {"resource_labels_nested_fields", flb_test_resource_labels_nested_fields },
+ {"resource_labels_layered_nested_fields", flb_test_resource_labels_layered_nested_fields },
+ {"resource_labels_original_does_not_exist", flb_test_resource_labels_original_does_not_exist },
+ {"resource_labels_nested_original_does_not_exist", flb_test_resource_labels_nested_original_does_not_exist },
+ {"resource_labels_nested_original_partially_exists", flb_test_resource_labels_nested_original_partially_exists },
+ {"resource_labels_one_field_with_spaces", flb_test_resource_labels_one_field_with_spaces },
+ {"resource_labels_multiple_fields_with_spaces", flb_test_resource_labels_multiple_fields_with_spaces },
+ {"resource_labels_empty_input", flb_test_resource_labels_empty_input },
+ {"resource_labels_duplicate_assignment", flb_test_resource_labels_duplicate_assignment },
+ {"resource_labels_project_id_not_overridden", flb_test_resource_labels_project_id_not_overridden },
+ {"resource_labels_has_priority", flb_test_resource_labels_has_priority },
+ {"resource_labels_fallsback_when_required_not_specified", flb_test_resource_labels_fallsback_when_required_not_specified },
+ {"resource_labels_fallsback_when_required_partially_specified", flb_test_resource_labels_fallsback_when_required_partially_specified },
+ {"resource_labels_k8s_container", flb_test_resource_labels_k8s_container },
+ {"resource_labels_k8s_node", flb_test_resource_labels_k8s_node },
+ {"resource_labels_k8s_pod", flb_test_resource_labels_k8s_pod },
+ {"resource_labels_generic_node", flb_test_resource_labels_generic_node },
+ {"resource_labels_generic_task", flb_test_resource_labels_generic_task },
+
+ /* test httpRequest */
+ {"httpRequest_common_case", flb_test_http_request_common_case},
+ {"empty_httpRequest", flb_test_empty_http_request},
+ {"httpRequest_not_a_map", flb_test_http_request_in_string},
+ {"httpRequest_partial_subfields", flb_test_http_request_partial_subfields},
+ {"httpRequest_subfields_in_incorret_type", flb_test_http_request_incorrect_type_subfields},
+ {"httpRequest_extra_subfields_exist", flb_test_http_request_extra_subfields},
+ {"httpRequest_common_latency", flb_test_http_request_latency_common_case},
+ {"httpRequest_latency_incorrect_spaces", flb_test_http_request_latency_invalid_spaces},
+ {"httpRequest_latency_incorrect_string", flb_test_http_request_latency_invalid_string},
+ {"httpRequest_latency_incorrect_end", flb_test_http_request_latency_invalid_end},
+
+ /* test timestamp */
+ {"timestamp_format_object_common_case", flb_test_timestamp_format_object_common},
+ {"timestamp_format_object_not_a_map", flb_test_timestamp_format_object_not_a_map},
+ {"timestamp_format_object_missing_subfield", flb_test_timestamp_format_object_missing_subfield},
+ {"timestamp_format_object_incorrect_type_subfields", flb_test_timestamp_format_object_incorrect_subfields},
+
+ {"timestamp_format_duo_fields_common_case", flb_test_timestamp_format_duo_fields_common_case},
+ {"timestamp_format_duo_fields_missing_nanos", flb_test_timestamp_format_duo_fields_missing_nanos},
+ {"timestamp_format_duo_fields_incorrect_type", flb_test_timestamp_format_duo_fields_incorrect_type},
+
+ {NULL, NULL}
+};
diff --git a/fluent-bit/tests/runtime/out_stdout.c b/fluent-bit/tests/runtime/out_stdout.c
new file mode 100644
index 00000000..26182fbe
--- /dev/null
+++ b/fluent-bit/tests/runtime/out_stdout.c
@@ -0,0 +1,138 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+#include <fluent-bit.h>
+#include "flb_tests_runtime.h"
+
+/* Test data */
+#include "data/common/json_invalid.h" /* JSON_INVALID */
+#include "data/common/json_long.h" /* JSON_LONG */
+#include "data/common/json_small.h" /* JSON_SMALL */
+
+/* Test functions */
+void flb_test_stdout_json_invalid(void);
+void flb_test_stdout_json_long(void);
+void flb_test_stdout_json_small(void);
+
+/* Test list */
+TEST_LIST = {
+ {"json_invalid", flb_test_stdout_json_invalid },
+ {"json_long", flb_test_stdout_json_long },
+ {"json_small", flb_test_stdout_json_small },
+ {NULL, NULL}
+};
+
+
+void flb_test_stdout_json_invalid(void)
+{
+ int i;
+ int ret;
+ int total;
+ int bytes;
+ char *p = (char *) JSON_INVALID;
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+
+ ctx = flb_create();
+ flb_service_set(ctx, "Flush", "1", "Grace", "1", "Log_Level", "error", NULL);
+
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ TEST_CHECK(in_ffd >= 0);
+ flb_input_set(ctx, in_ffd, "tag", "test", NULL);
+
+ out_ffd = flb_output(ctx, (char *) "stdout", NULL);
+ TEST_CHECK(out_ffd >= 0);
+ flb_output_set(ctx, out_ffd, "match", "test", NULL);
+
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ total = 0;
+ for (i = 0; i < (int) sizeof(JSON_INVALID) - 1; i++) {
+ bytes = flb_lib_push(ctx, in_ffd, p + i, 1);
+ TEST_CHECK(bytes == 1);
+ total++;
+ }
+
+ sleep(1); /* waiting flush */
+
+ flb_stop(ctx);
+ flb_destroy(ctx);
+}
+
+/* It writes a very long JSON map (> 100KB) byte by byte */
+void flb_test_stdout_json_long(void)
+{
+ int i;
+ int ret;
+ int total;
+ int bytes;
+ char *p = (char *) JSON_LONG;
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+
+ ctx = flb_create();
+ flb_service_set(ctx, "Flush", "1", "Grace", "1", "Log_Level", "error", NULL);
+
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ TEST_CHECK(in_ffd >= 0);
+ flb_input_set(ctx, in_ffd, "tag", "test", NULL);
+
+ out_ffd = flb_output(ctx, (char *) "stdout", NULL);
+ TEST_CHECK(out_ffd >= 0);
+ flb_output_set(ctx, out_ffd, "match", "test", NULL);
+
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ total = 0;
+ for (i = 0; i < (int) sizeof(JSON_LONG) - 1; i++) {
+ bytes = flb_lib_push(ctx, in_ffd, p + i, 1);
+ TEST_CHECK(bytes == 1);
+ total++;
+ }
+
+ sleep(1); /* waiting flush */
+
+ flb_stop(ctx);
+ flb_destroy(ctx);
+}
+
+void flb_test_stdout_json_small(void)
+{
+ int i;
+ int ret;
+ int total;
+ int bytes;
+ char *p = (char *) JSON_SMALL;
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+
+ ctx = flb_create();
+ flb_service_set(ctx, "Flush", "1", "Grace", "1", "Log_Level", "error", NULL);
+
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ TEST_CHECK(in_ffd >= 0);
+ flb_input_set(ctx, in_ffd, "tag", "test", NULL);
+
+ out_ffd = flb_output(ctx, (char *) "stdout", NULL);
+ TEST_CHECK(out_ffd >= 0);
+ flb_output_set(ctx, out_ffd, "match", "test", NULL);
+
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ total = 0;
+ for (i = 0; i < (int) sizeof(JSON_SMALL) - 1; i++) {
+ bytes = flb_lib_push(ctx, in_ffd, p + i, 1);
+ TEST_CHECK(bytes == 1);
+ total++;
+ }
+
+ sleep(1); /* waiting flush */
+
+ flb_stop(ctx);
+ flb_destroy(ctx);
+}
diff --git a/fluent-bit/tests/runtime/out_syslog.c b/fluent-bit/tests/runtime/out_syslog.c
new file mode 100644
index 00000000..aa9edab3
--- /dev/null
+++ b/fluent-bit/tests/runtime/out_syslog.c
@@ -0,0 +1,1644 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/* Fluent Bit
+ * ==========
+ * Copyright (C) 2019-2022 The Fluent Bit Authors
+ * Copyright (C) 2015-2018 Treasure Data Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <fluent-bit.h>
+#include <fluent-bit/flb_compat.h>
+#include <fluent-bit/flb_time.h>
+#include <fluent-bit/flb_pack.h>
+#include <fluent-bit/flb_socket.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include "flb_tests_runtime.h"
+
+struct test_ctx {
+ flb_ctx_t *flb; /* Fluent Bit library context */
+ int i_ffd; /* Input fd */
+ int f_ffd; /* Filter fd (unused) */
+ int o_ffd; /* Output fd */
+};
+
+
+pthread_mutex_t result_mutex = PTHREAD_MUTEX_INITIALIZER;
+int num_output = 0;
+static int get_output_num()
+{
+ int ret;
+ pthread_mutex_lock(&result_mutex);
+ ret = num_output;
+ pthread_mutex_unlock(&result_mutex);
+
+ return ret;
+}
+
+static void set_output_num(int num)
+{
+ pthread_mutex_lock(&result_mutex);
+ num_output = num;
+ pthread_mutex_unlock(&result_mutex);
+}
+
+static void clear_output_num()
+{
+ set_output_num(0);
+}
+
+struct str_list {
+ size_t size;
+ char **lists;
+};
+
+/* Callback to check expected results */
+static void cb_check_str_list(void *ctx, int ffd, int res_ret,
+ void *res_data, size_t res_size, void *data)
+{
+ char *p;
+ flb_sds_t out_line = res_data;
+ int num = get_output_num();
+ size_t i;
+ struct str_list *l = (struct str_list *)data;
+
+ if (!TEST_CHECK(res_data != NULL)) {
+ TEST_MSG("res_data is NULL");
+ return;
+ }
+
+ if (!TEST_CHECK(l != NULL)) {
+ TEST_MSG("l is NULL");
+ flb_sds_destroy(out_line);
+ return;
+ }
+
+ if(!TEST_CHECK(res_ret == 0)) {
+ TEST_MSG("callback ret=%d", res_ret);
+ }
+ if (!TEST_CHECK(res_data != NULL)) {
+ TEST_MSG("res_data is NULL");
+ flb_sds_destroy(out_line);
+ return;
+ }
+
+ for (i=0; i<l->size; i++) {
+ p = strstr(out_line, l->lists[i]);
+ if (!TEST_CHECK(p != NULL)) {
+ TEST_MSG(" Got :%s\n expect:%s", out_line, l->lists[i]);
+ }
+ }
+ set_output_num(num+1);
+
+ flb_sds_destroy(out_line);
+}
+
+static struct test_ctx *test_ctx_create()
+{
+ int i_ffd;
+ int o_ffd;
+ struct test_ctx *ctx = NULL;
+
+ ctx = flb_malloc(sizeof(struct test_ctx));
+ if (!TEST_CHECK(ctx != NULL)) {
+ TEST_MSG("malloc failed");
+ flb_errno();
+ return NULL;
+ }
+
+ /* Service config */
+ ctx->flb = flb_create();
+ flb_service_set(ctx->flb,
+ "Flush", "0.200000000",
+ "Grace", "1",
+ "Log_Level", "error",
+ NULL);
+
+ /* Input */
+ i_ffd = flb_input(ctx->flb, (char *) "lib", NULL);
+ TEST_CHECK(i_ffd >= 0);
+ ctx->i_ffd = i_ffd;
+
+ /* Output */
+ o_ffd = flb_output(ctx->flb, (char *) "syslog", NULL);
+ ctx->o_ffd = o_ffd;
+
+ return ctx;
+}
+
+static void test_ctx_destroy(struct test_ctx *ctx)
+{
+ TEST_CHECK(ctx != NULL);
+
+ sleep(1);
+ flb_stop(ctx->flb);
+ flb_destroy(ctx->flb);
+ flb_free(ctx);
+}
+
+void flb_test_syslog_rfc5424()
+{
+ struct test_ctx *ctx;
+ int ret;
+ int num;
+
+ char *buf = "[1, {\"msg\":\"hello world\"}]";
+ size_t size = strlen(buf);
+
+ char *expected_strs[] = {"hello world", "1970-01-01T00:00:01.000000Z"};
+ struct str_list expected = {
+ .size = sizeof(expected_strs)/sizeof(char*),
+ .lists = &expected_strs[0],
+ };
+
+ clear_output_num();
+
+ ctx = test_ctx_create();
+ if (!TEST_CHECK(ctx != NULL)) {
+ TEST_MSG("test_ctx_create failed");
+ exit(EXIT_FAILURE);
+ }
+
+ ret = flb_output_set(ctx->flb, ctx->o_ffd,
+ "match", "*",
+ "syslog_format", "rfc5424",
+ "syslog_message_key", "msg",
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ ret = flb_output_set_test(ctx->flb, ctx->o_ffd,
+ "formatter", cb_check_str_list,
+ &expected, NULL);
+ TEST_CHECK(ret == 0);
+
+ /* Start the engine */
+ ret = flb_start(ctx->flb);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data sample */
+ ret = flb_lib_push(ctx->flb, ctx->i_ffd, (char *) buf, size);
+ TEST_CHECK(ret >= 0);
+
+ /* waiting to flush */
+ flb_time_msleep(500);
+
+ num = get_output_num();
+ if (!TEST_CHECK(num > 0)) {
+ TEST_MSG("no outputs");
+ }
+
+ test_ctx_destroy(ctx);
+}
+
+void flb_test_severity_key_rfc5424()
+{
+ struct test_ctx *ctx;
+ int ret;
+ int num;
+
+ char *buf = "[1, {\"msg\":\"hello world\", \"s_key\":\"5\"}]";
+ size_t size = strlen(buf);
+
+ char *expected_strs[] = {"hello world", "1970-01-01T00:00:01.000000Z", "<13>" /* 1(user-level messages) * 8 + 5(severity) */,
+ "<13>1 1970-01-01T00:00:01.000000Z - - - - - hello world"};
+ struct str_list expected = {
+ .size = sizeof(expected_strs)/sizeof(char*),
+ .lists = &expected_strs[0],
+ };
+
+ clear_output_num();
+
+ ctx = test_ctx_create();
+ if (!TEST_CHECK(ctx != NULL)) {
+ TEST_MSG("test_ctx_create failed");
+ exit(EXIT_FAILURE);
+ }
+
+ ret = flb_output_set(ctx->flb, ctx->o_ffd,
+ "match", "*",
+ "syslog_format", "rfc5424",
+ "syslog_message_key", "msg",
+ "syslog_severity_key", "s_key",
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ ret = flb_output_set_test(ctx->flb, ctx->o_ffd,
+ "formatter", cb_check_str_list,
+ &expected, NULL);
+ TEST_CHECK(ret == 0);
+
+ /* Start the engine */
+ ret = flb_start(ctx->flb);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data sample */
+ ret = flb_lib_push(ctx->flb, ctx->i_ffd, (char *) buf, size);
+ TEST_CHECK(ret >= 0);
+
+ /* waiting to flush */
+ flb_time_msleep(500);
+
+ num = get_output_num();
+ if (!TEST_CHECK(num > 0)) {
+ TEST_MSG("no outputs");
+ }
+
+ test_ctx_destroy(ctx);
+}
+
+
+void flb_test_severity_preset_rfc5424()
+{
+ struct test_ctx *ctx;
+ int ret;
+ int num;
+
+ char *buf = "[1, {\"msg\":\"hello world\", \"s_key\":\"5\"}]";
+ size_t size = strlen(buf);
+
+ char *expected_strs[] = {"hello world", "1970-01-01T00:00:01.000000Z", "<13>" /* 1(user-level messages) * 8 + 5(severity) */,
+ "<13>1 1970-01-01T00:00:01.000000Z - - - - - hello world"};
+ struct str_list expected = {
+ .size = sizeof(expected_strs)/sizeof(char*),
+ .lists = &expected_strs[0],
+ };
+
+ clear_output_num();
+
+ ctx = test_ctx_create();
+ if (!TEST_CHECK(ctx != NULL)) {
+ TEST_MSG("test_ctx_create failed");
+ exit(EXIT_FAILURE);
+ }
+
+ ret = flb_output_set(ctx->flb, ctx->o_ffd,
+ "match", "*",
+ "syslog_format", "rfc5424",
+ "syslog_message_key", "msg",
+ "syslog_severity_preset", "5",
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ ret = flb_output_set_test(ctx->flb, ctx->o_ffd,
+ "formatter", cb_check_str_list,
+ &expected, NULL);
+ TEST_CHECK(ret == 0);
+
+ /* Start the engine */
+ ret = flb_start(ctx->flb);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data sample */
+ ret = flb_lib_push(ctx->flb, ctx->i_ffd, (char *) buf, size);
+ TEST_CHECK(ret >= 0);
+
+ /* waiting to flush */
+ flb_time_msleep(500);
+
+ num = get_output_num();
+ if (!TEST_CHECK(num > 0)) {
+ TEST_MSG("no outputs");
+ }
+
+ test_ctx_destroy(ctx);
+}
+
+void flb_test_severity_key_rfc3164()
+{
+ struct test_ctx *ctx;
+ int ret;
+ int num;
+
+ char *buf = "[1, {\"msg\":\"hello world\", \"s_key\":\"5\"}]";
+ size_t size = strlen(buf);
+
+ char *expected_strs[] = {"hello world", "Jan 1 00:00:01", "<13>" /* 1(user-level messages) * 8 + 5(severity) */,
+ "<13>Jan 1 00:00:01 hello world"};
+ struct str_list expected = {
+ .size = sizeof(expected_strs)/sizeof(char*),
+ .lists = &expected_strs[0],
+ };
+
+ clear_output_num();
+
+ ctx = test_ctx_create();
+ if (!TEST_CHECK(ctx != NULL)) {
+ TEST_MSG("test_ctx_create failed");
+ exit(EXIT_FAILURE);
+ }
+
+ ret = flb_output_set(ctx->flb, ctx->o_ffd,
+ "match", "*",
+ "syslog_format", "rfc3164",
+ "syslog_message_key", "msg",
+ "syslog_severity_key", "s_key",
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ ret = flb_output_set_test(ctx->flb, ctx->o_ffd,
+ "formatter", cb_check_str_list,
+ &expected, NULL);
+ TEST_CHECK(ret == 0);
+
+ /* Start the engine */
+ ret = flb_start(ctx->flb);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data sample */
+ ret = flb_lib_push(ctx->flb, ctx->i_ffd, (char *) buf, size);
+ TEST_CHECK(ret >= 0);
+
+ /* waiting to flush */
+ flb_time_msleep(500);
+
+ num = get_output_num();
+ if (!TEST_CHECK(num > 0)) {
+ TEST_MSG("no outputs");
+ }
+
+ test_ctx_destroy(ctx);
+}
+
+void flb_test_severity_preset_rfc3164()
+{
+ struct test_ctx *ctx;
+ int ret;
+ int num;
+
+ char *buf = "[1, {\"msg\":\"hello world\", \"s_key\":\"5\"}]";
+ size_t size = strlen(buf);
+
+ char *expected_strs[] = {"hello world", "Jan 1 00:00:01", "<13>" /* 1(user-level messages) * 8 + 5(severity) */,
+ "<13>Jan 1 00:00:01 hello world"};
+ struct str_list expected = {
+ .size = sizeof(expected_strs)/sizeof(char*),
+ .lists = &expected_strs[0],
+ };
+
+ clear_output_num();
+
+ ctx = test_ctx_create();
+ if (!TEST_CHECK(ctx != NULL)) {
+ TEST_MSG("test_ctx_create failed");
+ exit(EXIT_FAILURE);
+ }
+
+ ret = flb_output_set(ctx->flb, ctx->o_ffd,
+ "match", "*",
+ "syslog_format", "rfc3164",
+ "syslog_message_key", "msg",
+ "syslog_severity_preset", "5",
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ ret = flb_output_set_test(ctx->flb, ctx->o_ffd,
+ "formatter", cb_check_str_list,
+ &expected, NULL);
+ TEST_CHECK(ret == 0);
+
+ /* Start the engine */
+ ret = flb_start(ctx->flb);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data sample */
+ ret = flb_lib_push(ctx->flb, ctx->i_ffd, (char *) buf, size);
+ TEST_CHECK(ret >= 0);
+
+ /* waiting to flush */
+ flb_time_msleep(500);
+
+ num = get_output_num();
+ if (!TEST_CHECK(num > 0)) {
+ TEST_MSG("no outputs");
+ }
+
+ test_ctx_destroy(ctx);
+}
+
+void flb_test_facility_key_rfc5424()
+{
+ struct test_ctx *ctx;
+ int ret;
+ int num;
+
+ char *buf = "[1, {\"msg\":\"hello world\", \"f_key\":\"13\"}]";
+ size_t size = strlen(buf);
+
+ char *expected_strs[] = {"hello world", "1970-01-01T00:00:01.000000Z", "<110>" /* 13(log audit) * 8 + 6(default severity) */,
+ "<110>1 1970-01-01T00:00:01.000000Z - - - - - hello world"};
+ struct str_list expected = {
+ .size = sizeof(expected_strs)/sizeof(char*),
+ .lists = &expected_strs[0],
+ };
+
+ clear_output_num();
+
+ ctx = test_ctx_create();
+ if (!TEST_CHECK(ctx != NULL)) {
+ TEST_MSG("test_ctx_create failed");
+ exit(EXIT_FAILURE);
+ }
+
+ ret = flb_output_set(ctx->flb, ctx->o_ffd,
+ "match", "*",
+ "syslog_format", "rfc5424",
+ "syslog_message_key", "msg",
+ "syslog_facility_key", "f_key",
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ ret = flb_output_set_test(ctx->flb, ctx->o_ffd,
+ "formatter", cb_check_str_list,
+ &expected, NULL);
+ TEST_CHECK(ret == 0);
+
+ /* Start the engine */
+ ret = flb_start(ctx->flb);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data sample */
+ ret = flb_lib_push(ctx->flb, ctx->i_ffd, (char *) buf, size);
+ TEST_CHECK(ret >= 0);
+
+ /* waiting to flush */
+ flb_time_msleep(500);
+
+ num = get_output_num();
+ if (!TEST_CHECK(num > 0)) {
+ TEST_MSG("no outputs");
+ }
+
+ test_ctx_destroy(ctx);
+}
+
+void flb_test_facility_preset_rfc5424()
+{
+ struct test_ctx *ctx;
+ int ret;
+ int num;
+
+ char *buf = "[1, {\"msg\":\"hello world\", \"f_key\":\"13\"}]";
+ size_t size = strlen(buf);
+
+ char *expected_strs[] = {"hello world", "1970-01-01T00:00:01.000000Z", "<110>" /* 13(log audit) * 8 + 6(default severity) */,
+ "<110>1 1970-01-01T00:00:01.000000Z - - - - - hello world"};
+ struct str_list expected = {
+ .size = sizeof(expected_strs)/sizeof(char*),
+ .lists = &expected_strs[0],
+ };
+
+ clear_output_num();
+
+ ctx = test_ctx_create();
+ if (!TEST_CHECK(ctx != NULL)) {
+ TEST_MSG("test_ctx_create failed");
+ exit(EXIT_FAILURE);
+ }
+
+ ret = flb_output_set(ctx->flb, ctx->o_ffd,
+ "match", "*",
+ "syslog_format", "rfc5424",
+ "syslog_message_key", "msg",
+ "syslog_facility_preset", "13",
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ ret = flb_output_set_test(ctx->flb, ctx->o_ffd,
+ "formatter", cb_check_str_list,
+ &expected, NULL);
+ TEST_CHECK(ret == 0);
+
+ /* Start the engine */
+ ret = flb_start(ctx->flb);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data sample */
+ ret = flb_lib_push(ctx->flb, ctx->i_ffd, (char *) buf, size);
+ TEST_CHECK(ret >= 0);
+
+ /* waiting to flush */
+ flb_time_msleep(500);
+
+ num = get_output_num();
+ if (!TEST_CHECK(num > 0)) {
+ TEST_MSG("no outputs");
+ }
+
+ test_ctx_destroy(ctx);
+}
+
+void flb_test_facility_key_rfc3164()
+{
+ struct test_ctx *ctx;
+ int ret;
+ int num;
+
+ char *buf = "[1, {\"msg\":\"hello world\", \"f_key\":\"13\"}]";
+ size_t size = strlen(buf);
+
+ char *expected_strs[] = {"hello world", "Jan 1 00:00:01", "<110>" /* 13(log audit) * 8 + 6(default severity) */,
+ "<110>Jan 1 00:00:01 hello world"};
+ struct str_list expected = {
+ .size = sizeof(expected_strs)/sizeof(char*),
+ .lists = &expected_strs[0],
+ };
+
+ clear_output_num();
+
+ ctx = test_ctx_create();
+ if (!TEST_CHECK(ctx != NULL)) {
+ TEST_MSG("test_ctx_create failed");
+ exit(EXIT_FAILURE);
+ }
+
+ ret = flb_output_set(ctx->flb, ctx->o_ffd,
+ "match", "*",
+ "syslog_format", "rfc3164",
+ "syslog_message_key", "msg",
+ "syslog_facility_key", "f_key",
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ ret = flb_output_set_test(ctx->flb, ctx->o_ffd,
+ "formatter", cb_check_str_list,
+ &expected, NULL);
+ TEST_CHECK(ret == 0);
+
+ /* Start the engine */
+ ret = flb_start(ctx->flb);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data sample */
+ ret = flb_lib_push(ctx->flb, ctx->i_ffd, (char *) buf, size);
+ TEST_CHECK(ret >= 0);
+
+ /* waiting to flush */
+ flb_time_msleep(500);
+
+ num = get_output_num();
+ if (!TEST_CHECK(num > 0)) {
+ TEST_MSG("no outputs");
+ }
+
+ test_ctx_destroy(ctx);
+}
+
+void flb_test_facility_preset_rfc3164()
+{
+ struct test_ctx *ctx;
+ int ret;
+ int num;
+
+ char *buf = "[1, {\"msg\":\"hello world\", \"f_key\":\"13\"}]";
+ size_t size = strlen(buf);
+
+ char *expected_strs[] = {"hello world", "Jan 1 00:00:01", "<110>" /* 13(log audit) * 8 + 6(default severity) */,
+ "<110>Jan 1 00:00:01 hello world"};
+ struct str_list expected = {
+ .size = sizeof(expected_strs)/sizeof(char*),
+ .lists = &expected_strs[0],
+ };
+
+ clear_output_num();
+
+ ctx = test_ctx_create();
+ if (!TEST_CHECK(ctx != NULL)) {
+ TEST_MSG("test_ctx_create failed");
+ exit(EXIT_FAILURE);
+ }
+
+ ret = flb_output_set(ctx->flb, ctx->o_ffd,
+ "match", "*",
+ "syslog_format", "rfc3164",
+ "syslog_message_key", "msg",
+ "syslog_facility_preset", "13",
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ ret = flb_output_set_test(ctx->flb, ctx->o_ffd,
+ "formatter", cb_check_str_list,
+ &expected, NULL);
+ TEST_CHECK(ret == 0);
+
+ /* Start the engine */
+ ret = flb_start(ctx->flb);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data sample */
+ ret = flb_lib_push(ctx->flb, ctx->i_ffd, (char *) buf, size);
+ TEST_CHECK(ret >= 0);
+
+ /* waiting to flush */
+ flb_time_msleep(500);
+
+ num = get_output_num();
+ if (!TEST_CHECK(num > 0)) {
+ TEST_MSG("no outputs");
+ }
+
+ test_ctx_destroy(ctx);
+}
+
+void flb_test_severity_facility_key_rfc5424()
+{
+ struct test_ctx *ctx;
+ int ret;
+ int num;
+
+ char *buf = "[1, {\"msg\":\"hello world\", \"f_key\":\"13\", \"s_key\":\"5\"}]";
+ size_t size = strlen(buf);
+
+ char *expected_strs[] = {"hello world", "1970-01-01T00:00:01.000000Z", "<109>" /* 13(log audit) * 8 + 5(severity) */,
+ "<109>1 1970-01-01T00:00:01.000000Z - - - - - hello world"};
+ struct str_list expected = {
+ .size = sizeof(expected_strs)/sizeof(char*),
+ .lists = &expected_strs[0],
+ };
+
+ clear_output_num();
+
+ ctx = test_ctx_create();
+ if (!TEST_CHECK(ctx != NULL)) {
+ TEST_MSG("test_ctx_create failed");
+ exit(EXIT_FAILURE);
+ }
+
+ ret = flb_output_set(ctx->flb, ctx->o_ffd,
+ "match", "*",
+ "syslog_format", "rfc5424",
+ "syslog_message_key", "msg",
+ "syslog_severity_key", "s_key",
+ "syslog_facility_key", "f_key",
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ ret = flb_output_set_test(ctx->flb, ctx->o_ffd,
+ "formatter", cb_check_str_list,
+ &expected, NULL);
+ TEST_CHECK(ret == 0);
+
+ /* Start the engine */
+ ret = flb_start(ctx->flb);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data sample */
+ ret = flb_lib_push(ctx->flb, ctx->i_ffd, (char *) buf, size);
+ TEST_CHECK(ret >= 0);
+
+ /* waiting to flush */
+ flb_time_msleep(500);
+
+ num = get_output_num();
+ if (!TEST_CHECK(num > 0)) {
+ TEST_MSG("no outputs");
+ }
+
+ test_ctx_destroy(ctx);
+}
+
+void flb_test_severity_facility_key_rfc3164()
+{
+ struct test_ctx *ctx;
+ int ret;
+ int num;
+
+ char *buf = "[1, {\"msg\":\"hello world\", \"f_key\":\"13\", \"s_key\":\"5\"}]";
+ size_t size = strlen(buf);
+
+ char *expected_strs[] = {"hello world", "Jan 1 00:00:01", "<109>" /* 13(log audit) * 8 + 5(severity) */,
+ "<109>Jan 1 00:00:01 hello world"};
+ struct str_list expected = {
+ .size = sizeof(expected_strs)/sizeof(char*),
+ .lists = &expected_strs[0],
+ };
+
+ clear_output_num();
+
+ ctx = test_ctx_create();
+ if (!TEST_CHECK(ctx != NULL)) {
+ TEST_MSG("test_ctx_create failed");
+ exit(EXIT_FAILURE);
+ }
+
+ ret = flb_output_set(ctx->flb, ctx->o_ffd,
+ "match", "*",
+ "syslog_format", "rfc3164",
+ "syslog_message_key", "msg",
+ "syslog_severity_key", "s_key",
+ "syslog_facility_key", "f_key",
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ ret = flb_output_set_test(ctx->flb, ctx->o_ffd,
+ "formatter", cb_check_str_list,
+ &expected, NULL);
+ TEST_CHECK(ret == 0);
+
+ /* Start the engine */
+ ret = flb_start(ctx->flb);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data sample */
+ ret = flb_lib_push(ctx->flb, ctx->i_ffd, (char *) buf, size);
+ TEST_CHECK(ret >= 0);
+
+ /* waiting to flush */
+ flb_time_msleep(500);
+
+ num = get_output_num();
+ if (!TEST_CHECK(num > 0)) {
+ TEST_MSG("no outputs");
+ }
+
+ test_ctx_destroy(ctx);
+}
+
+void flb_test_hostname_key_rfc5424()
+{
+ struct test_ctx *ctx;
+ int ret;
+ int num;
+
+ char *buf = "[1, {\"msg\":\"hello world\", \"h_key\":\"localhost\"}]";
+ size_t size = strlen(buf);
+
+ char *expected_strs[] = {"hello world", "1970-01-01T00:00:01.000000Z", "localhost",
+ "<14>1 1970-01-01T00:00:01.000000Z localhost - - - - hello world"};
+ struct str_list expected = {
+ .size = sizeof(expected_strs)/sizeof(char*),
+ .lists = &expected_strs[0],
+ };
+
+ clear_output_num();
+
+ ctx = test_ctx_create();
+ if (!TEST_CHECK(ctx != NULL)) {
+ TEST_MSG("test_ctx_create failed");
+ exit(EXIT_FAILURE);
+ }
+
+ ret = flb_output_set(ctx->flb, ctx->o_ffd,
+ "match", "*",
+ "syslog_format", "rfc5424",
+ "syslog_message_key", "msg",
+ "syslog_hostname_key", "h_key",
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ ret = flb_output_set_test(ctx->flb, ctx->o_ffd,
+ "formatter", cb_check_str_list,
+ &expected, NULL);
+ TEST_CHECK(ret == 0);
+
+ /* Start the engine */
+ ret = flb_start(ctx->flb);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data sample */
+ ret = flb_lib_push(ctx->flb, ctx->i_ffd, (char *) buf, size);
+ TEST_CHECK(ret >= 0);
+
+ /* waiting to flush */
+ flb_time_msleep(500);
+
+ num = get_output_num();
+ if (!TEST_CHECK(num > 0)) {
+ TEST_MSG("no outputs");
+ }
+
+ test_ctx_destroy(ctx);
+}
+
+void flb_test_hostname_preset_rfc5424()
+{
+ struct test_ctx *ctx;
+ int ret;
+ int num;
+
+ char *buf = "[1, {\"msg\":\"hello world\", \"h_key\":\"localhost\"}]";
+ size_t size = strlen(buf);
+
+ char *expected_strs[] = {"hello world", "1970-01-01T00:00:01.000000Z", "localhost",
+ "<14>1 1970-01-01T00:00:01.000000Z localhost - - - - hello world"};
+ struct str_list expected = {
+ .size = sizeof(expected_strs)/sizeof(char*),
+ .lists = &expected_strs[0],
+ };
+
+ clear_output_num();
+
+ ctx = test_ctx_create();
+ if (!TEST_CHECK(ctx != NULL)) {
+ TEST_MSG("test_ctx_create failed");
+ exit(EXIT_FAILURE);
+ }
+
+ ret = flb_output_set(ctx->flb, ctx->o_ffd,
+ "match", "*",
+ "syslog_format", "rfc5424",
+ "syslog_message_key", "msg",
+ "syslog_hostname_preset", "localhost",
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ ret = flb_output_set_test(ctx->flb, ctx->o_ffd,
+ "formatter", cb_check_str_list,
+ &expected, NULL);
+ TEST_CHECK(ret == 0);
+
+ /* Start the engine */
+ ret = flb_start(ctx->flb);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data sample */
+ ret = flb_lib_push(ctx->flb, ctx->i_ffd, (char *) buf, size);
+ TEST_CHECK(ret >= 0);
+
+ /* waiting to flush */
+ flb_time_msleep(500);
+
+ num = get_output_num();
+ if (!TEST_CHECK(num > 0)) {
+ TEST_MSG("no outputs");
+ }
+
+ test_ctx_destroy(ctx);
+}
+
+void flb_test_hostname_key_rfc3164()
+{
+ struct test_ctx *ctx;
+ int ret;
+ int num;
+
+ char *buf = "[1, {\"msg\":\"hello world\", \"h_key\":\"localhost\"}]";
+ size_t size = strlen(buf);
+
+ char *expected_strs[] = {"hello world", "Jan 1 00:00:01", "localhost",
+ "<14>Jan 1 00:00:01 localhost hello world"};
+ struct str_list expected = {
+ .size = sizeof(expected_strs)/sizeof(char*),
+ .lists = &expected_strs[0],
+ };
+
+ clear_output_num();
+
+ ctx = test_ctx_create();
+ if (!TEST_CHECK(ctx != NULL)) {
+ TEST_MSG("test_ctx_create failed");
+ exit(EXIT_FAILURE);
+ }
+
+ ret = flb_output_set(ctx->flb, ctx->o_ffd,
+ "match", "*",
+ "syslog_format", "rfc3164",
+ "syslog_message_key", "msg",
+ "syslog_hostname_key", "h_key",
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ ret = flb_output_set_test(ctx->flb, ctx->o_ffd,
+ "formatter", cb_check_str_list,
+ &expected, NULL);
+ TEST_CHECK(ret == 0);
+
+ /* Start the engine */
+ ret = flb_start(ctx->flb);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data sample */
+ ret = flb_lib_push(ctx->flb, ctx->i_ffd, (char *) buf, size);
+ TEST_CHECK(ret >= 0);
+
+ /* waiting to flush */
+ flb_time_msleep(500);
+
+ num = get_output_num();
+ if (!TEST_CHECK(num > 0)) {
+ TEST_MSG("no outputs");
+ }
+
+ test_ctx_destroy(ctx);
+}
+
+void flb_test_hostname_preset_rfc3164()
+{
+ struct test_ctx *ctx;
+ int ret;
+ int num;
+
+ char *buf = "[1, {\"msg\":\"hello world\", \"h_key\":\"localhost\"}]";
+ size_t size = strlen(buf);
+
+ char *expected_strs[] = {"hello world", "Jan 1 00:00:01", "localhost",
+ "<14>Jan 1 00:00:01 localhost hello world"};
+ struct str_list expected = {
+ .size = sizeof(expected_strs)/sizeof(char*),
+ .lists = &expected_strs[0],
+ };
+
+ clear_output_num();
+
+ ctx = test_ctx_create();
+ if (!TEST_CHECK(ctx != NULL)) {
+ TEST_MSG("test_ctx_create failed");
+ exit(EXIT_FAILURE);
+ }
+
+ ret = flb_output_set(ctx->flb, ctx->o_ffd,
+ "match", "*",
+ "syslog_format", "rfc3164",
+ "syslog_message_key", "msg",
+ "syslog_hostname_preset", "localhost",
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ ret = flb_output_set_test(ctx->flb, ctx->o_ffd,
+ "formatter", cb_check_str_list,
+ &expected, NULL);
+ TEST_CHECK(ret == 0);
+
+ /* Start the engine */
+ ret = flb_start(ctx->flb);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data sample */
+ ret = flb_lib_push(ctx->flb, ctx->i_ffd, (char *) buf, size);
+ TEST_CHECK(ret >= 0);
+
+ /* waiting to flush */
+ flb_time_msleep(500);
+
+ num = get_output_num();
+ if (!TEST_CHECK(num > 0)) {
+ TEST_MSG("no outputs");
+ }
+
+ test_ctx_destroy(ctx);
+}
+
+void flb_test_appname_key_rfc5424()
+{
+ struct test_ctx *ctx;
+ int ret;
+ int num;
+
+ char *buf = "[1, {\"msg\":\"hello world\", \"a_key\":\"fluent-bit\"}]";
+ size_t size = strlen(buf);
+
+ char *expected_strs[] = {"hello world", "1970-01-01T00:00:01.000000Z", "fluent-bit",
+ "<14>1 1970-01-01T00:00:01.000000Z - fluent-bit - - - hello world"};
+ struct str_list expected = {
+ .size = sizeof(expected_strs)/sizeof(char*),
+ .lists = &expected_strs[0],
+ };
+
+ clear_output_num();
+
+ ctx = test_ctx_create();
+ if (!TEST_CHECK(ctx != NULL)) {
+ TEST_MSG("test_ctx_create failed");
+ exit(EXIT_FAILURE);
+ }
+
+ ret = flb_output_set(ctx->flb, ctx->o_ffd,
+ "match", "*",
+ "syslog_format", "rfc5424",
+ "syslog_message_key", "msg",
+ "syslog_appname_key", "a_key",
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ ret = flb_output_set_test(ctx->flb, ctx->o_ffd,
+ "formatter", cb_check_str_list,
+ &expected, NULL);
+ TEST_CHECK(ret == 0);
+
+ /* Start the engine */
+ ret = flb_start(ctx->flb);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data sample */
+ ret = flb_lib_push(ctx->flb, ctx->i_ffd, (char *) buf, size);
+ TEST_CHECK(ret >= 0);
+
+ /* waiting to flush */
+ flb_time_msleep(500);
+
+ num = get_output_num();
+ if (!TEST_CHECK(num > 0)) {
+ TEST_MSG("no outputs");
+ }
+
+ test_ctx_destroy(ctx);
+}
+
+void flb_test_appname_preset_rfc5424()
+{
+ struct test_ctx *ctx;
+ int ret;
+ int num;
+
+ char *buf = "[1, {\"msg\":\"hello world\", \"a_key\":\"fluent-bit\"}]";
+ size_t size = strlen(buf);
+
+ char *expected_strs[] = {"hello world", "1970-01-01T00:00:01.000000Z", "fluent-bit",
+ "<14>1 1970-01-01T00:00:01.000000Z - fluent-bit - - - hello world"};
+ struct str_list expected = {
+ .size = sizeof(expected_strs)/sizeof(char*),
+ .lists = &expected_strs[0],
+ };
+
+ clear_output_num();
+
+ ctx = test_ctx_create();
+ if (!TEST_CHECK(ctx != NULL)) {
+ TEST_MSG("test_ctx_create failed");
+ exit(EXIT_FAILURE);
+ }
+
+ ret = flb_output_set(ctx->flb, ctx->o_ffd,
+ "match", "*",
+ "syslog_format", "rfc5424",
+ "syslog_message_key", "msg",
+ "syslog_appname_preset", "fluent-bit",
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ ret = flb_output_set_test(ctx->flb, ctx->o_ffd,
+ "formatter", cb_check_str_list,
+ &expected, NULL);
+ TEST_CHECK(ret == 0);
+
+ /* Start the engine */
+ ret = flb_start(ctx->flb);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data sample */
+ ret = flb_lib_push(ctx->flb, ctx->i_ffd, (char *) buf, size);
+ TEST_CHECK(ret >= 0);
+
+ /* waiting to flush */
+ flb_time_msleep(500);
+
+ num = get_output_num();
+ if (!TEST_CHECK(num > 0)) {
+ TEST_MSG("no outputs");
+ }
+
+ test_ctx_destroy(ctx);
+}
+
+void flb_test_appname_key_rfc3164()
+{
+ struct test_ctx *ctx;
+ int ret;
+ int num;
+
+ char *buf = "[1, {\"msg\":\"hello world\", \"a_key\":\"fluent-bit\"}]";
+ size_t size = strlen(buf);
+
+ char *expected_strs[] = {"hello world", "Jan 1 00:00:01", "fluent-bit",
+ "<14>Jan 1 00:00:01 fluent-bit: hello world"};
+ struct str_list expected = {
+ .size = sizeof(expected_strs)/sizeof(char*),
+ .lists = &expected_strs[0],
+ };
+
+ clear_output_num();
+
+ ctx = test_ctx_create();
+ if (!TEST_CHECK(ctx != NULL)) {
+ TEST_MSG("test_ctx_create failed");
+ exit(EXIT_FAILURE);
+ }
+
+ ret = flb_output_set(ctx->flb, ctx->o_ffd,
+ "match", "*",
+ "syslog_format", "rfc3164",
+ "syslog_message_key", "msg",
+ "syslog_appname_key", "a_key",
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ ret = flb_output_set_test(ctx->flb, ctx->o_ffd,
+ "formatter", cb_check_str_list,
+ &expected, NULL);
+ TEST_CHECK(ret == 0);
+
+ /* Start the engine */
+ ret = flb_start(ctx->flb);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data sample */
+ ret = flb_lib_push(ctx->flb, ctx->i_ffd, (char *) buf, size);
+ TEST_CHECK(ret >= 0);
+
+ /* waiting to flush */
+ flb_time_msleep(500);
+
+ num = get_output_num();
+ if (!TEST_CHECK(num > 0)) {
+ TEST_MSG("no outputs");
+ }
+
+ test_ctx_destroy(ctx);
+}
+
+void flb_test_appname_preset_rfc3164()
+{
+ struct test_ctx *ctx;
+ int ret;
+ int num;
+
+ char *buf = "[1, {\"msg\":\"hello world\", \"a_key\":\"fluent-bit\"}]";
+ size_t size = strlen(buf);
+
+ char *expected_strs[] = {"hello world", "Jan 1 00:00:01", "fluent-bit",
+ "<14>Jan 1 00:00:01 fluent-bit: hello world"};
+ struct str_list expected = {
+ .size = sizeof(expected_strs)/sizeof(char*),
+ .lists = &expected_strs[0],
+ };
+
+ clear_output_num();
+
+ ctx = test_ctx_create();
+ if (!TEST_CHECK(ctx != NULL)) {
+ TEST_MSG("test_ctx_create failed");
+ exit(EXIT_FAILURE);
+ }
+
+ ret = flb_output_set(ctx->flb, ctx->o_ffd,
+ "match", "*",
+ "syslog_format", "rfc3164",
+ "syslog_message_key", "msg",
+ "syslog_appname_preset", "fluent-bit",
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ ret = flb_output_set_test(ctx->flb, ctx->o_ffd,
+ "formatter", cb_check_str_list,
+ &expected, NULL);
+ TEST_CHECK(ret == 0);
+
+ /* Start the engine */
+ ret = flb_start(ctx->flb);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data sample */
+ ret = flb_lib_push(ctx->flb, ctx->i_ffd, (char *) buf, size);
+ TEST_CHECK(ret >= 0);
+
+ /* waiting to flush */
+ flb_time_msleep(500);
+
+ num = get_output_num();
+ if (!TEST_CHECK(num > 0)) {
+ TEST_MSG("no outputs");
+ }
+
+ test_ctx_destroy(ctx);
+}
+
+void flb_test_procid_key_rfc5424()
+{
+ struct test_ctx *ctx;
+ int ret;
+ int num;
+
+ char *buf = "[1, {\"msg\":\"hello world\", \"p_key\":\"1234\"}]";
+ size_t size = strlen(buf);
+
+ char *expected_strs[] = {"hello world", "1970-01-01T00:00:01.000000Z", "1234",
+ "<14>1 1970-01-01T00:00:01.000000Z - - 1234 - - hello world"};
+ struct str_list expected = {
+ .size = sizeof(expected_strs)/sizeof(char*),
+ .lists = &expected_strs[0],
+ };
+
+ clear_output_num();
+
+ ctx = test_ctx_create();
+ if (!TEST_CHECK(ctx != NULL)) {
+ TEST_MSG("test_ctx_create failed");
+ exit(EXIT_FAILURE);
+ }
+
+ ret = flb_output_set(ctx->flb, ctx->o_ffd,
+ "match", "*",
+ "syslog_format", "rfc5424",
+ "syslog_message_key", "msg",
+ "syslog_procid_key", "p_key",
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ ret = flb_output_set_test(ctx->flb, ctx->o_ffd,
+ "formatter", cb_check_str_list,
+ &expected, NULL);
+ TEST_CHECK(ret == 0);
+
+ /* Start the engine */
+ ret = flb_start(ctx->flb);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data sample */
+ ret = flb_lib_push(ctx->flb, ctx->i_ffd, (char *) buf, size);
+ TEST_CHECK(ret >= 0);
+
+ /* waiting to flush */
+ flb_time_msleep(500);
+
+ num = get_output_num();
+ if (!TEST_CHECK(num > 0)) {
+ TEST_MSG("no outputs");
+ }
+
+ test_ctx_destroy(ctx);
+}
+
+void flb_test_procid_preset_rfc5424()
+{
+ struct test_ctx *ctx;
+ int ret;
+ int num;
+
+ char *buf = "[1, {\"msg\":\"hello world\", \"p_key\":\"1234\"}]";
+ size_t size = strlen(buf);
+
+ char *expected_strs[] = {"hello world", "1970-01-01T00:00:01.000000Z", "1234",
+ "<14>1 1970-01-01T00:00:01.000000Z - - 1234 - - hello world"};
+ struct str_list expected = {
+ .size = sizeof(expected_strs)/sizeof(char*),
+ .lists = &expected_strs[0],
+ };
+
+ clear_output_num();
+
+ ctx = test_ctx_create();
+ if (!TEST_CHECK(ctx != NULL)) {
+ TEST_MSG("test_ctx_create failed");
+ exit(EXIT_FAILURE);
+ }
+
+ ret = flb_output_set(ctx->flb, ctx->o_ffd,
+ "match", "*",
+ "syslog_format", "rfc5424",
+ "syslog_message_key", "msg",
+ "syslog_procid_preset", "1234",
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ ret = flb_output_set_test(ctx->flb, ctx->o_ffd,
+ "formatter", cb_check_str_list,
+ &expected, NULL);
+ TEST_CHECK(ret == 0);
+
+ /* Start the engine */
+ ret = flb_start(ctx->flb);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data sample */
+ ret = flb_lib_push(ctx->flb, ctx->i_ffd, (char *) buf, size);
+ TEST_CHECK(ret >= 0);
+
+ /* waiting to flush */
+ flb_time_msleep(500);
+
+ num = get_output_num();
+ if (!TEST_CHECK(num > 0)) {
+ TEST_MSG("no outputs");
+ }
+
+ test_ctx_destroy(ctx);
+}
+
+void flb_test_msgid_key_rfc5424()
+{
+ struct test_ctx *ctx;
+ int ret;
+ int num;
+
+ char *buf = "[1, {\"msg\":\"hello world\", \"m_key\":\"TCPIN\"}]";
+ size_t size = strlen(buf);
+
+ char *expected_strs[] = {"hello world", "1970-01-01T00:00:01.000000Z", "TCPIN",
+ "<14>1 1970-01-01T00:00:01.000000Z - - - TCPIN - hello world"};
+ struct str_list expected = {
+ .size = sizeof(expected_strs)/sizeof(char*),
+ .lists = &expected_strs[0],
+ };
+
+ clear_output_num();
+
+ ctx = test_ctx_create();
+ if (!TEST_CHECK(ctx != NULL)) {
+ TEST_MSG("test_ctx_create failed");
+ exit(EXIT_FAILURE);
+ }
+
+ ret = flb_output_set(ctx->flb, ctx->o_ffd,
+ "match", "*",
+ "syslog_format", "rfc5424",
+ "syslog_message_key", "msg",
+ "syslog_msgid_key", "m_key",
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ ret = flb_output_set_test(ctx->flb, ctx->o_ffd,
+ "formatter", cb_check_str_list,
+ &expected, NULL);
+ TEST_CHECK(ret == 0);
+
+ /* Start the engine */
+ ret = flb_start(ctx->flb);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data sample */
+ ret = flb_lib_push(ctx->flb, ctx->i_ffd, (char *) buf, size);
+ TEST_CHECK(ret >= 0);
+
+ /* waiting to flush */
+ flb_time_msleep(500);
+
+ num = get_output_num();
+ if (!TEST_CHECK(num > 0)) {
+ TEST_MSG("no outputs");
+ }
+
+ test_ctx_destroy(ctx);
+}
+
+void flb_test_msgid_preset_rfc5424()
+{
+ struct test_ctx *ctx;
+ int ret;
+ int num;
+
+ char *buf = "[1, {\"msg\":\"hello world\", \"m_key\":\"TCPIN\"}]";
+ size_t size = strlen(buf);
+
+ char *expected_strs[] = {"hello world", "1970-01-01T00:00:01.000000Z", "TCPIN",
+ "<14>1 1970-01-01T00:00:01.000000Z - - - TCPIN - hello world"};
+ struct str_list expected = {
+ .size = sizeof(expected_strs)/sizeof(char*),
+ .lists = &expected_strs[0],
+ };
+
+ clear_output_num();
+
+ ctx = test_ctx_create();
+ if (!TEST_CHECK(ctx != NULL)) {
+ TEST_MSG("test_ctx_create failed");
+ exit(EXIT_FAILURE);
+ }
+
+ ret = flb_output_set(ctx->flb, ctx->o_ffd,
+ "match", "*",
+ "syslog_format", "rfc5424",
+ "syslog_message_key", "msg",
+ "syslog_msgid_preset", "TCPIN",
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ ret = flb_output_set_test(ctx->flb, ctx->o_ffd,
+ "formatter", cb_check_str_list,
+ &expected, NULL);
+ TEST_CHECK(ret == 0);
+
+ /* Start the engine */
+ ret = flb_start(ctx->flb);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data sample */
+ ret = flb_lib_push(ctx->flb, ctx->i_ffd, (char *) buf, size);
+ TEST_CHECK(ret >= 0);
+
+ /* waiting to flush */
+ flb_time_msleep(500);
+
+ num = get_output_num();
+ if (!TEST_CHECK(num > 0)) {
+ TEST_MSG("no outputs");
+ }
+
+ test_ctx_destroy(ctx);
+}
+
+void flb_test_sd_key_rfc5424()
+{
+ struct test_ctx *ctx;
+ int ret;
+ int num;
+
+ char *buf = "[1, {\"msg\":\"hello world\", \"sd_key\": {\"logtype\": \"access\",\"clustername\": \"mycluster\",\"namespace\": \"mynamespace\"}}]";
+ size_t size = strlen(buf);
+
+ char *expected_strs[] = {"hello world", "1970-01-01T00:00:01.000000Z",
+ "<14>1 1970-01-01T00:00:01.000000Z - - - - [sd_key logtype=\"access\" clustername=\"mycluster\" namespace=\"mynamespace\"] hello world"};
+ struct str_list expected = {
+ .size = sizeof(expected_strs)/sizeof(char*),
+ .lists = &expected_strs[0],
+ };
+
+ clear_output_num();
+
+ ctx = test_ctx_create();
+ if (!TEST_CHECK(ctx != NULL)) {
+ TEST_MSG("test_ctx_create failed");
+ exit(EXIT_FAILURE);
+ }
+
+ ret = flb_output_set(ctx->flb, ctx->o_ffd,
+ "match", "*",
+ "syslog_format", "rfc5424",
+ "syslog_message_key", "msg",
+ "syslog_sd_key", "sd_key",
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ ret = flb_output_set_test(ctx->flb, ctx->o_ffd,
+ "formatter", cb_check_str_list,
+ &expected, NULL);
+ TEST_CHECK(ret == 0);
+
+ /* Start the engine */
+ ret = flb_start(ctx->flb);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data sample */
+ ret = flb_lib_push(ctx->flb, ctx->i_ffd, (char *) buf, size);
+ TEST_CHECK(ret >= 0);
+
+ /* waiting to flush */
+ flb_time_msleep(500);
+
+ num = get_output_num();
+ if (!TEST_CHECK(num > 0)) {
+ TEST_MSG("no outputs");
+ }
+
+ test_ctx_destroy(ctx);
+}
+
+void flb_test_allow_longer_sd_id_rfc5424()
+{
+ struct test_ctx *ctx;
+ int ret;
+ int num;
+
+ char *buf = "[1, {\"msg\":\"hello world\", \"sd_key_that_is_longer_than_32_characters\": {\"logtype_that_is_longer_than_32_characters\": \"access\",\"clustername\": \"mycluster\",\"namespace\": \"mynamespace\"}}]";
+ size_t size = strlen(buf);
+
+ char *expected_strs[] = {"hello world", "1970-01-01T00:00:01.000000Z",
+ "<14>1 1970-01-01T00:00:01.000000Z - - - - [sd_key_that_is_longer_than_32_characters logtype_that_is_longer_than_32_characters=\"access\" clustername=\"mycluster\" namespace=\"mynamespace\"] hello world"};
+ struct str_list expected = {
+ .size = sizeof(expected_strs)/sizeof(char*),
+ .lists = &expected_strs[0],
+ };
+
+ clear_output_num();
+
+ ctx = test_ctx_create();
+ if (!TEST_CHECK(ctx != NULL)) {
+ TEST_MSG("test_ctx_create failed");
+ exit(EXIT_FAILURE);
+ }
+
+ ret = flb_output_set(ctx->flb, ctx->o_ffd,
+ "match", "*",
+ "syslog_format", "rfc5424",
+ "syslog_message_key", "msg",
+ "syslog_sd_key", "sd_key_that_is_longer_than_32_characters",
+ "allow_longer_sd_id", "true",
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ ret = flb_output_set_test(ctx->flb, ctx->o_ffd,
+ "formatter", cb_check_str_list,
+ &expected, NULL);
+ TEST_CHECK(ret == 0);
+
+ /* Start the engine */
+ ret = flb_start(ctx->flb);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data sample */
+ ret = flb_lib_push(ctx->flb, ctx->i_ffd, (char *) buf, size);
+ TEST_CHECK(ret >= 0);
+
+ /* waiting to flush */
+ flb_time_msleep(500);
+
+ num = get_output_num();
+ if (!TEST_CHECK(num > 0)) {
+ TEST_MSG("no outputs");
+ }
+
+ test_ctx_destroy(ctx);
+}
+
+void flb_test_malformed_longer_sd_id_rfc5424()
+{
+ struct test_ctx *ctx;
+ int ret;
+ int num;
+
+ char *buf = "[1, {\"msg\":\"hello world\", \"sd_key_that_is_longer_than_32_characters\": {\"logtype_that_is_longer_than_32_characters\": \"access\",\"clustername\": \"mycluster\",\"namespace\": \"mynamespace\"}}]";
+ size_t size = strlen(buf);
+
+ char *expected_strs[] = {"hello world", "1970-01-01T00:00:01.000000Z",
+ "<14>1 1970-01-01T00:00:01.000000Z - - - - [sd_key_that_is_longer_than_32_ch logtype_that_is_longer_than_32_c=\"access\" clustername=\"mycluster\" namespace=\"mynamespace\"] hello world"};
+ struct str_list expected = {
+ .size = sizeof(expected_strs)/sizeof(char*),
+ .lists = &expected_strs[0],
+ };
+
+ clear_output_num();
+
+ ctx = test_ctx_create();
+ if (!TEST_CHECK(ctx != NULL)) {
+ TEST_MSG("test_ctx_create failed");
+ exit(EXIT_FAILURE);
+ }
+
+ ret = flb_output_set(ctx->flb, ctx->o_ffd,
+ "match", "*",
+ "syslog_format", "rfc5424",
+ "syslog_message_key", "msg",
+ "syslog_sd_key", "sd_key_that_is_longer_than_32_characters",
+ "allow_longer_sd_id", "false",
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ ret = flb_output_set_test(ctx->flb, ctx->o_ffd,
+ "formatter", cb_check_str_list,
+ &expected, NULL);
+ TEST_CHECK(ret == 0);
+
+ /* Start the engine */
+ ret = flb_start(ctx->flb);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data sample */
+ ret = flb_lib_push(ctx->flb, ctx->i_ffd, (char *) buf, size);
+ TEST_CHECK(ret >= 0);
+
+ /* waiting to flush */
+ flb_time_msleep(500);
+
+ num = get_output_num();
+ if (!TEST_CHECK(num > 0)) {
+ TEST_MSG("no outputs");
+ }
+
+ test_ctx_destroy(ctx);
+}
+
+TEST_LIST = {
+ /* rfc3164 */
+ /* procid_key, msgid_key, sd_key are not supported */
+ {"format_severity_key_rfc3164", flb_test_severity_key_rfc3164},
+ {"format_facility_key_rfc3164", flb_test_facility_key_rfc3164},
+ {"format_severity_facility_key_rfc3164", flb_test_severity_facility_key_rfc3164},
+ {"format_hostname_key_rfc3164", flb_test_hostname_key_rfc3164},
+ {"format_appname_key_rfc3164", flb_test_appname_key_rfc3164},
+ {"format_severity_preset_rfc3164", flb_test_severity_preset_rfc3164},
+ {"format_facility_preset_rfc3164", flb_test_facility_preset_rfc5424},
+ {"format_hostname_preset_rfc3164", flb_test_hostname_preset_rfc5424},
+ {"format_appname_preset_rfc3164", flb_test_appname_preset_rfc3164},
+
+ /* rfc5424 (Default) */
+ {"format_syslog_rfc5424", flb_test_syslog_rfc5424},
+ {"format_severity_key_rfc5424", flb_test_severity_key_rfc5424},
+ {"format_facility_key_rfc5424", flb_test_facility_key_rfc5424},
+ {"format_severity_facility_key_rfc5424", flb_test_severity_facility_key_rfc5424},
+ {"format_hostname_key_rfc5424", flb_test_hostname_key_rfc5424},
+ {"format_appname_key_rfc5424", flb_test_appname_key_rfc5424},
+ {"format_procid_key_rfc5424", flb_test_procid_key_rfc5424},
+ {"format_msgid_key_rfc5424", flb_test_msgid_key_rfc5424},
+ {"format_sd_key_rfc5424", flb_test_sd_key_rfc5424},
+ {"format_severity_preset_rfc5424", flb_test_severity_preset_rfc5424},
+ {"format_facility_preset_rfc5424", flb_test_facility_preset_rfc5424},
+ {"format_hostname_preset_rfc5424", flb_test_hostname_preset_rfc5424},
+ {"format_appname_preset_rfc5424", flb_test_appname_preset_rfc5424},
+ {"format_procid_preset_rfc5424", flb_test_procid_preset_rfc5424},
+ {"format_msgid_preset_rfc5424", flb_test_msgid_preset_rfc5424},
+ {"allow_longer_sd_id_rfc5424", flb_test_allow_longer_sd_id_rfc5424},
+ {"malformed_longer_sd_id_rfc5424", flb_test_malformed_longer_sd_id_rfc5424},
+ {NULL, NULL}
+};
+
diff --git a/fluent-bit/tests/runtime/out_tcp.c b/fluent-bit/tests/runtime/out_tcp.c
new file mode 100644
index 00000000..98f323ba
--- /dev/null
+++ b/fluent-bit/tests/runtime/out_tcp.c
@@ -0,0 +1,1004 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/* Fluent Bit
+ * ==========
+ * Copyright (C) 2019-2022 The Fluent Bit Authors
+ * Copyright (C) 2015-2018 Treasure Data Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <fluent-bit.h>
+#include <fluent-bit/flb_sds.h>
+#include <fluent-bit/flb_time.h>
+#include <fluent-bit/flb_stream.h>
+#include <fluent-bit/flb_upstream.h>
+#include <fluent-bit/flb_downstream.h>
+#include <string.h>
+#include <float.h>
+#include <math.h>
+#include <msgpack.h>
+#include "flb_tests_runtime.h"
+
+#define DEFAULT_IO_TIMEOUT 10
+#define DEFAULT_HOST "127.0.0.1"
+#define DEFAULT_PORT "5170"
+
+#define TLS_CERTIFICATE_HOSTNAME "leo.vcap.me"
+#define TLS_CERTIFICATE_FILENAME FLB_TESTS_DATA_PATH "/data/tls/certificate.pem"
+#define TLS_PRIVATE_KEY_FILENAME FLB_TESTS_DATA_PATH "/data/tls/private_key.pem"
+
+struct test_ctx {
+ flb_ctx_t *flb; /* Fluent Bit library context */
+ int i_ffd; /* Input fd */
+ int f_ffd; /* Filter fd (unused) */
+ int o_ffd; /* Output fd */
+};
+
+
+pthread_mutex_t result_mutex = PTHREAD_MUTEX_INITIALIZER;
+int num_output = 0;
+static int get_output_num()
+{
+ int ret;
+ pthread_mutex_lock(&result_mutex);
+ ret = num_output;
+ pthread_mutex_unlock(&result_mutex);
+
+ return ret;
+}
+
+static void set_output_num(int num)
+{
+ pthread_mutex_lock(&result_mutex);
+ num_output = num;
+ pthread_mutex_unlock(&result_mutex);
+}
+
+static void clear_output_num()
+{
+ set_output_num(0);
+}
+
+struct str_list {
+ size_t size;
+ char **lists;
+};
+
+/* Callback to check expected results */
+static void cb_check_str_list(void *ctx, int ffd, int res_ret,
+ void *res_data, size_t res_size, void *data)
+{
+ char *p;
+ flb_sds_t out_line = res_data;
+ int num = get_output_num();
+ size_t i;
+ struct str_list *l = (struct str_list *)data;
+
+ if (!TEST_CHECK(l != NULL)) {
+ TEST_MSG("l is NULL");
+ flb_sds_destroy(out_line);
+ return;
+ }
+
+ if(!TEST_CHECK(res_ret == 0)) {
+ TEST_MSG("callback ret=%d", res_ret);
+ }
+ if (!TEST_CHECK(res_data != NULL)) {
+ TEST_MSG("res_data is NULL");
+ flb_sds_destroy(out_line);
+ return;
+ }
+
+ for (i=0; i<l->size; i++) {
+ p = strstr(out_line, l->lists[i]);
+ if (!TEST_CHECK(p != NULL)) {
+ TEST_MSG(" Got :%s\n expect:%s", out_line, l->lists[i]);
+ }
+ }
+ set_output_num(num+1);
+
+ flb_sds_destroy(out_line);
+}
+
+static int msgpack_strncmp(char* str, size_t str_len, msgpack_object obj)
+{
+ int ret = -1;
+
+ if (str == NULL) {
+ flb_error("str is NULL");
+ return -1;
+ }
+
+ switch (obj.type) {
+ case MSGPACK_OBJECT_STR:
+ if (obj.via.str.size != str_len) {
+ return -1;
+ }
+ ret = strncmp(str, obj.via.str.ptr, str_len);
+ break;
+ case MSGPACK_OBJECT_POSITIVE_INTEGER:
+ {
+ unsigned long val = strtoul(str, NULL, 10);
+ if (val == (unsigned long)obj.via.u64) {
+ ret = 0;
+ }
+ }
+ break;
+ case MSGPACK_OBJECT_NEGATIVE_INTEGER:
+ {
+ long long val = strtoll(str, NULL, 10);
+ if (val == (unsigned long)obj.via.i64) {
+ ret = 0;
+ }
+ }
+ break;
+ case MSGPACK_OBJECT_FLOAT32:
+ case MSGPACK_OBJECT_FLOAT64:
+ {
+ double val = strtod(str, NULL);
+ if (fabs(val - obj.via.f64) < DBL_EPSILON) {
+ ret = 0;
+ }
+ }
+ break;
+ case MSGPACK_OBJECT_BOOLEAN:
+ if (obj.via.boolean) {
+ if (str_len != 4 /*true*/) {
+ return -1;
+ }
+ ret = strncasecmp(str, "true", 4);
+ }
+ else {
+ if (str_len != 5 /*false*/) {
+ return -1;
+ }
+ ret = strncasecmp(str, "false", 5);
+ }
+ break;
+ default:
+ flb_error("not supported");
+ }
+
+ return ret;
+}
+
+/* Callback to check expected results */
+static void cb_check_msgpack_kv(void *ctx, int ffd, int res_ret,
+ void *res_data, size_t res_size, void *data)
+{
+ msgpack_unpacked result;
+ msgpack_object obj;
+ size_t off = 0;
+ struct str_list *l = (struct str_list *)data;
+ int i_map;
+ int map_size;
+ int i_list;
+ int num = get_output_num();
+
+ if (!TEST_CHECK(res_data != NULL)) {
+ TEST_MSG("res_data is NULL");
+ return;
+ }
+
+ if (!TEST_CHECK(data != NULL)) {
+ flb_error("data is NULL");
+ return;
+ }
+
+ /* Iterate each item array and apply rules */
+ msgpack_unpacked_init(&result);
+ while (msgpack_unpack_next(&result, res_data, res_size, &off) == MSGPACK_UNPACK_SUCCESS) {
+ obj = result.data;
+ /*
+ msgpack_object_print(stdout, obj);
+ */
+ if (obj.type != MSGPACK_OBJECT_ARRAY || obj.via.array.size != 2) {
+ flb_error("array error. type = %d", obj.type);
+ continue;
+ }
+ obj = obj.via.array.ptr[1];
+ if (obj.type != MSGPACK_OBJECT_MAP) {
+ flb_error("map error. type = %d", obj.type);
+ continue;
+ }
+ map_size = obj.via.map.size;
+ for (i_map=0; i_map<map_size; i_map++) {
+ if (obj.via.map.ptr[i_map].key.type != MSGPACK_OBJECT_STR) {
+ flb_error("key is not string. type =%d", obj.via.map.ptr[i_map].key.type);
+ continue;
+ }
+ for (i_list=0; i_list< l->size/2; i_list++) {
+ if (msgpack_strncmp(l->lists[i_list*2], strlen(l->lists[i_list*2]),
+ obj.via.map.ptr[i_map].key) == 0 &&
+ msgpack_strncmp(l->lists[i_list*2+1], strlen(l->lists[i_list*2+1]),
+ obj.via.map.ptr[i_map].val) == 0) {
+ num++;
+ }
+ }
+ }
+ }
+ set_output_num(num);
+
+ msgpack_unpacked_destroy(&result);
+
+ return ;
+}
+
+static struct test_ctx *test_ctx_create()
+{
+ int i_ffd;
+ int o_ffd;
+ struct test_ctx *ctx = NULL;
+
+ ctx = flb_malloc(sizeof(struct test_ctx));
+ if (!TEST_CHECK(ctx != NULL)) {
+ TEST_MSG("malloc failed");
+ flb_errno();
+ return NULL;
+ }
+
+ /* Service config */
+ ctx->flb = flb_create();
+ flb_service_set(ctx->flb,
+ "Flush", "0.200000000",
+ "Grace", "1",
+ "Log_Level", "error",
+ NULL);
+
+ /* Input */
+ i_ffd = flb_input(ctx->flb, (char *) "lib", NULL);
+ TEST_CHECK(i_ffd >= 0);
+ ctx->i_ffd = i_ffd;
+
+ /* Output */
+ o_ffd = flb_output(ctx->flb, (char *) "tcp", NULL);
+ ctx->o_ffd = o_ffd;
+
+ return ctx;
+}
+
+static void test_ctx_destroy(struct test_ctx *ctx)
+{
+ TEST_CHECK(ctx != NULL);
+
+ sleep(1);
+ flb_stop(ctx->flb);
+ flb_destroy(ctx->flb);
+ flb_free(ctx);
+}
+
+void flb_test_tcp_with_tls()
+{
+ struct flb_net_setup downstream_net_setup;
+ struct flb_connection *client_connection;
+ size_t out_buf_size;
+ char in_buf[256];
+ struct flb_downstream *downstream;
+ const char *out_buf;
+ unsigned short int port;
+ struct test_ctx *ctx;
+ int ret;
+ struct flb_tls *tls;
+
+ port = strtoul(DEFAULT_PORT, NULL, 10);
+
+ out_buf = "[1, {\"msg\":\"hello world\"}]";
+ out_buf_size = strlen(out_buf);
+
+ memset(in_buf, 0, sizeof(in_buf));
+
+ ctx = test_ctx_create();
+ if (!TEST_CHECK(ctx != NULL)) {
+ TEST_MSG("test_ctx_create failed");
+ exit(EXIT_FAILURE);
+ }
+
+ ret = flb_output_set(ctx->flb, ctx->o_ffd,
+ "match", "*",
+ "format", "json",
+ "host", DEFAULT_HOST,
+ "port", DEFAULT_PORT,
+ "tls", "on",
+ "tls.verify", "no",
+ "tls.vhost", TLS_CERTIFICATE_HOSTNAME,
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ /* Start the engine */
+ ret = flb_start(ctx->flb);
+ TEST_CHECK(ret == 0);
+
+ ret = flb_tls_init();
+ TEST_CHECK(ret == 0);
+
+ tls = flb_tls_create(FLB_TLS_SERVER_MODE,
+ FLB_FALSE,
+ FLB_TRUE,
+ TLS_CERTIFICATE_HOSTNAME,
+ NULL,
+ NULL,
+ TLS_CERTIFICATE_FILENAME,
+ TLS_PRIVATE_KEY_FILENAME,
+ NULL);
+
+ TEST_CHECK(tls != NULL);
+
+ if (tls != NULL) {
+ /* Ingest data sample */
+ ret = flb_lib_push(ctx->flb, ctx->i_ffd, (char *) out_buf, out_buf_size);
+ TEST_CHECK(ret >= 0);
+
+ flb_net_setup_init(&downstream_net_setup);
+
+ downstream_net_setup.io_timeout = DEFAULT_IO_TIMEOUT;
+
+ downstream = flb_downstream_create(FLB_TRANSPORT_TCP,
+ FLB_IO_TCP | FLB_IO_TLS,
+ DEFAULT_HOST,
+ port,
+ tls,
+ ctx->flb->config,
+ &downstream_net_setup);
+
+ TEST_CHECK(downstream != NULL);
+
+ if (downstream != NULL) {
+ flb_stream_disable_async_mode(&downstream->base);
+
+ flb_net_socket_blocking(downstream->server_fd);
+
+ client_connection = flb_downstream_conn_get(downstream);
+
+ TEST_CHECK(client_connection != NULL);
+
+ ret = flb_io_net_read(client_connection,
+ (void *) in_buf,
+ sizeof(in_buf));
+
+ TEST_CHECK(ret > 0);
+
+ if (ret > 0) {
+ TEST_CHECK(strstr(in_buf, "hello world") != NULL);
+ }
+ }
+ }
+
+ sleep(1);
+
+ flb_stop(ctx->flb);
+ flb_downstream_destroy(downstream);
+ flb_tls_destroy(tls);
+ flb_destroy(ctx->flb);
+ flb_free(ctx);
+}
+
+void flb_test_format_msgpack()
+{
+ struct test_ctx *ctx;
+ int ret;
+ int num;
+
+ char *buf1 = "[1, {\"msg\":\"hello world\", \"val\":1000, \"nval\":-10000, \"bool\":true, \"float\":1.234}]";
+ size_t size1 = strlen(buf1);
+
+ char *expected_strs[] = {"msg", "hello world", "val", "1000", "nval", "-10000", "bool", "true", "float", "1.234"};
+ struct str_list expected = {
+ .size = sizeof(expected_strs)/sizeof(char*),
+ .lists = &expected_strs[0],
+ };
+
+ clear_output_num();
+
+ ctx = test_ctx_create();
+ if (!TEST_CHECK(ctx != NULL)) {
+ TEST_MSG("test_ctx_create failed");
+ exit(EXIT_FAILURE);
+ }
+
+ ret = flb_output_set(ctx->flb, ctx->o_ffd,
+ "match", "*",
+ "format", "msgpack",
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ ret = flb_output_set_test(ctx->flb, ctx->o_ffd,
+ "formatter", cb_check_msgpack_kv,
+ &expected, NULL);
+ TEST_CHECK(ret == 0);
+
+ /* Start the engine */
+ ret = flb_start(ctx->flb);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data sample */
+ ret = flb_lib_push(ctx->flb, ctx->i_ffd, (char *) buf1, size1);
+ TEST_CHECK(ret >= 0);
+
+ /* waiting to flush */
+ flb_time_msleep(500);
+
+ num = get_output_num();
+ if (!TEST_CHECK(num == expected.size / 2)) {
+ TEST_MSG("got %d, expected %lu", num, expected.size/2);
+ }
+
+ test_ctx_destroy(ctx);
+}
+
+void flb_test_format_json()
+{
+ struct test_ctx *ctx;
+ int ret;
+ int num;
+
+ char *buf1 = "[1, {\"msg\":\"hello world\"}]";
+ size_t size1 = strlen(buf1);
+ char *buf2 = "[2, {\"msg\":\"hello world\"}]";
+ size_t size2 = strlen(buf2);
+
+ char *expected_strs[] = {"[{\"date\":1.0,\"msg\":\"hello world\"},{\"date\":2.0,\"msg\":\"hello world\"}]"};
+ struct str_list expected = {
+ .size = sizeof(expected_strs)/sizeof(char*),
+ .lists = &expected_strs[0],
+ };
+
+ clear_output_num();
+
+ ctx = test_ctx_create();
+ if (!TEST_CHECK(ctx != NULL)) {
+ TEST_MSG("test_ctx_create failed");
+ exit(EXIT_FAILURE);
+ }
+
+ ret = flb_output_set(ctx->flb, ctx->o_ffd,
+ "match", "*",
+ "format", "json",
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ ret = flb_output_set_test(ctx->flb, ctx->o_ffd,
+ "formatter", cb_check_str_list,
+ &expected, NULL);
+ TEST_CHECK(ret == 0);
+
+ /* Start the engine */
+ ret = flb_start(ctx->flb);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data sample */
+ ret = flb_lib_push(ctx->flb, ctx->i_ffd, (char *) buf1, size1);
+ TEST_CHECK(ret >= 0);
+ ret = flb_lib_push(ctx->flb, ctx->i_ffd, (char *) buf2, size2);
+ TEST_CHECK(ret >= 0);
+
+ /* waiting to flush */
+ flb_time_msleep(500);
+
+ num = get_output_num();
+ if (!TEST_CHECK(num > 0)) {
+ TEST_MSG("no outputs");
+ }
+
+ test_ctx_destroy(ctx);
+}
+
+void flb_test_format_json_stream()
+{
+ struct test_ctx *ctx;
+ int ret;
+ int num;
+
+ char *buf1 = "[1, {\"msg\":\"hello world\"}]";
+ size_t size1 = strlen(buf1);
+ char *buf2 = "[2, {\"msg\":\"hello world\"}]";
+ size_t size2 = strlen(buf2);
+
+ char *expected_strs[] = {"{\"date\":1.0,\"msg\":\"hello world\"}{\"date\":2.0,\"msg\":\"hello world\"}"};
+ struct str_list expected = {
+ .size = sizeof(expected_strs)/sizeof(char*),
+ .lists = &expected_strs[0],
+ };
+
+ clear_output_num();
+
+ ctx = test_ctx_create();
+ if (!TEST_CHECK(ctx != NULL)) {
+ TEST_MSG("test_ctx_create failed");
+ exit(EXIT_FAILURE);
+ }
+
+ ret = flb_output_set(ctx->flb, ctx->o_ffd,
+ "match", "*",
+ "format", "json_stream",
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ ret = flb_output_set_test(ctx->flb, ctx->o_ffd,
+ "formatter", cb_check_str_list,
+ &expected, NULL);
+ TEST_CHECK(ret == 0);
+
+ /* Start the engine */
+ ret = flb_start(ctx->flb);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data sample */
+ ret = flb_lib_push(ctx->flb, ctx->i_ffd, (char *) buf1, size1);
+ TEST_CHECK(ret >= 0);
+ ret = flb_lib_push(ctx->flb, ctx->i_ffd, (char *) buf2, size2);
+ TEST_CHECK(ret >= 0);
+
+ /* waiting to flush */
+ flb_time_msleep(500);
+
+ num = get_output_num();
+ if (!TEST_CHECK(num > 0)) {
+ TEST_MSG("no outputs");
+ }
+
+ test_ctx_destroy(ctx);
+}
+
+void flb_test_format_json_lines()
+{
+ struct test_ctx *ctx;
+ int ret;
+ int num;
+
+ char *buf1 = "[1, {\"msg\":\"hello world\"}]";
+ size_t size1 = strlen(buf1);
+ char *buf2 = "[2, {\"msg\":\"hello world\"}]";
+ size_t size2 = strlen(buf2);
+
+ char *expected_strs[] = {"{\"date\":1.0,\"msg\":\"hello world\"}\n{\"date\":2.0,\"msg\":\"hello world\"}"};
+ struct str_list expected = {
+ .size = sizeof(expected_strs)/sizeof(char*),
+ .lists = &expected_strs[0],
+ };
+
+ clear_output_num();
+
+ ctx = test_ctx_create();
+ if (!TEST_CHECK(ctx != NULL)) {
+ TEST_MSG("test_ctx_create failed");
+ exit(EXIT_FAILURE);
+ }
+
+ ret = flb_output_set(ctx->flb, ctx->o_ffd,
+ "match", "*",
+ "format", "json_lines",
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ ret = flb_output_set_test(ctx->flb, ctx->o_ffd,
+ "formatter", cb_check_str_list,
+ &expected, NULL);
+ TEST_CHECK(ret == 0);
+
+ /* Start the engine */
+ ret = flb_start(ctx->flb);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data sample */
+ ret = flb_lib_push(ctx->flb, ctx->i_ffd, (char *) buf1, size1);
+ TEST_CHECK(ret >= 0);
+ ret = flb_lib_push(ctx->flb, ctx->i_ffd, (char *) buf2, size2);
+ TEST_CHECK(ret >= 0);
+
+ /* waiting to flush */
+ flb_time_msleep(500);
+
+ num = get_output_num();
+ if (!TEST_CHECK(num > 0)) {
+ TEST_MSG("no outputs");
+ }
+
+ test_ctx_destroy(ctx);
+}
+
+void flb_test_set_json_date_key()
+{
+ struct test_ctx *ctx;
+ int ret;
+ int num;
+
+ char *buf1 = "[1, {\"msg\":\"hello world\"}]";
+ size_t size1 = strlen(buf1);
+
+ char *expected_strs[] = {"{\"timestamp\":1.0,\"msg\":\"hello world\"}"};
+ struct str_list expected = {
+ .size = sizeof(expected_strs)/sizeof(char*),
+ .lists = &expected_strs[0],
+ };
+
+ clear_output_num();
+
+ ctx = test_ctx_create();
+ if (!TEST_CHECK(ctx != NULL)) {
+ TEST_MSG("test_ctx_create failed");
+ exit(EXIT_FAILURE);
+ }
+
+ ret = flb_output_set(ctx->flb, ctx->o_ffd,
+ "match", "*",
+ "format", "json",
+ "json_date_key", "timestamp",
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ ret = flb_output_set_test(ctx->flb, ctx->o_ffd,
+ "formatter", cb_check_str_list,
+ &expected, NULL);
+ TEST_CHECK(ret == 0);
+
+ /* Start the engine */
+ ret = flb_start(ctx->flb);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data sample */
+ ret = flb_lib_push(ctx->flb, ctx->i_ffd, (char *) buf1, size1);
+ TEST_CHECK(ret >= 0);
+
+ /* waiting to flush */
+ flb_time_msleep(500);
+
+ num = get_output_num();
+ if (!TEST_CHECK(num > 0)) {
+ TEST_MSG("no outputs");
+ }
+
+ test_ctx_destroy(ctx);
+}
+
+void flb_test_disable_json_date_key()
+{
+ struct test_ctx *ctx;
+ int ret;
+ int num;
+
+ char *buf1 = "[1, {\"msg\":\"hello world\"}]";
+ size_t size1 = strlen(buf1);
+
+ char *expected_strs[] = {"{\"msg\":\"hello world\"}"};
+ struct str_list expected = {
+ .size = sizeof(expected_strs)/sizeof(char*),
+ .lists = &expected_strs[0],
+ };
+
+ clear_output_num();
+
+ ctx = test_ctx_create();
+ if (!TEST_CHECK(ctx != NULL)) {
+ TEST_MSG("test_ctx_create failed");
+ exit(EXIT_FAILURE);
+ }
+
+ ret = flb_output_set(ctx->flb, ctx->o_ffd,
+ "match", "*",
+ "format", "json",
+ "json_date_key", "false",
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ ret = flb_output_set_test(ctx->flb, ctx->o_ffd,
+ "formatter", cb_check_str_list,
+ &expected, NULL);
+ TEST_CHECK(ret == 0);
+
+ /* Start the engine */
+ ret = flb_start(ctx->flb);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data sample */
+ ret = flb_lib_push(ctx->flb, ctx->i_ffd, (char *) buf1, size1);
+ TEST_CHECK(ret >= 0);
+
+ /* waiting to flush */
+ flb_time_msleep(500);
+
+ num = get_output_num();
+ if (!TEST_CHECK(num > 0)) {
+ TEST_MSG("no outputs");
+ }
+
+ test_ctx_destroy(ctx);
+}
+
+void flb_test_json_date_format_epoch()
+{
+ struct test_ctx *ctx;
+ int ret;
+ int num;
+
+ char *buf1 = "[1, {\"msg\":\"hello world\"}]";
+ size_t size1 = strlen(buf1);
+
+ char *expected_strs[] = {"{\"date\":1,\"msg\":\"hello world\"}"};
+ struct str_list expected = {
+ .size = sizeof(expected_strs)/sizeof(char*),
+ .lists = &expected_strs[0],
+ };
+
+ clear_output_num();
+
+ ctx = test_ctx_create();
+ if (!TEST_CHECK(ctx != NULL)) {
+ TEST_MSG("test_ctx_create failed");
+ exit(EXIT_FAILURE);
+ }
+
+ ret = flb_output_set(ctx->flb, ctx->o_ffd,
+ "match", "*",
+ "format", "json",
+ "json_date_format", "epoch",
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ ret = flb_output_set_test(ctx->flb, ctx->o_ffd,
+ "formatter", cb_check_str_list,
+ &expected, NULL);
+ TEST_CHECK(ret == 0);
+
+ /* Start the engine */
+ ret = flb_start(ctx->flb);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data sample */
+ ret = flb_lib_push(ctx->flb, ctx->i_ffd, (char *) buf1, size1);
+ TEST_CHECK(ret >= 0);
+
+ /* waiting to flush */
+ flb_time_msleep(500);
+
+ num = get_output_num();
+ if (!TEST_CHECK(num > 0)) {
+ TEST_MSG("no outputs");
+ }
+
+ test_ctx_destroy(ctx);
+}
+
+void flb_test_json_date_format_iso8601()
+{
+ struct test_ctx *ctx;
+ int ret;
+ int num;
+
+ char *buf1 = "[1, {\"msg\":\"hello world\"}]";
+ size_t size1 = strlen(buf1);
+
+ char *expected_strs[] = {"\"msg\":\"hello world\"", "\"date\":\"1970-01-01T00:00:01.000000Z\""};
+ struct str_list expected = {
+ .size = sizeof(expected_strs)/sizeof(char*),
+ .lists = &expected_strs[0],
+ };
+
+ clear_output_num();
+
+ ctx = test_ctx_create();
+ if (!TEST_CHECK(ctx != NULL)) {
+ TEST_MSG("test_ctx_create failed");
+ exit(EXIT_FAILURE);
+ }
+
+ ret = flb_output_set(ctx->flb, ctx->o_ffd,
+ "match", "*",
+ "format", "json",
+ "json_date_format", "iso8601",
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ ret = flb_output_set_test(ctx->flb, ctx->o_ffd,
+ "formatter", cb_check_str_list,
+ &expected, NULL);
+ TEST_CHECK(ret == 0);
+
+ /* Start the engine */
+ ret = flb_start(ctx->flb);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data sample */
+ ret = flb_lib_push(ctx->flb, ctx->i_ffd, (char *) buf1, size1);
+ TEST_CHECK(ret >= 0);
+
+ /* waiting to flush */
+ flb_time_msleep(500);
+
+ num = get_output_num();
+ if (!TEST_CHECK(num > 0)) {
+ TEST_MSG("no outputs");
+ }
+
+ test_ctx_destroy(ctx);
+}
+
+void flb_test_json_date_format_java_sql_timestamp()
+{
+ struct test_ctx *ctx;
+ int ret;
+ int num;
+
+ char *buf1 = "[1, {\"msg\":\"hello world\"}]";
+ size_t size1 = strlen(buf1);
+
+ char *expected_strs[] = {"\"msg\":\"hello world\"", "\"date\":\"1970-01-01 00:00:01.000000\""};
+ struct str_list expected = {
+ .size = sizeof(expected_strs)/sizeof(char*),
+ .lists = &expected_strs[0],
+ };
+
+ clear_output_num();
+
+ ctx = test_ctx_create();
+ if (!TEST_CHECK(ctx != NULL)) {
+ TEST_MSG("test_ctx_create failed");
+ exit(EXIT_FAILURE);
+ }
+
+ ret = flb_output_set(ctx->flb, ctx->o_ffd,
+ "match", "*",
+ "format", "json",
+ "json_date_format", "java_sql_timestamp",
+ NULL);
+ TEST_CHECK(ret == 0);
+
+ ret = flb_output_set_test(ctx->flb, ctx->o_ffd,
+ "formatter", cb_check_str_list,
+ &expected, NULL);
+ TEST_CHECK(ret == 0);
+
+ /* Start the engine */
+ ret = flb_start(ctx->flb);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest data sample */
+ ret = flb_lib_push(ctx->flb, ctx->i_ffd, (char *) buf1, size1);
+ TEST_CHECK(ret >= 0);
+
+ /* waiting to flush */
+ flb_time_msleep(500);
+
+ num = get_output_num();
+ if (!TEST_CHECK(num > 0)) {
+ TEST_MSG("no outputs");
+ }
+
+ test_ctx_destroy(ctx);
+}
+
+#define WAIT_STOP (5+1) /* pause in flb_engine_stop and buffer period */
+
+static void cb_check_basic(void *ctx, int ffd,
+ int res_ret, void *res_data, size_t res_size,
+ void *data)
+{
+ char *p;
+ flb_sds_t out_js = res_data;
+ char *index_line = "{\\\"key1\\\":\\\"123\\\"}";
+
+ p = strstr(out_js, index_line);
+ if (!TEST_CHECK(p != NULL)) {
+ TEST_MSG("Given:%s", out_js);
+ }
+
+ flb_sds_destroy(out_js);
+}
+
+
+
+void flb_test_tcp_exit_workers(void)
+{
+ int ret;
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+
+ ctx = flb_create();
+
+ /* Grace -1 to do a clean shutdown */
+ flb_service_set(ctx, "Flush", "1", "Grace", "-1", "Log_Level", "error", NULL);
+
+ /* Lib input mode */
+ in_ffd = flb_input(ctx, (char *) "dummy", NULL);
+ flb_input_set(ctx, in_ffd,
+ "tag", "test",
+ "samples", "1",
+ "dummy", "{\"key1\": 123, \"key2\": {\"s1\": \"fluent\"}}",
+ NULL);
+
+
+ /* TCP output */
+ out_ffd = flb_output(ctx, (char *) "tcp", NULL);
+ flb_output_set(ctx, out_ffd,
+ "match", "test",
+ NULL);
+
+ /* Enable test mode */
+ ret = flb_output_set_test(ctx, out_ffd, "formatter",
+ cb_check_basic,
+ NULL, NULL);
+
+ /* Start */
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ /* Stop right away */
+ flb_stop(ctx);
+
+ sleep(WAIT_STOP);
+
+ flb_destroy(ctx);
+}
+
+void flb_test_tcp_exit_no_workers(void)
+{
+ int ret;
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+
+ ctx = flb_create();
+
+ /* Grace -1 to do a clean shutdown */
+ flb_service_set(ctx, "Flush", "1", "Grace", "-1", "Log_Level", "error", NULL);
+
+ /* Lib input mode */
+ in_ffd = flb_input(ctx, (char *) "dummy", NULL);
+ flb_input_set(ctx, in_ffd,
+ "tag", "test",
+ "samples", "1",
+ "dummy", "{\"key1\": 123, \"key2\": {\"s1\": \"fluent\"}}",
+ NULL);
+
+
+ /* TCP output */
+ out_ffd = flb_output(ctx, (char *) "tcp", NULL);
+ flb_output_set(ctx, out_ffd,
+ "match", "test",
+ "workers", "0",
+ NULL);
+
+ /* Enable test mode */
+ ret = flb_output_set_test(ctx, out_ffd, "formatter",
+ cb_check_basic,
+ NULL, NULL);
+
+ /* Start */
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ /* Stop right away */
+ flb_stop(ctx);
+
+ sleep(WAIT_STOP);
+
+ flb_destroy(ctx);
+}
+
+/* Test list */
+TEST_LIST = {
+ {"tcp_with_tls", flb_test_tcp_with_tls},
+ {"format_msgpack" , flb_test_format_msgpack},
+ {"format_json" , flb_test_format_json},
+ {"format_json_stream" , flb_test_format_json_stream},
+ {"format_json_lines" , flb_test_format_json_lines},
+ {"set_json_date_key" , flb_test_set_json_date_key},
+ {"disable_json_date_key" , flb_test_disable_json_date_key},
+ {"json_date_format_epoch" , flb_test_json_date_format_epoch},
+ {"json_date_format_iso8601" , flb_test_json_date_format_iso8601},
+ {"json_date_format_java_sql_timestamp" , flb_test_json_date_format_java_sql_timestamp},
+ {"tcp_exit_workers", flb_test_tcp_exit_workers},
+ {"tcp_exit_no_workers", flb_test_tcp_exit_no_workers},
+ {NULL, NULL}
+};
diff --git a/fluent-bit/tests/runtime/out_td.c b/fluent-bit/tests/runtime/out_td.c
new file mode 100644
index 00000000..d5491605
--- /dev/null
+++ b/fluent-bit/tests/runtime/out_td.c
@@ -0,0 +1,45 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+#include <fluent-bit.h>
+#include "flb_tests_runtime.h"
+
+/* Test data */
+#include "data/td/json_td.h" /* JSON_TD */
+
+/* Test functions */
+void flb_test_td_json_long(void);
+
+/* Test list */
+TEST_LIST = {
+ {"json_long", flb_test_td_json_long },
+ {NULL, NULL}
+};
+
+
+/* It writes a big JSON message (> 3.5MB) */
+void flb_test_td_json_long(void)
+{
+ int ret;
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+
+ ctx = flb_create();
+
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ TEST_CHECK(in_ffd >= 0);
+ flb_input_set(ctx,in_ffd, "tag", "test", NULL);
+
+ out_ffd = flb_output(ctx, (char *) "td", NULL);
+ TEST_CHECK(out_ffd >= 0);
+ flb_output_set(ctx, out_ffd,"match", "test", NULL);
+
+ ret = flb_lib_config_file(ctx, (char *) "/tmp/td.conf");
+
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ flb_lib_push(ctx, in_ffd, (char *) JSON_TD , (int) sizeof(JSON_TD) - 1);
+
+ flb_stop(ctx);
+ flb_destroy(ctx);
+}
diff --git a/fluent-bit/tests/runtime/wasm/go/Makefile b/fluent-bit/tests/runtime/wasm/go/Makefile
new file mode 100644
index 00000000..ddd32f88
--- /dev/null
+++ b/fluent-bit/tests/runtime/wasm/go/Makefile
@@ -0,0 +1,24 @@
+.PHONY: build
+
+build: append_tag numeric_records say_hello modify_record
+
+append_tag:
+ tinygo build -wasm-abi=generic -target=wasi -o $@.wasm $@.go
+
+modify_record:
+ tinygo build -wasm-abi=generic -target=wasi -o $@.wasm $@.go
+
+numeric_records:
+ tinygo build -wasm-abi=generic -target=wasi -o $@.wasm $@.go
+
+say_hello:
+ tinygo build -wasm-abi=generic -target=wasi -o $@.wasm $@.go
+
+drop_record:
+ tinygo build -wasm-abi=generic -target=wasi -o $@.wasm $@.go
+
+testdata:
+ cp *.wasm ../../data/wasm
+
+clean:
+ rm -f *.wasm
diff --git a/fluent-bit/tests/runtime/wasm/go/append_tag.go b/fluent-bit/tests/runtime/wasm/go/append_tag.go
new file mode 100644
index 00000000..813afc51
--- /dev/null
+++ b/fluent-bit/tests/runtime/wasm/go/append_tag.go
@@ -0,0 +1,39 @@
+package main
+
+import (
+ "fmt"
+ "time"
+ "unsafe"
+
+ "github.com/valyala/fastjson"
+)
+
+//export filter_append_tag
+func filter_append_tag(tag *uint8, tag_len uint, time_sec uint, time_nsec uint, record *uint8, record_len uint) *uint8 {
+ btag := unsafe.Slice(tag, tag_len) // Note, requires Go 1.17+ (tinygo 0.20+)
+ brecord := unsafe.Slice(record, record_len)
+ _ = time.Unix(int64(time_sec), int64(time_nsec))
+
+ br := string(brecord)
+ var p fastjson.Parser
+ value, err := p.Parse(br)
+ if err != nil {
+ fmt.Println(err)
+ return nil
+ }
+ obj, err := value.Object()
+ if err != nil {
+ fmt.Println(err)
+ return nil
+ }
+
+ var arena fastjson.Arena
+ obj.Set("tag", arena.NewString(string(btag)))
+ s := obj.String()
+ s += string(rune(0)) // Note: explicit null terminator.
+ rv := []byte(s)
+
+ return &rv[0]
+}
+
+func main() {}
diff --git a/fluent-bit/tests/runtime/wasm/go/drop_record.go b/fluent-bit/tests/runtime/wasm/go/drop_record.go
new file mode 100644
index 00000000..30be9711
--- /dev/null
+++ b/fluent-bit/tests/runtime/wasm/go/drop_record.go
@@ -0,0 +1,8 @@
+package main
+
+//export filter_drop_record
+func filter_drop_record(tag *uint8, tag_len uint, time_sec uint, time_nsec uint, record *uint8, record_len uint) *uint8 {
+ return nil
+}
+
+func main() {}
diff --git a/fluent-bit/tests/runtime/wasm/go/go.mod b/fluent-bit/tests/runtime/wasm/go/go.mod
new file mode 100644
index 00000000..8250a72d
--- /dev/null
+++ b/fluent-bit/tests/runtime/wasm/go/go.mod
@@ -0,0 +1,9 @@
+module filter_wasm_go
+
+go 1.18
+
+require (
+ github.com/valyala/fastjson v1.6.3 // indirect
+ github.com/vmihailenco/msgpack/v5 v5.3.5 // indirect
+ github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect
+)
diff --git a/fluent-bit/tests/runtime/wasm/go/go.sum b/fluent-bit/tests/runtime/wasm/go/go.sum
new file mode 100644
index 00000000..8e55f9a7
--- /dev/null
+++ b/fluent-bit/tests/runtime/wasm/go/go.sum
@@ -0,0 +1,10 @@
+github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
+github.com/valyala/fastjson v1.6.3 h1:tAKFnnwmeMGPbwJ7IwxcTPCNr3uIzoIj3/Fh90ra4xc=
+github.com/valyala/fastjson v1.6.3/go.mod h1:CLCAqky6SMuOcxStkYQvblddUtoRxhYMGLrsQns1aXY=
+github.com/vmihailenco/msgpack/v5 v5.3.5/go.mod h1:7xyJ9e+0+9SaZT0Wt1RGleJXzli6Q/V5KbhBonMG9jc=
+github.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
diff --git a/fluent-bit/tests/runtime/wasm/go/modify_record.go b/fluent-bit/tests/runtime/wasm/go/modify_record.go
new file mode 100644
index 00000000..8a53a87f
--- /dev/null
+++ b/fluent-bit/tests/runtime/wasm/go/modify_record.go
@@ -0,0 +1,39 @@
+package main
+
+import (
+ "fmt"
+ "time"
+ "unsafe"
+
+ "github.com/valyala/fastjson"
+)
+
+//export filter_modify_record
+func filter_modify_record(tag *uint8, tag_len uint, time_sec uint, time_nsec uint, record *uint8, record_len uint) *uint8 {
+ _ = unsafe.Slice(tag, tag_len) // Note, requires Go 1.17+ (tinygo 0.20+)
+ brecord := unsafe.Slice(record, record_len)
+ _ = time.Unix(int64(time_sec), int64(time_nsec))
+
+ br := string(brecord)
+ var p fastjson.Parser
+ value, err := p.Parse(br)
+ if err != nil {
+ fmt.Println(err)
+ return nil
+ }
+ obj, err := value.Object()
+ if err != nil {
+ fmt.Println(err)
+ return nil
+ }
+
+ var arena fastjson.Arena
+ obj.Set("modify", arena.NewString(string("yes")))
+ s := obj.String()
+ s += string(rune(0)) // Note: explicit null terminator.
+ rv := []byte(s)
+
+ return &rv[0]
+}
+
+func main() {}
diff --git a/fluent-bit/tests/runtime/wasm/go/numeric_records.go b/fluent-bit/tests/runtime/wasm/go/numeric_records.go
new file mode 100644
index 00000000..4604f454
--- /dev/null
+++ b/fluent-bit/tests/runtime/wasm/go/numeric_records.go
@@ -0,0 +1,44 @@
+package main
+
+import (
+ "fmt"
+ "time"
+ "unsafe"
+
+ "github.com/valyala/fastjson"
+)
+
+//export filter_numeric_records
+func filter_numeric_records(tag *uint8, tag_len uint, time_sec uint, time_nsec uint, record *uint8, record_len uint) *uint8 {
+ _ = unsafe.Slice(tag, tag_len) // Note, requires Go 1.17+ (tinygo 0.20+)
+ brecord := unsafe.Slice(record, record_len)
+ _ = time.Unix(int64(time_sec), int64(time_nsec))
+
+ br := string(brecord)
+ var p fastjson.Parser
+ value, err := p.Parse(br)
+ if err != nil {
+ fmt.Println(err)
+ return nil
+ }
+ obj, err := value.Object()
+ if err != nil {
+ fmt.Println(err)
+ return nil
+ }
+
+ var arena fastjson.Arena
+ obj.Set("wasm_int1", arena.NewNumberInt(10))
+ obj.Set("wasm_int2", arena.NewNumberInt(100))
+ obj.Set("wasm_float1", arena.NewNumberFloat64(10.5))
+ obj.Set("wasm_float2", arena.NewNumberFloat64(100.5))
+ obj.Set("wasm_truncate_float", arena.NewNumberFloat64(120.0))
+ obj.Set("wasm_exp_float", arena.NewNumberFloat64(3.54789e-3))
+ s := obj.String()
+ s += string(rune(0)) // Note: explicit null terminator.
+ rv := []byte(s)
+
+ return &rv[0]
+}
+
+func main() {}
diff --git a/fluent-bit/tests/runtime/wasm/go/say_hello.go b/fluent-bit/tests/runtime/wasm/go/say_hello.go
new file mode 100644
index 00000000..e5efa3f9
--- /dev/null
+++ b/fluent-bit/tests/runtime/wasm/go/say_hello.go
@@ -0,0 +1,14 @@
+package main
+
+import (
+ "fmt"
+)
+
+//export filter_say_hello
+func filter_say_hello(tag *uint8, tag_len uint, time_sec uint, time_nsec uint, record *uint8, record_len uint) *uint8 {
+ fmt.Println("Hello from WASM!")
+
+ return record
+}
+
+func main() {}
diff --git a/fluent-bit/tests/runtime_shell/.gitignore b/fluent-bit/tests/runtime_shell/.gitignore
new file mode 100644
index 00000000..bf8c81f4
--- /dev/null
+++ b/fluent-bit/tests/runtime_shell/.gitignore
@@ -0,0 +1,2 @@
+runtime_shell.env
+
diff --git a/fluent-bit/tests/runtime_shell/CMakeLists.txt b/fluent-bit/tests/runtime_shell/CMakeLists.txt
new file mode 100644
index 00000000..79c96de5
--- /dev/null
+++ b/fluent-bit/tests/runtime_shell/CMakeLists.txt
@@ -0,0 +1,29 @@
+configure_file(
+ "${CMAKE_CURRENT_SOURCE_DIR}/runtime_shell.env.in"
+ "${CMAKE_CURRENT_SOURCE_DIR}/runtime_shell.env"
+ )
+
+set(UNIT_TESTS_SH
+ in_dummy_expect.sh
+ in_tail_expect.sh
+ in_http_tls_expect.sh
+ in_syslog_tcp_tls_expect.sh
+ in_syslog_tcp_plaintext_expect.sh
+ in_syslog_udp_plaintext_expect.sh
+ in_syslog_uds_dgram_plaintext_expect.sh
+ in_syslog_uds_stream_plaintext_expect.sh
+ )
+
+# Prepare list of unit tests
+foreach(script ${UNIT_TESTS_SH})
+ add_test(NAME ${script}
+ COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/${script}
+ )
+
+ set_tests_properties(${script} PROPERTIES ENVIRONMENT
+ "FLB_ROOT=${PROJECT_SOURCE_DIR};\
+FLB_RUNTIME_SHELL_PATH=${CMAKE_CURRENT_SOURCE_DIR};\
+FLB_RUNTIME_SHELL_CONF=${CMAKE_CURRENT_SOURCE_DIR}/conf;\
+FLB_BIN=${CMAKE_BINARY_DIR}/bin/fluent-bit"
+ )
+endforeach()
diff --git a/fluent-bit/tests/runtime_shell/common.sh b/fluent-bit/tests/runtime_shell/common.sh
new file mode 100644
index 00000000..1f4ec663
--- /dev/null
+++ b/fluent-bit/tests/runtime_shell/common.sh
@@ -0,0 +1,26 @@
+#!/bin/sh
+
+# This function is meant to be used alongside with
+# in_expect_base.conf or a similar setup where
+# fluent-bit is set up to create a file as soon
+# as it starts.
+#
+# I would rather use pidof but it's not viable.
+#
+wait_for_fluent_bit() {
+ result=1
+
+ for retry in `seq 10`
+ do
+ if test -f $1
+ then
+ sleep 1
+ result=0
+ break
+ fi
+
+ sleep 1
+ done
+
+ echo "$result"
+}
diff --git a/fluent-bit/tests/runtime_shell/conf/in_dummy_expect.conf b/fluent-bit/tests/runtime_shell/conf/in_dummy_expect.conf
new file mode 100644
index 00000000..1b72d64e
--- /dev/null
+++ b/fluent-bit/tests/runtime_shell/conf/in_dummy_expect.conf
@@ -0,0 +1,25 @@
+[SERVICE]
+ Flush 1
+ Grace 2
+ Log_Level info
+
+[INPUT]
+ Name dummy
+ Dummy {"r1": "someval", "s1": {"s2": null}, "r2": {"x": 0}}
+ Samples 1
+
+[FILTER]
+ Name expect
+ Match *
+ Log_Level debug
+ # Rules
+ key_exists $r1
+ key_not_exists $r0
+ key_val_is_null $s1['s2']
+ key_val_is_not_null $r2['x']
+ key_val_eq $r1 someval
+ action exit
+
+[OUTPUT]
+ Name exit
+ Match *
diff --git a/fluent-bit/tests/runtime_shell/conf/in_expect_base.conf b/fluent-bit/tests/runtime_shell/conf/in_expect_base.conf
new file mode 100644
index 00000000..760b47d5
--- /dev/null
+++ b/fluent-bit/tests/runtime_shell/conf/in_expect_base.conf
@@ -0,0 +1,28 @@
+[SERVICE]
+ Flush 1
+ Grace 1
+ Log_Level error
+ Parsers_File ${FLB_ROOT}/conf/parsers.conf
+
+[INPUT]
+ name dummy
+ samples 1
+
+[OUTPUT]
+ name file
+ match dummy.*
+ file ${SIGNAL_FILE_PATH}
+ mkdir on
+
+[FILTER]
+ Name expect
+ Match target_input
+ Log_Level debug
+ # Rules
+ key_exists $message
+ key_val_eq $message Hello!
+ action exit
+
+[OUTPUT]
+ Name exit
+ Match target_input
diff --git a/fluent-bit/tests/runtime_shell/conf/in_http_tls_expect.conf b/fluent-bit/tests/runtime_shell/conf/in_http_tls_expect.conf
new file mode 100644
index 00000000..2b66dff8
--- /dev/null
+++ b/fluent-bit/tests/runtime_shell/conf/in_http_tls_expect.conf
@@ -0,0 +1,13 @@
+@INCLUDE in_expect_base.conf
+
+[INPUT]
+ name http
+ tag target_input
+ listen ${LISTENER_HOST}
+ port ${LISTENER_PORT}
+ tls.verify no
+ tls on
+ tls.vhost ${LISTENER_VHOST}
+ tls.debug 4
+ tls.crt_file ${FLB_RUNTIME_SHELL_PATH}/tls/certificate.pem
+ tls.key_file ${FLB_RUNTIME_SHELL_PATH}/tls/private_key.pem \ No newline at end of file
diff --git a/fluent-bit/tests/runtime_shell/conf/in_syslog_tcp_plaintext_expect.conf b/fluent-bit/tests/runtime_shell/conf/in_syslog_tcp_plaintext_expect.conf
new file mode 100644
index 00000000..d47f6e33
--- /dev/null
+++ b/fluent-bit/tests/runtime_shell/conf/in_syslog_tcp_plaintext_expect.conf
@@ -0,0 +1,8 @@
+@INCLUDE in_expect_base.conf
+
+[INPUT]
+ name syslog
+ tag target_input
+ listen ${LISTENER_HOST}
+ port ${LISTENER_PORT}
+ mode tcp \ No newline at end of file
diff --git a/fluent-bit/tests/runtime_shell/conf/in_syslog_tcp_tls_expect.conf b/fluent-bit/tests/runtime_shell/conf/in_syslog_tcp_tls_expect.conf
new file mode 100644
index 00000000..afa28e6f
--- /dev/null
+++ b/fluent-bit/tests/runtime_shell/conf/in_syslog_tcp_tls_expect.conf
@@ -0,0 +1,14 @@
+@INCLUDE in_expect_base.conf
+
+[INPUT]
+ name syslog
+ tag target_input
+ listen ${LISTENER_HOST}
+ port ${LISTENER_PORT}
+ mode tcp
+ tls.verify no
+ tls on
+ tls.vhost ${LISTENER_VHOST}
+ tls.debug 4
+ tls.crt_file ${FLB_RUNTIME_SHELL_PATH}/tls/certificate.pem
+ tls.key_file ${FLB_RUNTIME_SHELL_PATH}/tls/private_key.pem \ No newline at end of file
diff --git a/fluent-bit/tests/runtime_shell/conf/in_syslog_udp_plaintext_expect.conf b/fluent-bit/tests/runtime_shell/conf/in_syslog_udp_plaintext_expect.conf
new file mode 100644
index 00000000..ae92b39e
--- /dev/null
+++ b/fluent-bit/tests/runtime_shell/conf/in_syslog_udp_plaintext_expect.conf
@@ -0,0 +1,8 @@
+@INCLUDE in_expect_base.conf
+
+[INPUT]
+ name syslog
+ tag target_input
+ listen ${LISTENER_HOST}
+ port ${LISTENER_PORT}
+ mode udp \ No newline at end of file
diff --git a/fluent-bit/tests/runtime_shell/conf/in_syslog_uds_dgram_plaintext_expect.conf b/fluent-bit/tests/runtime_shell/conf/in_syslog_uds_dgram_plaintext_expect.conf
new file mode 100644
index 00000000..48b6c375
--- /dev/null
+++ b/fluent-bit/tests/runtime_shell/conf/in_syslog_uds_dgram_plaintext_expect.conf
@@ -0,0 +1,7 @@
+@INCLUDE in_expect_base.conf
+
+[INPUT]
+ name syslog
+ tag target_input
+ path ${SOCKET_PATH}
+ mode unix_udp \ No newline at end of file
diff --git a/fluent-bit/tests/runtime_shell/conf/in_syslog_uds_stream_plaintext_expect.conf b/fluent-bit/tests/runtime_shell/conf/in_syslog_uds_stream_plaintext_expect.conf
new file mode 100644
index 00000000..cfebdd24
--- /dev/null
+++ b/fluent-bit/tests/runtime_shell/conf/in_syslog_uds_stream_plaintext_expect.conf
@@ -0,0 +1,7 @@
+@INCLUDE in_expect_base.conf
+
+[INPUT]
+ name syslog
+ tag target_input
+ path ${SOCKET_PATH}
+ mode unix_tcp \ No newline at end of file
diff --git a/fluent-bit/tests/runtime_shell/conf/in_tail_expect.conf b/fluent-bit/tests/runtime_shell/conf/in_tail_expect.conf
new file mode 100644
index 00000000..7348bacc
--- /dev/null
+++ b/fluent-bit/tests/runtime_shell/conf/in_tail_expect.conf
@@ -0,0 +1,38 @@
+[SERVICE]
+ Flush 1
+ Grace 2
+ Log_Level info
+ Parsers_File ${FLB_ROOT}/conf/parsers.conf
+
+[INPUT]
+ name tail
+ path /tmp/flb_tail_expect*.log
+ exclude_path /tmp/flb_*2.log
+ read_from_head true
+ parser json
+ refresh_interval 10
+ rotate_wait 1
+ docker_mode false
+ docker_mode_flush 4
+ path_key path_key
+ ignore_older 0
+ buffer_chunk_size 32k
+ buffer_max_size 32k
+ skip_long_lines false
+ exit_on_eof false
+ db /tmp/flb_tail_expect.db
+ db.sync full
+
+[FILTER]
+ Name expect
+ Match *
+ Log_Level debug
+ # Rules
+ key_exists $path_key
+ key_val_eq $path_key /tmp/flb_tail_expect_1.log
+ key_not_exists $nokey
+ action exit
+
+[OUTPUT]
+ Name exit
+ Match *
diff --git a/fluent-bit/tests/runtime_shell/in_dummy_expect.sh b/fluent-bit/tests/runtime_shell/in_dummy_expect.sh
new file mode 100755
index 00000000..b7547763
--- /dev/null
+++ b/fluent-bit/tests/runtime_shell/in_dummy_expect.sh
@@ -0,0 +1,8 @@
+#!/bin/sh
+
+test_in_dummy_filter_expect() {
+ $FLB_BIN -c $FLB_RUNTIME_SHELL_CONF/in_dummy_expect.conf
+}
+
+# The following command launch the unit test
+. $FLB_RUNTIME_SHELL_PATH/runtime_shell.env
diff --git a/fluent-bit/tests/runtime_shell/in_http_tls_expect.sh b/fluent-bit/tests/runtime_shell/in_http_tls_expect.sh
new file mode 100755
index 00000000..fb47e2ae
--- /dev/null
+++ b/fluent-bit/tests/runtime_shell/in_http_tls_expect.sh
@@ -0,0 +1,29 @@
+#!/bin/sh
+
+. ${FLB_RUNTIME_SHELL_PATH}/common.sh
+
+input_generator() {
+ result=$(wait_for_fluent_bit)
+
+ if test "$result" -eq "0"
+ then
+ curl -s -k \
+ -H 'content-type: application/json' \
+ -d '{"message": "Hello!"}' \
+ "https://${LISTENER_HOST}:${LISTENER_PORT}"
+ fi
+}
+
+test_in_http_tls_filter_expect() {
+ export SIGNAL_FILE_PATH="/tmp/fb_signal_$$"
+ export LISTENER_VHOST=leo.vcap.me
+ export LISTENER_HOST=127.0.0.1
+ export LISTENER_PORT=50000
+
+ input_generator &
+
+ $FLB_BIN -c $FLB_RUNTIME_SHELL_CONF/in_http_tls_expect.conf
+}
+
+# The following command launch the unit test
+. $FLB_RUNTIME_SHELL_PATH/runtime_shell.env
diff --git a/fluent-bit/tests/runtime_shell/in_syslog_tcp_plaintext_expect.sh b/fluent-bit/tests/runtime_shell/in_syslog_tcp_plaintext_expect.sh
new file mode 100755
index 00000000..414d4069
--- /dev/null
+++ b/fluent-bit/tests/runtime_shell/in_syslog_tcp_plaintext_expect.sh
@@ -0,0 +1,26 @@
+#!/bin/sh
+
+. ${FLB_RUNTIME_SHELL_PATH}/common.sh
+
+input_generator() {
+ result=$(wait_for_fluent_bit ${SIGNAL_FILE_PATH})
+
+ if test "$result" -eq "0"
+ then
+ echo '<13>1 1970-01-01T00:00:00.000000+00:00 testhost testuser - - [] Hello!' | \
+ nc -w 1 $LISTENER_HOST $LISTENER_PORT
+ fi
+}
+
+test_in_syslog_tcp_plaintext_filter_expect() {
+ export SIGNAL_FILE_PATH="/tmp/fb_signal_$$"
+ export LISTENER_HOST=127.0.0.1
+ export LISTENER_PORT=50001
+
+ input_generator &
+
+ $FLB_BIN -c $FLB_RUNTIME_SHELL_CONF/in_syslog_tcp_plaintext_expect.conf
+}
+
+# The following command launch the unit test
+. $FLB_RUNTIME_SHELL_PATH/runtime_shell.env
diff --git a/fluent-bit/tests/runtime_shell/in_syslog_tcp_tls_expect.sh b/fluent-bit/tests/runtime_shell/in_syslog_tcp_tls_expect.sh
new file mode 100755
index 00000000..d76c0e3c
--- /dev/null
+++ b/fluent-bit/tests/runtime_shell/in_syslog_tcp_tls_expect.sh
@@ -0,0 +1,27 @@
+#!/bin/sh
+
+. ${FLB_RUNTIME_SHELL_PATH}/common.sh
+
+input_generator() {
+ result=$(wait_for_fluent_bit ${SIGNAL_FILE_PATH})
+
+ if test "$result" -eq "0"
+ then
+ echo '<13>1 1970-01-01T00:00:00.000000+00:00 testhost testuser - - [] Hello!' | \
+ openssl s_client -connect $LISTENER_HOST:$LISTENER_PORT 2>&1 >/dev/null
+ fi
+}
+
+test_in_syslog_tcp_plaintext_filter_expect() {
+ export SIGNAL_FILE_PATH="/tmp/fb_signal_$$"
+ export LISTENER_VHOST=leo.vcap.me
+ export LISTENER_HOST=127.0.0.1
+ export LISTENER_PORT=50002
+
+ input_generator &
+
+ $FLB_BIN -c $FLB_RUNTIME_SHELL_CONF/in_syslog_tcp_tls_expect.conf
+}
+
+# The following command launch the unit test
+. $FLB_RUNTIME_SHELL_PATH/runtime_shell.env
diff --git a/fluent-bit/tests/runtime_shell/in_syslog_udp_plaintext_expect.sh b/fluent-bit/tests/runtime_shell/in_syslog_udp_plaintext_expect.sh
new file mode 100755
index 00000000..23970a13
--- /dev/null
+++ b/fluent-bit/tests/runtime_shell/in_syslog_udp_plaintext_expect.sh
@@ -0,0 +1,26 @@
+#!/bin/sh
+
+. ${FLB_RUNTIME_SHELL_PATH}/common.sh
+
+input_generator() {
+ result=$(wait_for_fluent_bit ${SIGNAL_FILE_PATH})
+
+ if test "$result" -eq "0"
+ then
+ echo '<13>1 1970-01-01T00:00:00.000000+00:00 testhost testuser - - [] Hello!' | \
+ nc -w 1 -u $LISTENER_HOST $LISTENER_PORT
+ fi
+}
+
+test_in_syslog_tcp_plaintext_filter_expect() {
+ export SIGNAL_FILE_PATH="/tmp/fb_signal_$$"
+ export LISTENER_HOST=127.0.0.1
+ export LISTENER_PORT=50003
+
+ input_generator &
+
+ $FLB_BIN -c $FLB_RUNTIME_SHELL_CONF/in_syslog_udp_plaintext_expect.conf
+}
+
+# The following command launch the unit test
+. $FLB_RUNTIME_SHELL_PATH/runtime_shell.env
diff --git a/fluent-bit/tests/runtime_shell/in_syslog_uds_dgram_plaintext_expect.sh b/fluent-bit/tests/runtime_shell/in_syslog_uds_dgram_plaintext_expect.sh
new file mode 100755
index 00000000..2ab7703b
--- /dev/null
+++ b/fluent-bit/tests/runtime_shell/in_syslog_uds_dgram_plaintext_expect.sh
@@ -0,0 +1,29 @@
+#!/bin/sh
+
+. ${FLB_RUNTIME_SHELL_PATH}/common.sh
+
+input_generator() {
+ result=$(wait_for_fluent_bit ${SIGNAL_FILE_PATH})
+
+ if test "$result" -eq "0"
+ then
+ echo '<13>Jan 1 00:00:00 testuser: Hello!' | nc -w 1 -U -u $SOCKET_PATH
+ fi
+}
+
+test_in_syslog_uds_stream_plaintext_filter_expect() {
+ platform=$(uname)
+
+ if test "$platform" != "Darwin"
+ then
+ export SIGNAL_FILE_PATH="/tmp/fb_signal_$$"
+ export SOCKET_PATH=/tmp/fluent_bit_syslog_uds_dgram.sock
+
+ input_generator &
+
+ $FLB_BIN -c $FLB_RUNTIME_SHELL_CONF/in_syslog_uds_dgram_plaintext_expect.conf
+ fi
+}
+
+# The following command launch the unit test
+. $FLB_RUNTIME_SHELL_PATH/runtime_shell.env
diff --git a/fluent-bit/tests/runtime_shell/in_syslog_uds_stream_plaintext_expect.sh b/fluent-bit/tests/runtime_shell/in_syslog_uds_stream_plaintext_expect.sh
new file mode 100755
index 00000000..b3a38cc2
--- /dev/null
+++ b/fluent-bit/tests/runtime_shell/in_syslog_uds_stream_plaintext_expect.sh
@@ -0,0 +1,24 @@
+#!/bin/sh
+
+. ${FLB_RUNTIME_SHELL_PATH}/common.sh
+
+input_generator() {
+ result=$(wait_for_fluent_bit ${SIGNAL_FILE_PATH})
+
+ if test "$result" -eq "0"
+ then
+ echo '<13>Jan 1 00:00:00 testuser: Hello!' | nc -w 1 -U $SOCKET_PATH
+ fi
+}
+
+test_in_syslog_uds_stream_plaintext_filter_expect() {
+ export SIGNAL_FILE_PATH="/tmp/fb_signal_$$"
+ export SOCKET_PATH=/tmp/fluent_bit_syslog_uds_stream.sock
+
+ input_generator &
+
+ $FLB_BIN -c $FLB_RUNTIME_SHELL_CONF/in_syslog_uds_stream_plaintext_expect.conf
+}
+
+# The following command launch the unit test
+. $FLB_RUNTIME_SHELL_PATH/runtime_shell.env
diff --git a/fluent-bit/tests/runtime_shell/in_tail/README.md b/fluent-bit/tests/runtime_shell/in_tail/README.md
new file mode 100644
index 00000000..78c77525
--- /dev/null
+++ b/fluent-bit/tests/runtime_shell/in_tail/README.md
@@ -0,0 +1,126 @@
+# Fluent Bit Tail Input Plugin Tests
+
+The following directory contains tests for Tail input plugin behaviors.
+
+## run_tests.sh
+
+This script provide validations for offsets, database file entries and rotation under different scenarios. The following tests are available in the script
+
+- test_normal_rotation
+- test_single_static_rotation
+- test_truncate
+- test_rotate_link
+- test_truncate_link
+
+Running the script ```test_rotation.sh``` will run every test listed above, to run a single test just append it name, e.g:
+
+```
+./test_rotation.sh -- test_truncate
+```
+
+### 1. Normal Rotation
+
+**Unit**
+
+```test_normal_rotation```
+
+**Description**
+
+Run the logger tool that creates 5 different files, write 100000 messages to each one while rotating at 256KB.
+
+This test enable the database backend for Tail so it also helps to validate expected entries into the 'in_tail_files' table.
+
+**Configuration File**
+
+```conf/normal_rotation.conf```
+
+### 2. Single Static Rotation
+
+**Unit**
+
+```test_single_static_rotation```
+
+**Description**
+
+Run the logger tool that creates 1 big file and let Fluent Bit process it in the static mode, before to promote it to 'events' and it gets rotated.
+
+**Configuration File**
+
+```conf/single_static_rotation.conf```
+
+### 3. Truncate
+
+**Unit**
+
+```test_truncate```
+
+**Description**
+
+ Some environments still rely on truncation mode or well known as copytruncate,
+ this is the definition by logrotate(8):
+
+> Truncate the original log file to zero size in place after creating a copy,
+> instead of moving the old log file and optionally creating a new one. It
+> can be used when some program cannot be told to close its logfile and
+> thus might continue writing (appending) to the previous log file forever.
+>
+> Note that there is a very small time slice between copying the file and
+> truncating it, so some logging data might be lost. When this option is
+> used, the create option will have no effect, as the old log file stays in
+> place.
+
+This test checks that after a truncation the new lines added are properly
+processed.
+
+**Configuration File**
+
+```conf/truncate_rotation.conf```
+
+### 4. Rotate Link
+
+**Unit**
+
+```test_rotate_link```
+
+**Description**
+
+This test checks that a monitored link, upon rotation, keeps the proper offset and database status for the real file.
+
+ Example:
+
+ - file with data: data.log
+ - monitored link: test.log
+
+ Check the behavior upon the following rotation: test.log -> test.log.1
+
+**Configuration File**
+
+```conf/rotate_link.conf```
+
+### 5. Truncate Link
+
+**Unit**
+
+```test_truncate_link```
+
+**Description**
+
+Test a link that gets a truncation and Fluent Bit properly use the new offset
+
+**Configuration File**
+
+```conf/truncate_link.conf```
+
+### 6. Multiline Rotation
+
+**Unit**
+
+```test_multiline_rotation```
+
+**Description**
+
+Test a multiline rotation for issue 4190.
+
+**Configuration File**
+
+```conf/multiline_rotation.conf```
diff --git a/fluent-bit/tests/runtime_shell/in_tail/conf/multiline_rotation.conf b/fluent-bit/tests/runtime_shell/in_tail/conf/multiline_rotation.conf
new file mode 100644
index 00000000..3695014a
--- /dev/null
+++ b/fluent-bit/tests/runtime_shell/in_tail/conf/multiline_rotation.conf
@@ -0,0 +1,60 @@
+[SERVICE]
+ flush 1
+ daemon off
+ log_level debug
+ log_file ${TEST_DIR}/out.log
+
+[INPUT]
+ name tail
+ tag a
+ path ${TEST_DIR}/a.log
+ db ${TEST_DIR}/a.db
+ db.sync full
+ multiline.parser cri
+ rotate_wait 5
+ refresh_interval 2
+
+[INPUT]
+ name tail
+ tag b
+ path ${TEST_DIR}/b.log
+ db ${TEST_DIR}/b.db
+ db.sync full
+ multiline.parser cri
+ rotate_wait 5
+ refresh_interval 2
+
+[INPUT]
+ name tail
+ tag c
+ path ${TEST_DIR}/c.log
+ db ${TEST_DIR}/c.db
+ db.sync full
+ multiline.parser cri
+ rotate_wait 5
+ refresh_interval 2
+
+[INPUT]
+ name tail
+ tag d
+ path ${TEST_DIR}/d.log
+ db ${TEST_DIR}/d.db
+ db.sync full
+ multiline.parser cri
+ rotate_wait 5
+ refresh_interval 2
+
+[INPUT]
+ name tail
+ tag e
+ path ${TEST_DIR}/e.log
+ db ${TEST_DIR}/e.db
+ db.sync full
+ multiline.parser cri
+ rotate_wait 5
+ refresh_interval 2
+
+[OUTPUT]
+ name file
+ match *
+ path ${TEST_DIR}
diff --git a/fluent-bit/tests/runtime_shell/in_tail/conf/normal_rotation.conf b/fluent-bit/tests/runtime_shell/in_tail/conf/normal_rotation.conf
new file mode 100644
index 00000000..0265df5d
--- /dev/null
+++ b/fluent-bit/tests/runtime_shell/in_tail/conf/normal_rotation.conf
@@ -0,0 +1,55 @@
+[SERVICE]
+ flush 1
+ daemon off
+ log_level debug
+ log_file ${TEST_DIR}/out.log
+
+[INPUT]
+ name tail
+ tag a
+ path ${TEST_DIR}/a.log
+ db ${TEST_DIR}/a.db
+ db.sync full
+ rotate_wait 5
+ refresh_interval 2
+
+[INPUT]
+ name tail
+ tag b
+ path ${TEST_DIR}/b.log
+ db ${TEST_DIR}/b.db
+ db.sync full
+ rotate_wait 5
+ refresh_interval 2
+
+[INPUT]
+ name tail
+ tag c
+ path ${TEST_DIR}/c.log
+ db ${TEST_DIR}/c.db
+ db.sync full
+ rotate_wait 5
+ refresh_interval 2
+
+[INPUT]
+ name tail
+ tag d
+ path ${TEST_DIR}/d.log
+ db ${TEST_DIR}/d.db
+ db.sync full
+ rotate_wait 5
+ refresh_interval 2
+
+[INPUT]
+ name tail
+ tag e
+ path ${TEST_DIR}/e.log
+ db ${TEST_DIR}/e.db
+ db.sync full
+ rotate_wait 5
+ refresh_interval 2
+
+[OUTPUT]
+ name file
+ match *
+ path ${TEST_DIR}
diff --git a/fluent-bit/tests/runtime_shell/in_tail/conf/rotate_link.conf b/fluent-bit/tests/runtime_shell/in_tail/conf/rotate_link.conf
new file mode 100644
index 00000000..f0b017d4
--- /dev/null
+++ b/fluent-bit/tests/runtime_shell/in_tail/conf/rotate_link.conf
@@ -0,0 +1,20 @@
+[SERVICE]
+ flush 1
+ daemon off
+ log_level debug
+ log_file ${TEST_DIR}/out.log
+
+[INPUT]
+ name tail
+ tag a
+ path ${TEST_DIR}/a.log
+ db ${TEST_DIR}/a.db
+ db.sync full
+ rotate_wait 5
+ watcher_interval 1
+ refresh_interval 2
+
+[OUTPUT]
+ name file
+ match *
+ path ${TEST_DIR}
diff --git a/fluent-bit/tests/runtime_shell/in_tail/conf/single_static_rotation.conf b/fluent-bit/tests/runtime_shell/in_tail/conf/single_static_rotation.conf
new file mode 100644
index 00000000..ac883c95
--- /dev/null
+++ b/fluent-bit/tests/runtime_shell/in_tail/conf/single_static_rotation.conf
@@ -0,0 +1,19 @@
+[SERVICE]
+ flush 1
+ daemon off
+ log_level debug
+ log_file ${TEST_DIR}/out.log
+
+[INPUT]
+ name tail
+ tag a
+ path ${TEST_DIR}/a.log
+ db ${TEST_DIR}/a.db
+ db.sync full
+ rotate_wait 5
+ refresh_interval 2
+
+[OUTPUT]
+ name file
+ match *
+ path ${TEST_DIR}
diff --git a/fluent-bit/tests/runtime_shell/in_tail/conf/truncate_link.conf b/fluent-bit/tests/runtime_shell/in_tail/conf/truncate_link.conf
new file mode 100644
index 00000000..6cfa5410
--- /dev/null
+++ b/fluent-bit/tests/runtime_shell/in_tail/conf/truncate_link.conf
@@ -0,0 +1,19 @@
+[SERVICE]
+ flush 1
+ daemon off
+ log_level debug
+ log_file ${TEST_DIR}/out.log
+
+[INPUT]
+ name tail
+ tag a
+ path ${TEST_DIR}/a.log
+ db ${TEST_DIR}/a.db
+ db.sync full
+ rotate_wait 5
+ refresh_interval 1
+
+[OUTPUT]
+ name file
+ match *
+ path ${TEST_DIR}
diff --git a/fluent-bit/tests/runtime_shell/in_tail/conf/truncate_rotation.conf b/fluent-bit/tests/runtime_shell/in_tail/conf/truncate_rotation.conf
new file mode 100644
index 00000000..ac883c95
--- /dev/null
+++ b/fluent-bit/tests/runtime_shell/in_tail/conf/truncate_rotation.conf
@@ -0,0 +1,19 @@
+[SERVICE]
+ flush 1
+ daemon off
+ log_level debug
+ log_file ${TEST_DIR}/out.log
+
+[INPUT]
+ name tail
+ tag a
+ path ${TEST_DIR}/a.log
+ db ${TEST_DIR}/a.db
+ db.sync full
+ rotate_wait 5
+ refresh_interval 2
+
+[OUTPUT]
+ name file
+ match *
+ path ${TEST_DIR}
diff --git a/fluent-bit/tests/runtime_shell/in_tail/logger_file.py b/fluent-bit/tests/runtime_shell/in_tail/logger_file.py
new file mode 100755
index 00000000..a8e2a1b3
--- /dev/null
+++ b/fluent-bit/tests/runtime_shell/in_tail/logger_file.py
@@ -0,0 +1,72 @@
+#!/bin/env python
+
+import sys
+import uuid
+import time
+import signal
+import logging
+from argparse import ArgumentParser
+from logging.handlers import RotatingFileHandler
+from threading import Thread
+
+class LoggerManager:
+ def __init__(self, args):
+ # KB to bytes
+ self.max_bytes = (args.size * 1000)
+ self.backup = args.backup
+ self.lines = args.lines
+ self.delay = args.delay
+ self.threads = []
+
+ # Create a thread for every writer
+ for f in args.filenames:
+ thread = Thread(target = self.single_logger_thread, args = (f,))
+ if thread is None:
+ print("error creating thread")
+ sys.exit(1)
+ self.threads.append(thread)
+ thread.start()
+ print("Logger thread for '" + f + "' has started")
+
+ for th in self.threads:
+ th.join()
+ print("Logger thread finished")
+
+ def single_logger_thread(self, name):
+ logger = logging.getLogger(name)
+ logger.setLevel(logging.DEBUG)
+ handler = RotatingFileHandler(name, maxBytes = self.max_bytes,
+ backupCount = self.backup)
+ logger.addHandler(handler)
+ rnd = uuid.uuid4()
+
+ i = 0
+ while i < self.lines:
+ logger.debug(rnd)
+ if self.delay > 0.0:
+ time.sleep(self.delay / 1000.0)
+ i = i + 1
+
+def signal_handler(sig, frame):
+ print("stopping logger")
+ sys.exit(0)
+
+if __name__ == '__main__':
+ signal.signal(signal.SIGINT, signal_handler)
+
+ # Define arguments
+ parser = ArgumentParser()
+ parser.add_argument("-b", "--backup", dest="backup", default=50, type=int)
+ parser.add_argument("-d", "--delay", dest="delay", default=0.1, type=float,
+ help="milliseconds delay between line writes")
+ parser.add_argument("-l", "--lines", dest="lines", default=1000, type=int)
+ parser.add_argument("-f", "--file", dest="filenames", action='append', required=True,
+ help="write logs to FILE", metavar="FILE")
+ parser.add_argument("-s", "--size", dest="size", type=int,
+ help="maximum log file size in KB before rotation",
+ default=256)
+ # Read arguments
+ args = parser.parse_args()
+
+ # Start the Logger
+ lm = LoggerManager(args)
diff --git a/fluent-bit/tests/runtime_shell/in_tail/run_tests.sh b/fluent-bit/tests/runtime_shell/in_tail/run_tests.sh
new file mode 100755
index 00000000..09bb9c43
--- /dev/null
+++ b/fluent-bit/tests/runtime_shell/in_tail/run_tests.sh
@@ -0,0 +1,530 @@
+#!/bin/sh
+
+# Environment variables
+FLB_BIN=`realpath ../../../build/bin/fluent-bit`
+FLB_RUNTIME_SHELL_PATH=`realpath $(pwd)/../`
+FLB_RUN_TEST=`realpath $FLB_RUNTIME_SHELL_PATH/../lib/shunit2/shunit2`
+
+# Colorize shunit2
+bold=$(tput bold)
+normal=$(tput sgr0)
+SHUNIT_TEST_PREFIX="$bold==========> UNIT TEST: $normal"
+
+# 1. Normal Rotation
+# ------------------
+# Run the logger tool that creates 5 different files, write 100000 messages to each one
+# while rotating at 256KB.
+#
+# This test enable the database backend for Tail so it also helps to validate expected
+# entries into the 'in_tail_files' table.
+#
+# Configuration file used: conf/normal_rotation.conf
+
+test_normal_rotation() {
+ # Helper function to check monitored files
+ sqlite_check()
+ {
+ # Incoming parameters:
+ # $1: temporal directory to store data
+ # $2: database file name
+ # $3: Fluent Bit PID
+ #
+ # This function store the remaining monitored files listed in the database,
+ # we send the output to an .inodes for troubleshooting purposes if required
+ sqlite3 $1/$2 -batch \
+ ".headers off" ".width 20" "SELECT inode FROM in_tail_files" > \
+ $1/$2.inodes
+
+ rows=`cat $1/$2.inodes | wc -l | tr -d -C '[0-9]'`
+ if [ $rows != "1" ]; then
+ echo "> database file $1/$2 contains $rows rows, inodes:"
+ cat $1/$2.inodes
+ echo "> open files"
+ ls -l /proc/$3/fd/ | grep \\.log
+ else
+ echo "> database file $1/$2 is OK"
+ fi
+ ${_ASSERT_EQUALS_} "1" $rows
+ }
+
+ # Prepare test directory
+ export TEST_DIR=tmp_test
+ rm -rf $TEST_DIR
+ mkdir $TEST_DIR
+
+ # Create empty files so Fluent Bit will enqueue them on start
+ for logfile in a b c d e ; do
+ touch $TEST_DIR/$logfile.log
+ done
+
+ # Run Fluent Bit
+ $FLB_BIN -c conf/normal_rotation.conf &
+ FLB_PID=$!
+ echo "Fluent Bit started, pid=$FLB_PID"
+
+ # Start the Logger: 5 files = 500000 log lines in total
+ python logger_file.py -l 100000 -s 256 -b 100 -d 0.1 \
+ -f $TEST_DIR/a.log \
+ -f $TEST_DIR/b.log \
+ -f $TEST_DIR/c.log \
+ -f $TEST_DIR/d.log \
+ -f $TEST_DIR/e.log
+
+ echo "Logger finished...wait 10 seconds"
+ sleep 10
+
+ # Count number of processed lines
+ write_lines=`cat $TEST_DIR/[abcdefghij].log* | wc -l`
+ read_lines=`cat $TEST_DIR/[abcdefghij] | wc -l`
+
+ echo "> write lines: $write_lines"
+ echo "> read lines : $read_lines"
+
+ # Check we processed same number of records
+ ${_ASSERT_EQUALS_} $write_lines $read_lines
+
+ # Validate our database files has only one remaining entry per database file
+ for logfile in a b c d e; do
+ sqlite_check $TEST_DIR "$logfile.db" $FLB_PID
+ done
+
+ # Stop Fluent Bit (SIGTERM)
+ kill -15 $FLB_PID
+}
+
+# 2. Single Static Rotation (static process mode + rotation)
+# ----------------------------------------------------------
+# Run the logger tool that creates 1 big file and let Fluent Bit process it in
+# the static mode, before to promote it to 'events' and it gets rotated.
+#
+# Configuration file used: conf/single_static_rotation.conf
+
+test_single_static_rotation() {
+ # Write a log file of 200000 lines
+
+ # Prepare test directory
+ export TEST_DIR=tmp_test
+ rm -rf $TEST_DIR
+ mkdir $TEST_DIR
+
+ # Create empty files so Fluent Bit will enqueue them on start
+ touch $TEST_DIR/a.log
+
+ # Start the Logger: 1 file with 400000 lines, we use a big size (-s) to
+ # avoid rotation
+ python logger_file.py -l 400000 -s 200000 -b 100 -d 0 \
+ -f $TEST_DIR/a.log
+ lines=`cat $TEST_DIR/a.log | wc -l`
+ echo "Logger done, written lines "$lines
+
+ # Run Fluent Bit
+ $FLB_BIN -c conf/single_static_rotation.conf &
+ FLB_PID=$!
+ echo "Fluent Bit started, pid=$FLB_PID"
+
+ # Wait 3 seconds before rotation
+ sleep 2
+ mv $TEST_DIR/a.log $TEST_DIR/a.log.1
+
+ lines=`cat $TEST_DIR/a | wc -l`
+ echo "file Rotated, mid-check: processed lines $lines"
+ sleep 30
+
+ # Count number of processed lines
+ write_lines=`cat $TEST_DIR/a.log.1 | wc -l`
+ read_lines=`cat $TEST_DIR/a | wc -l`
+
+ echo "> write lines: $write_lines"
+ echo "> read lines : $read_lines"
+
+ # Check we processed same number of records
+ ${_ASSERT_EQUALS_} $write_lines $read_lines
+
+ # Validate our database files has only one remaining entry per database file
+ #sqlite_check $TEST_DIR "$logfile.db" $FLB_PID
+
+ # Stop Fluent Bit (SIGTERM)
+ kill -15 $FLB_PID
+}
+
+# 3. Truncate
+# -----------
+# Some environments still rely on truncation mode or well known as copytruncate,
+# this is the definition by logrotate(8):
+#
+# "Truncate the original log file to zero size in place after creating a copy,
+# instead of moving the old log file and optionally creating a new one. It
+# can be used when some program cannot be told to close its logfile and
+# thus might continue writing (appending) to the previous log file forever.
+#
+# Note that there is a very small time slice between copying the file and
+# truncating it, so some logging data might be lost. When this option is
+# used, the create option will have no effect, as the old log file stays in
+# place."
+#
+# This test checks that after a truncation the new lines added are properly
+# processed.
+#
+# Configuration file used: conf/truncate_rotation.conf
+
+test_truncate() {
+ # Helper function to check monitored files
+ sqlite_check()
+ {
+ # Incoming parameters:
+ # $1: temporal directory to store data
+ # $2: database file name
+ # $3: Fluent Bit PID
+ #
+ # This function store the remaining monitored files listed in the database,
+ # we send the output to an .inodes for troubleshooting purposes if required
+
+ # Get the last size of the 'a.log' file and check we have the same value
+ # in the database
+ offset=`wc -c < $TEST_DIR/a.log`
+
+ sqlite3 $1/$2 -batch \
+ ".headers off" "SELECT inode FROM in_tail_files WHERE offset=$offset" > \
+ $1/$2.offset
+
+ rows=`cat $1/$2.offset | wc -l | tr -d -C '[0-9]'`
+ if [ $rows != "1" ]; then
+ echo "> invalid database content:"
+ cat $1/$2.offset
+ echo "> open files"
+ ls -l /proc/$3/fd/ | grep \\.log
+ else
+ echo "> database file $1/$2 is OK"
+ fi
+ ${_ASSERT_EQUALS_} "1" $rows
+ }
+
+ # Prepare test directory
+ export TEST_DIR=tmp_test
+ rm -rf $TEST_DIR
+ mkdir $TEST_DIR
+
+ # Create empty files so Fluent Bit will enqueue them on start
+ touch $TEST_DIR/a.log
+
+ # Start the Logger: 1 file with 200 lines, we use a big size limit (-s) to
+ # avoid rotation
+ python logger_file.py -l 200 -s 200000 -b 100 -d 0 -f $TEST_DIR/a.log
+ lines=`cat $TEST_DIR/a.log | wc -l`
+ echo "Logger done, written lines "$lines
+
+ # Run Fluent Bit
+ $FLB_BIN -c conf/truncate_rotation.conf &
+ FLB_PID=$!
+ echo "Fluent Bit started, pid=$FLB_PID"
+
+ # Wait 2 seconds before truncation
+ sleep 2
+ pre_lines=`cat $TEST_DIR/a.log | wc -l`
+ truncate -s 0 $TEST_DIR/a.log
+
+ lines=`cat $TEST_DIR/a | wc -l`
+ echo "file truncated, mid-check: processed lines $lines"
+
+ # Append 100 more lines
+ python logger_file.py -l 100 -s 200000 -b 100 -d 0 -f $TEST_DIR/a.log
+
+ sleep 3
+
+ # Count number of processed lines
+ write_lines=300
+ read_lines=`cat $TEST_DIR/a | wc -l`
+
+ echo "> write lines: $write_lines"
+ echo "> read lines : $read_lines"
+
+ # Check we processed same number of records
+ ${_ASSERT_EQUALS_} $write_lines $read_lines
+
+ sqlite_check $TEST_DIR a.db $FLB_PID
+
+ # Stop Fluent Bit (SIGTERM)
+ kill -15 $FLB_PID
+}
+
+# 4. Rotate Link
+# --------------
+# This case checks that a monitored link, upon rotation, keeps the proper offset
+# and database status for the real file.
+#
+# Example:
+#
+# - file with data: data.log
+# - monitored link: test.log
+#
+# Check the behavior upon test.log -> test.log.1 behavior
+#
+# Configuration file used: conf/rotate_link.conf
+
+test_rotate_link() {
+ # Helper function to check monitored files
+ sqlite_check()
+ {
+ # Incoming parameters:
+ # $1: temporal directory to store data
+ # $2: database file name
+ # $3: Fluent Bit PID
+ #
+ # This function store the remaining monitored files listed in the database,
+ # we send the output to an .inodes for troubleshooting purposes if required
+
+ # Get the last size of the file pointed by 'a.log.1' and check we have the
+ # same value in the database
+ offset=`wc -c < $TEST_DIR/a.log.1`
+
+ sqlite3 $1/$2 -batch \
+ ".headers off" "SELECT inode FROM in_tail_files WHERE offset=$offset \
+ AND rotated=1" > $1/$2.offset
+
+ rows=`cat $1/$2.offset | wc -l | tr -d -C '[0-9]'`
+ if [ $rows != "1" ]; then
+ echo "> invalid database content:"
+ cat $1/$2.offset
+ echo "> open files"
+ ls -l /proc/$3/fd/ | grep \\.log
+ else
+ echo "> offset database check $1/$2 is OK"
+ fi
+ ${_ASSERT_EQUALS_} "1" $rows
+
+ # After rotate_wait (5 secs + watcher) we expect an empty database
+ sleep 6
+ sqlite3 $1/$2 -batch \
+ ".headers off" "SELECT inode FROM in_tail_files WHERE offset=$offset \
+ AND rotated=1" > $1/$2.offset
+
+ rows=`cat $1/$2.offset | wc -l | tr -d -C '[0-9]'`
+ if [ $rows != "0" ]; then
+ echo "> invalid database content:"
+ cat $1/$2.offset
+ echo "> open files"
+ ls -l /proc/$3/fd/ | grep \\.log
+ else
+ echo "> empty database check $1/$2 is OK"
+ fi
+ ${_ASSERT_EQUALS_} "0" $rows
+ }
+
+ # Prepare test directory
+ export TEST_DIR=tmp_test
+ rm -rf $TEST_DIR
+ mkdir $TEST_DIR
+
+ # Create empty files so Fluent Bit will enqueue them on start
+ touch $TEST_DIR/data.log
+
+ # Start the Logger: 1 file with 100 lines, we use a big size limit (-s) to
+ # avoid rotation
+ python logger_file.py -l 100 -s 200000 -b 100 -d 0 -f $TEST_DIR/data.log
+ lines=`cat $TEST_DIR/data.log | wc -l`
+ ln -s data.log $TEST_DIR/a.log
+ echo "Logger done, written lines "$lines
+
+ # Run Fluent Bit
+ $FLB_BIN -c conf/rotate_link.conf &
+ FLB_PID=$!
+ echo "Fluent Bit started, pid=$FLB_PID"
+
+ # Wait 2 seconds and rotate file
+ sleep 2
+ pre_lines=`cat $TEST_DIR/a.log | wc -l`
+ mv $TEST_DIR/a.log $TEST_DIR/a.log.1
+
+ lines=`cat $TEST_DIR/a | wc -l`
+ echo "file rotated, mid-check: processed lines $lines"
+
+ # Append 200 more lines to the rotated link
+ python logger_file.py -l 200 -s 200000 -b 100 -d 0 -f $TEST_DIR/a.log.1
+
+ # Count number of processed lines
+ sleep 3
+ write_lines=300
+ read_lines=`cat $TEST_DIR/a | wc -l`
+
+ echo "> write lines: $write_lines"
+ echo "> read lines : $read_lines"
+
+ # Check we processed same number of records
+ ${_ASSERT_EQUALS_} $write_lines $read_lines
+
+ # Check that database file have the right offset and mark the file as rotated
+ sqlite_check $TEST_DIR a.db $FLB_PID
+
+ # Stop Fluent Bit (SIGTERM)
+ kill -15 $FLB_PID
+}
+
+# 5. Truncate Link
+#
+# Test a link that gets a truncation and Fluent Bit properly use the new offset
+#
+# Configuration file used: conf/truncate_link.conf
+
+test_truncate_link() {
+ # Helper function to check monitored files
+ sqlite_check()
+ {
+ # Incoming parameters:
+ # $1: temporal directory to store data
+ # $2: database file name
+ # $3: Fluent Bit PID
+ #
+ # This function store the remaining monitored files listed in the database,
+ # we send the output to an .inodes for troubleshooting purposes if required
+
+ # Get the last size of the 'a.log' file and check we have the same value
+ # in the database
+ offset=`wc -c < $TEST_DIR/a.log`
+
+ sqlite3 $1/$2 -batch \
+ ".headers off" "SELECT inode FROM in_tail_files WHERE offset=$offset" > \
+ $1/$2.offset
+
+ rows=`cat $1/$2.offset | wc -l | tr -d -C '[0-9]'`
+ if [ $rows != "1" ]; then
+ echo "> invalid database content:"
+ cat $1/$2.offset
+ echo "> open files"
+ ls -l /proc/$3/fd/ | grep \\.log
+ else
+ echo "> database file $1/$2 is OK"
+ fi
+ ${_ASSERT_EQUALS_} "1" $rows
+ }
+
+ # Prepare test directory
+ export TEST_DIR=tmp_test
+ rm -rf $TEST_DIR
+ mkdir $TEST_DIR
+
+ # Create empty files so Fluent Bit will enqueue them on start
+ touch $TEST_DIR/data.log
+
+ # Start the Logger: 1 file with 100 lines, we use a big size limit (-s) to
+ # avoid rotation
+ python logger_file.py -l 100 -s 200000 -b 100 -d 0 -f $TEST_DIR/data.log
+ lines=`cat $TEST_DIR/data.log | wc -l`
+ ln -s data.log $TEST_DIR/a.log
+ echo "Logger done, written lines "$lines
+
+ # Run Fluent Bit
+ $FLB_BIN -c conf/truncate_link.conf &
+ FLB_PID=$!
+ echo "Fluent Bit started, pid=$FLB_PID"
+
+ # Wait 1 second before truncation
+ sleep 1
+ pre_lines=`cat $TEST_DIR/a.log | wc -l`
+ truncate -s 0 $TEST_DIR/a.log
+
+ sleep 2
+ lines=`cat $TEST_DIR/a | wc -l`
+ echo "file truncated, mid-check: processed lines $lines"
+
+ # Append 200 more lines
+ python logger_file.py -l 200 -s 200000 -b 100 -d 0 -f $TEST_DIR/a.log
+
+ sleep 4
+
+ # Count number of processed lines
+ write_lines=300
+ read_lines=`cat $TEST_DIR/a | wc -l`
+
+ echo "> write lines: $write_lines"
+ echo "> read lines : $read_lines"
+
+ # Check we processed same number of records
+ ${_ASSERT_EQUALS_} $write_lines $read_lines
+
+ # Stop Fluent Bit (SIGTERM)
+ kill -15 $FLB_PID
+}
+
+# 6. Multiline + rotation
+# ------------------
+# Run the logger tool that creates 5 different files, write 100000 messages to each one
+# while rotating at 256KB.
+#
+# This test for issue 4190
+#
+# Configuration file used: conf/multiline_rotation.conf
+
+test_multiline_rotation() {
+ # Helper function to check monitored files
+ sqlite_check()
+ {
+ # Incoming parameters:
+ # $1: temporal directory to store data
+ # $2: database file name
+ # $3: Fluent Bit PID
+ #
+ # This function store the remaining monitored files listed in the database,
+ # we send the output to an .inodes for troubleshooting purposes if required
+ sqlite3 $1/$2 -batch \
+ ".headers off" ".width 20" "SELECT inode FROM in_tail_files" > \
+ $1/$2.inodes
+
+ rows=`cat $1/$2.inodes | wc -l | tr -d -C '[0-9]'`
+ if [ $rows != "1" ]; then
+ echo "> database file $1/$2 contains $rows rows, inodes:"
+ cat $1/$2.inodes
+ echo "> open files"
+ ls -l /proc/$3/fd/ | grep \\.log
+ else
+ echo "> database file $1/$2 is OK"
+ fi
+ ${_ASSERT_EQUALS_} "1" $rows
+ }
+
+ # Prepare test directory
+ export TEST_DIR=tmp_test
+ rm -rf $TEST_DIR
+ mkdir $TEST_DIR
+
+ # Create empty files so Fluent Bit will enqueue them on start
+ for logfile in a b c d e ; do
+ touch $TEST_DIR/$logfile.log
+ done
+
+ # Run Fluent Bit
+ $FLB_BIN -c conf/multiline_rotation.conf &
+ FLB_PID=$!
+ echo "Fluent Bit started, pid=$FLB_PID"
+
+ # Start the Logger: 5 files = 500000 log lines in total
+ python logger_file.py -l 100000 -s 256 -b 100 -d 0.1 \
+ -f $TEST_DIR/a.log \
+ -f $TEST_DIR/b.log \
+ -f $TEST_DIR/c.log \
+ -f $TEST_DIR/d.log \
+ -f $TEST_DIR/e.log
+
+ echo "Logger finished...wait 10 seconds"
+ sleep 10
+
+ # Count number of processed lines
+ write_lines=`cat $TEST_DIR/[abcdefghij].log* | wc -l`
+ read_lines=`cat $TEST_DIR/[abcdefghij] | wc -l`
+
+ echo "> write lines: $write_lines"
+ echo "> read lines : $read_lines"
+
+ # Check we processed same number of records
+ ${_ASSERT_EQUALS_} $write_lines $read_lines
+
+ # Validate our database files has only one remaining entry per database file
+ for logfile in a b c d e; do
+ sqlite_check $TEST_DIR "$logfile.db" $FLB_PID
+ done
+
+ # Stop Fluent Bit (SIGTERM)
+ kill -15 $FLB_PID
+}
+
+# Launch the tests
+. $FLB_RUN_TEST
diff --git a/fluent-bit/tests/runtime_shell/in_tail/test_rotation.sh b/fluent-bit/tests/runtime_shell/in_tail/test_rotation.sh
new file mode 100644
index 00000000..889fa7b0
--- /dev/null
+++ b/fluent-bit/tests/runtime_shell/in_tail/test_rotation.sh
@@ -0,0 +1,542 @@
+#!/bin/sh
+
+# Environment variables
+FLB_BIN=`realpath ../../../build/bin/fluent-bit`
+FLB_RUNTIME_SHELL_PATH=`realpath $(pwd)/../`
+FLB_RUN_TEST=`realpath $FLB_RUNTIME_SHELL_PATH/../lib/shunit2/shunit2`
+
+# Colorize shunit2
+bold=$(tput bold)
+normal=$(tput sgr0)
+SHUNIT_TEST_PREFIX="$bold==========> UNIT TEST: $normal"
+
+# 1. Normal Rotation
+# ------------------
+# Run the logger tool that creates 5 different files, write 100000 messages to each one
+# while rotating at 256KB.
+#
+# This test enable the database backend for Tail so it also helps to validate expected
+# entries into the 'in_tail_files' table.
+#
+# Configuration file used: conf/normal_rotation.conf
+
+test_normal_rotation() {
+ # Helper function to check monitored files
+ sqlite_check()
+ {
+ # Incoming parameters:
+ # $1: temporal directory to store data
+ # $2: database file name
+ # $3: Fluent Bit PID
+ #
+ # This function store the remaining monitored files listed in the database,
+ # we send the output to an .inodes for troubleshooting purposes if required
+ sqlite3 $1/$2 -batch \
+ ".headers off" ".width 20" "SELECT inode FROM in_tail_files" > \
+ $1/$2.inodes
+
+ rows=`cat $1/$2.inodes | wc -l | tr -d -C '[0-9]'`
+ if [ $rows != "1" ]; then
+ echo "> database file $1/$2 contains $rows rows, inodes:"
+ cat $1/$2.inodes
+ echo "> open files"
+ ls -l /proc/$3/fd/ | grep \\.log
+ else
+ echo "> database file $1/$2 is OK"
+ fi
+ ${_ASSERT_EQUALS_} "1" $rows
+ }
+
+ # Prepare test directory
+ export TEST_DIR=tmp_test
+ rm -rf $TEST_DIR
+ mkdir $TEST_DIR
+
+ # Create empty files so Fluent Bit will enqueue them on start
+ for logfile in a b c d e ; do
+ touch $TEST_DIR/$logfile.log
+ done
+
+ # Run Fluent Bit
+ $FLB_BIN -c conf/normal_rotation.conf &
+ FLB_PID=$!
+ echo "Fluent Bit started, pid=$FLB_PID"
+
+ # Start the Logger: 5 files = 500000 log lines in total
+ python logger_file.py -l 100000 -s 256 -b 100 -d 0.1 \
+ -f $TEST_DIR/a.log \
+ -f $TEST_DIR/b.log \
+ -f $TEST_DIR/c.log \
+ -f $TEST_DIR/d.log \
+ -f $TEST_DIR/e.log
+
+ echo "Logger finished...wait 10 seconds"
+ sleep 10
+
+ # Count number of processed lines
+ write_lines=`cat $TEST_DIR/[abcdefghij].log* | wc -l`
+ read_lines=`cat $TEST_DIR/[abcdefghij] | wc -l`
+
+ echo "> write lines: $write_lines"
+ echo "> read lines : $read_lines"
+
+ # Check we processed same number of records
+ ${_ASSERT_EQUALS_} $write_lines $read_lines
+
+ # Validate our database files has only one remaining entry per database file
+ for logfile in a b c d e; do
+ sqlite_check $TEST_DIR "$logfile.db" $FLB_PID
+ done
+
+ # Stop Fluent Bit (SIGTERM)
+ kill -15 $FLB_PID
+}
+
+# 2. Single Static Rotation (static process mode + rotation)
+# ----------------------------------------------------------
+# Run the logger tool that creates 1 big file and let Fluent Bit process it in
+# the static mode, before to promote it to 'events' and it gets rotated.
+#
+# Configuration file used: conf/single_static_rotation.conf
+
+test_single_static_rotation() {
+ # Write a log file of 200000 lines
+
+ # Prepare test directory
+ export TEST_DIR=tmp_test
+ rm -rf $TEST_DIR
+ mkdir $TEST_DIR
+
+ # Create empty files so Fluent Bit will enqueue them on start
+ touch $TEST_DIR/a.log
+
+ # Start the Logger: 1 file with 400000 lines, we use a big size (-s) to
+ # avoid rotation
+ python logger_file.py -l 400000 -s 200000 -b 100 -d 0 \
+ -f $TEST_DIR/a.log
+ lines=`cat $TEST_DIR/a.log | wc -l`
+ echo "Logger done, written lines "$lines
+
+ # Run Fluent Bit
+ $FLB_BIN -c conf/single_static_rotation.conf &
+ FLB_PID=$!
+ echo "Fluent Bit started, pid=$FLB_PID"
+
+ # Wait 3 seconds before rotation
+ sleep 2
+ mv $TEST_DIR/a.log $TEST_DIR/a.log.1
+
+ lines=`cat $TEST_DIR/a | wc -l`
+ echo "file Rotated, mid-check: processed lines $lines"
+ sleep 30
+
+ # Count number of processed lines
+ write_lines=`cat $TEST_DIR/a.log.1 | wc -l`
+ read_lines=`cat $TEST_DIR/a | wc -l`
+
+ echo "> write lines: $write_lines"
+ echo "> read lines : $read_lines"
+
+ # Check we processed same number of records
+ ${_ASSERT_EQUALS_} $write_lines $read_lines
+
+ # Validate our database files has only one remaining entry per database file
+ #sqlite_check $TEST_DIR "$logfile.db" $FLB_PID
+
+ # Stop Fluent Bit (SIGTERM)
+ kill -15 $FLB_PID
+}
+
+# 3. Truncate
+# -----------
+# Some environments still rely on truncation mode or well known as copytruncate,
+# this is the definition by logrotate(8):
+#
+# "Truncate the original log file to zero size in place after creating a copy,
+# instead of moving the old log file and optionally creating a new one. It
+# can be used when some program cannot be told to close its logfile and
+# thus might continue writing (appending) to the previous log file forever.
+#
+# Note that there is a very small time slice between copying the file and
+# truncating it, so some logging data might be lost. When this option is
+# used, the create option will have no effect, as the old log file stays in
+# place."
+#
+# This test checks that after a truncation the new lines added are properly
+# processed.
+#
+# Configuration file used: conf/truncate_rotation.conf
+
+test_truncate() {
+ # Helper function to check monitored files
+ sqlite_check()
+ {
+ # Incoming parameters:
+ # $1: temporal directory to store data
+ # $2: database file name
+ # $3: Fluent Bit PID
+ #
+ # This function store the remaining monitored files listed in the database,
+ # we send the output to an .inodes for troubleshooting purposes if required
+
+ # Get the last size of the 'a.log' file and check we have the same value
+ # in the database
+ offset=`wc -c < $TEST_DIR/a.log`
+
+ sqlite3 $1/$2 -batch \
+ ".headers off" "SELECT inode FROM in_tail_files WHERE offset=$offset" > \
+ $1/$2.offset
+
+ rows=`cat $1/$2.offset | wc -l | tr -d -C '[0-9]'`
+ if [ $rows != "1" ]; then
+ echo "> invalid database content:"
+ cat $1/$2.offset
+ echo "> open files"
+ ls -l /proc/$3/fd/ | grep \\.log
+ else
+ echo "> database file $1/$2 is OK"
+ fi
+ ${_ASSERT_EQUALS_} "1" $rows
+ }
+
+ # Prepare test directory
+ export TEST_DIR=tmp_test
+ rm -rf $TEST_DIR
+ mkdir $TEST_DIR
+
+ # Create empty files so Fluent Bit will enqueue them on start
+ touch $TEST_DIR/a.log
+
+ # Start the Logger: 1 file with 200 lines, we use a big size limit (-s) to
+ # avoid rotation
+ python logger_file.py -l 200 -s 200000 -b 100 -d 0 -f $TEST_DIR/a.log
+ lines=`cat $TEST_DIR/a.log | wc -l`
+ echo "Logger done, written lines "$lines
+
+ # Run Fluent Bit
+ $FLB_BIN -c conf/truncate_rotation.conf &
+ FLB_PID=$!
+ echo "Fluent Bit started, pid=$FLB_PID"
+
+ # Wait 2 seconds before truncation
+ sleep 2
+ pre_lines=`cat $TEST_DIR/a.log | wc -l`
+ truncate -s 0 $TEST_DIR/a.log
+
+ lines=`cat $TEST_DIR/a | wc -l`
+ echo "file truncated, mid-check: processed lines $lines"
+
+ # Append 100 more lines
+ python logger_file.py -l 100 -s 200000 -b 100 -d 0 -f $TEST_DIR/a.log
+
+ sleep 3
+
+ # Count number of processed lines
+ write_lines=300
+ read_lines=`cat $TEST_DIR/a | wc -l`
+
+ echo "> write lines: $write_lines"
+ echo "> read lines : $read_lines"
+
+ # Check we processed same number of records
+ ${_ASSERT_EQUALS_} $write_lines $read_lines
+
+ sqlite_check $TEST_DIR a.db $FLB_PID
+
+ # Stop Fluent Bit (SIGTERM)
+ kill -15 $FLB_PID
+}
+
+# 4. Rotate Link
+# --------------
+# This case checks that a monitored link, upon rotation, keeps the proper offset
+# and database status for the real file.
+#
+# Example:
+#
+# - file with data: data.log
+# - monitored link: test.log
+#
+# Check the behavior upon test.log -> test.log.1 behavior
+#
+# Configuration file used: conf/rotate_link.conf
+
+test_rotate_link() {
+ # Helper function to check monitored files
+ sqlite_check()
+ {
+ # Incoming parameters:
+ # $1: temporal directory to store data
+ # $2: database file name
+ # $3: Fluent Bit PID
+ #
+ # This function store the remaining monitored files listed in the database,
+ # we send the output to an .inodes for troubleshooting purposes if required
+
+ # Get the last size of the file pointed by 'a.log.1' and check we have the
+ # same value in the database
+ offset=`wc -c < $TEST_DIR/a.log.1`
+
+ sqlite3 $1/$2 -batch \
+ ".headers off" "SELECT inode FROM in_tail_files WHERE offset=$offset \
+ AND rotated=1" > $1/$2.offset
+
+ rows=`cat $1/$2.offset | wc -l | tr -d -C '[0-9]'`
+ if [ $rows != "1" ]; then
+ echo "> invalid database content:"
+ cat $1/$2.offset
+ echo "> open files"
+ ls -l /proc/$3/fd/ | grep \\.log
+ else
+ echo "> offset database check $1/$2 is OK"
+ fi
+ ${_ASSERT_EQUALS_} "1" $rows
+
+ # After rotate_wait (5 secs + watcher) we expect an empty database
+ sleep 6
+ sqlite3 $1/$2 -batch \
+ ".headers off" "SELECT inode FROM in_tail_files WHERE offset=$offset \
+ AND rotated=1" > $1/$2.offset
+
+ rows=`cat $1/$2.offset | wc -l | tr -d -C '[0-9]'`
+ if [ $rows != "0" ]; then
+ echo "> invalid database content:"
+ cat $1/$2.offset
+ echo "> open files"
+ ls -l /proc/$3/fd/ | grep \\.log
+ else
+ echo "> empty database check $1/$2 is OK"
+ fi
+ ${_ASSERT_EQUALS_} "0" $rows
+ }
+
+ # Prepare test directory
+ export TEST_DIR=tmp_test
+ rm -rf $TEST_DIR
+ mkdir $TEST_DIR
+
+ # Create empty files so Fluent Bit will enqueue them on start
+ touch $TEST_DIR/data.log
+
+ # Start the Logger: 1 file with 100 lines, we use a big size limit (-s) to
+ # avoid rotation
+ python logger_file.py -l 100 -s 200000 -b 100 -d 0 -f $TEST_DIR/data.log
+ lines=`cat $TEST_DIR/data.log | wc -l`
+ ln -s data.log $TEST_DIR/a.log
+ echo "Logger done, written lines "$lines
+
+ # Run Fluent Bit
+ $FLB_BIN -c conf/rotate_link.conf &
+ FLB_PID=$!
+ echo "Fluent Bit started, pid=$FLB_PID"
+
+ # Wait 2 seconds and rotate file
+ sleep 2
+ pre_lines=`cat $TEST_DIR/a.log | wc -l`
+ mv $TEST_DIR/a.log $TEST_DIR/a.log.1
+
+ lines=`cat $TEST_DIR/a | wc -l`
+ echo "file rotated, mid-check: processed lines $lines"
+
+ # Append 200 more lines to the rotated link
+ python logger_file.py -l 200 -s 200000 -b 100 -d 0 -f $TEST_DIR/a.log.1
+
+ # Count number of processed lines
+ sleep 3
+ write_lines=300
+ read_lines=`cat $TEST_DIR/a | wc -l`
+
+ echo "> write lines: $write_lines"
+ echo "> read lines : $read_lines"
+
+ # Check we processed same number of records
+ ${_ASSERT_EQUALS_} $write_lines $read_lines
+
+ # Check that database file have the right offset and mark the file as rotated
+ sqlite_check $TEST_DIR a.db $FLB_PID
+
+ # Stop Fluent Bit (SIGTERM)
+ kill -15 $FLB_PID
+}
+
+# 5. Truncate Link
+#
+# Test a link that gets a truncation and Fluent Bit properly use the new offset
+#
+# Configuration file used: conf/truncate_link.conf
+
+test_truncate_link() {
+ # Helper function to check monitored files
+ sqlite_check()
+ {
+ # Incoming parameters:
+ # $1: temporal directory to store data
+ # $2: database file name
+ # $3: Fluent Bit PID
+ #
+ # This function store the remaining monitored files listed in the database,
+ # we send the output to an .inodes for troubleshooting purposes if required
+
+ # Get the last size of the 'a.log' file and check we have the same value
+ # in the database
+ offset=`wc -c < $TEST_DIR/a.log`
+
+ sqlite3 $1/$2 -batch \
+ ".headers off" "SELECT inode FROM in_tail_files WHERE offset=$offset" > \
+ $1/$2.offset
+
+ rows=`cat $1/$2.offset | wc -l | tr -d -C '[0-9]'`
+ if [ $rows != "1" ]; then
+ echo "> invalid database content:"
+ cat $1/$2.offset
+ echo "> open files"
+ ls -l /proc/$3/fd/ | grep \\.log
+ else
+ echo "> database file $1/$2 is OK"
+ fi
+ ${_ASSERT_EQUALS_} "1" $rows
+ }
+
+ # Prepare test directory
+ export TEST_DIR=tmp_test
+ rm -rf $TEST_DIR
+ mkdir $TEST_DIR
+
+ # Create empty files so Fluent Bit will enqueue them on start
+ touch $TEST_DIR/data.log
+
+ # Start the Logger: 1 file with 100 lines, we use a big size limit (-s) to
+ # avoid rotation
+ python logger_file.py -l 100 -s 200000 -b 100 -d 0 -f $TEST_DIR/data.log
+ lines=`cat $TEST_DIR/data.log | wc -l`
+ ln -s data.log $TEST_DIR/a.log
+ echo "Logger done, written lines "$lines
+
+ # Run Fluent Bit
+ $FLB_BIN -c conf/truncate_link.conf &
+ FLB_PID=$!
+ echo "Fluent Bit started, pid=$FLB_PID"
+
+ # Wait 1 second before truncation
+ sleep 1
+ pre_lines=`cat $TEST_DIR/a.log | wc -l`
+ truncate -s 0 $TEST_DIR/a.log
+
+ sleep 2
+ lines=`cat $TEST_DIR/a | wc -l`
+ echo "file truncated, mid-check: processed lines $lines"
+
+ # Append 200 more lines
+ python logger_file.py -l 200 -s 200000 -b 100 -d 0 -f $TEST_DIR/a.log
+
+ sleep 4
+
+ # Count number of processed lines
+ write_lines=300
+ read_lines=`cat $TEST_DIR/a | wc -l`
+
+ echo "> write lines: $write_lines"
+ echo "> read lines : $read_lines"
+
+ # Check we processed same number of records
+ ${_ASSERT_EQUALS_} $write_lines $read_lines
+
+ # Stop Fluent Bit (SIGTERM)
+ kill -15 $FLB_PID
+}
+
+# 6. Database Resume
+#
+# Tests that if Fluent Bit stops, it can resume reading files from the last position
+#
+# Configuration file used: conf/database_resume.conf
+
+test_database_resume() {
+ # Helper function to check monitored files
+ sqlite_check()
+ {
+ # Incoming parameters:
+ # $1: temporal directory to store data
+ # $2: database file name
+ # $3: Fluent Bit PID
+ #
+ # This function store the remaining monitored files listed in the database,
+ # we send the output to an .inodes for troubleshooting purposes if required
+
+ # Get the last size of the 'a.log' file and check we have the same value
+ # in the database
+ offset=`stat -c %s $TEST_DIR/a.log`
+
+ sqlite3 $1/$2 -batch \
+ ".headers off" "SELECT inode FROM in_tail_files WHERE offset=$offset" > \
+ $1/$2.offset
+
+ rows=`cat $1/$2.offset | wc -l`
+ if [ $rows != "1" ]; then
+ echo "> invalid database content:"
+ cat $1/$2.offset
+ echo "> open files"
+ ls -l /proc/$3/fd/ | grep \\.log
+ else
+ echo "> database file $1/$2 is OK"
+ fi
+ ${_ASSERT_EQUALS_} "1" $rows
+ }
+
+ # Prepare test directory
+ export TEST_DIR=tmp_test
+ rm -rf $TEST_DIR
+ mkdir $TEST_DIR
+
+ # Start the Logger: 5 files with 10000 lines each, we use a big size limit (-s) to
+ # avoid rotation
+ python logger_file.py -l 10000 -s 200000 -b 100 -d 0 \
+ -f $TEST_DIR/a.log \
+ -f $TEST_DIR/b.log \
+ -f $TEST_DIR/c.log \
+ -f $TEST_DIR/d.log \
+ -f $TEST_DIR/e.log
+
+ lines=`cat $TEST_DIR/[abcde].log | wc -l`
+ echo "Logger done, written lines "$lines
+
+ # Run Fluent Bit
+ $FLB_BIN -c conf/database_resume.conf &
+ FLB_PID=$!
+ echo "Fluent Bit started, pid=$FLB_PID"
+
+ # Wait 1 second before stop Fluent Bit
+ sleep 1
+
+ # Stop Fluent Bit (SIGTERM)
+ kill -15 $FLB_PID
+
+ sqlite3 $TEST_DIR/logs.db "SELECT * FROM in_tail_files"
+ exit 0
+
+ sleep 2
+ lines=`cat $TEST_DIR/a | wc -l`
+ echo "file truncated, mid-check: processed lines $lines"
+
+ # Append 200 more lines
+ python logger_file.py -l 200 -s 200000 -b 100 -d 0 -f $TEST_DIR/a.log
+
+ sleep 4
+
+ # Count number of processed lines
+ write_lines=300
+ read_lines=`cat $TEST_DIR/a | wc -l`
+
+ echo "> write lines: $write_lines"
+ echo "> read lines : $read_lines"
+
+ # Check we processed same number of records
+ ${_ASSERT_EQUALS_} $write_lines $read_lines
+
+ # Stop Fluent Bit (SIGTERM)
+ kill -15 $FLB_PID
+}
+
+
+# Launch the tests
+. $FLB_RUN_TEST
diff --git a/fluent-bit/tests/runtime_shell/in_tail_expect.sh b/fluent-bit/tests/runtime_shell/in_tail_expect.sh
new file mode 100755
index 00000000..c847d65f
--- /dev/null
+++ b/fluent-bit/tests/runtime_shell/in_tail_expect.sh
@@ -0,0 +1,16 @@
+#!/bin/sh
+
+test_in_tail_filter_expect() {
+ rm -rf /tmp/flb_*
+
+ # Monitor this file
+ echo "{\"key\": \"val\"}" > /tmp/flb_tail_expect_1.log
+
+ # Excluded file
+ echo "{\"nokey\": \"\"}" > /tmp/flb_tail_expect_2.log
+
+ $FLB_BIN -c $FLB_RUNTIME_SHELL_CONF/in_tail_expect.conf
+}
+
+# The following command launch the unit test
+. $FLB_RUNTIME_SHELL_PATH/runtime_shell.env
diff --git a/fluent-bit/tests/runtime_shell/runtime_shell.env.in b/fluent-bit/tests/runtime_shell/runtime_shell.env.in
new file mode 100644
index 00000000..6a40c513
--- /dev/null
+++ b/fluent-bit/tests/runtime_shell/runtime_shell.env.in
@@ -0,0 +1,5 @@
+flb_runtime_shell_test_run() {
+ . $FLB_RUNTIME_SHELL_PATH/../lib/shunit2/shunit2
+}
+
+flb_runtime_shell_test_run
diff --git a/fluent-bit/tests/runtime_shell/tls/certificate.pem b/fluent-bit/tests/runtime_shell/tls/certificate.pem
new file mode 100644
index 00000000..b7971987
--- /dev/null
+++ b/fluent-bit/tests/runtime_shell/tls/certificate.pem
@@ -0,0 +1,22 @@
+-----BEGIN CERTIFICATE-----
+MIIDlzCCAn+gAwIBAgIUCl24aoQh3HuyObboivsozKKOb3cwDQYJKoZIhvcNAQEL
+BQAwWzELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoM
+GEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDEUMBIGA1UEAwwLbGVvLnZjYXAubWUw
+HhcNMjIwODE3MTM0OTAyWhcNMjMwODE3MTM0OTAyWjBbMQswCQYDVQQGEwJBVTET
+MBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0cyBQ
+dHkgTHRkMRQwEgYDVQQDDAtsZW8udmNhcC5tZTCCASIwDQYJKoZIhvcNAQEBBQAD
+ggEPADCCAQoCggEBAJ2cbLtGhSeGPAXKitw+PN7U8CkaSGL7BssPFSuqtuVkJ1AH
+puluHq62FHm4UgTyJB/Iundf1kBxiOehGFr9E1fGp6kUety6iBZK3mmmX0NdXY2A
+ObqfvxTgPNVPEj+5Qp0CJW6fEBRhHrFgpf+OdKzoKnjSCbl1EyJ1wFZeSNDTx98I
+h951xEOCobcXj1qXHDd0snRW5QNKTdSnj+3amqrNqV5DrkCyyb5Rq/VQX9LIyGV8
+tOyq50mUKeK+vOu/oNwfkZqgZ5wlY/AUyPMNvd8UKitnWeQMiMmr3ImQRZLkFnoE
+2mi2Xr6ZQhmsweWbTmmUYxr+ej7L5WvBVQEkbd0CAwEAAaNTMFEwHQYDVR0OBBYE
+FGliHqbvEsRN36C2w1cCq5M/gGCRMB8GA1UdIwQYMBaAFGliHqbvEsRN36C2w1cC
+q5M/gGCRMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBACWCNohz
+YbnnDzGXMhGlfV5PywH6+gguqTrtzJlfJB/FGHDMybBvEp9gda5ssPZfPdv6aAvo
+hz2XC+uwJqYn76ZcX+LOixsqhoXJ367pT0ggMSUB/UNvGkzMFWuGerxcB5Q7xvsh
+0Ui37Ojok7Ozj9Ofk/zroJB1WG3kPDtwoo+GwNmJaDtXhOutRPxCqp5Dw+dC0N0c
+TlPCBRB17hx49Zjw+h9sv1VmOUtQcplNZgRL9lguv7eXB6y0sSdpDyDLlTBbRMGl
+uVnQ20uKnqqALRfpXDWFL8g01xPNEIfV3KctpJfu1inLWtPuklerl0yeyUh9M1f0
+zCAi0URsGEHMFA0=
+-----END CERTIFICATE-----
diff --git a/fluent-bit/tests/runtime_shell/tls/private_key.pem b/fluent-bit/tests/runtime_shell/tls/private_key.pem
new file mode 100644
index 00000000..b6a70539
--- /dev/null
+++ b/fluent-bit/tests/runtime_shell/tls/private_key.pem
@@ -0,0 +1,28 @@
+-----BEGIN PRIVATE KEY-----
+MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCdnGy7RoUnhjwF
+yorcPjze1PApGkhi+wbLDxUrqrblZCdQB6bpbh6uthR5uFIE8iQfyLp3X9ZAcYjn
+oRha/RNXxqepFHrcuogWSt5ppl9DXV2NgDm6n78U4DzVTxI/uUKdAiVunxAUYR6x
+YKX/jnSs6Cp40gm5dRMidcBWXkjQ08ffCIfedcRDgqG3F49alxw3dLJ0VuUDSk3U
+p4/t2pqqzaleQ65Assm+Uav1UF/SyMhlfLTsqudJlCnivrzrv6DcH5GaoGecJWPw
+FMjzDb3fFCorZ1nkDIjJq9yJkEWS5BZ6BNpotl6+mUIZrMHlm05plGMa/no+y+Vr
+wVUBJG3dAgMBAAECggEANXQ/AEkLkfsZ0lD+RXIqTNzlUttiH4fJpwbHhFbSzvvn
+xWHC/zpk15ZTXXDhCGJjVBBNBX2QeazH5N8jFoDslYF/jX2vqbrturnLswNFHeDF
+gN8zNRNGyDrBBwtZQhl/+SYoMdtqpa7GrRv9UK4s7hOTjASYXbjSM4bCI8i4Y3JX
+5iKYnejTJ4GNXm1S5g4H8QpgXxrxYU7HY1UlMBGni1BSDPb5SxANnFvgw0oypaDG
+4a0KpnUmziBtZo8wIQ7+UeStS4xikIeaT2SQM/ilplPNx0HgPHbTMeTBHQylG1JU
+ztEE04Y1F9UF7cqZnxqQLlKmIqsOepLeDOkRXjDtNQKBgQDQtWYbewCmGuo1+AR7
+wStOXpfhqdu6NxAwX+S06ZdAV7f4duMFb//6uJ/c997qwRlNvRvpPlJBdcT3qziS
+zjvR8uk2YN5N9D+ZN0Yi11vltUYVPeGpoAk4L7+Ruj5RTVqk5iZEZ1RbAv5cRIm0
+D24H4W0Cj/aldDED2pwTb9ukEwKBgQDBUv/s7gUNM3LCkNLc1EDg2adSzoUopxHU
+jIQCitXt5iFpY2Lv2tpaIz4ux4HQGwhmao2MiUYOMit0++/pFaKur66tgRQRsr7F
+uS1fpjqtS51/5uFuc1xcWjVquidZXH9u9iChrYvim6SBkxzFsbu4kXNOj4I562cm
+E2VWH5GETwKBgQCo5ePX4VbJFYbsXeXi8JRHO63V5Uv4Co+DVlcTQOYyH8q1vCBE
+Sjrxf29/tugjOllr29o2i0StzMy1UU7bHyKx6M5qP0In+71sFJshnv6ziltI3Wc9
+ilFrstho6jt8OAle4RGe0bAmZunJaX22xbXZkshRBognpTv1Tnh4ElHBGQKBgHIW
+nVohjXGg7xTLiuUvjaokSI6hugunrOoWksE9VcqziPw83uJV8Y5IRiYtLvq1OVvX
+ffl1+ZXfHa5ID+kqD3uvyhIynrljFxpwkcpkuzQR77zPcDJSeis2QVfey+H8qGe/
+cLp5RJhS6d5eBxjULshZbgbqwhuURKc/wwn0T1gZAoGAXu2wqwBE8ikhpaDVFYZR
+EZe0G1iq03CtabFL0oPiddytV9jJy4Mg457tkCj58uCVkxw/8qjXEnm5OoJAzSlJ
+Goc4wh1N1rngYRbSGG+LxD0DeI/Hj3T5tWezMBLCE7J+hieUuVjOB9ELKip7aTj3
+KLNVudOATWYGXyvzux6ciu0=
+-----END PRIVATE KEY-----