Change ExpectedLogs to include better stack-traces
Also add support for verifying the absence of a message.
----Release Notes----
[]
-------------
Created by MOE: http://code.google.com/p/moe-java
MOE_MIGRATED_REVID=94922273
diff --git a/sdk/src/test/java/com/google/cloud/dataflow/sdk/options/PipelineOptionsFactoryTest.java b/sdk/src/test/java/com/google/cloud/dataflow/sdk/options/PipelineOptionsFactoryTest.java
index 6ffbc8e..576bdaf 100644
--- a/sdk/src/test/java/com/google/cloud/dataflow/sdk/options/PipelineOptionsFactoryTest.java
+++ b/sdk/src/test/java/com/google/cloud/dataflow/sdk/options/PipelineOptionsFactoryTest.java
@@ -577,8 +577,8 @@
String[] args = new String[] {
"--diskSizeGb=100",
"--diskSizeGb=200"};
- expectedLogs.expectWarn("Strict parsing is disabled, ignoring option");
PipelineOptionsFactory.fromArgs(args).withoutStrictParsing().create();
+ expectedLogs.verifyWarn("Strict parsing is disabled, ignoring option");
}
@Test
@@ -610,8 +610,8 @@
@Test
public void testUsingArgumentWithUnknownPropertyIsIgnoredWithoutStrictParsing() {
String[] args = new String[] {"--unknownProperty=value"};
- expectedLogs.expectWarn("missing a property named 'unknownProperty'");
PipelineOptionsFactory.fromArgs(args).withoutStrictParsing().create();
+ expectedLogs.verifyWarn("missing a property named 'unknownProperty'");
}
@Test
@@ -625,8 +625,8 @@
@Test
public void testUsingArgumentStartingWithIllegalCharacterIsIgnoredWithoutStrictParsing() {
String[] args = new String[] {" --diskSizeGb=100"};
- expectedLogs.expectWarn("Strict parsing is disabled, ignoring option");
PipelineOptionsFactory.fromArgs(args).withoutStrictParsing().create();
+ expectedLogs.verifyWarn("Strict parsing is disabled, ignoring option");
}
@Test
@@ -652,8 +652,8 @@
@Test
public void testUsingArgumentWithInvalidNameIsIgnoredWithoutStrictParsing() {
String[] args = new String[] {"--=100"};
- expectedLogs.expectWarn("Strict parsing is disabled, ignoring option");
PipelineOptionsFactory.fromArgs(args).withoutStrictParsing().create();
+ expectedLogs.verifyWarn("Strict parsing is disabled, ignoring option");
}
@Test
diff --git a/sdk/src/test/java/com/google/cloud/dataflow/sdk/runners/BlockingDataflowPipelineRunnerTest.java b/sdk/src/test/java/com/google/cloud/dataflow/sdk/runners/BlockingDataflowPipelineRunnerTest.java
index 069284d..fed862d 100644
--- a/sdk/src/test/java/com/google/cloud/dataflow/sdk/runners/BlockingDataflowPipelineRunnerTest.java
+++ b/sdk/src/test/java/com/google/cloud/dataflow/sdk/runners/BlockingDataflowPipelineRunnerTest.java
@@ -100,7 +100,6 @@
@Test
public void testJobWaitComplete() throws IOException, InterruptedException {
- expectedLogs.expectInfo("Job finished with status DONE");
DataflowPipelineRunner mockDataflowPipelineRunner = mock(DataflowPipelineRunner.class);
DataflowPipelineJob mockJob = mock(DataflowPipelineJob.class);
@@ -141,6 +140,7 @@
mockWait.signalJobComplete();
assertTrue("run() should return after job completion is mocked.",
jobCompleted.waitTillSet(2000));
+ expectedLogs.verifyInfo("Job finished with status DONE");
}
@Test
diff --git a/sdk/src/test/java/com/google/cloud/dataflow/sdk/testing/ExpectedLogs.java b/sdk/src/test/java/com/google/cloud/dataflow/sdk/testing/ExpectedLogs.java
index 8b1a688..e262e68 100644
--- a/sdk/src/test/java/com/google/cloud/dataflow/sdk/testing/ExpectedLogs.java
+++ b/sdk/src/test/java/com/google/cloud/dataflow/sdk/testing/ExpectedLogs.java
@@ -18,8 +18,6 @@
import static org.junit.Assert.fail;
-import com.google.common.collect.Lists;
-
import org.hamcrest.Description;
import org.hamcrest.Matcher;
import org.hamcrest.TypeSafeMatcher;
@@ -28,10 +26,12 @@
import java.util.Collection;
import java.util.concurrent.ConcurrentLinkedDeque;
+import java.util.logging.Formatter;
import java.util.logging.Handler;
import java.util.logging.Level;
import java.util.logging.LogRecord;
import java.util.logging.Logger;
+import java.util.logging.SimpleFormatter;
import javax.annotation.concurrent.ThreadSafe;
@@ -53,105 +53,143 @@
}
/**
- * Expect a logging event at the trace level with the given message.
+ * Verify a logging event at the trace level with the given message.
*
* @param substring The message to match against.
*/
- public void expectTrace(String substring) {
- expect(Level.FINEST, substring);
+ public void verifyTrace(String substring) {
+ verify(Level.FINEST, substring);
}
/**
- * Expect a logging event at the trace level with the given message and throwable.
+ * Verify a logging event at the trace level with the given message and throwable.
*
* @param substring The message to match against.
* @param t The throwable to match against.
*/
- public void expectTrace(String substring, Throwable t) {
- expect(Level.FINEST, substring, t);
+ public void verifyTrace(String substring, Throwable t) {
+ verify(Level.FINEST, substring, t);
}
/**
- * Expect a logging event at the debug level with the given message.
+ * Verify a logging event at the debug level with the given message.
*
* @param substring The message to match against.
*/
- public void expectDebug(String substring) {
- expect(Level.FINE, substring);
+ public void verifyDebug(String substring) {
+ verify(Level.FINE, substring);
}
/**
- * Expect a logging event at the debug level with the given message and throwable.
+ * Verify a logging event at the debug level with the given message and throwable.
*
* @param message The message to match against.
* @param t The throwable to match against.
*/
- public void expectDebug(String message, Throwable t) {
- expect(Level.FINE, message, t);
+ public void verifyDebug(String message, Throwable t) {
+ verify(Level.FINE, message, t);
}
/**
- * Expect a logging event at the info level with the given message.
+ * Verify a logging event at the info level with the given message.
+ *
* @param substring The message to match against.
*/
- public void expectInfo(String substring) {
- expect(Level.INFO, substring);
+ public void verifyInfo(String substring) {
+ verify(Level.INFO, substring);
}
/**
- * Expect a logging event at the info level with the given message and throwable.
+ * Verify a logging event at the info level with the given message and throwable.
*
* @param message The message to match against.
* @param t The throwable to match against.
*/
- public void expectInfo(String message, Throwable t) {
- expect(Level.INFO, message, t);
+ public void verifyInfo(String message, Throwable t) {
+ verify(Level.INFO, message, t);
}
/**
- * Expect a logging event at the warn level with the given message.
+ * Verify a logging event at the warn level with the given message.
*
* @param substring The message to match against.
*/
- public void expectWarn(String substring) {
- expect(Level.WARNING, substring);
+ public void verifyWarn(String substring) {
+ verify(Level.WARNING, substring);
}
/**
- * Expect a logging event at the warn level with the given message and throwable.
+ * Verify a logging event at the warn level with the given message and throwable.
*
* @param substring The message to match against.
* @param t The throwable to match against.
*/
- public void expectWarn(String substring, Throwable t) {
- expect(Level.WARNING, substring, t);
+ public void verifyWarn(String substring, Throwable t) {
+ verify(Level.WARNING, substring, t);
}
/**
- * Expect a logging event at the error level with the given message.
+ * Verify a logging event at the error level with the given message.
*
* @param substring The message to match against.
*/
- public void expectError(String substring) {
- expect(Level.SEVERE, substring);
+ public void verifyError(String substring) {
+ verify(Level.SEVERE, substring);
}
/**
- * Expect a logging event at the error level with the given message and throwable.
+ * Verify a logging event at the error level with the given message and throwable.
*
* @param substring The message to match against.
* @param t The throwable to match against.
*/
- public void expectError(String substring, Throwable t) {
- expect(Level.SEVERE, substring, t);
+ public void verifyError(String substring, Throwable t) {
+ verify(Level.SEVERE, substring, t);
}
- private void expect(final Level level, final String substring) {
- expectations.add(new TypeSafeMatcher<LogRecord>() {
+ /**
+ * Verify there are no logging events with messages containing the given substring.
+ *
+ * @param substring The message to match against.
+ */
+ public void verifyNotLogged(String substring) {
+ verifyNotLogged(matcher(substring));
+ }
+
+ /**
+ * Verify there is no logging event at the error level with the given message and throwable.
+ *
+ * @param substring The message to match against.
+ * @param t The throwable to match against.
+ */
+ public void verifyNoError(String substring, Throwable t) {
+ verifyNo(Level.SEVERE, substring, t);
+ }
+
+ private void verify(final Level level, final String substring) {
+ verifyLogged(matcher(level, substring));
+ }
+
+ private TypeSafeMatcher<LogRecord> matcher(final String substring) {
+ return new TypeSafeMatcher<LogRecord>() {
+ @Override
+ public void describeTo(Description description) {
+ description.appendText(String.format("log message containing message [%s]", substring));
+ }
+
+ @Override
+ protected boolean matchesSafely(LogRecord item) {
+ return item.getMessage().contains(substring);
+ }
+ };
+ }
+
+ private TypeSafeMatcher<LogRecord> matcher(final Level level, final String substring) {
+ return new TypeSafeMatcher<LogRecord>() {
@Override
public void describeTo(Description description) {
description.appendText(String.format(
- "Expected log message of level [%s] containing message [%s]", level, substring));
+ "log message of level [%s] containing message [%s]", level, substring));
}
@Override
@@ -159,15 +197,24 @@
return level.equals(item.getLevel())
&& item.getMessage().contains(substring);
}
- });
+ };
}
- private void expect(final Level level, final String substring, final Throwable throwable) {
- expectations.add(new TypeSafeMatcher<LogRecord>() {
+ private void verify(final Level level, final String substring, final Throwable throwable) {
+ verifyLogged(matcher(level, substring, throwable));
+ }
+
+ private void verifyNo(final Level level, final String substring, final Throwable throwable) {
+ verifyNotLogged(matcher(level, substring, throwable));
+ }
+
+ private TypeSafeMatcher<LogRecord> matcher(
+ final Level level, final String substring, final Throwable throwable) {
+ return new TypeSafeMatcher<LogRecord>() {
@Override
public void describeTo(Description description) {
description.appendText(String.format(
- "Expected log message of level [%s] containg message [%s] with exception [%s] "
+ "log message of level [%s] containg message [%s] with exception [%s] "
+ "containing message [%s]",
level, substring, throwable.getClass(), throwable.getMessage()));
}
@@ -179,7 +226,26 @@
&& item.getThrown().getClass().equals(throwable.getClass())
&& item.getThrown().getMessage().contains(throwable.getMessage());
}
- });
+ };
+ }
+
+ private void verifyLogged(Matcher<LogRecord> matcher) {
+ for (LogRecord record : logSaver.getLogs()) {
+ if (matcher.matches(record)) {
+ return;
+ }
+ }
+
+ fail(String.format("Missing match for [%s]", matcher));
+ }
+
+ private void verifyNotLogged(Matcher<LogRecord> matcher) {
+ // Don't use Matchers.everyItem(Matchers.not(matcher)) because it doesn't format the logRecord
+ for (LogRecord record : logSaver.getLogs()) {
+ if (matcher.matches(record)) {
+ fail(String.format("Unexpected match of [%s]: [%s]", matcher, logFormatter.format(record)));
+ }
+ }
}
@Override
@@ -193,30 +259,16 @@
protected void after() {
log.removeHandler(logSaver);
log.setLevel(previousLevel);
- Collection<Matcher<LogRecord>> missingExpecations = Lists.newArrayList();
- FOUND: for (Matcher<LogRecord> expectation : expectations) {
- for (LogRecord log : logSaver.getLogs()) {
- if (expectation.matches(log)) {
- continue FOUND;
- }
- }
- missingExpecations.add(expectation);
- }
-
- if (!missingExpecations.isEmpty()) {
- fail(String.format("Missed logging expectations: %s", missingExpecations));
- }
}
private final Logger log;
private final LogSaver logSaver;
- private final Collection<Matcher<LogRecord>> expectations;
+ private final Formatter logFormatter = new SimpleFormatter();
private Level previousLevel;
private ExpectedLogs(Class<?> klass) {
log = Logger.getLogger(klass.getName());
logSaver = new LogSaver();
- expectations = Lists.newArrayList();
}
/**
diff --git a/sdk/src/test/java/com/google/cloud/dataflow/sdk/testing/ExpectedLogsTest.java b/sdk/src/test/java/com/google/cloud/dataflow/sdk/testing/ExpectedLogsTest.java
index e10d58f..44b0b60 100644
--- a/sdk/src/test/java/com/google/cloud/dataflow/sdk/testing/ExpectedLogsTest.java
+++ b/sdk/src/test/java/com/google/cloud/dataflow/sdk/testing/ExpectedLogsTest.java
@@ -16,7 +16,7 @@
package com.google.cloud.dataflow.sdk.testing;
-import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
@@ -24,6 +24,8 @@
import org.slf4j.LoggerFactory;
import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
import java.util.Random;
import java.util.concurrent.Callable;
import java.util.concurrent.CompletionService;
@@ -35,74 +37,89 @@
public class ExpectedLogsTest {
private static final Logger LOG = LoggerFactory.getLogger(ExpectedLogsTest.class);
- private ExpectedLogs expectedLogs;
+ private Random random = new Random();
- @Before
- public void setUp() {
- expectedLogs = ExpectedLogs.none(ExpectedLogsTest.class);
- }
+ @Rule public ExpectedLogs expectedLogs = ExpectedLogs.none(ExpectedLogsTest.class);
@Test
public void testWhenNoExpectations() throws Throwable {
- expectedLogs.before();
LOG.error(generateRandomString());
- expectedLogs.after();
}
@Test
- public void testWhenExpectationIsMatchedFully() throws Throwable {
+ public void testVerifyWhenMatchedFully() throws Throwable {
String expected = generateRandomString();
- expectedLogs.before();
- expectedLogs.expectError(expected);
+
LOG.error(expected);
- expectedLogs.after();
+ expectedLogs.verifyError(expected);
}
@Test
- public void testWhenExpectationIsMatchedPartially() throws Throwable {
+ public void testVerifyWhenMatchedPartially() throws Throwable {
String expected = generateRandomString();
- expectedLogs.before();
- expectedLogs.expectError(expected);
LOG.error("Extra stuff around expected " + expected + " blah");
- expectedLogs.after();
+ expectedLogs.verifyError(expected);
}
@Test
- public void testWhenExpectationIsMatchedWithExceptionBeingLogged() throws Throwable {
+ public void testVerifyWhenMatchedWithExceptionBeingLogged() throws Throwable {
String expected = generateRandomString();
- expectedLogs.before();
- expectedLogs.expectError(expected);
LOG.error(expected, new IOException("Fake Exception"));
- expectedLogs.after();
+ expectedLogs.verifyError(expected);
}
@Test(expected = AssertionError.class)
- public void testWhenExpectationIsNotMatched() throws Throwable {
+ public void testVerifyWhenNotMatched() throws Throwable {
String expected = generateRandomString();
- expectedLogs.before();
- expectedLogs.expectError(expected);
- expectedLogs.after();
+
+ expectedLogs.verifyError(expected);
+ }
+
+ @Test(expected = AssertionError.class)
+ public void testVerifyNotLoggedWhenMatchedFully() throws Throwable {
+ String expected = generateRandomString();
+
+ LOG.error(expected);
+ expectedLogs.verifyNotLogged(expected);
+ }
+
+ @Test(expected = AssertionError.class)
+ public void testVerifyNotLoggedWhenMatchedPartially() throws Throwable {
+ String expected = generateRandomString();
+ LOG.error("Extra stuff around expected " + expected + " blah");
+ expectedLogs.verifyNotLogged(expected);
+ }
+
+ @Test(expected = AssertionError.class)
+ public void testVerifyNotLoggedWhenMatchedWithException() throws Throwable {
+ String expected = generateRandomString();
+ LOG.error(expected, new IOException("Fake Exception"));
+ expectedLogs.verifyNotLogged(expected);
+ }
+
+ @Test
+ public void testVerifyNotLoggedWhenNotMatched() throws Throwable {
+ String expected = generateRandomString();
+ expectedLogs.verifyNotLogged(expected);
}
@Test
public void testLogCaptureOccursAtLowestLogLevel() throws Throwable {
String expected = generateRandomString();
- expectedLogs.before();
- expectedLogs.expectTrace(expected);
LOG.trace(expected);
- expectedLogs.after();
+ expectedLogs.verifyTrace(expected);
}
@Test
public void testThreadSafetyOfLogSaver() throws Throwable {
- expectedLogs.before();
-
CompletionService<Void> completionService =
new ExecutorCompletionService<>(Executors.newCachedThreadPool());
final long scheduledLogTime = System.currentTimeMillis() + 500L;
+
+ List<String> expectedStrings = new ArrayList<>();
for (int i = 0; i < 100; i++) {
final String expected = generateRandomString();
- expectedLogs.expectTrace(expected);
+ expectedStrings.add(expected);
completionService.submit(new Callable<Void>() {
@Override
public Void call() throws Exception {
@@ -119,12 +136,13 @@
completionService.take();
}
- expectedLogs.after();
+ for (String expected : expectedStrings) {
+ expectedLogs.verifyTrace(expected);
+ }
}
// Generates a random fake error message.
- private static String generateRandomString() {
- Random random = new Random();
+ private String generateRandomString() {
return "Fake error message: " + random.nextInt();
}
}