/*
* Copyright 2014-present Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License. You may obtain
* a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
package com.facebook.buck.log;
import static org.hamcrest.Matchers.equalTo;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertThat;
import com.facebook.buck.testutil.FakeOutputStream;
import java.io.IOException;
import java.util.concurrent.ConcurrentHashMap;
import java.util.logging.Formatter;
import java.util.logging.Handler;
import java.util.logging.Level;
import java.util.logging.LogRecord;
import org.junit.Before;
import org.junit.Test;
/** Unit tests for {@link ConsoleHandler}. */
public class ConsoleHandlerTest {
private FakeOutputStream outputStream;
private ConcurrentHashMap<Long, String> threadIdToCommandId;
private ConcurrentHashMap<String, ConsoleHandlerState.Writer> commandIdToConsoleWriter;
private ConcurrentHashMap<String, Level> commandIdToLevel;
private ConsoleHandlerState state;
@Before
public void setUp() {
outputStream = new FakeOutputStream();
threadIdToCommandId = new ConcurrentHashMap<>();
commandIdToConsoleWriter = new ConcurrentHashMap<>();
commandIdToLevel = new ConcurrentHashMap<>();
state =
new ConsoleHandlerState() {
@Override
public Level getLogLevel(String commandId) {
return commandIdToLevel.get(commandId);
}
@Override
public ConsoleHandlerState.Writer getWriter(String commandId) {
return commandIdToConsoleWriter.get(commandId);
}
@Override
public Iterable<ConsoleHandlerState.Writer> getAllAvailableWriters() {
return commandIdToConsoleWriter.values();
}
@Override
public String threadIdToCommandId(long threadId) {
return threadIdToCommandId.get(threadId);
}
};
}
// We use a custom formatter so the test doesn't depend on locale, clock, or timezone.
private static class MessageOnlyFormatter extends Formatter {
@Override
public String format(LogRecord record) {
return record.getMessage();
}
}
@Test
public void consoleHandlerDoesNotWriteBelowLevelToStream() {
ConsoleHandler handler =
new ConsoleHandler(
ConsoleHandler.utf8OutputStreamWriter(outputStream),
new MessageOnlyFormatter(),
Level.INFO,
state);
publishAndFlush(handler, new LogRecord(Level.FINE, "Shh.."));
assertThat(outputStream.size(), equalTo(0));
}
@Test
public void consoleHandlerWritesAtLevelToStream() throws IOException {
ConsoleHandler handler =
new ConsoleHandler(
ConsoleHandler.utf8OutputStreamWriter(outputStream),
new MessageOnlyFormatter(),
Level.INFO,
state);
publishAndFlush(handler, new LogRecord(Level.INFO, "Hello"));
assertThat(outputStream.toString("UTF-8"), equalTo("Hello"));
}
@Test
public void consoleHandlerDoesNotFlushBelowSevere() {
ConsoleHandler handler =
new ConsoleHandler(
ConsoleHandler.utf8OutputStreamWriter(outputStream),
new MessageOnlyFormatter(),
Level.INFO,
state);
handler.publish(new LogRecord(Level.INFO, "Info"));
assertThat(outputStream.getLastFlushSize(), equalTo(0));
}
@Test
public void consoleHandlerFlushesSevere() {
ConsoleHandler handler =
new ConsoleHandler(
ConsoleHandler.utf8OutputStreamWriter(outputStream),
new MessageOnlyFormatter(),
Level.INFO,
state);
handler.publish(new LogRecord(Level.SEVERE, "Severe"));
assertThat(outputStream.getLastFlushSize(), equalTo(6));
}
@Test
public void consoleHandlerCanChangeOutputStreamWithoutClosing() throws IOException {
FakeOutputStream outputStream1 = new FakeOutputStream();
FakeOutputStream outputStream2 = new FakeOutputStream();
ConsoleHandler handler =
new ConsoleHandler(
ConsoleHandler.utf8OutputStreamWriter(outputStream1),
new MessageOnlyFormatter(),
Level.INFO,
state);
publishAndFlush(handler, new LogRecord(Level.INFO, "Stream 1"));
assertThat(outputStream1.toString("UTF-8"), equalTo("Stream 1"));
threadIdToCommandId.put(49152L, "commandIdForOutputStream2");
registerOutputStream("commandIdForOutputStream2", outputStream2);
assertThat(outputStream1.isClosed(), equalTo(false));
publishAndFlush(handler, newLogRecordWithThreadId(Level.INFO, "Stream 2", 49152));
assertThat(outputStream1.toString("UTF-8"), equalTo("Stream 1"));
assertThat(outputStream2.toString("UTF-8"), equalTo("Stream 2"));
unregisterOutputStream("commandIdForOutputStream2");
assertThat(outputStream2.isClosed(), equalTo(false));
publishAndFlush(handler, new LogRecord(Level.INFO, " - DONE"));
assertThat(outputStream1.toString("UTF-8"), equalTo("Stream 1 - DONE"));
assertThat(outputStream2.toString("UTF-8"), equalTo("Stream 2"));
}
private void unregisterOutputStream(String commandId) {
assertNotNull(
String.format("Command id [%s] never had a registered writer.", commandId),
commandIdToConsoleWriter.remove(commandId));
}
private void registerOutputStream(String commandId, FakeOutputStream outputStream) {
commandIdToConsoleWriter.put(commandId, ConsoleHandler.utf8OutputStreamWriter(outputStream));
}
@Test
public void logRecordsOnlyGoToRegisteredOutputStream() throws IOException {
FakeOutputStream outputStream1 = new FakeOutputStream();
FakeOutputStream outputStream2 = new FakeOutputStream();
FakeOutputStream outputStream3 = new FakeOutputStream();
ConsoleHandler handler =
new ConsoleHandler(
ConsoleHandler.utf8OutputStreamWriter(outputStream1),
new MessageOnlyFormatter(),
Level.INFO,
state);
threadIdToCommandId.put(49152L, "commandIdForOutputStream2");
threadIdToCommandId.put(64738L, "commandIdForOutputStream3");
registerOutputStream("commandIdForOutputStream2", outputStream2);
registerOutputStream("commandIdForOutputStream3", outputStream3);
publishAndFlush(handler, newLogRecordWithThreadId(Level.INFO, "Stream 2", 49152));
assertThat(outputStream1.toString("UTF-8"), equalTo(""));
assertThat(outputStream2.toString("UTF-8"), equalTo("Stream 2"));
assertThat(outputStream3.toString("UTF-8"), equalTo(""));
publishAndFlush(handler, newLogRecordWithThreadId(Level.INFO, "Stream 3", 64738));
assertThat(outputStream1.toString("UTF-8"), equalTo(""));
assertThat(outputStream2.toString("UTF-8"), equalTo("Stream 2"));
assertThat(outputStream3.toString("UTF-8"), equalTo("Stream 3"));
}
@Test
public void logRecordPublishedWithMultipleThreadIdsForSingleCommandId() throws IOException {
FakeOutputStream outputStream1 = new FakeOutputStream();
FakeOutputStream outputStream2 = new FakeOutputStream();
FakeOutputStream outputStream3 = new FakeOutputStream();
ConsoleHandler handler =
new ConsoleHandler(
ConsoleHandler.utf8OutputStreamWriter(outputStream1),
new MessageOnlyFormatter(),
Level.INFO,
state);
threadIdToCommandId.put(49152L, "commandIdForOutputStream2");
threadIdToCommandId.put(49153L, "commandIdForOutputStream2");
threadIdToCommandId.put(64738L, "commandIdForOutputStream3");
registerOutputStream("commandIdForOutputStream2", outputStream2);
registerOutputStream("commandIdForOutputStream3", outputStream3);
publishAndFlush(handler, newLogRecordWithThreadId(Level.INFO, "Stream 2", 49152));
assertThat(outputStream1.toString("UTF-8"), equalTo(""));
assertThat(outputStream2.toString("UTF-8"), equalTo("Stream 2"));
assertThat(outputStream3.toString("UTF-8"), equalTo(""));
publishAndFlush(handler, newLogRecordWithThreadId(Level.INFO, " - Another Stream 2", 49153));
assertThat(outputStream1.toString("UTF-8"), equalTo(""));
assertThat(outputStream2.toString("UTF-8"), equalTo("Stream 2 - Another Stream 2"));
assertThat(outputStream3.toString("UTF-8"), equalTo(""));
}
@Test
public void previouslyRegisteredOutputStreamCanBeOverridden() throws IOException {
FakeOutputStream outputStream1 = new FakeOutputStream();
FakeOutputStream outputStream2 = new FakeOutputStream();
FakeOutputStream outputStream3 = new FakeOutputStream();
ConsoleHandler handler =
new ConsoleHandler(
ConsoleHandler.utf8OutputStreamWriter(outputStream1),
new MessageOnlyFormatter(),
Level.INFO,
state);
threadIdToCommandId.put(49152L, "commandIdForOutputStream2");
registerOutputStream("commandIdForOutputStream2", outputStream2);
publishAndFlush(handler, newLogRecordWithThreadId(Level.INFO, "Stream 2", 49152));
assertThat(outputStream1.toString("UTF-8"), equalTo(""));
assertThat(outputStream2.toString("UTF-8"), equalTo("Stream 2"));
registerOutputStream("commandIdForOutputStream2", outputStream3);
publishAndFlush(handler, newLogRecordWithThreadId(Level.INFO, "Stream 3", 49152));
assertThat(outputStream1.toString("UTF-8"), equalTo(""));
assertThat(outputStream2.toString("UTF-8"), equalTo("Stream 2"));
assertThat(outputStream3.toString("UTF-8"), equalTo("Stream 3"));
}
@Test
public void levelOverrideAppliesOnlyToRegisteredStream() throws IOException {
FakeOutputStream outputStream1 = new FakeOutputStream();
FakeOutputStream outputStream2 = new FakeOutputStream();
FakeOutputStream outputStream3 = new FakeOutputStream();
ConsoleHandler handler =
new ConsoleHandler(
ConsoleHandler.utf8OutputStreamWriter(outputStream1),
new MessageOnlyFormatter(),
Level.INFO,
state);
threadIdToCommandId.put(49152L, "commandIdForOutputStream2");
threadIdToCommandId.put(64738L, "commandIdForOutputStream3");
registerOutputStream("commandIdForOutputStream2", outputStream2);
registerOutputStream("commandIdForOutputStream3", outputStream3);
publishAndFlush(handler, newLogRecordWithThreadId(Level.FINE, "Shh..", 49152));
assertThat(outputStream1.toString("UTF-8"), equalTo(""));
assertThat(outputStream2.toString("UTF-8"), equalTo(""));
assertThat(outputStream3.toString("UTF-8"), equalTo(""));
publishAndFlush(handler, newLogRecordWithThreadId(Level.FINE, "Shh..", 64738));
assertThat(outputStream1.toString("UTF-8"), equalTo(""));
assertThat(outputStream2.toString("UTF-8"), equalTo(""));
assertThat(outputStream3.toString("UTF-8"), equalTo(""));
commandIdToLevel.put("commandIdForOutputStream3", Level.ALL);
publishAndFlush(handler, newLogRecordWithThreadId(Level.FINE, "Stream 3", 64738));
assertThat(outputStream1.toString("UTF-8"), equalTo(""));
assertThat(outputStream2.toString("UTF-8"), equalTo(""));
assertThat(outputStream3.toString("UTF-8"), equalTo("Stream 3"));
}
@Test
public void levelOverrideCanBeRemoved() throws IOException {
FakeOutputStream outputStream1 = new FakeOutputStream();
FakeOutputStream outputStream2 = new FakeOutputStream();
FakeOutputStream outputStream3 = new FakeOutputStream();
ConsoleHandler handler =
new ConsoleHandler(
ConsoleHandler.utf8OutputStreamWriter(outputStream1),
new MessageOnlyFormatter(),
Level.INFO,
state);
threadIdToCommandId.put(49152L, "commandIdForOutputStream2");
threadIdToCommandId.put(64738L, "commandIdForOutputStream3");
registerOutputStream("commandIdForOutputStream2", outputStream2);
registerOutputStream("commandIdForOutputStream3", outputStream3);
commandIdToLevel.put("commandIdForOutputStream3", Level.FINE);
publishAndFlush(handler, newLogRecordWithThreadId(Level.FINE, "Stream 3", 64738));
assertThat(outputStream1.toString("UTF-8"), equalTo(""));
assertThat(outputStream2.toString("UTF-8"), equalTo(""));
assertThat(outputStream3.toString("UTF-8"), equalTo("Stream 3"));
commandIdToLevel.remove("commandIdForOutputStream3");
publishAndFlush(handler, newLogRecordWithThreadId(Level.FINE, "Shh...", 64738));
assertThat(outputStream1.toString("UTF-8"), equalTo(""));
assertThat(outputStream2.toString("UTF-8"), equalTo(""));
assertThat(outputStream3.toString("UTF-8"), equalTo("Stream 3"));
}
@Test
public void logMessageWithUnregisteredThreadIdGoesToAllConsoles() throws IOException {
FakeOutputStream outputStream1 = new FakeOutputStream();
FakeOutputStream outputStream2 = new FakeOutputStream();
FakeOutputStream outputStream3 = new FakeOutputStream();
ConsoleHandler handler =
new ConsoleHandler(
ConsoleHandler.utf8OutputStreamWriter(outputStream1),
new MessageOnlyFormatter(),
Level.INFO,
state);
threadIdToCommandId.put(49152L, "commandIdForOutputStream2");
threadIdToCommandId.put(64738L, "commandIdForOutputStream3");
registerOutputStream("commandIdForOutputStream2", outputStream2);
registerOutputStream("commandIdForOutputStream3", outputStream3);
publishAndFlush(handler, newLogRecordWithThreadId(Level.INFO, "What thread is this?", 999999));
assertThat(outputStream1.toString("UTF-8"), equalTo(""));
assertThat(outputStream2.toString("UTF-8"), equalTo("What thread is this?"));
assertThat(outputStream3.toString("UTF-8"), equalTo("What thread is this?"));
}
private static void publishAndFlush(Handler handler, LogRecord logRecord) {
handler.publish(logRecord);
handler.flush();
}
private static LogRecord newLogRecordWithThreadId(Level level, String contents, int threadId) {
LogRecord result = new LogRecord(level, contents);
result.setThreadID(threadId);
return result;
}
}