1package org.testng.internal;
2
3import java.lang.reflect.Method;
4import java.util.Arrays;
5import java.util.Collection;
6import java.util.Collections;
7import java.util.Comparator;
8import java.util.List;
9import java.util.Map;
10import java.util.Set;
11import java.util.concurrent.atomic.AtomicInteger;
12import java.util.regex.Pattern;
13
14import org.testng.IClass;
15import org.testng.IRetryAnalyzer;
16import org.testng.ITestClass;
17import org.testng.ITestNGMethod;
18import org.testng.annotations.ITestOrConfiguration;
19import org.testng.collections.Lists;
20import org.testng.collections.Maps;
21import org.testng.collections.Sets;
22import org.testng.internal.annotations.IAnnotationFinder;
23import org.testng.xml.XmlClass;
24import org.testng.xml.XmlInclude;
25import org.testng.xml.XmlTest;
26
27/**
28 * Superclass to represent both @Test and @Configuration methods.
29 */
30public abstract class BaseTestMethod implements ITestNGMethod {
31  private static final long serialVersionUID = -2666032602580652173L;
32  private static final Pattern SPACE_SEPARATOR_PATTERN = Pattern.compile(" +");
33
34  /**
35   * The test class on which the test method was found. Note that this is not
36   * necessarily the declaring class.
37   */
38  protected ITestClass m_testClass;
39
40  protected final transient Class<?> m_methodClass;
41  protected final transient ConstructorOrMethod m_method;
42  private transient String m_signature;
43  protected String m_id = "";
44  protected long m_date = -1;
45  protected final transient IAnnotationFinder m_annotationFinder;
46  protected String[] m_groups = {};
47  protected String[] m_groupsDependedUpon = {};
48  protected String[] m_methodsDependedUpon = {};
49  protected String[] m_beforeGroups = {};
50  protected String[] m_afterGroups = {};
51  private boolean m_isAlwaysRun;
52  private boolean m_enabled;
53
54  private final String m_methodName;
55  // If a depended group is not found
56  private String m_missingGroup;
57  private String m_description = null;
58  protected AtomicInteger m_currentInvocationCount = new AtomicInteger(0);
59  private int m_parameterInvocationCount = 1;
60  private IRetryAnalyzer m_retryAnalyzer = null;
61  private boolean m_skipFailedInvocations = true;
62  private long m_invocationTimeOut = 0L;
63
64  private List<Integer> m_invocationNumbers = Lists.newArrayList();
65  private final List<Integer> m_failedInvocationNumbers = Collections.synchronizedList(Lists.<Integer>newArrayList());
66  private long m_timeOut = 0;
67
68  private boolean m_ignoreMissingDependencies;
69  private int m_priority;
70
71  private XmlTest m_xmlTest;
72  private Object m_instance;
73
74  /**
75   * Constructs a <code>BaseTestMethod</code> TODO cquezel JavaDoc.
76   *
77   * @param method
78   * @param annotationFinder
79   * @param instance
80   */
81  public BaseTestMethod(String methodName, Method method, IAnnotationFinder annotationFinder, Object instance) {
82    this(methodName, new ConstructorOrMethod(method), annotationFinder, instance);
83  }
84
85  public BaseTestMethod(String methodName, ConstructorOrMethod com, IAnnotationFinder annotationFinder,
86      Object instance) {
87    m_methodClass = com.getDeclaringClass();
88    m_method = com;
89    m_methodName = methodName;
90    m_annotationFinder = annotationFinder;
91    m_instance = instance;
92  }
93
94  /**
95   * {@inheritDoc}
96   */
97  @Override
98  public boolean isAlwaysRun() {
99    return m_isAlwaysRun;
100  }
101
102  /**
103   * TODO cquezel JavaDoc.
104   *
105   * @param alwaysRun
106   */
107  protected void setAlwaysRun(boolean alwaysRun) {
108    m_isAlwaysRun = alwaysRun;
109  }
110
111  /**
112   * {@inheritDoc}
113   */
114  @Override
115  public Class<?> getRealClass() {
116    return m_methodClass;
117  }
118
119  /**
120   * {@inheritDoc}
121   */
122  @Override
123  public ITestClass getTestClass() {
124    return m_testClass;
125  }
126
127  /**
128   * {@inheritDoc}
129   */
130  @Override
131  public void setTestClass(ITestClass tc) {
132    assert null != tc;
133    if (! tc.getRealClass().equals(m_method.getDeclaringClass())) {
134      assert m_method.getDeclaringClass().isAssignableFrom(tc.getRealClass()) :
135        "\nMISMATCH : " + tc.getRealClass() + " " + m_method.getDeclaringClass();
136    }
137    m_testClass = tc;
138  }
139
140  @Override
141  public int compareTo(Object o) {
142    int result = -2;
143    Class<?> thisClass = getRealClass();
144    Class<?> otherClass = ((ITestNGMethod) o).getRealClass();
145    if (this == o) {
146      result = 0;
147    } else if (thisClass.isAssignableFrom(otherClass)) {
148      result = -1;
149    } else if (otherClass.isAssignableFrom(thisClass)) {
150      result = 1;
151    } else if (equals(o)) {
152      result = 0;
153    }
154
155    return result;
156  }
157
158  /**
159   * {@inheritDoc}
160   */
161  @Override
162  public Method getMethod() {
163    return m_method.getMethod();
164  }
165
166  /**
167   * {@inheritDoc}
168   */
169  @Override
170  public String getMethodName() {
171    return m_methodName;
172  }
173
174  /**
175   * {@inheritDoc}
176   */
177  @Override
178  public Object[] getInstances() {
179    return new Object[] { getInstance() };
180  }
181
182  @Override
183  public Object getInstance() {
184    return m_instance;
185  }
186
187  /**
188   * {@inheritDoc}
189   */
190  @Override
191  public long[] getInstanceHashCodes() {
192    return m_testClass.getInstanceHashCodes();
193  }
194
195  /**
196   * {@inheritDoc}
197   * @return the addition of groups defined on the class and on this method.
198   */
199  @Override
200  public String[] getGroups() {
201    return m_groups;
202  }
203
204  /**
205   * {@inheritDoc}
206   */
207  @Override
208  public String[] getGroupsDependedUpon() {
209    return m_groupsDependedUpon;
210  }
211
212  /**
213   * {@inheritDoc}
214   */
215  @Override
216  public String[] getMethodsDependedUpon() {
217    return m_methodsDependedUpon;
218  }
219
220  /**
221   * {@inheritDoc}
222   */
223  @Override
224  public boolean isTest() {
225    return false;
226  }
227
228  /**
229   * {@inheritDoc}
230   */
231  @Override
232  public boolean isBeforeSuiteConfiguration() {
233    return false;
234  }
235
236  /**
237   * {@inheritDoc}
238   */
239  @Override
240  public boolean isAfterSuiteConfiguration() {
241    return false;
242  }
243
244  /**
245   * {@inheritDoc}
246   */
247  @Override
248  public boolean isBeforeTestConfiguration() {
249    return false;
250  }
251
252  /**
253   * {@inheritDoc}
254   */
255  @Override
256  public boolean isAfterTestConfiguration() {
257    return false;
258  }
259
260  /**
261   * {@inheritDoc}
262   */
263  @Override
264  public boolean isBeforeGroupsConfiguration() {
265    return false;
266  }
267
268  /**
269   * {@inheritDoc}
270   */
271  @Override
272  public boolean isAfterGroupsConfiguration() {
273    return false;
274  }
275
276  /**
277   * {@inheritDoc}
278   */
279  @Override
280  public boolean isBeforeClassConfiguration() {
281    return false;
282  }
283
284  /**
285   * {@inheritDoc}
286   */
287  @Override
288  public boolean isAfterClassConfiguration() {
289    return false;
290  }
291
292  /**
293   * {@inheritDoc}
294   */
295  @Override
296  public boolean isBeforeMethodConfiguration() {
297    return false;
298  }
299
300  /**
301   * {@inheritDoc}
302   */
303  @Override
304  public boolean isAfterMethodConfiguration() {
305    return false;
306  }
307
308  /**
309   * {@inheritDoc}
310   */
311  @Override
312  public long getTimeOut() {
313    long result = m_timeOut != 0 ? m_timeOut : (m_xmlTest != null ? m_xmlTest.getTimeOut(0) : 0);
314    return result;
315  }
316
317  @Override
318  public void setTimeOut(long timeOut) {
319    m_timeOut = timeOut;
320  }
321
322  /**
323   * {@inheritDoc}
324   * @return the number of times this method needs to be invoked.
325   */
326  @Override
327  public int getInvocationCount() {
328    return 1;
329  }
330
331  /**
332   * No-op.
333   */
334  @Override
335  public void setInvocationCount(int counter) {
336  }
337
338  /**
339   * {@inheritDoc}
340   * @return the number of times this method or one of its clones must be invoked.
341   */
342  @Override
343  public int getTotalInvocationCount() {
344    return 1;
345  }
346
347  /**
348   * {@inheritDoc} Default value for successPercentage.
349   */
350  @Override
351  public int getSuccessPercentage() {
352    return 100;
353  }
354
355  /**
356   * {@inheritDoc}
357   */
358  @Override
359  public String getId() {
360    return m_id;
361  }
362
363  /**
364   * {@inheritDoc}
365   */
366  @Override
367  public void setId(String id) {
368    m_id = id;
369  }
370
371
372  /**
373   * {@inheritDoc}
374   * @return Returns the date.
375   */
376  @Override
377  public long getDate() {
378    return m_date;
379  }
380
381  /**
382   * {@inheritDoc}
383   * @param date The date to set.
384   */
385  @Override
386  public void setDate(long date) {
387    m_date = date;
388  }
389
390  /**
391   * {@inheritDoc}
392   */
393  @Override
394  public boolean canRunFromClass(IClass testClass) {
395    return m_methodClass.isAssignableFrom(testClass.getRealClass());
396  }
397
398  /**
399   * {@inheritDoc} Compares two BaseTestMethod using the test class then the associated
400   * Java Method.
401   */
402  @Override
403  public boolean equals(Object obj) {
404    if (this == obj) {
405      return true;
406    }
407    if (obj == null) {
408      return false;
409    }
410    if (getClass() != obj.getClass()) {
411      return false;
412    }
413
414    BaseTestMethod other = (BaseTestMethod) obj;
415
416    boolean isEqual = m_testClass == null ? other.m_testClass == null
417        : other.m_testClass != null &&
418          m_testClass.getRealClass().equals(other.m_testClass.getRealClass())
419          && m_instance == other.getInstance();
420
421    return isEqual && getConstructorOrMethod().equals(other.getConstructorOrMethod());
422  }
423
424  /**
425   * {@inheritDoc} This implementation returns the associated Java Method's hash code.
426   * @return the associated Java Method's hash code.
427   */
428  @Override
429  public int hashCode() {
430    return m_method.hashCode();
431  }
432
433  protected void initGroups(Class<? extends ITestOrConfiguration> annotationClass) {
434    //
435    // Init groups
436    //
437    {
438      ITestOrConfiguration annotation = getAnnotationFinder().findAnnotation(getMethod(), annotationClass);
439      ITestOrConfiguration classAnnotation = getAnnotationFinder().findAnnotation(getMethod().getDeclaringClass(), annotationClass);
440
441      setGroups(getStringArray(null != annotation ? annotation.getGroups() : null,
442          null != classAnnotation ? classAnnotation.getGroups() : null));
443    }
444
445    //
446    // Init groups depended upon
447    //
448    {
449      ITestOrConfiguration annotation = getAnnotationFinder().findAnnotation(getMethod(), annotationClass);
450      ITestOrConfiguration classAnnotation = getAnnotationFinder().findAnnotation(getMethod().getDeclaringClass(), annotationClass);
451
452      Map<String, Set<String>> xgd = calculateXmlGroupDependencies(m_xmlTest);
453      List<String> xmlGroupDependencies = Lists.newArrayList();
454      for (String g : getGroups()) {
455        Set<String> gdu = xgd.get(g);
456        if (gdu != null) {
457          xmlGroupDependencies.addAll(gdu);
458        }
459      }
460      setGroupsDependedUpon(
461          getStringArray(null != annotation ? annotation.getDependsOnGroups() : null,
462          null != classAnnotation ? classAnnotation.getDependsOnGroups() : null),
463          xmlGroupDependencies);
464
465      String[] methodsDependedUpon =
466        getStringArray(null != annotation ? annotation.getDependsOnMethods() : null,
467        null != classAnnotation ? classAnnotation.getDependsOnMethods() : null);
468      // Qualify these methods if they don't have a package
469      for (int i = 0; i < methodsDependedUpon.length; i++) {
470        String m = methodsDependedUpon[i];
471        if (!m.contains(".")) {
472          m = MethodHelper.calculateMethodCanonicalName(m_methodClass, methodsDependedUpon[i]);
473          methodsDependedUpon[i] = m != null ? m : methodsDependedUpon[i];
474        }
475      }
476      setMethodsDependedUpon(methodsDependedUpon);
477    }
478  }
479
480
481  private static Map<String, Set<String>> calculateXmlGroupDependencies(XmlTest xmlTest) {
482    Map<String, Set<String>> result = Maps.newHashMap();
483    if (xmlTest == null) {
484      return result;
485    }
486
487    for (Map.Entry<String, String> e : xmlTest.getXmlDependencyGroups().entrySet()) {
488      String name = e.getKey();
489      String dependsOn = e.getValue();
490      Set<String> set = result.get(name);
491      if (set == null) {
492        set = Sets.newHashSet();
493        result.put(name, set);
494      }
495      set.addAll(Arrays.asList(SPACE_SEPARATOR_PATTERN.split(dependsOn)));
496    }
497
498    return result;
499  }
500
501  protected IAnnotationFinder getAnnotationFinder() {
502    return m_annotationFinder;
503  }
504
505  protected IClass getIClass() {
506    return m_testClass;
507  }
508
509  private String computeSignature() {
510    String classLong = m_method.getDeclaringClass().getName();
511    String cls = classLong.substring(classLong.lastIndexOf(".") + 1);
512    StringBuilder result = new StringBuilder(cls).append(".").append(m_method.getName()).append("(");
513    int i = 0;
514    for (Class<?> p : m_method.getParameterTypes()) {
515      if (i++ > 0) {
516        result.append(", ");
517      }
518      result.append(p.getName());
519    }
520    result.append(")");
521    result.append("[pri:").append(getPriority()).append(", instance:").append(m_instance).append("]");
522
523    return result.toString();
524  }
525
526  protected String getSignature() {
527    if (m_signature == null) {
528      m_signature = computeSignature();
529    }
530    return m_signature;
531  }
532
533  /**
534   * {@inheritDoc}
535   */
536  @Override
537  public String toString() {
538    return getSignature();
539  }
540
541  protected String[] getStringArray(String[] methodArray, String[] classArray) {
542    final Set<String> vResult = Sets.newHashSet();
543    if (null != methodArray) {
544      Collections.addAll(vResult, methodArray);
545    }
546    if (null != classArray) {
547      Collections.addAll(vResult, classArray);
548    }
549    return vResult.toArray(new String[vResult.size()]);
550  }
551
552  protected void setGroups(String[] groups) {
553    m_groups = groups;
554  }
555
556  protected void setGroupsDependedUpon(String[] groups, Collection<String> xmlGroupDependencies) {
557    List<String> l = Lists.newArrayList();
558    l.addAll(Arrays.asList(groups));
559    l.addAll(xmlGroupDependencies);
560    m_groupsDependedUpon = l.toArray(new String[l.size()]);
561  }
562
563  protected void setMethodsDependedUpon(String[] methods) {
564    m_methodsDependedUpon = methods;
565  }
566
567  /**
568   * {@inheritDoc}
569   */
570  @Override
571  public void addMethodDependedUpon(String method) {
572    String[] newMethods = new String[m_methodsDependedUpon.length + 1];
573    newMethods[0] = method;
574    System.arraycopy(m_methodsDependedUpon, 0, newMethods, 1, m_methodsDependedUpon.length);
575    m_methodsDependedUpon = newMethods;
576  }
577
578  private static void ppp(String s) {
579    System.out.println("[BaseTestMethod] " + s);
580  }
581
582  /** Compares two ITestNGMethod by date. */
583  public static final Comparator<?> DATE_COMPARATOR = new Comparator<Object>() {
584    @Override
585    public int compare(Object o1, Object o2) {
586      try {
587        ITestNGMethod m1 = (ITestNGMethod) o1;
588        ITestNGMethod m2 = (ITestNGMethod) o2;
589        return (int) (m1.getDate() - m2.getDate());
590      }
591      catch(Exception ex) {
592        return 0; // TODO CQ document this logic
593      }
594    }
595  };
596
597  /**
598   * {@inheritDoc}
599   */
600  @Override
601  public String getMissingGroup() {
602    return m_missingGroup;
603  }
604
605  /**
606   * {@inheritDoc}
607   */
608  @Override
609  public void setMissingGroup(String group) {
610    m_missingGroup = group;
611  }
612
613  /**
614   * {@inheritDoc}
615   */
616  @Override
617  public int getThreadPoolSize() {
618    return 0;
619  }
620
621  /**
622   * No-op.
623   */
624  @Override
625  public void setThreadPoolSize(int threadPoolSize) {
626  }
627
628  @Override
629  public void setDescription(String description) {
630    m_description = description;
631  }
632
633  /**
634   * {@inheritDoc}
635   */
636  @Override
637  public String getDescription() {
638    return m_description;
639  }
640
641  public void setEnabled(boolean enabled) {
642    m_enabled = enabled;
643  }
644
645  @Override
646  public boolean getEnabled() {
647    return m_enabled;
648  }
649
650  /**
651   * {@inheritDoc}
652   */
653  @Override
654  public String[] getBeforeGroups() {
655    return m_beforeGroups;
656  }
657
658  /**
659   * {@inheritDoc}
660   */
661  @Override
662  public String[] getAfterGroups() {
663    return m_afterGroups;
664  }
665
666  @Override
667  public void incrementCurrentInvocationCount() {
668    m_currentInvocationCount.incrementAndGet();
669  }
670
671  @Override
672  public int getCurrentInvocationCount() {
673    return m_currentInvocationCount.get();
674  }
675
676  @Override
677  public void setParameterInvocationCount(int n) {
678    m_parameterInvocationCount = n;
679  }
680
681  @Override
682  public int getParameterInvocationCount() {
683    return m_parameterInvocationCount;
684  }
685
686  @Override
687  public abstract ITestNGMethod clone();
688
689  @Override
690  public IRetryAnalyzer getRetryAnalyzer() {
691    return m_retryAnalyzer;
692  }
693
694  @Override
695  public void setRetryAnalyzer(IRetryAnalyzer retryAnalyzer) {
696    m_retryAnalyzer = retryAnalyzer;
697  }
698
699  @Override
700  public boolean skipFailedInvocations() {
701    return m_skipFailedInvocations;
702  }
703
704  @Override
705  public void setSkipFailedInvocations(boolean s) {
706    m_skipFailedInvocations = s;
707  }
708
709  public void setInvocationTimeOut(long timeOut) {
710    m_invocationTimeOut = timeOut;
711  }
712
713  @Override
714  public long getInvocationTimeOut() {
715    return m_invocationTimeOut;
716  }
717
718  @Override
719  public boolean ignoreMissingDependencies() {
720    return m_ignoreMissingDependencies;
721  }
722
723  @Override
724  public void setIgnoreMissingDependencies(boolean i) {
725    m_ignoreMissingDependencies = i;
726  }
727
728  @Override
729  public List<Integer> getInvocationNumbers() {
730    return m_invocationNumbers;
731  }
732
733  @Override
734  public void setInvocationNumbers(List<Integer> numbers) {
735    m_invocationNumbers = numbers;
736  }
737
738  @Override
739  public List<Integer> getFailedInvocationNumbers() {
740    return m_failedInvocationNumbers;
741  }
742
743  @Override
744  public void addFailedInvocationNumber(int number) {
745    m_failedInvocationNumbers.add(number);
746  }
747
748  @Override
749  public int getPriority() {
750    return m_priority;
751  }
752
753  @Override
754  public void setPriority(int priority) {
755    m_priority = priority;
756  }
757
758  @Override
759  public XmlTest getXmlTest() {
760    return m_xmlTest;
761  }
762
763  public void setXmlTest(XmlTest xmlTest) {
764    m_xmlTest = xmlTest;
765  }
766
767  @Override
768  public ConstructorOrMethod getConstructorOrMethod() {
769    return m_method;
770  }
771
772  @Override
773  public Map<String, String> findMethodParameters(XmlTest test) {
774    // Get the test+suite parameters
775    Map<String, String> result = test.getAllParameters();
776    for (XmlClass xmlClass: test.getXmlClasses()) {
777      if (xmlClass.getName().equals(getTestClass().getName())) {
778        result.putAll(xmlClass.getLocalParameters());
779        for (XmlInclude include : xmlClass.getIncludedMethods()) {
780          if (include.getName().equals(getMethodName())) {
781            result.putAll(include.getLocalParameters());
782            break;
783          }
784        }
785      }
786    }
787
788    return result;
789  }
790}
791