org.testng.internal.Utils Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of testng Show documentation
Show all versions of testng Show documentation
A testing framework for the JVM
package org.testng.internal;
import javax.annotation.Nullable;
import org.testng.ITestNGMethod;
import org.testng.TestNG;
import org.testng.TestNGException;
import org.testng.TestRunner;
import org.testng.annotations.IConfigurationAnnotation;
import org.testng.annotations.ITestAnnotation;
import org.testng.collections.Lists;
import org.testng.internal.annotations.AnnotationHelper;
import org.testng.internal.annotations.IAnnotationFinder;
import org.testng.log4testng.Logger;
import org.testng.reporters.XMLStringBuffer;
import org.testng.xml.XmlClass;
import java.io.*;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.*;
/**
* Helper methods to parse annotations.
*/
public final class Utils {
private static final String LINE_SEP = System.getProperty("line.separator");
private static final char[] SPECIAL_CHARACTERS = {'*','/','\\','?','%',':',';','<','>','&','~','|'};
public static final char CHAR_REPLACEMENT = '_';
public static final char UNICODE_REPLACEMENT = 0xFFFD;
private static final String FORMAT = String.format("[%s]", Utils.class.getSimpleName());
private static final Logger LOG = Logger.getLogger(Utils.class);
private static final Map ESCAPES = new HashMap<>();
static {
ESCAPES.put('<', "<");
ESCAPES.put('>', ">");
ESCAPES.put('\'', "'");
ESCAPES.put('"', """);
ESCAPES.put('&', "&");
}
/**
* Hide constructor for utility class.
*/
private Utils() {
// Hide constructor
}
/**
* Splits the given String s into tokens where the separator is
* either the space character or the comma character. For example,
* if s is "a,b, c" this method returns {"a", "b", "c"}
*
* @param s the string to split
* @return the split token
*
* @deprecated Unused
*/
@Deprecated
public static String[] stringToArray(String s) {
// TODO CQ would s.split() be a better way of doing this?
StringTokenizer st = new StringTokenizer(s, " ,");
String[] result = new String[st.countTokens()];
for (int i = 0; i < result.length; i++) {
result[i] = st.nextToken();
}
return result;
}
public static XmlClass[] classesToXmlClasses(Class[] classes) {
List result = Lists.newArrayList();
for (Class cls : classes) {
result.add(new XmlClass(cls, true /* load classes */));
}
return result.toArray(new XmlClass[classes.length]);
}
/**
* @deprecated Unused
*/
@Deprecated
public static String[] parseMultiLine(String line) {
List vResult = Lists.newArrayList();
if (isStringNotBlank(line)) {
StringTokenizer st = new StringTokenizer(line, " ");
while (st.hasMoreTokens()) {
vResult.add(st.nextToken());
}
// Bug in split when passed " " : returns one too many result
// result = line.split(" ");
}
return vResult.toArray(new String[vResult.size()]);
}
public static void writeUtf8File(@Nullable String outputDir, String fileName, XMLStringBuffer xsb, String prefix) {
try {
final File outDir = (outputDir != null) ? new File(outputDir) : new File("").getAbsoluteFile();
if (!outDir.exists()) {
outDir.mkdirs();
}
final File file = new File(outDir, fileName);
if (!file.exists()) {
file.createNewFile();
}
try (final OutputStreamWriter w = new OutputStreamWriter(new FileOutputStream(file), "UTF-8")) {
if (prefix != null) {
w.append(prefix);
}
xsb.toWriter(w);
}
} catch(IOException ex) {
LOG.error(ex.getMessage(), ex);
}
}
/**
* Writes the content of the sb string to the file named filename in outDir encoding the output as UTF-8.
* If outDir does not exist, it is created.
*
* @param outputDir the output directory (may not exist). If null then current directory is used.
* @param fileName the filename
* @param sb the file content
*/
public static void writeUtf8File(@Nullable String outputDir, String fileName, String sb) {
final String outDirPath= outputDir != null ? outputDir : "";
final File outDir= new File(outDirPath);
writeFile(outDir, fileName, escapeUnicode(sb), "UTF-8");
}
/**
* Writes the content of the sb string to the file named filename in outDir. If
* outDir does not exist, it is created.
*
* @param outputDir the output directory (may not exist). If null then current directory is used.
* @param fileName the filename
* @param sb the file content
*/
public static void writeFile(@Nullable String outputDir, String fileName, String sb) {
final String outDirPath= outputDir != null ? outputDir : "";
final File outDir= new File(outDirPath);
writeFile(outDir, fileName, sb, null);
}
/**
* Writes the content of the sb string to the file named filename in outDir. If
* outDir does not exist, it is created.
*
* @param outputFolder the output directory (may not exist). If null then current directory is used.
* @param fileNameParameter the filename
* @param sb the file content
*/
private static void writeFile(@Nullable File outputFolder, String fileNameParameter, String sb, @Nullable String encoding) {
File outDir = outputFolder;
String fileName = fileNameParameter;
try {
if (outDir == null) {
outDir = new File("").getAbsoluteFile();
}
if (!outDir.exists()) {
outDir.mkdirs();
}
fileName = replaceSpecialCharacters(fileName);
File outputFile = new File(outDir, fileName);
outputFile.delete();
log(FORMAT, 3, "Attempting to create " + outputFile);
log(FORMAT, 3, " Directory " + outDir + " exists: " + outDir.exists());
outputFile.createNewFile();
writeFile(outputFile, sb, encoding);
}
catch (IOException e) {
if (TestRunner.getVerbose() > 1) {
LOG.error(e.getMessage(),e);
}
else {
log(FORMAT, 1, e.getMessage());
}
}
}
private static void writeFile(File outputFile, String sb, @Nullable String encoding) {
BufferedWriter fw = null;
try {
fw = openWriter(outputFile, encoding);
fw.write(sb);
Utils.log("", 3, "Creating " + outputFile.getAbsolutePath());
}
catch(IOException ex) {
if (TestRunner.getVerbose() > 1) {
LOG.error("ERROR WHILE WRITING TO " + outputFile,ex);
}
else {
log(FORMAT, 1, "Error while writing to " + outputFile + ": " + ex.getMessage());
}
}
finally {
try {
if (fw != null) {
fw.close();
}
}
catch (IOException e) {
// ignore
}
}
}
/**
* Open a BufferedWriter for the specified file. If output directory doesn't
* exist, it is created. If the output file exists, it is deleted. The output file is
* created in any case.
* @param outputDir output directory. If null, then current directory is used
* @param fileNameParameter file name
* @throws IOException if anything goes wrong while creating files.
*/
public static BufferedWriter openWriter(@Nullable String outputDir, String fileNameParameter) throws IOException {
String fileName = fileNameParameter;
String outDirPath= outputDir != null ? outputDir : "";
File outDir= new File(outDirPath);
if (!outDir.exists()) {
outDir.mkdirs();
}
fileName = replaceSpecialCharacters(fileName);
File outputFile = new File(outDir, fileName);
outputFile.delete();
return openWriter(outputFile, null);
}
private static BufferedWriter openWriter(File outputFile, @Nullable String encoding) throws IOException {
if (!outputFile.exists()) {
outputFile.createNewFile();
}
OutputStreamWriter osw;
if (null != encoding) {
osw = new OutputStreamWriter(new FileOutputStream(outputFile), encoding);
}
else {
osw = new OutputStreamWriter(new FileOutputStream(outputFile));
}
return new BufferedWriter(osw);
}
private static void ppp(String s) {
Utils.log("Utils", 0, s);
}
/**
* @deprecated Unused
*/
@Deprecated
public static void dumpMap(Map result) {
LOG.info("vvvvv");
for (Map.Entry entry : result.entrySet()) {
LOG.info(entry.getKey() + " => " + entry.getValue());
}
LOG.info("^^^^^");
}
/**
* @deprecated Unused
*/
@Deprecated
public static void dumpMethods(List allMethods) {
ppp("======== METHODS:");
for (ITestNGMethod tm : allMethods) {
ppp(" " + tm);
}
}
/**
* @deprecated Unused
*/
@Deprecated
public static String[] dependentGroupsForThisMethodForTest(Method m, IAnnotationFinder finder) {
List vResult = Lists.newArrayList();
Class cls = m.getDeclaringClass();
// Collect groups on the class
ITestAnnotation tc = AnnotationHelper.findTest(finder, cls);
if (null != tc) {
for (String group : tc.getDependsOnGroups()) {
vResult.add(group);
}
}
// Collect groups on the method
ITestAnnotation tm = AnnotationHelper.findTest(finder, m);
if (null != tm) {
String[] groups = tm.getDependsOnGroups();
for (String group : groups) {
vResult.add(group);
}
}
return vResult.toArray(new String[vResult.size()]);
}
/**
* @deprecated Unused
*/
@Deprecated
public static String[] groupsForThisMethodForTest(Method m, IAnnotationFinder finder) {
List vResult = Lists.newArrayList();
Class cls = m.getDeclaringClass();
// Collect groups on the class
ITestAnnotation tc = AnnotationHelper.findTest(finder, cls);
if (null != tc) {
for (String group : tc.getGroups()) {
vResult.add(group);
}
}
// Collect groups on the method
ITestAnnotation tm = AnnotationHelper.findTest(finder, m);
if (null != tm) {
String[] groups = tm.getGroups();
for (String group : groups) {
vResult.add(group);
}
}
return vResult.toArray(new String[vResult.size()]);
}
/**
* @deprecated Unused
*/
@Deprecated
public static String[] groupsForThisMethodForConfiguration(Method m, IAnnotationFinder finder) {
String[] result = {};
// Collect groups on the method
ITestAnnotation tm = AnnotationHelper.findTest(finder, m);
if (null != tm) {
result = tm.getGroups();
}
return result;
}
/**
* @deprecated Unused
*/
@Deprecated
public static String[] dependentGroupsForThisMethodForConfiguration(Method m,
IAnnotationFinder finder) {
String[] result = {};
// Collect groups on the method
IConfigurationAnnotation tm = AnnotationHelper.findConfiguration(finder, m);
if (null != tm) {
result = tm.getDependsOnGroups();
}
return result;
}
public static void log(String msg) {
log("Utils", 2, msg);
}
/**
* Logs the the message to System.out if level is greater than
* or equal to TestRunner.getVerbose(). The message is logged as:
*
* "[cls] msg"
*
*
* @param cls the class name to prefix the log message.
* @param level the logging level of the message.
* @param msg the message to log to System.out.
*/
public static void log(String cls, int level, String msg) {
// Why this coupling on a static member of TestRunner.getVerbose()?
if (TestRunner.getVerbose() >= level) {
if (cls.length() > 0) {
LOG.info("[" + cls + "] " + msg);
}
else {
LOG.info(msg);
}
}
}
public static void error(String errorMessage) {
LOG.error("[Error] " + errorMessage);
}
public static int calculateInvokedMethodCount(ITestNGMethod[] methods) {
return methods.length;
}
public static int calculateInvokedMethodCount(List methods) {
return methods.size();
}
/**
* Tokenize the string using the separator.
*/
public static String[] split(String string, String sep) {
if ((string == null) || (string.length() == 0)) {
return new String[0];
}
// TODO How different is this from:
// return string.split(sep);
int start = 0;
int idx = string.indexOf(sep, start);
int len = sep.length();
List strings = Lists.newArrayList();
while (idx != -1) {
strings.add(string.substring(start, idx).trim());
start = idx + len;
idx = string.indexOf(sep, start);
}
strings.add(string.substring(start).trim());
return strings.toArray(new String[strings.size()]);
}
/**
* @deprecated Unused
*/
@Deprecated
public static void logInvocation(String reason, Method thisMethod, Object[] parameters) {
String clsName = thisMethod.getDeclaringClass().getName();
int n = clsName.lastIndexOf('.');
if (n >= 0) {
clsName = clsName.substring(n + 1);
}
String methodName = clsName + '.' + thisMethod.getName();
if (TestRunner.getVerbose() >= 2) {
StringBuilder paramString = new StringBuilder();
if (parameters != null) {
for (Object p : parameters) {
paramString.append(p.toString()).append(' ');
}
}
log("", 2, "Invoking " + reason + methodName + '(' + paramString + ')');
}
}
public static void writeResourceToFile(File file, String resourceName, Class clasz) throws IOException {
InputStream inputStream = clasz.getResourceAsStream("/" + resourceName);
if (inputStream == null) {
LOG.error("Couldn't find resource on the class path: " + resourceName);
return;
}
try {
try (FileOutputStream outputStream = new FileOutputStream(file)) {
int nread;
byte[] buffer = new byte[4096];
while (0 < (nread = inputStream.read(buffer))) {
outputStream.write(buffer, 0, nread);
}
}
} finally {
inputStream.close();
}
}
public static String defaultIfStringEmpty(String s, String defaultValue) {
return isStringEmpty(s) ? defaultValue : s;
}
public static boolean isStringBlank(String s) {
return s == null || "".equals(s.trim());
}
public static boolean isStringEmpty(String s) {
return s == null || "".equals(s);
}
public static boolean isStringNotBlank(String s) {
return !isStringBlank(s);
}
public static boolean isStringNotEmpty(String s) {
return !isStringEmpty(s);
}
/**
* @return an array of two strings: the short stack trace and the long stack trace.
* @deprecated - Please consider using :
*
* - {@link Utils#longStackTrace(Throwable, boolean)} - for getting full stack trace
* - {@link Utils#shortStackTrace(Throwable, boolean)} - for getting short stack trace
*
*/
@Deprecated
public static String[] stackTrace(Throwable t, boolean toHtml) {
return new String[] {
shortStackTrace(t, toHtml), longStackTrace(t, toHtml)
};
}
/**
* Helper that returns a short stack trace.
* @param t - The {@link Throwable} exception
* @param toHtml - true
if the stacktrace should be translated to html as well
* @return - A string that represents the short stack trace.
*/
public static String longStackTrace(Throwable t, boolean toHtml) {
return buildStackTrace(t, toHtml, StackTraceType.FULL);
}
/**
* Helper that returns a long stack trace.
* @param t - The {@link Throwable} exception
* @param toHtml - true
if the stacktrace should be translated to html as well
* @return - A string that represents the full stack trace.
*/
public static String shortStackTrace(Throwable t, boolean toHtml) {
return buildStackTrace(t, toHtml, StackTraceType.SHORT);
}
private static String buildStackTrace(Throwable t, boolean toHtml, StackTraceType type) {
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw);
t.printStackTrace(pw);
pw.flush();
String stackTrace = sw.getBuffer().toString();
if (type == StackTraceType.SHORT && !isTooVerbose()) {
stackTrace = filterTrace(sw.getBuffer().toString());
}
if (toHtml) {
stackTrace = escapeHtml(stackTrace);
}
return stackTrace;
}
private static boolean isTooVerbose() {
return Boolean.getBoolean(TestNG.SHOW_TESTNG_STACK_FRAMES) || TestRunner.getVerbose() >= 2;
}
private enum StackTraceType {
SHORT,FULL
}
public static String escapeHtml(String s) {
if (s == null) {
return null;
}
StringBuilder result = new StringBuilder();
for (int i = 0; i < s.length(); i++) {
char c = s.charAt(i);
String nc = ESCAPES.get(c);
if (nc != null) {
result.append(nc);
} else {
result.append(c);
}
}
return result.toString();
}
public static String escapeUnicode(String s) {
if (s == null) {
return null;
}
StringBuilder result = new StringBuilder();
for (int i = 0; i < s.length(); i++) {
char c = s.charAt(i);
char ca = Character.isDefined(c) ? c: UNICODE_REPLACEMENT;
result.append(ca);
}
return result.toString();
}
static String filterTrace(String trace) {
StringReader stringReader = new StringReader(trace);
BufferedReader bufferedReader = new BufferedReader(stringReader);
StringBuilder buf = new StringBuilder();
try {
// first line contains the thrown exception
String line = bufferedReader.readLine();
if(line == null) {
return "";
}
buf.append(line).append(LINE_SEP);
//
// the stack frames of the trace
//
String[] excludedStrings = new String[] {
"org.testng",
"reflect",
"org.gradle",
"org.apache.maven.surefire"
};
int excludedCount = 0;
while((line = bufferedReader.readLine()) != null) {
boolean isExcluded = false;
for (String excluded : excludedStrings) {
if(line.contains(excluded)) {
isExcluded = true;
excludedCount++;
break;
}
}
if (! isExcluded) {
buf.append(line).append(LINE_SEP);
}
}
if (excludedCount > 0) {
buf.append("... Removed ").append(excludedCount).append(" stack frames");
}
}
catch(IOException ioex) {
// do nothing
}
return buf.toString();
}
public static String toString(Object object, Class objectClass) {
if(null == object) {
return "null";
}
final String toString= object.toString();
if(isStringEmpty(toString)) {
return "\"\"";
}
else if (String.class.equals(objectClass)) {
return "\"" + toString + '\"';
}
else {
return toString;
}
}
public static String detailedMethodName(ITestNGMethod method, boolean fqn) {
String tempName = annotationFormFor(method);
if (! tempName.isEmpty()) {
tempName += " ";
}
return tempName + (fqn ? method.toString() : method.getMethodName());
}
/**
* Given a TestNG method, returns the corresponding annotation based on the method type
* @param method - An {@link ITestNGMethod} object.
* @return - A String representation of the corresponding annotation.
*/
public static String annotationFormFor(ITestNGMethod method) {
if(method.isBeforeSuiteConfiguration()) {
return "@BeforeSuite";
}
if(method.isBeforeTestConfiguration()) {
return "@BeforeTest";
}
if(method.isBeforeClassConfiguration()) {
return "@BeforeClass";
}
if(method.isBeforeGroupsConfiguration()) {
return "@BeforeGroups";
}
if(method.isBeforeMethodConfiguration()) {
return "@BeforeMethod";
}
if(method.isAfterMethodConfiguration()) {
return "@AfterMethod";
}
if(method.isAfterGroupsConfiguration()) {
return "@AfterGroups";
}
if(method.isAfterClassConfiguration()) {
return "@AfterClass";
}
if (method.isAfterTestConfiguration()) {
return "@AfterTest";
}
if(method.isAfterSuiteConfiguration()) {
return "@AfterSuite";
}
return "";
}
public static String arrayToString(String[] strings) {
StringBuilder result = new StringBuilder("");
if ((strings != null) && (strings.length > 0)) {
for (int i = 0; i < strings.length; i++) {
result.append(strings[i]);
if (i < strings.length - 1) {
result.append(", ");
}
}
}
return result.toString();
}
/**
* If the file name contains special characters like *,/,\ and so on,
* exception will be thrown and report file will not be created.
* Special characters are platform specific and they are not same for
* example on Windows and Macintosh. * is not allowed on Windows, but it is on Macintosh.
* In order to have the same behavior of testng on the all platforms, characters like * will
* be replaced on all platforms whether they are causing the problem or not.
*
* @param fileNameParameter file name that could contain special characters.
* @return fileName with special characters replaced
*/
public static String replaceSpecialCharacters(String fileNameParameter) {
String fileName = fileNameParameter;
if (fileName == null || fileName.length() == 0) {
return fileName;
}
for (char element : SPECIAL_CHARACTERS) {
fileName = fileName.replace(element, CHAR_REPLACEMENT);
}
return fileName;
}
public static String join(List objects, String separator) {
StringBuilder result = new StringBuilder();
for (int i = 0; i < objects.size(); i++) {
if (i > 0) {
result.append(separator);
}
result.append(objects.get(i).toString());
}
return result.toString();
}
/**
* @deprecated Unused
*/
@Deprecated
public static void copyFile(File from, File to) {
to.getParentFile().mkdirs();
try (InputStream in = new FileInputStream(from); OutputStream out = new FileOutputStream(to)) {
byte[] buf = new byte[1024];
int len;
while ((len = in.read(buf)) > 0) {
out.write(buf, 0, len);
}
} catch(IOException e){
LOG.error(e.getMessage(),e);
}
}
/**
* @return a temporary file with the given content.
*/
public static File createTempFile(String content) {
try {
// Create temp file.
File result = File.createTempFile("testng-tmp", "");
// Delete temp file when program exits.
result.deleteOnExit();
// Write to temp file
try (BufferedWriter out = new BufferedWriter(new FileWriter(result))) {
out.write(content);
}
return result;
} catch (IOException e) {
throw new TestNGException(e);
}
}
/**
* Make sure that either we have an instance or if not, that the method is static
*/
public static void checkInstanceOrStatic(Object instance, Method method) {
if (instance == null && method != null && ! Modifier.isStatic(method.getModifiers())) {
throw new TestNGException("Can't invoke " + method + ": either make it static or add "
+ "a no-args constructor to your class");
}
}
public static void checkReturnType(Method method, Class... returnTypes) {
if (method == null) {
return;
}
for (Class returnType : returnTypes) {
if (method.getReturnType() == returnType) {
return;
}
}
throw new TestNGException(method.getDeclaringClass().getName() + "."
+ method.getName() + " MUST return " + toString(returnTypes) + " but returns " + method.getReturnType().getName());
}
private static String toString(Class[] classes) {
StringBuilder sb = new StringBuilder("[ ");
for (int i=0; i clazz = classes[i];
if (clazz.isArray()) {
sb.append(clazz.getComponentType().getName()).append("[]");
} else {
sb.append(clazz.getName());
}
if ( (i+1) < classes.length) { // increment and compare
sb.append(" or ");
}
}
sb.append(" ]");
return sb.toString();
}
/**
* Returns the string representation of the specified object, transparently
* handling null references and arrays.
*
* @param obj
* the object
* @return the string representation
*/
public static String toString(Object obj) {
String result;
if (obj != null) {
if (obj instanceof boolean[]) {
result = Arrays.toString((boolean[]) obj);
} else if (obj instanceof byte[]) {
result = Arrays.toString((byte[]) obj);
} else if (obj instanceof char[]) {
result = Arrays.toString((char[]) obj);
} else if (obj instanceof double[]) {
result = Arrays.toString((double[]) obj);
} else if (obj instanceof float[]) {
result = Arrays.toString((float[]) obj);
} else if (obj instanceof int[]) {
result = Arrays.toString((int[]) obj);
} else if (obj instanceof long[]) {
result = Arrays.toString((long[]) obj);
} else if (obj instanceof Object[]) {
result = Arrays.deepToString((Object[]) obj);
} else if (obj instanceof short[]) {
result = Arrays.toString((short[]) obj);
} else {
result = obj.toString();
}
} else {
result = "null";
}
return result;
}
}