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