001// ***************************************************************************************************************************
002// * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements.  See the NOTICE file *
003// * distributed with this work for additional information regarding copyright ownership.  The ASF licenses this file        *
004// * to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance            *
005// * with the License.  You may obtain a copy of the License at                                                              *
006// *                                                                                                                         *
007// *  http://www.apache.org/licenses/LICENSE-2.0                                                                             *
008// *                                                                                                                         *
009// * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an  *
010// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the License for the        *
011// * specific language governing permissions and limitations under the License.                                              *
012// ***************************************************************************************************************************
013package org.apache.juneau.assertions;
014
015import java.util.function.*;
016
017import org.apache.juneau.internal.*;
018import org.apache.juneau.json.*;
019import org.apache.juneau.marshall.*;
020import org.apache.juneau.reflect.*;
021import org.apache.juneau.serializer.*;
022
023/**
024 * Used for fluent assertion calls against POJOs.
025 *
026 * @param <R> The return type.
027 */
028@FluentSetters(returns="FluentObjectAssertion<R>")
029public class FluentObjectAssertion<R> extends FluentAssertion<R> {
030
031   private final Object value;
032
033   private static JsonSerializer JSON = JsonSerializer.create()
034      .ssq()
035      .build();
036
037   private static JsonSerializer JSON_SORTED = JsonSerializer.create()
038      .ssq()
039      .sortProperties()
040      .sortCollections()
041      .sortMaps()
042      .build();
043
044   /**
045    * Constructor.
046    *
047    * @param value The object being tested.
048    * @param returns The object to return after the test.
049    */
050   public FluentObjectAssertion(Object value, R returns) {
051      this(null, value, returns);
052   }
053
054   /**
055    * Constructor.
056    *
057    * @param creator The assertion that created this assertion.
058    * @param value The object being tested.
059    * @param returns The object to return after the test.
060    */
061   public FluentObjectAssertion(Assertion creator, Object value, R returns) {
062      super(creator, returns);
063      this.value = value;
064   }
065
066   /**
067    * Asserts that the object is an instance of the specified class.
068    *
069    * <h5 class='section'>Example:</h5>
070    * <p class='bcode w800'>
071    *    <jc>// Validates that the specified object is an instance of MyBean.</jc>
072    *    <jsm>assertObject<jsm>(myPojo).instanceOf(MyBean.<jk>class</jk>);
073    * </p>
074    *
075    * @param parent The value to check against.
076    * @return The response object (for method chaining).
077    * @throws AssertionError If assertion failed.
078    */
079   public R isType(Class<?> parent) throws AssertionError {
080      exists();
081      assertNotNull("parent", parent);
082      if (! ClassInfo.of(value).isChildOf(parent))
083         throw error("Unexpected class.\n\tExpected=[{0}]\n\tActual=[{1}]", className(parent), className(value));
084      return returns();
085   }
086
087   /**
088    * Converts this object to text using the specified serializer and returns it as a new assertion.
089    *
090    * <h5 class='section'>Example:</h5>
091    * <p class='bcode w800'>
092    *    <jc>// Validates that the specified object is an instance of MyBean.</jc>
093    *    <jsm>assertObject<jsm>(myPojo).serialized(XmlSerializer.<jsf>DEFAULT</jsf>).is(<js>"&lt;object>&lt;foo>bar&lt;/foo>&lt;baz>qux&lt;/baz>&lt;/object>"</js>);
094    * </p>
095    *
096    * @param ws The serializer to use to convert the object to text.
097    * @return A new fluent string assertion.
098    */
099   public FluentStringAssertion<R> serialized(WriterSerializer ws) {
100      try {
101         String s = ws.serialize(this.value);
102         return new FluentStringAssertion<>(this, s, returns());
103      } catch (SerializeException e) {
104         throw new RuntimeException(e);
105      }
106   }
107
108   /**
109    * Converts this object to a string using {@link Object#toString} and returns it as a new assertion.
110    *
111    * <h5 class='section'>Example:</h5>
112    * <p class='bcode w800'>
113    *    <jc>// Validates that the specified object is "foobar" after converting to a string.</jc>
114    *    <jsm>assertObject<jsm>(myPojo).string().is(<js>"foobar"</js>);
115    * </p>
116    *
117    * @return A new fluent string assertion.
118    */
119   public FluentStringAssertion<R> string() {
120      return new FluentStringAssertion<>(this, value == null ? null : value.toString(), returns());
121   }
122
123   /**
124    * Converts this object to a string using the specified function and returns it as a new assertion.
125    *
126    * <h5 class='section'>Example:</h5>
127    * <p class='bcode w800'>
128    *    <jc>// Validates that the specified object is "foobar" after converting to a string.</jc>
129    *    <jsm>assertObject<jsm>(myPojo).string(<jv>x</jv>-><jv>x</jv>.toString()).is(<js>"foobar"</js>);
130    * </p>
131    *
132    * @param function The conversion function.
133    * @return A new fluent string assertion.
134    */
135   public FluentStringAssertion<R> string(Function<Object,String> function) {
136      return new FluentStringAssertion<>(this, function.apply(value), returns());
137   }
138
139   /**
140    * Converts this object to a string using the specified function and returns it as a new assertion.
141    *
142    * <h5 class='section'>Example:</h5>
143    * <p class='bcode w800'>
144    *    <jc>// Validates that the specified object is "foobar" after converting to a string.</jc>
145    *    <jsm>assertObject<jsm>(myPojo).string(MyBean.<jk>class</jk>,<jv>x</jv>-><jv>x</jv>.myBeanMethod()).is(<js>"foobar"</js>);
146    * </p>
147    *
148    * @param c The class of the object being converted.
149    * @param function The conversion function.
150    * @param <T> The class of the object being converted.
151    * @return A new fluent string assertion.
152    */
153   @SuppressWarnings("unchecked")
154   public <T> FluentStringAssertion<R> string(Class<T> c, Function<T,String> function) {
155      return new FluentStringAssertion<>(this, function.apply((T)value), returns());
156   }
157
158   /**
159    * Converts this object to simplified JSON and returns it as a new assertion.
160    *
161    * <h5 class='section'>Example:</h5>
162    * <p class='bcode w800'>
163    *    <jc>// Validates that the specified object is an instance of MyBean.</jc>
164    *    <jsm>assertObject<jsm>(myPojo).json().is(<js>"{foo:'bar',baz:'qux'}"</js>);
165    * </p>
166    *
167    * @return A new fluent string assertion.
168    */
169   public FluentStringAssertion<R> json() {
170      return serialized(JSON);
171   }
172
173   /**
174    * Converts this object to sorted simplified JSON and returns it as a new assertion.
175    *
176    * <h5 class='section'>Example:</h5>
177    * <p class='bcode w800'>
178    *    <jc>// Validates that the specified object is an instance of MyBean.</jc>
179    *    <jsm>assertObject<jsm>(myPojo).jsonSorted().is(<js>"{baz:'qux',foo:'bar'}"</js>);
180    * </p>
181    *
182    * @return A new fluent string assertion.
183    */
184   public FluentStringAssertion<R> jsonSorted() {
185      return serialized(JSON_SORTED);
186   }
187
188   /**
189    * Verifies that two objects are equivalent after converting them both to JSON.
190    *
191    * @param o The object to compare against.
192    * @return The response object (for method chaining).
193    * @throws AssertionError If assertion failed.
194    */
195   public R sameAs(Object o) throws AssertionError {
196      return sameAsSerialized(o, JSON);
197   }
198
199   /**
200    * Verifies that two objects are equivalent after converting them both to sorted JSON.
201    *
202    * <p>
203    * Properties, maps, and collections are all sorted on both objects before comparison.
204    *
205    * @param o The object to compare against.
206    * @return The response object (for method chaining).
207    * @throws AssertionError If assertion failed.
208    */
209   public R sameAsSorted(Object o) {
210      return sameAsSerialized(o, JSON_SORTED);
211   }
212
213   /**
214    * Asserts that the specified object is the same as this object after converting both to strings using the specified serializer.
215    *
216    * @param o The object to compare against.
217    * @param serializer The serializer to use to serialize this object.
218    * @return The response object (for method chaining).
219    * @throws AssertionError If assertion failed.
220    */
221   public R sameAsSerialized(Object o, WriterSerializer serializer) {
222      try {
223         String s1 = serializer.serialize(this.value);
224         String s2 = serializer.serialize(o);
225         if (! StringUtils.isEquals(s1, s2))
226            throw error("Unexpected comparison.\n\tExpected=[{0}]\n\tActual=[{1}]", s2, s1);
227      } catch (SerializeException e) {
228         throw new RuntimeException(e);
229      }
230      return returns();
231   }
232
233   /**
234    * Asserts that the value equals the specified value.
235    *
236    * @param value The value to check against.
237    * @return The response object (for method chaining).
238    * @throws AssertionError If assertion failed.
239    */
240   public R isEqual(Object value) throws AssertionError {
241      if (this.value == value)
242         return returns();
243      exists();
244      if (! this.value.equals(equivalent(value)))
245         throw error("Unexpected value.\n\tExpected=[{0}]\n\tActual=[{1}]", value, this.value);
246      return returns();
247   }
248
249   /**
250    * Asserts that the value equals the specified value.
251    *
252    * <p>
253    * Equivalent to {@link #isEqual(Object)}.
254    *
255    * @param value The value to check against.
256    * @return The response object (for method chaining).
257    * @throws AssertionError If assertion failed.
258    */
259   public R is(Object value) throws AssertionError {
260      return isEqual(equivalent(value));
261   }
262
263   /**
264    * Asserts that the value equals the specified value.
265    *
266    * @param value The value to check against.
267    * @return The response object (for method chaining).
268    * @throws AssertionError If assertion failed.
269    */
270   public R doesNotEqual(Object value) throws AssertionError {
271      if (this.value == null && value != null || this.value != null && value == null)
272         return returns();
273      if (this.value == null || this.value.equals(equivalent(value)))
274         throw error("Unexpected value.\n\tExpected not=[{0}]\n\tActual=[{1}]", value, this.value);
275      return returns();
276   }
277
278   /**
279    * Asserts that the value passes the specified predicate test.
280    *
281    * @param test The predicate to use to test the value.
282    * @return The response object (for method chaining).
283    * @throws AssertionError If assertion failed.
284    */
285   public R passes(Predicate<Object> test) throws AssertionError {
286      if (! test.test(value))
287         throw error("Value did not pass predicate test.\n\tValue=[{0}]", value);
288      return returns();
289   }
290
291   /**
292    * Asserts that the value passes the specified predicate test.
293    *
294    * @param c The class type of the object being tested.
295    * @param <T> The class type of the object being tested.
296    * @param test The predicate to use to test the value.
297    * @return The response object (for method chaining).
298    * @throws AssertionError If assertion failed.
299    */
300   @SuppressWarnings("unchecked")
301   public <T> R passes(Class<T> c, Predicate<T> test) throws AssertionError {
302      isType(c);
303      if (! test.test((T)value))
304         throw error("Value did not pass predicate test.\n\tValue=[{0}]", value);
305      return returns();
306   }
307
308   /**
309    * Asserts that the object is not null.
310    *
311    * <p>
312    * Equivalent to {@link #isNotNull()}.
313    *
314    * @return The response object (for method chaining).
315    * @throws AssertionError If assertion failed.
316    */
317   public R exists() throws AssertionError {
318      return isNotNull();
319   }
320
321   /**
322    * Asserts that the object is null.
323    *
324    * <p>
325    * Equivalent to {@link #isNotNull()}.
326    *
327    * @return The response object (for method chaining).
328    * @throws AssertionError If assertion failed.
329    */
330   public R doesNotExist() throws AssertionError {
331      return isNull();
332   }
333
334   /**
335    * Asserts that the object is not null.
336    *
337    * <p>
338    * Equivalent to {@link #isNotNull()}.
339    *
340    * @return The response object (for method chaining).
341    * @throws AssertionError If assertion failed.
342    */
343   public R isNotNull() throws AssertionError {
344      if (value == null)
345         throw error("Value was null.");
346      return returns();
347   }
348
349   /**
350    * Asserts that the object i null.
351    *
352    * <p>
353    * Equivalent to {@link #isNotNull()}.
354    *
355    * @return The response object (for method chaining).
356    * @throws AssertionError If assertion failed.
357    */
358   public R isNull() throws AssertionError {
359      if (value != null)
360         throw error("Value was not null.");
361      return returns();
362   }
363
364   /**
365    * Asserts that the value equals the specified value.
366    *
367    * <p>
368    * Equivalent to {@link #doesNotEqual(Object)}.
369    *
370    * @param value The value to check against.
371    * @return The response object (for method chaining).
372    * @throws AssertionError If assertion failed.
373    */
374   public R isNot(Object value) throws AssertionError {
375      return doesNotEqual(equivalent(value));
376   }
377
378   /**
379    * Asserts that the value is one of the specified values.
380    *
381    * @param values The values to check against.
382    * @return The response object (for method chaining).
383    * @throws AssertionError If assertion failed.
384    */
385   public R isAny(Object...values) throws AssertionError {
386      exists();
387      for (Object v : values)
388         if (this.value.equals(equivalent(v)))
389            return returns();
390      throw error("Expected value not found.\n\tExpected=[{0}]\n\tActual=[{1}]", SimpleJson.DEFAULT.toString(values), value);
391   }
392
393   /**
394    * Asserts that the value is one of the specified values.
395    *
396    * @param values The values to check against.
397    * @return The response object (for method chaining).
398    * @throws AssertionError If assertion failed.
399    */
400   public R isNotAny(Object...values) throws AssertionError {
401      exists();
402      for (Object v : values)
403         if (this.value.equals(equivalent(v)))
404            throw error("Unexpected value found.\n\tUnexpected=[{0}]\n\tActual=[{1}]", v, value);
405      return returns();
406   }
407
408   /**
409    * Subclasses can override this method to provide special conversions on objects being compared.
410    *
411    * @param o The object to cast.
412    * @return The cast object.
413    */
414   protected Object equivalent(Object o) {
415      return o;
416   }
417
418   // <FluentSetters>
419
420   @Override /* GENERATED - Assertion */
421   public FluentObjectAssertion<R> msg(String msg, Object...args) {
422      super.msg(msg, args);
423      return this;
424   }
425
426   @Override /* GENERATED - Assertion */
427   public FluentObjectAssertion<R> stderr() {
428      super.stderr();
429      return this;
430   }
431
432   @Override /* GENERATED - Assertion */
433   public FluentObjectAssertion<R> stdout() {
434      super.stdout();
435      return this;
436   }
437
438   // </FluentSetters>
439}