blob: 6e04a5929a130cb27bd07beb797e63241e0dbd81 [file] [log] [blame]
/*
* 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$
*/
/*
*
* SystemIdTest.java
*
*/
package org.apache.qetest.trax;
import java.io.File;
import java.util.Properties;
import javax.xml.transform.Source;
import javax.xml.transform.Templates;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;
import org.apache.qetest.FileBasedTest;
import org.apache.qetest.Logger;
import org.apache.qetest.OutputNameManager;
import org.apache.qetest.QetestUtils;
import org.apache.qetest.xsl.XSLTestfileInfo;
//-------------------------------------------------------------------------
/**
* Test behavior of various types of systemId forms.
* <b>Note:</b> This test is directory-dependent, so if there are
* any fails, check the code to see what the test file is expecting
* the path/directory/etc. to be.
* I'm also using RFC1738 as the definition of what various
* systemId's should look like: the validation of this test is
* a little tricky, since it's not necessarily clear exactly which
* forms of file: always have to be supported by the application.
* (Or which forms of file: are supported by the underlying parser)
* Also, we really need to write some platform-dependent tests
* for this area, to account for 'c:\foo' type paths in Windows,
* and for '/usr/bin' type paths in UNIX-land, as well as various
* links and such (although that may be beyond the scope of what
* Xalan and it's parser do, and more into testing the JDK itself).
*
* Note RFC1738 specifies that file: URL's always use
* forward slash / as a file separator
* ABSOLUTE-PATHS
* file:///e:/path/file.txt absolute path to e:/path/file.txt
*
* file://localhost/e:/path/file.txt path to e:/path/file.txt
* on localhost, but goes direct to filesystem
*
* file://otherhost/e:/path/file.txt absolute path to
* otherhost, but RFC1738 doesn't actually specify the
* protocol to use to get to the other machine to actually
* get the file
*
* RELATIVE-PATHS
* file:e:/path/file.txt is theoretically a relative URL
* w/r/t the current Base URL
*
* file:/e:/path/file.txt is theoretically a relative URL
* that starts with /e:/... - not likely to be useful
*
* file://file.txt theoretically a hostname (not a filename)
* with no path or file portion
*
* ILLEGAL-PATHS
* file://e:\path\file.txt is illegal, since RFC1738
* specifically says / forward slashes
*
* @author shane_curcuru@lotus.com
* @version $Id$
*/
public class SystemIdTest extends FileBasedTest
{
/**
* Provides nextName(), currentName() functionality for tests
* that may produce any number of output files.
*/
protected OutputNameManager outNames;
/**
* Name of a valid, known-good xsl/xml file pair we can use.
*/
protected XSLTestfileInfo knownGoodFileInfo = new XSLTestfileInfo();
/**
* Just basename of a valid, known-good file, both .xsl/.xml .
*/
protected String knownGoodBaseName = null;
/** Subdirectory under test\tests\api for our xsl/xml files. */
public static final String TRAX_SUBDIR = "trax";
/** Convenience variable for user.dir - cached during test. */
protected String savedUserDir = null;
/** Internal flag for test that we have not yet verified expected result. */
protected static final String EXPECTED_RESULT_UNKNOWN = "EXPECTED_RESULT_UNKNOWN";
/** Internal flag for test that should return non-null (and no exceptions). */
protected static final String EXPECTED_RESULT_NONNULL = "EXPECTED_RESULT_NONNULL";
/**
* Internal flag for test that should do a transform.
* Presumably using the systemId item you just tested and
* one of our known-good test files.
*/
protected static final String EXPECTED_RESULT_DOTRANSFORM = "EXPECTED_RESULT_DOTRANSFORM";
/** Just initialize test name, comment, numTestCases. */
public SystemIdTest()
{
numTestCases = 2; // REPLACE_num
testName = "SystemIdTest";
testComment = "Test behavior of various types of systemId forms";
}
/**
* Initialize this test - Set names of xml/xsl test files,
* cache user.dir property.
*
* @param p Properties to initialize from (unused)
* @return false if we should abort the test; true otherwise
*/
public boolean doTestFileInit(Properties p)
{
// Used for all tests; just dump files in trax subdir
File outSubDir = new File(outputDir + File.separator + TRAX_SUBDIR);
if (!outSubDir.mkdirs())
reporter.logWarningMsg("Could not create output dir: " + outSubDir);
// Initialize an output name manager to that dir with .out extension
outNames = new OutputNameManager(outputDir + File.separator + TRAX_SUBDIR
+ File.separator + testName, ".out");
String testBasePath = inputDir
+ File.separator
+ TRAX_SUBDIR
+ File.separator;
String goldBasePath = goldDir
+ File.separator
+ TRAX_SUBDIR
+ File.separator;
// Just bare pathnames, not URI's
knownGoodBaseName = testName;
knownGoodFileInfo.inputName = testBasePath + knownGoodBaseName + ".xsl";
knownGoodFileInfo.xmlName = testBasePath + knownGoodBaseName + ".xml";
knownGoodFileInfo.goldName = goldBasePath + knownGoodBaseName + ".out";
// Cache user.dir property
savedUserDir = System.getProperty("user.dir");
reporter.logHashtable(Logger.STATUSMSG, System.getProperties(), "System.getProperties()");
reporter.logHashtable(Logger.STATUSMSG, testProps, "testProps");
return true;
}
/**
* Cleanup this test - uncache user.dir property.
*
* @param p Properties to initialize from (if needed)
* @return false if we should abort the test; true otherwise
*/
public boolean doTestFileClose(Properties p)
{
// Uncache user.dir property
System.getProperties().put("user.dir", savedUserDir);
return true;
}
/**
* Test various forms of XSL and XML systemIds to see what happens.
*
* @return false if we should abort the test; true otherwise
*/
public boolean testCase1()
{
reporter.testCaseInit("Test various forms of XSL and XML systemIds to see what happens");
// The path the user gave us in inputDir:
// inputDir + "/" + TRAX_SUBDIR + "/" filename
String inputDirPath = inputDir.replace('\\', '/')
+ "/" + TRAX_SUBDIR + "/" + knownGoodBaseName;
//@todo: determine what type of path inputDir itself is:
// downwards relative, upwards relative, or absolute
// Assumed correct user.dir path (should be in xml-xalan\test):
// System.getProperty("user.dir") + "/tests/api/" + TRAX_SUBDIR
// user.dir in theory should always be absolute
String userDirPath = System.getProperty("user.dir").replace('\\', '/')
+ "/tests/api/" + TRAX_SUBDIR + "/" + knownGoodBaseName;
// Verify that user.dir is in the right place relative to
// the checked-in known-good files
String userDirExpected = EXPECTED_RESULT_UNKNOWN;
File f1 = new File(userDirPath + ".xsl");
File f2 = new File(userDirPath + ".xml");
if (f1.exists() && f2.exists())
{
// The known-good files are there, so expect we can use it
userDirExpected = EXPECTED_RESULT_DOTRANSFORM;
}
else
{
reporter.logWarningMsg("Known good files does not appear to exist at: "
+ userDirPath + ".xml/.xsl");
}
String xslTestIds[][] =
{
// { systemId to test,
// description of the test,
// expected XSL behavior or exception,
// expected XSL inner exception,
// expected XML behavior or exception,
// expected XML inner exception
// }
// Test variations on the inputDir specified by the
// user, to be able to do some adhoc testing
{ "file:///" + inputDirPath,
"file:///, user-specified inputDir, /blah1[1a]",
EXPECTED_RESULT_UNKNOWN,
null,
EXPECTED_RESULT_UNKNOWN,
null },
{ "file://localhost/" + inputDirPath,
"file://localhost/, user-specified inputDir, /blah[1b]",
EXPECTED_RESULT_UNKNOWN,
null,
EXPECTED_RESULT_UNKNOWN,
null },
{ inputDirPath,
"Just user-specified inputDir, /blah (works normally, if relative)[1c]",
EXPECTED_RESULT_UNKNOWN,
null,
EXPECTED_RESULT_UNKNOWN,
null },
// Test variations on the System user.dir; validation
// depends on if it's set correctly
{ "file:///" + userDirPath,
"file:///, System(user.dir), /blah (works normally)[2a]",
userDirExpected,
null,
userDirExpected,
null },
{ "file://localhost/" + userDirPath,
"file://localhost/, System(user.dir), /blah (works normally)[2b]",
userDirExpected,
null,
userDirExpected,
null },
{ userDirPath,
"Just System(user.dir), /blah[2c]",
EXPECTED_RESULT_UNKNOWN,
null,
EXPECTED_RESULT_UNKNOWN,
null },
// Absolute path with blank . step
{ "file:///" + System.getProperty("user.dir").replace('\\', '/')
+ "/tests/./api/" + TRAX_SUBDIR + "/" + knownGoodBaseName,
"file:///, System(user.dir), /./blah (???)[2d]",
userDirExpected,
null,
userDirExpected,
null },
// Absolute path with up/down steps
{ "file:///" + System.getProperty("user.dir").replace('\\', '/')
+ "/tests/../tests/api/" + TRAX_SUBDIR + "/" + knownGoodBaseName,
"file:///, System(user.dir), /updir/../downdir/blah (???)[2e]",
userDirExpected,
null,
userDirExpected,
null },
// Just relative paths, should work if user.dir correct
// Arguable: comment out for 2.0
{ "file:tests/api/" + TRAX_SUBDIR + "/" + knownGoodBaseName,
"Just file:/blah relative path[3a]",
EXPECTED_RESULT_UNKNOWN,
null,
EXPECTED_RESULT_UNKNOWN,
null },
{ "tests/api/" + TRAX_SUBDIR + "/" + knownGoodBaseName,
"Just /blah relative path[3b]",
userDirExpected,
null,
userDirExpected,
null },
// file://blah should be interperted as a hostname,
// not as a filename, and should fail
{ "file://" + userDirPath,
"file://, System(user.dir), /blah (causes hostname error)[4a]",
EXPECTED_RESULT_UNKNOWN,
null,
EXPECTED_RESULT_UNKNOWN,
null },
// Comment out for 2.0 due to SPR SCUU4SUQXU
/*
"javax.xml.transform.TransformerConfigurationException",
"java.net.UnknownHostException",
"javax.xml.transform.TransformerException",
"java.net.UnknownHostException" },
*/
{ "file://" + inputDirPath,
"file://, user-specified inputDir, /blah (causes hostname error)[4b]",
EXPECTED_RESULT_UNKNOWN,
null,
EXPECTED_RESULT_UNKNOWN,
null },
// Comment out for 2.0 due to SPR SCUU4SUQXU
/*
"javax.xml.transform.TransformerConfigurationException",
"java.net.UnknownHostException",
"javax.xml.transform.TransformerException",
"java.net.UnknownHostException" },
*/
// file://host.does.not.exist/blah should fail, here we
// can also validate the error message completely
{ "file://this.host.does.not.exist/" + userDirPath,
"file://this.host.does.not.exist/userDir/blah (causes hostname error)[4c]",
EXPECTED_RESULT_UNKNOWN,
null,
EXPECTED_RESULT_UNKNOWN,
null },
// Comment out for 2.0 due to SPR SCUU4SUQXU
/*
"javax.xml.transform.TransformerConfigurationException: this.host.does.not.exist",
"java.net.UnknownHostException: this.host.does.not.exist",
"javax.xml.transform.TransformerException: this.host.does.not.exist",
"java.net.UnknownHostException" },
*/
{ "file://this.host.does.not.exist/" + inputDirPath,
"file://this.host.does.not.exist/inputDir/blah (causes hostname error)[4d]",
EXPECTED_RESULT_UNKNOWN,
null,
EXPECTED_RESULT_UNKNOWN,
null },
// Comment out for 2.0 due to SPR SCUU4SUQXU
/*
"javax.xml.transform.TransformerConfigurationException: this.host.does.not.exist",
"java.net.UnknownHostException: this.host.does.not.exist",
"javax.xml.transform.TransformerException: this.host.does.not.exist",
"java.net.UnknownHostException" },
*/
// Too few leading slashes for the file: spec, probably error
{ "file:/" + userDirPath,
"file:/, System(user.dir), /blah (probable error)[5a]",
EXPECTED_RESULT_UNKNOWN,
null,
EXPECTED_RESULT_UNKNOWN,
null },
{ "file:/" + inputDirPath,
"file:/, user-specified inputDir, /blah (probable error)[5b]",
EXPECTED_RESULT_UNKNOWN,
null,
EXPECTED_RESULT_UNKNOWN,
null },
// No leading slashes for the file: spec, behavior is?
{ "file:" + userDirPath,
"file:, System(user.dir), /blah (probable error)[6a]",
EXPECTED_RESULT_UNKNOWN,
null,
EXPECTED_RESULT_UNKNOWN,
null },
{ "file:" + inputDirPath,
"file:, user-specified inputDir, /blah (probable error)[6b]",
EXPECTED_RESULT_UNKNOWN,
null,
EXPECTED_RESULT_UNKNOWN,
null },
// Using backslashes in the path portion is explicitly
// forbidden in the RFC, should give error
{ "file:///" + userDirPath.replace('/', '\\'),
"file:///, System(user.dir) \\blah, (backslashes are illegal)[7a]",
EXPECTED_RESULT_UNKNOWN,
null,
EXPECTED_RESULT_UNKNOWN,
null },
{ "file:///" + inputDirPath.replace('/', '\\'),
"file:///, user-specified inputDir \\blah (backslashes are illegal)[7b]",
EXPECTED_RESULT_UNKNOWN,
null,
EXPECTED_RESULT_UNKNOWN,
null }
};
for (int i = 0; i < xslTestIds.length; i++)
{
// Loop and attempt to do newTemplates() with each
testNewTemplatesWithSystemId(xslTestIds[i][0] + ".xsl", xslTestIds[i][1],
xslTestIds[i][2], xslTestIds[i][3]);
// Loop and attempt to do newTransformer() with each
// as well, since they have slightly different codepaths
testNewTransformerWithSystemId(xslTestIds[i][0] + ".xsl", xslTestIds[i][1],
xslTestIds[i][2], xslTestIds[i][3]);
// Loop and attempt to do a transform of an xml
// document with each, using known-good stylesheet
testTransformWithSystemId(xslTestIds[i][0] + ".xml", xslTestIds[i][1],
xslTestIds[i][4], xslTestIds[i][5]);
}
reporter.testCaseClose();
return true;
}
/**
* Test setting various forms of systemIds to see what happens.
*
* @return false if we should abort the test; true otherwise
*/
public boolean testCase2()
{
reporter.testCaseInit("Test setting various forms of systemId to see what happens");
// This will have imports/includes on various levels, and then
// set the systemId of a Source to hopefully pull in the
// different imports/includes
reporter.checkPass("//@todo implement this testcase");
reporter.testCaseClose();
return true;
}
/**
* Worker method to test factory.newTemplates.
* Performs validation of various types based on the
* expected value.
*
* @param sysId systemId of XSL to test with
* @param desc description of test, used in check() calls
* @param expected either one of the EXPECTED_RESULT_* flags
* defined in this file, or the start of a .toString of the
* exception that you expect will be thrown
* @param innerExpected the start of a .toString of the
* inner or wrapper exception that you expect will be thrown,
* presumably wrapped inside a TransformerException; if null,
* then this is not checked for
* @return Templates object created as side effect
*/
protected Templates testNewTemplatesWithSystemId(String sysId, String desc,
String expected, String innerExpected)
{
TransformerFactory factory = null;
Templates templates = null;
Transformer transformer = null;
Throwable thrown = null;
try
{
factory = TransformerFactory.newInstance();
// Use a StreamSource with the systemId, which sets itself automatically
Source source = new StreamSource(sysId);
// Changed order of params for easier logging
reporter.logStatusMsg("newTemplates(" + desc + "): " + sysId + ", " + expected);
templates = factory.newTemplates(source);
reporter.logTraceMsg("newTemplates() no exceptions!");
// Just get the templates now for convenience,
// this implicitly tests that they're non-null
transformer = templates.newTransformer();
}
catch (Throwable t)
{
thrown = t;
reporter.logStatusMsg("newTemplates(" + desc + ") threw:" + t.toString());
reporter.logThrowable(reporter.ERRORMSG, t, "newTemplates(" + desc + ") threw");
}
// Call worker method to perform actual validation
validateWithSystemId(sysId, desc, expected, innerExpected, thrown, transformer);
return templates;
}
/**
* Worker method to test factory.newTransformer.
* Performs validation of various types based on the
* expected value.
*
* @param sysId systemId of XSL to test with
* @param desc description of test, used in check() calls
* @param expected either one of the EXPECTED_RESULT_* flags
* defined in this file, or the start of a .toString of the
* exception that you expect will be thrown
* @param innerExpected the start of a .toString of the
* inner or wrapper exception that you expect will be thrown,
* presumably wrapped inside a TransformerException; if null,
* then this is not checked for
* @return Transformer object created as side effect
*/
protected Transformer testNewTransformerWithSystemId(String sysId, String desc,
String expected, String innerExpected)
{
TransformerFactory factory = null;
Transformer transformer = null;
Throwable thrown = null;
try
{
factory = TransformerFactory.newInstance();
// Use a StreamSource with the systemId, which sets itself automatically
Source source = new StreamSource(sysId);
reporter.logStatusMsg("newTransformer(" + desc + "): " + sysId + ", " + expected);
transformer = factory.newTransformer(source);
reporter.logTraceMsg("newTransformer() no exceptions!");
}
catch (Throwable t)
{
thrown = t;
reporter.logStatusMsg("newTransformer(" + desc + ") threw:" + t.toString());
reporter.logThrowable(reporter.ERRORMSG, t, "newTransformer(" + desc + ") threw");
}
// Call worker method to perform actual validation
validateWithSystemId(sysId, desc, expected, innerExpected, thrown, transformer);
return transformer;
}
/**
* Worker method to test transformer.transform().
* Performs validation of various types based on the
* expected value, using a known-good XSL file.
*
* @param sysId systemId of XML file to test with
* @param desc description of test, used in check() calls
* @param expected either one of the EXPECTED_RESULT_* flags
* defined in this file, or the start of a .toString of the
* exception that you expect will be thrown
* @param innerExpected the start of a .toString of the
* inner or wrapper exception that you expect will be thrown,
* presumably wrapped inside a TransformerException; if null,
* then this is not checked for
*/
protected void testTransformWithSystemId(String sysId, String desc,
String expected, String innerExpected)
{
TransformerFactory factory = null;
Transformer transformer = null;
Throwable thrown = null;
try
{
factory = TransformerFactory.newInstance();
// Use a StreamSource with the known-good systemId, which sets itself automatically
Source source = new StreamSource(knownGoodFileInfo.inputName);
reporter.logStatusMsg("Transform(" + desc + "): " + sysId + ", " + expected);
transformer = factory.newTransformer(source);
// Always try the transform using a new StreamSource
reporter.logTraceMsg("About to transform(StreamSource(" + sysId + ", " + outNames.nextName() + ")");
transformer.transform(new StreamSource(sysId),
new StreamResult(outNames.currentName()));
}
catch (Throwable t)
{
thrown = t;
reporter.logStatusMsg("Transform(" + desc + ") threw:" + t.toString());
reporter.logThrowable(reporter.ERRORMSG, t, "Transform(" + desc + ") threw");
}
// Do our own validation since we've already done a transform implicitly
// Sorry for the icky if..else.. statement
if (EXPECTED_RESULT_UNKNOWN.equals(expected))
{
// Just log a message: no validation done
reporter.logWarningMsg("(" + desc + ") not validated!");
}
else if (EXPECTED_RESULT_NONNULL.equals(expected))
{
// Just validate that our object is non-null
reporter.check((transformer != null), true, "(" + desc + ") is non-null");
}
else if (EXPECTED_RESULT_DOTRANSFORM.equals(expected))
{
int result = fileChecker.check(reporter,
new File(outNames.currentName()),
new File(knownGoodFileInfo.goldName),
"(" + desc + ") transform into: " + outNames.currentName());
if (result == reporter.FAIL_RESULT)
reporter.logInfoMsg("(" + desc + ") transform failure reason:" + fileChecker.getExtendedInfo());
}
else
{
// Otherwise, assume it's a string that any exception
// thrown should match the start of our message
validateException(sysId, desc, expected, innerExpected, thrown);
}
}
/**
* Worker method to validate either a transform or an exception.
*
* @param sysId systemId that you were testing with; used in logging
* @param desc description of test, used in check() calls
* @param expected either one of the EXPECTED_RESULT_* flags
* defined in this file, or the start of a .toString of the
* exception that you expect will be thrown, only
* used when expected=EXPECTED_RESULT_DOTRANSFORM
* @param innerExpected the start of a .toString of the
* inner or wrapper exception that you expect will be thrown,
* presumably wrapped inside a TransformerException; if null,
* then this is not checked for
* @param thrown any Throwable that was thrown in your operation
* @param transformer that was created from your operation; only
* used for transform when expected=EXPECTED_RESULT_DOTRANSFORM
*/
protected void validateWithSystemId(String sysId, String desc,
String expected, String innerExpected,
Throwable thrown,
Transformer transformer)
{
// Sorry for the icky if..else.. statement
if (EXPECTED_RESULT_UNKNOWN.equals(expected))
{
// Just log a message: no validation done
reporter.logWarningMsg("(" + desc + ") not validated!");
}
else if (EXPECTED_RESULT_NONNULL.equals(expected))
{
// Just validate that our object is non-null
reporter.check((transformer != null), true, "(" + desc + ") is non-null");
}
else if (EXPECTED_RESULT_DOTRANSFORM.equals(expected))
{
try
{
// First validate that our object is non-null
if (transformer != null)
{
// Actually try to use the object in a transform
transformer.transform(new StreamSource(QetestUtils.filenameToURL(knownGoodFileInfo.xmlName)),
new StreamResult(outNames.nextName()));
int result = fileChecker.check(reporter,
new File(outNames.currentName()),
new File(knownGoodFileInfo.goldName),
"(" + desc + ") transform into: " + outNames.currentName());
if (result == reporter.FAIL_RESULT)
reporter.logInfoMsg("(" + desc + ") transform failure reason:" + fileChecker.getExtendedInfo());
}
else
{
reporter.checkFail("(" + desc + ") transformer was null!");
}
}
catch (Throwable t)
{
reporter.checkFail("(" + desc + ") do transform threw:" + t.toString());
reporter.logThrowable(reporter.ERRORMSG, t, "(" + desc + ") do transform threw");
}
}
else
{
// Otherwise, assume it's a string that any exception
// thrown should match the start of our message
validateException(sysId, desc, expected, innerExpected, thrown);
}
}
/**
* Worker method to validate just a thrown exception.
*
* @param sysId systemId that you were testing with; currently unused?
* @param desc description of test, used in check() calls
* @param expected the start of a .toString of the
* exception that you expect will be thrown
* @param innerExpected the start of a .toString of the
* inner or wrapper exception that you expect will be thrown,
* presumably wrapped inside a TransformerException; if null,
* then this is not checked for
* @param thrown any Throwable that was thrown in your operation
*/
protected void validateException(String sysId, String desc,
String expected, String innerExpected,
Throwable thrown)
{
if (thrown == null)
{
reporter.checkFail("(" + desc + ") No exception was thrown when expected");
}
else
{
reporter.check(thrown.toString().startsWith(expected), true, "(" + desc + ") expected exception");
if ((innerExpected != null)
&& (thrown instanceof TransformerException))
{
// Also validate any innerExceptions
Throwable inner = ((TransformerException)thrown).getException();
if (inner != null)
{
reporter.check(inner.toString().startsWith(innerExpected), true, "(" + desc + ") inner expected exception");
}
else
{
// User specified an innerException but none
// was found, so fail
reporter.checkFail("(" + desc + ") No innerException found like: " + innerExpected);
}
}
}
}
/**
* Convenience method to print out usage information - update if needed.
* @return String denoting usage of this test class
*/
public String usage()
{
return ("Common [optional] options supported by SystemIdTest:\n"
+ "(Note: assumes inputDir=.\\tests\\api)\n"
+ "(Note: test is directory-dependent!)\n"
+ super.usage()); // Grab our parent classes usage as well
}
/**
* Main method to run test from the command line - can be left alone.
* @param args command line argument array
*/
public static void main(String[] args)
{
SystemIdTest app = new SystemIdTest();
app.doMain(args);
}
}