1 package org.robolectric.shadows; 2 3 import static com.google.common.base.StandardSystemProperty.LINE_SEPARATOR; 4 import static com.google.common.truth.Truth.assertThat; 5 import static java.nio.charset.StandardCharsets.UTF_8; 6 import static org.junit.Assert.assertEquals; 7 import static org.junit.Assert.assertFalse; 8 import static org.junit.Assert.assertThrows; 9 import static org.junit.Assert.assertTrue; 10 11 import android.util.Log; 12 import android.util.Log.TerribleFailure; 13 import android.util.Log.TerribleFailureHandler; 14 import androidx.test.ext.junit.runners.AndroidJUnit4; 15 import com.google.common.collect.Iterables; 16 import java.io.ByteArrayOutputStream; 17 import java.io.PrintStream; 18 import java.util.List; 19 import org.junit.Test; 20 import org.junit.runner.RunWith; 21 import org.robolectric.annotation.Config; 22 import org.robolectric.shadows.ShadowLog.LogItem; 23 import org.robolectric.util.ReflectionHelpers; 24 import org.robolectric.versioning.AndroidVersions.L; 25 26 @RunWith(AndroidJUnit4.class) 27 public class ShadowLogTest { 28 29 @Test d_shouldLogAppropriately()30 public void d_shouldLogAppropriately() { 31 Log.d("tag", "msg"); 32 33 assertLogged(Log.DEBUG, "tag", "msg", null); 34 } 35 36 @Test d_shouldLogAppropriately_withThrowable()37 public void d_shouldLogAppropriately_withThrowable() { 38 Throwable throwable = new Throwable(); 39 40 Log.d("tag", "msg", throwable); 41 42 assertLogged(Log.DEBUG, "tag", "msg", throwable); 43 } 44 45 @Test e_shouldLogAppropriately()46 public void e_shouldLogAppropriately() { 47 Log.e("tag", "msg"); 48 49 assertLogged(Log.ERROR, "tag", "msg", null); 50 } 51 52 @Test e_shouldLogAppropriately_withThrowable()53 public void e_shouldLogAppropriately_withThrowable() { 54 Throwable throwable = new Throwable(); 55 56 Log.e("tag", "msg", throwable); 57 58 assertLogged(Log.ERROR, "tag", "msg", throwable); 59 } 60 61 @Test i_shouldLogAppropriately()62 public void i_shouldLogAppropriately() { 63 Log.i("tag", "msg"); 64 65 assertLogged(Log.INFO, "tag", "msg", null); 66 } 67 68 @Test i_shouldLogAppropriately_withThrowable()69 public void i_shouldLogAppropriately_withThrowable() { 70 Throwable throwable = new Throwable(); 71 72 Log.i("tag", "msg", throwable); 73 74 assertLogged(Log.INFO, "tag", "msg", throwable); 75 } 76 77 @Test v_shouldLogAppropriately()78 public void v_shouldLogAppropriately() { 79 Log.v("tag", "msg"); 80 81 assertLogged(Log.VERBOSE, "tag", "msg", null); 82 } 83 84 @Test v_shouldLogAppropriately_withThrowable()85 public void v_shouldLogAppropriately_withThrowable() { 86 Throwable throwable = new Throwable(); 87 88 Log.v("tag", "msg", throwable); 89 90 assertLogged(Log.VERBOSE, "tag", "msg", throwable); 91 } 92 93 @Test w_shouldLogAppropriately()94 public void w_shouldLogAppropriately() { 95 Log.w("tag", "msg"); 96 97 assertLogged(Log.WARN, "tag", "msg", null); 98 } 99 100 @Test w_shouldLogAppropriately_withThrowable()101 public void w_shouldLogAppropriately_withThrowable() { 102 Throwable throwable = new Throwable(); 103 104 Log.w("tag", "msg", throwable); 105 106 assertLogged(Log.WARN, "tag", "msg", throwable); 107 } 108 109 @Test w_shouldLogAppropriately_withJustThrowable()110 public void w_shouldLogAppropriately_withJustThrowable() { 111 Throwable throwable = new Throwable(); 112 Log.w("tag", throwable); 113 assertLogged(Log.WARN, "tag", null, throwable); 114 } 115 116 @Test wtf_shouldLogAppropriately()117 public void wtf_shouldLogAppropriately() { 118 Log.wtf("tag", "msg"); 119 120 assertLogged(Log.ASSERT, "tag", "msg", null); 121 } 122 123 @Test wtf_shouldLogAppropriately_withThrowable()124 public void wtf_shouldLogAppropriately_withThrowable() { 125 Throwable throwable = new Throwable(); 126 127 Log.wtf("tag", "msg", throwable); 128 129 assertLogged(Log.ASSERT, "tag", "msg", throwable); 130 } 131 132 @Test wtf_wtfIsFatalIsSet_shouldThrowTerribleFailure()133 public void wtf_wtfIsFatalIsSet_shouldThrowTerribleFailure() { 134 ShadowLog.setWtfIsFatal(true); 135 136 Throwable throwable = new Throwable(); 137 assertThrows(TerribleFailure.class, () -> Log.wtf("tag", "msg", throwable)); 138 assertLogged(Log.ASSERT, "tag", "msg", throwable); 139 } 140 141 @Test 142 @Config(minSdk = L.SDK_INT) wtf_shouldLogAppropriately_withNewWtfHandler()143 public void wtf_shouldLogAppropriately_withNewWtfHandler() { 144 Throwable throwable = new Throwable(); 145 146 String[] captured = new String[1]; 147 148 TerribleFailureHandler prevWtfHandler = 149 Log.setWtfHandler( 150 ReflectionHelpers.createDelegatingProxy( 151 TerribleFailureHandler.class, 152 new Object() { 153 public void onTerribleFailure(String tag, TerribleFailure what, boolean system) { 154 captured[0] = what.getMessage(); 155 } 156 })); 157 158 Log.wtf("tag", "msg", throwable); 159 // assert that the new handler captures the message 160 assertEquals("msg", captured[0]); 161 // reset the handler 162 Log.setWtfHandler(prevWtfHandler); 163 164 assertLogged(Log.ASSERT, "tag", "msg", throwable); 165 } 166 167 @Test println_shouldLogAppropriately()168 public void println_shouldLogAppropriately() { 169 int len = Log.println(Log.ASSERT, "tag", "msg"); 170 assertLogged(Log.ASSERT, "tag", "msg", null); 171 assertThat(len).isEqualTo(11); 172 } 173 174 @Test println_shouldLogNullTagAppropriately()175 public void println_shouldLogNullTagAppropriately() { 176 int len = Log.println(Log.ASSERT, null, "msg"); 177 assertLogged(Log.ASSERT, null, "msg", null); 178 assertThat(len).isEqualTo(8); 179 } 180 181 @Test println_shouldLogNullMessageAppropriately()182 public void println_shouldLogNullMessageAppropriately() { 183 int len = Log.println(Log.ASSERT, "tag", null); 184 assertLogged(Log.ASSERT, "tag", null, null); 185 assertThat(len).isEqualTo(8); 186 } 187 188 @Test println_shouldLogNullTagAndNullMessageAppropriately()189 public void println_shouldLogNullTagAndNullMessageAppropriately() { 190 int len = Log.println(Log.ASSERT, null, null); 191 assertLogged(Log.ASSERT, null, null, null); 192 assertThat(len).isEqualTo(5); 193 } 194 195 @Test shouldLogToProvidedStream()196 public void shouldLogToProvidedStream() throws Exception { 197 final ByteArrayOutputStream bos = new ByteArrayOutputStream(); 198 PrintStream old = ShadowLog.stream; 199 try { 200 ShadowLog.stream = new PrintStream(bos); 201 Log.d("tag", "msg"); 202 assertThat(new String(bos.toByteArray(), UTF_8)) 203 .isEqualTo("D/tag: msg" + System.getProperty("line.separator")); 204 205 Log.w("tag", new RuntimeException()); 206 assertTrue(new String(bos.toByteArray(), UTF_8).contains("RuntimeException")); 207 } finally { 208 ShadowLog.stream = old; 209 } 210 } 211 specificMethodName()212 private static RuntimeException specificMethodName() { 213 return new RuntimeException(); 214 } 215 216 @Test shouldLogAccordingToTag()217 public void shouldLogAccordingToTag() throws Exception { 218 ShadowLog.reset(); 219 Log.d("tag1", "1"); 220 Log.i("tag2", "2"); 221 Log.e("tag3", "3"); 222 Log.w("tag1", "4"); 223 Log.i("tag1", "5"); 224 Log.d("tag2", "6"); 225 Log.d("throwable", "7", specificMethodName()); 226 227 List<LogItem> allItems = ShadowLog.getLogs(); 228 assertThat(allItems.size()).isEqualTo(7); 229 int i = 1; 230 for (LogItem item : allItems) { 231 assertThat(item.msg).isEqualTo(Integer.toString(i)); 232 assertThat(item.toString()).contains(item.msg); 233 i++; 234 } 235 assertThat(allItems.get(6).toString()).contains("specificMethodName"); 236 assertUniformLogsForTag("tag1", 3); 237 assertUniformLogsForTag("tag2", 2); 238 assertUniformLogsForTag("tag3", 1); 239 } 240 assertUniformLogsForTag(String tag, int count)241 private static void assertUniformLogsForTag(String tag, int count) { 242 List<LogItem> tag1Items = ShadowLog.getLogsForTag(tag); 243 assertThat(tag1Items.size()).isEqualTo(count); 244 int last = -1; 245 for (LogItem item : tag1Items) { 246 assertThat(item.tag).isEqualTo(tag); 247 int current = Integer.parseInt(item.msg); 248 assertThat(current > last).isTrue(); 249 last = current; 250 } 251 } 252 253 @Test infoIsDefaultLoggableLevel()254 public void infoIsDefaultLoggableLevel() throws Exception { 255 PrintStream old = ShadowLog.stream; 256 ShadowLog.stream = null; 257 assertFalse(Log.isLoggable("FOO", Log.VERBOSE)); 258 assertFalse(Log.isLoggable("FOO", Log.DEBUG)); 259 260 assertTrue(Log.isLoggable("FOO", Log.INFO)); 261 assertTrue(Log.isLoggable("FOO", Log.WARN)); 262 assertTrue(Log.isLoggable("FOO", Log.ERROR)); 263 assertTrue(Log.isLoggable("FOO", Log.ASSERT)); 264 ShadowLog.stream = old; 265 } 266 assertLogged(int type, String tag, String msg, Throwable throwable)267 private static void assertLogged(int type, String tag, String msg, Throwable throwable) { 268 LogItem lastLog = Iterables.getLast(ShadowLog.getLogsForTag(tag)); 269 assertEquals(type, lastLog.type); 270 assertEquals(msg, lastLog.msg); 271 assertEquals(tag, lastLog.tag); 272 assertEquals(throwable, lastLog.throwable); 273 } 274 assertLogged( String timeString, int type, String tag, String msg, Throwable throwable)275 private static void assertLogged( 276 String timeString, int type, String tag, String msg, Throwable throwable) { 277 LogItem lastLog = Iterables.getLast(ShadowLog.getLogsForTag(tag)); 278 assertEquals(timeString, lastLog.timeString); 279 assertLogged(type, tag, msg, throwable); 280 } 281 282 @Test identicalLogItemInstancesAreEqual()283 public void identicalLogItemInstancesAreEqual() { 284 LogItem item1 = new LogItem(Log.VERBOSE, "Foo", "Bar", null); 285 LogItem item2 = new LogItem(Log.VERBOSE, "Foo", "Bar", null); 286 assertThat(item1).isEqualTo(item2); 287 assertThat(item2).isEqualTo(item1); 288 } 289 290 @Test logsAfterSetLoggable()291 public void logsAfterSetLoggable() { 292 ShadowLog.setLoggable("Foo", Log.VERBOSE); 293 assertTrue(Log.isLoggable("Foo", Log.DEBUG)); 294 } 295 296 @Test noLogAfterSetLoggable()297 public void noLogAfterSetLoggable() { 298 PrintStream old = ShadowLog.stream; 299 ShadowLog.stream = new PrintStream(new ByteArrayOutputStream()); 300 ShadowLog.setLoggable("Foo", Log.DEBUG); 301 assertFalse(Log.isLoggable("Foo", Log.VERBOSE)); 302 ShadowLog.stream = old; 303 } 304 305 @Test getLogs_shouldReturnCopy()306 public void getLogs_shouldReturnCopy() { 307 Log.d("tag1", "1"); 308 assertThat(ShadowLog.getLogs()).isNotSameInstanceAs(ShadowLog.getLogs()); 309 assertThat(ShadowLog.getLogs()).isEqualTo(ShadowLog.getLogs()); 310 } 311 312 @Test getLogsForTag_empty()313 public void getLogsForTag_empty() { 314 assertThat(ShadowLog.getLogsForTag("non_existent")).isEmpty(); 315 } 316 317 @Test clear()318 public void clear() { 319 assertThat(ShadowLog.getLogsForTag("tag1")).isEmpty(); 320 Log.d("tag1", "1"); 321 assertThat(ShadowLog.getLogsForTag("tag1")).isNotEmpty(); 322 ShadowLog.clear(); 323 assertThat(ShadowLog.getLogsForTag("tag1")).isEmpty(); 324 assertThat(ShadowLog.getLogs()).isEmpty(); 325 } 326 327 @Test shouldLogTimeWithTimeSupplier()328 public void shouldLogTimeWithTimeSupplier() { 329 ShadowLog.setTimeSupplier(() -> "20 July 1969 20:17"); 330 331 Log.d("tag", "msg"); 332 333 assertLogged("20 July 1969 20:17", Log.DEBUG, "tag", "msg", null); 334 } 335 336 @Test shouldLogToProvidedStreamWithTimeSupplier()337 public void shouldLogToProvidedStreamWithTimeSupplier() { 338 ShadowLog.setTimeSupplier(() -> "20 July 1969 20:17"); 339 340 ByteArrayOutputStream bos = new ByteArrayOutputStream(); 341 PrintStream old = ShadowLog.stream; 342 try { 343 ShadowLog.stream = new PrintStream(bos); 344 Log.d("tag", "msg"); 345 assertThat(new String(bos.toByteArray(), UTF_8)) 346 .isEqualTo("20 July 1969 20:17 D/tag: msg" + LINE_SEPARATOR.value()); 347 348 Log.w("tag", new RuntimeException()); 349 assertThat(new String(bos.toByteArray(), UTF_8)).contains("RuntimeException"); 350 } finally { 351 ShadowLog.stream = old; 352 } 353 } 354 355 @Test resetShouldClearTimeSupplier()356 public void resetShouldClearTimeSupplier() { 357 ShadowLog.setTimeSupplier(() -> "15 June 1977 20:17"); 358 ShadowLog.reset(); 359 360 ByteArrayOutputStream bos = new ByteArrayOutputStream(); 361 PrintStream old = ShadowLog.stream; 362 try { 363 ShadowLog.stream = new PrintStream(bos); 364 Log.d("tag", "msg"); 365 assertThat(new String(bos.toByteArray(), UTF_8)) 366 .isEqualTo("D/tag: msg" + LINE_SEPARATOR.value()); 367 368 Log.w("tag", new RuntimeException()); 369 assertThat(new String(bos.toByteArray(), UTF_8)).contains("RuntimeException"); 370 } finally { 371 ShadowLog.stream = old; 372 } 373 } 374 } 375