xref: /4.0.0/couchstore/tests/mapreduce/map.c (revision 81a8545e)
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
30static const mapreduce_json_t doc1 = {
31    ASSIGN(.json) "{\"value\": 1}",
32    ASSIGN(.length) sizeof("{\"value\": 1}") - 1
33};
34static const mapreduce_json_t meta1 = {
35    ASSIGN(.json) "{\"id\":\"doc1\"}",
36    ASSIGN(.length) sizeof("{\"id\":\"doc1\"}") - 1
37};
38
39static const mapreduce_json_t doc2 = {
40    ASSIGN(.json) "{\"value\": 2}",
41    ASSIGN(.length) sizeof("{\"value\": 2}") - 1
42};
43static const mapreduce_json_t meta2 = {
44    ASSIGN(.json) "{\"id\":\"doc2\"}",
45    ASSIGN(.length) sizeof("{\"id\":\"doc2\"}") - 1
46};
47
48static const mapreduce_json_t doc3 = {
49    ASSIGN(.json) "{\"value\": 3}",
50    ASSIGN(.length) sizeof("{\"value\": 3}") - 1
51};
52static const mapreduce_json_t meta3 = {
53    ASSIGN(.json) "{\"id\":\"doc3\"}",
54    ASSIGN(.length) sizeof("{\"id\":\"doc3\"}") - 1
55};
56
57
58static void test_bad_syntax_functions(void)
59{
60    void *context = NULL;
61    char *error_msg = NULL;
62    mapreduce_error_t ret;
63    const char *functions[] = {
64        "function(doc, meta) { emit(meta.id, null); }",
65        "function(doc, meta { emit(doc.field, meta.id); }"
66    };
67
68    ret = mapreduce_start_map_context(functions, 2, &context, &error_msg);
69    cb_assert(ret == MAPREDUCE_SYNTAX_ERROR);
70    cb_assert(error_msg != NULL);
71    cb_assert(strlen(error_msg) > 0);
72    cb_assert(context == NULL);
73
74    mapreduce_free_error_msg(error_msg);
75}
76
77
78static void test_runtime_exception(void)
79{
80    void *context = NULL;
81    char *error_msg = NULL;
82    mapreduce_error_t ret;
83    const char *functions[] = {
84        "function(doc, meta) { throw('foobar'); }"
85    };
86    mapreduce_map_result_list_t *result = NULL;
87
88    ret = mapreduce_start_map_context(functions, 1, &context, &error_msg);
89    cb_assert(ret == MAPREDUCE_SUCCESS);
90    cb_assert(error_msg == NULL);
91    cb_assert(context != NULL);
92
93    ret = mapreduce_map(context, &doc1, &meta1, &result);
94    cb_assert(ret == MAPREDUCE_SUCCESS);
95    cb_assert(result != NULL);
96    cb_assert(result->length == 1);
97    cb_assert(result->list != NULL);
98
99    cb_assert(result->list[0].error == MAPREDUCE_RUNTIME_ERROR);
100    cb_assert(result->list[0].result.error_msg != NULL);
101    cb_assert(strcmp("foobar", result->list[0].result.error_msg) == 0);
102
103    mapreduce_free_map_result_list(result);
104    mapreduce_free_context(context);
105}
106
107
108static void test_runtime_error(void)
109{
110    void *context = NULL;
111    char *error_msg = NULL;
112    mapreduce_error_t ret;
113    const char *functions[] = {
114        "function(doc, meta) { emit(doc.foo.bar, meta.id); }"
115    };
116    mapreduce_map_result_list_t *result = NULL;
117
118    ret = mapreduce_start_map_context(functions, 1, &context, &error_msg);
119    cb_assert(ret == MAPREDUCE_SUCCESS);
120    cb_assert(error_msg == NULL);
121    cb_assert(context != NULL);
122
123    ret = mapreduce_map(context, &doc1, &meta1, &result);
124    cb_assert(ret == MAPREDUCE_SUCCESS);
125    cb_assert(result != NULL);
126    cb_assert(result->length == 1);
127    cb_assert(result->list != NULL);
128
129    cb_assert(result->list[0].error == MAPREDUCE_RUNTIME_ERROR);
130    cb_assert(result->list[0].result.error_msg != NULL);
131    cb_assert(strcmp("TypeError: Cannot read property 'bar' of undefined",
132                  result->list[0].result.error_msg) == 0);
133
134    mapreduce_free_map_result_list(result);
135    mapreduce_free_context(context);
136}
137
138
139static void test_map_no_emit(void)
140{
141    void *context = NULL;
142    char *error_msg = NULL;
143    mapreduce_error_t ret;
144    const char *functions[] = {
145        "function(doc, meta) { }",
146        "function(doc, meta) { if (doc.value > 12345) { emit(meta.id, null); } }"
147    };
148    mapreduce_map_result_list_t *result = NULL;
149
150    ret = mapreduce_start_map_context(functions, 2, &context, &error_msg);
151    cb_assert(ret == MAPREDUCE_SUCCESS);
152    cb_assert(error_msg == NULL);
153    cb_assert(context != NULL);
154
155    ret = mapreduce_map(context, &doc1, &meta1, &result);
156    cb_assert(ret == MAPREDUCE_SUCCESS);
157    cb_assert(result != NULL);
158    cb_assert(result->length == 2);
159    cb_assert(result->list != NULL);
160
161    cb_assert(result->list[0].error == MAPREDUCE_SUCCESS);
162    cb_assert(result->list[0].result.kvs.length == 0);
163
164    cb_assert(result->list[1].error == MAPREDUCE_SUCCESS);
165    cb_assert(result->list[1].result.kvs.length == 0);
166
167    mapreduce_free_map_result_list(result);
168    mapreduce_free_context(context);
169}
170
171
172static void test_map_single_emit(void)
173{
174    void *context = NULL;
175    char *error_msg = NULL;
176    mapreduce_error_t ret;
177    const char *functions[] = {
178        "function(doc, meta) { emit(meta.id, doc.value); }",
179        "function(doc, meta) { emit(doc.value, meta.id); }"
180    };
181    mapreduce_map_result_list_t *result = NULL;
182
183    ret = mapreduce_start_map_context(functions, 2, &context, &error_msg);
184    cb_assert(ret == MAPREDUCE_SUCCESS);
185    cb_assert(error_msg == NULL);
186    cb_assert(context != NULL);
187
188    ret = mapreduce_map(context, &doc1, &meta1, &result);
189    cb_assert(ret == MAPREDUCE_SUCCESS);
190    cb_assert(result != NULL);
191    cb_assert(result->length == 2);
192    cb_assert(result->list != NULL);
193
194    cb_assert(result->list[0].error == MAPREDUCE_SUCCESS);
195    cb_assert(result->list[0].result.kvs.length == 1);
196    cb_assert(result->list[0].result.kvs.kvs[0].key.length == (sizeof("\"doc1\"") - 1));
197    cb_assert(memcmp(result->list[0].result.kvs.kvs[0].key.json,
198                  "\"doc1\"",
199                  (sizeof("\"doc1\"") - 1)) == 0);
200    cb_assert(result->list[0].result.kvs.kvs[0].value.length == (sizeof("1") - 1));
201    cb_assert(memcmp(result->list[0].result.kvs.kvs[0].value.json,
202                  "1",
203                  (sizeof("1") - 1)) == 0);
204
205    cb_assert(result->list[1].error == MAPREDUCE_SUCCESS);
206    cb_assert(result->list[1].result.kvs.length == 1);
207    cb_assert(result->list[1].result.kvs.kvs[0].key.length == (sizeof("1") - 1));
208    cb_assert(memcmp(result->list[1].result.kvs.kvs[0].key.json,
209                  "1",
210                  (sizeof("1") - 1)) == 0);
211    cb_assert(result->list[1].result.kvs.kvs[0].value.length == (sizeof("\"doc1\"") - 1));
212    cb_assert(memcmp(result->list[1].result.kvs.kvs[0].value.json,
213                  "\"doc1\"",
214                  (sizeof("\"doc1\"") - 1)) == 0);
215
216    mapreduce_free_map_result_list(result);
217
218    ret = mapreduce_map(context, &doc2, &meta2, &result);
219    cb_assert(ret == MAPREDUCE_SUCCESS);
220    cb_assert(result != NULL);
221    cb_assert(result->length == 2);
222    cb_assert(result->list != NULL);
223
224    cb_assert(result->list[0].error == MAPREDUCE_SUCCESS);
225    cb_assert(result->list[0].result.kvs.length == 1);
226    cb_assert(result->list[0].result.kvs.kvs[0].key.length == (sizeof("\"doc2\"") - 1));
227    cb_assert(memcmp(result->list[0].result.kvs.kvs[0].key.json,
228                  "\"doc2\"",
229                  (sizeof("\"doc2\"") - 1)) == 0);
230    cb_assert(result->list[0].result.kvs.kvs[0].value.length == (sizeof("2") - 1));
231    cb_assert(memcmp(result->list[0].result.kvs.kvs[0].value.json,
232                  "2",
233                  (sizeof("2") - 1)) == 0);
234
235    cb_assert(result->list[1].error == MAPREDUCE_SUCCESS);
236    cb_assert(result->list[1].result.kvs.length == 1);
237    cb_assert(result->list[1].result.kvs.kvs[0].key.length == (sizeof("2") - 1));
238    cb_assert(memcmp(result->list[1].result.kvs.kvs[0].key.json,
239                  "2",
240                  (sizeof("2") - 1)) == 0);
241    cb_assert(result->list[1].result.kvs.kvs[0].value.length == (sizeof("\"doc2\"") - 1));
242    cb_assert(memcmp(result->list[1].result.kvs.kvs[0].value.json,
243                  "\"doc2\"",
244                  (sizeof("\"doc2\"") - 1)) == 0);
245
246    mapreduce_free_map_result_list(result);
247
248    mapreduce_free_context(context);
249}
250
251
252static void test_map_multiple_emits(void)
253{
254    void *context = NULL;
255    char *error_msg = NULL;
256    mapreduce_error_t ret;
257    const char *functions[] = {
258        "function(doc, meta) {\n"
259        "  if (doc.value != 1) { throw('foobar'); } else { emit(meta.id, doc.value); }\n"
260        "}\n",
261        "function(doc, meta) {\n"
262        "  emit(doc.value, meta.id);\n"
263        "  emit([meta.id, doc.value], null);\n"
264        "  emit(doc.value * 5, -doc.value);\n"
265        "}\n",
266        "function(doc, meta) {\n"
267        "  if (doc.value != 3) { emit(doc.value, 0); } else { emit(meta.id, doc.value.f.z); }\n"
268        "}\n"
269    };
270    mapreduce_map_result_list_t *result = NULL;
271
272    ret = mapreduce_start_map_context(functions, 3, &context, &error_msg);
273    cb_assert(ret == MAPREDUCE_SUCCESS);
274    cb_assert(error_msg == NULL);
275    cb_assert(context != NULL);
276
277    /* map doc1 */
278    ret = mapreduce_map(context, &doc1, &meta1, &result);
279    cb_assert(ret == MAPREDUCE_SUCCESS);
280    cb_assert(result != NULL);
281    cb_assert(result->length == 3);
282    cb_assert(result->list != NULL);
283
284    /* function 1 */
285    cb_assert(result->list[0].error == MAPREDUCE_SUCCESS);
286    cb_assert(result->list[0].result.kvs.length == 1);
287    cb_assert(result->list[0].result.kvs.kvs[0].key.length == (sizeof("\"doc1\"") - 1));
288    cb_assert(memcmp(result->list[0].result.kvs.kvs[0].key.json,
289                  "\"doc1\"",
290                  (sizeof("\"doc1\"") - 1)) == 0);
291    cb_assert(result->list[0].result.kvs.kvs[0].value.length == (sizeof("1") - 1));
292    cb_assert(memcmp(result->list[0].result.kvs.kvs[0].value.json,
293                  "1",
294                  (sizeof("1") - 1)) == 0);
295
296    /* function 2 */
297    cb_assert(result->list[1].error == MAPREDUCE_SUCCESS);
298    cb_assert(result->list[1].result.kvs.length == 3);
299    cb_assert(result->list[1].result.kvs.kvs[0].key.length == (sizeof("1") - 1));
300    cb_assert(memcmp(result->list[1].result.kvs.kvs[0].key.json,
301                  "1",
302                  (sizeof("1") - 1)) == 0);
303    cb_assert(result->list[1].result.kvs.kvs[0].value.length == (sizeof("\"doc1\"") - 1));
304    cb_assert(memcmp(result->list[1].result.kvs.kvs[0].value.json,
305                  "\"doc1\"",
306                  (sizeof("\"doc1\"") - 1)) == 0);
307    cb_assert(result->list[1].result.kvs.kvs[1].key.length == (sizeof("[\"doc1\",1]") - 1));
308    cb_assert(memcmp(result->list[1].result.kvs.kvs[1].key.json,
309                  "[\"doc1\",1]",
310                  (sizeof("[\"doc1\",1]") - 1)) == 0);
311    cb_assert(result->list[1].result.kvs.kvs[1].value.length == (sizeof("null") - 1));
312    cb_assert(memcmp(result->list[1].result.kvs.kvs[1].value.json,
313                  "null",
314                  (sizeof("null") - 1)) == 0);
315    cb_assert(result->list[1].result.kvs.kvs[2].key.length == (sizeof("5") - 1));
316    cb_assert(memcmp(result->list[1].result.kvs.kvs[2].key.json,
317                  "5",
318                  (sizeof("5") - 1)) == 0);
319    cb_assert(result->list[1].result.kvs.kvs[2].value.length == (sizeof("-1") - 1));
320    cb_assert(memcmp(result->list[1].result.kvs.kvs[2].value.json,
321                  "-1",
322                  (sizeof("-1") - 1)) == 0);
323
324    /* function 3 */
325    cb_assert(result->list[2].error == MAPREDUCE_SUCCESS);
326    cb_assert(result->list[2].result.kvs.length == 1);
327    cb_assert(result->list[2].result.kvs.kvs[0].key.length == (sizeof("1") - 1));
328    cb_assert(memcmp(result->list[2].result.kvs.kvs[0].key.json,
329                  "1",
330                  (sizeof("1") - 1)) == 0);
331    cb_assert(result->list[2].result.kvs.kvs[0].value.length == (sizeof("0") - 1));
332    cb_assert(memcmp(result->list[2].result.kvs.kvs[0].value.json,
333                  "0",
334                  (sizeof("0") - 1)) == 0);
335
336    mapreduce_free_map_result_list(result);
337
338    /* map doc2 */
339    ret = mapreduce_map(context, &doc2, &meta2, &result);
340    cb_assert(ret == MAPREDUCE_SUCCESS);
341    cb_assert(result != NULL);
342    cb_assert(result->length == 3);
343    cb_assert(result->list != NULL);
344
345    /* function 1 */
346    cb_assert(result->list[0].error == MAPREDUCE_RUNTIME_ERROR);
347    cb_assert(result->list[0].result.error_msg != NULL);
348    cb_assert(strcmp("foobar", result->list[0].result.error_msg) == 0);
349
350    /* function 2 */
351    cb_assert(result->list[1].error == MAPREDUCE_SUCCESS);
352    cb_assert(result->list[1].result.kvs.length == 3);
353    cb_assert(result->list[1].result.kvs.kvs[0].key.length == (sizeof("2") - 1));
354    cb_assert(memcmp(result->list[1].result.kvs.kvs[0].key.json,
355                  "2",
356                  (sizeof("2") - 1)) == 0);
357    cb_assert(result->list[1].result.kvs.kvs[0].value.length == (sizeof("\"doc2\"") - 1));
358    cb_assert(memcmp(result->list[1].result.kvs.kvs[0].value.json,
359                  "\"doc2\"",
360                  (sizeof("\"doc2\"") - 1)) == 0);
361    cb_assert(result->list[1].result.kvs.kvs[1].key.length == (sizeof("[\"doc2\",2]") - 1));
362    cb_assert(memcmp(result->list[1].result.kvs.kvs[1].key.json,
363                  "[\"doc2\",2]",
364                  (sizeof("[\"doc2\",2]") - 1)) == 0);
365    cb_assert(result->list[1].result.kvs.kvs[1].value.length == (sizeof("null") - 1));
366    cb_assert(memcmp(result->list[1].result.kvs.kvs[1].value.json,
367                  "null",
368                  (sizeof("null") - 1)) == 0);
369    cb_assert(result->list[1].result.kvs.kvs[2].key.length == (sizeof("10") - 1));
370    cb_assert(memcmp(result->list[1].result.kvs.kvs[2].key.json,
371                  "10",
372                  (sizeof("10") - 1)) == 0);
373    cb_assert(result->list[1].result.kvs.kvs[2].value.length == (sizeof("-2") - 1));
374    cb_assert(memcmp(result->list[1].result.kvs.kvs[2].value.json,
375                  "-2",
376                  (sizeof("-2") - 1)) == 0);
377
378    /* function 3 */
379    cb_assert(result->list[2].error == MAPREDUCE_SUCCESS);
380    cb_assert(result->list[2].result.kvs.length == 1);
381    cb_assert(result->list[2].result.kvs.kvs[0].key.length == (sizeof("2") - 1));
382    cb_assert(memcmp(result->list[2].result.kvs.kvs[0].key.json,
383                  "2",
384                  (sizeof("2") - 1)) == 0);
385    cb_assert(result->list[2].result.kvs.kvs[0].value.length == (sizeof("0") - 1));
386    cb_assert(memcmp(result->list[2].result.kvs.kvs[0].value.json,
387                  "0",
388                  (sizeof("0") - 1)) == 0);
389
390    mapreduce_free_map_result_list(result);
391
392    /* map doc3 */
393    ret = mapreduce_map(context, &doc3, &meta3, &result);
394    cb_assert(ret == MAPREDUCE_SUCCESS);
395    cb_assert(result != NULL);
396    cb_assert(result->length == 3);
397    cb_assert(result->list != NULL);
398
399    /* function 1 */
400    cb_assert(result->list[0].error == MAPREDUCE_RUNTIME_ERROR);
401    cb_assert(result->list[0].result.error_msg != NULL);
402    cb_assert(strcmp("foobar", result->list[0].result.error_msg) == 0);
403
404    /* function 2 */
405    cb_assert(result->list[1].error == MAPREDUCE_SUCCESS);
406    cb_assert(result->list[1].result.kvs.length == 3);
407    cb_assert(result->list[1].result.kvs.kvs[0].key.length == (sizeof("3") - 1));
408    cb_assert(memcmp(result->list[1].result.kvs.kvs[0].key.json,
409                  "3",
410                  (sizeof("3") - 1)) == 0);
411    cb_assert(result->list[1].result.kvs.kvs[0].value.length == (sizeof("\"doc3\"") - 1));
412    cb_assert(memcmp(result->list[1].result.kvs.kvs[0].value.json,
413                  "\"doc3\"",
414                  (sizeof("\"doc3\"") - 1)) == 0);
415    cb_assert(result->list[1].result.kvs.kvs[1].key.length == (sizeof("[\"doc3\",3]") - 1));
416    cb_assert(memcmp(result->list[1].result.kvs.kvs[1].key.json,
417                  "[\"doc3\",3]",
418                  (sizeof("[\"doc3\",3]") - 1)) == 0);
419    cb_assert(result->list[1].result.kvs.kvs[1].value.length == (sizeof("null") - 1));
420    cb_assert(memcmp(result->list[1].result.kvs.kvs[1].value.json,
421                  "null",
422                  (sizeof("null") - 1)) == 0);
423    cb_assert(result->list[1].result.kvs.kvs[2].key.length == (sizeof("15") - 1));
424    cb_assert(memcmp(result->list[1].result.kvs.kvs[2].key.json,
425                  "15",
426                  (sizeof("15") - 1)) == 0);
427    cb_assert(result->list[1].result.kvs.kvs[2].value.length == (sizeof("-3") - 1));
428    cb_assert(memcmp(result->list[1].result.kvs.kvs[2].value.json,
429                  "-3",
430                  (sizeof("-3") - 1)) == 0);
431
432    /* function 3 */
433    cb_assert(result->list[2].error == MAPREDUCE_RUNTIME_ERROR);
434    cb_assert(result->list[2].result.error_msg != NULL);
435    cb_assert(strcmp("TypeError: Cannot read property 'z' of undefined",
436                  result->list[2].result.error_msg) == 0);
437
438    mapreduce_free_map_result_list(result);
439
440    mapreduce_free_context(context);
441}
442
443
444static void test_timeout(void)
445{
446    void *context = NULL;
447    char *error_msg = NULL;
448    mapreduce_error_t ret;
449    const char *functions[] = {
450        "function(doc, meta) {"
451        "  if (doc.value === 1) {"
452        "    while (true) { };"
453        "  } else {"
454        "    emit(meta.id, doc.value);"
455        "  }"
456        "}"
457    };
458    mapreduce_map_result_list_t *result = NULL;
459
460    ret = mapreduce_start_map_context(functions, 1, &context, &error_msg);
461    cb_assert(ret == MAPREDUCE_SUCCESS);
462    cb_assert(error_msg == NULL);
463    cb_assert(context != NULL);
464
465    ret = mapreduce_map(context, &doc1, &meta1, &result);
466    cb_assert(ret == MAPREDUCE_TIMEOUT);
467    cb_assert(result == NULL);
468
469    ret = mapreduce_map(context, &doc2, &meta2, &result);
470    cb_assert(ret == MAPREDUCE_SUCCESS);
471    cb_assert(result != NULL);
472    cb_assert(result->length == 1);
473    cb_assert(result->list != NULL);
474
475    cb_assert(result->list[0].error == MAPREDUCE_SUCCESS);
476    cb_assert(result->list[0].result.kvs.length == 1);
477    cb_assert(result->list[0].result.kvs.kvs[0].key.length == (sizeof("\"doc2\"") - 1));
478    cb_assert(memcmp(result->list[0].result.kvs.kvs[0].key.json,
479                  "\"doc2\"",
480                  (sizeof("\"doc2\"") - 1)) == 0);
481    cb_assert(result->list[0].result.kvs.kvs[0].value.length == (sizeof("2") - 1));
482    cb_assert(memcmp(result->list[0].result.kvs.kvs[0].value.json,
483                  "2",
484                  (sizeof("2") - 1)) == 0);
485
486    mapreduce_free_map_result_list(result);
487    mapreduce_free_context(context);
488}
489
490void map_tests(void)
491{
492    int i;
493
494    fprintf(stderr, "Running map tests\n");
495
496    mapreduce_set_timeout(1);
497    test_timeout();
498
499    for (i = 0; i < 100; ++i) {
500        test_bad_syntax_functions();
501        test_runtime_exception();
502        test_runtime_error();
503        test_map_no_emit();
504        test_map_single_emit();
505        test_map_multiple_emits();
506    }
507
508    test_timeout();
509}
510