1 /*********************************************************************
2  *   Copyright 2018, UCAR/Unidata
3  *   See netcdf/COPYRIGHT file for copying and redistribution conditions.
4  *   $Header: /upc/share/CVS/netcdf-3/nctest/vardef.c,v 1.18 2009/10/28 18:30:50 dmh Exp $
5  *********************************************************************/
6 
7 #include <config.h>
8 #include <stdio.h>
9 #include <string.h>
10 #include <stdlib.h>		/* for free() */
11 #ifndef NO_FLOAT_H
12 #include <float.h>		/* for FLT_EPSILON, DBL_EPSILON */
13 #endif /* NO_FLOAT_H */
14 #include <netcdf.h>
15 #include "testcdf.h"		/* defines in-memory test cdf structure */
16 #include "emalloc.h"
17 #include "add.h"		/* functions to update in-memory netcdf */
18 #include "error.h"
19 #include "tests.h"
20 
21 #define LEN_OF(array) ((sizeof array) / (sizeof array[0]))
22 
23 static float float_eps;
24 static double double_eps;
25 
26 static float
float_epsilon()27 float_epsilon()
28 {
29     float float_eps;
30 #ifndef NO_FLOAT_H
31     float_eps = FLT_EPSILON;
32 #else /* NO_FLOAT_H */
33     {
34 	float etop, ebot, eps;
35 	float one = 1.0;
36 	float two = 2.0;
37 	etop = 1.0;
38 	ebot = 0.0;
39 	eps = ebot + (etop - ebot)/two;
40 	while (eps != ebot && eps != etop) {
41 	    float epsp1;
42 
43 	    epsp1 = one + eps;
44 	    if (epsp1 > one)
45 		etop = eps;
46 	    else
47 		ebot = eps;
48 	    eps = ebot + (etop - ebot)/two;
49 	}
50 	float_eps = two * etop;
51     }
52 #endif /* NO_FLOAT_H */
53     return float_eps;
54 }
55 
56 
57 static double
double_epsilon()58 double_epsilon()
59 {
60     double double_eps;
61 #ifndef NO_FLOAT_H
62     double_eps = DBL_EPSILON;
63 #else /* NO_FLOAT_H */
64     {
65 	double etop, ebot, eps;
66 	double one = 1.0;
67 	double two = 2.0;
68 	etop = 1.0;
69 	ebot = 0.0;
70 	eps = ebot + (etop - ebot)/two;
71 	while (eps != ebot && eps != etop) {
72 	    double epsp1;
73 
74 	    epsp1 = one + eps;
75 	    if (epsp1 > one)
76 		etop = eps;
77 	    else
78 		ebot = eps;
79 	    eps = ebot + (etop - ebot)/two;
80 	}
81 	double_eps = two * etop;
82     }
83 #endif /* NO_FLOAT_H */
84     return double_eps;
85 }
86 
87 
88 static void
init_epsilons()89 init_epsilons()
90 {
91     float_eps = float_epsilon();
92     double_eps = double_epsilon();
93 }
94 
95 
96 /*
97  * Test ncvardef
98  *    check that proper define worked with ncvarinq
99  *    check that returned id is one more than previous id
100  *    try redefining an existing variable, check error
101  *    try adding scalar variable (no dimensions)
102  *    try with bad datatype, check error
103  *    try with bad number of dimensions, check error
104  *    try with bad dimension ids, check error
105  *    try in data mode, check error
106  */
107 int
test_ncvardef(path)108 test_ncvardef(path)
109      const char *path;		/* name of writable netcdf file to open */
110 {
111     int nerrs = 0;
112     int cdfid;			/* netcdf id */
113     static char pname[] = "test_ncvardef";
114     int id, iv;
115     static struct cdfvar va[] = { /* variables of all shapes and sizes */
116 	{"bytev", NC_BYTE, 6, ___, 0},
117 	{"charv", NC_CHAR, 5, ___, 0},
118 	{"shortv", NC_SHORT, 4, ___, 0},
119 	{"longv", NC_LONG, 3, ___, 0},
120 	{"floatv", NC_FLOAT, 2, ___, 0},
121 	{"doublev", NC_DOUBLE, 1, ___, 0},
122 	{"scalarv", NC_DOUBLE, 0, ___, 0}
123     };
124     int nv = LEN_OF(va);	/* number of variables to define */
125     int va_id[LEN_OF(va)];	/* variable ids */
126     static struct cdfvar tmp =	/* variable for testing bad types, etc. */
127 	{"tmpv", NC_DOUBLE, 1, ___, 0};
128     /* if d5 >= 91 in following, problem on machines with 16-bit ints ??? */
129     static struct cdfdim di[] = { /* a bunch of dimensions */
130 	{"d0", 2}, {"d1",3}, {"d2",5}, {"d3", 6}, {"d4", 4}, {"d5", 31}};
131     int nd = LEN_OF(di);	/* number of dimensions */
132     int di_id[LEN_OF(di)];	/* dimension ids */
133 
134     (void) fprintf(stderr, "*** Testing %s ...\t", &pname[5]);
135 
136     init_epsilons();
137     if ((cdfid = ncopen(path, NC_WRITE)) == -1) {
138 	error("%s: ncopen failed", pname);
139 	return ++nerrs;
140     }
141     /* opened, defining a variable should fail in data mode */
142     if (ncvardef(cdfid, va[0].name, va[0].type, va[0].ndims, va[0].dims)
143 	!= -1) {
144 	error("%s: ncvardef should have failed in data mode", pname);
145 	ncclose(cdfid); return ++nerrs;
146     }
147     /* enter define mode */
148     if (ncredef(cdfid) == -1) {
149 	error("%s: cdredef failed", pname);
150 	ncclose(cdfid); return ++nerrs;
151     }
152 
153     /* Add nd more dimensions */
154     for (id = 0; id < nd; id++) {
155 	if ((di_id[id] = ncdimdef(cdfid, di[id].name, di[id].size)) == -1) {
156 	    error("%s: ncdimdef failed for %s, size %d",
157 		  pname, di[id].name, di[id].size);
158 	    ncclose(cdfid); return ++nerrs;
159 	}
160 	add_dim(&test, &di[id]);	/* keep in-memory netcdf in sync */
161     }
162 
163     tmp.dims = (int *) emalloc(sizeof(int) * MAX_VAR_DIMS);
164     tmp.name = (char *) emalloc(MAX_NC_NAME);
165 
166     /* in define mode, add variables of each type with various shapes */
167     for (iv = 0; iv < nv; iv++) {
168 	/* set shape to use subset of dimensions previously defined */
169 	va[iv].dims = (int *) emalloc(sizeof(int) * va[iv].ndims);
170 	for (id = 0; id < va[iv].ndims; id++)
171 	  va[iv].dims[id] = di_id[id];
172 	if ((va_id[iv] = ncvardef(cdfid, va[iv].name, va[iv].type,
173 				  va[iv].ndims, va[iv].dims)) == -1) {
174 	    error("%s: ncvardef failed", pname);
175 	    errvar(&test,&va[iv]); /* prints details about variable */
176 	    ncclose(cdfid); return ++nerrs;
177 	}
178 	add_var(&test, &va[iv]); /* keep in-memory netcdf in sync */
179 	/* check that var id returned is one more than previous var id */
180 	if (va_id[iv] != test.nvars - 1) {
181 	    error("%s: ncvardef returned %d for var id, expected %d",
182 		  pname, va_id[iv], test.nvars-1);
183 	    ncclose(cdfid); return ++nerrs;
184 	}
185 	/* use ncvarinq to get values just set and compare values */
186 	if (ncvarinq(cdfid, va_id[iv], tmp.name, &tmp.type,
187 		      &tmp.ndims, tmp.dims, &tmp.natts) == -1) {
188 	    error("%s: ncvarinq failed", pname);
189 	    errvar(&test,&va[iv]); /* prints details about variable */
190 	    ncclose(cdfid); return ++nerrs;
191 	}
192 	if (strcmp(tmp.name, va[iv].name) != 0 ||
193 	    tmp.type != va[iv].type ||
194 	    tmp.ndims != va[iv].ndims ||
195 	    tmp.natts != va[iv].natts) {
196 	    error("%s: ncvardef and ncvarinq don't agree for %s",
197 		  pname, va[iv].name);
198 	    nerrs++;
199 	    errvar(&test,&va[iv]);
200 	    errvar(&test,&tmp);
201 	}
202 	for (id = 0; id < va[iv].ndims; id++) {
203 	    if (tmp.dims[id] != va[iv].dims[id]) {
204 	    error("%s: ncvardef and ncvarinq don't agree on shape of %s",
205 		  pname, va[iv].name);
206 	    nerrs++;
207 	    errvar(&test,&va[iv]);
208 	    errvar(&test,&tmp);
209 	    }
210 	}
211     }
212     /* try adding same variable again, this should fail */
213     if (ncvardef(cdfid, va[0].name, va[0].type,
214 		  va[0].ndims, va[0].dims) != -1) {
215 	error("%s: ncvardef should not allow redefinition", pname);
216 	ncclose(cdfid); return ++nerrs;
217     }
218     /* try bad type, should fail */
219     if (ncvardef(cdfid, "badtype", BAD_TYPE, va[0].ndims, va[0].dims) != -1) {
220 	error("%s: ncvardef should have failed on bad type", pname);
221 	ncclose(cdfid); return ++nerrs;
222     }
223     /* try bad ndims, should fail */
224     if (ncvardef(cdfid, "badndims", va[0].type, -1, va[0].dims) != -1) {
225 	error("%s: ncvardef should have failed on bad ndims", pname);
226 	ncclose(cdfid); return ++nerrs;
227     }
228     /* try bad ids in dims vector, should fail */
229     va[0].dims[va[0].ndims-1] = -1;
230     if (ncvardef(cdfid, "baddims", va[0].type, va[0].ndims, va[0].dims)
231 	!= -1) {
232 	error("%s: ncvardef should have failed on negative dim id in dims",
233 	      pname);
234 	ncclose(cdfid); return ++nerrs;
235     }
236     if (ncendef (cdfid) == -1) {
237 	error("%s: ncendef failed", pname);
238 	ncclose(cdfid); return ++nerrs;
239     }
240 
241     /* try reading a value of each type, should get appropriate fill value */
242     for (iv = 0; iv < nv; iv++) {
243 	static long where[] = {0,0,0,0,0,0};
244 
245 	switch(va[iv].type) {
246 	  case NC_BYTE:
247 	    {
248 		char val, fillval = FILL_BYTE;
249 		if (ncvarget1(cdfid, va_id[iv], where, (void *) &val) != -1) {
250 		    if (val != fillval) {
251 			error("%s: unwritten byte not FILL_BYTE", pname);
252 			nerrs++;
253 		    }
254 		} else {
255 		    error("%s: ncvarget1 failure for byte", pname);
256 		    nerrs++;
257 		}
258 	    }
259 	    break;
260 	  case NC_CHAR:
261 	    {
262 		char val, fillval = FILL_CHAR;
263 		if (ncvarget1(cdfid, va_id[iv], where, (void *) &val) != -1) {
264 		    if (val != fillval) {
265 			error("%s: unwritten char not FILL_CHAR", pname);
266 			nerrs++;
267 		    }
268 		} else {
269 		    error("%s: ncvarget1 failure for char", pname);
270 		    nerrs++;
271 		}
272 	    }
273 	    break;
274 	  case NC_SHORT:
275 	    {
276 		short val, fillval = FILL_SHORT;
277 		if (ncvarget1(cdfid, va_id[iv], where, (void *) &val) != -1) {
278 		    if (val != fillval) {
279 			error("%s: unwritten short not FILL_SHORT", pname);
280 			nerrs++;
281 		    }
282 		} else {
283 		    error("%s: ncvarget1 failure for short", pname);
284 		    nerrs++;
285 		}
286 	    }
287 	    break;
288 	  case NC_LONG:
289 	    {
290 		nclong val, fillval = FILL_LONG;
291 		if (ncvarget1(cdfid, va_id[iv], where, (void *) &val) != -1) {
292 		    if (val != fillval) {
293 			error("%s: unwritten long not FILL_LONG", pname);
294 			nerrs++;
295 		    }
296 		} else {
297 		    error("%s: ncvarget1 failure for long", pname);
298 		    nerrs++;
299 		}
300 	    }
301 	    break;
302 #define absval(x)  ( (x) < 0 ? -(x) : (x) )
303 	  case NC_FLOAT:
304 	    {
305 		float val, fillval = FILL_FLOAT;
306 		if (ncvarget1(cdfid, va_id[iv], where, (void *) &val) != -1) {
307 		    if (absval(val-fillval) > absval(float_eps * fillval)) {
308 			error("%s: unwritten float not FILL_FLOAT", pname);
309 			nerrs++;
310 		    }
311 		} else {
312 		    error("%s: ncvarget1 failure for float", pname);
313 		    nerrs++;
314 		}
315 	    }
316 	    break;
317 	  case NC_DOUBLE:
318 	    {
319 		double val, fillval = FILL_DOUBLE;
320 		if (ncvarget1(cdfid, va_id[iv], where, (void *) &val) != -1) {
321 #define DBL_FUDGE 8.0		/* can be 1.0 on every platform we've seen
322 				   except for VAX/Ultrix 4.3 with cc */
323 		    if (absval(val-fillval) > DBL_FUDGE * absval(double_eps * fillval)) {
324 			error("%s: unwritten double not FILL_DOUBLE", pname);
325 			nerrs++;
326 		    }
327 		} else {
328 		    error("%s: ncvarget1 failure for double", pname);
329 		    nerrs++;
330 		}
331 	    }
332 	    break;
333 	  default: break;
334 	}
335     }
336 
337     if (ncclose (cdfid) == -1) {
338 	error("%s: ncclose failed", pname);
339 	return ++nerrs;
340     }
341     free (tmp.dims);
342     free (tmp.name);
343     for (iv = 0; iv < nv; iv++)
344       if (va[iv].dims)
345 	free(va[iv].dims);
346     if (nerrs > 0)
347       (void) fprintf(stderr,"FAILED! ***\n");
348     else
349       (void) fprintf(stderr,"ok ***\n");
350 
351     return nerrs;
352 }
353