/********************************************************************* * Copyright 2018, UCAR/Unidata * See netcdf/COPYRIGHT file for copying and redistribution conditions. * $Header: /upc/share/CVS/netcdf-3/nctest/vardef.c,v 1.18 2009/10/28 18:30:50 dmh Exp $ *********************************************************************/ #include #include #include #include /* for free() */ #ifndef NO_FLOAT_H #include /* for FLT_EPSILON, DBL_EPSILON */ #endif /* NO_FLOAT_H */ #include #include "testcdf.h" /* defines in-memory test cdf structure */ #include "emalloc.h" #include "add.h" /* functions to update in-memory netcdf */ #include "error.h" #include "tests.h" #define LEN_OF(array) ((sizeof array) / (sizeof array[0])) static float float_eps; static double double_eps; static float float_epsilon() { float float_eps; #ifndef NO_FLOAT_H float_eps = FLT_EPSILON; #else /* NO_FLOAT_H */ { float etop, ebot, eps; float one = 1.0; float two = 2.0; etop = 1.0; ebot = 0.0; eps = ebot + (etop - ebot)/two; while (eps != ebot && eps != etop) { float epsp1; epsp1 = one + eps; if (epsp1 > one) etop = eps; else ebot = eps; eps = ebot + (etop - ebot)/two; } float_eps = two * etop; } #endif /* NO_FLOAT_H */ return float_eps; } static double double_epsilon() { double double_eps; #ifndef NO_FLOAT_H double_eps = DBL_EPSILON; #else /* NO_FLOAT_H */ { double etop, ebot, eps; double one = 1.0; double two = 2.0; etop = 1.0; ebot = 0.0; eps = ebot + (etop - ebot)/two; while (eps != ebot && eps != etop) { double epsp1; epsp1 = one + eps; if (epsp1 > one) etop = eps; else ebot = eps; eps = ebot + (etop - ebot)/two; } double_eps = two * etop; } #endif /* NO_FLOAT_H */ return double_eps; } static void init_epsilons() { float_eps = float_epsilon(); double_eps = double_epsilon(); } /* * Test ncvardef * check that proper define worked with ncvarinq * check that returned id is one more than previous id * try redefining an existing variable, check error * try adding scalar variable (no dimensions) * try with bad datatype, check error * try with bad number of dimensions, check error * try with bad dimension ids, check error * try in data mode, check error */ int test_ncvardef(const char *path) /* name of writable netcdf file to open */ { int nerrs = 0; int cdfid; /* netcdf id */ static char pname[] = "test_ncvardef"; int id, iv; static struct cdfvar va[] = { /* variables of all shapes and sizes */ {"bytev", NC_BYTE, 6, ___, 0}, {"charv", NC_CHAR, 5, ___, 0}, {"shortv", NC_SHORT, 4, ___, 0}, {"longv", NC_LONG, 3, ___, 0}, {"floatv", NC_FLOAT, 2, ___, 0}, {"doublev", NC_DOUBLE, 1, ___, 0}, {"scalarv", NC_DOUBLE, 0, ___, 0} }; int nv = LEN_OF(va); /* number of variables to define */ int va_id[LEN_OF(va)]; /* variable ids */ static struct cdfvar tmp = /* variable for testing bad types, etc. */ {"tmpv", NC_DOUBLE, 1, ___, 0}; /* if d5 >= 91 in following, problem on machines with 16-bit ints ??? */ static struct cdfdim di[] = { /* a bunch of dimensions */ {"d0", 2}, {"d1",3}, {"d2",5}, {"d3", 6}, {"d4", 4}, {"d5", 31}}; int nd = LEN_OF(di); /* number of dimensions */ int di_id[LEN_OF(di)]; /* dimension ids */ (void) fprintf(stderr, "*** Testing %s ...\t", &pname[5]); init_epsilons(); if ((cdfid = ncopen(path, NC_WRITE)) == -1) { error("%s: ncopen failed", pname); return ++nerrs; } /* opened, defining a variable should fail in data mode */ if (ncvardef(cdfid, va[0].name, va[0].type, va[0].ndims, va[0].dims) != -1) { error("%s: ncvardef should have failed in data mode", pname); ncclose(cdfid); return ++nerrs; } /* enter define mode */ if (ncredef(cdfid) == -1) { error("%s: cdredef failed", pname); ncclose(cdfid); return ++nerrs; } /* Add nd more dimensions */ for (id = 0; id < nd; id++) { if ((di_id[id] = ncdimdef(cdfid, di[id].name, di[id].size)) == -1) { error("%s: ncdimdef failed for %s, size %d", pname, di[id].name, di[id].size); ncclose(cdfid); return ++nerrs; } add_dim(&test, &di[id]); /* keep in-memory netcdf in sync */ } tmp.dims = (int *) emalloc(sizeof(int) * MAX_VAR_DIMS); tmp.name = (char *) emalloc(MAX_NC_NAME); /* in define mode, add variables of each type with various shapes */ for (iv = 0; iv < nv; iv++) { /* set shape to use subset of dimensions previously defined */ va[iv].dims = (int *) emalloc(sizeof(int) * (size_t)va[iv].ndims); for (id = 0; id < va[iv].ndims; id++) va[iv].dims[id] = di_id[id]; if ((va_id[iv] = ncvardef(cdfid, va[iv].name, va[iv].type, va[iv].ndims, va[iv].dims)) == -1) { error("%s: ncvardef failed", pname); errvar(&test,&va[iv]); /* prints details about variable */ ncclose(cdfid); return ++nerrs; } add_var(&test, &va[iv]); /* keep in-memory netcdf in sync */ /* check that var id returned is one more than previous var id */ if (va_id[iv] != test.nvars - 1) { error("%s: ncvardef returned %d for var id, expected %d", pname, va_id[iv], test.nvars-1); ncclose(cdfid); return ++nerrs; } /* use ncvarinq to get values just set and compare values */ if (ncvarinq(cdfid, va_id[iv], tmp.name, &tmp.type, &tmp.ndims, tmp.dims, &tmp.natts) == -1) { error("%s: ncvarinq failed", pname); errvar(&test,&va[iv]); /* prints details about variable */ ncclose(cdfid); return ++nerrs; } if (strcmp(tmp.name, va[iv].name) != 0 || tmp.type != va[iv].type || tmp.ndims != va[iv].ndims || tmp.natts != va[iv].natts) { error("%s: ncvardef and ncvarinq don't agree for %s", pname, va[iv].name); nerrs++; errvar(&test,&va[iv]); errvar(&test,&tmp); } for (id = 0; id < va[iv].ndims; id++) { if (tmp.dims[id] != va[iv].dims[id]) { error("%s: ncvardef and ncvarinq don't agree on shape of %s", pname, va[iv].name); nerrs++; errvar(&test,&va[iv]); errvar(&test,&tmp); } } } /* try adding same variable again, this should fail */ if (ncvardef(cdfid, va[0].name, va[0].type, va[0].ndims, va[0].dims) != -1) { error("%s: ncvardef should not allow redefinition", pname); ncclose(cdfid); return ++nerrs; } /* try bad type, should fail */ if (ncvardef(cdfid, "badtype", BAD_TYPE, va[0].ndims, va[0].dims) != -1) { error("%s: ncvardef should have failed on bad type", pname); ncclose(cdfid); return ++nerrs; } /* try bad ndims, should fail */ if (ncvardef(cdfid, "badndims", va[0].type, -1, va[0].dims) != -1) { error("%s: ncvardef should have failed on bad ndims", pname); ncclose(cdfid); return ++nerrs; } /* try bad ids in dims vector, should fail */ va[0].dims[va[0].ndims-1] = -1; if (ncvardef(cdfid, "baddims", va[0].type, va[0].ndims, va[0].dims) != -1) { error("%s: ncvardef should have failed on negative dim id in dims", pname); ncclose(cdfid); return ++nerrs; } if (ncendef (cdfid) == -1) { error("%s: ncendef failed", pname); ncclose(cdfid); return ++nerrs; } /* try reading a value of each type, should get appropriate fill value */ for (iv = 0; iv < nv; iv++) { static long where[] = {0,0,0,0,0,0}; switch(va[iv].type) { case NC_BYTE: { char val, fillval = FILL_BYTE; if (ncvarget1(cdfid, va_id[iv], where, (void *) &val) != -1) { if (val != fillval) { error("%s: unwritten byte not FILL_BYTE", pname); nerrs++; } } else { error("%s: ncvarget1 failure for byte", pname); nerrs++; } } break; case NC_CHAR: { char val, fillval = FILL_CHAR; if (ncvarget1(cdfid, va_id[iv], where, (void *) &val) != -1) { if (val != fillval) { error("%s: unwritten char not FILL_CHAR", pname); nerrs++; } } else { error("%s: ncvarget1 failure for char", pname); nerrs++; } } break; case NC_SHORT: { short val, fillval = FILL_SHORT; if (ncvarget1(cdfid, va_id[iv], where, (void *) &val) != -1) { if (val != fillval) { error("%s: unwritten short not FILL_SHORT", pname); nerrs++; } } else { error("%s: ncvarget1 failure for short", pname); nerrs++; } } break; case NC_LONG: { nclong val, fillval = FILL_LONG; if (ncvarget1(cdfid, va_id[iv], where, (void *) &val) != -1) { if (val != fillval) { error("%s: unwritten long not FILL_LONG", pname); nerrs++; } } else { error("%s: ncvarget1 failure for long", pname); nerrs++; } } break; #define absval(x) ( (x) < 0 ? -(x) : (x) ) case NC_FLOAT: { float val, fillval = FILL_FLOAT; if (ncvarget1(cdfid, va_id[iv], where, (void *) &val) != -1) { if (absval(val-fillval) > absval(float_eps * fillval)) { error("%s: unwritten float not FILL_FLOAT", pname); nerrs++; } } else { error("%s: ncvarget1 failure for float", pname); nerrs++; } } break; case NC_DOUBLE: { double val, fillval = FILL_DOUBLE; if (ncvarget1(cdfid, va_id[iv], where, (void *) &val) != -1) { #define DBL_FUDGE 8.0 /* can be 1.0 on every platform we've seen except for VAX/Ultrix 4.3 with cc */ if (absval(val-fillval) > DBL_FUDGE * absval(double_eps * fillval)) { error("%s: unwritten double not FILL_DOUBLE", pname); nerrs++; } } else { error("%s: ncvarget1 failure for double", pname); nerrs++; } } break; default: break; } } if (ncclose (cdfid) == -1) { error("%s: ncclose failed", pname); return ++nerrs; } free (tmp.dims); free (tmp.name); for (iv = 0; iv < nv; iv++) if (va[iv].dims) free(va[iv].dims); if (nerrs > 0) (void) fprintf(stderr,"FAILED! ***\n"); else (void) fprintf(stderr,"ok ***\n"); return nerrs; }