1   /**
2    * Copyright 2010, CSIRO Australia.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *         http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  
17  package au.csiro.netcdf;
18  
19  
20  import java.io.ByteArrayInputStream;
21  import java.io.File;
22  import java.io.IOException;
23  import java.util.List;
24  
25  import junit.framework.TestCase;
26  
27  import org.apache.commons.cli.MissingOptionException;
28  import org.apache.commons.cli.Options;
29  
30  import ucar.ma2.DataType;
31  import ucar.nc2.Attribute;
32  import ucar.nc2.NCdumpW;
33  import ucar.nc2.NetcdfFile;
34  import ucar.nc2.Variable;
35  import au.csiro.netcdf.cli.Command;
36  import au.csiro.netcdf.util.NetCDFUtils;
37  
38  /**
39   * This class is a unit test suite to verify that the NcDefineVariable command operates correctly.
40   * <p>
41   * Copyright 2010, CSIRO Australia All rights reserved.
42   * 
43   * @author Martin Pienaar on 23/03/2010
44   * @version $Revision: 78 $ $Date: 2010-07-24 16:23:13 +1000 (Sat, 24 Jul 2010) $
45   */
46  public class TestNcDefineVariable extends TestCase
47  {
48      /**
49       * The name of the command line option used for specifying the output netCDF file name.
50       */
51      private static final String OUTPUT_FILE = "outputFileName";
52  
53      /**
54       * The name of the command line option used for specifying the name of the variable.
55       */
56      private static final String VARIABLE_NAME = "variableName";
57  
58      /**
59       * The name of the command line option used for specifying the name of the variable.
60       */
61      private static final String VARIABLE_DATA_TYPE = "variableDataType";
62      
63      /**
64       * The name of the command line option used for specifying the attributes of the variable.
65       */
66      public static final String VARIABLE_ATTRIBUTES = "variableAttributes";
67      
68      /**
69       * The name of the command line option used for specifying the size of the dimension.
70       */
71      private static final String DIMENSION_SIZE = "dimensionSize";
72  
73      /**
74       * The name of the command line option used for specifying the name of the dimensions.
75       */
76      private static final String DIMENSION_NAMES = "dimensionNames";
77  
78      /**
79       * The name of the command line option used for specifying the name of the dimension.
80       */
81      private static final String DIMENSION_NAME = "dimensionName";
82      
83      /**
84       * The name of the command line option used for specifying large file support.
85       */
86      private static final String LARGE_FILE_SUPPORT = "largeFileSupport";
87  
88      /**
89       * The name of the command line option used for specifying fillVar.
90       */
91      private static final String FILL_VAR = "fillVar";
92      
93      /**
94       * The name of the netCDF file to write to.
95       */
96      private static final String NC_FILE_NAME = System.getProperty("user.dir") + "\\test.nc";
97  
98      /**
99       * The testing value for the variableName option.
100      */
101     private static final String VARIABLE_NAME_VALUE = "myVariable";
102 
103     /**
104      * The testing value for the variableAttributes option.
105      */
106     private static final String VARIABLE_ATTRIBUTES_VALUE = "units=mm";
107 
108     
109     /**
110      * The testing value for the variableDataType option.
111      */
112     private static final String VARIABLE_DATA_TYPE_VALUE = "Char";
113     
114     /**
115      * The testing value for the dimensionName option.
116      */
117     private static final String DIMENSION_NAME_VALUE = "myDimension";
118     
119     /**
120      * The testing value for the dimension size.
121      */
122     private static final String DIMENSION_SIZE_VALUE = "10";
123     
124     /** Name for history Attribute */
125     private static final String ATTR_HISTORY = "history";
126     
127     /** Value for history Attribute */
128     private static final String ATTR_HISTORY_VALUE = "LAST UPDATED: 2010-03-23 05:02:18 UTC\n";
129 
130     /** Name for author Attribute */
131     private static final String ATTR_AUTHOR = "author";
132     
133     /** Value for author Attribute */
134     private static final String ATTR_AUTHOR_VALUE = "John Citizen";
135     
136     /** Complex list of attribute values including special characters */
137     private static final String SPECIAL_CHAR_INPUT_ATTRIBUTE_STRING = ATTR_HISTORY + "=" +
138         " 01234567890 "  +
139         " +\\=-~!@#$%^&*() " +
140         " []{}|" +
141         " ;': " +
142         " \\,./<>? " +
143         ATTR_HISTORY_VALUE +
144         "," + 
145         "" +
146         ATTR_AUTHOR + "=" +
147         ATTR_AUTHOR_VALUE;
148     
149     /**
150      * An instance of the NcDefineVariable command that we want to test.
151      */
152     private Command ncDefineVariable = new NcDefineVariable();
153 
154     /**
155      * An instance of the NcDefineDimension command that we want to test.
156      */
157     private NcDefineDimension ncDefineDimension = new NcDefineDimension();
158     
159     private File ncFile = null;
160     
161     private static final String COMMAND_NAME = "ncdefineVar";
162     
163     /*
164      * (non-Javadoc)
165      * 
166      * @see junit.framework.TestCase#setUp()
167      */
168     @Override
169     protected void setUp() throws Exception
170     {
171         String[] defineDimensionArgs = new String[] { 
172                 ncDefineDimension.getCommandName(), 
173                 "-" + OUTPUT_FILE, NC_FILE_NAME,
174                 "-" + DIMENSION_NAME, DIMENSION_NAME_VALUE,
175                 "-" + DIMENSION_SIZE, DIMENSION_SIZE_VALUE};
176         
177         super.setUp();
178 
179         ncFile = new File(NC_FILE_NAME);
180         if (ncFile.exists())
181         {
182             ncFile.delete();
183         }
184 
185         /*
186          * We need to create a dimension before creating a variable.
187          */
188         ncDefineDimension.execute(defineDimensionArgs);
189         assertTrue("The nc file was not created: " + NC_FILE_NAME, ncFile.exists());
190     }
191 
192     /*
193      * (non-Javadoc)
194      * 
195      * @see junit.framework.TestCase#tearDown()
196      */
197     @Override
198     protected void tearDown() throws Exception
199     {
200         super.tearDown();
201         ncFile.delete();
202     }
203 
204     /**
205      * Test that the commands options are used.
206      */
207     public final void testCreateOptions()
208     {
209         Options options = ncDefineVariable.createOptions();
210 
211         assertTrue("The following option is not recognised by the command: " + OUTPUT_FILE, 
212                 options.hasOption(OUTPUT_FILE));
213         assertTrue("The following option is not recognised by the command: " + VARIABLE_NAME, 
214                 options.hasOption(VARIABLE_NAME));
215         assertTrue("The following option is not recognised by the command: " + VARIABLE_DATA_TYPE, 
216                 options.hasOption(VARIABLE_DATA_TYPE));
217         assertTrue("The following option is not recognised by the command: " + DIMENSION_NAMES, 
218                 options.hasOption(DIMENSION_NAMES));
219         assertTrue("The following option is not recognised by the command: " + LARGE_FILE_SUPPORT, 
220                 options.hasOption(LARGE_FILE_SUPPORT));
221         assertTrue("The following option is not recognised by the command: " + FILL_VAR, 
222                 options.hasOption(FILL_VAR));
223     }
224 
225     /**
226      * Test that the commands options are used only when valid.
227      */
228     public final void testIllegalCreateOptions()
229     {
230         Options options = ncDefineVariable.createOptions();
231 
232         assertFalse("The command incorrectly detected 'garbage' as a valid option", 
233                 options.hasOption("garbage"));
234     }
235     
236     /**
237      * Test a valid command creates a netCDF file with the specified Variable name
238      */
239     public final void testValidCommand() throws Exception
240     {
241         String[] defineVariableArgs = new String[] { 
242                 ncDefineVariable.getCommandName(), 
243                 "-" + OUTPUT_FILE, NC_FILE_NAME,
244                 "-" + VARIABLE_NAME, VARIABLE_NAME_VALUE,
245                 "-" + VARIABLE_DATA_TYPE, VARIABLE_DATA_TYPE_VALUE,
246                 "-" + DIMENSION_NAMES, DIMENSION_NAME_VALUE};
247 
248         ncDefineVariable.execute(defineVariableArgs);
249 
250         NetcdfFile netcdfFile = NetcdfFile.open(NC_FILE_NAME);
251         
252         Variable variable = netcdfFile.findVariable(VARIABLE_NAME_VALUE);
253         assertTrue("The Variable was not defined: " + VARIABLE_NAME_VALUE, (variable != null));
254         
255         netcdfFile.close();
256     }
257 
258     /**
259      * Test a non-existent outputFileName argument.
260      */
261     public void testNonExistentOutputFileNameInExecuteCommand() throws Exception
262     {
263         String[] defineVariableArgs = new String[] { 
264                 ncDefineVariable.getCommandName(), 
265                 "-" + OUTPUT_FILE, "notGonnaFindMe.nc",
266                 "-" + VARIABLE_NAME, VARIABLE_NAME_VALUE,
267                 "-" + VARIABLE_DATA_TYPE, VARIABLE_DATA_TYPE_VALUE,
268                 "-" + DIMENSION_NAMES, DIMENSION_NAME_VALUE};
269 
270         try {
271             ncDefineVariable.execute(defineVariableArgs);
272         } catch(IllegalArgumentException iae) {
273             return;
274         }
275         assertTrue("outputFileName was invalid, and should have thrown an IllegalArgumentException", false);
276     }
277 
278     /**
279      * Test missing outputFileName argument.
280      */
281     public void testMissingOutputFileNameInExecuteCommand() throws Exception
282     {
283         String[] defineVariableArgs = new String[] { 
284                 ncDefineVariable.getCommandName(), 
285                 "-" + VARIABLE_NAME, VARIABLE_NAME_VALUE,
286                 "-" + VARIABLE_DATA_TYPE, VARIABLE_DATA_TYPE_VALUE,
287                 "-" + DIMENSION_NAMES, DIMENSION_NAME_VALUE};
288 
289         try {
290             ncDefineVariable.execute(defineVariableArgs);
291         } catch(MissingOptionException moe) {
292             return;
293         }
294         assertTrue("outputFileName argument was missing, and should have thrown a MissingOptionException", false);
295     }
296     
297     /**
298      * Test valid variableAttributes argument.
299      */
300     public void testValidVariableAttributesInExecuteCommand() throws Exception
301     {
302         String[] defineVariableArgs = new String[] { 
303                 ncDefineVariable.getCommandName(), 
304                 "-" + OUTPUT_FILE, NC_FILE_NAME,
305                 "-" + VARIABLE_NAME, VARIABLE_NAME_VALUE,
306                 "-" + VARIABLE_DATA_TYPE, VARIABLE_DATA_TYPE_VALUE,
307                 "-" + VARIABLE_ATTRIBUTES, VARIABLE_ATTRIBUTES_VALUE,
308                 "-" + DIMENSION_NAMES, DIMENSION_NAME_VALUE};
309 
310             ncDefineVariable.execute(defineVariableArgs);
311 
312             NetcdfFile netcdfFile = NetcdfFile.open(NC_FILE_NAME);
313             
314             Variable variable = netcdfFile.findVariable(VARIABLE_NAME_VALUE);
315             assertTrue("The Variable was not defined: " + VARIABLE_NAME_VALUE, (variable != null));
316             
317             netcdfFile.close();
318     }
319     
320     /**
321      * Test invalid variableAttributes argument.
322      */
323     public void testInvalidVariableAttributesInExecuteCommand() throws Exception
324     {
325         String[] defineVariableArgs = new String[] { 
326                 ncDefineVariable.getCommandName(), 
327                 "-" + OUTPUT_FILE, NC_FILE_NAME,
328                 "-" + VARIABLE_NAME, VARIABLE_NAME_VALUE,
329                 "-" + VARIABLE_DATA_TYPE, VARIABLE_DATA_TYPE_VALUE,
330                 "-" + VARIABLE_ATTRIBUTES, "missingEquals",
331                 "-" + DIMENSION_NAMES, DIMENSION_NAME_VALUE};
332 
333         try {
334             ncDefineVariable.execute(defineVariableArgs);
335         } catch(IllegalArgumentException iae) {
336             return;
337         }
338         assertTrue("variableAttributes are invalid, and should have thrown an IllegalArgumentException", false);
339     }
340     
341     /**
342      * Test that fillVar being set to true pads the variable with the fillVar value.
343      * In the case of a String, the fillVar value is "" (null).
344      */
345     public final void testFillVar() throws Exception
346     {
347         String[] defineVariableArgs = new String[] { 
348                 ncDefineVariable.getCommandName(), 
349                 "-" + OUTPUT_FILE, NC_FILE_NAME,
350                 "-" + VARIABLE_NAME, VARIABLE_NAME_VALUE,
351                 "-" + VARIABLE_DATA_TYPE, VARIABLE_DATA_TYPE_VALUE,
352                 "-" + DIMENSION_NAMES, DIMENSION_NAME_VALUE,
353                 "-" + FILL_VAR, "true"};
354         
355         ncDefineVariable.execute(defineVariableArgs);
356 
357         NetcdfFile netcdfFile = NetcdfFile.open(NC_FILE_NAME);
358         
359         Variable variable = netcdfFile.findVariable(VARIABLE_NAME_VALUE);
360 
361         String savedData = NCdumpW.printVariableData(variable, null);
362         
363         assertEquals("The Variable should have a value of \"\" because of fillVar being set to true: " + 
364                 VARIABLE_NAME_VALUE, savedData, VARIABLE_NAME_VALUE + " =  \"\"\n");
365         
366         netcdfFile.close();
367     }
368     
369     /**
370      * Test required arguments option
371      */
372     public final void testMissingRequiredOption()
373     {
374         String errors = null;
375 
376         String[] validArgs = new String[] { 
377                 ncDefineVariable.getCommandName(), 
378                 "-" + OUTPUT_FILE, NC_FILE_NAME,
379                 "-" + VARIABLE_NAME, VARIABLE_NAME_VALUE,
380                 "-" + VARIABLE_DATA_TYPE, VARIABLE_DATA_TYPE_VALUE,
381                 "-" + DIMENSION_NAMES, DIMENSION_NAME_VALUE};
382         
383         String[] missingOutputFileNameArgs = new String[] { 
384                 ncDefineVariable.getCommandName(), 
385                 "-" + VARIABLE_NAME, VARIABLE_NAME_VALUE,
386                 "-" + VARIABLE_DATA_TYPE, VARIABLE_DATA_TYPE_VALUE,
387                 "-" + DIMENSION_NAMES, DIMENSION_NAME_VALUE};
388 
389         String[] nonExistentOutputFileNameArgs = new String[] { 
390                 ncDefineVariable.getCommandName(), 
391                 "-" + OUTPUT_FILE, "notGonnaFindMe.nc",
392                 "-" + VARIABLE_NAME, VARIABLE_NAME_VALUE,
393                 "-" + VARIABLE_DATA_TYPE, VARIABLE_DATA_TYPE_VALUE,
394                 "-" + DIMENSION_NAMES, DIMENSION_NAME_VALUE};
395         
396         String[] missingVariableNameArgs = new String[] { 
397                 ncDefineVariable.getCommandName(), 
398                 "-" + OUTPUT_FILE, NC_FILE_NAME,
399                 "-" + VARIABLE_DATA_TYPE, VARIABLE_DATA_TYPE_VALUE,
400                 "-" + DIMENSION_NAMES, DIMENSION_NAME_VALUE};
401 
402         String[] missingVariableDataTypeArgs = new String[] { 
403                 ncDefineVariable.getCommandName(), 
404                 "-" + OUTPUT_FILE, NC_FILE_NAME,
405                 "-" + VARIABLE_NAME, VARIABLE_NAME_VALUE,
406                 "-" + DIMENSION_NAMES, DIMENSION_NAME_VALUE};
407 
408         String[] invalidVariableAttributesArgs = new String[] { 
409                 ncDefineVariable.getCommandName(), 
410                 "-" + OUTPUT_FILE, NC_FILE_NAME,
411                 "-" + VARIABLE_NAME, VARIABLE_NAME_VALUE,
412                 "-" + VARIABLE_ATTRIBUTES, "missingEquals",
413                 "-" + DIMENSION_NAMES, DIMENSION_NAME_VALUE};
414         
415         String[] missingDimensionNamesArgs = new String[] { 
416                 ncDefineVariable.getCommandName(), 
417                 "-" + OUTPUT_FILE, NC_FILE_NAME,
418                 "-" + VARIABLE_NAME, VARIABLE_NAME_VALUE,
419                 "-" + VARIABLE_DATA_TYPE, VARIABLE_DATA_TYPE_VALUE};
420 
421         String[] invalidVariableDataTypeArgs = new String[] { 
422                 ncDefineVariable.getCommandName(), 
423                 "-" + OUTPUT_FILE, NC_FILE_NAME,
424                 "-" + VARIABLE_NAME, VARIABLE_NAME_VALUE,
425                 "-" + VARIABLE_DATA_TYPE, "SQIUD"};
426         
427         errors = ncDefineVariable.validCommand(validArgs);
428         assertTrue("Command was valid, this should not cause an error", errors.isEmpty());
429         
430         errors = ncDefineVariable.validCommand(missingOutputFileNameArgs);
431         assertTrue("Command without outputFileName option should return an error", !errors.isEmpty());
432 
433         errors = ncDefineVariable.validCommand(nonExistentOutputFileNameArgs);
434         assertTrue("Command without existing outputFileName option should return an error", !errors.isEmpty());
435         
436         errors = ncDefineVariable.validCommand(missingVariableNameArgs);
437         assertTrue("Command without variableName option should return an error", !errors.isEmpty());
438         
439         errors = ncDefineVariable.validCommand(missingVariableDataTypeArgs);
440         assertTrue("Command without variableDataType option should return an error", !errors.isEmpty());
441 
442         errors = ncDefineVariable.validCommand(invalidVariableAttributesArgs);
443         assertTrue("Command with invalid variableAttributes option should return an error", !errors.isEmpty());
444 
445         errors = ncDefineVariable.validCommand(invalidVariableDataTypeArgs);
446         assertTrue("Command with invalid variableDataType option should return an error", !errors.isEmpty());
447     }
448     
449     /**
450      * Make sure that the correct command name comes back.
451      */
452     public void testCommandName() {
453         assertEquals("Command name should be " + COMMAND_NAME + 
454             ", but was " + ncDefineVariable.getCommandName(), 
455             COMMAND_NAME, ncDefineVariable.getCommandName());
456     }
457     
458     /**
459      * Test the command's usage string
460      */
461     public final void testGetUsage()
462     {
463         String usage = ncDefineVariable.getUsageString(); 
464         assertTrue("The command does not display any usage information.", 
465                 !usage.isEmpty());
466         assertTrue("The usage information does not contain the command name.", 
467                 usage.contains(ncDefineVariable.getCommandName()));
468     }
469         
470     /**
471      * Test setting a complex global attribute via standard input
472      * 
473      * @throws IOException
474      */
475     public final void testStandardInputComplexAttribute() throws IOException
476     {
477         NetcdfFile netcdfFile = null;
478         try
479         {
480             List<Attribute> attributes = NetCDFUtils.readAttributesFromStream(new ByteArrayInputStream(
481                     SPECIAL_CHAR_INPUT_ATTRIBUTE_STRING.getBytes("UTF-8")));
482 
483             ((NcDefineVariable) ncDefineVariable).execute(new File(NC_FILE_NAME), VARIABLE_NAME_VALUE, DataType.CHAR,
484                     attributes /*
485                                 * no attributes specified on command line
486                                 */, DIMENSION_NAME_VALUE, true /* isLargeFileSupport */, false /* isFillVariable */);
487 
488             netcdfFile = NetcdfFile.open(NC_FILE_NAME);
489             Variable var = netcdfFile.findVariable(VARIABLE_NAME_VALUE);
490 
491             boolean found = false;
492             for (Attribute attribute : var.getAttributes())
493             {
494                 if (attribute.getName().equals(ATTR_HISTORY))
495                 {
496                     found = true;
497                     assertTrue("The Attrbute had the wrong data: " + ATTR_HISTORY, attribute.getStringValue().contains(
498                             ATTR_HISTORY_VALUE));
499                 }
500                 if (attribute.getName().equals(ATTR_AUTHOR))
501                 {
502                     found = true;
503                     assertTrue("The Attrbute had the wrong data: " + ATTR_AUTHOR, attribute.getStringValue().contains(
504                             ATTR_AUTHOR_VALUE));
505                 }
506             }
507 
508             assertTrue("Could not find variable Attribute: " + ATTR_AUTHOR + " or " + ATTR_HISTORY, found);
509         }
510         catch (Exception e)
511         {
512             e.printStackTrace();
513             fail(e.getMessage());
514         }
515         finally
516         {
517             if (netcdfFile != null)
518             {
519                 netcdfFile.close();
520             }
521         }
522     }
523 }