1/**
2 * Copyright (C) 2008 Google Inc.
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
18package com.google.inject;
19
20import static com.google.inject.internal.InternalFlags.IncludeStackTraceOption;
21import static com.google.inject.internal.InternalFlags.getIncludeStackTraceOption;
22import static junit.framework.Assert.assertEquals;
23import static junit.framework.Assert.assertNotNull;
24import static junit.framework.Assert.assertSame;
25import static junit.framework.Assert.assertTrue;
26
27import com.google.common.base.Function;
28import com.google.common.base.Joiner;
29import com.google.common.collect.ImmutableList;
30import com.google.common.collect.Iterables;
31import com.google.common.testing.GcFinalization;
32
33import junit.framework.Assert;
34
35import java.io.ByteArrayInputStream;
36import java.io.ByteArrayOutputStream;
37import java.io.IOException;
38import java.io.NotSerializableException;
39import java.io.ObjectInputStream;
40import java.io.ObjectOutputStream;
41import java.lang.ref.ReferenceQueue;
42import java.lang.ref.WeakReference;
43
44/**
45 * @author jessewilson@google.com (Jesse Wilson)
46 */
47public class Asserts {
48  private Asserts() {}
49
50  /**
51   * Returns the String that would appear in an error message for this chain of classes
52   * as modules.
53   */
54  public static String asModuleChain(Class... classes) {
55    return Joiner.on(" -> ").appendTo(new StringBuilder(" (via modules: "),
56        Iterables.transform(ImmutableList.copyOf(classes), new Function<Class, String>() {
57          @Override
58          public String apply(Class input) {
59            return input.getName();
60          }
61        })).append(")").toString();
62  }
63
64  /**
65   * Returns the source file appears in error messages based on {@link
66   * #getIncludeStackTraceOption()} value.
67   */
68  public static String getDeclaringSourcePart(Class clazz) {
69    if (getIncludeStackTraceOption() == IncludeStackTraceOption.OFF) {
70      return ".configure(Unknown Source";
71    }
72    return ".configure(" + clazz.getSimpleName() + ".java:";
73  }
74
75  /**
76   * Returns true if {@link #getIncludeStackTraceOption()} returns {@link
77   * IncludeStackTraceOption#OFF}.
78   */
79  public static boolean isIncludeStackTraceOff() {
80    return getIncludeStackTraceOption() == IncludeStackTraceOption.OFF;
81  }
82
83  /**
84   * Returns true if {@link #getIncludeStackTraceOption()} returns {@link
85   * IncludeStackTraceOption#COMPLETE}.
86   */
87  public static boolean isIncludeStackTraceComplete() {
88    return getIncludeStackTraceOption() == IncludeStackTraceOption.COMPLETE;
89  }
90
91  /**
92   * Fails unless {@code expected.equals(actual)}, {@code
93   * actual.equals(expected)} and their hash codes are equal. This is useful
94   * for testing the equals method itself.
95   */
96  public static void assertEqualsBothWays(Object expected, Object actual) {
97    assertNotNull(expected);
98    assertNotNull(actual);
99    assertEquals("expected.equals(actual)", actual, expected);
100    assertEquals("actual.equals(expected)", expected, actual);
101    assertEquals("hashCode", expected.hashCode(), actual.hashCode());
102  }
103
104  /**
105   * Fails unless {@code text} includes all {@code substrings}, in order.
106   */
107  public static void assertContains(String text, String... substrings) {
108    /*if[NO_AOP]
109    // when we strip out bytecode manipulation, we lose the ability to generate some source lines.
110    if (text.contains("(Unknown Source)")) {
111      return;
112    }
113    end[NO_AOP]*/
114
115    int startingFrom = 0;
116    for (String substring : substrings) {
117      int index = text.indexOf(substring, startingFrom);
118      assertTrue(String.format("Expected \"%s\" to contain substring \"%s\"", text, substring),
119          index >= startingFrom);
120      startingFrom = index + substring.length();
121    }
122
123    String lastSubstring = substrings[substrings.length - 1];
124    assertTrue(String.format("Expected \"%s\" to contain substring \"%s\" only once),",
125        text, lastSubstring), text.indexOf(lastSubstring, startingFrom) == -1);
126  }
127
128  /**
129   * Fails unless {@code object} doesn't equal itself when reserialized.
130   */
131  public static void assertEqualWhenReserialized(Object object)
132      throws IOException {
133    Object reserialized = reserialize(object);
134    assertEquals(object, reserialized);
135    assertEquals(object.hashCode(), reserialized.hashCode());
136  }
137
138  /**
139   * Fails unless {@code object} has the same toString value when reserialized.
140   */
141  public static void assertSimilarWhenReserialized(Object object) throws IOException {
142    Object reserialized = reserialize(object);
143    assertEquals(object.toString(), reserialized.toString());
144  }
145
146  public static <E> E reserialize(E original) throws IOException {
147    try {
148      ByteArrayOutputStream out = new ByteArrayOutputStream();
149      new ObjectOutputStream(out).writeObject(original);
150      ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray());
151      @SuppressWarnings("unchecked") // the reserialized type is assignable
152      E reserialized = (E) new ObjectInputStream(in).readObject();
153      return reserialized;
154    } catch (ClassNotFoundException e) {
155      throw new RuntimeException(e);
156    }
157  }
158
159  public static void assertNotSerializable(Object object) throws IOException {
160    try {
161      reserialize(object);
162      Assert.fail();
163    } catch (NotSerializableException expected) {
164    }
165  }
166
167  public static void awaitFullGc() {
168    // GcFinalization *should* do it, but doesn't work well in practice...
169    // so we put a second latch and wait for a ReferenceQueue to tell us.
170    ReferenceQueue<Object> queue = new ReferenceQueue<Object>();
171    WeakReference ref = new WeakReference<Object>(new Object(), queue);
172    GcFinalization.awaitFullGc();
173    try {
174      assertSame("queue didn't return ref in time", ref, queue.remove(5000));
175    } catch (IllegalArgumentException e) {
176      throw new RuntimeException(e);
177    } catch (InterruptedException e) {
178      throw new RuntimeException(e);
179    }
180  }
181
182  public static void awaitClear(WeakReference<?> ref) {
183    // GcFinalization *should* do it, but doesn't work well in practice...
184    // so we put a second latch and wait for a ReferenceQueue to tell us.
185    Object data = ref.get();
186    ReferenceQueue<Object> queue = null;
187    WeakReference extraRef = null;
188    if (data != null) {
189      queue = new ReferenceQueue<Object>();
190      extraRef = new WeakReference<Object>(data, queue);
191      data = null;
192    }
193    GcFinalization.awaitClear(ref);
194    if (queue != null) {
195      try {
196        assertSame("queue didn't return ref in time", extraRef, queue.remove(5000));
197      } catch (IllegalArgumentException e) {
198        throw new RuntimeException(e);
199      } catch (InterruptedException e) {
200        throw new RuntimeException(e);
201      }
202    }
203  }
204}
205