You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@aurora.apache.org by zm...@apache.org on 2015/08/25 20:19:21 UTC

[07/37] aurora git commit: Import of Twitter Commons.

http://git-wip-us.apache.org/repos/asf/aurora/blob/86a547b9/commons/src/test/java/com/twitter/common/logging/RootLogConfigTest.java
----------------------------------------------------------------------
diff --git a/commons/src/test/java/com/twitter/common/logging/RootLogConfigTest.java b/commons/src/test/java/com/twitter/common/logging/RootLogConfigTest.java
new file mode 100644
index 0000000..b47a2cd
--- /dev/null
+++ b/commons/src/test/java/com/twitter/common/logging/RootLogConfigTest.java
@@ -0,0 +1,320 @@
+package com.twitter.common.logging;
+
+import java.io.ByteArrayOutputStream;
+import java.io.PrintStream;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+import java.util.UUID;
+import java.util.logging.ConsoleHandler;
+import java.util.logging.Handler;
+import java.util.logging.Level;
+import java.util.logging.LogRecord;
+import java.util.logging.Logger;
+
+import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableMap;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * @author Franco Callari
+ * @author Keith Tsui.
+ */
+public class RootLogConfigTest {
+  private static class FakeHandler extends Handler {
+    boolean hasPublished = false;
+    FakeHandler() { reset(); }
+    void reset() { hasPublished = false; }
+    public void publish(LogRecord record) { hasPublished = true; }
+    public void flush() {}
+    public void close() throws SecurityException {}
+  }
+
+  // Dummy classes used by the tests for --vmodule.
+  private static class ClassA {
+    static Logger logger = Logger.getLogger(ClassA.class.getName());
+  }
+
+  private static class ClassB {
+    static Logger logger = Logger.getLogger(ClassB.class.getName());
+  }
+
+  private ByteArrayOutputStream fakeErrorLog;
+  private PrintStream errPrintStream;
+  Logger fakeRootLogger, testLogger;
+  FakeHandler fakeFileLog;
+  ConsoleHandler consoleHandler;
+
+  private void assertHasLoggedToStderr() {
+    errPrintStream.flush();
+    assertTrue(fakeErrorLog.size() > 0);
+  }
+
+  private void assertHasNotLoggedToStderr() {
+    errPrintStream.flush();
+    assertEquals(fakeErrorLog.size(), 0);
+  }
+
+  private void assertHasLoggedToFile() {
+    assertTrue(fakeFileLog.hasPublished);
+  }
+
+  private void assertHasNotLoggedToFile() {
+    assertFalse(fakeFileLog.hasPublished);
+  }
+
+  // Passes if logger log at least at the given level, directing to stderr.
+  private void assertErrorLogAtLevel(Logger logger, Level level) {
+    List<Level> levels = Arrays.asList(
+        Level.SEVERE, Level.WARNING, Level.INFO, Level.CONFIG,
+        Level.FINE, Level.FINER, Level.FINEST);
+    for (Level l : levels) {
+      logger.log(l, "Message");
+      if (level.intValue() <= l.intValue()) {
+        assertHasLoggedToStderr();
+      } else {
+        assertHasNotLoggedToStderr();
+      }
+      resetLogs();
+    }
+  }
+
+  // Passes if logger does not allow verbose logging.
+  private void assertNoVerboseLogging(Logger logger) {
+    logger.config("Config");
+    logger.fine("Fine");
+    logger.finer("Finer");
+    logger.finest("Finest");
+    assertHasNotLoggedToStderr();
+    assertHasNotLoggedToFile();
+  }
+
+  private void resetLogs() {
+    errPrintStream.flush();
+    fakeErrorLog.reset();
+    fakeFileLog.reset();
+  }
+
+  // The following two methods are used to inject our fake root logger, so to avoid test flakyness
+  // due to background threads.
+  private RootLogConfig.Builder getConfig() {
+    return RootLogConfig.builder().rootLoggerName(fakeRootLogger.getName());
+  }
+
+  private void setFakeRootForLogger(Logger logger) {
+    Preconditions.checkArgument(logger != fakeRootLogger);
+    logger.setUseParentHandlers(true);
+    logger.setParent(fakeRootLogger);
+  }
+
+  @Before
+  public void setUp() {
+    // Intercept stderr (this must be done first).
+    fakeErrorLog = new ByteArrayOutputStream();
+    errPrintStream = new PrintStream(fakeErrorLog);
+    System.setErr(errPrintStream);
+
+    // Create other members
+    consoleHandler = new ConsoleHandler();
+    fakeFileLog = new FakeHandler();
+
+    // Emulate default setup (just a console handler), but avoiding the use
+    // of the global root logger so not to get a flaky test due to background threads.
+    fakeRootLogger = Logger.getLogger("FakeRoot-" + UUID.randomUUID().toString());
+    fakeRootLogger.setUseParentHandlers(false);
+    for (Handler h : fakeRootLogger.getHandlers()) {
+      fakeRootLogger.removeHandler(h);
+    }
+    fakeRootLogger.addHandler(consoleHandler);
+
+    testLogger = Logger.getLogger(RootLogConfigTest.class.getName());
+    testLogger.setUseParentHandlers(true);
+    testLogger.setParent(fakeRootLogger);
+
+    setFakeRootForLogger(ClassA.logger);
+    setFakeRootForLogger(ClassB.logger);
+
+    resetLogs();
+  }
+
+  @Test
+  public void testDefaultConfig() {
+    // Verify that default info, warning, severe logging goes to stderr, no logging below info.
+    assertErrorLogAtLevel(testLogger, Level.INFO);
+    resetLogs();
+    assertNoVerboseLogging(testLogger);
+  }
+
+  @Test
+  public void testLogToStderr() {
+    // Add a fake handler, verify that it works along with the console.
+    fakeRootLogger.addHandler(fakeFileLog);
+    testLogger.info("Info");
+    assertHasLoggedToStderr();
+    assertHasLoggedToFile();
+
+    // Configure logtostderr
+    getConfig().logToStderr(true).build().apply();
+    resetLogs();
+
+    // Verify that severe, warning, info logs go to stderr only.
+    testLogger.severe("Severe");
+    assertHasLoggedToStderr();
+    assertHasNotLoggedToFile();
+    resetLogs();
+    testLogger.warning("Warning");
+    assertHasLoggedToStderr();
+    assertHasNotLoggedToFile();
+    resetLogs();
+    testLogger.info("Info");
+    assertHasLoggedToStderr();
+    assertHasNotLoggedToFile();
+    resetLogs();
+
+    assertNoVerboseLogging(testLogger);
+  }
+
+  @Test
+  public void testAlsoLogToStderr() {
+    // Add a fake handler, remove console handler, verify that it works.
+    fakeRootLogger.removeHandler(consoleHandler);
+    fakeRootLogger.addHandler(fakeFileLog);
+    testLogger.info("Info");
+    assertHasNotLoggedToStderr();
+    assertHasLoggedToFile();
+    resetLogs();
+
+    // Configure alsologtostderr
+    getConfig().alsoLogToStderr(true).build().apply();
+    resetLogs();
+
+    // Verify that severe, warning, info logs go to both.
+    testLogger.severe("Severe");
+    assertHasLoggedToStderr();
+    assertHasLoggedToFile();
+    resetLogs();
+    testLogger.warning("Warning");
+    assertHasLoggedToStderr();
+    assertHasLoggedToFile();
+    resetLogs();
+    testLogger.info("Info");
+    assertHasLoggedToStderr();
+    assertHasLoggedToFile();
+    resetLogs();
+
+    assertNoVerboseLogging(testLogger);
+  }
+
+  @Test
+  public void testLogToStderrOverridesAlsoLogToStderr() {
+    // Add a fake handler, remove console handler, verify that it works.
+    fakeRootLogger.removeHandler(consoleHandler);
+    fakeRootLogger.addHandler(fakeFileLog);
+    testLogger.info("Info");
+    assertHasNotLoggedToStderr();
+    assertHasLoggedToFile();
+
+    // Configure with logtostderr AND alsologtostderr
+    getConfig().logToStderr(true).alsoLogToStderr(true).build().apply();
+    resetLogs();
+
+    // Verify that severe, warning, info logs go to stderr only.
+    testLogger.severe("Severe");
+    assertHasLoggedToStderr();
+    assertHasNotLoggedToFile();
+    resetLogs();
+    testLogger.warning("Warning");
+    assertHasLoggedToStderr();
+    assertHasNotLoggedToFile();
+    resetLogs();
+    testLogger.info("Info");
+    assertHasLoggedToStderr();
+    assertHasNotLoggedToFile();
+    resetLogs();
+
+    assertNoVerboseLogging(testLogger);
+  }
+
+  @Test
+  public void testUseGLogFormatter() {
+    // Configure glogformatter. We test in "logtostderr" mode so to verify correct formatting
+    // for both handlers.
+    getConfig().logToStderr(true).useGLogFormatter(true).build().apply();
+    resetLogs();
+
+    testLogger.severe("Severe Log Message");
+    assertHasLoggedToStderr();
+    String output = fakeErrorLog.toString();
+    // Verify that it is all in one line and chope the \n.
+    assertTrue(output.split("\n").length == 1);
+    assertTrue(output.endsWith("\n"));
+    output = output.replaceAll("\n", "");
+
+    // Verify that it is on glog format.
+    assertTrue("Unexpected output: " + output,
+        output.matches("E\\d+ " // Level, month, day.
+            + "\\d\\d:\\d\\d:\\d\\d\\.\\d+ " // Timestamp.
+            + "THREAD\\d+ " // Thread id.
+            + RootLogConfigTest.class.getName() + "\\.testUseGLogFormatter: " // Class name.
+            + "Severe Log Message" // Message.
+        ));
+  }
+
+  @Test
+  public void testVlog() {
+    // Configure with logtoStderr and vlog==FINE;
+    getConfig().logToStderr(true).vlog(RootLogConfig.LogLevel.FINE).build().apply();
+    resetLogs();
+
+    // Verify logging at levels fine and above.
+    assertErrorLogAtLevel(testLogger, Level.FINE);
+  }
+
+  @Test
+  public void testVModule() {
+    // Configure with ClassA using FINE and ClassB using WARNING;
+    Map<Class<?>, RootLogConfig.LogLevel> vmoduleMap =
+        ImmutableMap.of(ClassA.class, RootLogConfig.LogLevel.FINE,
+                        ClassB.class, RootLogConfig.LogLevel.WARNING);
+    getConfig().logToStderr(true).vmodule(vmoduleMap).build().apply();
+    resetLogs();
+
+    // No verbose logging other than in ClassA and ClassB.
+    assertNoVerboseLogging(testLogger);
+
+    // ClassA logs at FINE and above
+    assertErrorLogAtLevel(ClassA.logger, Level.FINE);
+    resetLogs();
+
+    // ClassB logs at WARNING and above
+    assertErrorLogAtLevel(ClassB.logger, Level.WARNING);
+  }
+
+  @Test
+  public void testVModuleOverridesVlog() {
+    // Configure with ClassA using FINE and ClassB using FINER;
+    Map<Class<?>, RootLogConfig.LogLevel> vmoduleMap =
+        ImmutableMap.of(ClassA.class, RootLogConfig.LogLevel.FINEST,
+                        ClassB.class, RootLogConfig.LogLevel.INFO);
+    // Configure setting default vlog=FINER
+    getConfig()
+        .logToStderr(true).vlog(RootLogConfig.LogLevel.FINER).vmodule(vmoduleMap).build().apply();
+    resetLogs();
+
+    // Default logging is at finer level.
+    assertErrorLogAtLevel(testLogger, Level.FINER);
+
+    // ClassA logs at FINEST and above
+    assertErrorLogAtLevel(ClassA.logger, Level.FINEST);
+    resetLogs();
+
+    // ClassB logs at INFO and above
+    assertErrorLogAtLevel(ClassB.logger, Level.INFO);
+  }
+}

http://git-wip-us.apache.org/repos/asf/aurora/blob/86a547b9/commons/src/test/java/com/twitter/common/logging/julbridge/JULBridgeHandlerTest.java
----------------------------------------------------------------------
diff --git a/commons/src/test/java/com/twitter/common/logging/julbridge/JULBridgeHandlerTest.java b/commons/src/test/java/com/twitter/common/logging/julbridge/JULBridgeHandlerTest.java
new file mode 100644
index 0000000..621a333
--- /dev/null
+++ b/commons/src/test/java/com/twitter/common/logging/julbridge/JULBridgeHandlerTest.java
@@ -0,0 +1,100 @@
+// =================================================================================================
+// Copyright 2013 Twitter, Inc.
+// -------------------------------------------------------------------------------------------------
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this work except in compliance with the License.
+// You may obtain a copy of the License in the LICENSE file, or 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.twitter.common.logging.julbridge;
+
+import java.util.ListResourceBundle;
+import java.util.ResourceBundle;
+import java.util.logging.Level;
+import java.util.logging.LogRecord;
+
+import org.apache.log4j.Category;
+import org.apache.log4j.Logger;
+import org.apache.log4j.spi.LocationInfo;
+import org.apache.log4j.spi.LoggingEvent;
+import org.junit.Test;
+
+import com.twitter.common.logging.julbridge.JULBridgeLevelConverter;
+import com.twitter.common.logging.julbridge.JULBridgeHandler;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.CoreMatchers.sameInstance;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+public class JULBridgeHandlerTest {
+
+  @Test
+  public void checkMessageWithParametersIsFormatted() {
+    LogRecord record = new LogRecord(Level.FINEST, "test is {0}");
+    record.setParameters(new Object[] {"successful"});
+
+    assertThat(JULBridgeHandler.formatMessage(record), is("test is successful"));
+  }
+
+  @Test
+  public void checkMessageWithResourceBundleIsFormatted() {
+    ResourceBundle bundle = new ListResourceBundle() {
+      @Override protected Object[][] getContents() {
+        return new Object[][] {
+            {"test is successful", "le test fonctionne"}
+        };
+      }
+    };
+
+    LogRecord record = new LogRecord(Level.FINEST, "test is successful");
+    record.setResourceBundle(bundle);
+
+    assertThat(JULBridgeHandler.formatMessage(record), is("le test fonctionne"));
+  }
+
+  @Test
+  public void checkGetLoggerReturnsLoggerWithSameName() {
+    LogRecord record = new LogRecord(Level.FINEST, "test message");
+    record.setLoggerName("test.checkGetLogger");
+
+    assertThat(new JULBridgeHandler().getLogger(record).getName(), is("test.checkGetLogger"));
+  }
+
+  @Test
+  public void checkToLoggingEvent() {
+    LogRecord record = new LogRecord(Level.FINEST, "test is {0}");
+    record.setParameters(new Object[] {"successful"});
+
+    record.setThreadID(42);
+    Throwable t = new Throwable();
+    record.setThrown(t);
+
+    // source class and method names are usually inferred, but because there's no JUL in the stack
+    // frame, it won't work as expected.
+    record.setSourceClassName(getClass().getName());
+    record.setSourceMethodName("checkToLoggingEvent");
+
+    Logger log4jLogger = new JULBridgeHandler().getLogger(record);
+    org.apache.log4j.Level log4jLevel = JULBridgeLevelConverter.toLog4jLevel(Level.FINEST);
+    LoggingEvent event = JULBridgeHandler.toLoggingEvent(record, log4jLogger, log4jLevel, false);
+
+    assertThat(event.getLogger(), is((Category) log4jLogger));
+    assertThat(event.getLevel(), is(log4jLevel));
+    assertThat(event.getMessage(), is((Object) "test is successful"));
+    assertThat(event.getThreadName(), is("42"));
+    assertThat(event.getTimeStamp(), is(record.getMillis()));
+    assertThat(event.getThrowableInformation().getThrowable(), is(sameInstance(t)));
+
+    LocationInfo info = event.getLocationInformation();
+    assertThat(info.getClassName(), is(getClass().getName()));
+    assertThat(info.getMethodName(), is("checkToLoggingEvent"));
+  }
+}

http://git-wip-us.apache.org/repos/asf/aurora/blob/86a547b9/commons/src/test/java/com/twitter/common/logging/julbridge/JULBridgeLevelConverterTest.java
----------------------------------------------------------------------
diff --git a/commons/src/test/java/com/twitter/common/logging/julbridge/JULBridgeLevelConverterTest.java b/commons/src/test/java/com/twitter/common/logging/julbridge/JULBridgeLevelConverterTest.java
new file mode 100644
index 0000000..17428d8
--- /dev/null
+++ b/commons/src/test/java/com/twitter/common/logging/julbridge/JULBridgeLevelConverterTest.java
@@ -0,0 +1,83 @@
+package com.twitter.common.logging.julbridge;
+
+import java.util.Arrays;
+import java.util.logging.Level;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+import com.twitter.common.logging.julbridge.JULBridgeLevelConverter;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+public class JULBridgeLevelConverterTest {
+
+  @RunWith(Parameterized.class)
+  public static class JULMappingToLog4JTest {
+    @SuppressWarnings("serial")
+    @Parameters
+    public static Iterable<Object[]> data() {
+      return Arrays.asList(new Object[][] {
+          { Level.FINEST,  org.apache.log4j.Level.TRACE },
+          { Level.FINER,   org.apache.log4j.Level.DEBUG },
+          { Level.FINE,    org.apache.log4j.Level.DEBUG },
+          { Level.INFO,    org.apache.log4j.Level.INFO },
+          { Level.WARNING, org.apache.log4j.Level.WARN },
+          { Level.SEVERE,  org.apache.log4j.Level.ERROR },
+          { Level.ALL,     org.apache.log4j.Level.ALL },
+          { Level.OFF,     org.apache.log4j.Level.OFF },
+          // Unknown level should map to DEBUG
+          { new Level("test", 42) {}, org.apache.log4j.Level.DEBUG }
+      });
+    }
+
+    private final Level level;
+    private final org.apache.log4j.Level expected;
+
+    public JULMappingToLog4JTest(Level level, org.apache.log4j.Level expected) {
+      this.level = level;
+      this.expected = expected;
+    }
+
+    @Test
+    public void checkJULMapsToLog4J() {
+      assertThat(JULBridgeLevelConverter.toLog4jLevel(level), is(expected));
+    }
+  }
+
+  @RunWith(Parameterized.class)
+  public static class Log4JMappingToJULTest {
+    @SuppressWarnings("serial")
+    @Parameters
+    public static Iterable<Object[]> data() {
+      return Arrays.asList(new Object[][] {
+          { org.apache.log4j.Level.TRACE, Level.FINEST },
+          { org.apache.log4j.Level.DEBUG, Level.FINE },
+          { org.apache.log4j.Level.INFO,  Level.INFO },
+          { org.apache.log4j.Level.WARN,  Level.WARNING },
+          { org.apache.log4j.Level.ERROR, Level.SEVERE },
+          { org.apache.log4j.Level.FATAL, Level.SEVERE },
+          { org.apache.log4j.Level.ALL,   Level.ALL },
+          { org.apache.log4j.Level.OFF,   Level.OFF },
+          // Unknown level should map to FINE
+          { new org.apache.log4j.Level(42, "test", 42) {}, Level.FINE }
+      });
+    }
+
+    private final org.apache.log4j.Level level;
+    private final Level expected;
+
+    public Log4JMappingToJULTest(org.apache.log4j.Level level, Level expected) {
+      this.level = level;
+      this.expected = expected;
+    }
+
+    @Test
+    public void checkJULMapsToLog4J() {
+      assertThat(JULBridgeLevelConverter.fromLog4jLevel(level), is(expected));
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/aurora/blob/86a547b9/commons/src/test/java/com/twitter/common/logging/julbridge/JULBridgeLogManagerTest.java
----------------------------------------------------------------------
diff --git a/commons/src/test/java/com/twitter/common/logging/julbridge/JULBridgeLogManagerTest.java b/commons/src/test/java/com/twitter/common/logging/julbridge/JULBridgeLogManagerTest.java
new file mode 100644
index 0000000..c518d89
--- /dev/null
+++ b/commons/src/test/java/com/twitter/common/logging/julbridge/JULBridgeLogManagerTest.java
@@ -0,0 +1,78 @@
+// =================================================================================================
+// Copyright 2013 Twitter, Inc.
+// -------------------------------------------------------------------------------------------------
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this work except in compliance with the License.
+// You may obtain a copy of the License in the LICENSE file, or 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.twitter.common.logging.julbridge;
+
+import java.io.IOException;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.logging.Level;
+import java.util.logging.LogManager;
+import java.util.logging.Logger;
+
+import org.apache.log4j.AppenderSkeleton;
+import org.apache.log4j.Hierarchy;
+import org.apache.log4j.spi.LoggerRepository;
+import org.apache.log4j.spi.LoggingEvent;
+import org.apache.log4j.spi.RootLogger;
+import org.junit.After;
+import org.junit.Test;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+public class JULBridgeLogManagerTest {
+
+  @After
+  public void restoreLogConfiguration() throws SecurityException, IOException {
+    LogManager.getLogManager().readConfiguration();
+  }
+
+  @Test
+  public void checkAssimilateTakesOver() {
+    // Create a test log4j environment
+    final List<LoggingEvent> events = new LinkedList<LoggingEvent>();
+
+    org.apache.log4j.Logger log4jRoot = new RootLogger(org.apache.log4j.Level.ALL);
+    LoggerRepository loggerRepository = new Hierarchy(log4jRoot);
+    loggerRepository.setThreshold(org.apache.log4j.Level.INFO);
+
+    log4jRoot.addAppender(new AppenderSkeleton() {
+      @Override public boolean requiresLayout() {
+        return false;
+      }
+
+      @Override public void close() {}
+
+      @Override protected void append(LoggingEvent event) {
+        events.add(event);
+      }
+    });
+
+
+    JULBridgeLogManager.assimilate(loggerRepository);
+
+    Logger.getLogger("test.1").log(Level.INFO, "test message 1");
+    Logger.getLogger("test.2").log(Level.FINE, "test message 2");
+    Logger.getLogger("test.3").log(Level.WARNING, "test message 3");
+
+    assertThat(events.size(), is(2));
+    assertThat(events.get(0).getLoggerName(), is("test.1"));
+    assertThat(events.get(0).getMessage(), is((Object) "test message 1"));
+    assertThat(events.get(1).getLoggerName(), is("test.3"));
+    assertThat(events.get(1).getMessage(), is((Object) "test message 3"));
+  }
+}

http://git-wip-us.apache.org/repos/asf/aurora/blob/86a547b9/commons/src/test/java/com/twitter/common/net/InetSocketAddressHelperTest.java
----------------------------------------------------------------------
diff --git a/commons/src/test/java/com/twitter/common/net/InetSocketAddressHelperTest.java b/commons/src/test/java/com/twitter/common/net/InetSocketAddressHelperTest.java
new file mode 100644
index 0000000..b90c8ca
--- /dev/null
+++ b/commons/src/test/java/com/twitter/common/net/InetSocketAddressHelperTest.java
@@ -0,0 +1,114 @@
+// =================================================================================================
+// Copyright 2011 Twitter, Inc.
+// -------------------------------------------------------------------------------------------------
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this work except in compliance with the License.
+// You may obtain a copy of the License in the LICENSE file, or 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.twitter.common.net;
+
+import org.junit.Test;
+
+import java.net.InetSocketAddress;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+/**
+ * @author John Sirois
+ */
+public class InetSocketAddressHelperTest {
+
+  @Test
+  public void testParseValueInvalid() {
+    try {
+      InetSocketAddressHelper.parse(null);
+      fail();
+    } catch (NullPointerException e) {
+      // expected
+    }
+
+    try {
+      InetSocketAddressHelper.parse("");
+      fail();
+    } catch (IllegalArgumentException e) {
+      // expected
+    }
+
+    try {
+      InetSocketAddressHelper.parse(":");
+      fail();
+    } catch (IllegalArgumentException e) {
+      // expected
+    }
+
+    try {
+      InetSocketAddressHelper.parse("*:");
+      fail();
+    } catch (IllegalArgumentException e) {
+      // expected
+    }
+
+    try {
+      InetSocketAddressHelper.parse(":jake");
+      fail();
+    } catch (IllegalArgumentException e) {
+      // expected
+    }
+
+    try {
+      InetSocketAddressHelper.parse(":70000");
+      fail();
+    } catch (IllegalArgumentException e) {
+      // expected
+    }
+
+    try {
+      InetSocketAddressHelper.parse("localhost:");
+      fail();
+    } catch (IllegalArgumentException e) {
+      // expected
+    }
+  }
+
+  @Test
+  public void testParseArgValuePort() {
+    assertEquals(new InetSocketAddress(6666), InetSocketAddressHelper.parse(":6666"));
+    assertEquals(new InetSocketAddress(0), InetSocketAddressHelper.parse(":*"));
+  }
+
+  @Test
+  public void testParseArgValueHostPort() {
+    assertEquals(InetSocketAddress.createUnresolved("localhost", 5555),
+        InetSocketAddressHelper.parse("localhost:5555"));
+
+    assertEquals(InetSocketAddress.createUnresolved("127.0.0.1", 4444),
+        InetSocketAddressHelper.parse("127.0.0.1:4444"));
+  }
+
+  @Test
+  public void testInetSocketAddressToServerString() {
+    assertEquals("localhost:8000",
+        InetSocketAddressHelper.toString(InetSocketAddress.createUnresolved("localhost", 8000)));
+
+    assertEquals("foo.bar.baz:8000",
+        InetSocketAddressHelper.toString(InetSocketAddress.createUnresolved("foo.bar.baz", 8000)));
+
+    assertEquals("127.0.0.1:8000",
+        InetSocketAddressHelper.toString(InetSocketAddress.createUnresolved("127.0.0.1", 8000)));
+
+    assertEquals("10.0.0.1:8000",
+        InetSocketAddressHelper.toString(InetSocketAddress.createUnresolved("10.0.0.1", 8000)));
+
+    assertEquals("0.0.0.0:80", InetSocketAddressHelper.toString(new InetSocketAddress(80)));
+  }
+}

http://git-wip-us.apache.org/repos/asf/aurora/blob/86a547b9/commons/src/test/java/com/twitter/common/net/UrlHelperTest.java
----------------------------------------------------------------------
diff --git a/commons/src/test/java/com/twitter/common/net/UrlHelperTest.java b/commons/src/test/java/com/twitter/common/net/UrlHelperTest.java
new file mode 100644
index 0000000..0bc02e8
--- /dev/null
+++ b/commons/src/test/java/com/twitter/common/net/UrlHelperTest.java
@@ -0,0 +1,123 @@
+// =================================================================================================
+// Copyright 2011 Twitter, Inc.
+// -------------------------------------------------------------------------------------------------
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this work except in compliance with the License.
+// You may obtain a copy of the License in the LICENSE file, or 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.twitter.common.net;
+
+import com.google.common.collect.ImmutableList;
+import org.junit.Test;
+
+import java.net.URISyntaxException;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+/**
+ * @author John Sirois
+ */
+public class UrlHelperTest {
+  @Test
+  public void testGetDomain() {
+    assertEquals("www.twitter.com", UrlHelper.getDomain("www.twitter.com"));
+    assertEquals("www.twitter.com", UrlHelper.getDomain("www.twitter.com/a/b/c?foo=bar&bar=baz"));
+
+    assertEquals("www.bign.com",
+        UrlHelper.getDomain("www.bign.com/davidvandiverhttp://cli.gs/da0e0"));
+    assertEquals("www.thesun.co.uk",
+        UrlHelper.getDomain("www.thesun.co.uk/sol/homepa-http://dragtotop.com/thesun.co.uk"));
+    assertEquals("www.formspring.me",
+        UrlHelper.getDomain("www.formspring.me/chuuworangerrhttp://bit.ly/7pydt3"));
+    assertEquals("www.bign.com",
+        UrlHelper.getDomain("www.bign.com/davidvandiverhttp://cli.gs/da0e0"));
+    assertEquals("www.roundplace.com",
+        UrlHelper.getDomain("www.roundplace.com/en/watch/108/3/"
+                            + "baltimore-rave-http://dragtotop.com/patriots_vs_ravens"));
+    assertEquals("www.bign.com",
+        UrlHelper.getDomain("www.bign.com/davidvandiverhttp://cli.gs/da0e0"));
+    assertEquals(null, UrlHelper.getDomain("http://?idonthaveadomain=true"));
+    assertEquals(null, UrlHelper.getDomain(":::<<<<<::IAMNOTAVALIDURIā€œ"));
+  }
+
+  @Test
+  public void testGetDomainChecked() throws Exception {
+    assertEquals("www.twitter.com", UrlHelper.getDomainChecked("http://www.twitter.com"));
+    assertEquals("www.twitter.com", UrlHelper.getDomainChecked("https://www.twitter.com/?a=b"));
+    assertEquals(null, UrlHelper.getDomainChecked("http://?idonthaveadomain=true"));
+    try {
+      UrlHelper.getDomainChecked(":::<<<<<::IAMNOTAVALIDURIā€œ");
+      fail();
+    } catch (URISyntaxException e) {
+      // Expected
+    }
+  }
+
+  @Test
+  public void testGetPath() {
+    assertEquals("", UrlHelper.getPath("www.twitter.com"));
+    assertEquals("/", UrlHelper.getPath("www.twitter.com/"));
+    assertEquals("/foo", UrlHelper.getPath("http://www.twitter.com/foo"));
+    assertEquals("/bar", UrlHelper.getPath("https://www.twitter.com/bar"));
+    assertEquals("/a/b/c", UrlHelper.getPath("www.twitter.com/a/b/c"));
+
+    assertEquals("/davidvandiverhttp://cli.gs/da0e0",
+        UrlHelper.getPath("www.bign.com/davidvandiverhttp://cli.gs/da0e0"));
+    assertEquals("/sol/homepa-http://dragtotop.com/thesun.co.uk",
+        UrlHelper.getPath("www.thesun.co.uk/sol/homepa-http://dragtotop.com/thesun.co.uk"));
+    assertEquals("/chuuworangerrhttp://bit.ly/7pydt3",
+        UrlHelper.getPath("www.formspring.me/chuuworangerrhttp://bit.ly/7pydt3"));
+    assertEquals("/davidvandiverhttp://cli.gs/da0e0",
+        UrlHelper.getPath("www.bign.com/davidvandiverhttp://cli.gs/da0e0"));
+    assertEquals("/en/watch/10855/3/baltimore-rave-http://dragtotop.com/patriots_vs_ravens",
+        UrlHelper.getPath("www.roundplace.com/en/watch/10855/3/"
+                            + "baltimore-rave-http://dragtotop.com/patriots_vs_ravens"));
+    assertEquals("/davidvandiverhttp://cli.gs/da0e0",
+        UrlHelper.getPath("www.bign.com/davidvandiverhttp://cli.gs/da0e0"));
+  }
+
+  @Test
+  public void testAddProtocol() {
+    assertEquals("http://www.twitter.com", UrlHelper.addProtocol("www.twitter.com"));
+    assertEquals("http://www.twitter.com", UrlHelper.addProtocol("http://www.twitter.com"));
+    assertEquals("https://www.twitter.com", UrlHelper.addProtocol("https://www.twitter.com"));
+
+    assertEquals("http://www.twitter.com/this/is/a/http://stange/but/valid/path",
+        UrlHelper.addProtocol("http://www.twitter.com/this/is/a/http://stange/but/valid/path"));
+    assertEquals("http://www.twitter.com/this/is/a/http://stange/but/valid/path",
+        UrlHelper.addProtocol("www.twitter.com/this/is/a/http://stange/but/valid/path"));
+  }
+
+  @Test
+  public void testStripUrlParameters() {
+    assertEquals("www.twitter.com", UrlHelper.stripUrlParameters("www.twitter.com"));
+    assertEquals("www.twitter.com", UrlHelper.stripUrlParameters("www.twitter.com?foo-bar"));
+    assertEquals("www.twitter.com/a/b/",
+        UrlHelper.stripUrlParameters("www.twitter.com/a/b/?foo-bar"));
+
+    assertEquals("http://www.twitter.com", UrlHelper.stripUrlParameters("http://www.twitter.com"));
+    assertEquals("http://www.twitter.com",
+        UrlHelper.stripUrlParameters("http://www.twitter.com?foo=bar"));
+    assertEquals("http://www.twitter.com/a",
+        UrlHelper.stripUrlParameters("http://www.twitter.com/a?foo=bar"));
+  }
+
+  @Test
+  public void testGetDomainLevels() {
+    assertEquals(ImmutableList.of("www.fred", "fred"), UrlHelper.getDomainLevels("fred"));
+    assertEquals(ImmutableList.of("www.twitter.com", "twitter.com", "com"),
+        UrlHelper.getDomainLevels("www.twitter.com"));
+    assertEquals(ImmutableList.of("www.twitter.co.uk", "twitter.co.uk", "co.uk", "uk"),
+        UrlHelper.getDomainLevels("twitter.co.uk"));
+  }
+}

http://git-wip-us.apache.org/repos/asf/aurora/blob/86a547b9/commons/src/test/java/com/twitter/common/net/UrlResolverTest.java
----------------------------------------------------------------------
diff --git a/commons/src/test/java/com/twitter/common/net/UrlResolverTest.java b/commons/src/test/java/com/twitter/common/net/UrlResolverTest.java
new file mode 100644
index 0000000..2eedc17
--- /dev/null
+++ b/commons/src/test/java/com/twitter/common/net/UrlResolverTest.java
@@ -0,0 +1,174 @@
+// =================================================================================================
+// Copyright 2011 Twitter, Inc.
+// -------------------------------------------------------------------------------------------------
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this work except in compliance with the License.
+// You may obtain a copy of the License in the LICENSE file, or 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.twitter.common.net;
+
+import com.twitter.common.base.ExceptionalFunction;
+import com.twitter.common.net.UrlResolver.ResolvedUrl;
+import com.twitter.common.net.UrlResolver.ResolvedUrl.EndState;
+import com.twitter.common.util.BackoffStrategy;
+import com.twitter.common.util.Clock;
+import org.easymock.IMocksControl;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.io.IOException;
+
+import static com.google.common.testing.junit4.JUnitAsserts.assertContentsInOrder;
+import static org.easymock.EasyMock.createControl;
+import static org.easymock.EasyMock.expect;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+
+/**
+ * @author John Sirois
+ */
+public class UrlResolverTest {
+  private IMocksControl control;
+  private ExceptionalFunction<String, String, IOException> resolver;
+  private Clock clock;
+  private BackoffStrategy backoffStrategy;
+
+  @Before
+  public void setUp() throws Exception {
+    control = createControl();
+
+    @SuppressWarnings("unchecked")
+    ExceptionalFunction<String, String, IOException> resolver =
+        control.createMock(ExceptionalFunction.class);
+    this.resolver = resolver;
+    this.clock = control.createMock(Clock.class);
+    this.backoffStrategy = control.createMock(BackoffStrategy.class);
+  }
+
+  @Test
+  public void testResolveUrlResolved() throws Exception {
+    expect(resolver.apply("jake")).andReturn("jake");
+    control.replay();
+
+    ResolvedUrl resolvedUrl = createResolver(3).resolveUrl("jake");
+    assertEquals("jake", resolvedUrl.getStartUrl());
+    assertContentsInOrder("Expected no intermediate urls", resolvedUrl.getIntermediateUrls());
+    assertNull(resolvedUrl.getEndUrl());
+    assertEquals(EndState.REACHED_LANDING, resolvedUrl.getEndState());
+
+    control.verify();
+  }
+
+  @Test
+  public void testResolveUrlSingleRedirect() throws Exception {
+    expect(resolver.apply("jake")).andReturn("joe");
+    expect(resolver.apply("joe")).andReturn("joe");
+    control.replay();
+
+    ResolvedUrl resolvedUrl = createResolver(3).resolveUrl("jake");
+    assertEquals("jake", resolvedUrl.getStartUrl());
+    assertContentsInOrder("Expected no intermediate urls", resolvedUrl.getIntermediateUrls());
+    assertEquals("joe", resolvedUrl.getEndUrl());
+    assertEquals(EndState.REACHED_LANDING, resolvedUrl.getEndState());
+
+    control.verify();
+  }
+
+  @Test
+  public void testResolveUrlMultipleRedirects() throws Exception {
+    expect(resolver.apply("jake")).andReturn("joe");
+    expect(resolver.apply("joe")).andReturn("bill");
+    expect(resolver.apply("bill")).andReturn("bob");
+    expect(resolver.apply("bob")).andReturn("fred");
+    expect(resolver.apply("fred")).andReturn("fred");
+    control.replay();
+
+    ResolvedUrl resolvedUrl = createResolver(5).resolveUrl("jake");
+    assertEquals("jake", resolvedUrl.getStartUrl());
+    assertContentsInOrder(resolvedUrl.getIntermediateUrls(), "joe", "bill", "bob");
+    assertEquals("fred", resolvedUrl.getEndUrl());
+    assertEquals(EndState.REACHED_LANDING, resolvedUrl.getEndState());
+
+    control.verify();
+  }
+
+  @Test
+  public void testResolveUrlRedirectLimit() throws Exception {
+    expect(resolver.apply("jake")).andReturn("joe");
+    expect(resolver.apply("joe")).andReturn("bill");
+    control.replay();
+
+    ResolvedUrl resolvedUrl = createResolver(2).resolveUrl("jake");
+    assertEquals("jake", resolvedUrl.getStartUrl());
+    assertContentsInOrder(resolvedUrl.getIntermediateUrls(), "joe");
+    assertEquals("bill", resolvedUrl.getEndUrl());
+    assertEquals(EndState.REDIRECT_LIMIT, resolvedUrl.getEndState());
+
+    control.verify();
+  }
+
+  @Test
+  public void testResolveUrlResolveError() throws Exception {
+    expect(resolver.apply("jake")).andThrow(new IOException());
+    control.replay();
+
+    ResolvedUrl resolvedUrl = createResolver(3).resolveUrl("jake");
+    assertEquals("jake", resolvedUrl.getStartUrl());
+    assertContentsInOrder("Expected no intermediate urls", resolvedUrl.getIntermediateUrls());
+    assertNull(resolvedUrl.getEndUrl());
+    assertEquals(EndState.ERROR, resolvedUrl.getEndState());
+
+    control.verify();
+  }
+
+  @Test
+  public void testResolveUrlResolveErrorCode() throws Exception {
+    expect(resolver.apply("jake")).andReturn(null);
+    expect(backoffStrategy.calculateBackoffMs(0L)).andReturn(1L);
+    clock.waitFor(1L);
+
+    expect(resolver.apply("jake")).andReturn(null);
+    expect(backoffStrategy.calculateBackoffMs(1L)).andReturn(2L);
+    clock.waitFor(2L);
+
+    expect(resolver.apply("jake")).andReturn(null);
+    // we shouldn't back off after the last attempt
+    control.replay();
+
+    ResolvedUrl resolvedUrl = createResolver(3).resolveUrl("jake");
+    assertEquals("jake", resolvedUrl.getStartUrl());
+    assertContentsInOrder("Expected no intermediate urls", resolvedUrl.getIntermediateUrls());
+    assertNull(resolvedUrl.getEndUrl());
+    assertEquals(EndState.ERROR, resolvedUrl.getEndState());
+
+    control.verify();
+  }
+
+  @Test
+  public void testResolveStepsToPermanentError() throws Exception {
+    expect(resolver.apply("jake")).andReturn("joe");
+    expect(resolver.apply("joe")).andReturn("fred");
+    expect(resolver.apply("fred")).andReturn(null);
+    // we shouldn't back off after the last attempt
+    control.replay();
+
+    ResolvedUrl resolvedUrl = createResolver(3).resolveUrl("jake");
+    assertEquals("jake", resolvedUrl.getStartUrl());
+    assertContentsInOrder(resolvedUrl.getIntermediateUrls(), "joe");
+    assertEquals("fred", resolvedUrl.getEndUrl());
+    assertEquals(EndState.ERROR, resolvedUrl.getEndState());
+  }
+
+  private UrlResolver createResolver(int maxRedirects) {
+    return new UrlResolver(clock, backoffStrategy, resolver, maxRedirects);
+  }
+}

http://git-wip-us.apache.org/repos/asf/aurora/blob/86a547b9/commons/src/test/java/com/twitter/common/net/UrlResolverUtilTest.java
----------------------------------------------------------------------
diff --git a/commons/src/test/java/com/twitter/common/net/UrlResolverUtilTest.java b/commons/src/test/java/com/twitter/common/net/UrlResolverUtilTest.java
new file mode 100644
index 0000000..635ae13
--- /dev/null
+++ b/commons/src/test/java/com/twitter/common/net/UrlResolverUtilTest.java
@@ -0,0 +1,165 @@
+// =================================================================================================
+// Copyright 2011 Twitter, Inc.
+// -------------------------------------------------------------------------------------------------
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this work except in compliance with the License.
+// You may obtain a copy of the License in the LICENSE file, or 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.twitter.common.net;
+
+import com.google.common.base.Function;
+import com.google.common.testing.TearDown;
+import com.google.common.testing.junit4.TearDownTestCase;
+import com.twitter.common.base.Closure;
+import com.twitter.common.collections.Pair;
+import org.easymock.Capture;
+import org.easymock.IAnswer;
+import org.easymock.IMocksControl;
+import org.junit.Before;
+import org.junit.Test;
+import org.mortbay.jetty.Server;
+import org.mortbay.jetty.bio.SocketConnector;
+import org.mortbay.jetty.servlet.Context;
+import org.mortbay.jetty.servlet.ServletHolder;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.net.URL;
+
+import static org.easymock.EasyMock.*;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * TODO(John Sirois): add test for error conditions
+ *
+ * @author John Sirois
+ */
+public class UrlResolverUtilTest extends TearDownTestCase {
+  private static final String REDIRECT_LOCATION = "http://bar";
+
+  private IMocksControl control;
+  private Function<URL, String> urlToUA;
+  private Closure<Pair<HttpServletRequest, HttpServletResponse>> requestHandler;
+  private UrlResolverUtil urlResolverUtil;
+  private HttpServlet servlet;
+  private String url;
+
+  @Before
+  public void setUp() {
+    control = createControl();
+
+    @SuppressWarnings("unchecked")
+    Function<URL, String> urlToUA = control.createMock(Function.class);
+    this.urlToUA = urlToUA;
+
+    @SuppressWarnings("unchecked")
+    Closure<Pair<HttpServletRequest, HttpServletResponse>> handler =
+        control.createMock(Closure.class);
+    requestHandler = handler;
+
+    urlResolverUtil = new UrlResolverUtil(urlToUA);
+
+    servlet = new HttpServlet() {
+      @Override protected void service(HttpServletRequest req, HttpServletResponse resp)
+          throws ServletException, IOException {
+        requestHandler.execute(Pair.of(req, resp));
+      }
+    };
+  }
+
+  @Test
+  public void testUASelection() throws Exception {
+    url = startServer();
+    String redirectLocation = "http://bar.com";
+
+    expect(urlToUA.apply(new URL(url))).andReturn("foo-agent");
+    expectRequestAndRedirect(redirectLocation);
+    control.replay();
+
+    String effectiveUrl = urlResolverUtil.getEffectiveUrl(url, null /* no proxy */);
+    assertEquals(redirectLocation, effectiveUrl);
+
+    control.verify();
+  }
+
+  @Test
+  public void testRelativeRedirect() throws Exception {
+    url = startServer();
+    String relativeRedirect = "relatively/speaking";
+
+    expect(urlToUA.apply(new URL(url))).andReturn("foo-agent");
+    expectRequestAndRedirect(relativeRedirect);
+    control.replay();
+
+    String effectiveUrl = urlResolverUtil.getEffectiveUrl(url, null /* no proxy */);
+    assertEquals(url + relativeRedirect, effectiveUrl);
+
+    control.verify();
+  }
+
+  @Test
+  public void testInvalidRedirect() throws Exception {
+    url = startServer();
+    String badRedirect = ":::<<<<<::IAMNOTAVALIDURI";
+
+    expect(urlToUA.apply(new URL(url))).andReturn("foo-agent");
+    expectRequestAndRedirect(badRedirect);
+    control.replay();
+
+    String effectiveUrl = urlResolverUtil.getEffectiveUrl(url, null /* no proxy */);
+    assertEquals(badRedirect, effectiveUrl);
+
+    control.verify();
+  }
+
+  private void expectRequestAndRedirect(final String location) throws Exception {
+    final Capture<Pair<HttpServletRequest, HttpServletResponse>> requestCapture =
+        new Capture<Pair<HttpServletRequest, HttpServletResponse>>();
+    requestHandler.execute(capture(requestCapture));
+    expectLastCall().andAnswer(new IAnswer<Void>() {
+      @Override public Void answer() throws Throwable {
+        assertTrue(requestCapture.hasCaptured());
+        Pair<HttpServletRequest, HttpServletResponse> pair = requestCapture.getValue();
+
+        HttpServletRequest request = pair.getFirst();
+        assertEquals("HEAD", request.getMethod());
+        assertEquals("foo-agent", request.getHeader("User-Agent"));
+
+        pair.getSecond().sendRedirect(location);
+        return null;
+      }
+    });
+  }
+
+  private String startServer() throws Exception {
+    final Server server = new Server();
+    final SocketConnector connector = new SocketConnector();
+    connector.setHost("127.0.0.1");
+    connector.setPort(0);
+    server.addConnector(connector);
+
+    Context context = new Context(server, "/", Context.NO_SECURITY);
+    context.addServlet(new ServletHolder(servlet), "/*");
+    server.start();
+    addTearDown(new TearDown() {
+      @Override public void tearDown() throws Exception {
+        server.stop();
+      }
+    });
+
+    return "http://" + connector.getHost() + ":" + connector.getLocalPort() + "/";
+  }
+}

http://git-wip-us.apache.org/repos/asf/aurora/blob/86a547b9/commons/src/test/java/com/twitter/common/net/UrlTokenizerUtilTest.java
----------------------------------------------------------------------
diff --git a/commons/src/test/java/com/twitter/common/net/UrlTokenizerUtilTest.java b/commons/src/test/java/com/twitter/common/net/UrlTokenizerUtilTest.java
new file mode 100644
index 0000000..193394e
--- /dev/null
+++ b/commons/src/test/java/com/twitter/common/net/UrlTokenizerUtilTest.java
@@ -0,0 +1,97 @@
+// =================================================================================================
+// Copyright 2011 Twitter, Inc.
+// -------------------------------------------------------------------------------------------------
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this work except in compliance with the License.
+// You may obtain a copy of the License in the LICENSE file, or 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.twitter.common.net;
+
+import java.util.Arrays;
+import java.util.List;
+
+import com.google.common.collect.Lists;
+
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * @author Adam Samet
+ */
+public class UrlTokenizerUtilTest {
+
+  @Test
+  public void testGetReversedDomainParts() {
+    List<String> list1 = Lists.newArrayList();
+    String url1 = "www.twitter.com";
+    list1.add("com");
+    assertEquals(list1, UrlTokenizerUtil.getReversedDomainParts(url1, 1));
+    list1.add("twitter");
+    assertEquals(list1, UrlTokenizerUtil.getReversedDomainParts(url1, 2));
+    list1.add("www");
+    assertEquals(list1, UrlTokenizerUtil.getReversedDomainParts(url1, 3));
+    list1.add("");
+    assertEquals(list1, UrlTokenizerUtil.getReversedDomainParts(url1, 4));
+    list1.add("");
+    assertEquals(list1, UrlTokenizerUtil.getReversedDomainParts(url1, 5));
+
+    List<String> list2 = Lists.newArrayList();
+    String url2 = "www.twitter.co.uk";
+    list2.add("co.uk");
+    assertEquals(list2, UrlTokenizerUtil.getReversedDomainParts(url2, 1));
+    list2.add("twitter");
+    assertEquals(list2, UrlTokenizerUtil.getReversedDomainParts(url2, 2));
+    list2.add("www");
+    assertEquals(list2, UrlTokenizerUtil.getReversedDomainParts(url2, 3));
+    list2.add("");
+    assertEquals(list2, UrlTokenizerUtil.getReversedDomainParts(url2, 4));
+    list2.add("");
+    assertEquals(list2, UrlTokenizerUtil.getReversedDomainParts(url2, 5));
+
+    List<String> list3 = Lists.newArrayList();
+    String url3= "www.twitter.co.ukNOT";
+    list3.add("ukNOT");
+    assertEquals(list3, UrlTokenizerUtil.getReversedDomainParts(url3, 1));
+    list3.add("co");
+    assertEquals(list3, UrlTokenizerUtil.getReversedDomainParts(url3, 2));
+    list3.add("twitter");
+    assertEquals(list3, UrlTokenizerUtil.getReversedDomainParts(url3, 3));
+    list3.add("www");
+    assertEquals(list3, UrlTokenizerUtil.getReversedDomainParts(url3, 4));
+    list3.add("");
+    assertEquals(list3, UrlTokenizerUtil.getReversedDomainParts(url3, 5));
+
+    assertEquals(Arrays.asList("co.jp", "google"),
+                 UrlTokenizerUtil.getReversedDomainParts("news.google.co.jp", 2));
+    assertEquals(Arrays.asList("co.jp", "google"),
+                 UrlTokenizerUtil.getReversedDomainParts("news.google.co.jp", 2));
+    assertEquals(Arrays.asList("com", "google"),
+                 UrlTokenizerUtil.getReversedDomainParts("news.google.com", 2));
+    assertEquals(Arrays.asList("com", "google", "news"),
+                 UrlTokenizerUtil.getReversedDomainParts("news.google.com", 3));
+  }
+
+  @Test
+  public void testIsTLD() throws Exception {
+    assertTrue(UrlTokenizerUtil.isTLD("com.cn", false));
+    assertTrue(UrlTokenizerUtil.isTLD("com", false));
+    assertTrue(UrlTokenizerUtil.isTLD("co.jp", false));
+    assertTrue(UrlTokenizerUtil.isTLD("co.uk", false));
+    assertTrue(UrlTokenizerUtil.isTLD("uk.co", true));
+    assertTrue(UrlTokenizerUtil.isTLD("cn.com", true));
+    assertFalse(UrlTokenizerUtil.isTLD("google.co.uk", false));
+    assertFalse(UrlTokenizerUtil.isTLD("google.jp", false));
+  }
+}

http://git-wip-us.apache.org/repos/asf/aurora/blob/86a547b9/commons/src/test/java/com/twitter/common/net/http/RequestLoggerTest.java
----------------------------------------------------------------------
diff --git a/commons/src/test/java/com/twitter/common/net/http/RequestLoggerTest.java b/commons/src/test/java/com/twitter/common/net/http/RequestLoggerTest.java
new file mode 100644
index 0000000..49c072e
--- /dev/null
+++ b/commons/src/test/java/com/twitter/common/net/http/RequestLoggerTest.java
@@ -0,0 +1,90 @@
+package com.twitter.common.net.http;
+
+import java.util.logging.Level;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mortbay.jetty.HttpHeaders;
+import org.mortbay.jetty.HttpURI;
+import org.mortbay.jetty.Request;
+import org.mortbay.jetty.RequestLog;
+import org.mortbay.jetty.Response;
+
+import com.twitter.common.net.http.RequestLogger.LogSink;
+import com.twitter.common.quantity.Amount;
+import com.twitter.common.quantity.Time;
+import com.twitter.common.testing.easymock.EasyMockTest;
+import com.twitter.common.util.testing.FakeClock;
+
+import static org.easymock.EasyMock.expect;
+
+public class RequestLoggerTest extends EasyMockTest {
+
+  private FakeClock clock;
+  private LogSink sink;
+  private Request request;
+  private Response response;
+
+  private RequestLog log;
+
+  @Before
+  public void setUp() throws Exception {
+    clock = new FakeClock();
+    sink = createMock(LogSink.class);
+    request = createMock(Request.class);
+    response = createMock(Response.class);
+    log = new RequestLogger(clock, sink);
+  }
+
+  @Test
+  public void testFormat200() throws Exception {
+    clock.advance(Amount.of(40L * 365, Time.DAYS));
+
+    expect(response.getStatus()).andReturn(200).atLeastOnce();
+    expect(request.getServerName()).andReturn("snoopy");
+    expect(request.getHeader(HttpHeaders.X_FORWARDED_FOR)).andReturn(null);
+    expect(request.getMethod()).andReturn("GET");
+    expect(request.getUri()).andReturn(new HttpURI("/"));
+    expect(request.getProtocol()).andReturn("http");
+    expect(response.getContentCount()).andReturn(256L);
+    expect(request.getRemoteAddr()).andReturn("easymock-test");
+    expect(request.getHeader(HttpHeaders.REFERER)).andReturn(null);
+    expect(request.getHeader(HttpHeaders.USER_AGENT)).andReturn("junit");
+    expect(request.getTimeStamp()).andReturn(clock.nowMillis()).atLeastOnce();
+
+    expect(sink.isLoggable(Level.FINE)).andReturn(true);
+    sink.log(Level.FINE, "snoopy easymock-test [22/Dec/2009:00:00:00 +0000]"
+        + " \"GET / http\" 200 256 \"-\" \"junit\" 110");
+
+    control.replay();
+
+    clock.advance(Amount.of(110L, Time.MILLISECONDS));
+    log.log(request, response);
+  }
+
+  @Test
+  public void testFormat500() throws Exception {
+    clock.advance(Amount.of(40L * 365, Time.DAYS));
+
+    expect(response.getStatus()).andReturn(500).atLeastOnce();
+    expect(request.getServerName()).andReturn("woodstock");
+    expect(request.getHeader(HttpHeaders.X_FORWARDED_FOR)).andReturn(null);
+    expect(request.getMethod()).andReturn("POST");
+    expect(request.getUri()).andReturn(new HttpURI("/data"));
+    expect(request.getProtocol()).andReturn("http");
+    expect(response.getContentCount()).andReturn(128L);
+    expect(request.getRemoteAddr()).andReturn("easymock-test");
+    expect(request.getHeader(HttpHeaders.REFERER)).andReturn(null);
+    expect(request.getHeader(HttpHeaders.USER_AGENT)).andReturn("junit");
+    expect(request.getTimeStamp()).andReturn(clock.nowMillis()).atLeastOnce();
+
+    expect(sink.isLoggable(Level.INFO)).andReturn(true);
+    sink.log(Level.INFO, "woodstock easymock-test [22/Dec/2009:00:00:00 +0000]"
+        + " \"POST /data http\" 500 128 \"-\" \"junit\" 500");
+
+    control.replay();
+
+    clock.advance(Amount.of(500L, Time.MILLISECONDS));
+    log.log(request, response);
+  }
+}

http://git-wip-us.apache.org/repos/asf/aurora/blob/86a547b9/commons/src/test/java/com/twitter/common/net/http/filters/HttpStatsFilterIntegrationTest.java
----------------------------------------------------------------------
diff --git a/commons/src/test/java/com/twitter/common/net/http/filters/HttpStatsFilterIntegrationTest.java b/commons/src/test/java/com/twitter/common/net/http/filters/HttpStatsFilterIntegrationTest.java
new file mode 100644
index 0000000..8ff6811
--- /dev/null
+++ b/commons/src/test/java/com/twitter/common/net/http/filters/HttpStatsFilterIntegrationTest.java
@@ -0,0 +1,153 @@
+package com.twitter.common.net.http.filters;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+
+import com.google.common.collect.ImmutableMap;
+import com.google.inject.AbstractModule;
+import com.google.inject.Guice;
+import com.google.inject.Injector;
+import com.google.inject.Singleton;
+import com.google.inject.servlet.GuiceFilter;
+import com.google.inject.servlet.GuiceServletContextListener;
+import com.sun.jersey.api.client.Client;
+import com.sun.jersey.api.client.UniformInterfaceException;
+import com.sun.jersey.api.client.config.ClientConfig;
+import com.sun.jersey.api.client.config.DefaultClientConfig;
+import com.sun.jersey.guice.JerseyServletModule;
+import com.sun.jersey.guice.spi.container.servlet.GuiceContainer;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import com.twitter.common.net.http.HttpServerDispatch;
+import com.twitter.common.net.http.JettyHttpServerDispatch;
+import com.twitter.common.stats.Stat;
+import com.twitter.common.stats.Stats;
+import com.twitter.common.util.Clock;
+import com.twitter.common.util.testing.FakeClock;
+
+import static com.sun.jersey.api.core.ResourceConfig.PROPERTY_CONTAINER_RESPONSE_FILTERS;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+public class HttpStatsFilterIntegrationTest {
+  private Client client;
+  private FakeClock clock;
+  private JettyHttpServerDispatch server;
+
+  @Before
+  public void setUp() {
+    Stats.flush();
+
+    server = new JettyHttpServerDispatch();
+    server.listen(0);
+    server.registerFilter(GuiceFilter.class, "/*");
+
+    clock = new FakeClock();
+
+    final Injector injector = Guice.createInjector(
+        new AbstractModule() {
+          @Override
+          protected void configure() {
+            bind(TestServlet.class).in(Singleton.class);
+
+            bind(Clock.class).toInstance(clock);
+            bind(HttpStatsFilter.class).in(Singleton.class);
+          }
+        },
+        new JerseyServletModule() {
+          @Override
+          protected void configureServlets() {
+            filter("/*").through(HttpStatsFilter.class);
+            serve("/*").with(GuiceContainer.class, ImmutableMap.of(
+                PROPERTY_CONTAINER_RESPONSE_FILTERS, HttpStatsFilter.class.getName()));
+          }
+        }
+    );
+    server.getRootContext().addEventListener(new GuiceServletContextListener() {
+      @Override protected Injector getInjector() {
+        return injector;
+      }
+    });
+
+    ClientConfig config = new DefaultClientConfig();
+    client = Client.create(config);
+  }
+
+  @Path("/")
+  public static class TestServlet {
+    @GET
+    @Path("/hello")
+    @Produces(MediaType.TEXT_PLAIN)
+    @HttpStatsFilter.TrackRequestStats("hello")
+    public Response hello() {
+      return Response.ok("hello world").build();
+    }
+
+    @GET
+    @Path("/hola")
+    @Produces(MediaType.TEXT_PLAIN)
+    @HttpStatsFilter.TrackRequestStats("hola")
+    public Response hola() {
+      return Response.ok("hola mundo").build();
+    }
+
+    @GET
+    @Path("/goodbye")
+    @Produces(MediaType.TEXT_PLAIN)
+    public Response goodbye() {
+      return Response.ok("goodbye cruel world").build();
+    }
+  }
+
+  private String getResource(String path) {
+    return client
+        .resource(String.format("http://localhost:%s%s", server.getPort(), path))
+        .accept(MediaType.TEXT_PLAIN)
+        .get(String.class);
+  }
+
+  private void assertStatValue(String statName, long expectedValue) {
+    Stat<Long> stat = Stats.getVariable(statName);
+    assertEquals(expectedValue, stat.read().longValue());
+  }
+
+  @Test
+  public void testStatsTracking() throws Exception {
+    getResource("/hello");
+
+    assertStatValue("http_hello_200_responses_events", 1);
+  }
+
+  @Test
+  public void testRepeatedContextInjection() throws Exception {
+    getResource("/hello");
+    getResource("/hola");
+    getResource("/hello");
+
+    assertStatValue("http_hello_200_responses_events", 2);
+    assertStatValue("http_hola_200_responses_events", 1);
+  }
+
+  @Test
+  public void testNoStatsTracking() throws Exception {
+    getResource("/goodbye");
+
+    assertNull(Stats.getVariable("http_goodbye_200_responses_events"));
+  }
+
+  @Test
+  public void testNoMatchedMethod() throws Exception {
+    try {
+      getResource("/what");
+      fail("Should have thrown a 404.");
+    } catch (UniformInterfaceException e) {
+      assertStatValue("http_404_responses_events", 1);
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/aurora/blob/86a547b9/commons/src/test/java/com/twitter/common/net/http/filters/HttpStatsFilterTest.java
----------------------------------------------------------------------
diff --git a/commons/src/test/java/com/twitter/common/net/http/filters/HttpStatsFilterTest.java b/commons/src/test/java/com/twitter/common/net/http/filters/HttpStatsFilterTest.java
new file mode 100644
index 0000000..ba9783c
--- /dev/null
+++ b/commons/src/test/java/com/twitter/common/net/http/filters/HttpStatsFilterTest.java
@@ -0,0 +1,214 @@
+package com.twitter.common.net.http.filters;
+
+import java.io.IOException;
+import java.lang.reflect.Field;
+import java.util.List;
+import javax.servlet.FilterChain;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.ws.rs.core.Context;
+
+import com.google.common.collect.Lists;
+import com.sun.jersey.api.core.ExtendedUriInfo;
+import com.sun.jersey.api.model.AbstractResourceMethod;
+import com.sun.jersey.spi.container.ContainerRequest;
+import com.sun.jersey.spi.container.ContainerResponse;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import com.twitter.common.collections.Pair;
+import com.twitter.common.net.http.filters.HttpStatsFilter.TrackRequestStats;
+import com.twitter.common.quantity.Amount;
+import com.twitter.common.quantity.Time;
+import com.twitter.common.stats.SlidingStats;
+import com.twitter.common.testing.easymock.EasyMockTest;
+import com.twitter.common.util.testing.FakeClock;
+
+import static org.easymock.EasyMock.anyObject;
+import static org.easymock.EasyMock.expect;
+import static org.easymock.EasyMock.expectLastCall;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+public class HttpStatsFilterTest extends EasyMockTest {
+  private FakeClock clock;
+  private HttpServletRequest request;
+  private HttpServletResponse response;
+  private FilterChain filterChain;
+  private HttpStatsFilter filter;
+  private ContainerRequest containerRequest;
+  private ContainerResponse containerResponse;
+  private ExtendedUriInfo extendedUriInfo;
+  private HttpServletRequest servletRequest;
+
+  private static final Amount<Long, Time> REQUEST_TIME =  Amount.of(1000L, Time.NANOSECONDS);
+
+  private void injectContextVars() throws Exception {
+    extendedUriInfo = createMock(ExtendedUriInfo.class);
+    servletRequest = createMock(HttpServletRequest.class);
+
+    List<Object> injectables = Lists.newArrayList(extendedUriInfo, servletRequest);
+
+    for (Field f : filter.getClass().getDeclaredFields()) {
+      if (f.isAnnotationPresent(Context.class)) {
+        for (Object injectable : injectables) {
+          if (f.getType().isInstance(injectable)) {
+            f.setAccessible(true);
+            f.set(filter, injectable);
+          }
+        }
+      }
+    }
+  }
+
+  @Before
+  public void setUp() throws Exception {
+    clock = new FakeClock();
+    request = createMock(HttpServletRequest.class);
+    response = createMock(HttpServletResponse.class);
+    filterChain = createMock(FilterChain.class);
+    filter = new HttpStatsFilter(clock);
+
+    containerRequest = createMock(ContainerRequest.class);
+    containerResponse = createMock(ContainerResponse.class);
+
+    injectContextVars();
+  }
+
+  @Test
+  public void testStartTimeIsSetAsRequestAttribute() throws Exception {
+    request.setAttribute(HttpStatsFilter.REQUEST_START_TIME, REQUEST_TIME.getValue());
+    filterChain.doFilter(request, response);
+
+    control.replay();
+
+    clock.advance(REQUEST_TIME);
+    filter.doFilter(request, response, filterChain);
+  }
+
+  @Test
+  public void testExceptionStatsCounting() throws Exception {
+    request.setAttribute(HttpStatsFilter.REQUEST_START_TIME, REQUEST_TIME.getValue());
+    expectLastCall().times(2);
+    clock.advance(REQUEST_TIME);
+
+    filterChain.doFilter(anyObject(HttpServletRequest.class), anyObject(HttpServletResponse.class));
+    expectLastCall().andThrow(new IOException());
+
+    filterChain.doFilter(anyObject(HttpServletRequest.class), anyObject(HttpServletResponse.class));
+    expectLastCall().andThrow(new ServletException());
+
+    control.replay();
+
+    try {
+      filter.doFilter(request, response, filterChain);
+      fail("Filter should have re-thrown the exception.");
+    } catch (IOException e) {
+      // Exception is expected, but we still want to assert on the stat tracking, so we can't
+      //  just use @Test(expected...)
+      assertEquals(1, filter.exceptionCount.get());
+    }
+
+    try {
+      filter.doFilter(request, response, filterChain);
+      fail("Filter should have re-thrown the exception.");
+    } catch (ServletException e) {
+      // See above.
+      assertEquals(2, filter.exceptionCount.get());
+    }
+  }
+
+  private void expectAnnotationValue(String value, int times) {
+    AbstractResourceMethod matchedMethod = createMock(AbstractResourceMethod.class);
+    expect(extendedUriInfo.getMatchedMethod()).andReturn(matchedMethod).times(times);
+
+    TrackRequestStats annotation = createMock(TrackRequestStats.class);
+    expect(matchedMethod.getAnnotation(TrackRequestStats.class)).andReturn(annotation).times(times);
+
+    expect(annotation.value()).andReturn(value).times(times);
+  }
+
+  private void expectAnnotationValue(String value) {
+    expectAnnotationValue(value, 1);
+  }
+
+  @Test
+  public void testBasicStatsCounting() throws Exception {
+    expect(containerResponse.getStatus()).andReturn(HttpServletResponse.SC_OK);
+
+    expect(servletRequest.getAttribute(HttpStatsFilter.REQUEST_START_TIME))
+        .andReturn(clock.nowNanos());
+
+    String value = "some_value";
+    expectAnnotationValue(value);
+
+    control.replay();
+
+    clock.advance(REQUEST_TIME);
+    assertEquals(containerResponse, filter.filter(containerRequest, containerResponse));
+
+    SlidingStats stat = filter.requestCounters.get(Pair.of(value, HttpServletResponse.SC_OK));
+    assertEquals(1, stat.getEventCounter().get());
+    assertEquals(REQUEST_TIME.getValue().longValue(), stat.getTotalCounter().get());
+    assertEquals(1, filter.statusCounters.get(HttpServletResponse.SC_OK).getEventCounter().get());
+  }
+
+  @Test
+  public void testMultipleRequests() throws Exception {
+    int numCalls = 2;
+
+    expect(containerResponse.getStatus()).andReturn(HttpServletResponse.SC_OK).times(numCalls);
+
+    expect(servletRequest.getAttribute(HttpStatsFilter.REQUEST_START_TIME))
+        .andReturn(clock.nowNanos()).times(numCalls);
+
+    String value = "some_value";
+    expectAnnotationValue(value, numCalls);
+
+    control.replay();
+
+    clock.advance(REQUEST_TIME);
+    for (int i = 0; i < numCalls; i++) {
+      filter.filter(containerRequest, containerResponse);
+    }
+
+    SlidingStats stat = filter.requestCounters.get(Pair.of(value, HttpServletResponse.SC_OK));
+    assertEquals(numCalls, stat.getEventCounter().get());
+    assertEquals(REQUEST_TIME.getValue() * numCalls, stat.getTotalCounter().get());
+    assertEquals(numCalls,
+        filter.statusCounters.get(HttpServletResponse.SC_OK).getEventCounter().get());
+  }
+
+  @Test
+  public void testNoStartTime() throws Exception {
+    expect(servletRequest.getAttribute(HttpStatsFilter.REQUEST_START_TIME))
+        .andReturn(null);
+
+    expect(containerResponse.getStatus()).andReturn(HttpServletResponse.SC_OK);
+
+    control.replay();
+
+    assertEquals(containerResponse, filter.filter(containerRequest, containerResponse));
+
+    assertEquals(0, filter.statusCounters.asMap().keySet().size());
+  }
+
+  @Test
+  public void testNoMatchedMethod() throws Exception {
+    expect(containerResponse.getStatus()).andReturn(HttpServletResponse.SC_NOT_FOUND);
+
+    expect(servletRequest.getAttribute(HttpStatsFilter.REQUEST_START_TIME))
+        .andReturn(clock.nowNanos());
+
+    expect(extendedUriInfo.getMatchedMethod()).andReturn(null);
+
+    control.replay();
+
+    clock.advance(REQUEST_TIME);
+    assertEquals(containerResponse, filter.filter(containerRequest, containerResponse));
+    assertEquals(1,
+        filter.statusCounters.get(HttpServletResponse.SC_NOT_FOUND).getEventCounter().get());
+  }
+}

http://git-wip-us.apache.org/repos/asf/aurora/blob/86a547b9/commons/src/test/java/com/twitter/common/net/http/handlers/AssetHandlerTest.java
----------------------------------------------------------------------
diff --git a/commons/src/test/java/com/twitter/common/net/http/handlers/AssetHandlerTest.java b/commons/src/test/java/com/twitter/common/net/http/handlers/AssetHandlerTest.java
new file mode 100644
index 0000000..94adf60
--- /dev/null
+++ b/commons/src/test/java/com/twitter/common/net/http/handlers/AssetHandlerTest.java
@@ -0,0 +1,383 @@
+// =================================================================================================
+// Copyright 2011 Twitter, Inc.
+// -------------------------------------------------------------------------------------------------
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this work except in compliance with the License.
+// You may obtain a copy of the License in the LICENSE file, or 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.twitter.common.net.http.handlers;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.zip.GZIPInputStream;
+
+import javax.servlet.ServletOutputStream;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import com.google.common.io.ByteStreams;
+import com.google.common.io.InputSupplier;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import com.twitter.common.net.http.handlers.AssetHandler.StaticAsset;
+import com.twitter.common.testing.easymock.EasyMockTest;
+
+import static com.twitter.common.net.http.handlers.AssetHandler.CACHE_CONTROL_MAX_AGE_SECS;
+import static javax.servlet.http.HttpServletResponse.SC_NOT_MODIFIED;
+import static javax.servlet.http.HttpServletResponse.SC_OK;
+import static org.easymock.EasyMock.expect;
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertThat;
+
+/**
+ * @author William Farner
+ */
+public class AssetHandlerTest extends EasyMockTest {
+
+  private static final String TEST_DATA = "here is my great test data";
+  // Checksum of the gzipped TEST_DATA.
+  private static final String TEST_DATA_CHECKSUM = "ePvVhtAeVRu85KSOLKL0oQ==";
+  private static final String CONTENT_TYPE = "text/plain";
+
+  private InputSupplier<InputStream> inputSupplier;
+
+  @Before
+  public void setUp() {
+    inputSupplier = createMock(new Clazz<InputSupplier<InputStream>>() {});
+  }
+
+  private static class Request {
+    private final HttpServletRequest req;
+    private final HttpServletResponse resp;
+    private final ByteArrayOutputStream responseBody;
+
+    Request(HttpServletRequest req, HttpServletResponse resp, ByteArrayOutputStream responseBody) {
+      this.req = req;
+      this.resp = resp;
+      this.responseBody = responseBody;
+    }
+  }
+
+  private Request doGet(String suppliedChecksum, String supportedEncodings,
+      int expectedResponseCode, boolean expectRead) throws Exception {
+    HttpServletRequest req = createMock(HttpServletRequest.class);
+    HttpServletResponse resp = createMock(HttpServletResponse.class);
+
+    if (expectRead) {
+      expect(inputSupplier.getInput()).andReturn(new ByteArrayInputStream(TEST_DATA.getBytes()));
+    }
+
+    expect(req.getHeader("If-None-Match")).andReturn(suppliedChecksum);
+
+    resp.setStatus(expectedResponseCode);
+    if (expectedResponseCode == SC_OK) {
+      expect(req.getHeader("Accept-Encoding")).andReturn(supportedEncodings);
+      resp.setHeader("Cache-Control", "public,max-age=" + CACHE_CONTROL_MAX_AGE_SECS);
+      resp.setHeader("ETag", TEST_DATA_CHECKSUM);
+      resp.setContentType(CONTENT_TYPE);
+
+      if (supportedEncodings != null && supportedEncodings.contains("gzip")) {
+        resp.setHeader("Content-Encoding", "gzip");
+      }
+    }
+    return new Request(req, resp, expectPayload(resp));
+  }
+
+  @Test
+  public void testCached() throws Exception {
+
+    // First request - no cached value
+    Request test1 = doGet(
+        null,  // No local checksum.
+        null,  // No encodings supported.
+        SC_OK,
+        true   // Triggers a data read.
+    );
+
+    // Second request - client performs conditional GET with wrong checksum.
+    Request test2 = doGet(
+        "foo", // Wrong checksum.
+        null,  // No encodings supported.
+        SC_OK,
+        false   // No read.
+    );
+
+    // Third request - client performs conditional GET with correct checksum.
+    Request test3 = doGet(
+        TEST_DATA_CHECKSUM,  // Correct checksum.
+        null,  // No encodings supported.
+        SC_NOT_MODIFIED,
+        false   // No read.
+    );
+
+    control.replay();
+
+    AssetHandler handler = new AssetHandler(new StaticAsset(inputSupplier, CONTENT_TYPE, true));
+
+    handler.doGet(test1.req, test1.resp);
+    assertThat(new String(test1.responseBody.toByteArray()), is(TEST_DATA));
+
+    handler.doGet(test2.req, test2.resp);
+    assertThat(new String(test2.responseBody.toByteArray()), is(TEST_DATA));
+
+    handler.doGet(test3.req, test3.resp);
+    assertThat(new String(test3.responseBody.toByteArray()), is(""));
+  }
+
+  @Test
+  public void testCachedGzipped() throws Exception {
+
+    // First request - no cached value
+    Request test1 = doGet(
+        null,  // No local checksum.
+        "gzip",  // Supported encodings.
+        SC_OK,
+        true   // Triggers a data read.
+    );
+
+    // Second request - client performs conditional GET with wrong checksum.
+    Request test2 = doGet(
+        "foo", // Wrong checksum.
+        "gzip,fakeencoding",  // Supported encodings.
+        SC_OK,
+        false   // No read.
+    );
+
+    // Third request - client performs conditional GET with correct checksum.
+    Request test3 = doGet(
+        TEST_DATA_CHECKSUM,  // Correct checksum.
+        "gzip,deflate",  // Supported encodings.
+        SC_NOT_MODIFIED,
+        false   // No read.
+    );
+
+    control.replay();
+
+    AssetHandler handler = new AssetHandler(new StaticAsset(inputSupplier, CONTENT_TYPE, true));
+
+    handler.doGet(test1.req, test1.resp);
+    assertThat(unzip(test1.responseBody), is(TEST_DATA));
+
+    handler.doGet(test2.req, test2.resp);
+    assertThat(unzip(test2.responseBody), is(TEST_DATA));
+
+    handler.doGet(test3.req, test3.resp);
+    assertThat(new String(test3.responseBody.toByteArray()), is(""));
+  }
+
+  @Test
+  public void testUncached() throws Exception {
+
+    // First request - no cached value
+    Request test1 = doGet(
+        null,  // No local checksum.
+        null,  // No encodings supported.
+        SC_OK,
+        true   // Triggers a data read.
+    );
+
+    // Second request - client performs conditional GET with wrong checksum.
+    Request test2 = doGet(
+        "foo", // Wrong checksum.
+        null,  // No encodings supported.
+        SC_OK,
+        true   // Triggers a data read.
+    );
+
+    // Third request - client performs conditional GET with correct checksum.
+    Request test3 = doGet(
+        TEST_DATA_CHECKSUM,  // Correct checksum.
+        null,  // No encodings supported.
+        SC_NOT_MODIFIED,
+        true   // Triggers a data read.
+    );
+
+    control.replay();
+
+    AssetHandler handler = new AssetHandler(new StaticAsset(inputSupplier, CONTENT_TYPE, false));
+
+    handler.doGet(test1.req, test1.resp);
+    assertThat(new String(test1.responseBody.toByteArray()), is(TEST_DATA));
+
+    handler.doGet(test2.req, test2.resp);
+    assertThat(new String(test2.responseBody.toByteArray()), is(TEST_DATA));
+
+    handler.doGet(test3.req, test3.resp);
+    assertThat(new String(test3.responseBody.toByteArray()), is(""));
+  }
+
+  @Test
+  public void testUncachedGzipped() throws Exception {
+
+    // First request - no cached value
+    Request test1 = doGet(
+        null,  // No local checksum.
+        "gzip",  // Supported encodings.
+        SC_OK,
+        true   // Triggers a data read.
+    );
+
+    // Second request - client performs conditional GET with wrong checksum.
+    Request test2 = doGet(
+        "foo", // Wrong checksum.
+        "gzip,fakeencoding",  // Supported encodings.
+        SC_OK,
+        true   // Triggers a data read.
+    );
+
+    // Third request - client performs conditional GET with correct checksum.
+    Request test3 = doGet(
+        TEST_DATA_CHECKSUM,  // Correct checksum.
+        "gzip,deflate",  // Supported encodings.
+        SC_NOT_MODIFIED,
+        true   // Triggers a data read.
+    );
+
+    control.replay();
+
+    AssetHandler handler = new AssetHandler(new StaticAsset(inputSupplier, CONTENT_TYPE, false));
+
+    handler.doGet(test1.req, test1.resp);
+    assertThat(unzip(test1.responseBody), is(TEST_DATA));
+
+    handler.doGet(test2.req, test2.resp);
+    assertThat(unzip(test2.responseBody), is(TEST_DATA));
+
+    handler.doGet(test3.req, test3.resp);
+    assertThat(new String(test3.responseBody.toByteArray()), is(""));
+  }
+
+  private static ByteArrayOutputStream expectPayload(HttpServletResponse resp) throws Exception {
+    ByteArrayOutputStream responseBody = new ByteArrayOutputStream();
+    expect(resp.getOutputStream()).andReturn(new FakeServletOutputStream(responseBody));
+    return responseBody;
+  }
+
+  private static String unzip(ByteArrayOutputStream streamData) throws IOException {
+    ByteArrayInputStream in = new ByteArrayInputStream(streamData.toByteArray());
+    GZIPInputStream unzip = new GZIPInputStream(in);
+    return new String(ByteStreams.toByteArray(unzip));
+  }
+
+  private static class FakeServletOutputStream extends ServletOutputStream {
+    private final OutputStream realStream;
+
+    FakeServletOutputStream(OutputStream realStream) {
+      this.realStream = realStream;
+    }
+
+    @Override
+    public void write(int b) throws IOException {
+      realStream.write(b);
+    }
+
+    @Override
+    public void write(byte[] b) throws IOException {
+      realStream.write(b);
+    }
+
+    @Override
+    public void write(byte[] b, int off, int len) throws IOException {
+      realStream.write(b, off, len);
+    }
+
+    @Override
+    public void flush() throws IOException {
+      realStream.flush();
+    }
+
+    @Override
+    public void close() throws IOException {
+      realStream.close();
+    }
+
+    @Override
+    public void print(String s) throws IOException {
+      throw new UnsupportedOperationException("Not implemented");
+    }
+
+    @Override
+    public void print(boolean b) throws IOException {
+      throw new UnsupportedOperationException("Not implemented");
+    }
+
+    @Override
+    public void print(char c) throws IOException {
+      throw new UnsupportedOperationException("Not implemented");
+    }
+
+    @Override
+    public void print(int i) throws IOException {
+      throw new UnsupportedOperationException("Not implemented");
+    }
+
+    @Override
+    public void print(long l) throws IOException {
+      throw new UnsupportedOperationException("Not implemented");
+    }
+
+    @Override
+    public void print(float f) throws IOException {
+      throw new UnsupportedOperationException("Not implemented");
+    }
+
+    @Override
+    public void print(double d) throws IOException {
+      throw new UnsupportedOperationException("Not implemented");
+    }
+
+    @Override
+    public void println() throws IOException {
+      throw new UnsupportedOperationException("Not implemented");
+    }
+
+    @Override
+    public void println(String s) throws IOException {
+      throw new UnsupportedOperationException("Not implemented");
+    }
+
+    @Override
+    public void println(boolean b) throws IOException {
+      throw new UnsupportedOperationException("Not implemented");
+    }
+
+    @Override
+    public void println(char c) throws IOException {
+      throw new UnsupportedOperationException("Not implemented");
+    }
+
+    @Override
+    public void println(int i) throws IOException {
+      throw new UnsupportedOperationException("Not implemented");
+    }
+
+    @Override
+    public void println(long l) throws IOException {
+      throw new UnsupportedOperationException("Not implemented");
+    }
+
+    @Override
+    public void println(float f) throws IOException {
+      throw new UnsupportedOperationException("Not implemented");
+    }
+
+    @Override
+    public void println(double d) throws IOException {
+      throw new UnsupportedOperationException("Not implemented");
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/aurora/blob/86a547b9/commons/src/test/java/com/twitter/common/net/http/handlers/HttpServletRequestParamsTest.java
----------------------------------------------------------------------
diff --git a/commons/src/test/java/com/twitter/common/net/http/handlers/HttpServletRequestParamsTest.java b/commons/src/test/java/com/twitter/common/net/http/handlers/HttpServletRequestParamsTest.java
new file mode 100644
index 0000000..5891cb1
--- /dev/null
+++ b/commons/src/test/java/com/twitter/common/net/http/handlers/HttpServletRequestParamsTest.java
@@ -0,0 +1,69 @@
+package com.twitter.common.net.http.handlers;
+
+import javax.servlet.http.HttpServletRequest;
+
+import org.easymock.EasyMock;
+import org.junit.Before;
+import org.junit.Test;
+
+import com.twitter.common.testing.easymock.EasyMockTest;
+
+import static org.easymock.EasyMock.expect;
+import static org.junit.Assert.assertEquals;
+
+public class HttpServletRequestParamsTest extends EasyMockTest {
+  private static final String INT_PARAM = "int_param";
+  private static final String LONG_PARAM = "long_param";
+  private static final String STRING_PARAM = "string_param";
+  private static final String UNSET_PARAM = "unset_param";
+  private static final String BOOL_PARAM = "bool_param";
+
+  private HttpServletRequest request;
+
+  @Before
+  public void setUp() throws Exception {
+    request = createMock(HttpServletRequest.class);
+    expect(request.getParameter(INT_PARAM)).andReturn("123").anyTimes();
+    expect(request.getParameter(LONG_PARAM)).andReturn("260833376896966656").anyTimes();
+    expect(request.getParameter(STRING_PARAM)).andReturn("asdf").anyTimes();
+    expect(request.getParameter(UNSET_PARAM)).andReturn(null).anyTimes();
+    expect(request.getParameter(BOOL_PARAM)).andReturn("TRUE").anyTimes();
+  }
+
+  @Test
+  public void testGetIntParam() {
+    EasyMock.replay(request);
+    assertEquals(123, HttpServletRequestParams.getInt(request, INT_PARAM, 456));
+    assertEquals(456, HttpServletRequestParams.getInt(request, STRING_PARAM, 456));
+    assertEquals(456, HttpServletRequestParams.getInt(request, UNSET_PARAM, 456));
+    assertEquals(456, HttpServletRequestParams.getInt(request, LONG_PARAM, 456));
+  }
+
+  @Test
+  public void testGetLongParam() {
+    EasyMock.replay(request);
+    assertEquals(123, HttpServletRequestParams.getLong(request, INT_PARAM, 456));
+    assertEquals(260833376896966656L, HttpServletRequestParams.getLong(request, LONG_PARAM, 456));
+    assertEquals(123456789012345678L,
+                 HttpServletRequestParams.getLong(request, STRING_PARAM, 123456789012345678L));
+    assertEquals(456, HttpServletRequestParams.getLong(request, UNSET_PARAM, 456));
+  }
+
+  @Test
+  public void testGetStringParam() {
+    EasyMock.replay(request);
+    assertEquals("123", HttpServletRequestParams.getString(request, INT_PARAM, "default"));
+    assertEquals("260833376896966656",
+                 HttpServletRequestParams.getString(request, LONG_PARAM, "default"));
+    assertEquals("asdf", HttpServletRequestParams.getString(request, STRING_PARAM, "default"));
+    assertEquals("default", HttpServletRequestParams.getString(request, UNSET_PARAM, "default"));
+  }
+
+  @Test
+  public void testGetBoolParam() {
+    EasyMock.replay(request);
+    assertEquals(false, HttpServletRequestParams.getBool(request, INT_PARAM, true));
+    assertEquals(false, HttpServletRequestParams.getBool(request, LONG_PARAM, false));
+    assertEquals(true, HttpServletRequestParams.getBool(request, BOOL_PARAM, false));
+  }
+}

http://git-wip-us.apache.org/repos/asf/aurora/blob/86a547b9/commons/src/test/java/com/twitter/common/net/http/handlers/LogPrinterTest.java
----------------------------------------------------------------------
diff --git a/commons/src/test/java/com/twitter/common/net/http/handlers/LogPrinterTest.java b/commons/src/test/java/com/twitter/common/net/http/handlers/LogPrinterTest.java
new file mode 100644
index 0000000..8f40913
--- /dev/null
+++ b/commons/src/test/java/com/twitter/common/net/http/handlers/LogPrinterTest.java
@@ -0,0 +1,74 @@
+package com.twitter.common.net.http.handlers;
+
+import java.io.File;
+import java.util.List;
+
+import com.google.common.base.Joiner;
+import com.google.common.collect.Lists;
+
+import org.junit.Test;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
+
+/**
+ * Test for the LogPrinter.
+ *
+ * @author William Farner
+ */
+public class LogPrinterTest {
+
+  @Test
+  public void testRelativeFileHandling() {
+    LogPrinter printer = new LogPrinter(new File("/this/is/the/log/dir"), true);
+    LogPrinter.LogFile absFile = printer.new LogFile("/absolute/path.log");
+    assertEquals("/absolute/path.log", absFile.getPath());
+    LogPrinter.LogFile relFile = printer.new LogFile("relative/file.log");
+    assertEquals("/this/is/the/log/dir/relative/file.log", relFile.getPath());
+  }
+
+  @Test
+  public void testFilterLines() {
+    testFilterLinesHelper(TEST_LINES, FILTER0, FILTERED_LINES0);
+    testFilterLinesHelper(TEST_LINES, FILTER1, FILTERED_LINES1);
+    testFilterLinesHelper(TEST_LINES, FILTER2, FILTERED_LINES2);
+    testFilterLinesHelper(TEST_LINES, FILTER3, FILTERED_LINES3);
+  }
+
+  private void testFilterLinesHelper(List<String> testLines,
+                                     String filter,
+                                     List<String> expectedLines) {
+
+    List<String> filteredLines = Lists.newArrayList(
+      LogPrinter.filterLines(Joiner.on("\n").join(testLines), filter).split("\n"));
+
+    assertThat(filteredLines, is(expectedLines));
+  }
+
+  private static final List<String> TEST_LINES = Lists.newArrayList(
+      "Matching line 1 twittttter",
+      "Matching line 2 twitter",
+      "Not matching line 1 twiter",
+      "Matching line 3"
+  );
+
+  private static final String FILTER0 = "";
+  private static final List<String> FILTERED_LINES0 = TEST_LINES;
+
+  private static final String FILTER1 = "Matching.*";
+  private static final List<String> FILTERED_LINES1 = Lists.newArrayList(
+      "Matching line 1 twittttter",
+      "Matching line 2 twitter",
+      "Matching line 3"
+  );
+
+  private static final String FILTER2 = "^.*twitt+er$";
+  private static final List<String> FILTERED_LINES2 = Lists.newArrayList(
+      "Matching line 1 twittttter",
+      "Matching line 2 twitter"
+  );
+
+  private static final String FILTER3 = "^.*\\d.*$";
+  private static final List<String> FILTERED_LINES3 = TEST_LINES;
+}