1 /* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 
3 /**
4  * @copyright 2013 Couchbase, Inc.
5  *
6  * @author Filipe Manana  <filipe@couchbase.com>
7  *
8  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
9  * use this file except in compliance with the License. You may obtain a copy of
10  * the License at
11  *
12  *  http://www.apache.org/licenses/LICENSE-2.0
13  *
14  * Unless required by applicable law or agreed to in writing, software
15  * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
16  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
17  * License for the specific language governing permissions and limitations under
18  * the License.
19  **/
20 
21 #include "mapreduce_tests.h"
22 #include <string.h>
23 
24 #if __STDC_VERSION__ >=199901L
25 #define ASSIGN(x) x =
26 #else
27 #define ASSIGN(x)
28 #endif
29 
30 static const mapreduce_json_t key1 = {
31     ASSIGN(.json) "[1,1]",
32     ASSIGN(.length) sizeof("[1,1]") - 1
33 };
34 static const mapreduce_json_t value1 = {
35     ASSIGN(.json) "11",
36     ASSIGN(.length) sizeof("11") - 1
37 };
38 
39 static const mapreduce_json_t key2 = {
40     ASSIGN(.json) "[2,2]",
41     ASSIGN(.length) sizeof("[2,2]") - 1
42 };
43 static const mapreduce_json_t value2 = {
44     ASSIGN(.json) "22",
45     ASSIGN(.length) sizeof("22") - 1
46 };
47 
48 static const mapreduce_json_t key3 = {
49     ASSIGN(.json) "[3,3]",
50     ASSIGN(.length) sizeof("[3,3]") - 1
51 };
52 static const mapreduce_json_t value3 = {
53     ASSIGN(.json) "33",
54     ASSIGN(.length) sizeof("33") - 1
55 };
56 
57 static const mapreduce_json_t key4 = {
58     ASSIGN(.json) "[4,4]",
59     ASSIGN(.length) sizeof("[4,4]") - 1
60 };
61 static const mapreduce_json_t value4 = {
62     ASSIGN(.json) "44",
63     ASSIGN(.length) sizeof("44") - 1
64 };
65 
66 static mapreduce_json_list_t *all_keys(void);
67 static mapreduce_json_list_t *all_values(void);
68 static void free_json_list(mapreduce_json_list_t *list);
69 
test_bad_syntax_functions(void)70 static void test_bad_syntax_functions(void)
71 {
72     void *context = NULL;
73     char *error_msg = NULL;
74     mapreduce_error_t ret;
75     const char *functions[] = {
76         "function(key, values, rereduce) { return values.length; }",
77         "function(key, values, rereduce) { return values.length * 2;"
78     };
79 
80     ret = mapreduce_start_reduce_context(functions, 2, &context, &error_msg);
81     assert(ret == MAPREDUCE_SYNTAX_ERROR);
82     assert(error_msg != NULL);
83     assert(strlen(error_msg) > 0);
84     assert(context == NULL);
85 
86     mapreduce_free_error_msg(error_msg);
87 }
88 
test_runtime_exception(void)89 static void test_runtime_exception(void)
90 {
91     void *context = NULL;
92     char *error_msg = NULL;
93     mapreduce_error_t ret;
94     const char *functions[] = {
95         "function(key, values, rereduce) { throw('foobar'); }"
96     };
97     mapreduce_json_list_t *result = NULL;
98     mapreduce_json_list_t *keys;
99     mapreduce_json_list_t *values;
100 
101     ret = mapreduce_start_reduce_context(functions, 1, &context, &error_msg);
102     assert(ret == MAPREDUCE_SUCCESS);
103     assert(error_msg == NULL);
104     assert(context != NULL);
105 
106     keys = all_keys();
107     values = all_values();
108 
109     ret = mapreduce_reduce_all(context, keys, values, &result, &error_msg);
110     assert(ret == MAPREDUCE_RUNTIME_ERROR);
111     assert(result == NULL);
112     assert(error_msg != NULL);
113 
114     mapreduce_free_error_msg(error_msg);
115     mapreduce_free_context(context);
116     free_json_list(keys);
117     free_json_list(values);
118 }
119 
test_runtime_error(void)120 static void test_runtime_error(void)
121 {
122     void *context = NULL;
123     char *error_msg = NULL;
124     mapreduce_error_t ret;
125     const char *functions[] = {
126         "function(key, values, rereduce) { return sum(values); }",
127         "function(key, values, rereduce) { return values[0].foo.bar; }"
128     };
129     mapreduce_json_list_t *result = NULL;
130     mapreduce_json_list_t *keys;
131     mapreduce_json_list_t *values;
132     mapreduce_json_t *reduction = NULL;
133 
134     ret = mapreduce_start_reduce_context(functions, 2, &context, &error_msg);
135     assert(ret == MAPREDUCE_SUCCESS);
136     assert(error_msg == NULL);
137     assert(context != NULL);
138 
139     keys = all_keys();
140     values = all_values();
141 
142     /* reduce all */
143     ret = mapreduce_reduce_all(context, keys, values, &result, &error_msg);
144     assert(ret == MAPREDUCE_RUNTIME_ERROR);
145     assert(result == NULL);
146     assert(error_msg != NULL);
147     assert(strcmp("TypeError: Cannot read property 'bar' of undefined",
148                   error_msg) == 0);
149 
150     mapreduce_free_error_msg(error_msg);
151     error_msg = NULL;
152 
153     /* reduce single function (2nd) */
154 
155     ret = mapreduce_reduce(context, 2, keys, values, &reduction, &error_msg);
156     assert(ret == MAPREDUCE_RUNTIME_ERROR);
157     assert(reduction == NULL);
158     assert(error_msg != NULL);
159     assert(strcmp("TypeError: Cannot read property 'bar' of undefined",
160                   error_msg) == 0);
161 
162     mapreduce_free_error_msg(error_msg);
163 
164     /* reduce single function (1st), should succeed */
165 
166     ret = mapreduce_reduce(context, 1, keys, values, &reduction, &error_msg);
167     assert(ret == MAPREDUCE_SUCCESS);
168     assert(reduction != NULL);
169     assert(error_msg == NULL);
170     assert(reduction->length == (sizeof("110") - 1));
171     assert(strncmp(reduction->json, "110", sizeof("110") - 1) == 0);
172 
173     mapreduce_free_json(reduction);
174 
175     mapreduce_free_context(context);
176     free_json_list(keys);
177     free_json_list(values);
178 }
179 
test_reduce_emits(void)180 static void test_reduce_emits(void)
181 {
182     void *context = NULL;
183     char *error_msg = NULL;
184     mapreduce_error_t ret;
185     const char *functions[] = {
186         "function(key, values, rereduce) { emit(key, values); return sum(values); }"
187     };
188     mapreduce_json_list_t *result = NULL;
189     mapreduce_json_list_t *keys;
190     mapreduce_json_list_t *values;
191 
192     ret = mapreduce_start_reduce_context(functions, 1, &context, &error_msg);
193     assert(ret == MAPREDUCE_SUCCESS);
194     assert(error_msg == NULL);
195     assert(context != NULL);
196 
197     keys = all_keys();
198     values = all_values();
199 
200     ret = mapreduce_reduce_all(context, keys, values, &result, &error_msg);
201     assert(ret == MAPREDUCE_SUCCESS);
202     assert(error_msg == NULL);
203     assert(result != NULL);
204     assert(result->length == 1);
205     assert(result->values[0].length == (sizeof("110") - 1));
206     assert(strncmp("110", result->values[0].json, sizeof("110") - 1) == 0);
207 
208     mapreduce_free_json_list(result);
209     mapreduce_free_context(context);
210     free_json_list(keys);
211     free_json_list(values);
212 }
213 
214 
test_reduce_and_rereduce_success(void)215 static void test_reduce_and_rereduce_success(void)
216 {
217     void *context = NULL;
218     char *error_msg = NULL;
219     mapreduce_error_t ret;
220     const char *functions[] = {
221         "function(key, values, rereduce) {"
222         "  if (rereduce) {"
223         "    return sum(values);"
224         "  } else {"
225         "    return values.length;"
226         "  }"
227         "}",
228         "function(key, values, rereduce) {"
229         "  if (rereduce) {"
230         "    return values[values.length - 1];"
231         "  } else {"
232         "    return values[0];"
233         "  }"
234         "}"
235     };
236     mapreduce_json_list_t *result = NULL;
237     mapreduce_json_list_t *keys = all_keys();
238     mapreduce_json_list_t *values = all_values();
239     mapreduce_json_t *reduction = NULL;
240 
241     ret = mapreduce_start_reduce_context(functions, 2, &context, &error_msg);
242     assert(ret == MAPREDUCE_SUCCESS);
243     assert(error_msg == NULL);
244     assert(context != NULL);
245 
246     keys = all_keys();
247     values = all_values();
248 
249     /* reduce all */
250     ret = mapreduce_reduce_all(context, keys, values, &result, &error_msg);
251     assert(ret == MAPREDUCE_SUCCESS);
252     assert(error_msg == NULL);
253     assert(result != NULL);
254     assert(result->length == 2);
255     assert(result->values[0].length == (sizeof("4") - 1));
256     assert(strncmp(result->values[0].json, "4", (sizeof("4") - 1)) == 0);
257     assert(result->values[1].length == (sizeof("11") - 1));
258     assert(strncmp(result->values[1].json, "11", (sizeof("11") - 1)) == 0);
259 
260     mapreduce_free_json_list(result);
261 
262     /* reduce single function (1st) */
263 
264     ret = mapreduce_reduce(context, 1, keys, values, &reduction, &error_msg);
265     assert(ret == MAPREDUCE_SUCCESS);
266     assert(error_msg == NULL);
267     assert(reduction != NULL);
268     assert(reduction->length == (sizeof("4") - 1));
269     assert(strncmp(reduction->json, "4", sizeof("4") - 1) == 0);
270 
271     mapreduce_free_json(reduction);
272     reduction = NULL;
273 
274     /* reduce single function (2nd), should succeed */
275 
276     ret = mapreduce_reduce(context, 2, keys, values, &reduction, &error_msg);
277     assert(ret == MAPREDUCE_SUCCESS);
278     assert(reduction != NULL);
279     assert(error_msg == NULL);
280     assert(reduction->length == (sizeof("11") - 1));
281     assert(strncmp(reduction->json, "11", sizeof("11") - 1) == 0);
282 
283     mapreduce_free_json(reduction);
284     reduction = NULL;
285 
286     /* rereduce, 1st function */
287 
288     ret = mapreduce_rereduce(context, 1, values, &reduction, &error_msg);
289     assert(ret == MAPREDUCE_SUCCESS);
290     assert(error_msg == NULL);
291     assert(reduction != NULL);
292     assert(reduction->length == (sizeof("110") - 1));
293     assert(strncmp(reduction->json, "110", sizeof("110") - 1) == 0);
294 
295     mapreduce_free_json(reduction);
296     reduction = NULL;
297 
298     /* rereduce, 2nd function */
299 
300     ret = mapreduce_rereduce(context, 2, values, &reduction, &error_msg);
301     assert(ret == MAPREDUCE_SUCCESS);
302     assert(error_msg == NULL);
303     assert(reduction != NULL);
304     assert(reduction->length == (sizeof("44") - 1));
305     assert(strncmp(reduction->json, "44", sizeof("44") - 1) == 0);
306 
307     mapreduce_free_json(reduction);
308 
309     mapreduce_free_context(context);
310     free_json_list(keys);
311     free_json_list(values);
312 }
313 
test_timeout(void)314 static void test_timeout(void)
315 {
316     void *context = NULL;
317     char *error_msg = NULL;
318     mapreduce_error_t ret;
319     const char *functions[] = {
320         "function(key, values, rereduce) {"
321         "  if (rereduce) {"
322         "    return sum(values);"
323         "  } else {"
324         "    while (true) { };"
325         "    return values.length;"
326         "  }"
327         "}"
328     };
329     mapreduce_json_list_t *result = NULL;
330     mapreduce_json_list_t *keys = all_keys();
331     mapreduce_json_list_t *values = all_values();
332     mapreduce_json_t *reduction = NULL;
333 
334     ret = mapreduce_start_reduce_context(functions, 1, &context, &error_msg);
335     assert(ret == MAPREDUCE_SUCCESS);
336     assert(error_msg == NULL);
337     assert(context != NULL);
338 
339     keys = all_keys();
340     values = all_values();
341 
342     /* reduce all */
343     ret = mapreduce_reduce_all(context, keys, values, &result, &error_msg);
344     assert(ret == MAPREDUCE_TIMEOUT);
345     assert(result == NULL);
346     assert(error_msg != NULL);
347     assert(strcmp(error_msg, "timeout") == 0);
348 
349     mapreduce_free_error_msg(error_msg);
350     error_msg = NULL;
351 
352     /* rereduce, 1st function */
353     ret = mapreduce_rereduce(context, 1, values, &reduction, &error_msg);
354     assert(ret == MAPREDUCE_SUCCESS);
355     assert(error_msg == NULL);
356     assert(reduction != NULL);
357     assert(reduction->length == (sizeof("110") - 1));
358     assert(strncmp(reduction->json, "110", sizeof("110") - 1) == 0);
359 
360     mapreduce_free_json(reduction);
361     mapreduce_free_context(context);
362     free_json_list(keys);
363     free_json_list(values);
364 }
365 
all_keys(void)366 static mapreduce_json_list_t *all_keys(void)
367 {
368     mapreduce_json_list_t *ret = (mapreduce_json_list_t *) malloc(sizeof(*ret));
369 
370     assert(ret != NULL);
371     ret->length = 4;
372     ret->values = (mapreduce_json_t *) malloc(sizeof(mapreduce_json_t) * ret->length);
373     assert(ret->values != NULL);
374     ret->values[0] = key1;
375     ret->values[1] = key2;
376     ret->values[2] = key3;
377     ret->values[3] = key4;
378 
379     return ret;
380 }
381 
all_values(void)382 static mapreduce_json_list_t *all_values(void)
383 {
384     mapreduce_json_list_t *ret = (mapreduce_json_list_t *) malloc(sizeof(*ret));
385 
386     assert(ret != NULL);
387     ret->length = 4;
388     ret->values = (mapreduce_json_t *) malloc(sizeof(mapreduce_json_t) * ret->length);
389     assert(ret->values != NULL);
390     ret->values[0] = value1;
391     ret->values[1] = value2;
392     ret->values[2] = value3;
393     ret->values[3] = value4;
394 
395     return ret;
396 }
397 
free_json_list(mapreduce_json_list_t *list)398 static void free_json_list(mapreduce_json_list_t *list)
399 {
400     free(list->values);
401     free(list);
402 }
403 
reduce_tests(void)404 void reduce_tests(void)
405 {
406     int i;
407 
408     fprintf(stderr, "Running reduce tests\n");
409 
410     mapreduce_set_timeout(1);
411     test_timeout();
412 
413     for (i = 0; i < 100; ++i) {
414         test_bad_syntax_functions();
415         test_runtime_exception();
416         test_runtime_error();
417         test_reduce_emits();
418         test_reduce_and_rereduce_success();
419     }
420 
421     test_timeout();
422 }
423