bundle-uri: unit test "key=value" parsing

Create a new 'test-tool bundle-uri' test helper. This helper will assist in testing logic deep in the bundle URI feature. This change introduces the 'parse-key-values' subcommand, which parses an input file as a list of lines. These are fed into bundle_uri_parse_line() to test how we construct a 'struct bundle_list' from that data. The list is then output to stdout as if the key-value pairs were a Git config file. We use an input file instead of stdin because of a future change to parse in config-file format that works better as an input file. Co-authored-by: Derrick Stolee <derrickstolee@github.com> Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com> Signed-off-by: Derrick Stolee <derrickstolee@github.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Ævar Arnfjörð Bjarmason committed Oct 12, 2022 at 12:52 UTC d796cedbe8ca77310a7dabcafe60b040aa0e2b67
8 files changed +241
Makefile
+1
@@ -722,6 +722,7 @@ PROGRAMS += $(patsubst %.o,git-%$X,$(PROGRAM_OBJS))
722 TEST_BUILTINS_OBJS += test-advise.o
723 TEST_BUILTINS_OBJS += test-bitmap.o
724 TEST_BUILTINS_OBJS += test-bloom.o
725 +TEST_BUILTINS_OBJS += test-bundle-uri.o
726 TEST_BUILTINS_OBJS += test-chmtime.o
727 TEST_BUILTINS_OBJS += test-config.o
728 TEST_BUILTINS_OBJS += test-crontab.o
bundle-uri.c
+33
@@ -66,6 +66,39 @@ int for_all_bundles_in_list(struct bundle_list *list,
66 return 0;
67 }
68
69 +static int summarize_bundle(struct remote_bundle_info *info, void *data)
70 +{
71 + FILE *fp = data;
72 + fprintf(fp, "[bundle \"%s\"]\n", info->id);
73 + fprintf(fp, "\turi = %s\n", info->uri);
74 + return 0;
75 +}
76 +
77 +void print_bundle_list(FILE *fp, struct bundle_list *list)
78 +{
79 + const char *mode;
80 +
81 + switch (list->mode) {
82 + case BUNDLE_MODE_ALL:
83 + mode = "all";
84 + break;
85 +
86 + case BUNDLE_MODE_ANY:
87 + mode = "any";
88 + break;
89 +
90 + case BUNDLE_MODE_NONE:
91 + default:
92 + mode = "<unknown>";
93 + }
94 +
95 + fprintf(fp, "[bundle]\n");
96 + fprintf(fp, "\tversion = %d\n", list->version);
97 + fprintf(fp, "\tmode = %s\n", mode);
98 +
99 + for_all_bundles_in_list(list, summarize_bundle, fp);
100 +}
101 +
102 /**
103 * Given a key-value pair, update the state of the given bundle list.
104 * Returns 0 if the key-value pair is understood. Returns -1 if the key
bundle-uri.h
+3
@@ -59,6 +59,9 @@ int for_all_bundles_in_list(struct bundle_list *list,
59 bundle_iterator iter,
60 void *data);
61
62 +struct FILE;
63 +void print_bundle_list(FILE *fp, struct bundle_list *list);
64 +
65 /**
66 * Fetch data from the given 'uri' and unbundle the bundle data found
67 * based on that information.
t/helper/test-bundle-uri.c new
+70
@@ -0,0 +1,70 @@
1 +#include "test-tool.h"
2 +#include "parse-options.h"
3 +#include "bundle-uri.h"
4 +#include "strbuf.h"
5 +#include "string-list.h"
6 +
7 +static int cmd__bundle_uri_parse(int argc, const char **argv)
8 +{
9 + const char *key_value_usage[] = {
10 + "test-tool bundle-uri parse-key-values <input>",
11 + NULL
12 + };
13 + const char **usage = key_value_usage;
14 + struct option options[] = {
15 + OPT_END(),
16 + };
17 + struct strbuf sb = STRBUF_INIT;
18 + struct bundle_list list;
19 + int err = 0;
20 + FILE *fp;
21 +
22 + argc = parse_options(argc, argv, NULL, options, usage, 0);
23 + if (argc != 1)
24 + goto usage;
25 +
26 + init_bundle_list(&list);
27 + fp = fopen(argv[0], "r");
28 + if (!fp)
29 + die("failed to open '%s'", argv[0]);
30 +
31 + while (strbuf_getline(&sb, fp) != EOF) {
32 + if (bundle_uri_parse_line(&list, sb.buf))
33 + err = error("bad line: '%s'", sb.buf);
34 + }
35 + strbuf_release(&sb);
36 + fclose(fp);
37 +
38 + print_bundle_list(stdout, &list);
39 +
40 + clear_bundle_list(&list);
41 +
42 + return !!err;
43 +
44 +usage:
45 + usage_with_options(usage, options);
46 +}
47 +
48 +int cmd__bundle_uri(int argc, const char **argv)
49 +{
50 + const char *usage[] = {
51 + "test-tool bundle-uri <subcommand> [<options>]",
52 + NULL
53 + };
54 + struct option options[] = {
55 + OPT_END(),
56 + };
57 +
58 + argc = parse_options(argc, argv, NULL, options, usage,
59 + PARSE_OPT_STOP_AT_NON_OPTION |
60 + PARSE_OPT_KEEP_ARGV0);
61 + if (argc == 1)
62 + goto usage;
63 +
64 + if (!strcmp(argv[1], "parse-key-values"))
65 + return cmd__bundle_uri_parse(argc - 1, argv + 1);
66 + error("there is no test-tool bundle-uri tool '%s'", argv[1]);
67 +
68 +usage:
69 + usage_with_options(usage, options);
70 +}
t/helper/test-tool.c
+1
@@ -17,6 +17,7 @@ static struct test_cmd cmds[] = {
17 { "advise", cmd__advise_if_enabled },
18 { "bitmap", cmd__bitmap },
19 { "bloom", cmd__bloom },
20 + { "bundle-uri", cmd__bundle_uri },
21 { "chmtime", cmd__chmtime },
22 { "config", cmd__config },
23 { "crontab", cmd__crontab },
t/helper/test-tool.h
+1
@@ -7,6 +7,7 @@
7 int cmd__advise_if_enabled(int argc, const char **argv);
8 int cmd__bitmap(int argc, const char **argv);
9 int cmd__bloom(int argc, const char **argv);
10 +int cmd__bundle_uri(int argc, const char **argv);
11 int cmd__chmtime(int argc, const char **argv);
12 int cmd__config(int argc, const char **argv);
13 int cmd__crontab(int argc, const char **argv);
t/t5750-bundle-uri-parse.sh new
+121
@@ -0,0 +1,121 @@
1 +#!/bin/sh
2 +
3 +test_description="Test bundle-uri bundle_uri_parse_line()"
4 +
5 +TEST_NO_CREATE_REPO=1
6 +TEST_PASSES_SANITIZE_LEAK=true
7 +. ./test-lib.sh
8 +
9 +test_expect_success 'bundle_uri_parse_line() just URIs' '
10 + cat >in <<-\EOF &&
11 + bundle.one.uri=http://example.com/bundle.bdl
12 + bundle.two.uri=https://example.com/bundle.bdl
13 + bundle.three.uri=file:///usr/share/git/bundle.bdl
14 + EOF
15 +
16 + cat >expect <<-\EOF &&
17 + [bundle]
18 + version = 1
19 + mode = all
20 + [bundle "one"]
21 + uri = http://example.com/bundle.bdl
22 + [bundle "two"]
23 + uri = https://example.com/bundle.bdl
24 + [bundle "three"]
25 + uri = file:///usr/share/git/bundle.bdl
26 + EOF
27 +
28 + test-tool bundle-uri parse-key-values in >actual 2>err &&
29 + test_must_be_empty err &&
30 + test_cmp_config_output expect actual
31 +'
32 +
33 +test_expect_success 'bundle_uri_parse_line() parsing edge cases: empty key or value' '
34 + cat >in <<-\EOF &&
35 + =bogus-value
36 + bogus-key=
37 + EOF
38 +
39 + cat >err.expect <<-EOF &&
40 + error: bundle-uri: line has empty key or value
41 + error: bad line: '\''=bogus-value'\''
42 + error: bundle-uri: line has empty key or value
43 + error: bad line: '\''bogus-key='\''
44 + EOF
45 +
46 + cat >expect <<-\EOF &&
47 + [bundle]
48 + version = 1
49 + mode = all
50 + EOF
51 +
52 + test_must_fail test-tool bundle-uri parse-key-values in >actual 2>err &&
53 + test_cmp err.expect err &&
54 + test_cmp_config_output expect actual
55 +'
56 +
57 +test_expect_success 'bundle_uri_parse_line() parsing edge cases: empty lines' '
58 + cat >in <<-\EOF &&
59 + bundle.one.uri=http://example.com/bundle.bdl
60 +
61 + bundle.two.uri=https://example.com/bundle.bdl
62 +
63 + bundle.three.uri=file:///usr/share/git/bundle.bdl
64 + EOF
65 +
66 + cat >err.expect <<-\EOF &&
67 + error: bundle-uri: got an empty line
68 + error: bad line: '\'''\''
69 + error: bundle-uri: got an empty line
70 + error: bad line: '\'''\''
71 + EOF
72 +
73 + # We fail, but try to continue parsing regardless
74 + cat >expect <<-\EOF &&
75 + [bundle]
76 + version = 1
77 + mode = all
78 + [bundle "one"]
79 + uri = http://example.com/bundle.bdl
80 + [bundle "two"]
81 + uri = https://example.com/bundle.bdl
82 + [bundle "three"]
83 + uri = file:///usr/share/git/bundle.bdl
84 + EOF
85 +
86 + test_must_fail test-tool bundle-uri parse-key-values in >actual 2>err &&
87 + test_cmp err.expect err &&
88 + test_cmp_config_output expect actual
89 +'
90 +
91 +test_expect_success 'bundle_uri_parse_line() parsing edge cases: duplicate lines' '
92 + cat >in <<-\EOF &&
93 + bundle.one.uri=http://example.com/bundle.bdl
94 + bundle.two.uri=https://example.com/bundle.bdl
95 + bundle.one.uri=https://example.com/bundle-2.bdl
96 + bundle.three.uri=file:///usr/share/git/bundle.bdl
97 + EOF
98 +
99 + cat >err.expect <<-\EOF &&
100 + error: bad line: '\''bundle.one.uri=https://example.com/bundle-2.bdl'\''
101 + EOF
102 +
103 + # We fail, but try to continue parsing regardless
104 + cat >expect <<-\EOF &&
105 + [bundle]
106 + version = 1
107 + mode = all
108 + [bundle "one"]
109 + uri = http://example.com/bundle.bdl
110 + [bundle "two"]
111 + uri = https://example.com/bundle.bdl
112 + [bundle "three"]
113 + uri = file:///usr/share/git/bundle.bdl
114 + EOF
115 +
116 + test_must_fail test-tool bundle-uri parse-key-values in >actual 2>err &&
117 + test_cmp err.expect err &&
118 + test_cmp_config_output expect actual
119 +'
120 +
121 +test_done
t/test-lib-functions.sh
+11
@@ -1868,3 +1868,14 @@ test_is_magic_mtime () {
1868 rm -f .git/test-mtime-actual
1869 return $ret
1870 }
1871 +
1872 +# Given two filenames, parse both using 'git config --list --file'
1873 +# and compare the sorted output of those commands. Useful when
1874 +# wanting to ignore whitespace differences and sorting concerns.
1875 +test_cmp_config_output () {
1876 + git config --list --file="$1" >config-expect &&
1877 + git config --list --file="$2" >config-actual &&
1878 + sort config-expect >sorted-expect &&
1879 + sort config-actual >sorted-actual &&
1880 + test_cmp sorted-expect sorted-actual
1881 +}