/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* * $Id$ */ package org.apache.qetest; import java.io.File; import java.util.Enumeration; import java.util.Hashtable; import java.util.Properties; import java.util.StringTokenizer; /** * Datalet representing set of paths to files: input, output, gold. * *

This is a fairly simplistic Datalet implementation that's * useful for the many programs where the test requires reading * an input file, performing an operation with the program, and * then verifying an output file.

* *

We normally operate on local path/filenames, since the Java * SDK's implementation of URLs and File objects is so poor at * handling proper URI/URL's according to the RFCs. A potential * future improvement is to add convenience accessor methods, like * getInputName() (just the bare filename) etc. but I'm not quite * convinced we need them yet.

* * @see FileTestlet * @see FileTestletDriver * @see FileDataletManager * @author Shane_Curcuru@us.ibm.com * @version $Id$ */ public class FileDatalet implements Datalet { /** Path of the location to get input resources from. */ protected String input = "tests/defaultInput"; /** Accessor method for input; never null. */ public String getInput() { return input; } /** Path to put actual output resources into. */ protected String output = "output/defaultOutput"; /** Accessor method for output; never null. */ public String getOutput() { return output; } /** Path of the location to get gold or reference resources from. */ protected String gold = "gold/defaultGold"; /** Accessor method for gold; never null. */ public String getGold() { return gold; } /** * Worker method to validate the files/dirs we represent. * *

By default, ensures that the input already exists in some * format; and attempts to create the output. If asked to be * strict, then we will fail if the output cannot be created, * and we additionally will attempt to create the gold and * will fail if it can't be created.

* *

Note that we only attempt to create the gold if asked to * be strict, since users may simply want to run a 'crash test' * and get all {@link org.apache.qetest.Logger#AMBG_RESULT AMBG} * results when prototyping new tests.

* * @param strict if true, requires that output and gold must * be created; otherwise they're optional * @return false if input doesn't already exist; true otherwise */ public boolean validate(boolean strict) { File f = new File(getInput()); if (!f.exists()) return false; f = new File(getOutput()); if (!f.exists()) { if (!f.mkdirs()) { // Only fail if asked to be strict if (strict) return false; } } f = new File(getGold()); if (!f.exists()) { // For gold, only attempt to mkdirs if asked... if (strict) { // ...Only fail here if we can't if (!f.mkdirs()) return false; } } // If we get here, we're happy either way return true; } /** * Generic placeholder for any additional options. * *

This allows FileDatalets to support additional kinds * of tests, like performance tests, without having to change * this data model. These options can serve as a catch-all * for any new properties or options or what-not that new * tests need, without having to change how the most basic * member variables here work.

*

Note that while this needs to be a Properties object to * take advantage of the parent/default behavior in * getProperty(), this doesn't necessarily mean they can only * store Strings; however only String-valued items can make * use of the default properties mechanisim.

* *

Default is a null object; note that getOptions() will * never return null, but will create a blank Properties * block if needed.

*/ protected Properties options = null; /** * Accessor method for optional properties. * * Should never return null; if it has no options then * it will create a blank Properties block to return. */ public Properties getOptions() { if (null == options) options = new Properties(); return options; } /** * Accessor method for optional properties; settable. *

Note this method creates a new Properties that simply * defaults to the passed in properties instead of cloning it.

*

This used to simply assign this variable to the passed * in variable, but that leads to problems when errant * Testlets mutate their Datalet's Options, which can result * in the master test's testProps getting changed.

* * @param p Properties to set as our defaults; if null, we * do not change anything */ public void setOptions(Properties p) { if (null != p) options = new Properties(p); } /** * Accessor method for optional properties that are ints. * Convenience method to take care of parse/cast to int. * * @param name of property to try to get * @param defValue value if property not available * @return integer value of property, or the default */ public int getIntOption(String name, int defValue) { if (null == options) return defValue; try { return Integer.parseInt(options.getProperty(name)); } catch (Exception e) { return defValue; } } /** Description of what this Datalet tests. */ protected String description = "FileDatalet: default description"; /** * Accesor method for a brief description of this Datalet. * * @return String describing the specific set of data * this Datalet contains (can often be used as the description * of any check() calls made from the Testlet). */ public String getDescription() { return description; } /** * Accesor method for a brief description of this Datalet. * * @param s description to use for this Datalet. */ public void setDescription(String s) { description = s; } /** * Worker method to auto-set the description of this Datalet. * Conglomeration of input, output, gold values. */ protected void setDescription() { setDescription("input=" + input + " output=" + output + " gold=" + gold); } /** * No argument constructor is a no-op; not very useful. */ public FileDatalet() { /* no-op */ } /** * Initialize this datalet from another FileDatalet which * serves as a 'base' location, and a filename. * *

We set each of our input, output, gold to be concatenations * of the base + File.separator + fileName, and also copy * over the options from the base. Note we always attempt to * deal with local path/filename conventions.

* * @param base FileDatalet object to serve as base directories * @param fileName to concatenate for each of input/output/gold */ public FileDatalet(FileDatalet base, String fileName) { if ((null == base) || (null == fileName)) throw new IllegalArgumentException("FileDatalet(base, fileName) called with null base!"); StringBuffer buf = new StringBuffer(File.separator + fileName); input = base.getInput() + buf; output = base.getOutput() + buf; gold = base.getGold() + buf; setOptions(base.getOptions()); setDescription(); } /** * Initialize this datalet from a list of paths. * *

Our options are not initialized, and left as null.

* * @param i path for input * @param o path for output * @param g path for gold */ public FileDatalet(String i, String o, String g) { input = i; output = o; gold = g; setDescription(); } /** * Initialize this datalet from a string and defaults. * *

Our options are created as a new Properties block that * defaults to the existing one passed in, then * we parse the string for input, output, gold, and * optionally add additional args to the options.

* * @param args command line to initialize from * @param defaults for our options */ public FileDatalet(String args, Properties defaults) { options = new Properties(defaults); StringTokenizer st = new StringTokenizer(args); // Fill in as many items as we can; leave as default otherwise // Note that order is important! if (st.hasMoreTokens()) { input = st.nextToken(); if (st.hasMoreTokens()) { output = st.nextToken(); if (st.hasMoreTokens()) { gold = st.nextToken(); } } } // EXPERIMENTAL add extra name value pairs to our options while (st.hasMoreTokens()) { String name = st.nextToken(); if (st.hasMoreTokens()) { options.put(name, st.nextToken()); } else { // Just put it as 'true' for boolean options options.put(name, "true"); } } } /** * Load fields of this Datalet from a Hashtable. * Caller must provide data for all of our fields. * Additionally, we set all values from the hash into * our options block. * * @param Hashtable to load */ public void load(Hashtable h) { if (null == h) return; //@todo should this have a return val or exception? input = (String)h.get("input"); output = (String)h.get("output"); gold = (String)h.get("gold"); // Also copy over all items in hash to options options = new Properties(); for (Enumeration keys = h.keys(); keys.hasMoreElements(); /* no increment portion */) { String key = (String)keys.nextElement(); options.put(key, h.get(key)); } } /** * Load fields of this Datalet from an array. * Order: input, output, gold. Options are left null. * If too few args, then fields at end of list are left at default value. * @param args array of Strings */ public void load(String[] args) { if (null == args) return; //@todo should this have a return val or exception? try { input = args[0]; output = args[1]; gold = args[2]; } catch (ArrayIndexOutOfBoundsException aioobe) { // No-op, leave remaining items as default } } } // end of class FileDatalet