1 /*********************************************************************
2  *   Copyright 2018, University Corporation for Atmospheric Research
3  *   See netcdf/COPYRIGHT file for copying and redistribution conditions.
4  *   $Header: /upc/share/CVS/netcdf-3/nctest/rec.c,v 1.11 2006/10/31 16:21:58 ed Exp $
5  *********************************************************************/
6 
7 #include <config.h>
8 #include <stdio.h>
9 #include <stdlib.h>		/* for free() */
10 #include "netcdf.h"
11 #include "testcdf.h"		/* defines in-memory test cdf structure */
12 #include "emalloc.h"
13 #include "val.h"
14 #include "error.h"
15 #include "tests.h"
16 
17 /*
18  * Returns number of record variables in an open netCDF file, and an array of
19  * the record variable ids, if the array parameter is non-null.  Returns -1 on
20  * error.
21  */
22 static int
numrecvars(ncid,recvarids)23 numrecvars(ncid, recvarids)
24      int ncid;
25      int *recvarids;
26 {
27     int ndims, iv, nvars;
28     int nrecvars;
29     int recdimid;
30     int dimids[MAX_NC_DIMS];
31 
32     if (ncinquire(ncid, 0, &nvars, 0, &recdimid) == -1)
33       return -1;
34     if (recdimid == -1)
35       return 0;
36     nrecvars = 0;
37     for (iv = 0; iv < nvars; iv++) {
38 	if (ncvarinq(ncid, iv, 0, 0, &ndims, dimids, 0) == -1)
39 	  return -1;
40 	if (ndims > 0 && dimids[0] == recdimid) {
41 	    if (recvarids)
42 	      recvarids[nrecvars] = iv;
43 	    nrecvars++;
44 	}
45     }
46     return nrecvars;
47 }
48 
49 
50 /*
51  * Returns record size (in bytes) of the record variable with a specified
52  * variable id.  Returns 0 if not a record variable.  Returns -1 on error.
53  */
54 static long
ncrecsize(ncid,vid)55 ncrecsize(ncid,vid)
56      int ncid;
57      int vid;
58 {
59     int recdimid;
60     nc_type type;
61     int ndims;
62     int dimids[MAX_NC_DIMS];
63     int id;
64     long size;
65 
66     if (ncinquire(ncid, 0, 0, 0, &recdimid) == -1)
67       return -1;
68     if (ncvarinq(ncid, vid, 0, &type, &ndims, dimids, 0) == -1)
69       return -1;
70     if (ndims == 0 || dimids[0] != recdimid)
71       return 0;
72     size = nctypelen(type);
73     for (id = 1; id < ndims; id++) {
74 	long len;
75 	(void) ncdiminq(ncid, dimids[id], 0, &len);
76 	size *= len;
77     }
78     return size;
79 }
80 
81 
82 /*
83  * Retrieves the number of record variables, the record variable ids, and the
84  * record size of each record variable.  If any pointer to info to be returned
85  * is null, the associated information is not returned.  Returns -1 on error.
86  * This is the same as the ncrecinq() in the library, except that can handle
87  * errors better.
88  */
89 static int
recinq(ncid,nrecvars,recvarids,recsizes)90 recinq(ncid, nrecvars, recvarids, recsizes)
91      int ncid;
92      int *nrecvars;
93      int *recvarids;
94      long *recsizes;
95 {
96     int iv;
97     int rvarids[MAX_NC_VARS];
98     int nrvars = numrecvars(ncid, rvarids);
99 
100     if (nrvars == -1)
101       return -1;
102 
103     if (nrecvars)
104       *nrecvars = nrvars;
105     if (recvarids)
106       for (iv = 0; iv < nrvars; iv++)
107 	recvarids[iv] = rvarids[iv];
108     if (recsizes)
109       for (iv = 0; iv < nrvars; iv++)
110 	recsizes[iv] = ncrecsize(ncid, rvarids[iv]);
111     return 0;
112 }
113 
114 
115 /*
116  * Test ncrecinq
117  *    try in both data and define modes
118  *    check returned values against independently computed values
119  *    try with bad netCDF handle, check error
120  */
121 int
test_ncrecinq(path)122 test_ncrecinq(path)
123      const char *path;		/* name of netcdf file to open */
124 {
125     int nerrs = 0;
126     static char pname[] = "test_ncrecinq";
127     int ncid;
128     int nrvars;			/* number of record variables */
129     int rvarids[MAX_NC_VARS];	/* id of each record variable */
130     long rvarsizes[MAX_NC_VARS]; /* record size of each record variable */
131     int tnrvars;		/* true number of record variables */
132     int trvarids[MAX_NC_VARS];	/* true id of each record variable */
133     long trvarsizes[MAX_NC_VARS]; /* true rec size of each record variable */
134     int iv;
135 
136     (void) fprintf(stderr, "*** Testing %s ...\t", &pname[5]);
137 
138     if ((ncid = ncopen(path, NC_WRITE)) == -1) {
139 	error("%s: ncopen failed", pname);
140 	return ++nerrs;
141     }
142 
143     /* First compute independently what ncrecinq should return */
144     if (recinq(ncid, &tnrvars, trvarids, trvarsizes) == -1) {
145 	error("%s: recinq failed", pname);
146 	ncclose(ncid);
147 	return ++nerrs;
148     }
149 
150     /* check that ncrecinq() returns correct information in data mode */
151     if (ncrecinq(ncid, &nrvars, rvarids, rvarsizes) == -1) {
152 	error("%s: ncrecinq failed", pname);
153 	ncclose(ncid);
154 	return ++nerrs;
155     }
156 
157     if (nrvars != tnrvars) {
158 	error("ncrecinq returned wrong number of rec vars, %d != %d",
159 	      nrvars, tnrvars);
160 	nerrs++;
161     }
162 
163     for (iv = 0; iv < nrvars; iv++) {
164 	if (rvarids[iv] != trvarids[iv]) {
165 	    error("ncrecinq returned wrong record id for var %d",
166 		  trvarids[iv]);
167 	    nerrs++;
168 	}
169 	if (rvarsizes[iv] != trvarsizes[iv]) {
170 	    error("ncrecinq returned wrong record size for var %d",
171 		  trvarids[iv]);
172 	    nerrs++;
173 	}
174     }
175 
176     if (ncredef(ncid) == -1) {
177 	error("%s: ncredef failed", pname);
178 	ncclose(ncid);
179 	return ++nerrs;
180     }
181     /* check that ncrecinq() returns correct information in define mode too */
182     if (ncrecinq(ncid, &nrvars, rvarids, rvarsizes) == -1) {
183 	error("%s: ncrecinq failed in define mode", pname);
184 	ncclose(ncid);
185 	return ++nerrs;
186     }
187     if (nrvars != tnrvars) {
188 	error("define mode, ncrecinq returned wrong num of rec vars, %d != %d",
189 	      nrvars, tnrvars);
190 	nerrs++;
191     }
192     for (iv = 0; iv < nrvars; iv++) {
193 	if (rvarids[iv] != trvarids[iv]) {
194 	    error("define mode, ncrecinq returned wrong record id for var %d",
195 		  trvarids[iv]);
196 	    nerrs++;
197 	}
198 	if (rvarsizes[iv] != trvarsizes[iv]) {
199 	    error("define mode, ncrecinq returned wrong rec size for var %d",
200 		  trvarids[iv]);
201 	    nerrs++;
202 	}
203     }
204 
205     if (ncclose (ncid) == -1) {
206 	error("%s: ncclose failed", pname);
207 	return ++nerrs;
208     }
209 
210     if (ncrecinq(ncid, &nrvars, rvarids, rvarsizes) != -1) {
211 	error("%s: ncrecinq failed to report bad handle", pname);
212 	nerrs++;
213     }
214 
215     if (nerrs > 0)
216       (void) fprintf(stderr,"FAILED! ***\n");
217     else
218       (void) fprintf(stderr,"ok ***\n");
219     return nerrs;
220 }
221 
222 
223 /*
224  * Retrieves the dimension sizes of a variable with a specified variable id in
225  * an open netCDF file.  Returns -1 on error.
226  */
227 static int
dimsizes(ncid,varid,sizes)228 dimsizes(ncid, varid, sizes)
229      int ncid;
230      int varid;
231      long *sizes;
232 {
233     int ndims;
234     int id;
235     int dimids[MAX_NC_DIMS];
236 
237     if (ncvarinq(ncid, varid, 0, 0, &ndims, dimids, 0) == -1)
238       return -1;
239     if (ndims == 0 || sizes == 0)
240       return 0;
241     for (id = 0; id < ndims; id++)
242       (void) ncdiminq(ncid, dimids[id], 0, &sizes[id]);
243     return 0;
244 }
245 
246 
247 /*
248  * Write one record's worth of data, except don't write to variables for which
249  * the address of the data to be written is NULL.  Return -1 on error.  This is
250  * the same as the ncrecput() in the library, except that can handle errors
251  * better.
252  */
253 static int
recput(ncid,recnum,datap)254 recput(ncid, recnum, datap)
255      int ncid;
256      long recnum;
257      void **datap;
258 {
259     int iv;
260     int rvids[MAX_NC_VARS];
261     int nrvars = numrecvars(ncid, rvids);
262     long start[MAX_NC_DIMS];
263     long edges[MAX_NC_DIMS];
264 
265     if (nrvars == -1)
266       return -1;
267 
268     start[0] = recnum;
269     for (iv = 1; iv < nrvars; iv++)
270 	start[iv] = 0;
271 
272     for (iv = 0; iv < nrvars; iv++) {
273 	if (datap[iv] != 0) {
274 	    (void) dimsizes(ncid, rvids[iv], edges);
275 	    edges[0] = 1;		/* only 1 record's worth */
276 	    if (ncvarput(ncid, rvids[iv], start, edges, datap[iv]) == -1)
277 	      return -1;
278 	}
279     }
280     return 0;
281 }
282 
283 
284 /*
285  * Read one record's worth of data, except don't read from variables for which
286  * the address of the data to be read is null.  Return -1 on error.  This is
287  * the same as the ncrecget() in the library, except that can handle errors
288  * better.
289  */
290 static int
recget(ncid,recnum,datap)291 recget(ncid, recnum, datap)
292      int ncid;
293      long recnum;
294      void **datap;
295 {
296     int iv;
297     int rvids[MAX_NC_VARS];
298     int nrvars = numrecvars(ncid, rvids);
299     long start[MAX_NC_DIMS];
300     long edges[MAX_NC_DIMS];
301 
302     if (nrvars == -1)
303       return -1;
304 
305     start[0] = recnum;
306     for (iv = 1; iv < nrvars; iv++)
307 	start[iv] = 0;
308 
309     for (iv = 0; iv < nrvars; iv++) {
310 	if (datap[iv] != 0) {
311 	    (void) dimsizes(ncid, rvids[iv], edges);
312 	    edges[0] = 1;		/* only 1 record's worth */
313 	    if (ncvarget(ncid, rvids[iv], start, edges, datap[iv]) == -1)
314 	      return -1;
315 	}
316     }
317     return 0;
318 }
319 
320 
321 /*
322  * Test ncrecput
323  *    check that proper call works putting all recoerd variables
324  *    try putting only a proper subset of variables
325  *    try putting the empty subset of variables
326  *    try in define mode, check error
327  *    try with bad netCDF handle, check error
328  */
329 int
test_ncrecput(path)330 test_ncrecput(path)
331      const char *path;		/* name of writable netcdf file to open */
332 {
333     int nerrs = 0;
334     static char pname[] = "test_ncrecput";
335     int nrvars;			/* number of record variables */
336     int rvarids[MAX_NC_VARS];	/* id of each record variable */
337     long rvarsizes[MAX_NC_VARS]; /* record size of each record variable */
338     int ncid;			/* netcdf id */
339     void *datap[MAX_NC_VARS];	/* array of address pointers for rec vars */
340     void *datar[MAX_NC_VARS];	/* pointers for comparison data */
341     long recnum = 1;		/* we'll write the second record */
342     int iv;
343     long recsize[MAX_NC_VARS];	/* record size in data elements */
344     nc_type vartype[MAX_NC_VARS];
345     void *zeros[MAX_NC_VARS];
346 
347     (void) fprintf(stderr, "*** Testing %s ...\t", &pname[5]);
348 
349     if ((ncid = ncopen(path, NC_WRITE)) == -1) {
350 	error("%s: ncopen failed", pname);
351 	return ++nerrs;
352     }
353 
354     if (ncrecinq(ncid, &nrvars, rvarids, rvarsizes) == -1) {
355 	error("%s: ncrecinq failed", pname);
356 	ncclose(ncid);
357 	return ++nerrs;
358     }
359 
360     /* get a block of data of the right type for each record variable */
361     for (iv = 0; iv < nrvars; iv++) {
362 	datap[iv] = emalloc(rvarsizes[iv]);
363 	datar[iv] = emalloc(rvarsizes[iv]); /* for comparison values */
364 	if (ncvarinq(ncid, rvarids[iv], 0, &vartype[iv], 0, 0, 0) == -1) {
365 	    error("%s: ncvarinq failed", pname);
366 	    ncclose(ncid);
367 	    return ++nerrs;
368 	}
369 	recsize[iv] = rvarsizes[iv]/nctypelen(vartype[iv]);
370 	/* Fill data blocks with 0,1,2,3,... */
371 	val_fill(vartype[iv], recsize[iv], datap[iv]);
372 	/* Zero out comparison data */
373 	val_fill_zero(vartype[iv], recsize[iv], datar[iv]);
374     }
375 
376     /* Zero data in recnum record, before trying to put non-zero data */
377     if (recput(ncid, recnum, datar) == -1) {
378 	error("%s: recput failed", pname);
379 	ncclose(ncid);
380 	return ++nerrs;
381     }
382 
383     /* opened in data mode, try putting a complete record */
384     if (ncrecput(ncid, recnum, datap) == -1) {
385 	error("%s: ncrecput failed on complete record", pname);
386 	nerrs++;
387     }
388 
389     /* Check that right values were put */
390     if (recget(ncid, recnum, datar) == -1) {
391 	error("%s: recget failed", pname);
392 	nerrs++;
393     }
394     for (iv = 0; iv < nrvars; iv++) {
395 	if (val_cmp(vartype[iv], recsize[iv], datap[iv], datar[iv]) != 0) {
396 	    error("%s: bad values written by recput", pname);
397 	    nerrs++;
398 	}
399 	val_fill_zero(vartype[iv], recsize[iv], datap[iv]);
400 	val_fill_zero(vartype[iv], recsize[iv], datar[iv]);
401 	zeros[iv] = 0;
402     }
403 
404     if (nrvars > 0) {
405 	void *datap0 = datap[0];
406 
407 	/* put a partial record, everything but first record variable */
408 	datap[0] = 0;
409 	val_fill(vartype[0], recsize[0], datar[0]);
410 	if (ncrecput(ncid, recnum, datap) == -1) {
411 	    error("%s: ncrecput failed on partial record", pname);
412 	    nerrs++;
413 	}
414 
415 	/* Check right values were put, first record variable undisturbed */
416 	datap[0] = datap0;
417 	if (recget(ncid, recnum, datap) == -1) {
418 	    error("%s: recget failed after partial record put", pname);
419 	    nerrs++;
420 	}
421 	for (iv = 0; iv < nrvars; iv++) {
422 	    if (val_cmp(vartype[iv], recsize[iv], datap[iv], datar[iv]) != 0) {
423 		error("%s: bad values written by partial recput", pname);
424 		nerrs++;
425 	    }
426 	}
427     }
428 
429     /* Put an empty record, check that values remain undisturbed */
430     if (ncrecput(ncid, recnum, zeros) == -1) {
431 	error("%s: ncrecput failed on empty record", pname);
432 	nerrs++;
433     }
434     if (recget(ncid, recnum, datap) == -1) {
435 	error("%s: recget failed after empty record put", pname);
436 	nerrs++;
437     }
438     for (iv = 0; iv < nrvars; iv++) {
439 	if (val_cmp(vartype[iv], recsize[iv], datap[iv], datar[iv]) != 0) {
440 	    error("%s: bad values written by empty recput", pname);
441 	    nerrs++;
442 	}
443     }
444 
445     /* try in define mode, check error */
446     if (ncredef(ncid) == -1) {
447 	error("%s: ncredef failed", pname);
448  	ncclose(ncid);
449 	return ++nerrs;
450     }
451 
452     if (ncrecput(ncid, recnum, datap) != -1) {
453 	error("%s: ncrecput should fail in define mode", pname);
454 	nerrs++;
455     }
456 
457     /* try with bad netCDF handle, check error */
458     if (ncclose (ncid) == -1) {
459 	error("%s: ncclose failed", pname);
460 	return ++nerrs;
461     }
462     if (ncrecput(ncid, recnum, datap) != -1) {
463 	error("%s: ncrecput failed to report bad handle", pname);
464 	nerrs++;
465     }
466     for (iv = 0; iv < nrvars; iv++) {
467 	free(datap[iv]);
468 	free(datar[iv]);
469     }
470 
471     if (nerrs > 0)
472       (void) fprintf(stderr,"FAILED! ***\n");
473     else
474       (void) fprintf(stderr,"ok ***\n");
475 
476     return nerrs;
477 }
478 
479 
480 /*
481  * Test ncrecget
482  *    check that proper call works getting all record variables
483  *    try getting only a proper subset of variables
484  *    try getting the empty subset of variables
485  *    try with bad netCDF handle, check error
486  */
487 int
test_ncrecget(path)488 test_ncrecget(path)
489      const char *path;		/* name of netcdf file to open */
490 {
491     int nerrs = 0;
492     static char pname[] = "test_ncrecget";
493     int nrvars;			/* number of record variables */
494     int rvarids[MAX_NC_VARS];	/* id of each record variable */
495     long rvarsizes[MAX_NC_VARS]; /* record size of each record variable */
496     int ncid;			/* netcdf id */
497     void *datap[MAX_NC_VARS];	/* array of address pointers for rec vars */
498     void *datar[MAX_NC_VARS];	/* pointers for comparison data */
499     long recnum = 1;		/* we'll write the second record */
500     int iv;
501     long recsize[MAX_NC_VARS];	/* record size in data elements */
502     nc_type vartype[MAX_NC_VARS];
503     void *zeros[MAX_NC_VARS];
504 
505     (void) fprintf(stderr, "*** Testing %s ...\t", &pname[5]);
506 
507     if ((ncid = ncopen(path, NC_WRITE)) == -1) {
508 	error("%s: ncopen failed", pname);
509 	return ++nerrs;
510     }
511 
512     if (ncrecinq(ncid, &nrvars, rvarids, rvarsizes) == -1) {
513 	error("%s: ncrecinq failed", pname);
514 	ncclose(ncid);
515 	return ++nerrs;
516     }
517 
518     /* get a block of data of the right type for each record variable */
519     for (iv = 0; iv < nrvars; iv++) {
520 	datap[iv] = emalloc(rvarsizes[iv]);
521 	datar[iv] = emalloc(rvarsizes[iv]); /* for comparison values */
522 	if (ncvarinq(ncid, rvarids[iv], 0, &vartype[iv], 0, 0, 0) == -1) {
523 	    error("%s: ncvarinq failed", pname);
524 	    ncclose(ncid);
525 	    return ++nerrs;
526 	}
527 	recsize[iv] = rvarsizes[iv]/nctypelen(vartype[iv]);
528 	/* Fill data blocks with 0,1,2,3,... */
529 	val_fill(vartype[iv], recsize[iv], datap[iv]);
530 	/* Zero out comparison data */
531 	val_fill_zero(vartype[iv], recsize[iv], datar[iv]);
532     }
533 
534     if (recput(ncid, recnum, datap) == -1) {
535 	error("%s: recput failed", pname);
536 	ncclose(ncid);
537 	return ++nerrs;
538     }
539 
540     /* opened in data mode, try getting a complete record */
541     if (recget(ncid, recnum, datap) == -1) {
542 	error("%s: recget failed on complete record", pname);
543 	nerrs++;
544     }
545     if (ncrecget(ncid, recnum, datar) == -1) {
546 	error("%s: ncrecget failed on complete record", pname);
547 	nerrs++;
548     }
549 
550     for (iv = 0; iv < nrvars; iv++) {
551 	if (val_cmp(vartype[iv], recsize[iv], datap[iv], datar[iv]) != 0) {
552 	    error("%s: bad values written by recget", pname);
553 	    nerrs++;
554 	}
555 	val_fill_zero(vartype[iv], recsize[iv], datap[iv]);
556 	val_fill_zero(vartype[iv], recsize[iv], datar[iv]);
557 	zeros[iv] = 0;
558     }
559 
560     if (nrvars > 0) {
561 	void *datap0 = datap[0];
562 	void *datar0 = datar[0];
563 
564 	/* get a partial record, everything but first record variable */
565 	datap[0] = 0;
566 	if (ncrecget(ncid, recnum, datap) == -1) {
567 	    error("%s: ncrecget failed on partial record", pname);
568 	    nerrs++;
569 	}
570 	datar[0] = 0;
571 	if (recget(ncid, recnum, datar) == -1) {
572 	    error("%s: recget failed on partial record", pname);
573 	    nerrs++;
574 	}
575 	/* Check right values were got, first record variable undisturbed */
576 	datap[0] = datap0;
577 	datar[0] = datar0;
578 	for (iv = 0; iv < nrvars; iv++) {
579 	    if (val_cmp(vartype[iv], recsize[iv], datap[iv], datar[iv]) != 0) {
580 		error("%s: bad values read by partial recget", pname);
581 		nerrs++;
582 	    }
583 	}
584     }
585 
586     /* Get an empty record */
587     if (ncrecget(ncid, recnum, zeros) == -1) {
588 	error("%s: ncrecget failed on empty record", pname);
589 	nerrs++;
590     }
591 
592     /* try with bad netCDF handle, check error */
593     if (ncclose (ncid) == -1) {
594 	error("%s: ncclose failed", pname);
595 	return ++nerrs;
596     }
597     if (ncrecget(ncid, recnum, datap) != -1) {
598 	error("%s: ncrecget failed to report bad handle", pname);
599 	nerrs++;
600     }
601     for (iv = 0; iv < nrvars; iv++) {
602 	free(datap[iv]);
603 	free(datar[iv]);
604     }
605 
606     if (nerrs > 0)
607       (void) fprintf(stderr,"FAILED! ***\n");
608     else
609       (void) fprintf(stderr,"ok ***\n");
610 
611     return nerrs;
612 }
613 
614