001    // Copyright 2007-2009 Waterken Inc. under the terms of the MIT X license
002    // found at http://www.opensource.org/licenses/mit-license.html
003    package org.waterken.eq;
004    
005    import static org.ref_send.promise.Eventual.ref;
006    import static org.ref_send.test.Logic.join;
007    
008    import java.io.Serializable;
009    
010    import org.ref_send.list.List;
011    import org.ref_send.promise.Deferred;
012    import org.ref_send.promise.Do;
013    import org.ref_send.promise.Eventual;
014    import org.ref_send.promise.Promise;
015    import org.ref_send.promise.Receiver;
016    
017    /**
018     * Checks invariants of the ref_send API.
019     */
020    public final class
021    SoundCheck {
022        private SoundCheck() {}
023    
024        /**
025         * Runs a unit test.
026         * @param _ eventual operator
027         */
028        static public Promise<?>
029        make(final Eventual _) throws Exception {
030            final Deferred<?> x = _.defer();
031            return join(_, testNormal(_, x.resolver),
032                           testNull(_, null),
033                           testDouble(_),
034                           testFloat(_));
035        }
036    
037        /**
038         * Tests promises for a normal reference.
039         */
040        static private <T extends Receiver<?>> Promise<?>
041        testNormal(final Eventual _, final T x) throws Exception {
042            final Promise<T> p = ref(x);
043            check(p.equals(p));
044            check(ref(x).equals(p));
045            check(x.equals(p.call()));
046            class EQ extends Do<T,Promise<Boolean>> implements Serializable {
047                static private final long serialVersionUID = 1L;
048    
049                public Promise<Boolean>
050                fulfill(final T arg) throws Exception {
051                    check(x.equals(arg));
052                    return ref(true);
053                }
054            }
055            final Promise<?> a = _.when(p, new EQ());
056            final Promise<?> b = _.when(x, new EQ());
057            final Promise<?> c = _.when(new Sneaky<T>(x), new EQ());
058    
059            final T x_ = _._(x);
060            check(x_.equals(x_));
061            check(_._(x_).equals(x_));
062            check(_._(x).equals(x_));
063            check(ref(x_).equals(p));
064            final Promise<?> d = _.when(x_, new EQ());
065    
066            return join(_, a, b, c, d);
067        }
068    
069        /**
070         * Tests promises for a <code>null</code>.
071         */
072        static private <T extends Receiver<?>> Promise<?>
073        testNull(final Eventual _, final T x) throws Exception {
074            final Promise<T> p = ref(x);
075            check(null == p);
076            class NE extends Do<T,Promise<Boolean>> implements Serializable {
077                static private final long serialVersionUID = 1L;
078    
079                public Promise<Boolean>
080                fulfill(final T arg) throws Exception { throw new Exception(); }
081    
082                public Promise<Boolean>
083                reject(final Exception reason) throws Exception {
084                    if (reason instanceof NullPointerException) {return ref(true);}
085                    throw reason;
086                }
087            }
088            final Promise<?> a = _.when(p, new NE());
089            final Promise<?> b = _.when(x, new NE());
090            final Promise<?> c = _.when(new Sneaky<T>(x), new NE());
091    
092            final T x_ = Eventual.cast(Receiver.class, p);
093            check(null == x_);
094            check(null == ref(x_));
095            final Promise<?> d = _.when(x_, new NE());
096    
097            return join(_, a, b, c, d);
098        }
099    
100        static private <T> Promise<?>
101        testNaN(final Eventual _, final T x) throws Exception {
102            final Promise<T> p = ref(x);
103            check(p.equals(p));
104            check(!p.equals(ref(x)));
105            try {
106                p.call();
107                check(false);
108            } catch (final ArithmeticException e) { /* expected */ }
109            class ENaN extends Do<T,Promise<Boolean>> implements Serializable {
110                static private final long serialVersionUID = 1L;
111    
112                public Promise<Boolean>
113                fulfill(final T arg) throws Exception { throw new Exception(); }
114    
115                public Promise<Boolean>
116                reject(final Exception reason) throws Exception {
117                    if (reason instanceof ArithmeticException) { return ref(true); }
118                    throw reason;
119                }
120            }
121            final Promise<?> a = _.when(p, new ENaN());
122            final Promise<?> b = _.when(x, new ENaN());
123            final Promise<?> c = _.when(new Sneaky<T>(x), new ENaN());
124    
125            return join(_, a, b, c);
126        }
127    
128        /**
129         * Tests promises for {@link Double}.
130         */
131        static private Promise<?>
132        testDouble(final Eventual _) throws Exception {
133            // check normal handling
134            final Promise<Double> pMin = ref(Double.MIN_VALUE);
135            check(pMin.equals(pMin));
136            check(ref(Double.MIN_VALUE).equals(pMin));
137            check(Double.MIN_VALUE == pMin.call());
138            class EQ extends Do<Double,Promise<Boolean>> implements Serializable {
139                static private final long serialVersionUID = 1L;
140    
141                public Promise<Boolean>
142                fulfill(final Double arg) throws Exception {
143                    check(Double.MIN_VALUE == arg);
144                    return ref(true);
145                }
146            }
147            final Promise<?> a = _.when(pMin, new EQ());
148            final Promise<?> b = _.when(Double.MIN_VALUE, new EQ());
149    
150            final Promise<?> c = testNaN(_, Double.NaN);
151            final Promise<?> d = testNaN(_, Double.NEGATIVE_INFINITY);
152            final Promise<?> e = testNaN(_, Double.POSITIVE_INFINITY);
153    
154            return join(_, a, b, c, d, e);
155        }
156    
157        /**
158         * Tests promises for {@link Float}.
159         */
160        static private Promise<?>
161        testFloat(final Eventual _) throws Exception {
162            // check normal handling
163            final Promise<Float> pMin = ref(Float.MIN_VALUE);
164            check(pMin.equals(pMin));
165            check(ref(Float.MIN_VALUE).equals(pMin));
166            check(Float.MIN_VALUE == pMin.call());
167            class EQ extends Do<Float,Promise<Boolean>> implements Serializable {
168                static private final long serialVersionUID = 1L;
169    
170                public Promise<Boolean>
171                fulfill(final Float arg) throws Exception {
172                    check(Float.MIN_VALUE == arg);
173                    return ref(true);
174                }
175            }
176            final Promise<?> a = _.when(pMin, new EQ());
177            final Promise<?> b = _.when(Float.MIN_VALUE, new EQ());
178    
179            final Promise<?> c = testNaN(_, Float.NaN);
180            final Promise<?> d = testNaN(_, Float.NEGATIVE_INFINITY);
181            final Promise<?> e = testNaN(_, Float.POSITIVE_INFINITY);
182    
183            return join(_, a, b, c, d, e);
184        }
185    
186        static protected void
187        check(final boolean valid) throws Exception {
188            if (!valid) { throw new Exception(); }
189        }
190    
191        // Command line interface
192    
193        /**
194         * Executes the test.
195         * @param args  ignored
196         * @throws Exception    test failed
197         */
198        static public void
199        main(final String[] args) throws Exception {
200            final List<Promise<?>> work = List.list();
201            final Promise<?> result = make(new Eventual(work.appender()));
202            while (!work.isEmpty()) { work.pop().call(); }
203            result.call();
204        }
205    }