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