• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2006 The Guava Authors
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.google.common.base;
18 
19 import static com.google.common.truth.Truth.assertThat;
20 import static org.junit.Assert.assertThrows;
21 
22 import com.google.common.annotations.GwtCompatible;
23 import com.google.common.annotations.GwtIncompatible;
24 import com.google.common.annotations.J2ktIncompatible;
25 import com.google.common.collect.ImmutableList;
26 import com.google.common.collect.ImmutableSet;
27 import com.google.common.collect.Lists;
28 import com.google.common.testing.ArbitraryInstances;
29 import java.lang.reflect.InvocationTargetException;
30 import java.lang.reflect.Method;
31 import java.util.ArrayList;
32 import java.util.Arrays;
33 import java.util.List;
34 import junit.framework.AssertionFailedError;
35 import junit.framework.TestCase;
36 import org.checkerframework.checker.nullness.qual.Nullable;
37 
38 /**
39  * Unit test for {@link Preconditions}.
40  *
41  * @author Kevin Bourrillion
42  * @author Jared Levy
43  */
44 @ElementTypesAreNonnullByDefault
45 @J2ktIncompatible // TODO(b/278877942): Enable
46 @SuppressWarnings("LenientFormatStringValidation") // Intentional for testing
47 @GwtCompatible(emulated = true)
48 public class PreconditionsTest extends TestCase {
testCheckArgument_simple_success()49   public void testCheckArgument_simple_success() {
50     Preconditions.checkArgument(true);
51   }
52 
testCheckArgument_simple_failure()53   public void testCheckArgument_simple_failure() {
54     try {
55       Preconditions.checkArgument(false);
56       fail("no exception thrown");
57     } catch (IllegalArgumentException expected) {
58     }
59   }
60 
testCheckArgument_simpleMessage_success()61   public void testCheckArgument_simpleMessage_success() {
62     Preconditions.checkArgument(true, IGNORE_ME);
63   }
64 
testCheckArgument_simpleMessage_failure()65   public void testCheckArgument_simpleMessage_failure() {
66     try {
67       Preconditions.checkArgument(false, new Message());
68       fail("no exception thrown");
69     } catch (IllegalArgumentException expected) {
70       verifySimpleMessage(expected);
71     }
72   }
73 
testCheckArgument_nullMessage_failure()74   public void testCheckArgument_nullMessage_failure() {
75     try {
76       Preconditions.checkArgument(false, null);
77       fail("no exception thrown");
78     } catch (IllegalArgumentException expected) {
79       assertThat(expected).hasMessageThat().isEqualTo("null");
80     }
81   }
82 
testCheckArgument_nullMessageWithArgs_failure()83   public void testCheckArgument_nullMessageWithArgs_failure() {
84     try {
85       Preconditions.checkArgument(false, null, "b", "d");
86       fail("no exception thrown");
87     } catch (IllegalArgumentException e) {
88       assertThat(e).hasMessageThat().isEqualTo("null [b, d]");
89     }
90   }
91 
testCheckArgument_nullArgs_failure()92   public void testCheckArgument_nullArgs_failure() {
93     try {
94       Preconditions.checkArgument(false, "A %s C %s E", null, null);
95       fail("no exception thrown");
96     } catch (IllegalArgumentException e) {
97       assertThat(e).hasMessageThat().isEqualTo("A null C null E");
98     }
99   }
100 
testCheckArgument_notEnoughArgs_failure()101   public void testCheckArgument_notEnoughArgs_failure() {
102     try {
103       Preconditions.checkArgument(false, "A %s C %s E", "b");
104       fail("no exception thrown");
105     } catch (IllegalArgumentException e) {
106       assertThat(e).hasMessageThat().isEqualTo("A b C %s E");
107     }
108   }
109 
testCheckArgument_tooManyArgs_failure()110   public void testCheckArgument_tooManyArgs_failure() {
111     try {
112       Preconditions.checkArgument(false, "A %s C %s E", "b", "d", "f");
113       fail("no exception thrown");
114     } catch (IllegalArgumentException e) {
115       assertThat(e).hasMessageThat().isEqualTo("A b C d E [f]");
116     }
117   }
118 
testCheckArgument_singleNullArg_failure()119   public void testCheckArgument_singleNullArg_failure() {
120     try {
121       Preconditions.checkArgument(false, "A %s C", (Object) null);
122       fail("no exception thrown");
123     } catch (IllegalArgumentException e) {
124       assertThat(e).hasMessageThat().isEqualTo("A null C");
125     }
126   }
127 
testCheckArgument_singleNullArray_failure()128   public void testCheckArgument_singleNullArray_failure() {
129     try {
130       Preconditions.checkArgument(false, "A %s C", (Object[]) null);
131       fail("no exception thrown");
132     } catch (IllegalArgumentException e) {
133       assertThat(e).hasMessageThat().isEqualTo("A (Object[])null C");
134     }
135   }
136 
testCheckArgument_complexMessage_success()137   public void testCheckArgument_complexMessage_success() {
138     Preconditions.checkArgument(true, "%s", IGNORE_ME);
139   }
140 
testCheckArgument_complexMessage_failure()141   public void testCheckArgument_complexMessage_failure() {
142     try {
143       Preconditions.checkArgument(false, FORMAT, 5);
144       fail("no exception thrown");
145     } catch (IllegalArgumentException expected) {
146       verifyComplexMessage(expected);
147     }
148   }
149 
testCheckState_simple_success()150   public void testCheckState_simple_success() {
151     Preconditions.checkState(true);
152   }
153 
testCheckState_simple_failure()154   public void testCheckState_simple_failure() {
155     try {
156       Preconditions.checkState(false);
157       fail("no exception thrown");
158     } catch (IllegalStateException expected) {
159     }
160   }
161 
testCheckState_simpleMessage_success()162   public void testCheckState_simpleMessage_success() {
163     Preconditions.checkState(true, IGNORE_ME);
164   }
165 
testCheckState_simpleMessage_failure()166   public void testCheckState_simpleMessage_failure() {
167     try {
168       Preconditions.checkState(false, new Message());
169       fail("no exception thrown");
170     } catch (IllegalStateException expected) {
171       verifySimpleMessage(expected);
172     }
173   }
174 
testCheckState_nullMessage_failure()175   public void testCheckState_nullMessage_failure() {
176     try {
177       Preconditions.checkState(false, null);
178       fail("no exception thrown");
179     } catch (IllegalStateException expected) {
180       assertThat(expected).hasMessageThat().isEqualTo("null");
181     }
182   }
183 
testCheckState_complexMessage_success()184   public void testCheckState_complexMessage_success() {
185     Preconditions.checkState(true, "%s", IGNORE_ME);
186   }
187 
testCheckState_complexMessage_failure()188   public void testCheckState_complexMessage_failure() {
189     try {
190       Preconditions.checkState(false, FORMAT, 5);
191       fail("no exception thrown");
192     } catch (IllegalStateException expected) {
193       verifyComplexMessage(expected);
194     }
195   }
196 
197   private static final String NON_NULL_STRING = "foo";
198 
testCheckNotNull_simple_success()199   public void testCheckNotNull_simple_success() {
200     String result = Preconditions.checkNotNull(NON_NULL_STRING);
201     assertSame(NON_NULL_STRING, result);
202   }
203 
testCheckNotNull_simple_failure()204   public void testCheckNotNull_simple_failure() {
205     try {
206       Preconditions.checkNotNull(null);
207       fail("no exception thrown");
208     } catch (NullPointerException expected) {
209     }
210   }
211 
testCheckNotNull_simpleMessage_success()212   public void testCheckNotNull_simpleMessage_success() {
213     String result = Preconditions.checkNotNull(NON_NULL_STRING, IGNORE_ME);
214     assertSame(NON_NULL_STRING, result);
215   }
216 
testCheckNotNull_simpleMessage_failure()217   public void testCheckNotNull_simpleMessage_failure() {
218     try {
219       Preconditions.checkNotNull(null, new Message());
220       fail("no exception thrown");
221     } catch (NullPointerException expected) {
222       verifySimpleMessage(expected);
223     }
224   }
225 
testCheckNotNull_complexMessage_success()226   public void testCheckNotNull_complexMessage_success() {
227     String result = Preconditions.checkNotNull(NON_NULL_STRING, "%s", IGNORE_ME);
228     assertSame(NON_NULL_STRING, result);
229   }
230 
testCheckNotNull_complexMessage_failure()231   public void testCheckNotNull_complexMessage_failure() {
232     try {
233       Preconditions.checkNotNull(null, FORMAT, 5);
234       fail("no exception thrown");
235     } catch (NullPointerException expected) {
236       verifyComplexMessage(expected);
237     }
238   }
239 
testCheckElementIndex_ok()240   public void testCheckElementIndex_ok() {
241     assertEquals(0, Preconditions.checkElementIndex(0, 1));
242     assertEquals(0, Preconditions.checkElementIndex(0, 2));
243     assertEquals(1, Preconditions.checkElementIndex(1, 2));
244   }
245 
testCheckElementIndex_badSize()246   public void testCheckElementIndex_badSize() {
247     try {
248       Preconditions.checkElementIndex(1, -1);
249       fail();
250     } catch (IllegalArgumentException expected) {
251       // don't care what the message text is, as this is an invalid usage of
252       // the Preconditions class, unlike all the other exceptions it throws
253     }
254   }
255 
testCheckElementIndex_negative()256   public void testCheckElementIndex_negative() {
257     try {
258       Preconditions.checkElementIndex(-1, 1);
259       fail();
260     } catch (IndexOutOfBoundsException expected) {
261       assertThat(expected).hasMessageThat().isEqualTo("index (-1) must not be negative");
262     }
263   }
264 
testCheckElementIndex_tooHigh()265   public void testCheckElementIndex_tooHigh() {
266     try {
267       Preconditions.checkElementIndex(1, 1);
268       fail();
269     } catch (IndexOutOfBoundsException expected) {
270       assertThat(expected).hasMessageThat().isEqualTo("index (1) must be less than size (1)");
271     }
272   }
273 
testCheckElementIndex_withDesc_negative()274   public void testCheckElementIndex_withDesc_negative() {
275     try {
276       Preconditions.checkElementIndex(-1, 1, "foo");
277       fail();
278     } catch (IndexOutOfBoundsException expected) {
279       assertThat(expected).hasMessageThat().isEqualTo("foo (-1) must not be negative");
280     }
281   }
282 
testCheckElementIndex_withDesc_tooHigh()283   public void testCheckElementIndex_withDesc_tooHigh() {
284     try {
285       Preconditions.checkElementIndex(1, 1, "foo");
286       fail();
287     } catch (IndexOutOfBoundsException expected) {
288       assertThat(expected).hasMessageThat().isEqualTo("foo (1) must be less than size (1)");
289     }
290   }
291 
testCheckPositionIndex_ok()292   public void testCheckPositionIndex_ok() {
293     assertEquals(0, Preconditions.checkPositionIndex(0, 0));
294     assertEquals(0, Preconditions.checkPositionIndex(0, 1));
295     assertEquals(1, Preconditions.checkPositionIndex(1, 1));
296   }
297 
testCheckPositionIndex_badSize()298   public void testCheckPositionIndex_badSize() {
299     try {
300       Preconditions.checkPositionIndex(1, -1);
301       fail();
302     } catch (IllegalArgumentException expected) {
303       // don't care what the message text is, as this is an invalid usage of
304       // the Preconditions class, unlike all the other exceptions it throws
305     }
306   }
307 
testCheckPositionIndex_negative()308   public void testCheckPositionIndex_negative() {
309     try {
310       Preconditions.checkPositionIndex(-1, 1);
311       fail();
312     } catch (IndexOutOfBoundsException expected) {
313       assertThat(expected).hasMessageThat().isEqualTo("index (-1) must not be negative");
314     }
315   }
316 
testCheckPositionIndex_tooHigh()317   public void testCheckPositionIndex_tooHigh() {
318     try {
319       Preconditions.checkPositionIndex(2, 1);
320       fail();
321     } catch (IndexOutOfBoundsException expected) {
322       assertThat(expected)
323           .hasMessageThat()
324           .isEqualTo("index (2) must not be greater than size (1)");
325     }
326   }
327 
testCheckPositionIndex_withDesc_negative()328   public void testCheckPositionIndex_withDesc_negative() {
329     try {
330       Preconditions.checkPositionIndex(-1, 1, "foo");
331       fail();
332     } catch (IndexOutOfBoundsException expected) {
333       assertThat(expected).hasMessageThat().isEqualTo("foo (-1) must not be negative");
334     }
335   }
336 
testCheckPositionIndex_withDesc_tooHigh()337   public void testCheckPositionIndex_withDesc_tooHigh() {
338     try {
339       Preconditions.checkPositionIndex(2, 1, "foo");
340       fail();
341     } catch (IndexOutOfBoundsException expected) {
342       assertThat(expected).hasMessageThat().isEqualTo("foo (2) must not be greater than size (1)");
343     }
344   }
345 
testCheckPositionIndexes_ok()346   public void testCheckPositionIndexes_ok() {
347     Preconditions.checkPositionIndexes(0, 0, 0);
348     Preconditions.checkPositionIndexes(0, 0, 1);
349     Preconditions.checkPositionIndexes(0, 1, 1);
350     Preconditions.checkPositionIndexes(1, 1, 1);
351   }
352 
testCheckPositionIndexes_badSize()353   public void testCheckPositionIndexes_badSize() {
354     try {
355       Preconditions.checkPositionIndexes(1, 1, -1);
356       fail();
357     } catch (IllegalArgumentException expected) {
358     }
359   }
360 
testCheckPositionIndex_startNegative()361   public void testCheckPositionIndex_startNegative() {
362     try {
363       Preconditions.checkPositionIndexes(-1, 1, 1);
364       fail();
365     } catch (IndexOutOfBoundsException expected) {
366       assertThat(expected).hasMessageThat().isEqualTo("start index (-1) must not be negative");
367     }
368   }
369 
testCheckPositionIndexes_endTooHigh()370   public void testCheckPositionIndexes_endTooHigh() {
371     try {
372       Preconditions.checkPositionIndexes(0, 2, 1);
373       fail();
374     } catch (IndexOutOfBoundsException expected) {
375       assertThat(expected)
376           .hasMessageThat()
377           .isEqualTo("end index (2) must not be greater than size (1)");
378     }
379   }
380 
testCheckPositionIndexes_reversed()381   public void testCheckPositionIndexes_reversed() {
382     try {
383       Preconditions.checkPositionIndexes(1, 0, 1);
384       fail();
385     } catch (IndexOutOfBoundsException expected) {
386       assertThat(expected)
387           .hasMessageThat()
388           .isEqualTo("end index (0) must not be less than start index (1)");
389     }
390   }
391 
392   @GwtIncompatible("Reflection")
393   @J2ktIncompatible
testAllOverloads_checkArgument()394   public void testAllOverloads_checkArgument() throws Exception {
395     for (ImmutableList<Class<?>> sig : allSignatures(boolean.class)) {
396       Method checkArgumentMethod =
397           Preconditions.class.getMethod("checkArgument", sig.toArray(new Class<?>[] {}));
398       checkArgumentMethod.invoke(null /* static method */, getParametersForSignature(true, sig));
399 
400       Object[] failingParams = getParametersForSignature(false, sig);
401       InvocationTargetException ite =
402           assertThrows(
403               InvocationTargetException.class,
404               () -> checkArgumentMethod.invoke(null /* static method */, failingParams));
405       assertFailureCause(ite.getCause(), IllegalArgumentException.class, failingParams);
406     }
407   }
408 
409   @GwtIncompatible("Reflection")
410   @J2ktIncompatible
testAllOverloads_checkState()411   public void testAllOverloads_checkState() throws Exception {
412     for (ImmutableList<Class<?>> sig : allSignatures(boolean.class)) {
413       Method checkArgumentMethod =
414           Preconditions.class.getMethod("checkState", sig.toArray(new Class<?>[] {}));
415       checkArgumentMethod.invoke(null /* static method */, getParametersForSignature(true, sig));
416 
417       Object[] failingParams = getParametersForSignature(false, sig);
418       InvocationTargetException ite =
419           assertThrows(
420               InvocationTargetException.class,
421               () -> checkArgumentMethod.invoke(null /* static method */, failingParams));
422       assertFailureCause(ite.getCause(), IllegalStateException.class, failingParams);
423     }
424   }
425 
426   @GwtIncompatible("Reflection")
427   @J2ktIncompatible
testAllOverloads_checkNotNull()428   public void testAllOverloads_checkNotNull() throws Exception {
429     for (ImmutableList<Class<?>> sig : allSignatures(Object.class)) {
430       Method checkArgumentMethod =
431           Preconditions.class.getMethod("checkNotNull", sig.toArray(new Class<?>[] {}));
432       checkArgumentMethod.invoke(
433           null /* static method */, getParametersForSignature(new Object(), sig));
434 
435       Object[] failingParams = getParametersForSignature(null, sig);
436       InvocationTargetException ite =
437           assertThrows(
438               InvocationTargetException.class,
439               () -> checkArgumentMethod.invoke(null /* static method */, failingParams));
440       assertFailureCause(ite.getCause(), NullPointerException.class, failingParams);
441     }
442   }
443 
444   /**
445    * Asserts that the given throwable has the given class and then asserts on the message as using
446    * the full set of method parameters.
447    */
assertFailureCause( Throwable throwable, Class<? extends Throwable> clazz, Object[] params)448   private void assertFailureCause(
449       Throwable throwable, Class<? extends Throwable> clazz, Object[] params) {
450     assertThat(throwable).isInstanceOf(clazz);
451     if (params.length == 1) {
452       assertThat(throwable).hasMessageThat().isNull();
453     } else if (params.length == 2) {
454       assertThat(throwable).hasMessageThat().isEmpty();
455     } else {
456       assertThat(throwable)
457           .hasMessageThat()
458           .isEqualTo(Strings.lenientFormat("", Arrays.copyOfRange(params, 2, params.length)));
459     }
460   }
461 
462   /**
463    * Returns an array containing parameters for invoking a checkArgument, checkNotNull or checkState
464    * method reflectively
465    *
466    * @param firstParam The first parameter
467    * @param sig The method signature
468    */
469   @GwtIncompatible("ArbitraryInstances")
470   @J2ktIncompatible
getParametersForSignature( @ullable Object firstParam, ImmutableList<Class<?>> sig)471   private Object[] getParametersForSignature(
472       @Nullable Object firstParam, ImmutableList<Class<?>> sig) {
473     Object[] params = new Object[sig.size()];
474     params[0] = firstParam;
475     if (params.length > 1) {
476       params[1] = "";
477       if (params.length > 2) {
478         // fill in the rest of the array with arbitrary instances
479         for (int i = 2; i < params.length; i++) {
480           params[i] = ArbitraryInstances.get(sig.get(i));
481         }
482       }
483     }
484     return params;
485   }
486 
487   private static final ImmutableList<Class<?>> possibleParamTypes =
488       ImmutableList.of(char.class, int.class, long.class, Object.class);
489 
490   /**
491    * Returns a list of parameters for invoking an overload of checkState, checkArgument or
492    * checkNotNull
493    *
494    * @param predicateType The first parameter to the method (boolean or Object)
495    */
allSignatures(Class<?> predicateType)496   private static ImmutableList<ImmutableList<Class<?>>> allSignatures(Class<?> predicateType) {
497     ImmutableSet.Builder<ImmutableList<Class<?>>> allOverloads = ImmutableSet.builder();
498     // The first two are for the overloads that don't take formatting args, e.g.
499     // checkArgument(boolean) and checkArgument(boolean, Object)
500     allOverloads.add(ImmutableList.<Class<?>>of(predicateType));
501     allOverloads.add(ImmutableList.<Class<?>>of(predicateType, Object.class));
502 
503     List<List<Class<?>>> typesLists = new ArrayList<>();
504     for (int i = 0; i < 2; i++) {
505       typesLists.add(possibleParamTypes);
506       for (List<Class<?>> curr : Lists.cartesianProduct(typesLists)) {
507         allOverloads.add(
508             ImmutableList.<Class<?>>builder()
509                 .add(predicateType)
510                 .add(String.class) // the format string
511                 .addAll(curr)
512                 .build());
513       }
514     }
515     return allOverloads.build().asList();
516   }
517 
518   // 'test' to demonstrate some potentially ambiguous overloads.  This 'test' is kind of strange,
519   // but essentially each line will be a call to a Preconditions method that, but for a documented
520   // change would be a compiler error.
521   // See http://docs.oracle.com/javase/specs/jls/se7/html/jls-15.html#jls-15.12.2 for the spec on
522   // how javac selects overloads
523   @SuppressWarnings("null")
overloadSelection()524   public void overloadSelection() {
525     Boolean boxedBoolean = null;
526     boolean aBoolean = true;
527     Long boxedLong = null;
528     int anInt = 1;
529     // With a boxed predicate, no overloads can be selected in phase 1
530     // ambiguous without the call to .booleanValue to unbox the Boolean
531     Preconditions.checkState(boxedBoolean.booleanValue(), "", 1);
532     // ambiguous without the cast to Object because the boxed predicate prevents any overload from
533     // being selected in phase 1
534     Preconditions.checkState(boxedBoolean, "", (Object) boxedLong);
535 
536     // ternaries introduce their own problems. because of the ternary (which requires a boxing
537     // operation) no overload can be selected in phase 1.  and in phase 2 it is ambiguous since it
538     // matches with the second parameter being boxed and without it being boxed.  The cast to Object
539     // avoids this.
540     Preconditions.checkState(aBoolean, "", aBoolean ? "" : anInt, (Object) anInt);
541 
542     // ambiguous without the .booleanValue() call since the boxing forces us into phase 2 resolution
543     short s = 2;
544     Preconditions.checkState(boxedBoolean.booleanValue(), "", s);
545   }
546 
547   @J2ktIncompatible
548   @GwtIncompatible // NullPointerTester
testNullPointers()549   public void testNullPointers() {
550     /*
551      * Don't bother testing: Preconditions defines a bunch of methods that accept a template (or
552      * even entire message) that simultaneously:
553      *
554      * - _shouldn't_ be null, so we don't annotate it with @Nullable
555      *
556      * - _can_ be null without causing a runtime failure (because we don't want the interesting
557      *   details of precondition failure to be hidden by an exception we throw about an unexpectedly
558      *   null _failure message_)
559      *
560      * That combination upsets NullPointerTester, which wants any call that passes null for a
561      * non-@Nullable parameter to trigger a NullPointerException.
562      *
563      * (We still define this empty method to keep PackageSanityTests from generating its own
564      * automated nullness tests, which would fail.)
565      */
566   }
567 
568   private static final Object IGNORE_ME =
569       new Object() {
570         @Override
571         public String toString() {
572           throw new AssertionFailedError();
573         }
574       };
575 
576   private static class Message {
577     boolean invoked;
578 
579     @Override
toString()580     public String toString() {
581       assertFalse(invoked);
582       invoked = true;
583       return "A message";
584     }
585   }
586 
587   private static final String FORMAT = "I ate %s pies.";
588 
verifySimpleMessage(Exception e)589   private static void verifySimpleMessage(Exception e) {
590     assertThat(e).hasMessageThat().isEqualTo("A message");
591   }
592 
verifyComplexMessage(Exception e)593   private static void verifyComplexMessage(Exception e) {
594     assertThat(e).hasMessageThat().isEqualTo("I ate 5 pies.");
595   }
596 }
597