Skip to content

Commit

Permalink
Merge pull request #3 from LachlanMcKee/feature/issue-2-only-log-when…
Browse files Browse the repository at this point in the history
…-test-fails

Issue #2 Added behaviour to only log when a test fails
  • Loading branch information
LachlanMcKee authored Nov 19, 2017
2 parents 2956126 + 74b00c9 commit 44541a1
Show file tree
Hide file tree
Showing 6 changed files with 192 additions and 55 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ public static class DebugMin {
public TimberTestRule mTimberTestRule = TimberTestRule.builder()
.minPriority(Log.DEBUG)
.showTimestamp(false)
.onlyLogWhenTestFails(false)
.build();

@Parameterized.Parameters
Expand Down Expand Up @@ -65,6 +66,7 @@ public static class InfoMin {
public TimberTestRule mTimberTestRule = TimberTestRule.builder()
.minPriority(Log.INFO)
.showTimestamp(false)
.onlyLogWhenTestFails(false)
.build();

@Parameterized.Parameters
Expand Down Expand Up @@ -107,6 +109,7 @@ public static class WarnMin {
public TimberTestRule mTimberTestRule = TimberTestRule.builder()
.minPriority(Log.WARN)
.showTimestamp(false)
.onlyLogWhenTestFails(false)
.build();

@Parameterized.Parameters
Expand Down Expand Up @@ -149,6 +152,7 @@ public static class ErrorMin {
public TimberTestRule mTimberTestRule = TimberTestRule.builder()
.minPriority(Log.ERROR)
.showTimestamp(false)
.onlyLogWhenTestFails(false)
.build();

@Parameterized.Parameters
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ public class LogTestWithNoTimeOrThreadRules {
public TimberTestRule mTimberTestRule = TimberTestRule.builder()
.showThread(false)
.showTimestamp(false)
.onlyLogWhenTestFails(false)
.build();

@Parameterized.Parameters
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package net.lachlanmckee.timberjunit.sample;

import net.lachlanmckee.timberjunit.TimberTestRule;

import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.junit.rules.RuleChain;

import java.io.OutputStream;

import static org.junit.Assert.fail;

public class LogTestWithOnlyOnTestFailure {

private ExpectedException expectedException = ExpectedException.none();

@Rule
public RuleChain chain = RuleChain
.outerRule(expectedException)
.around(TimberTestRule.builder()
.showThread(false)
.showTimestamp(false)
.onlyLogWhenTestFails(true)
.build());

private static OutputStream outputStream;

@BeforeClass
public static void setupConsoleOutput() {
outputStream = LogTesterTestUtils.setupConsoleOutputStream();
}

@Test
public void given_when_then() throws InterruptedException {
LogTester.log(LogTester.LogType.ERROR, "Test");

expectedException.expect(AssertionError.class);
// given

// when

// then
fail();
}

@AfterClass
public static void verifyErrorIsOutput() {
LogTesterTestUtils.assertOutput(outputStream, "E/LogTester: Test");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ public class LogTestWithThreadRules {
public TimberTestRule mTimberTestRule = TimberTestRule.builder()
.showThread(true)
.showTimestamp(false)
.onlyLogWhenTestFails(false)
.build();

@Parameterized.Parameters
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ public class LogTestWithTimestampRules {
@Rule
public TimberTestRule mTimberTestRule = TimberTestRule.builder()
.showTimestamp(true)
.onlyLogWhenTestFails(false)
.build();

@Parameterized.Parameters
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@

import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;

import timber.log.Timber;
Expand All @@ -26,25 +29,46 @@ private TimberTestRule(Rules rules) {
mRules = rules;
}

/**
* @return a {@link TimberTestRule} that logs messages of any priority regardless of the
* test outcome
*/
public static TimberTestRule logAllAlways() {
return new Rules()
.onlyLogWhenTestFails(false)
.build();
}

/**
* @return a {@link TimberTestRule} that logs all messages, regardless of their priority.
*/
public static TimberTestRule logAll() {
public static TimberTestRule logAllWhenTestFails() {
return new Rules()
.build();
}

/**
* @return a {@link TimberTestRule} that only logs error messages regardless of the test
* outcome.
*/
public static TimberTestRule logErrorsAlways() {
return new Rules()
.onlyLogWhenTestFails(false)
.minPriority(Log.ERROR)
.build();
}

/**
* @return a {@link TimberTestRule} that only logs error messages.
* @return a {@link TimberTestRule} that only logs error messages when a unit test fails.
*/
public static TimberTestRule logErrorsOnly() {
public static TimberTestRule logErrorsWhenTestFails() {
return new Rules()
.minPriority(Log.ERROR)
.build();
}

/**
* @return a {@link Rules} class which is used as a builder to create a {@link TimberTestRule}
* @return a {@link Rules} class which is used as a builder to create a {@link TimberTestRule}.
*/
public static Rules builder() {
return new Rules();
Expand All @@ -70,11 +94,13 @@ public static final class Rules {
private int mMinPriority;
private boolean mShowThread;
private boolean mShowTimestamp;
private boolean mOnlyLogWhenTestFails;

Rules() {
mMinPriority = Log.VERBOSE;
mShowThread = false;
mShowTimestamp = true;
mOnlyLogWhenTestFails = true;
}

/**
Expand Down Expand Up @@ -119,6 +145,17 @@ public Rules showTimestamp(boolean showTimestamp) {
return this;
}

/**
* Defines whether the logs are only output if the unit test fails.
*
* @param onlyLogWhenTestFails whether the logs are only output when a test fails.
* @return the mutated {@link Rules}
*/
public Rules onlyLogWhenTestFails(boolean onlyLogWhenTestFails) {
mOnlyLogWhenTestFails = onlyLogWhenTestFails;
return this;
}

/**
* Builds the JUnit test rule based on the defined rules.
*
Expand All @@ -134,20 +171,24 @@ public TimberTestRule build() {
*/
private static class TimberStatement extends Statement {
private final Statement mNext;
private final JUnitTimberTree mTree;
private final BufferedJUnitTimberTree mTree;

TimberStatement(Statement base, Rules rules) {
mNext = base;
mTree = new JUnitTimberTree(rules);
mTree = new BufferedJUnitTimberTree(rules);
}

@Override
public void evaluate() throws Throwable {
Timber.plant(mTree);
try {
mNext.evaluate();
}
finally {

} catch (Throwable t) {
mTree.flushLogs();
throw t;

} finally {
// Ensure the tree is removed to avoid duplicate logging.
Timber.uproot(mTree);
}
Expand All @@ -157,71 +198,107 @@ public void evaluate() throws Throwable {
/**
* A Timber tree that logs to the Java System.out rather than using the Android logger.
*/
private static class JUnitTimberTree extends Timber.DebugTree {
private static final class BufferedJUnitTimberTree extends Timber.DebugTree {
private final Rules mRules;
private final List<String> mLogMessageBuffer;

JUnitTimberTree(Rules rules) {
BufferedJUnitTimberTree(Rules rules) {
mRules = rules;
mLogMessageBuffer = new ArrayList<>();
}

@Override
protected void log(int priority, String tag, String message, Throwable t) {
// Avoid logging if the priority is too low.
if (priority < mRules.mMinPriority) {
String logMessage = createLogMessage(mRules, priority, tag, message);
if (logMessage == null) {
return;
}

// Obtain the correct log type prefix.
final char type;
switch (priority) {
case Log.VERBOSE:
type = 'V';
break;

case Log.DEBUG:
type = 'D';
break;

case Log.INFO:
type = 'I';
break;

case Log.WARN:
type = 'W';
break;

case Log.ERROR:
default:
type = 'E';
break;
}

StringBuilder logBuilder = new StringBuilder();
if (mRules.mOnlyLogWhenTestFails) {
mLogMessageBuffer.add(logMessage);

if (mRules.mShowTimestamp) {
logBuilder
.append(THREAD_LOCAL_FORMAT.get().format(System.currentTimeMillis()))
.append(" ");
} else {
System.out.println(logMessage);
}
}

if (mRules.mShowThread) {
Thread thread = Thread.currentThread();
logBuilder
.append(thread.getId())
.append("/")
.append(thread.getName())
.append(" ");
/**
* Flushes all the previously stored log messages.
*/
private void flushLogs() {
Iterator<String> iterator = mLogMessageBuffer.iterator();
while (iterator.hasNext()) {
System.out.println(iterator.next());
iterator.remove();
}
}
}

/**
* Creates a log message based on the rules and Timber log details.
*
* @param rules the rules used to construct the message.
* @param priority the priority of the log.
* @param tag the tag of the log.
* @param message the message of the log.
* @return a log message (may be null).
*/
private static String createLogMessage(Rules rules, int priority, String tag, String message) {
// Avoid logging if the priority is too low.
if (priority < rules.mMinPriority) {
return null;
}

// Obtain the correct log type prefix.
final char type;
switch (priority) {
case Log.VERBOSE:
type = 'V';
break;

case Log.DEBUG:
type = 'D';
break;

case Log.INFO:
type = 'I';
break;

case Log.WARN:
type = 'W';
break;

case Log.ERROR:
default:
type = 'E';
break;
}

StringBuilder logBuilder = new StringBuilder();

if (rules.mShowTimestamp) {
logBuilder
.append(type)
.append("/")
.append(tag)
.append(": ")
.append(message);
.append(THREAD_LOCAL_FORMAT.get().format(System.currentTimeMillis()))
.append(" ");
}

System.out.println(logBuilder);
if (rules.mShowThread) {
Thread thread = Thread.currentThread();
logBuilder
.append(thread.getId())
.append("/")
.append(thread.getName())
.append(" ");
}

logBuilder
.append(type)
.append("/")
.append(tag)
.append(": ")
.append(message);

return logBuilder.toString();
}

/**
Expand Down

0 comments on commit 44541a1

Please sign in to comment.