~stub/ubuntu/trusty/avro-c/trunk

« back to all changes in this revision

Viewing changes to tests/test_avro_984.c

  • Committer: Stuart Bishop
  • Date: 2015-05-14 11:53:53 UTC
  • Revision ID: stuart@stuartbishop.net-20150514115353-0cvnrcyohcq5l7yj
Tags: upstream-1.7.7
ImportĀ upstreamĀ versionĀ 1.7.7

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * Licensed to the Apache Software Foundation (ASF) under one or more
 
3
 * contributor license agreements.  See the NOTICE file distributed with
 
4
 * this work for additional information regarding copyright ownership.
 
5
 * The ASF licenses this file to you under the Apache License, Version 2.0
 
6
 * (the "License"); you may not use this file except in compliance with
 
7
 * the License.  You may obtain a copy of the License at
 
8
 *
 
9
 * http://www.apache.org/licenses/LICENSE-2.0
 
10
 *
 
11
 * Unless required by applicable law or agreed to in writing, software
 
12
 * distributed under the License is distributed on an "AS IS" BASIS,
 
13
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
 
14
 * implied.  See the License for the specific language governing
 
15
 * permissions and limitations under the License.
 
16
 */
 
17
 
 
18
#include <avro.h>
 
19
#include <stdio.h>
 
20
#include <stdlib.h>
 
21
 
 
22
 
 
23
/* Test code for JIRA Issue AVRO-984. 
 
24
 * 
 
25
 * AVRO-984: Avro-C schema resolution fails on nested array
 
26
 * 
 
27
 * This program tests schema resolution for nested arrays. For the
 
28
 * purposes of this test, there are two schemas "old" and "new" which
 
29
 * are created by reading the same JSON schema.
 
30
 * 
 
31
 * The test creates and populates a nested array, and serializes it to
 
32
 * memory. The raw memory is written to a file, primarily to decouple
 
33
 * writing and reading. Note that the schema is not written to the
 
34
 * file. The nested array is also printed to the screen. 
 
35
 * 
 
36
 * The binary file is then read using two separate readers -- the
 
37
 * matched reader and the resolved reader.
 
38
 * 
 
39
 * In the matched reader case, the "old" and "new" schemas are known
 
40
 * to match, and therefore no schema resolution is done. The binary
 
41
 * buffer is deserialized into an avro value and the nested array
 
42
 * encoded in the avro value is printed to the screen. 
 
43
 * 
 
44
 * In the resolved reader case, the "old" and "new" schemas are not
 
45
 * known to match, and therefore schema resolution is performed. (Note
 
46
 * that the schemas *do* match, but we perform schema resolution
 
47
 * anyway, to test the resolution process). The schema resolution
 
48
 * appears to succeed. However, once the code tries to perform an
 
49
 * "avro_value_read()" the code fails to read the nested array into
 
50
 * the avro value.
 
51
 * 
 
52
 * Additionally valgrind indicates that conditional jumps are being
 
53
 * performed based on uninitialized values. 
 
54
 * 
 
55
 * AVRO-C was compiled with CMAKE_INSTALL_PREFIX=avrolib
 
56
 * The static library (libavro.a) was copied into a subdirectory of avrolib/lib/static
 
57
 * 
 
58
 * This file was compiled under Linux using:
 
59
 *   gcc -g avro-984-test.c -o avro984 -I../../build/avrolib/include -L../../build/avrolib/lib/static -lavro
 
60
 * 
 
61
 * The code was tested with valgrind using the command:
 
62
 *   valgrind -v --leak-check=full --track-origins=yes ./avro984
 
63
 * 
 
64
 */
 
65
 
 
66
 
 
67
// Encode the following json string in NESTED_ARRAY
 
68
// {"type":"array", "items": {"type": "array", "items": "long"}}
 
69
// 
 
70
#define NESTED_ARRAY \
 
71
  "{\"type\":\"array\", \"items\": {\"type\": \"array\", \"items\": \"long\"}}"
 
72
 
 
73
avro_schema_t schema_old = NULL;
 
74
avro_schema_t schema_new = NULL;
 
75
 
 
76
/* Parse schema into a schema data structure */
 
77
void init_schema(void)
 
78
{
 
79
  avro_schema_error_t error;
 
80
  if (avro_schema_from_json(NESTED_ARRAY, sizeof(NESTED_ARRAY),
 
81
                            &schema_old, &error)) {
 
82
    printf( "Unable to parse old schema\n");
 
83
    exit(EXIT_FAILURE);
 
84
  }
 
85
 
 
86
  if (avro_schema_from_json(NESTED_ARRAY, sizeof(NESTED_ARRAY),
 
87
                            &schema_new, &error)) {
 
88
    printf( "Unable to parse new schema\n");
 
89
    exit(EXIT_FAILURE);
 
90
  }
 
91
}
 
92
 
 
93
#define try(call, msg) \
 
94
        do { \
 
95
                if (call) { \
 
96
                        printf( msg ":\n  %s\n", avro_strerror()); \
 
97
                        exit (EXIT_FAILURE);                       \
 
98
                } \
 
99
        } while (0)
 
100
 
 
101
 
 
102
/* The input avro_value_t p_array should contain a nested array.
 
103
 * Print the fields of this nested array to the screen.
 
104
 */
 
105
int print_array_fields ( avro_value_t *p_array )
 
106
{
 
107
  size_t idx;
 
108
  size_t length;
 
109
  avro_type_t val_type;
 
110
 
 
111
  val_type = avro_value_get_type( p_array );
 
112
  printf( "Main array type = %d\n", val_type );
 
113
 
 
114
  try( avro_value_get_size( p_array, &length ),
 
115
       "Couldn't get array size" );
 
116
  printf( "Main array length = %d\n", (int) length );
 
117
  
 
118
  for ( idx = 0; idx < length; idx ++ )
 
119
  {
 
120
    avro_value_t subarray;
 
121
    size_t sublength;
 
122
    size_t jdx;
 
123
    const char *unused;
 
124
    
 
125
    try ( avro_value_get_by_index( p_array, idx, &subarray, &unused ),
 
126
          "Couldn't get subarray" );
 
127
 
 
128
    val_type = avro_value_get_type( &subarray );
 
129
    printf( "Subarray type = %d\n", val_type );
 
130
 
 
131
    try( avro_value_get_size( &subarray, &sublength ),
 
132
         "Couldn't get subarray size" );
 
133
    printf( "Subarray length = %d\n", (int) sublength );
 
134
 
 
135
    for ( jdx = 0; jdx < sublength; jdx++ )
 
136
    {
 
137
      avro_value_t element;
 
138
      int64_t val;
 
139
 
 
140
      try ( avro_value_get_by_index( &subarray, jdx, &element, &unused  ),
 
141
            "Couldn't get subarray element" );
 
142
 
 
143
      val_type = avro_value_get_type( &element );
 
144
 
 
145
      try ( avro_value_get_long( &element, &val ),
 
146
            "Couldn't get subarray element value" );
 
147
 
 
148
      printf( "nested_array[%d][%d]: type = %d value = %lld\n", 
 
149
              (int) idx, (int) jdx, (int) val_type, (long long) val );
 
150
 
 
151
    }
 
152
  }
 
153
 
 
154
  return 0;
 
155
}
 
156
 
 
157
 
 
158
/* The input avro_value_t p_subarray should contain an array of long
 
159
 * integers. Add "elements" number of long integers to this array. Set
 
160
 * the values to be distinct based on the iteration parameter.
 
161
 */
 
162
int add_subarray( avro_value_t *p_subarray,
 
163
                  int32_t elements, 
 
164
                  int32_t iteration )
 
165
{
 
166
  avro_value_t element;
 
167
  size_t index;
 
168
  size_t idx;
 
169
 
 
170
  for ( idx = 0; idx < (size_t) elements; idx ++ )
 
171
  {
 
172
    // Append avro array element to subarray
 
173
    try ( avro_value_append( p_subarray, &element, &index ),
 
174
          "Error appending element in subarray" );
 
175
 
 
176
    try ( avro_value_set_long( &element, (iteration+1)*100 + (iteration+1) ),
 
177
          "Error setting subarray element" );
 
178
  }
 
179
 
 
180
  return 0;
 
181
}
 
182
 
 
183
 
 
184
/* Create a nested array using the schema NESTED_ARRAY. Populate its
 
185
 * elements with unique values. Serialize the nested array to the
 
186
 * memory buffer in avro_writer_t. The number of elements in the first
 
187
 * dimension of the nested array is "elements". The number of elements
 
188
 * in the second dimension of the nested array is hardcoded to 2.
 
189
 */
 
190
int add_array( avro_writer_t writer, 
 
191
               int32_t elements )
 
192
{
 
193
  avro_schema_t chosen_schema;
 
194
  avro_value_iface_t *nested_array_class;
 
195
  avro_value_t nested;
 
196
  int32_t idx;
 
197
 
 
198
  // Select (hardcode) schema to use
 
199
  chosen_schema = schema_old;
 
200
 
 
201
  // Create avro class and value
 
202
  nested_array_class = avro_generic_class_from_schema( chosen_schema );
 
203
  try ( avro_generic_value_new( nested_array_class, &nested ), 
 
204
        "Error creating instance of record" );
 
205
 
 
206
  for ( idx = 0; idx < elements; idx ++ )
 
207
  {
 
208
    avro_value_t subarray;
 
209
    size_t index;
 
210
 
 
211
    // Append avro array element for top level array
 
212
    try ( avro_value_append( &nested, &subarray, &index ),
 
213
          "Error appending subarray" );
 
214
 
 
215
    // Populate array element with subarray of length 2
 
216
#define SUBARRAY_LENGTH (2)
 
217
    try ( add_subarray( &subarray, SUBARRAY_LENGTH, idx ),
 
218
          "Error populating subarray" );
 
219
  }
 
220
 
 
221
  // Write the value to memory
 
222
  try ( avro_value_write( writer, &nested ),
 
223
        "Unable to write nested into memory" );
 
224
 
 
225
  print_array_fields( &nested );
 
226
 
 
227
  // Release the record
 
228
  avro_value_decref( &nested );
 
229
  avro_value_iface_decref( nested_array_class );
 
230
 
 
231
  return 0;
 
232
}
 
233
 
 
234
/* Create a raw binary file containing a serialized version of a
 
235
 * nested array. This file will later be read by
 
236
 * read_nested_array_file().
 
237
 */
 
238
int write_nested_array_file ( int64_t buf_len, const char *raw_binary_file_name )
 
239
{
 
240
  char *buf;
 
241
  avro_writer_t nested_writer;
 
242
  FILE *fid = NULL;
 
243
 
 
244
  fprintf( stdout, "Create %s\n", raw_binary_file_name );
 
245
 
 
246
  // Allocate a buffer
 
247
  buf = (char *) malloc( buf_len * sizeof( char ) );
 
248
  if ( buf == NULL )
 
249
  {
 
250
    printf( "There was an error creating the nested buffer %s.\n", raw_binary_file_name);
 
251
    exit(EXIT_FAILURE);
 
252
  }
 
253
 
 
254
  /* Create a new memory writer */
 
255
  nested_writer = avro_writer_memory( buf, buf_len );
 
256
  if ( nested_writer == NULL )
 
257
  {
 
258
    printf( "There was an error creating the buffer for writing %s.\n", raw_binary_file_name);
 
259
    exit(EXIT_FAILURE);
 
260
  }
 
261
 
 
262
  /* Add an array containing 4 subarrays */
 
263
  printf( "before avro_writer_tell %d\n", (int) avro_writer_tell( nested_writer ) );
 
264
#define ARRAY_LENGTH (4)
 
265
  add_array( nested_writer, ARRAY_LENGTH );
 
266
  printf( "after avro_writer_tell %d\n", (int) avro_writer_tell( nested_writer ) );
 
267
 
 
268
  /* Serialize the nested array */
 
269
  printf( "Serialize the data to a file\n");
 
270
 
 
271
  /* Delete the nested array if it exists, and create a new one */
 
272
  remove(raw_binary_file_name);
 
273
  fid = fopen( raw_binary_file_name, "w+");
 
274
  if ( fid == NULL )
 
275
  {
 
276
    printf( "There was an error creating the file %s.\n", raw_binary_file_name);
 
277
    exit(EXIT_FAILURE);
 
278
  }
 
279
  fwrite( buf, 1, avro_writer_tell( nested_writer ), fid );
 
280
  fclose(fid);
 
281
  avro_writer_free( nested_writer );
 
282
  free(buf);
 
283
  return 0;
 
284
}
 
285
 
 
286
 
 
287
/* Read the raw binary file containing a serialized version of a
 
288
 * nested array, written by write_nested_array_file()
 
289
 */
 
290
int read_nested_array_file ( int64_t buf_len, 
 
291
                             const char *raw_binary_file_name, 
 
292
                             avro_schema_t writer_schema,
 
293
                             avro_schema_t reader_schema,
 
294
                             int use_resolving_reader
 
295
                           )
 
296
{
 
297
 
 
298
  char *buf;
 
299
  FILE *fid = NULL;
 
300
  avro_reader_t nested_reader;
 
301
  int64_t file_len;
 
302
 
 
303
  // For Matched Reader and Resolving Reader
 
304
  avro_value_iface_t *reader_class;
 
305
  avro_value_t nested;
 
306
  
 
307
  // For Resolving Reader
 
308
  avro_value_iface_t *resolver;
 
309
  avro_value_t resolved_value;
 
310
 
 
311
  fprintf( stdout, "Use %s reader\n", use_resolving_reader ? "Resolving":"Matched" );
 
312
 
 
313
  // Allocate a buffer
 
314
  buf = (char *) calloc( buf_len, sizeof( char ) );
 
315
  if ( buf == NULL )
 
316
  {
 
317
    printf( "There was an error creating the buffer for reading %s.\n", raw_binary_file_name);
 
318
    exit(EXIT_FAILURE);
 
319
  }
 
320
  // Start with a garbage buffer
 
321
  memset(buf, 0xff, buf_len );
 
322
 
 
323
  // Read the file into the buffer
 
324
  fid = fopen( raw_binary_file_name, "r" );
 
325
  if ( fid == NULL )
 
326
  {
 
327
    printf( "There was an error reading the file %s.\n", raw_binary_file_name);
 
328
    exit(EXIT_FAILURE);
 
329
  }
 
330
  file_len = fread( buf, 1, buf_len, fid );
 
331
  printf( "Read %d bytes\n", (int) file_len );
 
332
  fclose(fid);
 
333
 
 
334
  if ( use_resolving_reader )
 
335
  {
 
336
    // Resolving Reader
 
337
 
 
338
    /* First resolve the writer and reader schemas */
 
339
    resolver = avro_resolved_writer_new( writer_schema, reader_schema );
 
340
    if ( !resolver )
 
341
    {
 
342
      printf( "Could not create resolver\n");
 
343
      free(buf);
 
344
      exit(EXIT_FAILURE);
 
345
    }
 
346
 
 
347
    /* Create a value that the resolver can write into. This is just
 
348
     * an interface value, that is not directly read from.
 
349
     */
 
350
    if ( avro_resolved_writer_new_value( resolver, &resolved_value ) )
 
351
    {
 
352
      avro_value_iface_decref( resolver );
 
353
      free(buf);      
 
354
      exit(EXIT_FAILURE);
 
355
    }
 
356
 
 
357
    /* Then create the value with the reader schema, that we are going
 
358
     * to use to read from.
 
359
     */
 
360
    reader_class = avro_generic_class_from_schema(reader_schema);
 
361
    try ( avro_generic_value_new( reader_class, &nested ),
 
362
          "Error creating instance of nested array" );
 
363
 
 
364
    // When we read the memory using the resolved writer, we want to
 
365
    // populate the instance of the value with the reader schema. This
 
366
    // is done by set_dest.
 
367
    avro_resolved_writer_set_dest(&resolved_value, &nested);
 
368
 
 
369
    // Create a memory reader
 
370
    nested_reader = avro_reader_memory( buf, buf_len );
 
371
 
 
372
    if ( avro_value_read( nested_reader, &resolved_value ) )
 
373
    {
 
374
      printf( "Avro value read failed\n" );
 
375
 
 
376
      avro_value_decref( &nested );
 
377
      avro_value_iface_decref( reader_class );
 
378
      avro_value_iface_decref( resolver );
 
379
      avro_value_decref( &resolved_value );
 
380
 
 
381
      exit(EXIT_FAILURE);
 
382
    }
 
383
  }
 
384
  else
 
385
  {
 
386
    // Matched Reader
 
387
    reader_class = avro_generic_class_from_schema(reader_schema);
 
388
 
 
389
    try ( avro_generic_value_new( reader_class, &nested ),
 
390
          "Error creating instance of nested array" );
 
391
 
 
392
    // Send the memory in the buffer into the reader
 
393
    nested_reader = avro_reader_memory( buf, buf_len );
 
394
 
 
395
    try ( avro_value_read( nested_reader, &nested ),
 
396
          "Could not read value from memory" );
 
397
  }
 
398
 
 
399
 
 
400
  /* Now the resolved record has been read into "nested" which is
 
401
   * a value of type reader_class
 
402
   */
 
403
  print_array_fields( &nested );
 
404
 
 
405
  if ( use_resolving_reader )
 
406
  {
 
407
    // Resolving Reader
 
408
    avro_value_decref( &nested );
 
409
    avro_value_iface_decref( reader_class );
 
410
    avro_value_iface_decref( resolver );
 
411
    avro_value_decref( &resolved_value );
 
412
  }
 
413
  else
 
414
  {
 
415
    // Matched Reader
 
416
    avro_value_decref( &nested );    
 
417
    avro_value_iface_decref( reader_class );
 
418
  }
 
419
 
 
420
  fprintf( stdout, "Done.\n\n");
 
421
  avro_reader_free( nested_reader );
 
422
  free(buf);
 
423
  return 0;
 
424
}
 
425
 
 
426
 
 
427
/* Top level function to impelement a test for the JIRA issue
 
428
 * AVRO-984. See detailed documentation at the top of this file.
 
429
 */
 
430
int main(void)
 
431
{
 
432
  const char *raw_binary_file_name = "nested_array.bin";
 
433
  int64_t buf_len = 2048;
 
434
  int use_resolving_reader;
 
435
 
 
436
  /* Initialize the schema structure from JSON */
 
437
  init_schema();
 
438
 
 
439
  printf( "Write the serialized nested array to %s\n", raw_binary_file_name );
 
440
 
 
441
  write_nested_array_file( buf_len, raw_binary_file_name );
 
442
 
 
443
  printf("\nNow read all the array back out\n\n");
 
444
 
 
445
  for ( use_resolving_reader = 0; use_resolving_reader < 2; use_resolving_reader++ )
 
446
  {
 
447
    read_nested_array_file( buf_len, 
 
448
                            raw_binary_file_name,
 
449
                            schema_old,
 
450
                            schema_new,
 
451
                            use_resolving_reader
 
452
                          );
 
453
  }
 
454
 
 
455
  // Close out schemas
 
456
  avro_schema_decref(schema_old);
 
457
  avro_schema_decref(schema_new);
 
458
 
 
459
  // Remove the binary file
 
460
  remove(raw_binary_file_name);
 
461
  
 
462
  printf("\n");
 
463
  return 0;
 
464
}