Add a helper function to compare file contents
[git.git] / t / helper / test-fsmonitor-client.c
blob54a4856c48c55af0af4e3ffe7cc3a62dce993842
1 /*
2 * test-fsmonitor-client.c: client code to send commands/requests to
3 * a `git fsmonitor--daemon` daemon.
4 */
6 #include "test-tool.h"
7 #include "cache.h"
8 #include "parse-options.h"
9 #include "fsmonitor-ipc.h"
10 #include "thread-utils.h"
11 #include "trace2.h"
13 #ifndef HAVE_FSMONITOR_DAEMON_BACKEND
14 int cmd__fsmonitor_client(int argc, const char **argv)
16 die("fsmonitor--daemon not available on this platform");
18 #else
21 * Read the `.git/index` to get the last token written to the
22 * FSMonitor Index Extension.
24 static const char *get_token_from_index(void)
26 struct index_state *istate = the_repository->index;
28 if (do_read_index(istate, the_repository->index_file, 0) < 0)
29 die("unable to read index file");
30 if (!istate->fsmonitor_last_update)
31 die("index file does not have fsmonitor extension");
33 return istate->fsmonitor_last_update;
37 * Send an IPC query to a `git-fsmonitor--daemon` daemon and
38 * ask for the changes since the given token or from the last
39 * token in the index extension.
41 * This will implicitly start a daemon process if necessary. The
42 * daemon process will persist after we exit.
44 static int do_send_query(const char *token)
46 struct strbuf answer = STRBUF_INIT;
47 int ret;
49 if (!token || !*token)
50 token = get_token_from_index();
52 ret = fsmonitor_ipc__send_query(token, &answer);
53 if (ret < 0)
54 die("could not query fsmonitor--daemon");
56 write_in_full(1, answer.buf, answer.len);
57 strbuf_release(&answer);
59 return 0;
63 * Send a "flush" command to the `git-fsmonitor--daemon` (if running)
64 * and tell it to flush its cache.
66 * This feature is primarily used by the test suite to simulate a loss of
67 * sync with the filesystem where we miss kernel events.
69 static int do_send_flush(void)
71 struct strbuf answer = STRBUF_INIT;
72 int ret;
74 ret = fsmonitor_ipc__send_command("flush", &answer);
75 if (ret)
76 return ret;
78 write_in_full(1, answer.buf, answer.len);
79 strbuf_release(&answer);
81 return 0;
84 struct hammer_thread_data
86 pthread_t pthread_id;
87 int thread_nr;
89 int nr_requests;
90 const char *token;
92 int sum_successful;
93 int sum_errors;
96 static void *hammer_thread_proc(void *_hammer_thread_data)
98 struct hammer_thread_data *data = _hammer_thread_data;
99 struct strbuf answer = STRBUF_INIT;
100 int k;
101 int ret;
103 trace2_thread_start("hammer");
105 for (k = 0; k < data->nr_requests; k++) {
106 strbuf_reset(&answer);
108 ret = fsmonitor_ipc__send_query(data->token, &answer);
109 if (ret < 0)
110 data->sum_errors++;
111 else
112 data->sum_successful++;
115 strbuf_release(&answer);
116 trace2_thread_exit();
117 return NULL;
121 * Start a pool of client threads that will each send a series of
122 * commands to the daemon.
124 * The goal is to overload the daemon with a sustained series of
125 * concurrent requests.
127 static int do_hammer(const char *token, int nr_threads, int nr_requests)
129 struct hammer_thread_data *data = NULL;
130 int k;
131 int sum_join_errors = 0;
132 int sum_commands = 0;
133 int sum_errors = 0;
135 if (!token || !*token)
136 token = get_token_from_index();
137 if (nr_threads < 1)
138 nr_threads = 1;
139 if (nr_requests < 1)
140 nr_requests = 1;
142 CALLOC_ARRAY(data, nr_threads);
144 for (k = 0; k < nr_threads; k++) {
145 struct hammer_thread_data *p = &data[k];
146 p->thread_nr = k;
147 p->nr_requests = nr_requests;
148 p->token = token;
150 if (pthread_create(&p->pthread_id, NULL, hammer_thread_proc, p)) {
151 warning("failed to create thread[%d] skipping remainder", k);
152 nr_threads = k;
153 break;
157 for (k = 0; k < nr_threads; k++) {
158 struct hammer_thread_data *p = &data[k];
160 if (pthread_join(p->pthread_id, NULL))
161 sum_join_errors++;
162 sum_commands += p->sum_successful;
163 sum_errors += p->sum_errors;
166 fprintf(stderr, "HAMMER: [threads %d][requests %d] [ok %d][err %d][join %d]\n",
167 nr_threads, nr_requests, sum_commands, sum_errors, sum_join_errors);
169 free(data);
172 * Return an error if any of the _send_query requests failed.
173 * We don't care about thread create/join errors.
175 return sum_errors > 0;
178 int cmd__fsmonitor_client(int argc, const char **argv)
180 const char *subcmd;
181 const char *token = NULL;
182 int nr_threads = 1;
183 int nr_requests = 1;
185 const char * const fsmonitor_client_usage[] = {
186 "test-tool fsmonitor-client query [<token>]",
187 "test-tool fsmonitor-client flush",
188 "test-tool fsmonitor-client hammer [<token>] [<threads>] [<requests>]",
189 NULL,
192 struct option options[] = {
193 OPT_STRING(0, "token", &token, "token",
194 "command token to send to the server"),
196 OPT_INTEGER(0, "threads", &nr_threads, "number of client threads"),
197 OPT_INTEGER(0, "requests", &nr_requests, "number of requests per thread"),
199 OPT_END()
202 argc = parse_options(argc, argv, NULL, options, fsmonitor_client_usage, 0);
204 if (argc != 1)
205 usage_with_options(fsmonitor_client_usage, options);
207 subcmd = argv[0];
209 setup_git_directory();
211 if (!strcmp(subcmd, "query"))
212 return !!do_send_query(token);
214 if (!strcmp(subcmd, "flush"))
215 return !!do_send_flush();
217 if (!strcmp(subcmd, "hammer"))
218 return !!do_hammer(token, nr_threads, nr_requests);
220 die("Unhandled subcommand: '%s'", subcmd);
222 #endif