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 }