blob: 3edef95064da8b89067a30e7df22ecbd07917154 [file]
/*
* The Apache Software License, Version 1.1
*
*
* Copyright (c) 2001-2003 The Apache Software Foundation. All rights
* reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The end-user documentation included with the redistribution,
* if any, must include the following acknowledgment:
* "This product includes software developed by the
* Apache Software Foundation (http://www.apache.org/)."
* Alternately, this acknowledgment may appear in the software itself,
* if and wherever such third-party acknowledgments normally appear.
*
* 4. The names "Xalan" and "Apache Software Foundation" must
* not be used to endorse or promote products derived from this
* software without prior written permission. For written
* permission, please contact apache@apache.org.
*
* 5. Products derived from this software may not be called "Apache",
* nor may "Apache" appear in their name, without prior written
* permission of the Apache Software Foundation.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation and was
* originally based on software copyright (c) 2000, Lotus
* Development Corporation., http://www.lotus.com. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*/
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.
*
* <p>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.</p>
*
* <p>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.</p>
*
* @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.
*
* <p>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.</p>
*
* <p>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.</p>
*
* @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.
*
* <p>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.</p>
* <p>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.</p>
*
* <p>Default is a null object; note that getOptions() will
* never return null, but will create a blank Properties
* block if needed.</p>
*/
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.
* <p>Note this method creates a new Properties that simply
* defaults to the passed in properties instead of cloning it.</p>
* <p>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.</p>
*
* @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.
*
* <p>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.</p>
*
* @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.
*
* <p>Our options are not initialized, and left as null.<p>
*
* @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.
*
* <p>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.</p>
*
* @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 enum = h.keys();
enum.hasMoreElements();
/* no increment portion */)
{
String key = (String)enum.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