1package org.testng.internal;
2
3import org.testng.IExpectedExceptionsHolder;
4import org.testng.ITestNGMethod;
5import org.testng.TestException;
6import org.testng.annotations.IExpectedExceptionsAnnotation;
7import org.testng.annotations.ITestAnnotation;
8import org.testng.internal.annotations.IAnnotationFinder;
9
10import java.util.Arrays;
11
12public class ExpectedExceptionsHolder {
13
14  protected final IAnnotationFinder finder;
15  protected final ITestNGMethod method;
16  private final Class<?>[] expectedClasses;
17  private final IExpectedExceptionsHolder holder;
18
19  protected ExpectedExceptionsHolder(IAnnotationFinder finder, ITestNGMethod method, IExpectedExceptionsHolder holder) {
20    this.finder = finder;
21    this.method = method;
22    expectedClasses = findExpectedClasses(finder, method);
23    this.holder = holder;
24  }
25
26  private static Class<?>[] findExpectedClasses(IAnnotationFinder finder, ITestNGMethod method) {
27    IExpectedExceptionsAnnotation expectedExceptions =
28        finder.findAnnotation(method, IExpectedExceptionsAnnotation.class);
29    // Old syntax
30    if (expectedExceptions != null) {
31      return expectedExceptions.getValue();
32    }
33
34    // New syntax
35    ITestAnnotation testAnnotation = finder.findAnnotation(method, ITestAnnotation.class);
36    if (testAnnotation != null) {
37      return testAnnotation.getExpectedExceptions();
38    }
39
40    return new Class<?>[0];
41  }
42
43  /**
44   * @param ite The exception that was just thrown
45   * @return true if the exception that was just thrown is part of the
46   * expected exceptions
47   */
48  public boolean isExpectedException(Throwable ite) {
49    if (!hasExpectedClasses()) {
50      return false;
51    }
52
53    // TestException is the wrapper exception that TestNG will be throwing when an exception was
54    // expected but not thrown
55    if (ite.getClass() == TestException.class) {
56      return false;
57    }
58
59    Class<?> realExceptionClass= ite.getClass();
60
61    for (Class<?> exception : expectedClasses) {
62      if (exception.isAssignableFrom(realExceptionClass) && holder.isThrowableMatching(ite)) {
63        return true;
64      }
65    }
66
67    return false;
68  }
69
70  public Throwable wrongException(Throwable ite) {
71    if (!hasExpectedClasses()) {
72      return ite;
73    }
74
75    if (holder.isThrowableMatching(ite)) {
76      return new TestException("Expected exception of " +
77                               getExpectedExceptionsPluralize()
78                               + " but got " + ite, ite);
79    } else {
80      return new TestException(holder.getWrongExceptionMessage(ite), ite);
81    }
82  }
83
84  public TestException noException(ITestNGMethod testMethod) {
85    if (!hasExpectedClasses()) {
86      return null;
87    }
88    return new TestException("Method " + testMethod + " should have thrown an exception of "
89                             + getExpectedExceptionsPluralize());
90  }
91
92  private boolean hasExpectedClasses() {
93    return expectedClasses != null && expectedClasses.length > 0;
94  }
95
96  private String getExpectedExceptionsPluralize() {
97    StringBuilder sb = new StringBuilder();
98    if (expectedClasses.length > 1) {
99      sb.append("any of types ");
100      sb.append(Arrays.toString(expectedClasses));
101    } else {
102      sb.append("type ");
103      sb.append(expectedClasses[0]);
104    }
105    return sb.toString();
106  }
107}
108