• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Licensed to the Apache Software Foundation (ASF) under one
3  * or more contributor license agreements. See the NOTICE file
4  * distributed with this work for additional information
5  * regarding copyright ownership. The ASF licenses this file
6  * to you under the Apache License, Version 2.0 (the  "License");
7  * you may not use this file except in compliance with the License.
8  * You may obtain a copy of the License at
9  *
10  *     http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  */
18 /*
19  * $Id$
20  */
21 
22 package org.apache.qetest;
23 
24 import java.io.File;
25 import java.util.Enumeration;
26 import java.util.Hashtable;
27 import java.util.Properties;
28 import java.util.StringTokenizer;
29 
30 /**
31  * Datalet representing set of paths to files: input, output, gold.
32  *
33  * <p>This is a fairly simplistic Datalet implementation that's
34  * useful for the many programs where the test requires reading
35  * an input file, performing an operation with the program, and
36  * then verifying an output file.</p>
37  *
38  * <p>We normally operate on local path/filenames, since the Java
39  * SDK's implementation of URLs and File objects is so poor at
40  * handling proper URI/URL's according to the RFCs.  A potential
41  * future improvement is to add convenience accessor methods, like
42  * getInputName() (just the bare filename) etc. but I'm not quite
43  * convinced we need them yet.</p>
44  *
45  * @see FileTestlet
46  * @see FileTestletDriver
47  * @see FileDataletManager
48  * @author Shane_Curcuru@us.ibm.com
49  * @version $Id$
50  */
51 public class FileDatalet implements Datalet
52 {
53     /** Path of the location to get input resources from.  */
54     protected String input = "tests/defaultInput";
55 
56     /** Accessor method for input; never null.  */
getInput()57     public String getInput() { return input; }
58 
59     /** Path to put actual output resources into.  */
60     protected String output = "output/defaultOutput";
61 
62     /** Accessor method for output; never null.  */
getOutput()63     public String getOutput() { return output; }
64 
65     /** Path of the location to get gold or reference resources from.  */
66     protected String gold = "gold/defaultGold";
67 
68     /** Accessor method for gold; never null.  */
getGold()69     public String getGold() { return gold; }
70 
71 
72     /**
73      * Worker method to validate the files/dirs we represent.
74      *
75      * <p>By default, ensures that the input already exists in some
76      * format; and attempts to create the output.  If asked to be
77      * strict, then we will fail if the output cannot be created,
78      * and we additionally will attempt to create the gold and
79      * will fail if it can't be created.</p>
80      *
81      * <p>Note that we only attempt to create the gold if asked to
82      * be strict, since users may simply want to run a 'crash test'
83      * and get all {@link org.apache.qetest.Logger#AMBG_RESULT AMBG}
84      * results when prototyping new tests.</p>
85      *
86      * @param strict if true, requires that output and gold must
87      * be created; otherwise they're optional
88      * @return false if input doesn't already exist; true otherwise
89      */
validate(boolean strict)90     public boolean validate(boolean strict)
91     {
92         File f = new File(getInput());
93         if (!f.exists())
94             return false;
95 
96         f = new File(getOutput());
97         if (!f.exists())
98         {
99             if (!f.mkdirs())
100             {
101                 // Only fail if asked to be strict
102                 if (strict)
103                     return false;
104             }
105         }
106 
107         f = new File(getGold());
108         if (!f.exists())
109         {
110             // For gold, only attempt to mkdirs if asked...
111             if (strict)
112             {
113                 // ...Only fail here if we can't
114                 if (!f.mkdirs())
115                     return false;
116             }
117         }
118         // If we get here, we're happy either way
119         return true;
120     }
121 
122 
123     /**
124      * Generic placeholder for any additional options.
125      *
126      * <p>This allows FileDatalets to support additional kinds
127      * of tests, like performance tests, without having to change
128      * this data model.  These options can serve as a catch-all
129      * for any new properties or options or what-not that new
130      * tests need, without having to change how the most basic
131      * member variables here work.</p>
132      * <p>Note that while this needs to be a Properties object to
133      * take advantage of the parent/default behavior in
134      * getProperty(), this doesn't necessarily mean they can only
135      * store Strings; however only String-valued items can make
136      * use of the default properties mechanisim.</p>
137      *
138      * <p>Default is a null object; note that getOptions() will
139      * never return null, but will create a blank Properties
140      * block if needed.</p>
141      */
142     protected Properties options = null;
143 
144     /**
145      * Accessor method for optional properties.
146      *
147      * Should never return null; if it has no options then
148      * it will create a blank Properties block to return.
149      */
getOptions()150     public Properties getOptions()
151     {
152         if (null == options)
153             options = new Properties();
154 
155         return options;
156     }
157 
158     /**
159      * Accessor method for optional properties; settable.
160      * <p>Note this method creates a new Properties that simply
161      * defaults to the passed in properties instead of cloning it.</p>
162      * <p>This used to simply assign this variable to the passed
163      * in variable, but that leads to problems when errant
164      * Testlets mutate their Datalet's Options, which can result
165      * in the master test's testProps getting changed.</p>
166      *
167      * @param p Properties to set as our defaults; if null, we
168      * do not change anything
169      */
setOptions(Properties p)170     public void setOptions(Properties p)
171     {
172         if (null != p)
173             options = new Properties(p);
174     }
175 
176 
177     /**
178      * Accessor method for optional properties that are ints.
179      * Convenience method to take care of parse/cast to int.
180      *
181      * @param name of property to try to get
182      * @param defValue value if property not available
183      * @return integer value of property, or the default
184      */
getIntOption(String name, int defValue)185     public int getIntOption(String name, int defValue)
186     {
187         if (null == options)
188             return defValue;
189 
190         try
191         {
192             return Integer.parseInt(options.getProperty(name));
193         }
194         catch (Exception e)
195         {
196             return defValue;
197         }
198     }
199 
200 
201     /** Description of what this Datalet tests.  */
202     protected String description = "FileDatalet: default description";
203 
204 
205     /**
206      * Accesor method for a brief description of this Datalet.
207      *
208      * @return String describing the specific set of data
209      * this Datalet contains (can often be used as the description
210      * of any check() calls made from the Testlet).
211      */
getDescription()212     public String getDescription()
213     {
214         return description;
215     }
216 
217 
218     /**
219      * Accesor method for a brief description of this Datalet.
220      *
221      * @param s description to use for this Datalet.
222      */
setDescription(String s)223     public void setDescription(String s)
224     {
225         description = s;
226     }
227 
228 
229     /**
230      * Worker method to auto-set the description of this Datalet.
231      * Conglomeration of input, output, gold values.
232      */
setDescription()233     protected void setDescription()
234     {
235         setDescription("input=" + input
236                        + " output=" + output
237                        + " gold=" + gold);
238     }
239 
240 
241     /**
242      * No argument constructor is a no-op; not very useful.
243      */
FileDatalet()244     public FileDatalet() { /* no-op */ }
245 
246 
247     /**
248      * Initialize this datalet from another FileDatalet which
249      * serves as a 'base' location, and a filename.
250      *
251      * <p>We set each of our input, output, gold to be concatenations
252      * of the base + File.separator + fileName, and also copy
253      * over the options from the base.  Note we always attempt to
254      * deal with local path/filename conventions.</p>
255      *
256      * @param base FileDatalet object to serve as base directories
257      * @param fileName to concatenate for each of input/output/gold
258      */
FileDatalet(FileDatalet base, String fileName)259     public FileDatalet(FileDatalet base, String fileName)
260     {
261         if ((null == base) || (null == fileName))
262             throw new IllegalArgumentException("FileDatalet(base, fileName) called with null base!");
263 
264         StringBuffer buf = new StringBuffer(File.separator + fileName);
265         input = base.getInput() + buf;
266         output = base.getOutput() + buf;
267         gold = base.getGold() + buf;
268         setOptions(base.getOptions());
269 
270         setDescription();
271     }
272 
273 
274     /**
275      * Initialize this datalet from a list of paths.
276      *
277      * <p>Our options are not initialized, and left as null.<p>
278      *
279      * @param i path for input
280      * @param o path for output
281      * @param g path for gold
282      */
FileDatalet(String i, String o, String g)283     public FileDatalet(String i, String o, String g)
284     {
285         input = i;
286         output = o;
287         gold = g;
288 
289         setDescription();
290     }
291 
292 
293     /**
294      * Initialize this datalet from a string and defaults.
295      *
296      * <p>Our options are created as a new Properties block that
297      * defaults to the existing one passed in, then
298      * we parse the string for input, output, gold, and
299      * optionally add additional args to the options.</p>
300      *
301      * @param args command line to initialize from
302      * @param defaults for our options
303      */
FileDatalet(String args, Properties defaults)304     public FileDatalet(String args, Properties defaults)
305     {
306         options = new Properties(defaults);
307 
308         StringTokenizer st = new StringTokenizer(args);
309 
310         // Fill in as many items as we can; leave as default otherwise
311         // Note that order is important!
312         if (st.hasMoreTokens())
313         {
314             input = st.nextToken();
315             if (st.hasMoreTokens())
316             {
317                 output = st.nextToken();
318                 if (st.hasMoreTokens())
319                 {
320                     gold = st.nextToken();
321                 }
322             }
323         }
324         // EXPERIMENTAL add extra name value pairs to our options
325         while (st.hasMoreTokens())
326         {
327             String name = st.nextToken();
328             if (st.hasMoreTokens())
329             {
330                 options.put(name, st.nextToken());
331             }
332             else
333             {
334                 // Just put it as 'true' for boolean options
335                 options.put(name, "true");
336             }
337         }
338     }
339 
340 
341     /**
342      * Load fields of this Datalet from a Hashtable.
343      * Caller must provide data for all of our fields.
344      * Additionally, we set all values from the hash into
345      * our options block.
346      *
347      * @param Hashtable to load
348      */
load(Hashtable h)349     public void load(Hashtable h)
350     {
351         if (null == h)
352             return; //@todo should this have a return val or exception?
353 
354         input = (String)h.get("input");
355         output = (String)h.get("output");
356         gold = (String)h.get("gold");
357 
358         // Also copy over all items in hash to options
359         options = new Properties();
360         for (Enumeration keys = h.keys();
361              keys.hasMoreElements();
362              /* no increment portion */)
363         {
364             String key = (String)keys.nextElement();
365             options.put(key, h.get(key));
366         }
367     }
368 
369 
370     /**
371      * Load fields of this Datalet from an array.
372      * Order: input, output, gold.  Options are left null.
373      * If too few args, then fields at end of list are left at default value.
374      * @param args array of Strings
375      */
load(String[] args)376     public void load(String[] args)
377     {
378         if (null == args)
379             return; //@todo should this have a return val or exception?
380 
381         try
382         {
383             input = args[0];
384             output = args[1];
385             gold = args[2];
386         }
387         catch (ArrayIndexOutOfBoundsException  aioobe)
388         {
389             // No-op, leave remaining items as default
390         }
391     }
392 
393 }  // end of class FileDatalet
394 
395