1 package org.testng.internal;
2 
3 import java.io.BufferedReader;
4 import java.io.BufferedWriter;
5 import java.io.File;
6 import java.io.FileInputStream;
7 import java.io.FileOutputStream;
8 import java.io.FileWriter;
9 import java.io.IOException;
10 import java.io.InputStream;
11 import java.io.OutputStream;
12 import java.io.OutputStreamWriter;
13 import java.io.PrintWriter;
14 import java.io.StringReader;
15 import java.io.StringWriter;
16 import java.lang.reflect.Method;
17 import java.lang.reflect.Modifier;
18 import java.util.Arrays;
19 import java.util.HashMap;
20 import java.util.List;
21 import java.util.Map;
22 import java.util.StringTokenizer;
23 import java.util.logging.FileHandler;
24 import java.util.logging.Level;
25 import java.util.logging.Logger;
26 
27 import org.testng.ITestNGMethod;
28 import org.testng.TestNG;
29 import org.testng.TestNGException;
30 import org.testng.TestRunner;
31 import org.testng.annotations.IConfigurationAnnotation;
32 import org.testng.annotations.ITestAnnotation;
33 import org.testng.collections.Lists;
34 import org.testng.internal.annotations.AnnotationHelper;
35 import org.testng.internal.annotations.IAnnotationFinder;
36 import org.testng.log.TextFormatter;
37 import org.testng.reporters.XMLStringBuffer;
38 import org.testng.xml.XmlClass;
39 
40 /**
41  * Helper methods to parse annotations.
42  *
43  * @author Cedric Beust, Apr 26, 2004
44  */
45 public final class Utils {
46   private static final String LINE_SEP = System.getProperty("line.separator");
47 
48   public static final char[] SPECIAL_CHARACTERS =
49       {'*','/','\\','?','%',':',';','<','>','&','~','|'};
50   public static final char CHAR_REPLACEMENT = '_';
51   public static final char UNICODE_REPLACEMENT = 0xFFFD;
52 
53   /**
54    * Hide constructor for utility class.
55    */
Utils()56   private Utils() {
57     // Hide constructor
58   }
59 
60   /**
61    * Splits the given String s into tokens where the separator is
62    * either the space character or the comma character. For example,
63    * if s is "a,b, c" this method returns {"a", "b", "c"}
64    *
65    * @param s the string to split
66    * @return the split token
67    */
stringToArray(String s)68   public static String[] stringToArray(String s) {
69     // TODO CQ would s.split() be a better way of doing this?
70     StringTokenizer st = new StringTokenizer(s, " ,");
71     String[] result = new String[st.countTokens()];
72     for (int i = 0; i < result.length; i++) {
73       result[i] = st.nextToken();
74     }
75 
76     return result;
77   }
78 
classesToXmlClasses(Class<?>[] classes)79   public static XmlClass[] classesToXmlClasses(Class<?>[] classes) {
80     List<XmlClass> result = Lists.newArrayList();
81 
82     for (Class<?> cls : classes) {
83       result.add(new XmlClass(cls, true /* load classes */));
84     }
85 
86     return result.toArray(new XmlClass[classes.length]);
87   }
88 
parseMultiLine(String line)89   public static String[] parseMultiLine(String line) {
90     List<String> vResult = Lists.newArrayList();
91     if (isStringNotBlank(line)) {
92       StringTokenizer st = new StringTokenizer(line, " ");
93       while (st.hasMoreTokens()) {
94         vResult.add(st.nextToken());
95       }
96       // Bug in split when passed " " : returns one too many result
97       //      result = line.split(" ");
98     }
99 
100     return vResult.toArray(new String[vResult.size()]);
101   }
102 
writeUtf8File(@ullable String outputDir, String fileName, XMLStringBuffer xsb, String prefix)103   public static void writeUtf8File(@Nullable String outputDir, String fileName, XMLStringBuffer xsb, String prefix) {
104     try {
105       final File outDir = (outputDir != null) ? new File(outputDir) : new File("").getAbsoluteFile();
106       if (!outDir.exists()) {
107         outDir.mkdirs();
108       }
109       final File file = new File(outDir, fileName);
110       if (!file.exists()) {
111         file.createNewFile();
112       }
113       final OutputStreamWriter w = new OutputStreamWriter(new FileOutputStream(file), "UTF-8");
114       if (prefix != null) {
115         w.append(prefix);
116       }
117       xsb.toWriter(w);
118       w.close();
119     } catch(IOException ex) {
120       ex.printStackTrace();
121     }
122   }
123 
124   /**
125    * Writes the content of the sb string to the file named filename in outDir encoding the output as UTF-8.
126    * If outDir does not exist, it is created.
127    *
128    * @param outputDir the output directory (may not exist). If <tt>null</tt> then current directory is used.
129    * @param fileName the filename
130    * @param sb the file content
131    */
writeUtf8File(@ullable String outputDir, String fileName, String sb)132   public static void writeUtf8File(@Nullable String outputDir, String fileName, String sb) {
133     final String outDirPath= outputDir != null ? outputDir : "";
134     final File outDir= new File(outDirPath);
135     writeFile(outDir, fileName, escapeUnicode(sb), "UTF-8", false /* don't append */);
136   }
137 
138   /**
139    * Writes the content of the sb string to the file named filename in outDir. If
140    * outDir does not exist, it is created.
141    *
142    * @param outputDir the output directory (may not exist). If <tt>null</tt> then current directory is used.
143    * @param fileName the filename
144    * @param sb the file content
145    */
writeFile(@ullable String outputDir, String fileName, String sb)146   public static void writeFile(@Nullable String outputDir, String fileName, String sb) {
147     final String outDirPath= outputDir != null ? outputDir : "";
148     final File outDir= new File(outDirPath);
149     writeFile(outDir, fileName, sb, null, false /* don't append */);
150   }
151 
152   /**
153    * Writes the content of the sb string to the file named filename in outDir. If
154    * outDir does not exist, it is created.
155    *
156    * @param outDir the output directory (may not exist). If <tt>null</tt> then current directory is used.
157    * @param fileName the filename
158    * @param sb the file content
159    */
writeFile(@ullable File outDir, String fileName, String sb, @Nullable String encoding, boolean append)160   private static void writeFile(@Nullable File outDir, String fileName, String sb, @Nullable String encoding, boolean append) {
161     try {
162       if (outDir == null) {
163         outDir = new File("").getAbsoluteFile();
164       }
165       if (!outDir.exists()) {
166         outDir.mkdirs();
167       }
168 
169       fileName = replaceSpecialCharacters(fileName);
170       File outputFile = new File(outDir, fileName);
171       if (!append) {
172         outputFile.delete();
173         outputFile.createNewFile();
174       }
175       writeFile(outputFile, sb, encoding, append);
176     }
177     catch (IOException e) {
178       if (TestRunner.getVerbose() > 1) {
179         e.printStackTrace();
180       }
181       else {
182         log("[Utils]", 1, e.getMessage());
183       }
184     }
185   }
186 
writeFile(File outputFile, String sb, @Nullable String encoding, boolean append)187   private static void writeFile(File outputFile, String sb, @Nullable String encoding, boolean append) {
188     BufferedWriter fw = null;
189     try {
190       fw = openWriter(outputFile, encoding, append);
191       fw.write(sb);
192 
193       Utils.log("", 3, "Creating " + outputFile.getAbsolutePath());
194     }
195     catch(IOException ex) {
196       if (TestRunner.getVerbose() > 1) {
197         System.err.println("ERROR WHILE WRITING TO " + outputFile);
198         ex.printStackTrace();
199       }
200       else {
201         log("[Utils]", 1, "Error while writing to " + outputFile + ": " + ex.getMessage());
202       }
203     }
204     finally {
205       try {
206         if (fw != null) {
207           fw.close();
208         }
209       }
210       catch (IOException e) {
211         ; // ignore
212       }
213     }
214   }
215 
216   /**
217    * Open a BufferedWriter for the specified file. If output directory doesn't
218    * exist, it is created. If the output file exists, it is deleted. The output file is
219    * created in any case.
220    * @param outputDir output directory. If <tt>null</tt>, then current directory is used
221    * @param fileName file name
222    * @throws IOException if anything goes wrong while creating files.
223    */
openWriter(@ullable String outputDir, String fileName)224   public static BufferedWriter openWriter(@Nullable String outputDir, String fileName) throws IOException {
225     String outDirPath= outputDir != null ? outputDir : "";
226     File outDir= new File(outDirPath);
227     if (outDir.exists()) {
228       outDir.mkdirs();
229     }
230     fileName = replaceSpecialCharacters(fileName);
231     File outputFile = new File(outDir, fileName);
232     outputFile.delete();
233     return openWriter(outputFile, null, false);
234   }
235 
openWriter(File outputFile, @Nullable String encoding, boolean append)236   private static BufferedWriter openWriter(File outputFile, @Nullable String encoding, boolean append) throws IOException {
237     if (!outputFile.exists()) {
238       outputFile.createNewFile();
239     }
240     OutputStreamWriter osw= null;
241     if (null != encoding) {
242       osw = new OutputStreamWriter(new FileOutputStream(outputFile, append), encoding);
243     }
244     else {
245       osw = new OutputStreamWriter(new FileOutputStream(outputFile, append));
246     }
247     return new BufferedWriter(osw);
248   }
249 
ppp(String s)250   private static void ppp(String s) {
251     Utils.log("Utils", 0, s);
252   }
253 
254   /**
255    * @param result
256    */
dumpMap(Map<?, ?> result)257   public static void dumpMap(Map<?, ?> result) {
258     System.out.println("vvvvv");
259     for (Map.Entry<?, ?> entry : result.entrySet()) {
260       System.out.println(entry.getKey() + " => " + entry.getValue());
261     }
262     System.out.println("^^^^^");
263   }
264 
265   /**
266    * @param allMethods
267    */
dumpMethods(List<ITestNGMethod> allMethods)268   public static void dumpMethods(List<ITestNGMethod> allMethods) {
269     ppp("======== METHODS:");
270     for (ITestNGMethod tm : allMethods) {
271       ppp("  " + tm);
272     }
273   }
274 
275   /**
276    * @return The list of dependent groups for this method, including the
277    * class groups
278    */
dependentGroupsForThisMethodForTest(Method m, IAnnotationFinder finder)279   public static String[] dependentGroupsForThisMethodForTest(Method m, IAnnotationFinder finder) {
280     List<String> vResult = Lists.newArrayList();
281     Class<?> cls = m.getDeclaringClass();
282 
283     // Collect groups on the class
284     ITestAnnotation tc = AnnotationHelper.findTest(finder, cls);
285     if (null != tc) {
286       for (String group : tc.getDependsOnGroups()) {
287         vResult.add(group);
288       }
289     }
290 
291     // Collect groups on the method
292     ITestAnnotation tm = AnnotationHelper.findTest(finder, m);
293     if (null != tm) {
294       String[] groups = tm.getDependsOnGroups();
295 
296       //       ppp("Method:" + m + " #Groups:" + groups.length);
297       for (String group : groups) {
298         vResult.add(group);
299       }
300     }
301 
302     return vResult.toArray(new String[vResult.size()]);
303   }
304 
305   /**
306    * @return The list of groups this method belongs to, including the
307    * class groups
308    */
groupsForThisMethodForTest(Method m, IAnnotationFinder finder)309   public static String[] groupsForThisMethodForTest(Method m, IAnnotationFinder finder) {
310     List<String> vResult = Lists.newArrayList();
311     Class<?> cls = m.getDeclaringClass();
312 
313     // Collect groups on the class
314     ITestAnnotation tc = AnnotationHelper.findTest(finder, cls);
315     if (null != tc) {
316       for (String group : tc.getGroups()) {
317         vResult.add(group);
318       }
319     }
320 
321     // Collect groups on the method
322     ITestAnnotation tm = AnnotationHelper.findTest(finder, m);
323     if (null != tm) {
324       String[] groups = tm.getGroups();
325 
326       //       ppp("Method:" + m + " #Groups:" + groups.length);
327       for (String group : groups) {
328         vResult.add(group);
329       }
330     }
331 
332     return vResult.toArray(new String[vResult.size()]);
333   }
334 
335   /**
336    * @return The list of groups this method belongs to, including the
337    * class groups
338    */
groupsForThisMethodForConfiguration(Method m, IAnnotationFinder finder)339   public static String[] groupsForThisMethodForConfiguration(Method m, IAnnotationFinder finder) {
340     String[] result = {};
341 
342     // Collect groups on the method
343     ITestAnnotation tm = AnnotationHelper.findTest(finder, m);
344     if (null != tm) {
345       result = tm.getGroups();
346     }
347 
348     return result;
349   }
350 
351   /**
352    * @return The list of groups this method depends on, including the
353    * class groups
354    */
dependentGroupsForThisMethodForConfiguration(Method m, IAnnotationFinder finder)355   public static String[] dependentGroupsForThisMethodForConfiguration(Method m,
356                                                                       IAnnotationFinder finder) {
357     String[] result = {};
358 
359     // Collect groups on the method
360     IConfigurationAnnotation tm = AnnotationHelper.findConfiguration(finder, m);
361     if (null != tm) {
362       result = tm.getDependsOnGroups();
363     }
364 
365     return result;
366   }
367 
log(String msg)368   public static void log(String msg) {
369     log("Utils", 2, msg);
370   }
371 
372   /**
373    * Logs the the message to System.out if level is greater than
374    * or equal to TestRunner.getVerbose(). The message is logged as:
375    * <pre>
376    *     "[cls] msg"
377    * </pre>
378    *
379    * @param cls the class name to prefix the log message.
380    * @param level the logging level of the message.
381    * @param msg the message to log to System.out.
382    */
log(String cls, int level, String msg)383   public static void log(String cls, int level, String msg) {
384     // Why this coupling on a static member of TestRunner.getVerbose()?
385     if (TestRunner.getVerbose() >= level) {
386       if (cls.length() > 0) {
387         System.out.println("[" + cls + "] " + msg);
388       }
389       else {
390         System.out.println(msg);
391       }
392     }
393   }
394 
error(String errorMessage)395   public static void error(String errorMessage) {
396     System.err.println("[Error] " + errorMessage);
397   }
398 
399   /**
400    * @return The number of methods invoked, taking into account the number
401    * of instances.
402    */
403 //  public static int calculateInvokedMethodCount(IResultMap map) {
404 //    return calculateInvokedMethodCount(
405 //        (ITestNGMethod[]) map.getAllMethods().toArray(new ITestNGMethod[map.size()]));
406 //  }
407 
calculateInvokedMethodCount(ITestNGMethod[] methods)408   public static int calculateInvokedMethodCount(ITestNGMethod[] methods) {
409     return methods.length;
410 //    int result = 0;
411 //
412 //    for (ITestNGMethod method : methods) {
413 //      int instanceCount = method.getInvocationCount();
414 //      result += instanceCount;
415 //    }
416 //
417 //    return result;
418   }
419 
420 //  public static int calculateInvokedMethodCount(Map<ITestNGMethod, ITestResult> methods) {
421 //    return calculateInvokedMethodCount(methods.keySet().toArray(new ITestNGMethod[methods.values()
422 //                                                                .size()]));
423 //  }
424 
425   /**
426    * Tokenize the string using the separator.
427    */
split(String string, String sep)428   public static String[] split(String string, String sep) {
429     if ((string == null) || (string.length() == 0)) {
430       return new String[0];
431     }
432 
433     // TODO How different is this from:
434     // return string.split(sep);
435 
436     int start = 0;
437     int idx = string.indexOf(sep, start);
438     int len = sep.length();
439     List<String> strings = Lists.newArrayList();
440 
441     while (idx != -1) {
442       strings.add(string.substring(start, idx).trim());
443       start = idx + len;
444       idx = string.indexOf(sep, start);
445     }
446 
447     strings.add(string.substring(start).trim());
448 
449     return strings.toArray(new String[strings.size()]);
450   }
451 
initLogger(Logger logger, String outputLogPath)452   public static void initLogger(Logger logger, String outputLogPath) {
453     try {
454       logger.setUseParentHandlers(false);
455       FileHandler fh = new FileHandler(outputLogPath);
456       fh.setFormatter(new TextFormatter());
457       fh.setLevel(Level.INFO);
458       logger.addHandler(fh);
459     }
460     catch (SecurityException | IOException se) {
461       se.printStackTrace();
462     }
463   }
464 
logInvocation(String reason, Method thisMethod, Object[] parameters)465   public static void logInvocation(String reason, Method thisMethod, Object[] parameters) {
466     String clsName = thisMethod.getDeclaringClass().getName();
467     int n = clsName.lastIndexOf(".");
468     if (n >= 0) {
469       clsName = clsName.substring(n + 1);
470     }
471     String methodName = clsName + '.' + thisMethod.getName();
472     if (TestRunner.getVerbose() >= 2) {
473       StringBuffer paramString = new StringBuffer();
474       if (parameters != null) {
475         for (Object p : parameters) {
476           paramString.append(p.toString()).append(' ');
477         }
478       }
479       log("", 2, "Invoking " + reason + methodName + '(' + paramString + ')');
480     }
481   }
482 
writeResourceToFile(File file, String resourceName, Class<?> clasz)483   public static void writeResourceToFile(File file, String resourceName, Class<?> clasz) throws IOException {
484     InputStream inputStream = clasz.getResourceAsStream("/" + resourceName);
485     if (inputStream == null) {
486       System.err.println("Couldn't find resource on the class path: " + resourceName);
487 //      throw new IllegalArgumentException("Resource does not exist: " + resourceName);
488     }
489 
490     else {
491 
492       try {
493         FileOutputStream outputStream = new FileOutputStream(file);
494         try {
495           int nread;
496           byte[] buffer = new byte[4096];
497           while (0 < (nread = inputStream.read(buffer))) {
498             outputStream.write(buffer, 0, nread);
499           }
500         } finally {
501           outputStream.close();
502         }
503       } finally {
504         inputStream.close();
505       }
506     }
507   }
508 
defaultIfStringEmpty(String s, String defaultValue)509   public static String defaultIfStringEmpty(String s, String defaultValue) {
510     return isStringEmpty(s) ? defaultValue : s;
511   }
512 
isStringBlank(String s)513   public static boolean isStringBlank(String s) {
514     return s == null || "".equals(s.trim());
515   }
516 
isStringEmpty(String s)517   public static boolean isStringEmpty(String s) {
518     return s == null || "".equals(s);
519   }
520 
isStringNotBlank(String s)521   public static boolean isStringNotBlank(String s) {
522     return !isStringBlank(s);
523   }
524 
isStringNotEmpty(String s)525   public static boolean isStringNotEmpty(String s) {
526     return !isStringEmpty(s);
527   }
528 
529   /**
530    * @return an array of two strings: the short stack trace and the long stack trace.
531    */
stackTrace(Throwable t, boolean toHtml)532   public static String[] stackTrace(Throwable t, boolean toHtml) {
533     StringWriter sw = new StringWriter();
534     PrintWriter pw = new PrintWriter(sw);
535     t.printStackTrace(pw);
536     pw.flush();
537 
538     String fullStackTrace = sw.getBuffer().toString();
539     String shortStackTrace;
540 
541     if (Boolean.getBoolean(TestNG.SHOW_TESTNG_STACK_FRAMES) || TestRunner.getVerbose() >= 2) {
542       shortStackTrace = fullStackTrace;
543     } else {
544       shortStackTrace = filterTrace(sw.getBuffer().toString());
545     }
546 
547     if (toHtml) {
548       shortStackTrace = escapeHtml(shortStackTrace);
549       fullStackTrace = escapeHtml(fullStackTrace);
550     }
551 
552     return new String[] {
553         shortStackTrace, fullStackTrace
554     };
555   }
556 
557   private static final Map<Character, String> ESCAPES = new HashMap<Character, String>() {
558     private static final long serialVersionUID = 1285607660247157523L;
559 
560   {
561     put('<', "&lt;");
562     put('>', "&gt;");
563     put('\'', "&apos;");
564     put('"', "&quot;");
565     put('&', "&amp;");
566   }};
567 
escapeHtml(String s)568   public static String escapeHtml(String s) {
569     if (s == null) {
570       return null;
571     }
572 
573     StringBuilder result = new StringBuilder();
574 
575     for (int i = 0; i < s.length(); i++) {
576       char c = s.charAt(i);
577       String nc = ESCAPES.get(c);
578       if (nc != null) {
579         result.append(nc);
580       } else {
581         result.append(c);
582       }
583     }
584 
585     return result.toString();
586   }
587 
escapeUnicode(String s)588   public static String escapeUnicode(String s) {
589     if (s == null) {
590       return null;
591     }
592 
593     StringBuilder result = new StringBuilder();
594 
595     for (int i = 0; i < s.length(); i++) {
596       char c = s.charAt(i);
597       char ca = (Character.isDefined(c)) ? c: UNICODE_REPLACEMENT;
598       result.append(ca);
599     }
600 
601     return result.toString();
602   }
603 
filterTrace(String trace)604   private static String filterTrace(String trace) {
605     StringReader   stringReader = new StringReader(trace);
606     BufferedReader bufferedReader = new BufferedReader(stringReader);
607     StringBuffer buf = new StringBuffer();
608 
609     try {
610       // first line contains the thrown exception
611       String line = bufferedReader.readLine();
612       if(line == null) {
613         return "";
614       }
615       buf.append(line).append(LINE_SEP);
616 
617       //
618       // the stack frames of the trace
619       //
620       String[] excludedStrings = new String[] {
621           "org.testng",
622           "reflect",
623           "org.apache.maven.surefire"
624       };
625 
626       int excludedCount = 0;
627       while((line = bufferedReader.readLine()) != null) {
628         boolean isExcluded = false;
629         for (String excluded : excludedStrings) {
630           if(line.contains(excluded)) {
631             isExcluded = true;
632             excludedCount++;
633             break;
634            }
635         }
636         if (! isExcluded) {
637           buf.append(line).append(LINE_SEP);
638         }
639       }
640       if (excludedCount > 0) {
641         buf.append("... Removed " + excludedCount + " stack frames");
642       }
643     }
644     catch(IOException ioex) {
645       ; // do nothing
646     }
647 
648     return buf.toString();
649   }
650 
toString(Object object, Class<?> objectClass)651   public static String toString(Object object, Class<?> objectClass) {
652     if(null == object) {
653       return "null";
654     }
655     final String toString= object.toString();
656     if(isStringEmpty(toString)) {
657       return "\"\"";
658     }
659     else if (String.class.equals(objectClass)) {
660       return "\"" + toString + '\"';
661     }
662     else {
663       return toString;
664     }
665   }
666 
detailedMethodName(ITestNGMethod method, boolean fqn)667   public static String detailedMethodName(ITestNGMethod method, boolean fqn) {
668     StringBuffer buf= new StringBuffer();
669     if(method.isBeforeSuiteConfiguration()) {
670       buf.append("@BeforeSuite ");
671     }
672     else if(method.isBeforeTestConfiguration()) {
673       buf.append("@BeforeTest ");
674     }
675     else if(method.isBeforeClassConfiguration()) {
676       buf.append("@BeforeClass ");
677     }
678     else if(method.isBeforeGroupsConfiguration()) {
679       buf.append("@BeforeGroups ");
680     }
681     else if(method.isBeforeMethodConfiguration()) {
682       buf.append("@BeforeMethod ");
683     }
684     else if(method.isAfterMethodConfiguration()) {
685       buf.append("@AfterMethod ");
686     }
687     else if(method.isAfterGroupsConfiguration()) {
688       buf.append("@AfterGroups ");
689     }
690     else if(method.isAfterClassConfiguration()) {
691       buf.append("@AfterClass ");
692     }
693     else if(method.isAfterTestConfiguration()) {
694       buf.append("@AfterTest ");
695     }
696     else if(method.isAfterSuiteConfiguration()) {
697       buf.append("@AfterSuite ");
698     }
699 
700     return buf.append(fqn ? method.toString() : method.getMethodName()).toString();
701   }
702 
arrayToString(String[] strings)703   public static String arrayToString(String[] strings) {
704     StringBuffer result = new StringBuffer("");
705     if ((strings != null) && (strings.length > 0)) {
706       for (int i = 0; i < strings.length; i++) {
707         result.append(strings[i]);
708         if (i < strings.length - 1) {
709           result.append(", ");
710         }
711       }
712     }
713     return result.toString();
714   }
715 
716   /**
717    * If the file name contains special characters like *,/,\ and so on,
718    * exception will be thrown and report file will not be created.<br>
719    * Special characters are platform specific and they are not same for
720    * example on Windows and Macintosh. * is not allowed on Windows, but it is on Macintosh.<br>
721    * In order to have the same behavior of testng on the all platforms, characters like * will
722    * be replaced on all platforms whether they are causing the problem or not.
723    *
724    * @param fileName file name that could contain special characters.
725    * @return fileName with special characters replaced
726    * @author Borojevic
727    */
replaceSpecialCharacters(String fileName)728   public static String replaceSpecialCharacters(String fileName) {
729    if (fileName == null || fileName.length() == 0) {
730      return fileName;
731    }
732    for (char element : SPECIAL_CHARACTERS) {
733      fileName = fileName.replace(element, CHAR_REPLACEMENT);
734    }
735 
736    return fileName;
737   }
738 
join(List<T> objects, String separator)739   public static <T> String join(List<T> objects, String separator) {
740     StringBuilder result = new StringBuilder();
741     for (int i = 0; i < objects.size(); i++) {
742       if (i > 0) {
743         result.append(separator);
744       }
745       result.append(objects.get(i).toString());
746     }
747     return result.toString();
748   }
749 
copyFile(File from, File to)750   public static void copyFile(File from, File to) {
751     to.getParentFile().mkdirs();
752     try (InputStream in = new FileInputStream(from); OutputStream out = new FileOutputStream(to)) {
753       byte[] buf = new byte[1024];
754       int len;
755       while ((len = in.read(buf)) > 0) {
756         out.write(buf, 0, len);
757       }
758     } catch(IOException e){
759       e.printStackTrace();
760     }
761   }
762 
763   /**
764    * @return a temporary file with the given content.
765    */
createTempFile(String content)766   public static File createTempFile(String content) {
767     try {
768       // Create temp file.
769       File result = File.createTempFile("testng-tmp", "");
770 
771       // Delete temp file when program exits.
772       result.deleteOnExit();
773 
774       // Write to temp file
775       BufferedWriter out = new BufferedWriter(new FileWriter(result));
776       out.write(content);
777       out.close();
778 
779       return result;
780     } catch (IOException e) {
781       throw new TestNGException(e);
782     }
783   }
784 
785   /**
786    * Make sure that either we have an instance or if not, that the method is static
787    */
checkInstanceOrStatic(Object instance, Method method)788   public static void checkInstanceOrStatic(Object instance, Method method) {
789     if (instance == null && method != null && ! Modifier.isStatic(method.getModifiers())) {
790       throw new TestNGException("Can't invoke " + method + ": either make it static or add "
791           + "a no-args constructor to your class");
792     }
793   }
794 
checkReturnType(Method method, Class<?>... returnTypes)795   public static void checkReturnType(Method method, Class<?>... returnTypes) {
796     if (method == null) {
797       return;
798     }
799     for (Class<?> returnType : returnTypes) {
800       if (method.getReturnType() == returnType) {
801         return;
802       }
803     }
804     throw new TestNGException(method.getDeclaringClass().getName() + "."
805               + method.getName() + " MUST return " + toString(returnTypes) + " but returns " + method.getReturnType().getName());
806   }
807 
toString(Class<?>[] classes)808   private static String toString(Class<?>[] classes) {
809     StringBuilder sb = new StringBuilder("[ ");
810     for (int i=0; i<classes.length;) {
811       Class<?> clazz = classes[i];
812       if (clazz.isArray()) {
813         sb.append(clazz.getComponentType().getName()).append("[]");
814       } else {
815         sb.append(clazz.getName());
816       }
817       if (++i < classes.length) { // increment and compare
818         sb.append(" or ");
819       }
820     }
821     sb.append(" ]");
822     return sb.toString();
823   }
824 
825   /**
826    * Returns the string representation of the specified object, transparently
827    * handling null references and arrays.
828    *
829    * @param obj
830    *            the object
831    * @return the string representation
832    */
toString(Object obj)833   public static String toString(Object obj) {
834     String result;
835     if (obj != null) {
836       if (obj instanceof boolean[]) {
837         result = Arrays.toString((boolean[]) obj);
838       } else if (obj instanceof byte[]) {
839         result = Arrays.toString((byte[]) obj);
840       } else if (obj instanceof char[]) {
841         result = Arrays.toString((char[]) obj);
842       } else if (obj instanceof double[]) {
843         result = Arrays.toString((double[]) obj);
844       } else if (obj instanceof float[]) {
845         result = Arrays.toString((float[]) obj);
846       } else if (obj instanceof int[]) {
847         result = Arrays.toString((int[]) obj);
848       } else if (obj instanceof long[]) {
849         result = Arrays.toString((long[]) obj);
850       } else if (obj instanceof Object[]) {
851         result = Arrays.deepToString((Object[]) obj);
852       } else if (obj instanceof short[]) {
853         result = Arrays.toString((short[]) obj);
854       } else {
855         result = obj.toString();
856       }
857     } else {
858       result = "null";
859     }
860     return result;
861   }
862 }
863