mirror of
https://github.com/Unidata/netcdf-c.git
synced 2024-11-27 07:30:33 +08:00
594 lines
16 KiB
C
594 lines
16 KiB
C
/*********************************************************************
|
|
* Copyright 2018, University Corporation for Atmospheric Research
|
|
* See netcdf/COPYRIGHT file for copying and redistribution conditions.
|
|
* $Header: /upc/share/CVS/netcdf-3/nctest/rec.c,v 1.11 2006/10/31 16:21:58 ed Exp $
|
|
*********************************************************************/
|
|
|
|
#include <config.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h> /* for free() */
|
|
#include "netcdf.h"
|
|
#include "testcdf.h" /* defines in-memory test cdf structure */
|
|
#include "emalloc.h"
|
|
#include "val.h"
|
|
#include "error.h"
|
|
#include "tests.h"
|
|
|
|
/*
|
|
* Returns number of record variables in an open netCDF file, and an array of
|
|
* the record variable ids, if the array parameter is non-null. Returns -1 on
|
|
* error.
|
|
*/
|
|
static int
|
|
numrecvars(int ncid, int *recvarids)
|
|
{
|
|
int ndims, iv, nvars;
|
|
int nrecvars;
|
|
int recdimid;
|
|
int dimids[MAX_NC_DIMS];
|
|
|
|
if (ncinquire(ncid, 0, &nvars, 0, &recdimid) == -1)
|
|
return -1;
|
|
if (recdimid == -1)
|
|
return 0;
|
|
nrecvars = 0;
|
|
for (iv = 0; iv < nvars; iv++) {
|
|
if (ncvarinq(ncid, iv, 0, 0, &ndims, dimids, 0) == -1)
|
|
return -1;
|
|
if (ndims > 0 && dimids[0] == recdimid) {
|
|
if (recvarids)
|
|
recvarids[nrecvars] = iv;
|
|
nrecvars++;
|
|
}
|
|
}
|
|
return nrecvars;
|
|
}
|
|
|
|
|
|
/*
|
|
* Returns record size (in bytes) of the record variable with a specified
|
|
* variable id. Returns 0 if not a record variable. Returns -1 on error.
|
|
*/
|
|
static long
|
|
ncrecsize(int ncid,int vid)
|
|
{
|
|
int recdimid;
|
|
nc_type type;
|
|
int ndims;
|
|
int dimids[MAX_NC_DIMS];
|
|
int id;
|
|
long size;
|
|
|
|
if (ncinquire(ncid, 0, 0, 0, &recdimid) == -1)
|
|
return -1;
|
|
if (ncvarinq(ncid, vid, 0, &type, &ndims, dimids, 0) == -1)
|
|
return -1;
|
|
if (ndims == 0 || dimids[0] != recdimid)
|
|
return 0;
|
|
size = nctypelen(type);
|
|
for (id = 1; id < ndims; id++) {
|
|
long len;
|
|
(void) ncdiminq(ncid, dimids[id], 0, &len);
|
|
size *= len;
|
|
}
|
|
return size;
|
|
}
|
|
|
|
|
|
/*
|
|
* Retrieves the number of record variables, the record variable ids, and the
|
|
* record size of each record variable. If any pointer to info to be returned
|
|
* is null, the associated information is not returned. Returns -1 on error.
|
|
* This is the same as the ncrecinq() in the library, except that can handle
|
|
* errors better.
|
|
*/
|
|
static int
|
|
recinq(int ncid, int *nrecvars, int *recvarids, long *recsizes)
|
|
{
|
|
int iv;
|
|
int rvarids[MAX_NC_VARS];
|
|
int nrvars = numrecvars(ncid, rvarids);
|
|
|
|
if (nrvars == -1)
|
|
return -1;
|
|
|
|
if (nrecvars)
|
|
*nrecvars = nrvars;
|
|
if (recvarids)
|
|
for (iv = 0; iv < nrvars; iv++)
|
|
recvarids[iv] = rvarids[iv];
|
|
if (recsizes)
|
|
for (iv = 0; iv < nrvars; iv++)
|
|
recsizes[iv] = ncrecsize(ncid, rvarids[iv]);
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*
|
|
* Test ncrecinq
|
|
* try in both data and define modes
|
|
* check returned values against independently computed values
|
|
* try with bad netCDF handle, check error
|
|
*/
|
|
int
|
|
test_ncrecinq(const char *path) /* name of netcdf file to open */
|
|
{
|
|
int nerrs = 0;
|
|
static char pname[] = "test_ncrecinq";
|
|
int ncid;
|
|
int nrvars; /* number of record variables */
|
|
int rvarids[MAX_NC_VARS]; /* id of each record variable */
|
|
long rvarsizes[MAX_NC_VARS]; /* record size of each record variable */
|
|
int tnrvars; /* true number of record variables */
|
|
int trvarids[MAX_NC_VARS]; /* true id of each record variable */
|
|
long trvarsizes[MAX_NC_VARS]; /* true rec size of each record variable */
|
|
int iv;
|
|
|
|
(void) fprintf(stderr, "*** Testing %s ...\t", &pname[5]);
|
|
|
|
if ((ncid = ncopen(path, NC_WRITE)) == -1) {
|
|
error("%s: ncopen failed", pname);
|
|
return ++nerrs;
|
|
}
|
|
|
|
/* First compute independently what ncrecinq should return */
|
|
if (recinq(ncid, &tnrvars, trvarids, trvarsizes) == -1) {
|
|
error("%s: recinq failed", pname);
|
|
ncclose(ncid);
|
|
return ++nerrs;
|
|
}
|
|
|
|
/* check that ncrecinq() returns correct information in data mode */
|
|
if (ncrecinq(ncid, &nrvars, rvarids, rvarsizes) == -1) {
|
|
error("%s: ncrecinq failed", pname);
|
|
ncclose(ncid);
|
|
return ++nerrs;
|
|
}
|
|
|
|
if (nrvars != tnrvars) {
|
|
error("ncrecinq returned wrong number of rec vars, %d != %d",
|
|
nrvars, tnrvars);
|
|
nerrs++;
|
|
}
|
|
|
|
for (iv = 0; iv < nrvars; iv++) {
|
|
if (rvarids[iv] != trvarids[iv]) {
|
|
error("ncrecinq returned wrong record id for var %d",
|
|
trvarids[iv]);
|
|
nerrs++;
|
|
}
|
|
if (rvarsizes[iv] != trvarsizes[iv]) {
|
|
error("ncrecinq returned wrong record size for var %d",
|
|
trvarids[iv]);
|
|
nerrs++;
|
|
}
|
|
}
|
|
|
|
if (ncredef(ncid) == -1) {
|
|
error("%s: ncredef failed", pname);
|
|
ncclose(ncid);
|
|
return ++nerrs;
|
|
}
|
|
/* check that ncrecinq() returns correct information in define mode too */
|
|
if (ncrecinq(ncid, &nrvars, rvarids, rvarsizes) == -1) {
|
|
error("%s: ncrecinq failed in define mode", pname);
|
|
ncclose(ncid);
|
|
return ++nerrs;
|
|
}
|
|
if (nrvars != tnrvars) {
|
|
error("define mode, ncrecinq returned wrong num of rec vars, %d != %d",
|
|
nrvars, tnrvars);
|
|
nerrs++;
|
|
}
|
|
for (iv = 0; iv < nrvars; iv++) {
|
|
if (rvarids[iv] != trvarids[iv]) {
|
|
error("define mode, ncrecinq returned wrong record id for var %d",
|
|
trvarids[iv]);
|
|
nerrs++;
|
|
}
|
|
if (rvarsizes[iv] != trvarsizes[iv]) {
|
|
error("define mode, ncrecinq returned wrong rec size for var %d",
|
|
trvarids[iv]);
|
|
nerrs++;
|
|
}
|
|
}
|
|
|
|
if (ncclose (ncid) == -1) {
|
|
error("%s: ncclose failed", pname);
|
|
return ++nerrs;
|
|
}
|
|
|
|
if (ncrecinq(ncid, &nrvars, rvarids, rvarsizes) != -1) {
|
|
error("%s: ncrecinq failed to report bad handle", pname);
|
|
nerrs++;
|
|
}
|
|
|
|
if (nerrs > 0)
|
|
(void) fprintf(stderr,"FAILED! ***\n");
|
|
else
|
|
(void) fprintf(stderr,"ok ***\n");
|
|
return nerrs;
|
|
}
|
|
|
|
|
|
/*
|
|
* Retrieves the dimension sizes of a variable with a specified variable id in
|
|
* an open netCDF file. Returns -1 on error.
|
|
*/
|
|
static int
|
|
dimsizes(int ncid, int varid, long *sizes)
|
|
{
|
|
int ndims;
|
|
int id;
|
|
int dimids[MAX_NC_DIMS];
|
|
|
|
if (ncvarinq(ncid, varid, 0, 0, &ndims, dimids, 0) == -1)
|
|
return -1;
|
|
if (ndims == 0 || sizes == 0)
|
|
return 0;
|
|
for (id = 0; id < ndims; id++)
|
|
(void) ncdiminq(ncid, dimids[id], 0, &sizes[id]);
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*
|
|
* Write one record's worth of data, except don't write to variables for which
|
|
* the address of the data to be written is NULL. Return -1 on error. This is
|
|
* the same as the ncrecput() in the library, except that can handle errors
|
|
* better.
|
|
*/
|
|
static int
|
|
recput(int ncid, long recnum, void **datap)
|
|
{
|
|
int iv;
|
|
int rvids[MAX_NC_VARS];
|
|
int nrvars = numrecvars(ncid, rvids);
|
|
long start[MAX_NC_DIMS];
|
|
long edges[MAX_NC_DIMS];
|
|
|
|
if (nrvars == -1)
|
|
return -1;
|
|
|
|
start[0] = recnum;
|
|
for (iv = 1; iv < nrvars; iv++)
|
|
start[iv] = 0;
|
|
|
|
for (iv = 0; iv < nrvars; iv++) {
|
|
if (datap[iv] != 0) {
|
|
(void) dimsizes(ncid, rvids[iv], edges);
|
|
edges[0] = 1; /* only 1 record's worth */
|
|
if (ncvarput(ncid, rvids[iv], start, edges, datap[iv]) == -1)
|
|
return -1;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*
|
|
* Read one record's worth of data, except don't read from variables for which
|
|
* the address of the data to be read is null. Return -1 on error. This is
|
|
* the same as the ncrecget() in the library, except that can handle errors
|
|
* better.
|
|
*/
|
|
static int
|
|
recget(int ncid, long recnum, void **datap)
|
|
{
|
|
int iv;
|
|
int rvids[MAX_NC_VARS];
|
|
int nrvars = numrecvars(ncid, rvids);
|
|
long start[MAX_NC_DIMS];
|
|
long edges[MAX_NC_DIMS];
|
|
|
|
if (nrvars == -1)
|
|
return -1;
|
|
|
|
start[0] = recnum;
|
|
for (iv = 1; iv < nrvars; iv++)
|
|
start[iv] = 0;
|
|
|
|
for (iv = 0; iv < nrvars; iv++) {
|
|
if (datap[iv] != 0) {
|
|
(void) dimsizes(ncid, rvids[iv], edges);
|
|
edges[0] = 1; /* only 1 record's worth */
|
|
if (ncvarget(ncid, rvids[iv], start, edges, datap[iv]) == -1)
|
|
return -1;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*
|
|
* Test ncrecput
|
|
* check that proper call works putting all recoerd variables
|
|
* try putting only a proper subset of variables
|
|
* try putting the empty subset of variables
|
|
* try in define mode, check error
|
|
* try with bad netCDF handle, check error
|
|
*/
|
|
int
|
|
test_ncrecput(const char *path) /* name of writable netcdf file to open */
|
|
{
|
|
int nerrs = 0;
|
|
static char pname[] = "test_ncrecput";
|
|
int nrvars; /* number of record variables */
|
|
int rvarids[MAX_NC_VARS]; /* id of each record variable */
|
|
long rvarsizes[MAX_NC_VARS]; /* record size of each record variable */
|
|
int ncid; /* netcdf id */
|
|
void *datap[MAX_NC_VARS]; /* array of address pointers for rec vars */
|
|
void *datar[MAX_NC_VARS]; /* pointers for comparison data */
|
|
long recnum = 1; /* we'll write the second record */
|
|
int iv;
|
|
long recsize[MAX_NC_VARS]; /* record size in data elements */
|
|
nc_type vartype[MAX_NC_VARS];
|
|
void *zeros[MAX_NC_VARS];
|
|
|
|
(void) fprintf(stderr, "*** Testing %s ...\t", &pname[5]);
|
|
|
|
if ((ncid = ncopen(path, NC_WRITE)) == -1) {
|
|
error("%s: ncopen failed", pname);
|
|
return ++nerrs;
|
|
}
|
|
|
|
if (ncrecinq(ncid, &nrvars, rvarids, rvarsizes) == -1) {
|
|
error("%s: ncrecinq failed", pname);
|
|
ncclose(ncid);
|
|
return ++nerrs;
|
|
}
|
|
|
|
/* get a block of data of the right type for each record variable */
|
|
for (iv = 0; iv < nrvars; iv++) {
|
|
datap[iv] = emalloc(rvarsizes[iv]);
|
|
datar[iv] = emalloc(rvarsizes[iv]); /* for comparison values */
|
|
if (ncvarinq(ncid, rvarids[iv], 0, &vartype[iv], 0, 0, 0) == -1) {
|
|
error("%s: ncvarinq failed", pname);
|
|
ncclose(ncid);
|
|
return ++nerrs;
|
|
}
|
|
recsize[iv] = rvarsizes[iv]/nctypelen(vartype[iv]);
|
|
/* Fill data blocks with 0,1,2,3,... */
|
|
val_fill(vartype[iv], recsize[iv], datap[iv]);
|
|
/* Zero out comparison data */
|
|
val_fill_zero(vartype[iv], recsize[iv], datar[iv]);
|
|
}
|
|
|
|
/* Zero data in recnum record, before trying to put non-zero data */
|
|
if (recput(ncid, recnum, datar) == -1) {
|
|
error("%s: recput failed", pname);
|
|
ncclose(ncid);
|
|
return ++nerrs;
|
|
}
|
|
|
|
/* opened in data mode, try putting a complete record */
|
|
if (ncrecput(ncid, recnum, datap) == -1) {
|
|
error("%s: ncrecput failed on complete record", pname);
|
|
nerrs++;
|
|
}
|
|
|
|
/* Check that right values were put */
|
|
if (recget(ncid, recnum, datar) == -1) {
|
|
error("%s: recget failed", pname);
|
|
nerrs++;
|
|
}
|
|
for (iv = 0; iv < nrvars; iv++) {
|
|
if (val_cmp(vartype[iv], recsize[iv], datap[iv], datar[iv]) != 0) {
|
|
error("%s: bad values written by recput", pname);
|
|
nerrs++;
|
|
}
|
|
val_fill_zero(vartype[iv], recsize[iv], datap[iv]);
|
|
val_fill_zero(vartype[iv], recsize[iv], datar[iv]);
|
|
zeros[iv] = 0;
|
|
}
|
|
|
|
if (nrvars > 0) {
|
|
void *datap0 = datap[0];
|
|
|
|
/* put a partial record, everything but first record variable */
|
|
datap[0] = 0;
|
|
val_fill(vartype[0], recsize[0], datar[0]);
|
|
if (ncrecput(ncid, recnum, datap) == -1) {
|
|
error("%s: ncrecput failed on partial record", pname);
|
|
nerrs++;
|
|
}
|
|
|
|
/* Check right values were put, first record variable undisturbed */
|
|
datap[0] = datap0;
|
|
if (recget(ncid, recnum, datap) == -1) {
|
|
error("%s: recget failed after partial record put", pname);
|
|
nerrs++;
|
|
}
|
|
for (iv = 0; iv < nrvars; iv++) {
|
|
if (val_cmp(vartype[iv], recsize[iv], datap[iv], datar[iv]) != 0) {
|
|
error("%s: bad values written by partial recput", pname);
|
|
nerrs++;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Put an empty record, check that values remain undisturbed */
|
|
if (ncrecput(ncid, recnum, zeros) == -1) {
|
|
error("%s: ncrecput failed on empty record", pname);
|
|
nerrs++;
|
|
}
|
|
if (recget(ncid, recnum, datap) == -1) {
|
|
error("%s: recget failed after empty record put", pname);
|
|
nerrs++;
|
|
}
|
|
for (iv = 0; iv < nrvars; iv++) {
|
|
if (val_cmp(vartype[iv], recsize[iv], datap[iv], datar[iv]) != 0) {
|
|
error("%s: bad values written by empty recput", pname);
|
|
nerrs++;
|
|
}
|
|
}
|
|
|
|
/* try in define mode, check error */
|
|
if (ncredef(ncid) == -1) {
|
|
error("%s: ncredef failed", pname);
|
|
ncclose(ncid);
|
|
return ++nerrs;
|
|
}
|
|
|
|
if (ncrecput(ncid, recnum, datap) != -1) {
|
|
error("%s: ncrecput should fail in define mode", pname);
|
|
nerrs++;
|
|
}
|
|
|
|
/* try with bad netCDF handle, check error */
|
|
if (ncclose (ncid) == -1) {
|
|
error("%s: ncclose failed", pname);
|
|
return ++nerrs;
|
|
}
|
|
if (ncrecput(ncid, recnum, datap) != -1) {
|
|
error("%s: ncrecput failed to report bad handle", pname);
|
|
nerrs++;
|
|
}
|
|
for (iv = 0; iv < nrvars; iv++) {
|
|
free(datap[iv]);
|
|
free(datar[iv]);
|
|
}
|
|
|
|
if (nerrs > 0)
|
|
(void) fprintf(stderr,"FAILED! ***\n");
|
|
else
|
|
(void) fprintf(stderr,"ok ***\n");
|
|
|
|
return nerrs;
|
|
}
|
|
|
|
|
|
/*
|
|
* Test ncrecget
|
|
* check that proper call works getting all record variables
|
|
* try getting only a proper subset of variables
|
|
* try getting the empty subset of variables
|
|
* try with bad netCDF handle, check error
|
|
*/
|
|
int
|
|
test_ncrecget(const char *path) /* name of netcdf file to open */
|
|
{
|
|
int nerrs = 0;
|
|
static char pname[] = "test_ncrecget";
|
|
int nrvars; /* number of record variables */
|
|
int rvarids[MAX_NC_VARS]; /* id of each record variable */
|
|
long rvarsizes[MAX_NC_VARS]; /* record size of each record variable */
|
|
int ncid; /* netcdf id */
|
|
void *datap[MAX_NC_VARS]; /* array of address pointers for rec vars */
|
|
void *datar[MAX_NC_VARS]; /* pointers for comparison data */
|
|
long recnum = 1; /* we'll write the second record */
|
|
int iv;
|
|
long recsize[MAX_NC_VARS]; /* record size in data elements */
|
|
nc_type vartype[MAX_NC_VARS];
|
|
void *zeros[MAX_NC_VARS];
|
|
|
|
(void) fprintf(stderr, "*** Testing %s ...\t", &pname[5]);
|
|
|
|
if ((ncid = ncopen(path, NC_WRITE)) == -1) {
|
|
error("%s: ncopen failed", pname);
|
|
return ++nerrs;
|
|
}
|
|
|
|
if (ncrecinq(ncid, &nrvars, rvarids, rvarsizes) == -1) {
|
|
error("%s: ncrecinq failed", pname);
|
|
ncclose(ncid);
|
|
return ++nerrs;
|
|
}
|
|
|
|
/* get a block of data of the right type for each record variable */
|
|
for (iv = 0; iv < nrvars; iv++) {
|
|
datap[iv] = emalloc(rvarsizes[iv]);
|
|
datar[iv] = emalloc(rvarsizes[iv]); /* for comparison values */
|
|
if (ncvarinq(ncid, rvarids[iv], 0, &vartype[iv], 0, 0, 0) == -1) {
|
|
error("%s: ncvarinq failed", pname);
|
|
ncclose(ncid);
|
|
return ++nerrs;
|
|
}
|
|
recsize[iv] = rvarsizes[iv]/nctypelen(vartype[iv]);
|
|
/* Fill data blocks with 0,1,2,3,... */
|
|
val_fill(vartype[iv], recsize[iv], datap[iv]);
|
|
/* Zero out comparison data */
|
|
val_fill_zero(vartype[iv], recsize[iv], datar[iv]);
|
|
}
|
|
|
|
if (recput(ncid, recnum, datap) == -1) {
|
|
error("%s: recput failed", pname);
|
|
ncclose(ncid);
|
|
return ++nerrs;
|
|
}
|
|
|
|
/* opened in data mode, try getting a complete record */
|
|
if (recget(ncid, recnum, datap) == -1) {
|
|
error("%s: recget failed on complete record", pname);
|
|
nerrs++;
|
|
}
|
|
if (ncrecget(ncid, recnum, datar) == -1) {
|
|
error("%s: ncrecget failed on complete record", pname);
|
|
nerrs++;
|
|
}
|
|
|
|
for (iv = 0; iv < nrvars; iv++) {
|
|
if (val_cmp(vartype[iv], recsize[iv], datap[iv], datar[iv]) != 0) {
|
|
error("%s: bad values written by recget", pname);
|
|
nerrs++;
|
|
}
|
|
val_fill_zero(vartype[iv], recsize[iv], datap[iv]);
|
|
val_fill_zero(vartype[iv], recsize[iv], datar[iv]);
|
|
zeros[iv] = 0;
|
|
}
|
|
|
|
if (nrvars > 0) {
|
|
void *datap0 = datap[0];
|
|
void *datar0 = datar[0];
|
|
|
|
/* get a partial record, everything but first record variable */
|
|
datap[0] = 0;
|
|
if (ncrecget(ncid, recnum, datap) == -1) {
|
|
error("%s: ncrecget failed on partial record", pname);
|
|
nerrs++;
|
|
}
|
|
datar[0] = 0;
|
|
if (recget(ncid, recnum, datar) == -1) {
|
|
error("%s: recget failed on partial record", pname);
|
|
nerrs++;
|
|
}
|
|
/* Check right values were got, first record variable undisturbed */
|
|
datap[0] = datap0;
|
|
datar[0] = datar0;
|
|
for (iv = 0; iv < nrvars; iv++) {
|
|
if (val_cmp(vartype[iv], recsize[iv], datap[iv], datar[iv]) != 0) {
|
|
error("%s: bad values read by partial recget", pname);
|
|
nerrs++;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Get an empty record */
|
|
if (ncrecget(ncid, recnum, zeros) == -1) {
|
|
error("%s: ncrecget failed on empty record", pname);
|
|
nerrs++;
|
|
}
|
|
|
|
/* try with bad netCDF handle, check error */
|
|
if (ncclose (ncid) == -1) {
|
|
error("%s: ncclose failed", pname);
|
|
return ++nerrs;
|
|
}
|
|
if (ncrecget(ncid, recnum, datap) != -1) {
|
|
error("%s: ncrecget failed to report bad handle", pname);
|
|
nerrs++;
|
|
}
|
|
for (iv = 0; iv < nrvars; iv++) {
|
|
free(datap[iv]);
|
|
free(datar[iv]);
|
|
}
|
|
|
|
if (nerrs > 0)
|
|
(void) fprintf(stderr,"FAILED! ***\n");
|
|
else
|
|
(void) fprintf(stderr,"ok ***\n");
|
|
|
|
return nerrs;
|
|
}
|
|
|