1 /*
2  *  Copyright (C) 2015, Northwestern University and Argonne National Laboratory
3  *  See COPYRIGHT notice in top-level directory.
4  *
5  *  $Id$
6  */
7 
8 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
9  *
10  * This program tests whether NC_ERANGE error code can be reported correctly.
11  * Note in CDF-1 and CDF-2, a special case is made to NOT report NC_ERANGE
12  * when the variable is of NC_BYTE type and the calling APIs are of uchar. See
13  * http://www.unidata.ucar.edu/software/netcdf/docs/data_type.html#type_conversion
14  *
15  * In CDF-5, NC_ERANGE is checked for when the external data type mismatches the
16  * internal one.
17  *
18  * The test uses the following 2 case.
19  * 1. get a value of 255 from a NC_UBYTE variable defined in a netCDF file to a
20  *    memory buffer of signed char through e.g. API ncmpi_get_var_schar_all
21  * 2. put a value of -1 of signed char from an in-memory buffer to a NC_UBYTE
22  *    variable defined in a netCDF file
23  *
24  * The compile and run commands are given below.
25  *
26  *    % mpicc -g -o test_erange test_erange.c -lpnetcdf
27  *
28  *    % mpiexec -l -n 1 test_erange testfile.nc
29  *
30  * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <string.h>
34 #include <libgen.h> /* basename() */
35 #include <pnetcdf.h>
36 
37 #include <testutils.h>
38 
39 #define ERR if (err!=NC_NOERR) {printf("Error at line %d: %s\n",__LINE__,ncmpi_strerror(err));nerrs++;}
40 #define EXPECT_ERR if (err != NC_ERANGE) {printf("Error at line %d: expecting NC_ERANGE, but got %d\n",__LINE__,err);nerrs++;}
41 
42 static
test_cdf2(char * filename)43 int test_cdf2(char *filename)
44 {
45     int err, nerrs=0, ncid, vid, dimid;
46     unsigned char uc[1];
47     signed char sc[1];
48     int si[1];
49 
50     err = ncmpi_create(MPI_COMM_WORLD, filename, NC_CLOBBER, MPI_INFO_NULL, &ncid); ERR
51 
52     /* for CDF-1 and CDF-2, a special case is made: there is no NC_ERANGE
53      * error can occur converting between NC_BYTE and unsigned char.
54      * http://www.unidata.ucar.edu/software/netcdf/docs/data_type.html#type_conversion
55      * In brief, NC_BYTE is signed in all signed CDF-2 APIs, and unsigned in
56      * all unsigned APIs. In CDF-2, there is only one unsigned API, _uchar.
57      */
58     uc[0] = 255;
59     err = ncmpi_put_att_uchar(ncid, NC_GLOBAL, "att1", NC_BYTE, 1, uc); ERR
60     uc[0] = 0; /* initialize with a number that is not 0 */
61     err = ncmpi_get_att_uchar(ncid, NC_GLOBAL, "att1", uc); ERR
62     if (uc[0] != 255) {
63         printf("Error at line %d: unexpected read value %d (expecting 255)\n",__LINE__,(int)uc[0]);
64         nerrs++;
65     }
66     sc[0] = 3; /* initialize with a number that is not -1 or -0 */
67     /* No NC_ERANGE as the internal and external types are considered the same */
68     err = ncmpi_get_att_schar(ncid, NC_GLOBAL, "att1", sc); ERR
69     if (   sc[0] != -1     /* 2-complement bit representation */
70         && sc[0] != -0) {  /* 1-complement bit representation */
71         printf("Error at line %d: unexpected read value %d (expecting 255)\n",__LINE__,(int)uc[0]);
72         nerrs++;
73     }
74 
75     err = ncmpi_def_dim(ncid, "x", 1, &dimid); ERR
76     err = ncmpi_def_var(ncid, "var_byte", NC_BYTE, 1, &dimid, &vid); ERR
77     err = ncmpi_enddef(ncid); ERR
78 
79     /* No NC_ERANGE should be returned for CDF-1 and 2 */
80     uc[0] = 255;
81     err = ncmpi_put_var_uchar_all(ncid, vid, uc); ERR
82     uc[0] = 3; /* initialize with a number that is not -1 or -0 */
83     err = ncmpi_get_var_uchar_all(ncid, vid, uc); ERR
84     if (uc[0] != 255) {
85         printf("Error at line %d: unexpected read value %d (expecting 255)\n",__LINE__,(int)uc[0]);
86         nerrs++;
87     }
88 
89     /* No NC_ERANGE should be returned for CDF-1 and 2 */
90     sc[0] = -128;
91     err = ncmpi_put_var_schar_all(ncid, vid, sc); ERR
92     sc[0] = 0;
93     err = ncmpi_get_var_schar_all(ncid, vid, sc); ERR
94     if (sc[0] != -128) {
95         printf("Error at line %d: unexpected read value %d (expecting -128)\n",__LINE__,(int)sc[0]);
96         nerrs++;
97     }
98 
99     /* expect NC_ERANGE */
100     si[0] = -129;
101     err = ncmpi_put_var_int_all(ncid, vid, si); EXPECT_ERR
102     if (si[0] != -129) { /* check if put buffer content is altered */
103         printf("Error at line %d: put buffer content altered %d (expecting -128)\n",__LINE__,si[0]);
104         nerrs++;
105     }
106 
107     /* expect NC_ERANGE */
108     si[0] = 256;
109     err = ncmpi_put_var_int_all(ncid, vid, si); EXPECT_ERR
110     if (si[0] != 256) { /* check if put buffer content is altered */
111         printf("Error at line %d: put buffer content altered %d (expecting 256)\n",__LINE__,si[0]);
112         nerrs++;
113     }
114 
115     /* expect no error */
116     si[0] = -128;
117     err = ncmpi_put_var_int_all(ncid, vid, si); ERR
118     si[0] = 0;
119     err = ncmpi_get_var_int_all(ncid, vid, si); ERR
120     if (si[0] != -128) {
121         printf("Error at line %d: unexpected read value %d (expecting -128)\n",__LINE__,si[0]);
122         nerrs++;
123     }
124 
125     err = ncmpi_close(ncid); ERR
126 
127     return nerrs;
128 }
129 
130 static
test_cdf5(char * filename)131 int test_cdf5(char *filename)
132 {
133     int err, nerrs=0, ncid, uc_vid, sc_vid, dimid;
134     unsigned char uc[1];
135     signed char sc[1];
136 
137     err = ncmpi_create(MPI_COMM_WORLD, filename, NC_CLOBBER|NC_64BIT_DATA, MPI_INFO_NULL, &ncid); ERR
138 
139     /* CDF-5 considers NC_BYTE a signed 1-byte integer and NC_UBYTE an
140      * unsigned 1-byte integer. The special case in CDF-2 for skipping
141      * NC_ERANGE checking for converting between NC_BYTE and unsigned
142      * char is no longer held.
143      */
144     uc[0] = 255;
145     err = ncmpi_put_att_uchar(ncid, NC_GLOBAL, "att1", NC_UBYTE, 1, uc); ERR
146 
147     /* in CDF-5, get 255 to a schar buffer should result in NC_ERANGE */
148     err = ncmpi_get_att_schar(ncid, NC_GLOBAL, "att1", sc); EXPECT_ERR
149 
150     sc[0] = -1; /* a value should cause NC_ERANGE */
151     err = ncmpi_put_att_schar(ncid, NC_GLOBAL, "att2", NC_UBYTE, 1, sc); EXPECT_ERR
152 
153     err = ncmpi_def_dim(ncid, "x", 1, &dimid); ERR
154     err = ncmpi_def_var(ncid, "var_ubyte", NC_UBYTE, 1, &dimid, &uc_vid); ERR
155     err = ncmpi_def_var(ncid, "var_byte",  NC_BYTE,  1, &dimid, &sc_vid); ERR
156     err = ncmpi_enddef(ncid); ERR
157 
158     uc[0] = 255;
159     err = ncmpi_put_var_uchar_all(ncid, uc_vid, uc); ERR
160 
161     /* in CDF-5, get 255 to an schar should result in NC_ERANGE */
162     err = ncmpi_get_var_schar_all(ncid, uc_vid, sc); EXPECT_ERR
163 
164     sc[0] = -1; /* in CDF-5, put -1 to an uchar should result in NC_ERANGE */
165     err = ncmpi_put_var_schar_all(ncid, uc_vid, sc); EXPECT_ERR
166 
167     uc[0] = 255; /* in CDF-5, put 255 to a schar should result in NC_ERANGE */
168     err = ncmpi_put_var_uchar_all(ncid, sc_vid, uc); EXPECT_ERR
169 
170     sc[0] = -1;
171     err = ncmpi_put_var_schar_all(ncid, sc_vid, sc); ERR
172     uc[0] = 0; /* in CDF-5, get -1 to an uchar should result in NC_ERANGE */
173     err = ncmpi_get_var_uchar_all(ncid, sc_vid, uc); EXPECT_ERR
174 
175     err = ncmpi_close(ncid); ERR
176 
177     return nerrs;
178 }
179 
main(int argc,char * argv[])180 int main(int argc, char* argv[])
181 {
182     char filename[256];
183     int err, nerrs=0, rank;
184 
185     MPI_Init(&argc, &argv);
186     MPI_Comm_rank(MPI_COMM_WORLD, &rank);
187 
188     if (argc > 2) {
189         if (!rank) printf("Usage: %s [filename]\n",argv[0]);
190         MPI_Finalize();
191         return 0;
192     }
193     if (argc == 2) snprintf(filename, 256, "%s", argv[1]);
194     else           strcpy(filename, "testfile.nc");
195     MPI_Bcast(filename, 256, MPI_CHAR, 0, MPI_COMM_WORLD);
196 
197     if (rank == 0) {
198         char *cmd_str = (char*)malloc(strlen(argv[0]) + 256);
199         sprintf(cmd_str, "*** TESTING C   %s for checking for NC_ERANGE ", basename(argv[0]));
200         printf("%-66s ------ ", cmd_str); fflush(stdout);
201         free(cmd_str);
202     }
203 
204     nerrs += test_cdf2(filename);
205     nerrs += test_cdf5(filename);
206 
207     /* check if PnetCDF freed all internal malloc */
208     MPI_Offset malloc_size, sum_size;
209     err = ncmpi_inq_malloc_size(&malloc_size);
210     if (err == NC_NOERR) {
211         MPI_Reduce(&malloc_size, &sum_size, 1, MPI_OFFSET, MPI_SUM, 0, MPI_COMM_WORLD);
212         if (rank == 0 && sum_size > 0)
213             printf("heap memory allocated by PnetCDF internally has %lld bytes yet to be freed\n",
214                    sum_size);
215     }
216 
217     MPI_Allreduce(MPI_IN_PLACE, &nerrs, 1, MPI_INT, MPI_SUM, MPI_COMM_WORLD);
218     if (rank == 0) {
219         if (nerrs) printf(FAIL_STR,nerrs);
220         else       printf(PASS_STR);
221     }
222 
223     MPI_Finalize();
224 
225     return 0;
226 }
227