1 /* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 #include <stdio.h>
3 #include <stdlib.h>
4 #include <string.h>
5 #include <sys/stat.h>
6 #include <strings.h>
7 
8 #include <libvbucket/vbucket.h>
9 
10 #include "macros.h"
11 
12 struct key_st {
13     char *key;
14     int vbucket;
15 };
16 
17 static const struct key_st keys[] =
18 {
19     { "hello", 0 },
20     { "doctor", 0 },
21     { "name", 3 },
22     { "continue", 3 },
23     { "yesterday", 0 },
24     { "tomorrow", 1 },
25     { "another key", 2 },
26     { NULL, -1 }
27 };
28 
29 static const char *servers[] = { "server1:11211",
30                                  "server2:11210",
31                                  "server3:11211" };
32 
33 struct vb_st {
34     int master;
35     int replicas[2];
36 };
37 
38 static const struct vb_st vbuckets[] =
39 {
40     { 0, { 1, 2 } },
41     { 1, { 2, 0 } },
42     { 2, { 1, -1 } },
43     { 1, { 2, 0 } }
44 };
45 
configPath(const char *fname)46 static char *configPath(const char *fname) {
47     static char buffer[FILENAME_MAX];
48     const char *root = getenv("CMAKE_CURRENT_SOURCE_DIR");
49     struct stat st;
50 
51     if (root == NULL) {
52         root = ".";
53     }
54 
55     snprintf(buffer, FILENAME_MAX, "%s/tests/vbucket/config/testapp-%s",
56              root, fname);
57     if (stat(buffer, &st) == -1) {
58         snprintf(buffer, FILENAME_MAX, "%s/tests/vbucket/config/%s",
59                  root, fname);
60         if (stat(buffer, &st) == -1) {
61             fprintf(stderr, "cannot find config %s\n", fname);
62             abort();
63         }
64     }
65 
66     return buffer;
67 }
68 
testConfig(const char *fname)69 static void testConfig(const char *fname) {
70     int whoops = 0;
71     const struct key_st *k;
72     int i = 0;
73 
74     VBUCKET_CONFIG_HANDLE vb = vbucket_config_parse_file(configPath(fname));
75     if (vb == NULL) {
76         fprintf(stderr, "vbucket_config_parse_file error: %s\n",
77                 vbucket_get_error());
78         abort();
79     }
80 
81     while ((k = &keys[i++])->key != NULL) {
82         int id = vbucket_get_vbucket_by_key(vb, k->key, strlen(k->key));
83         if (id != k->vbucket) {
84             fprintf(stderr, "Expected vbucket %d for key '%s' but got %d\n",
85                     k->vbucket, k->key, id);
86             whoops = 1;
87         }
88     }
89 
90     if (whoops) {
91         abort();
92     }
93 
94     assert(vbucket_config_get_num_servers(vb) == 3 || vbucket_config_get_num_servers(vb) == 4);
95     assert(vbucket_config_get_num_replicas(vb) == 2);
96 
97     for (i = 0; i < 3; ++i) {
98         assert(strcmp(vbucket_config_get_server(vb, i), servers[i]) == 0);
99     }
100 
101     for (i = 0; i < 4; ++i) {
102         assert(vbucket_get_master(vb, i) == vbuckets[i].master);
103         assert(vbucket_get_replica(vb, i, 0) == vbuckets[i].replicas[0]);
104         assert(vbucket_get_replica(vb, i, 1) == vbuckets[i].replicas[1]);
105     }
106 
107     assert(vbucket_config_get_user(vb) == NULL);
108     assert(vbucket_config_get_password(vb) == NULL);
109 
110     vbucket_config_destroy(vb);
111 }
112 
113 
testWrongServer(const char *fname)114 static void testWrongServer(const char *fname) {
115     VBUCKET_CONFIG_HANDLE vb = vbucket_config_parse_file(configPath(fname));
116     if (vb == NULL) {
117         fprintf(stderr, "vbucket_config_parse_file error: %s\n",
118                 vbucket_get_error());
119         abort();
120     }
121 
122     /* Starts at 0 */
123     assert(vbucket_get_master(vb, 0) == 0);
124     /* Does not change when I told it I found the wrong thing */
125     assert(vbucket_found_incorrect_master(vb, 0, 1) == 0);
126     assert(vbucket_get_master(vb, 0) == 0);
127     /* Does change if I tell it I got the right thing and it was wrong. */
128     assert(vbucket_found_incorrect_master(vb, 0, 0) == 1);
129     assert(vbucket_get_master(vb, 0) == 1);
130     /* ...and again */
131     assert(vbucket_found_incorrect_master(vb, 0, 1) == 2);
132     assert(vbucket_get_master(vb, 0) == 2);
133     /* ...and then wraps */
134     assert(vbucket_found_incorrect_master(vb, 0, 2) == 0);
135     assert(vbucket_get_master(vb, 0) == 0);
136 
137     vbucket_config_destroy(vb);
138 }
139 
testWrongNumVbuckets(const char *fname)140 static void testWrongNumVbuckets(const char *fname) {
141     VBUCKET_CONFIG_HANDLE vb = vbucket_config_create();
142     assert(vb != NULL);
143     assert(vbucket_config_parse(vb, LIBVBUCKET_SOURCE_FILE, configPath(fname)) != 0);
144     assert(strcmp(vbucket_get_error_message(vb),
145                   "Number of vBuckets must be a power of two > 0 and <= 65536") == 0);
146     vbucket_config_destroy(vb);
147 }
148 
testZeroNumVbuckets(const char *fname)149 static void testZeroNumVbuckets(const char *fname) {
150     VBUCKET_CONFIG_HANDLE vb = vbucket_config_create();
151     assert(vb != NULL);
152     assert(vbucket_config_parse(vb, LIBVBUCKET_SOURCE_FILE, configPath(fname)) != 0);
153     assert(strcmp(vbucket_get_error_message(vb),
154                   "No vBuckets available; service maybe still initializing") == 0);
155     vbucket_config_destroy(vb);
156 }
157 
testWrongServerFFT(const char *fname)158 static void testWrongServerFFT(const char *fname) {
159     VBUCKET_CONFIG_HANDLE vb = vbucket_config_parse_file(configPath(fname));
160     int rv = 0;
161     int nvb = 0;
162     int i = 0;
163 
164     if (vb == NULL) {
165         fprintf(stderr, "vbucket_config_parse_file error: %s\n",
166                 vbucket_get_error());
167         abort();
168     }
169 
170     /* found incorrect master should not be the same as get master now */
171     nvb = vbucket_config_get_num_vbuckets(vb);
172     for (i = 0; i < nvb; i++) {
173         rv = vbucket_get_master(vb, i);
174         assert(rv != vbucket_found_incorrect_master(vb, i, rv));
175     }
176     /* the ideal test case should be that we check that the vbucket */
177     /* and the fvbucket map are identical at this point. TODO untill */
178     /* we have a vbucketlib function that diffs vbuckets and fvbuckets */
179     vbucket_config_destroy(vb);
180 }
181 
testConfigDiff(void)182 static void testConfigDiff(void) {
183     VBUCKET_CONFIG_HANDLE vb1 = vbucket_config_parse_file(configPath("config-diff1"));
184     VBUCKET_CONFIG_HANDLE vb2 = vbucket_config_parse_file(configPath("config-diff2"));
185     VBUCKET_CONFIG_DIFF *diff;
186     assert(vb2);
187 
188     diff = vbucket_compare(vb1, vb2);
189     assert(vb1);
190     assert(diff);
191 
192     assert(diff->sequence_changed);
193     assert(diff->n_vb_changes == 1);
194     assert(strcmp(diff->servers_added[0], "server4:11211") == 0);
195     assert(diff->servers_added[1] == NULL);
196     assert(strcmp(diff->servers_removed[0], "server3:11211") == 0);
197     assert(diff->servers_removed[1] == NULL);
198 
199     vbucket_free_diff(diff);
200     vbucket_config_destroy(vb2);
201 
202     vb2 = vbucket_config_parse_file(configPath("config-diff3"));
203     assert(vb2);
204 
205     diff = vbucket_compare(vb1, vb2);
206     assert(diff);
207 
208     assert(diff->sequence_changed);
209     assert(diff->n_vb_changes == -1);
210     assert(diff->servers_added[0] == NULL);
211     assert(strcmp(diff->servers_removed[0], "server3:11211") == 0);
212     assert(diff->servers_removed[1] == NULL);
213 }
214 
testConfigDiffSame(void)215 static void testConfigDiffSame(void) {
216     VBUCKET_CONFIG_HANDLE vb1 = vbucket_config_parse_file(configPath("config"));
217     VBUCKET_CONFIG_HANDLE vb2 = vbucket_config_parse_file(configPath("config"));
218     VBUCKET_CONFIG_DIFF *diff;
219     assert(vb1);
220     assert(vb2);
221     diff = vbucket_compare(vb1, vb2);
222     assert(diff);
223 
224     assert(diff->sequence_changed == 0);
225     assert(diff->n_vb_changes == 0);
226     assert(diff->servers_added[0] == NULL);
227     assert(diff->servers_removed[0] == NULL);
228 
229     vbucket_free_diff(diff);
230     vbucket_config_destroy(vb1);
231     vbucket_config_destroy(vb2);
232 }
233 
testConfigDiffKetamaSame(void)234 static void testConfigDiffKetamaSame(void) {
235     VBUCKET_CONFIG_HANDLE vb1 = vbucket_config_parse_file(configPath("ketama-eight-nodes"));
236     VBUCKET_CONFIG_HANDLE vb2 = vbucket_config_parse_file(configPath("ketama-ordered-eight-nodes"));
237     VBUCKET_CONFIG_DIFF *diff;
238     assert(vb1);
239     assert(vb2);
240     diff = vbucket_compare(vb1, vb2);
241     assert(diff);
242 
243     assert(diff->sequence_changed == 0);
244     assert(diff->n_vb_changes == 0);
245     assert(diff->servers_added[0] == NULL);
246     assert(diff->servers_removed[0] == NULL);
247 
248     vbucket_free_diff(diff);
249     vbucket_config_destroy(vb1);
250     vbucket_config_destroy(vb2);
251 }
252 
testConfigUserPassword(void)253 static void testConfigUserPassword(void) {
254     VBUCKET_CONFIG_HANDLE vb1;
255     VBUCKET_CONFIG_HANDLE vb2;
256     VBUCKET_CONFIG_DIFF *diff;
257 
258     vb1 = vbucket_config_parse_file(configPath("config-user-password1"));
259     assert(vb1);
260     assert(strcmp(vbucket_config_get_user(vb1), "theUser") == 0);
261     assert(strcmp(vbucket_config_get_password(vb1), "thePassword") == 0);
262 
263     vb2 = vbucket_config_parse_file(configPath("config-user-password2"));
264     assert(vb2);
265     assert(strcmp(vbucket_config_get_user(vb2), "theUserIsDifferent") == 0);
266     assert(strcmp(vbucket_config_get_password(vb2), "thePasswordIsDifferent") == 0);
267 
268     diff = vbucket_compare(vb1, vb2);
269     assert(diff);
270 
271     assert(diff->sequence_changed);
272     assert(diff->n_vb_changes == 0);
273     assert(diff->servers_added[0] == NULL);
274     assert(diff->servers_removed[0] == NULL);
275 
276     vbucket_free_diff(diff);
277 
278     diff = vbucket_compare(vb1, vb1);
279     assert(diff);
280 
281     assert(diff->sequence_changed == 0);
282     assert(diff->n_vb_changes == 0);
283     assert(diff->servers_added[0] == NULL);
284     assert(diff->servers_removed[0] == NULL);
285 
286     vbucket_free_diff(diff);
287 
288     vbucket_config_destroy(vb1);
289     vbucket_config_destroy(vb2);
290 }
291 
testConfigCouchApiBase(void)292 static void testConfigCouchApiBase(void)
293 {
294     VBUCKET_CONFIG_HANDLE vb = vbucket_config_parse_file(configPath("config-couch-api-base"));
295     assert(vb);
296     assert(strcmp(vbucket_config_get_couch_api_base(vb, 0), "http://192.168.2.123:9500/default") == 0);
297     assert(strcmp(vbucket_config_get_couch_api_base(vb, 1), "http://192.168.2.123:9501/default") == 0);
298     assert(strcmp(vbucket_config_get_couch_api_base(vb, 2), "http://192.168.2.123:9502/default") == 0);
299     assert(strcmp(vbucket_config_get_rest_api_server(vb, 0), "192.168.2.123:9000") == 0);
300     assert(strcmp(vbucket_config_get_rest_api_server(vb, 1), "192.168.2.123:9001") == 0);
301     assert(strcmp(vbucket_config_get_rest_api_server(vb, 2), "192.168.2.123:9002") == 0);
302     assert(strcmp(vbucket_config_get_server(vb, 0), "192.168.2.123:12000") == 0);
303     assert(strcmp(vbucket_config_get_server(vb, 1), "192.168.2.123:12002") == 0);
304     assert(strcmp(vbucket_config_get_server(vb, 2), "192.168.2.123:12004") == 0);
305 }
306 
main(int argc, char **argv)307 int main(int argc, char **argv)
308 {
309     char buffer[1024];
310     if (argc > 1 && getenv("CMAKE_CURRENT_SOURCE_DIR") == NULL) {
311         snprintf(buffer, sizeof(buffer), "CMAKE_CURRENT_SOURCE_DIR=%s",
312                  argv[1]);
313         putenv(buffer);
314     }
315   testConfig("config");
316   testConfig("config-flat");
317   testConfig("config-in-envelope");
318   testConfig("config-in-envelope2");
319   testConfig("config-in-envelope-fft");
320   testWrongServer("config");
321   testWrongServerFFT("config-in-envelope-fft");
322   testWrongNumVbuckets("config-wrong-num-vbuckets");
323   testZeroNumVbuckets("config-zero-num-vbuckets");
324   testConfigDiff();
325   testConfigDiffSame();
326   testConfigUserPassword();
327   testConfigCouchApiBase();
328   testConfigDiffKetamaSame();
329   exit(EXIT_SUCCESS);
330 }
331