001    package fj.test;
002    
003    import static fj.Bottom.decons;
004    import fj.F;
005    import fj.data.List;
006    import static fj.data.List.asString;
007    import fj.data.Option;
008    import static fj.data.Option.some;
009    import fj.pre.Show;
010    import static fj.pre.Show.listShow;
011    import static fj.pre.Show.showS;
012    import static fj.test.Arg.argShow;
013    
014    import java.io.StringWriter;
015    import java.io.PrintWriter;
016    
017    /**
018     * An enumeration of the possible results after checking a property. A <code>CheckResult</code> may
019     * be in one of six states:
020     * <ol>
021     * <li>Passed</li>
022     * <li>Proven</li>
023     * <li>Falsified</li>
024     * <li>Exhausted</li>
025     * <li>Exception executing the property</li>
026     * <li>Exception generating values to check the property</li>
027     * </ol>
028     *
029     * @version %build.number%<br>
030     *          <ul>
031     *          <li>$LastChangedRevision: 127 $</li>
032     *          <li>$LastChangedDate: 2009-05-10 01:46:32 +1000 (Sun, 10 May 2009) $</li>
033     *          <li>$LastChangedBy: runarorama $</li>
034     *          </ul>
035     */
036    public final class CheckResult {
037      private final R r;
038      private final Option<List<Arg<?>>> args;
039      private final Option<Throwable> ex;
040      private final int succeeded;
041      private final int discarded;
042    
043      private enum R {
044        Passed, Proven, Falsified, Exhausted, PropException, GenException
045      }
046    
047      private CheckResult(final R r, final Option<List<Arg<?>>> args, final Option<Throwable> ex, final int succeeded,
048                          final int discarded) {
049        this.r = r;
050        this.args = args;
051        this.ex = ex;
052        this.succeeded = succeeded;
053        this.discarded = discarded;
054      }
055    
056      /**
057       * Returns a result that the property has passed.
058       *
059       * @param succeeded The number of checks that succeeded.
060       * @param discarded The number of checks that were discarded.
061       * @return A result that the property has passed.
062       */
063      public static CheckResult passed(final int succeeded, final int discarded) {
064        return new CheckResult(R.Passed, Option.<List<Arg<?>>>none(), Option.<Throwable>none(), succeeded, discarded);
065      }
066    
067      /**
068       * Returns a result that the property has been proven.
069       *
070       * @param args      The arguments used to prove the property.
071       * @param succeeded The number of checks that succeeded.
072       * @param discarded The number of checks that were discarded.
073       * @return A result that the property has been proven.
074       */
075      public static CheckResult proven(final List<Arg<?>> args, final int succeeded, final int discarded) {
076        return new CheckResult(R.Proven, some(args), Option.<Throwable>none(), succeeded, discarded);
077      }
078    
079      /**
080       * Returns a result that the property has been falsified.
081       *
082       * @param args      The arguments used to falsify the property.
083       * @param succeeded The number of checks that succeeded.
084       * @param discarded The number of checks that were discarded.
085       * @return A result that the property has been falsified.
086       */
087      public static CheckResult falsified(final List<Arg<?>> args, final int succeeded, final int discarded) {
088        return new CheckResult(R.Falsified, some(args), Option.<Throwable>none(), succeeded, discarded);
089      }
090    
091      /**
092       * Returns a result that the property been exhausted in checking.
093       *
094       * @param succeeded The number of checks that succeeded.
095       * @param discarded The number of checks that were discarded.
096       * @return A result that the property has been exhausted in checking.
097       */
098      public static CheckResult exhausted(final int succeeded, final int discarded) {
099        return new CheckResult(R.Exhausted, Option.<List<Arg<?>>>none(), Option.<Throwable>none(), succeeded, discarded);
100      }
101    
102      /**
103       * Returns a result that checking the property threw an exception.
104       *
105       * @param args      The arguments used when the exception was thrown.
106       * @param ex        The exception that was thrown.
107       * @param succeeded The number of checks that succeeded.
108       * @param discarded The number of checks that were discarded.
109       * @return A result that checking the property threw an exception.
110       */
111      public static CheckResult propException(final List<Arg<?>> args, final Throwable ex, final int succeeded,
112                                              final int discarded) {
113        return new CheckResult(R.PropException, some(args), some(ex), succeeded, discarded);
114      }
115    
116    
117      /**
118       * Returns a result that generating values to check the property threw an exception.
119       *
120       * @param ex        The exception that was thrown.
121       * @param succeeded The number of checks that succeeded.
122       * @param discarded The number of checks that were discarded.
123       * @return A result that generating values to check the property threw an exception.
124       */
125      public static CheckResult genException(final Throwable ex, final int succeeded, final int discarded) {
126        return new CheckResult(R.GenException, Option.<List<Arg<?>>>none(), some(ex), succeeded, discarded);
127      }
128    
129      /**
130       * Returns <code>true</code> if this result is passed, <code>false</code> otherwise.
131       *
132       * @return <code>true</code> if this result is passed, <code>false</code> otherwise.
133       */
134      public boolean isPassed() {
135        return r == R.Passed;
136      }
137    
138      /**
139       * Returns <code>true</code> if this result is proven, <code>false</code> otherwise.
140       *
141       * @return <code>true</code> if this result is proven, <code>false</code> otherwise.
142       */
143      public boolean isProven() {
144        return r == R.Proven;
145      }
146    
147      /**
148       * Returns <code>true</code> if this result is falsified, <code>false</code> otherwise.
149       *
150       * @return <code>true</code> if this result is falsified, <code>false</code> otherwise.
151       */
152      public boolean isFalsified() {
153        return r == R.Falsified;
154      }
155    
156      /**
157       * Returns <code>true</code> if this result is exhausted, <code>false</code> otherwise.
158       *
159       * @return <code>true</code> if this result is exhausted, <code>false</code> otherwise.
160       */
161      public boolean isExhausted() {
162        return r == R.Exhausted;
163      }
164    
165    
166      /**
167       * Returns <code>true</code> if this result is an exception during property execution,
168       * <code>false</code> otherwise.
169       *
170       * @return <code>true</code> if this result is an exception during property execution,
171       *         <code>false</code> otherwise.
172       */
173      public boolean isPropException() {
174        return r == R.PropException;
175      }
176    
177      /**
178       * Returns <code>true</code> if this result is an exception during generating of values for
179       * property checking, <code>false</code> otherwise.
180       *
181       * @return <code>true</code> if this result is an exception during generating of values for
182       *         property checking, <code>false</code> otherwise.
183       */
184      public boolean isGenException() {
185        return r == R.GenException;
186      }
187    
188      /**
189       * Returns the arguments if the result is one of; proven, falsified or exception during property
190       * execution, otherwise, no arguments are returned.
191       *
192       * @return The arguments if the result is one of; proven, falsified or exception during property
193       *         execution, otherwise, no arguments are returned.
194       */
195      public Option<List<Arg<?>>> args() {
196        return args;
197      }
198    
199      /**
200       * Returns the execption if the result is one of; exception during property execution or exception
201       * during argument value generation, otherwise, no exception are returned.
202       *
203       * @return The execption if the result is one of; exception during property execution or exception
204       *         during argument value generation, otherwise, no exception are returned.
205       */
206      public Option<Throwable> exception() {
207        return ex;
208      }
209    
210      /**
211       * Returns the number of succeeded checks of the property in this result.
212       *
213       * @return The number of succeeded checks of the property in this result.
214       */
215      public int succeeded() {
216        return succeeded;
217      }
218    
219      /**
220       * Returns the number of discarded checks of the property in this result.
221       *
222       * @return The number of discarded checks of the property in this result.
223       */
224      public int discarded() {
225        return discarded;
226      }
227    
228      /**
229       * A rendering of a check result that summarises in one line.
230       *
231       * @param sa The rendering of arguments.
232       * @return A rendering of a check result that summarises in one line.
233       */
234      public static Show<CheckResult> summary(final Show<Arg<?>> sa) {
235        return showS(new F<CheckResult, String>() {
236          private String test(final CheckResult r) {
237            return r.succeeded() == 1 ? "test" : "tests";
238          }
239    
240          private String arguments(final CheckResult r) {
241            final List<Arg<?>> args = r.args().some();
242            return args.length() == 1 ? "argument: " + sa.showS(args.head()) : "arguments: " + listShow(sa).showS(args);
243          }
244    
245          public String f(final CheckResult r) {
246            if (r.isProven())
247              return "OK, property proven with " + arguments(r);
248            else if (r.isPassed())
249              return "OK, passed " + r.succeeded() + ' ' + test(r) +
250                  (r.discarded() > 0 ? " (" + r.discarded() + " discarded)" : "") + '.';
251            else if (r.isFalsified())
252              return "Falsified after " + r.succeeded() + " passed " + test(r) + " with " + arguments(r);
253            else if (r.isExhausted())
254              return "Gave up after " + r.succeeded() + " passed " + test(r) + " and " + r.discarded() +
255                  " discarded tests.";
256            else if (r.isPropException()) {
257              final StringWriter sw = new StringWriter();
258              final PrintWriter pw = new PrintWriter(sw);
259              r.exception().some().printStackTrace(pw);
260              return "Exception on property evaluation with " + arguments(r) + System.getProperty("line.separator") + sw;
261            } else if (r.isGenException()) {
262              final StringWriter sw = new StringWriter();
263              final PrintWriter pw = new PrintWriter(sw);
264              r.exception().some().printStackTrace(pw);
265              return "Exception on argument generation " + System.getProperty("line.separator") + sw;
266            } else
267              throw decons(r.getClass());
268          }
269        });
270      }
271    
272      /**
273       * A rendering of a check result that summarises in one line.
274       */
275      public static final Show<CheckResult> summary = summary(argShow);
276    
277      /**
278       * A rendering of a check result that summarises in one line but throws an exception in the result
279       * is a failure (falsified, property exception or generator exception).
280       */
281      public static final Show<CheckResult> summaryEx = summaryEx(argShow);
282    
283      /**
284       * A rendering of a check result that summarises in one line but throws an exception in the result
285       * is a failure (falsified, property exception or generator exception).
286       *
287       * @param sa The rendering of arguments.
288       * @return A rendering of a check result that summarises in one line but throws an exception in
289       *         the result is a failure (falsified, property exception or generator exception).
290       */
291      public static Show<CheckResult> summaryEx(final Show<Arg<?>> sa) {
292        return showS(new F<CheckResult, String>() {
293          public String f(final CheckResult r) {
294            final String s = summary(sa).show(r).toString();
295            if (r.isProven() || r.isPassed() || r.isExhausted())
296              return s;
297            else if (r.isFalsified() || r.isPropException() || r.isGenException())
298              throw new Error(s);
299            else
300              throw decons(r.getClass());
301          }
302        });
303      }
304    }