#include "test-lib.h" #include "trailer.h" struct contents { const char *raw; const char *key; const char *val; }; static void t_trailer_iterator(const char *msg, size_t num_expected, struct contents *contents) { struct trailer_iterator iter; size_t i = 0; trailer_iterator_init(&iter, msg); while (trailer_iterator_advance(&iter)) { if (num_expected) { check_str(iter.raw, contents[i].raw); check_str(iter.key.buf, contents[i].key); check_str(iter.val.buf, contents[i].val); } i++; } trailer_iterator_release(&iter); check_uint(i, ==, num_expected); } static void run_t_trailer_iterator(void) { static struct test_cases { const char *name; const char *msg; size_t num_expected; struct contents contents[10]; } tc[] = { { "empty input", "", 0, {{0}}, }, { "no newline at beginning", "Fixes: x\n" "Acked-by: x\n" "Reviewed-by: x\n", 0, {{0}}, }, { "newline at beginning", "\n" "Fixes: x\n" "Acked-by: x\n" "Reviewed-by: x\n", 3, { { .raw = "Fixes: x\n", .key = "Fixes", .val = "x", }, { .raw = "Acked-by: x\n", .key = "Acked-by", .val = "x", }, { .raw = "Reviewed-by: x\n", .key = "Reviewed-by", .val = "x", }, { 0 }, }, }, { "without body text", "subject: foo bar\n" "\n" "Fixes: x\n" "Acked-by: x\n" "Reviewed-by: x\n", 3, { { .raw = "Fixes: x\n", .key = "Fixes", .val = "x", }, { .raw = "Acked-by: x\n", .key = "Acked-by", .val = "x", }, { .raw = "Reviewed-by: x\n", .key = "Reviewed-by", .val = "x", }, { 0 }, }, }, { "with body text, without divider", "my subject\n" "\n" "my body which is long\n" "and contains some special\n" "chars like : = ? !\n" "hello\n" "\n" "Fixes: x\n" "Acked-by: x\n" "Reviewed-by: x\n" "Signed-off-by: x\n", 4, { { .raw = "Fixes: x\n", .key = "Fixes", .val = "x", }, { .raw = "Acked-by: x\n", .key = "Acked-by", .val = "x", }, { .raw = "Reviewed-by: x\n", .key = "Reviewed-by", .val = "x", }, { .raw = "Signed-off-by: x\n", .key = "Signed-off-by", .val = "x", }, { 0 }, }, }, { "with body text, without divider (second trailer block)", "my subject\n" "\n" "my body which is long\n" "and contains some special\n" "chars like : = ? !\n" "hello\n" "\n" "Fixes: x\n" "Acked-by: x\n" "Reviewed-by: x\n" "Signed-off-by: x\n" "\n" /* * Because this is the last trailer block, it takes * precedence over the first one encountered above. */ "Helped-by: x\n" "Signed-off-by: x\n", 2, { { .raw = "Helped-by: x\n", .key = "Helped-by", .val = "x", }, { .raw = "Signed-off-by: x\n", .key = "Signed-off-by", .val = "x", }, { 0 }, }, }, { "with body text, with divider", "my subject\n" "\n" "my body which is long\n" "and contains some special\n" "chars like : = ? !\n" "hello\n" "\n" "---\n" "\n" /* * This trailer still counts because the iterator * always ignores the divider. */ "Signed-off-by: x\n", 1, { { .raw = "Signed-off-by: x\n", .key = "Signed-off-by", .val = "x", }, { 0 }, }, }, { "with non-trailer lines in trailer block", "subject: foo bar\n" "\n" /* * Even though this trailer block has a non-trailer line * in it, it's still a valid trailer block because it's * at least 25% trailers and is Git-generated (see * git_generated_prefixes[] in trailer.c). */ "not a trailer line\n" "not a trailer line\n" "not a trailer line\n" "Signed-off-by: x\n", /* * Even though there is only really 1 real "trailer" * (Signed-off-by), we still have 4 trailer objects * because we still want to iterate through the entire * block. */ 4, { { .raw = "not a trailer line\n", .key = "not a trailer line", .val = "", }, { .raw = "not a trailer line\n", .key = "not a trailer line", .val = "", }, { .raw = "not a trailer line\n", .key = "not a trailer line", .val = "", }, { .raw = "Signed-off-by: x\n", .key = "Signed-off-by", .val = "x", }, { 0 }, }, }, { "with non-trailer lines (one too many) in trailer block", "subject: foo bar\n" "\n" /* * This block has only 20% trailers, so it's below the * 25% threshold. */ "not a trailer line\n" "not a trailer line\n" "not a trailer line\n" "not a trailer line\n" "Signed-off-by: x\n", 0, {{0}}, }, { "with non-trailer lines (only 1) in trailer block, but no Git-generated trailers", "subject: foo bar\n" "\n" /* * This block has only 1 non-trailer out of 10 (IOW, 90% * trailers) but is not considered a trailer block * because the 25% threshold only applies to cases where * there was a Git-generated trailer. */ "Reviewed-by: x\n" "Reviewed-by: x\n" "Reviewed-by: x\n" "Helped-by: x\n" "Helped-by: x\n" "Helped-by: x\n" "Acked-by: x\n" "Acked-by: x\n" "Acked-by: x\n" "not a trailer line\n", 0, {{0}}, }, }; for (int i = 0; i < sizeof(tc) / sizeof(tc[0]); i++) { TEST(t_trailer_iterator(tc[i].msg, tc[i].num_expected, tc[i].contents), "%s", tc[i].name); } } int cmd_main(int argc, const char **argv) { run_t_trailer_iterator(); return test_done(); }