/*
* Copyright 2006-2010 the original author or authors.
*
* Licensed 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.
*/
package com.consol.citrus;
import com.consol.citrus.exceptions.CitrusRuntimeException;
import com.consol.citrus.exceptions.TestEngineFailedException;
import org.apache.commons.cli.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.io.ClassPathResource;
import org.testng.TestNG;
import org.testng.xml.*;
import java.io.*;
import java.util.*;
/**
* Citrus command line application.
*
* @author Christoph Deppisch
* @since 2008
*/
public final class Citrus {
/** Logger */
private static Logger log = LoggerFactory.getLogger(Citrus.class);
/** XML file extension */
private static final String XML_FILE_EXTENSION = ".xml";
/** Citrus version */
private static String version;
/** Command line arguments */
private CommandLine cmdArgs;
/** TestNG */
private TestNG testng = new TestNG(true);
/** Load Citrus version */
static {
Properties versionProperties = new Properties();
try (final InputStream in = new ClassPathResource("META-INF/citrus.version").getInputStream()) {
versionProperties.load(in);
} catch (IOException e) {
version = "";
}
version = versionProperties.get("citrus.version").toString();
}
/**
* Default constructor.
* @param cmdArgs the command line arguments.
*/
public Citrus(CommandLine cmdArgs) {
this.cmdArgs = cmdArgs;
}
/**
* Main CLI method.
* @param args
*/
public static void main(String[] args) {
Options options = new CitrusCliOptions();
HelpFormatter formatter = new HelpFormatter();
try {
CommandLine cmd = new GnuParser().parse(options, args);
if (cmd.hasOption("help")) {
formatter.printHelp("CITRUS TestFramework", options);
return;
}
Citrus citrus = new Citrus(cmd);
citrus.run();
} catch (ParseException e) {
log.error("Failed to parse command line arguments", e);
formatter.printHelp("CITRUS TestFramework", options);
}
}
/**
* Runs all tests using TestNG.
*/
public void run() {
log.info("CITRUS " + getVersion());
log.info("");
String testDirectory = cmdArgs.getOptionValue("testdir", CitrusConstants.DEFAULT_TEST_DIRECTORY);
if (!testDirectory.endsWith(File.separator)) {
testDirectory = testDirectory + File.separator;
}
XmlSuite suite = new XmlSuite();
suite.setName(cmdArgs.getOptionValue("suitename", "citrus-test-suite"));
if (cmdArgs.hasOption("test")) {
for (String testName : cmdArgs.getOptionValues("test")) {
addTest(testName, testDirectory, suite);
}
}
if (cmdArgs.hasOption("package")) {
for (String packageName : cmdArgs.getOptionValues("package")) {
addTest(packageName, suite);
}
}
if (cmdArgs.getArgList().size() > 0) {
testng.setTestSuites(getTestSuites(cmdArgs.getArgs()));
}
List<XmlSuite> suites = new ArrayList<XmlSuite>();
suites.add(suite);
testng.setXmlSuites(suites);
testng.run();
if (testng.hasFailure()) {
throw new TestEngineFailedException("Citrus test run failed!");
}
}
/**
* Get suites defined in external testng files.
* @param testNgXmlArgs
* @return
*/
private List<String> getTestSuites(String[] testNgXmlArgs) {
List<String> suites = new ArrayList<String>();
for (String testNgXmlFile : testNgXmlArgs) {
if (testNgXmlFile.endsWith(XML_FILE_EXTENSION)) {
suites.add(testNgXmlFile);
} else {
log.warn("Unrecognized argument '" + testNgXmlFile + "'");
}
}
return suites;
}
/**
* Adds all tests in package to test suite.
* @param packageName the test package.
* @param suite the XML suite.
*/
private void addTest(String packageName, XmlSuite suite) {
XmlTest test = new XmlTest(suite);
test.setName(packageName);
XmlPackage xmlPackage = new XmlPackage();
xmlPackage.setName(packageName);
test.setXmlPackages(Collections.singletonList(xmlPackage));
}
/**
* Adds a new XML class to test suite.
* @param testName the test name.
* @param testDirectory the test directory.
* @param suite the XML suite.
*/
private void addTest(String testName, String testDirectory, XmlSuite suite) {
XmlTest test = new XmlTest(suite);
test.setName(testName);
try {
test.setXmlClasses(Collections.singletonList(
new XmlClass(getClassNameForTest(testDirectory, testName.trim()))));
} catch (FileNotFoundException e) {
throw new TestEngineFailedException("TestSuite failed with error", e);
}
}
/**
* Method to retrieve the full class name for a test.
* Hierarchy of folders is supported, too.
*
* @param startDir directory where to start the search
* @param testName test name to search for
* @throws CitrusRuntimeException
* @return the class name of the test
*/
private String getClassNameForTest(final String startDir, final String testName)
throws FileNotFoundException {
/* Stack to hold potential sub directories */
final Stack<File> dirs = new Stack<File>();
/* start directory */
final File startdir = new File(startDir);
if (startdir.isDirectory()) {
dirs.push(startdir);
}
log.info("Starting test search in dir: " + startdir.getAbsolutePath());
/* walk through the directories */
while (dirs.size() > 0) {
File file = dirs.pop();
File[] found = file.listFiles(new TestCaseFileNameFilter());
for (int i = 0; i < found.length; i++) {
/* Subfolder support */
if (found[i].isDirectory()) {
dirs.push(found[i]);
} else {
if ((testName + XML_FILE_EXTENSION).equalsIgnoreCase(found[i].getName())) {
String fileName = found[i].getPath();
fileName = fileName.substring(0, (fileName.length() - XML_FILE_EXTENSION.length()));
if (fileName.startsWith(File.separator)) {
fileName = fileName.substring(File.separator.length());
}
//replace operating system path separator and translate to class package string
fileName = fileName.substring(startDir.startsWith(File.separator) ? startDir.length()-1 : startDir.length()).replace(File.separatorChar, '.');
if (log.isDebugEnabled()) {
log.debug("Found test '" + fileName + "'");
}
return fileName;
}
}
}
}
throw new CitrusRuntimeException("Could not find test with name '"
+ testName + "'. Test directory is: " + startDir);
}
/**
* Filter for test case files (usually .xml files)
*/
private static final class TestCaseFileNameFilter implements FilenameFilter {
public boolean accept(File dir, String name) {
File tmp = new File(dir.getPath() + File.separator + name);
/* Only allowing XML files as spring configuration files */
return (name.endsWith(XML_FILE_EXTENSION) || tmp.isDirectory()) && !name.startsWith("CVS") && !name.startsWith(".svn");
}
}
/**
* Gets the Citrus version from classpath resource properties.
* @return
*/
public static String getVersion() {
return version;
}
/**
* Sets the testng.
* @param testng the testng to set
*/
public void setTestNG(TestNG testng) {
this.testng = testng;
}
}