package org.mitre.test;
import org.apache.commons.configuration.ConfigurationException;
import org.apache.commons.configuration.XMLConfiguration;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;
import org.jdom.Document;
import org.jdom.Element;
import org.mitre.rhex.*;
import org.mitre.test.impl.HtmlReporter;
import org.mitre.test.impl.TextReporter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.*;
import java.util.*;
import java.util.concurrent.atomic.AtomicBoolean;
/**
* Main Loader loads test units, executes them, and generates output report
*
* @author Jason Mathews, MITRE Corp.
*/
public final class Loader {
private static final Logger log = LoggerFactory.getLogger(Loader.class);
private final Context context = new Context();
/**
* Hash of TestUnit instances indexed by class to allow for dependency checking
*/
private final Map<Class<? extends TestUnit>, TestUnit> list =
new HashMap<Class<? extends TestUnit>, TestUnit>();
/**
* Sorted set of loaded tests
*/
private final Set<TestUnit> sortedSet = new TreeSet<TestUnit>();
private final Set<String> idSet = new HashSet<String>();
/**
* Singleton Loader instance
*/
private static final Loader loader = new Loader();
private static final AtomicBoolean initialized = new AtomicBoolean();
private Loader() {
// private constructor
}
/**
* Get singleton Loader instance.
*
* @param initCheck Flag to check if Loader needs one-time initialization at run-time.
* If <code>false</code> then bypassed the initCheck check.
* @return Loader
*/
public static Loader getInstance(boolean initCheck) {
if (initCheck) {
loader.init();
}
return loader;
}
public static Loader getInstance() {
loader.init();
return loader;
}
private void init() {
if (initialized.getAndSet(true)) {
return;
}
log.debug("XXX: init Loader");
final Reporter reporter = getContext().getReporter();
reporter.startGroup("Configuration");
try {
XMLConfiguration config = new XMLConfiguration();
// load from cmd-line parameter. default=config.xml
String configName = System.getProperty("configFile");
if (StringUtils.isBlank(configName)) configName = "config.xml"; // default
File configFile = new File(configName);
config.setFile(configFile);
config.load();
// dump the config file to output
System.out.println("Config: " + configFile);
FileInputStream reader = null;
try {
// strip comments, passwords, and blank lines
reader = new FileInputStream(configFile);
final String content = IOUtils.toString(reader, "UTF-8")
.replaceAll("(?s)<!--.*?-->\r?\n?", "")
.replaceAll("(?s)<password>[^>]+</password>", "")
.replaceAll("(?s)\\s*[\r\n]+", "\n");
System.out.println(content);
} catch(IOException e) {
log.debug("", e);
} finally {
IOUtils.closeQuietly(reader);
}
System.out.println("---------------------------------------------------------------------");
context.load(config);
String profile = context.getString("profileDocumentFile");
if (StringUtils.isNotBlank(profile)) {
loadProfile(profile);
} else {
loadDefaultTests();
}
} catch (ConfigurationException e) {
log.error("", e);
} catch (IllegalStateException e) {
log.error("", e);
} catch (IllegalArgumentException e) {
log.error("", e);
} catch (IOException e) {
log.error("Failed to load assertions from profile document", e);
} finally {
reporter.endGroup();
}
}
private void loadProfile(String profile) throws IOException {
System.out.println("Profile document=" + profile);
File file = new File(profile);
if (!file.isFile()) {
throw new IllegalArgumentException("file file " + file +
" does not exist or isn't regular file");
}
try {
Document doc = context.getBuilder(null).build(file);
for(Object child : doc.getRootElement().getChildren("testAssertion")) {
if (!(child instanceof Element)) continue;
Element e = (Element)child;
String className = StringUtils.trimToEmpty(e.getAttributeValue("class"));
if (className == null) continue;
System.out.println(className);
Class objClass = Class.forName(className);
TestUnit testUnit = (TestUnit) objClass.newInstance();
load(testUnit);
}
} catch (IOException e) {
throw e;
} catch (Exception e) {
throw new IOException(e);
}
}
private void loadDefaultTests() throws IllegalArgumentException {
// load instances of all possible tests in any order
// prerequisites will be ordered in the execution plan such that
// they're executed before those tests that require them
load(new BaseUrlNotFoundTest()); // 6.2.1.1
load(new BaseUrlGetNoAcceptTest()); // 6.2.1.2
load(new BaseUrlGetStarAcceptTest()); // 6.2.1.3
load(new BaseUrlGetTest()); // 6.2.1.4
load(new BaseUrlGetHtmlAcceptTest()); // 6.2.1.5
load(new CreateDuplicateSection()); // 6.2.2.3 [req=6.3.1.1]
load(new BaseUrlOptions()); // 6.2.5.1
load(new BaseUrlOptionsSecurityHeader()); // 6.2.5.2 [req=6.2.5.1]
load(new BaseUrlOptionsHcpHeader()); // 6.2.5.3 [req=6.2.5.1]
load(new BaseUrlOptionsExtHeader()); // 6.2.5.4 [req=6.2.5.1]
load(new BaseUrlOptionNoBody()); // 6.2.5.6 [req=6.2.5.1]
load(new BaseUrlOptionsNotFound()); // 6.2.5.7
load(new BaseUrlRootXml()); // 6.3.1.1
load(new BaseUrlRootXmlNotFound()); // 6.3.1.2
load(new BaseUrlRootXmlPost()); // 6.3.2.1
load(new BaseUrlRootXmlPut()); // 6.3.2.2
load(new BaseUrlRootXmlDeleteTest()); // 6.3.2.3
load(new BaseSectionFromRootXml()); // 6.4.1.1 [req=6.3.1.1]
load(new SectionNotFound()); // 6.4.1.2
load(new DocumentCreate()); // 6.4.2.2 [req=6.3.1.1]
load(new DocumentCreateCheck()); // 6.4.2.3 [req=6.4.2.2]
load(new DocumentBadCreate()); // 6.4.2.4 [req=6.3.1.1]
load(new SectionPut()); // 6.4.3 [req=6.4.1.1]
load(new DocumentTest()); // 6.5.1.2 [req=6.4.1.1]
load(new DocumentNotFound()); // 6.5.1.3 [req=6.4.1.1]
load(new DocumentUpdate()); // 6.5.2.1
load(new DocumentPut()); // 6.5.2.3 [req=6.4.1.1]
}
/**
* Creates and executes an execution plan for all test loaded tests
*
* @return timestamp when execution started
*/
public void execute() {
Reporter reporter = getContext().getReporter();
reporter.startGroup("Build Execution Plan");
ExcecutionPlan exec = new ExcecutionPlan(sortedSet.iterator());
reporter.endGroup();
exec.execute();
}
public void load(TestUnit test) throws IllegalArgumentException {
String id = test.getId();
if (!idSet.add(id)) {
// assertion failed
if (list.containsKey(test.getClass()))
log.warn("Test already loaded: " + getClassName(test) + " [" + id + "]");
else
log.error(String.format("Duplicate id [%s] found with test %s already defined in %s",
id, getClassName(test), getClassName(findTestById(id))));
return;
}
final Class<? extends TestUnit> aClass = test.getClass();
for(Class<? extends TestUnit> depend : test.getDependencyClasses()) {
if (aClass == depend) {
// assertion failed
log.error("dependency cannot include self: " + aClass.getName());
return;
}
}
// add to hash of test units by class
list.put(aClass, test);
sortedSet.add(test);
}
private static String getClassName(Object o) {
return o == null ? null : o.getClass().getName();
}
private TestUnit findTestById(String id) {
if (id != null) {
for(TestUnit t : sortedSet) {
if (id.equals(t.getId())) return t;
}
}
return null;
}
public Context getContext() {
return context;
}
public TestUnit getTest(Class<? extends TestUnit> aClass) {
return list.get(aClass);
}
public int getCount() {
return list.size();
}
public Set<TestUnit> getSortedSet() {
return Collections.unmodifiableSet(sortedSet);
}
public Set<String> getIdSet() {
return Collections.unmodifiableSet(idSet);
}
public static void main(String[] args) {
Reporter reporter = null;
String outFile = null;
for (String arg : args) {
if ("-html".equals(arg))
reporter = new HtmlReporter();
else if (arg.startsWith("-out=")) {
outFile = arg.substring(5);
}
}
if (reporter == null) reporter = new TextReporter();
if (outFile != null)
try {
reporter.setOutputFile(outFile);
} catch (IOException e) {
log.error("", e);
System.exit(1);
}
// must setup report before initializing the Loader
// and must get Loader with false argument to bypass init check
final Loader loader = Loader.getInstance(false);
Context context = loader.getContext();
context.setReporter(reporter);
reporter.setup();
loader.init();
loader.execute();
int failed = reporter.generateSummary();
System.exit(failed == 0 ? 0 : 1);
}
}