Home | History | Annotate | Download | only in testng
      1 package org.testng;
      2 
      3 import java.util.ArrayList;
      4 import java.util.List;
      5 import java.util.Map;
      6 import java.util.Vector;
      7 
      8 import org.testng.collections.Lists;
      9 import org.testng.collections.Maps;
     10 import org.testng.util.Strings;
     11 
     12 /**
     13  * This class is used for test methods to log messages that will be
     14  * included in the HTML reports generated by TestNG.
     15  * <br>
     16  * <br>
     17  * <b>Implementation details.</b>
     18  * <br>
     19  * <br>
     20  * The reporter keeps a combined output of strings (in m_output) and also
     21  * a record of which method output which line.  In order to do this, callers
     22  * specify what the current method is with setCurrentTestResult() and the
     23  * Reporter maintains a mapping of each test result with a list of integers.
     24  * These integers are indices in the combined output (avoids duplicating
     25  * the output).
     26  *
     27  * Created on Nov 2, 2005
     28  * @author cbeust
     29  */
     30 public class Reporter {
     31   // when tests are run in parallel, each thread may be working with different
     32   // 'current test result'. Also, this value should be inherited if the test code
     33   // spawns its own thread.
     34   private static ThreadLocal<ITestResult> m_currentTestResult = new InheritableThreadLocal<>();
     35 
     36   /**
     37    * All output logged in a sequential order.
     38    */
     39   private static List<String> m_output = new Vector<>();
     40 
     41   /** The key is the hashCode of the ITestResult */
     42   private static Map<Integer, List<Integer>> m_methodOutputMap = Maps.newHashMap();
     43 
     44   private static boolean m_escapeHtml = false;
     45   //This variable is responsible for persisting all output that is yet to be associated with any
     46   //valid TestResult objects.
     47   private static ThreadLocal<List<String>> m_orphanedOutput = new InheritableThreadLocal<>();
     48 
     49   public static void setCurrentTestResult(ITestResult m) {
     50     m_currentTestResult.set(m);
     51   }
     52 
     53   public static List<String> getOutput() {
     54     return m_output;
     55   }
     56 
     57   /**
     58    * Erase the content of all the output generated so far.
     59    */
     60   public static void clear() {
     61     m_methodOutputMap.clear();
     62     m_output.clear();
     63   }
     64 
     65   /**
     66    * @param escapeHtml If true, use HTML entities for special HTML characters (<, >, &, ...).
     67    */
     68   public static void setEscapeHtml(boolean escapeHtml) {
     69     m_escapeHtml = escapeHtml;
     70   }
     71 
     72   private static synchronized void log(String s, ITestResult m) {
     73     // Escape for the HTML reports
     74     if (m_escapeHtml) {
     75       s = Strings.escapeHtml(s);
     76     }
     77 
     78     if (m == null) {
     79       //Persist the output temporarily into a Threadlocal String list.
     80       if (m_orphanedOutput.get() == null) {
     81         m_orphanedOutput.set(new ArrayList<String>());
     82       }
     83       m_orphanedOutput.get().add(s);
     84       return;
     85     }
     86 
     87     // synchronization needed to ensure the line number and m_output are updated atomically
     88     int n = getOutput().size();
     89 
     90     List<Integer> lines = m_methodOutputMap.get(m.hashCode());
     91     if (lines == null) {
     92       lines = Lists.newArrayList();
     93       m_methodOutputMap.put(m.hashCode(), lines);
     94     }
     95 
     96     // Check if there was already some orphaned output for the current thread.
     97     if (m_orphanedOutput.get() != null) {
     98       n = n + m_orphanedOutput.get().size();
     99       getOutput().addAll(m_orphanedOutput.get());
    100       // Since we have already added all of the orphaned output to the current
    101       // TestResult, lets clear it off
    102       m_orphanedOutput.remove();
    103     }
    104     lines.add(n);
    105     getOutput().add(s);
    106   }
    107 
    108   /**
    109    * Log the passed string to the HTML reports
    110    * @param s The message to log
    111    */
    112   public static void log(String s) {
    113     log(s, getCurrentTestResult());
    114   }
    115 
    116   /**
    117    * Log the passed string to the HTML reports if the current verbosity
    118    * is equal or greater than the one passed in parameter. If logToStandardOut
    119    * is true, the string will also be printed on standard out.
    120    *
    121    * @param s The message to log
    122    * @param level The verbosity of this message
    123    * @param logToStandardOut Whether to print this string on standard
    124    * out too
    125    */
    126   public static void log(String s, int level, boolean logToStandardOut) {
    127     if (TestRunner.getVerbose() >= level) {
    128       log(s, getCurrentTestResult());
    129       if (logToStandardOut) {
    130         System.out.println(s);
    131       }
    132     }
    133   }
    134 
    135   /**
    136    * Log the passed string to the HTML reports.  If logToStandardOut
    137    * is true, the string will also be printed on standard out.
    138    *
    139    * @param s The message to log
    140    * @param logToStandardOut Whether to print this string on standard
    141    * out too
    142    */
    143   public static void log(String s, boolean logToStandardOut) {
    144     log(s, getCurrentTestResult());
    145     if (logToStandardOut) {
    146       System.out.println(s);
    147     }
    148   }
    149   /**
    150    * Log the passed string to the HTML reports if the current verbosity
    151    * is equal or greater than the one passed in parameter
    152    *
    153    * @param s The message to log
    154    * @param level The verbosity of this message
    155    */
    156   public static void log(String s, int level) {
    157     if (TestRunner.getVerbose() >= level) {
    158       log(s, getCurrentTestResult());
    159     }
    160   }
    161 
    162   /**
    163    * @return the current test result.
    164    */
    165   public static ITestResult getCurrentTestResult() {
    166     return m_currentTestResult.get();
    167   }
    168 
    169   public static synchronized List<String> getOutput(ITestResult tr) {
    170     List<String> result = Lists.newArrayList();
    171     if (tr == null) {
    172       //guard against a possible NPE in scenarios wherein the test result object itself could be a null value.
    173       return result;
    174     }
    175     List<Integer> lines = m_methodOutputMap.get(tr.hashCode());
    176     if (lines != null) {
    177       for (Integer n : lines) {
    178         result.add(getOutput().get(n));
    179       }
    180     }
    181 
    182     return result;
    183   }
    184 }
    185