1 /*
2  *  Copyright (C) 2014, Northwestern University and Argonne National Laboratory
3  *  See COPYRIGHT notice in top-level directory.
4  */
5 /* $Id: tst_small.c 2744 2016-12-28 16:25:22Z wkliao $ */
6 
7 /* This program is based on the test program tst_small.c of the netCDF package */
8 
9 /* This is part of the netCDF package. Copyright 2005 University
10    Corporation for Atmospheric Research/Unidata See COPYRIGHT file for
11    conditions of use. See www.unidata.ucar.edu for more info.
12 
13    Test small files.
14 */
15 
16 #include <stdio.h>
17 #include <string.h>
18 #include <libgen.h> /* basename() */
19 #include <mpi.h>
20 #include <pnetcdf.h>
21 
22 #include <testutils.h>
23 
24 /* Test everything for classic, 64-bit offseti, and 64-bit data files. */
25 #define NUM_FORMATS 3
26 
27 #define ATT_NAME "Atom"
28 #define MAX_LEN 7
29 
30 #define ERR {if (err != NC_NOERR) {printf("Error at %s line %d: %s\n",__func__,__LINE__,ncmpi_strerror(err)); return 1;}}
31 
32 static int
test_small_atts(const char * testfile,int cmode)33 test_small_atts(const char *testfile, int cmode)
34 {
35    int ncid, err;
36    char att[MAX_LEN + 1], att_in[MAX_LEN + 1], source[MAX_LEN + 1] = "0123456";
37    int ndims, nvars, natts, unlimdimid;
38    MPI_Offset len_in;
39    int t, f;
40 
41    /* Run this with and without fill mode. */
42    for (f = 0; f < 2; f++)
43    {
44       /* Create small files with an attribute that grows by one each
45        * time. */
46       for (t = 1; t < MAX_LEN; t++)
47       {
48 	 /* Create null-terminated text string of correct length. */
49 	 strncpy(att, source, t);
50          att[t] = '\0';
51 
52 	 /* Create a file with one attribute. */
53          err = ncmpi_create(MPI_COMM_WORLD, testfile,cmode, MPI_INFO_NULL, &ncid); ERR
54 	 err = ncmpi_put_att_text(ncid, NC_GLOBAL, ATT_NAME, t + 1, att); ERR
55 	 if (f) { err=ncmpi_set_fill(ncid, NC_NOFILL, NULL); ERR}
56 	 err=ncmpi_close(ncid); ERR;
57 
58 	 /* Reopen the file and check it. */
59          err=ncmpi_open(MPI_COMM_WORLD, testfile, NC_NOWRITE, MPI_INFO_NULL, &ncid); ERR
60 	 err=ncmpi_inq(ncid, &ndims, &nvars, &natts, &unlimdimid); ERR
61 	 if (ndims != 0 && nvars != 0 && natts != 1 && unlimdimid != -1) {printf("Error at line %d\n",__LINE__);return 1;}
62 	 err=ncmpi_inq_attlen(ncid, NC_GLOBAL, ATT_NAME, &len_in); ERR
63 	 if (len_in != t + 1) {printf("Error at line %d\n",__LINE__);return 1;}
64 	 err=ncmpi_get_att_text(ncid, NC_GLOBAL, ATT_NAME, att_in); ERR
65 	 if (strncmp(att_in, att, t)) {printf("Error at line %d\n",__LINE__);return 1;}
66 	 err=ncmpi_close(ncid); ERR
67       }
68    }
69    return 0;
70 }
71 
72 #define DIM1_NAME "Time"
73 #define DIM2_NAME "DataStrLen"
74 #define VAR_NAME "Times"
75 #define STR_LEN 19
76 #define NUM_VALS 2
77 #define NDIMS 2
78 #define TITLE " OUTPUT FROM WRF V2.0.3.1 MODEL"
79 #define ATT_NAME2 "TITLE"
80 
81 /* Test a small file with an unlimited dimension. NOTE: Normally I
82  * write a NULL terminator for my attributes and text strings, but
83  * this reproduces a bug that a fortran user sent us. So string data
84  * are written to the file without null terminators. - Ed */
85 static int
test_small_unlim(const char * testfile,int cmode)86 test_small_unlim(const char *testfile, int cmode)
87 {
88    int i, err, ncid, dimids[NDIMS], varid;
89    char data[NUM_VALS][STR_LEN + 1], data_in[NUM_VALS][STR_LEN];
90    int ndims, nvars, natts, unlimdimid;
91    MPI_Offset start[NDIMS], count[NDIMS];
92 
93    /* Create null-terminated text strings of correct length. */
94    /*for (i = 0; i < NUM_VALS; i++)
95      strcpy(data[i], source);*/
96    strcpy(data[0], "2005-04-11_12:00:00");
97    strcpy(data[1], "2005-04-11_13:00:00");
98 
99    /* Create a file with two dimensions, one unlimited, and one
100     * var, and a global att. */
101    err=ncmpi_create(MPI_COMM_WORLD, testfile,cmode, MPI_INFO_NULL, &ncid); ERR
102    err=ncmpi_def_dim(ncid, DIM1_NAME, NC_UNLIMITED, dimids); ERR
103    err=ncmpi_def_dim(ncid, DIM2_NAME, STR_LEN, &dimids[1]); ERR
104    err=ncmpi_def_var(ncid, VAR_NAME, NC_CHAR, 2, dimids, &varid); ERR
105    err=ncmpi_put_att_text(ncid, NC_GLOBAL, ATT_NAME2, strlen(TITLE), TITLE); ERR
106    err=ncmpi_enddef(ncid); ERR
107 
108    /* Write some records of var data. */
109    count[0] = 1;
110    count[1] = STR_LEN;
111    start[1] = 0;
112    for (start[0] = 0; start[0] < NUM_VALS; start[0]++) {
113       err=ncmpi_put_vara_text_all(ncid, varid, start, count, data[start[0]]); ERR
114    }
115 
116    /* We're done! */
117    err=ncmpi_close(ncid); ERR
118 
119    /* Reopen the file and check it. */
120    err=ncmpi_open(MPI_COMM_WORLD, testfile, NC_NOWRITE, MPI_INFO_NULL, &ncid); ERR
121    err=ncmpi_inq(ncid, &ndims, &nvars, &natts, &unlimdimid); ERR
122    if (ndims != 2 && nvars != 1 && natts != 0 && unlimdimid != 0) {printf("Error at line %d\n",__LINE__);return 1;}
123    err=ncmpi_get_var_text_all(ncid, varid, (char *)data_in); ERR
124    for (i = 0; i < NUM_VALS; i++)
125       /* if (strncmp(data[i], data_in[i], STR_LEN)) {printf("Error at line %d\n",__LINE__);return 1;} */
126       if (strncmp(data[i], data_in[i], STR_LEN)) {
127 printf("i=%d data=%s data_in=%s\n",i,data[i],data_in[i]);
128       }
129    err=ncmpi_close(ncid); ERR
130    return 0;
131 }
132 
133 /* Test a small file with a fixed dimension. */
134 static int
test_small_fixed(const char * testfile,int cmode)135 test_small_fixed(const char *testfile, int cmode)
136 {
137    int i, err, ncid, dimids[NDIMS], varid;
138    char data[NUM_VALS][STR_LEN + 1], data_in[NUM_VALS][STR_LEN];
139    int ndims, nvars, natts, unlimdimid;
140    MPI_Offset start[NDIMS], count[NDIMS];
141 
142    /* Create null-terminated text strings of correct length. */
143    /*for (i = 0; i < NUM_VALS; i++)
144      strcpy(data[i], source);*/
145    strcpy(data[0], "2005-04-11_12:00:00");
146    strcpy(data[1], "2005-04-11_13:00:00");
147 
148    /* Create a file with two dimensions, one unlimited, and one
149     * var, and a global att. */
150    err=ncmpi_create(MPI_COMM_WORLD, testfile,cmode, MPI_INFO_NULL, &ncid); ERR
151    err=ncmpi_def_dim(ncid, DIM1_NAME, NUM_VALS, dimids); ERR
152    err=ncmpi_def_dim(ncid, DIM2_NAME, STR_LEN, &dimids[1]); ERR
153    err=ncmpi_def_var(ncid, VAR_NAME, NC_CHAR, NDIMS, dimids, &varid); ERR
154    err=ncmpi_put_att_text(ncid, NC_GLOBAL, ATT_NAME2, strlen(TITLE), TITLE); ERR
155    err=ncmpi_enddef(ncid); ERR
156 
157    /* Write some records of var data. */
158    count[0] = 1;
159    count[1] = STR_LEN;
160    start[1] = 0;
161    for (start[0] = 0; start[0] < NUM_VALS; start[0]++) {
162       err=ncmpi_put_vara_text_all(ncid, varid, start, count, data[start[0]]); ERR
163    }
164 
165    /* We're done! */
166    err=ncmpi_close(ncid); ERR
167 
168    /* Reopen the file and check it. */
169    err=ncmpi_open(MPI_COMM_WORLD, testfile, NC_NOWRITE, MPI_INFO_NULL, &ncid); ERR
170    err=ncmpi_inq(ncid, &ndims, &nvars, &natts, &unlimdimid); ERR
171    if (ndims != 2 && nvars != 1 && natts != 0 && unlimdimid != -1) {printf("Error at line %d\n",__LINE__);return 1;}
172    err=ncmpi_get_var_text_all(ncid, varid, (char *)data_in); ERR
173    for (i = 0; i < NUM_VALS; i++)
174       if (strncmp(data[i], data_in[i], STR_LEN)) {printf("Error at line %d\n",__LINE__);return 1;}
175    err=ncmpi_close(ncid); ERR
176    return 0;
177 }
178 
179 /* Test a small file with one var. */
180 static int
test_small_one(const char * testfile,int cmode)181 test_small_one(const char *testfile, int cmode)
182 {
183    int err, ncid, dimid, varid;
184    char data = 'h', data_in;
185    int ndims, nvars, natts, unlimdimid;
186    MPI_Offset start[NDIMS], count[NDIMS];
187 
188    /* Create a file with one ulimited dimensions, and one var. */
189    err=ncmpi_create(MPI_COMM_WORLD, testfile,cmode, MPI_INFO_NULL, &ncid); ERR
190    err=ncmpi_def_dim(ncid, DIM1_NAME, NC_UNLIMITED, &dimid); ERR
191    err=ncmpi_def_var(ncid, VAR_NAME, NC_CHAR, 1, &dimid, &varid); ERR
192    err=ncmpi_enddef(ncid); ERR
193 
194    /* Write one record of var data, a single character. */
195    count[0] = 1;
196    start[0] = 0;
197    err=ncmpi_put_vara_text_all(ncid, varid, start, count, &data); ERR
198 
199    /* We're done! */
200    err=ncmpi_close(ncid); ERR
201 
202    /* Reopen the file and check it. */
203    err=ncmpi_open(MPI_COMM_WORLD, testfile, NC_NOWRITE, MPI_INFO_NULL, &ncid); ERR
204    err=ncmpi_inq(ncid, &ndims, &nvars, &natts, &unlimdimid); ERR
205    if (ndims != 1 && nvars != 1 && natts != 0 && unlimdimid != 0) {printf("Error at line %d\n",__LINE__);return 1;}
206    err=ncmpi_get_var_text_all(ncid, varid, &data_in); ERR
207    if (data_in != data) {printf("Error at line %d\n",__LINE__);return 1;}
208    err=ncmpi_close(ncid); ERR
209    return 0;
210 }
211 
212 #define ONE_DIM 1
213 #define MAX_RECS 10
214 
215 /* Test a small file with one record var, which grows. */
216 static int
test_one_growing(const char * testfile,int cmode)217 test_one_growing(const char *testfile, int cmode)
218 {
219    int err, ncid, dimid, varid;
220    char data[MAX_RECS], data_in;
221    MPI_Offset start[ONE_DIM], count[ONE_DIM], index[ONE_DIM], len_in;
222    int r, f;
223 
224    /* Create some phoney data. */
225    for (data[0] = 'a', r = 1; r < MAX_RECS; r++)
226       data[r] = data[r - 1] + 1;
227 
228    /* Run this with and without fill mode. */
229    for (f = 0; f < 2; f++)
230    {
231       /* Create a file with one ulimited dimensions, and one var. */
232       err=ncmpi_create(MPI_COMM_WORLD, testfile,cmode, MPI_INFO_NULL, &ncid); ERR
233       err=ncmpi_def_dim(ncid, DIM1_NAME, NC_UNLIMITED, &dimid); ERR
234       err=ncmpi_def_var(ncid, VAR_NAME, NC_CHAR, 1, &dimid, &varid); ERR
235       err=ncmpi_close(ncid); ERR
236 
237       /* Normally one would not close and reopen the file for each
238        * record, but I am giving the library a little work-out here... */
239       for (r = 0; r < MAX_RECS; r++)
240       {
241 	 /* Write one record of var data, a single character. */
242          err=ncmpi_open(MPI_COMM_WORLD, testfile, NC_WRITE, MPI_INFO_NULL, &ncid); ERR
243 	 /* if (f) { err=ncmpi_set_fill(ncid, NC_NOFILL, NULL); ERR} */
244 	 count[0] = 1;
245 	 start[0] = r;
246 	 err=ncmpi_put_vara_text_all(ncid, varid, start, count, &data[r]); ERR
247 	 err=ncmpi_close(ncid); ERR
248 
249 	 /* Reopen the file and check it. */
250          err=ncmpi_open(MPI_COMM_WORLD, testfile, NC_NOWRITE, MPI_INFO_NULL, &ncid); ERR
251 	 err=ncmpi_inq_dimlen(ncid, 0, &len_in); ERR
252 	 if (len_in != r + 1) {printf("Error at line %d\n",__LINE__);return 1;}
253 	 index[0] = r;
254 	 err=ncmpi_begin_indep_data(ncid); ERR
255 	 err=ncmpi_get_var1_text(ncid, 0, index, &data_in); ERR
256 	 if (data_in != data[r]) {printf("Error at line %d\n",__LINE__);return 1;}
257 	 err=ncmpi_close(ncid); ERR
258       } /* Next record. */
259    }
260    return 0;
261 }
262 
263 #define ONE_DIM 1
264 #define MAX_RECS 10
265 
266 /* Test a small file with one record var, which grows, and has
267  * attributes. */
268 static int
test_one_growing_with_att(const char * testfile,int cmode)269 test_one_growing_with_att(const char *testfile, int cmode)
270 {
271    int err, ncid, dimid, varid;
272    char data[MAX_RECS], data_in;
273    char att_name[NC_MAX_NAME + 1];
274    MPI_Offset start[ONE_DIM], count[ONE_DIM], index[ONE_DIM], len_in;
275    int r;
276 
277    /* Create a file with one ulimited dimensions, and one var. */
278    err=ncmpi_create(MPI_COMM_WORLD, testfile,cmode, MPI_INFO_NULL, &ncid); ERR
279    err=ncmpi_def_dim(ncid, DIM1_NAME, NC_UNLIMITED, &dimid); ERR
280    err=ncmpi_def_var(ncid, VAR_NAME, NC_CHAR, 1, &dimid, &varid); ERR
281    err=ncmpi_close(ncid); ERR
282 
283    /* Create some phoney data. */
284    for (data[0] = 'a', r = 1; r < MAX_RECS; r++)
285       data[r] = data[r - 1] + 1;
286 
287    /* Normally one would not close and reopen the file for each
288     * record, nor add an attribute each time I add a record, but I am
289     * giving the library a little work-out here... */
290    for (r = 0; r < MAX_RECS; r++)
291    {
292       /* Write one record of var data, a single character. */
293       err=ncmpi_open(MPI_COMM_WORLD, testfile, NC_WRITE, MPI_INFO_NULL, &ncid); ERR
294       count[0] = 1;
295       start[0] = r;
296       err=ncmpi_put_vara_text_all(ncid, varid, start, count, &data[r]); ERR
297       sprintf(att_name, "a_%d", data[r]);
298       err=ncmpi_redef(ncid); ERR
299       err=ncmpi_put_att_text(ncid, varid, att_name, 1, &data[r]); ERR
300       err=ncmpi_close(ncid); ERR
301 
302       /* Reopen the file and check it. */
303       err=ncmpi_open(MPI_COMM_WORLD, testfile, NC_NOWRITE, MPI_INFO_NULL, &ncid); ERR
304       err=ncmpi_inq_dimlen(ncid, 0, &len_in); ERR
305       if (len_in != r + 1) {printf("Error at line %d\n",__LINE__);return 1;}
306       index[0] = r;
307       err=ncmpi_begin_indep_data(ncid); ERR
308       err=ncmpi_get_var1_text(ncid, 0, index, &data_in); ERR
309       if (data_in != data[r]) {printf("Error at line %d\n",__LINE__);return 1;}
310       err=ncmpi_get_att_text(ncid, varid, att_name, &data_in); ERR
311       if (data_in != data[r]) {printf("Error at line %d\n",__LINE__);return 1;}
312       err=ncmpi_close(ncid); ERR
313    } /* Next record. */
314    return 0;
315 }
316 
317 #define VAR_NAME2 "var2"
318 #define NUM_VARS 2
319 
320 /* Test a small file with two record vars, which grow, and has
321  * attributes added. */
322 static int
test_two_growing_with_att(const char * testfile,int cmode)323 test_two_growing_with_att(const char *testfile, int cmode)
324 {
325    int err, ncid, dimid, varid[NUM_VARS];
326    char data[MAX_RECS], data_in;
327    char att_name[NC_MAX_NAME + 1];
328    MPI_Offset start[ONE_DIM], count[ONE_DIM], index[ONE_DIM], len_in;
329    int v, r;
330 
331    /* Create a file with one ulimited dimensions, and one var. */
332    err=ncmpi_create(MPI_COMM_WORLD, testfile,cmode, MPI_INFO_NULL, &ncid); ERR
333    err=ncmpi_def_dim(ncid, DIM1_NAME, NC_UNLIMITED, &dimid); ERR
334    err=ncmpi_def_var(ncid, VAR_NAME, NC_CHAR, 1, &dimid, &varid[0]); ERR
335    err=ncmpi_def_var(ncid, VAR_NAME2, NC_CHAR, 1, &dimid, &varid[1]); ERR
336    err=ncmpi_close(ncid); ERR
337 
338    /* Create some phoney data. */
339    for (data[0] = 'a', r = 1; r < MAX_RECS; r++)
340       data[r] = data[r - 1] + 1;
341 
342    /* Normally one would not close and reopen the file for each
343     * record, nor add an attribute each time I add a record, but I am
344     * giving the library a little work-out here... */
345    for (r = 0; r < MAX_RECS; r++)
346    {
347       /* Write one record of var data, a single character. */
348       err=ncmpi_open(MPI_COMM_WORLD, testfile, NC_WRITE, MPI_INFO_NULL, &ncid); ERR
349       count[0] = 1;
350       start[0] = r;
351       sprintf(att_name, "a_%d", data[r]);
352       for (v = 0; v < NUM_VARS; v++)
353       {
354 	 err=ncmpi_put_vara_text_all(ncid, varid[v], start, count, &data[r]); ERR
355 	 err=ncmpi_redef(ncid); ERR
356 	 err=ncmpi_put_att_text(ncid, varid[v], att_name, 1, &data[r]); ERR
357 	 err=ncmpi_enddef(ncid); ERR
358       }
359       err=ncmpi_close(ncid); ERR
360 
361       /* Reopen the file and check it. */
362       err=ncmpi_open(MPI_COMM_WORLD, testfile, NC_NOWRITE, MPI_INFO_NULL, &ncid); ERR
363       err=ncmpi_inq_dimlen(ncid, 0, &len_in); ERR
364       if (len_in != r + 1) {printf("Error at line %d\n",__LINE__);return 1;}
365       index[0] = r;
366       err=ncmpi_begin_indep_data(ncid); ERR
367       for (v = 0; v < NUM_VARS; v++)
368       {
369 	 err=ncmpi_get_var1_text(ncid, varid[v], index, &data_in); ERR
370 	 if (data_in != data[r]) {printf("Error at line %d\n",__LINE__);return 1;}
371       }
372       err=ncmpi_close(ncid); ERR
373    } /* Next record. */
374    return 0;
375 }
376 
377 /* Test a small file with one var and one att. */
378 static int
test_one_with_att(const char * testfile,int cmode)379 test_one_with_att(const char *testfile, int cmode)
380 {
381    int err, ncid, dimid, varid;
382    char data = 'h', data_in;
383    int ndims, nvars, natts, unlimdimid;
384    MPI_Offset start[NDIMS], count[NDIMS];
385 
386    /* Create a file with one ulimited dimensions, and one var. */
387    err=ncmpi_create(MPI_COMM_WORLD, testfile,cmode, MPI_INFO_NULL, &ncid); ERR
388    err=ncmpi_def_dim(ncid, DIM1_NAME, NC_UNLIMITED, &dimid); ERR
389    err=ncmpi_def_var(ncid, VAR_NAME, NC_CHAR, 1, &dimid, &varid); ERR
390    err=ncmpi_put_att_text(ncid, NC_GLOBAL, ATT_NAME, 1, &data); ERR
391    err=ncmpi_enddef(ncid); ERR
392 
393    /* Write one record of var data, a single character. */
394    count[0] = 1;
395    start[0] = 0;
396    err=ncmpi_put_vara_text_all(ncid, varid, start, count, &data); ERR
397 
398    /* We're done! */
399    err=ncmpi_close(ncid); ERR
400 
401    /* Reopen the file and check it. */
402    err=ncmpi_open(MPI_COMM_WORLD, testfile, NC_NOWRITE, MPI_INFO_NULL, &ncid); ERR
403    err=ncmpi_inq(ncid, &ndims, &nvars, &natts, &unlimdimid); ERR
404    if (ndims != 1 && nvars != 1 && natts != 0 && unlimdimid != 0) {printf("Error at line %d\n",__LINE__);return 1;}
405    err=ncmpi_get_var_text_all(ncid, varid, &data_in); ERR
406    if (data_in != data) {printf("Error at line %d\n",__LINE__);return 1;}
407    err=ncmpi_get_att_text(ncid, NC_GLOBAL, ATT_NAME, &data_in); ERR
408    if (data_in != data) {printf("Error at line %d\n",__LINE__);return 1;}
409    err=ncmpi_close(ncid); ERR
410    return 0;
411 }
412 
main(int argc,char * argv[])413 int main(int argc, char *argv[])
414 {
415     char filename[256];
416     int i, rank, nprocs, err, nerrs=0;
417     int cmode[NUM_FORMATS]={0, NC_64BIT_OFFSET, NC_64BIT_DATA};
418 
419     MPI_Init(&argc, &argv);
420     MPI_Comm_size(MPI_COMM_WORLD, &nprocs);
421     MPI_Comm_rank(MPI_COMM_WORLD, &rank);
422 
423     if (argc > 2) {
424         if (!rank) printf("Usage: %s [filename]\n",argv[0]);
425         MPI_Finalize();
426         return 0;
427     }
428     if (argc == 2) snprintf(filename, 256, "%s", argv[1]);
429     else           strcpy(filename, "testfile.nc");
430     MPI_Bcast(filename, 256, MPI_CHAR, 0, MPI_COMM_WORLD);
431 
432     char *cmd_str = (char*)malloc(strlen(argv[0]) + 256);
433     sprintf(cmd_str, "*** TESTING C   %s for emulating netCDF tst_small ", basename(argv[0]));
434     if (rank == 0) printf("%-66s ------ ", cmd_str);
435     free(cmd_str);
436 
437     for (i=0; i<NUM_FORMATS; i++) {
438 #ifdef DEBUG
439       printf("*** testing simple small file with a global attribute...");
440 #endif
441       nerrs += test_small_atts(filename, cmode[i]|NC_CLOBBER);
442 
443 #ifdef DEBUG
444       printf("*** testing simple small file with fixed dimensions...");
445 #endif
446       nerrs += test_small_fixed(filename, cmode[i]|NC_CLOBBER);
447 
448 #ifdef DEBUG
449       printf("*** testing simple small file with an unlimited dimension...");
450 #endif
451       nerrs += test_small_unlim(filename, cmode[i]|NC_CLOBBER);
452 
453 #ifdef DEBUG
454       printf("*** testing small file with one variable...");
455 #endif
456       nerrs += test_small_one(filename, cmode[i]|NC_CLOBBER);
457 
458 #ifdef DEBUG
459       printf("*** testing small file with one variable and one att...");
460 #endif
461       nerrs += test_one_with_att(filename, cmode[i]|NC_CLOBBER);
462 
463 #ifdef DEBUG
464       printf("*** testing small file with one record variable, which grows...");
465 #endif
466       nerrs += test_one_growing(filename, cmode[i]|NC_CLOBBER);
467 
468 #ifdef DEBUG
469       printf("*** testing small file with one growing record "
470 	     "variable, with attributes added...");
471 #endif
472       nerrs += test_one_growing_with_att(filename, cmode[i]|NC_CLOBBER);
473 
474 #ifdef DEBUG
475       if (verbose) printf("*** testing small file with two growing record "
476 	     "variables, with attributes added...");
477 #endif
478       nerrs += test_two_growing_with_att(filename, cmode[i]|NC_CLOBBER);
479    }
480 
481     /* check if PnetCDF freed all internal malloc */
482     MPI_Offset malloc_size, sum_size;
483     err = ncmpi_inq_malloc_size(&malloc_size);
484     if (err == NC_NOERR) {
485         MPI_Reduce(&malloc_size, &sum_size, 1, MPI_OFFSET, MPI_SUM, 0, MPI_COMM_WORLD);
486         if (rank == 0 && sum_size > 0)
487             printf("heap memory allocated by PnetCDF internally has %lld bytes yet to be freed\n",
488                    sum_size);
489     }
490 
491     MPI_Allreduce(MPI_IN_PLACE, &nerrs, 1, MPI_INT, MPI_SUM, MPI_COMM_WORLD);
492     if (rank == 0) {
493         if (nerrs) printf(FAIL_STR,nerrs);
494         else       printf(PASS_STR);
495     }
496 
497     MPI_Finalize();
498     return 0;
499 }
500 
501