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:23 UTC
[09/37] aurora git commit: Import of Twitter Commons.
http://git-wip-us.apache.org/repos/asf/aurora/blob/86a547b9/commons/src/main/thrift/com/twitter/thrift/endpoint.thrift
----------------------------------------------------------------------
diff --git a/commons/src/main/thrift/com/twitter/thrift/endpoint.thrift b/commons/src/main/thrift/com/twitter/thrift/endpoint.thrift
new file mode 100644
index 0000000..2d14541
--- /dev/null
+++ b/commons/src/main/thrift/com/twitter/thrift/endpoint.thrift
@@ -0,0 +1,115 @@
+// =================================================================================================
+// 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.
+// =================================================================================================
+
+// Author: jsirois
+
+// TODO(wickman) Fix uses of this in python+science! Especially expertsearch
+// This should be aliased against twitter.thrift or twitter.common.service, or
+// wherever the python service discovery stack lands.
+
+namespace java com.twitter.thrift
+#@namespace scala com.twitter.thrift.endpoint.thriftscala
+namespace rb Twitter.Thrift
+namespace py gen.twitter.thrift.endpoint
+
+/*
+ * Represents the status of a service.
+ */
+enum Status {
+
+ /*
+ * The service is dead and can no longer be contacted.
+ */
+ DEAD = 0,
+
+ /*
+ * The service is in the process of starting up for the first time or from a STOPPED state.
+ */
+ STARTING = 1,
+
+ /*
+ * The service is alive and ready to receive requests.
+ */
+ ALIVE = 2,
+
+ /*
+ * The service is in the process of stopping and should no longer be contacted. In this state
+ * well behaved services will typically finish existing requests but accept no new rtequests.
+ */
+ STOPPING = 3,
+
+ /*
+ * The service is stopped and cannot be contacted unless started again.
+ */
+ STOPPED = 4,
+
+ /*
+ * The service is alive but in a potentially bad state.
+ */
+ WARNING = 5,
+}
+
+/*
+ * Represents a TCP service network endpoint.
+ */
+struct Endpoint {
+
+ /*
+ * The remote hostname or ip address of the endpoint.
+ */
+ 1: string host
+
+ /*
+ * The TCP port the endpoint listens on.
+ */
+ 2: i32 port
+}
+
+/*
+ * Represents information about the state of a service instance.
+ */
+struct ServiceInstance {
+
+ /*
+ * Represents the primary service interface endpoint. This is typically a thrift service
+ * endpoint.
+ */
+ 1: Endpoint serviceEndpoint
+
+ /*
+ * A mapping of any additional interfaces the service exports. The mapping is from logical
+ * interface names to endpoints. The map may be empty, but a typical additional endpoint mapping
+ * would provide the endoint got the "http-admin" debug interface for example.
+ *
+ * TODO(John Sirois): consider promoting string -> Enum or adding thrift string constants for common
+ * service names to help identify common beasts like ostrich-http-admin, ostrich-telnet and
+ * process-http-admin but still allow for new experimental interfaces as well without having to
+ * change this thift file.
+ */
+ 2: map<string, Endpoint> additionalEndpoints
+
+ /*
+ * The status of this service instance.
+ * NOTE: Only status ALIVE should be used. This field is pending removal.
+ * TODO(Sathya Hariesh): Remove the status field.
+ */
+ 3: Status status;
+
+ /*
+ * The shard identifier for this instance.
+ */
+ 4: optional i32 shard;
+}
http://git-wip-us.apache.org/repos/asf/aurora/blob/86a547b9/commons/src/test/java/com/twitter/common/application/AppLauncherTest.java
----------------------------------------------------------------------
diff --git a/commons/src/test/java/com/twitter/common/application/AppLauncherTest.java b/commons/src/test/java/com/twitter/common/application/AppLauncherTest.java
new file mode 100644
index 0000000..b428f5e
--- /dev/null
+++ b/commons/src/test/java/com/twitter/common/application/AppLauncherTest.java
@@ -0,0 +1,77 @@
+// =================================================================================================
+// 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.application;
+
+import java.lang.reflect.Field;
+
+import com.google.common.base.Predicates;
+
+import org.junit.Test;
+
+import com.twitter.common.args.Arg;
+import com.twitter.common.args.ArgFilters;
+import com.twitter.common.args.CmdLine;
+import com.twitter.common.args.constraints.NotNull;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * @author John Sirois
+ */
+public class AppLauncherTest {
+
+ public static class TestApp1 extends AbstractApplication {
+ @NotNull
+ @CmdLine(name = "user", help = "a username")
+ static final Arg<String> USER = Arg.create();
+
+ private static boolean hasRun;
+
+ @Override public void run() {
+ hasRun = true;
+ }
+ }
+
+ @Test
+ public void testLaunch1() {
+ AppLauncher.launch(TestApp1.class, ArgFilters.selectClass(TestApp1.class), "-user", "jake");
+ assertTrue(TestApp1.hasRun);
+ assertEquals("jake", TestApp1.USER.get());
+ }
+
+ public static class TestApp2 extends AbstractApplication {
+ @NotNull
+ @CmdLine(name = "user", help = "a username")
+ static final Arg<String> USER = Arg.create(null);
+
+ private static boolean hasRun;
+
+ @Override public void run() {
+ hasRun = true;
+ }
+ }
+
+ @Test
+ public void testLaunch2() {
+ // We filter out the NotNull Arg so we should be able to launch without specifying it.
+ AppLauncher.launch(TestApp2.class, Predicates.<Field>alwaysFalse());
+ assertTrue(TestApp2.hasRun);
+ assertNull(TestApp2.USER.get());
+ }
+}
http://git-wip-us.apache.org/repos/asf/aurora/blob/86a547b9/commons/src/test/java/com/twitter/common/application/modules/LifecycleModuleTest.java
----------------------------------------------------------------------
diff --git a/commons/src/test/java/com/twitter/common/application/modules/LifecycleModuleTest.java b/commons/src/test/java/com/twitter/common/application/modules/LifecycleModuleTest.java
new file mode 100644
index 0000000..2d8d61e
--- /dev/null
+++ b/commons/src/test/java/com/twitter/common/application/modules/LifecycleModuleTest.java
@@ -0,0 +1,124 @@
+package com.twitter.common.application.modules;
+
+import java.lang.Thread.UncaughtExceptionHandler;
+import java.net.InetSocketAddress;
+
+import com.google.common.base.Optional;
+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.Module;
+
+import org.junit.Test;
+
+import com.twitter.common.application.ShutdownRegistry.ShutdownRegistryImpl;
+import com.twitter.common.application.modules.LifecycleModule.LaunchException;
+import com.twitter.common.application.modules.LifecycleModule.ServiceRunner;
+import com.twitter.common.application.modules.LocalServiceRegistry.LocalService;
+import com.twitter.common.base.Command;
+import com.twitter.common.testing.easymock.EasyMockTest;
+
+import static org.easymock.EasyMock.expect;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+import static com.twitter.common.application.modules.LifecycleModule.bindLocalService;
+import static com.twitter.common.net.InetSocketAddressHelper.getLocalAddress;
+
+/**
+ * @author William Farner
+ */
+public class LifecycleModuleTest extends EasyMockTest {
+
+ private static class SystemModule extends AbstractModule {
+ @Override protected void configure() {
+ install(new LifecycleModule());
+ bind(UncaughtExceptionHandler.class).toInstance(new UncaughtExceptionHandler() {
+ @Override public void uncaughtException(Thread thread, Throwable throwable) {
+ fail("Uncaught exception.");
+ }
+ });
+ }
+ }
+
+ @Test
+ public void testNoServices() {
+ control.replay();
+
+ Injector injector = Guice.createInjector(new SystemModule());
+
+ LocalServiceRegistry registry = injector.getInstance(LocalServiceRegistry.class);
+ assertEquals(Optional.<InetSocketAddress>absent(), registry.getPrimarySocket());
+ assertEquals(ImmutableMap.<String, InetSocketAddress>of(), registry.getAuxiliarySockets());
+ }
+
+ @Test
+ public void testNoRunner() throws Exception {
+ final Command primaryShutdown = createMock(Command.class);
+ final Command auxShutdown = createMock(Command.class);
+
+ primaryShutdown.execute();
+ auxShutdown.execute();
+
+ Module testModule = new AbstractModule() {
+ @Override protected void configure() {
+ bindLocalService(binder(), LocalService.primaryService(99, primaryShutdown));
+ bindLocalService(binder(), LocalService.auxiliaryService("foo", 100, auxShutdown));
+ }
+ };
+
+ Injector injector = Guice.createInjector(new SystemModule(), testModule);
+ LocalServiceRegistry registry = injector.getInstance(LocalServiceRegistry.class);
+
+ control.replay();
+
+ assertEquals(Optional.of(getLocalAddress(99)), registry.getPrimarySocket());
+ assertEquals(ImmutableMap.of("foo", getLocalAddress(100)), registry.getAuxiliarySockets());
+
+ injector.getInstance(ShutdownRegistryImpl.class).execute();
+ }
+
+ @Test
+ public void testOrdering() throws Exception {
+ final ServiceRunner runner = createMock(ServiceRunner.class);
+ Command shutdown = createMock(Command.class);
+
+ expect(runner.launch()).andReturn(LocalService.primaryService(100, shutdown));
+ shutdown.execute();
+
+ Module testModule = new AbstractModule() {
+ @Override protected void configure() {
+ LifecycleModule.runnerBinder(binder()).addBinding().toInstance(runner);
+ }
+ };
+
+ Injector injector = Guice.createInjector(new SystemModule(), testModule);
+ LocalServiceRegistry registry = injector.getInstance(LocalServiceRegistry.class);
+
+ control.replay();
+
+ assertEquals(Optional.of(getLocalAddress(100)), registry.getPrimarySocket());
+ injector.getInstance(ShutdownRegistryImpl.class).execute();
+ }
+
+ @Test(expected = IllegalStateException.class)
+ public void testFailedLauncher() throws Exception {
+ final ServiceRunner runner = createMock(ServiceRunner.class);
+
+ expect(runner.launch()).andThrow(new LaunchException("Injected failure."));
+
+ Module testModule = new AbstractModule() {
+ @Override protected void configure() {
+ LifecycleModule.runnerBinder(binder()).addBinding().toInstance(runner);
+ }
+ };
+
+ Injector injector = Guice.createInjector(new SystemModule(), testModule);
+ LocalServiceRegistry registry = injector.getInstance(LocalServiceRegistry.class);
+
+ control.replay();
+
+ assertEquals(Optional.of(getLocalAddress(100)), registry.getPrimarySocket());
+ }
+}
http://git-wip-us.apache.org/repos/asf/aurora/blob/86a547b9/commons/src/test/java/com/twitter/common/application/modules/LocalServiceRegistryTest.java
----------------------------------------------------------------------
diff --git a/commons/src/test/java/com/twitter/common/application/modules/LocalServiceRegistryTest.java b/commons/src/test/java/com/twitter/common/application/modules/LocalServiceRegistryTest.java
new file mode 100644
index 0000000..50acdf7
--- /dev/null
+++ b/commons/src/test/java/com/twitter/common/application/modules/LocalServiceRegistryTest.java
@@ -0,0 +1,157 @@
+package com.twitter.common.application.modules;
+
+import java.net.InetSocketAddress;
+import java.util.Map;
+import java.util.Set;
+
+import com.google.common.base.Function;
+import com.google.common.base.Optional;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Maps;
+import com.google.inject.Provider;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import com.twitter.common.application.ShutdownRegistry;
+import com.twitter.common.application.modules.LifecycleModule.LaunchException;
+import com.twitter.common.application.modules.LifecycleModule.ServiceRunner;
+import com.twitter.common.application.modules.LocalServiceRegistry.LocalService;
+import com.twitter.common.base.Commands;
+import com.twitter.common.testing.easymock.EasyMockTest;
+
+import static org.easymock.EasyMock.expect;
+import static org.easymock.EasyMock.expectLastCall;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+
+/**
+ * @author William Farner
+ */
+public class LocalServiceRegistryTest extends EasyMockTest {
+
+ private static final Function<InetSocketAddress, Integer> INET_TO_PORT =
+ new Function<InetSocketAddress, Integer>() {
+ @Override public Integer apply(InetSocketAddress address) {
+ return address.getPort();
+ }
+ };
+
+ private static final String A = "a";
+ private static final String B = "b";
+ private static final String C = "c";
+
+ private ServiceRunner runner1;
+ private ServiceRunner runner2;
+ private Provider<Set<ServiceRunner>> serviceProvider;
+ private ShutdownRegistry shutdownRegistry;
+ private LocalServiceRegistry registry;
+
+ @Before
+ public void setUp() {
+ runner1 = createMock(ServiceRunner.class);
+ runner2 = createMock(ServiceRunner.class);
+ serviceProvider = createMock(new Clazz<Provider<Set<ServiceRunner>>>() { });
+ shutdownRegistry = createMock(ShutdownRegistry.class);
+ registry = new LocalServiceRegistry(serviceProvider, shutdownRegistry);
+ }
+
+ @Test
+ public void testCreate() throws LaunchException {
+ expect(serviceProvider.get()).andReturn(ImmutableSet.of(runner1, runner2));
+ expect(runner1.launch()).andReturn(primary(1));
+ expect(runner2.launch()).andReturn(auxiliary(A, 2));
+ shutdownRegistry.addAction(Commands.NOOP);
+ expectLastCall().times(2);
+
+ control.replay();
+
+ checkPorts(Optional.of(1), ImmutableMap.of(A, 2));
+ }
+
+ private LocalService primary(int port) {
+ return LocalService.primaryService(port, Commands.NOOP);
+ }
+
+ private LocalService auxiliary(String name, int port) {
+ return LocalService.auxiliaryService(name, port, Commands.NOOP);
+ }
+
+ private LocalService auxiliary(Set<String> names, int port) {
+ return LocalService.auxiliaryService(names, port, Commands.NOOP);
+ }
+
+ @Test
+ public void testNoPrimary() throws LaunchException {
+ expect(serviceProvider.get()).andReturn(ImmutableSet.of(runner1));
+ expect(runner1.launch()).andReturn(auxiliary(A, 2));
+ shutdownRegistry.addAction(Commands.NOOP);
+ expectLastCall().times(1);
+
+ control.replay();
+
+ assertFalse(registry.getPrimarySocket().isPresent());
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testMultiplePrimaries() throws LaunchException {
+ expect(serviceProvider.get()).andReturn(ImmutableSet.of(runner1, runner2));
+ expect(runner1.launch()).andReturn(primary(1));
+ expect(runner2.launch()).andReturn(primary(2));
+ shutdownRegistry.addAction(Commands.NOOP);
+ expectLastCall().times(2);
+
+ control.replay();
+
+ registry.getPrimarySocket();
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testDuplicateName() throws LaunchException {
+ expect(serviceProvider.get()).andReturn(ImmutableSet.of(runner1, runner2));
+ expect(runner1.launch()).andReturn(auxiliary(A, 1));
+ expect(runner2.launch()).andReturn(auxiliary(A, 2));
+ shutdownRegistry.addAction(Commands.NOOP);
+ expectLastCall().times(2);
+
+ control.replay();
+
+ registry.getPrimarySocket();
+ }
+
+ @Test
+ public void testAllowsPortReuse() throws LaunchException {
+ expect(serviceProvider.get()).andReturn(ImmutableSet.of(runner1, runner2));
+ expect(runner1.launch()).andReturn(auxiliary(A, 2));
+ expect(runner2.launch()).andReturn(auxiliary(B, 2));
+ shutdownRegistry.addAction(Commands.NOOP);
+ expectLastCall().times(2);
+
+ control.replay();
+
+ checkPorts(Optional.<Integer>absent(), ImmutableMap.of(A, 2, B, 2));
+ }
+
+ @Test
+ public void testMultiNameBreakout() throws LaunchException {
+ expect(serviceProvider.get()).andReturn(ImmutableSet.of(runner1, runner2));
+ expect(runner1.launch()).andReturn(auxiliary(A, 2));
+ expect(runner2.launch()).andReturn(auxiliary(ImmutableSet.of(B, C), 6));
+ shutdownRegistry.addAction(Commands.NOOP);
+ expectLastCall().times(2);
+
+ control.replay();
+
+ checkPorts(Optional.<Integer>absent(), ImmutableMap.of(A, 2, B, 6, C, 6));
+ }
+
+ private void checkPorts(Optional<Integer> primary, Map<String, Integer> expected) {
+ Optional<InetSocketAddress> registeredSocket = registry.getPrimarySocket();
+ Optional<Integer> registeredPort = registeredSocket.isPresent()
+ ? Optional.of(registeredSocket.get().getPort()) : Optional.<Integer>absent();
+
+ assertEquals(primary, registeredPort);
+ assertEquals(expected, Maps.transformValues(registry.getAuxiliarySockets(), INET_TO_PORT));
+ }
+}
http://git-wip-us.apache.org/repos/asf/aurora/blob/86a547b9/commons/src/test/java/com/twitter/common/application/modules/StartStatPollerTest.java
----------------------------------------------------------------------
diff --git a/commons/src/test/java/com/twitter/common/application/modules/StartStatPollerTest.java b/commons/src/test/java/com/twitter/common/application/modules/StartStatPollerTest.java
new file mode 100644
index 0000000..5def8a8
--- /dev/null
+++ b/commons/src/test/java/com/twitter/common/application/modules/StartStatPollerTest.java
@@ -0,0 +1,50 @@
+package com.twitter.common.application.modules;
+
+import java.util.Properties;
+
+import org.junit.Test;
+
+import com.twitter.common.application.ShutdownRegistry;
+import com.twitter.common.stats.Stat;
+import com.twitter.common.stats.Stats;
+import com.twitter.common.stats.TimeSeriesRepository;
+import com.twitter.common.testing.easymock.EasyMockTest;
+import com.twitter.common.util.BuildInfo;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+
+public class StartStatPollerTest extends EasyMockTest {
+ @Test
+ public void testStartStatPollerExecute() {
+ ShutdownRegistry shutdownRegistry = createMock(ShutdownRegistry.class);
+ TimeSeriesRepository repository = createMock(TimeSeriesRepository.class);
+
+ Properties properties = new Properties();
+ final Long gitRevisionNumber = 1404461016779713L;
+ properties.setProperty(BuildInfo.Key.GIT_REVISION_NUMBER.value, gitRevisionNumber.toString());
+ String gitRevision = "foo_branch";
+ properties.setProperty(BuildInfo.Key.GIT_REVISION.value, gitRevision);
+ BuildInfo buildInfo = new BuildInfo(properties);
+
+ StatsModule.StartStatPoller poller =
+ new StatsModule.StartStatPoller(shutdownRegistry, buildInfo, repository);
+
+ repository.start(shutdownRegistry);
+ control.replay();
+
+ poller.execute();
+
+ Stat<Long> gitRevisionNumberStat =
+ Stats.getVariable(Stats.normalizeName(BuildInfo.Key.GIT_REVISION_NUMBER.value));
+ assertEquals(gitRevisionNumber, gitRevisionNumberStat.read());
+
+ Stat<String> gitRevisionStat =
+ Stats.getVariable(Stats.normalizeName(BuildInfo.Key.GIT_REVISION.value));
+ assertEquals(gitRevision, gitRevisionStat.read());
+
+ Stat<String> gitBranchNameStat =
+ Stats.getVariable(Stats.normalizeName(BuildInfo.Key.GIT_BRANCHNAME.value));
+ assertNull(gitBranchNameStat);
+ }
+}
http://git-wip-us.apache.org/repos/asf/aurora/blob/86a547b9/commons/src/test/java/com/twitter/common/args/ArgFiltersTest.java
----------------------------------------------------------------------
diff --git a/commons/src/test/java/com/twitter/common/args/ArgFiltersTest.java b/commons/src/test/java/com/twitter/common/args/ArgFiltersTest.java
new file mode 100644
index 0000000..6dc8ad5
--- /dev/null
+++ b/commons/src/test/java/com/twitter/common/args/ArgFiltersTest.java
@@ -0,0 +1,108 @@
+// =================================================================================================
+// 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.args;
+
+import java.io.IOException;
+import java.lang.reflect.Field;
+
+import com.google.common.base.Optional;
+import com.google.common.base.Predicate;
+import com.google.common.base.Predicates;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Iterables;
+
+import org.junit.Test;
+
+import com.twitter.common.args.apt.Configuration;
+import com.twitter.common.args.argfilterstest.ArgsRoot;
+import com.twitter.common.args.argfilterstest.subpackageA.ArgsA;
+import com.twitter.common.args.argfilterstest.subpackageA.subsubpackage1.ArgsA1;
+import com.twitter.common.args.argfilterstest.subpackageB.ArgsB;
+import com.twitter.common.args.argfilterstest.subpackageBwithSuffix.ArgsBWithSuffix;
+
+import static org.junit.Assert.assertEquals;
+
+import static com.twitter.common.args.apt.Configuration.ArgInfo;
+
+/**
+ * @author John Sirois
+ */
+public class ArgFiltersTest {
+
+ @CmdLine(name = "a", help = "")
+ static final Arg<String> A = Arg.create();
+
+ @CmdLine(name = "b", help = "")
+ static final Arg<String> B = Arg.create();
+
+ @Test
+ public void testpackage() throws IOException {
+ testFilter(ArgFilters.selectPackage(ArgsRoot.class.getPackage()),
+ fieldInfo(ArgsRoot.class, "ARGS_ROOT"));
+ }
+
+ @Test
+ public void testAllPackagesUnderHere() throws IOException {
+ testFilter(ArgFilters.selectAllPackagesUnderHere(ArgsRoot.class.getPackage()),
+ fieldInfo(ArgsRoot.class, "ARGS_ROOT"),
+ fieldInfo(ArgsA.class, "ARGS_A"),
+ fieldInfo(ArgsB.class, "ARGS_B"),
+ fieldInfo(ArgsA1.class, "ARGS_A1"),
+ fieldInfo(ArgsBWithSuffix.class, "ARGS_B_WITH_SUFFIX"));
+
+ testFilter(ArgFilters.selectAllPackagesUnderHere(ArgsB.class.getPackage()),
+ fieldInfo(ArgsB.class, "ARGS_B"));
+ }
+
+ @Test
+ public void testClass() throws IOException {
+ testFilter(ArgFilters.selectClass(ArgFiltersTest.class),
+ fieldInfo(ArgFiltersTest.class, "A"), fieldInfo(ArgFiltersTest.class, "B"));
+ }
+
+ @Test
+ public void testClasses() throws IOException {
+ testFilter(ArgFilters.selectClasses(ArgFiltersTest.class, ArgsA.class),
+ fieldInfo(ArgFiltersTest.class, "A"),
+ fieldInfo(ArgFiltersTest.class, "B"),
+ fieldInfo(ArgsA.class, "ARGS_A"));
+ }
+
+ @Test
+ public void testArg() throws IOException {
+ testFilter(ArgFilters.selectCmdLineArg(ArgFiltersTest.class, "b"),
+ fieldInfo(ArgFiltersTest.class, "B"));
+ }
+
+ private static ArgInfo fieldInfo(Class<?> declaringClass, String fieldName) {
+ return new ArgInfo(declaringClass.getName(), fieldName);
+ }
+
+ private void testFilter(final Predicate<Field> filter, ArgInfo... expected)
+ throws IOException {
+
+ Predicate<Optional<Field>> fieldFilter = new Predicate<Optional<Field>>() {
+ @Override public boolean apply(Optional<Field> maybeField) {
+ return maybeField.isPresent() && filter.apply(maybeField.get());
+ }
+ };
+
+ assertEquals(ImmutableSet.copyOf(expected),
+ ImmutableSet.copyOf(Iterables.filter(Configuration.load().optionInfo(),
+ Predicates.compose(fieldFilter, Args.TO_FIELD))));
+ }
+}
http://git-wip-us.apache.org/repos/asf/aurora/blob/86a547b9/commons/src/test/java/com/twitter/common/args/ArgScannerTest.java
----------------------------------------------------------------------
diff --git a/commons/src/test/java/com/twitter/common/args/ArgScannerTest.java b/commons/src/test/java/com/twitter/common/args/ArgScannerTest.java
new file mode 100644
index 0000000..3a47f56
--- /dev/null
+++ b/commons/src/test/java/com/twitter/common/args/ArgScannerTest.java
@@ -0,0 +1,851 @@
+// =================================================================================================
+// 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.args;
+
+import java.io.File;
+import java.io.PrintStream;
+import java.io.Serializable;
+import java.lang.annotation.Annotation;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.regex.Pattern;
+
+import com.google.common.base.Preconditions;
+import com.google.common.base.Predicate;
+import com.google.common.base.Predicates;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Iterables;
+import com.google.common.io.ByteStreams;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import com.twitter.common.args.ArgScannerTest.StandardArgs.Optimizations;
+import com.twitter.common.args.constraints.NotEmpty;
+import com.twitter.common.args.constraints.NotNegative;
+import com.twitter.common.args.constraints.NotNull;
+import com.twitter.common.args.constraints.Positive;
+import com.twitter.common.args.constraints.Range;
+import com.twitter.common.args.parsers.NonParameterizedTypeParser;
+import com.twitter.common.base.Command;
+import com.twitter.common.base.Function;
+import com.twitter.common.base.MorePreconditions;
+import com.twitter.common.collections.Pair;
+import com.twitter.common.quantity.Amount;
+import com.twitter.common.quantity.Data;
+import com.twitter.common.quantity.Time;
+
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.CoreMatchers.nullValue;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+/**
+ * @author William Farner
+ */
+public class ArgScannerTest {
+
+ private static final Function<Class<?>, Predicate<Field>> TO_SCOPE_PREDICATE =
+ new Function<Class<?>, Predicate<Field>>() {
+ @Override public Predicate<Field> apply(final Class<?> cls) {
+ return new Predicate<Field>() {
+ @Override public boolean apply(Field field) {
+ return field.getDeclaringClass() == cls;
+ }
+ };
+ }
+ };
+
+ @Before
+ public void setUp() {
+ // Reset args in all classes before each test.
+ for (Class<?> cls : this.getClass().getDeclaredClasses()) {
+ resetArgs(cls);
+ }
+ }
+
+ public static class StandardArgs {
+ enum Optimizations { NONE, MINIMAL, ALL }
+ @CmdLine(name = "enum", help = "help")
+ static final Arg<Optimizations> ENUM_VAL = Arg.create(Optimizations.MINIMAL);
+ @CmdLine(name = "string", help = "help")
+ static final Arg<String> STRING_VAL = Arg.create("string");
+ @CmdLine(name = "char", help = "help")
+ static final Arg<Character> CHAR_VAL = Arg.create('c');
+ @CmdLine(name = "byte", help = "help")
+ static final Arg<Byte> BYTE_VAL = Arg.create((byte) 0);
+ @CmdLine(name = "short", help = "help")
+ static final Arg<Short> SHORT_VAL = Arg.create((short) 0);
+ @CmdLine(name = "int", help = "help")
+ static final Arg<Integer> INT_VAL = Arg.create(0);
+ @CmdLine(name = "long", help = "help")
+ static final Arg<Long> LONG_VAL = Arg.create(0L);
+ @CmdLine(name = "float", help = "help")
+ static final Arg<Float> FLOAT_VAL = Arg.create(0F);
+ @CmdLine(name = "double", help = "help")
+ static final Arg<Double> DOUBLE_VAL = Arg.create(0D);
+ @CmdLine(name = "bool", help = "help")
+ static final Arg<Boolean> BOOL = Arg.create(false);
+ @CmdLine(name = "regex", help = "help")
+ static final Arg<Pattern> REGEX = Arg.create(null);
+ @CmdLine(name = "time_amount", help = "help")
+ static final Arg<Amount<Long, Time>> TIME_AMOUNT = Arg.create(Amount.of(1L, Time.SECONDS));
+ @CmdLine(name = "data_amount", help = "help")
+ static final Arg<Amount<Long, Data>> DATA_AMOUNT = Arg.create(Amount.of(1L, Data.MB));
+ @CmdLine(name = "range", help = "help")
+ static final Arg<com.google.common.collect.Range<Integer>> RANGE =
+ Arg.create(com.google.common.collect.Range.closed(1, 5));
+ @Positional(help = "help")
+ static final Arg<List<Amount<Long, Time>>> POSITIONAL =
+ Arg.<List<Amount<Long, Time>>>create(ImmutableList.<Amount<Long, Time>>of());
+ }
+
+ @Test
+ public void testStandardArgs() {
+ test(StandardArgs.class,
+ new Command() {
+ @Override public void execute() {
+ assertThat(StandardArgs.ENUM_VAL.get(), is(Optimizations.ALL));
+ }
+ }, "enum", "ALL");
+ test(StandardArgs.class,
+ new Command() {
+ @Override public void execute() {
+ assertThat(StandardArgs.STRING_VAL.get(), is("newstring"));
+ }
+ },
+ "string", "newstring");
+ test(StandardArgs.class,
+ new Command() {
+ @Override public void execute() { assertThat(StandardArgs.CHAR_VAL.get(), is('x')); }
+ },
+ "char", "x");
+ test(StandardArgs.class,
+ new Command() {
+ @Override public void execute() {
+ assertThat(StandardArgs.BYTE_VAL.get(), is((byte) 10));
+ }
+ },
+ "byte", "10");
+ test(StandardArgs.class,
+ new Command() {
+ @Override public void execute() {
+ assertThat(StandardArgs.SHORT_VAL.get(), is((short) 10));
+ }
+ },
+ "short", "10");
+ test(StandardArgs.class,
+ new Command() {
+ @Override public void execute() { assertThat(StandardArgs.INT_VAL.get(), is(10)); }
+ },
+ "int", "10");
+ test(StandardArgs.class,
+ new Command() {
+ @Override public void execute() { assertThat(StandardArgs.LONG_VAL.get(), is(10L)); }
+ },
+ "long", "10");
+ test(StandardArgs.class,
+ new Command() {
+ @Override public void execute() { assertThat(StandardArgs.FLOAT_VAL.get(), is(10f)); }
+ },
+ "float", "10.0");
+ test(StandardArgs.class,
+ new Command() {
+ @Override public void execute() { assertThat(StandardArgs.DOUBLE_VAL.get(), is(10d)); }
+ },
+ "double", "10.0");
+ test(StandardArgs.class,
+ new Command() {
+ @Override public void execute() { assertThat(StandardArgs.BOOL.get(), is(true)); }
+ },
+ "bool", "true");
+ test(StandardArgs.class,
+ new Command() {
+ @Override public void execute() { assertThat(StandardArgs.BOOL.get(), is(true)); }
+ },
+ "bool", "");
+ test(StandardArgs.class,
+ new Command() {
+ @Override public void execute() {
+ assertThat(StandardArgs.REGEX.get().matcher("jack").matches(), is(true));
+ }
+ },
+ "regex", ".*ack$");
+ test(StandardArgs.class,
+ new Command() {
+ @Override public void execute() { assertThat(StandardArgs.BOOL.get(), is(false)); }
+ },
+ "no_bool", "");
+ test(StandardArgs.class,
+ new Command() {
+ @Override public void execute() { assertThat(StandardArgs.BOOL.get(), is(true)); }
+ },
+ "no_bool", "false");
+ test(StandardArgs.class,
+ new Command() {
+ @Override public void execute() {
+ assertThat(StandardArgs.TIME_AMOUNT.get(), is(Amount.of(100L, Time.SECONDS)));
+ }
+ },
+ "time_amount", "100secs");
+ test(StandardArgs.class,
+ new Command() {
+ @Override public void execute() {
+ assertThat(StandardArgs.DATA_AMOUNT.get(), is(Amount.of(1L, Data.Gb)));
+ }
+ },
+ "data_amount", "1Gb");
+ test(StandardArgs.class,
+ new Command() {
+ @Override public void execute() {
+ assertThat(StandardArgs.RANGE.get(), is(com.google.common.collect.Range.closed(1, 5)));
+ }
+ },
+ "range", "1-5");
+
+ resetArgs(StandardArgs.class);
+ assertTrue(parse(StandardArgs.class, "1mins", "2secs"));
+ assertEquals(ImmutableList.builder()
+ .add(Amount.of(60L, Time.SECONDS))
+ .add(Amount.of(2L, Time.SECONDS)).build(), StandardArgs.POSITIONAL.get());
+ }
+
+ public static class Name {
+ private final String name;
+
+ public Name(String name) {
+ this.name = MorePreconditions.checkNotBlank(name);
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ @Override
+ public int hashCode() {
+ return this.name.hashCode();
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ return (obj instanceof Name) && name.equals(((Name) obj).name);
+ }
+ }
+
+ @ArgParser
+ public static class NameParser extends NonParameterizedTypeParser<Name> {
+ @Override public Name doParse(String raw) {
+ return new Name(raw);
+ }
+ }
+
+ public static class MeaningOfLife {
+ private final Long answer;
+
+ public MeaningOfLife(Long answer) {
+ this.answer = Preconditions.checkNotNull(answer);
+ }
+
+ @Override
+ public int hashCode() {
+ return this.answer.hashCode();
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ return (obj instanceof MeaningOfLife) && answer.equals(((MeaningOfLife) obj).answer);
+ }
+ }
+
+ public static class Monty extends NonParameterizedTypeParser<MeaningOfLife> {
+ @Override public MeaningOfLife doParse(String raw) {
+ return new MeaningOfLife(42L);
+ }
+ }
+
+ public static class CustomArgs {
+ @CmdLine(name = "custom1", help = "help")
+ static final Arg<Name> NAME_VAL = Arg.create(new Name("jim"));
+
+ @CmdLine(name = "custom2", help = "help", parser = Monty.class)
+ static final Arg<MeaningOfLife> MEANING_VAL = Arg.create(new MeaningOfLife(13L));
+ }
+
+ @Test
+ public void testCustomArgs() {
+ test(CustomArgs.class,
+ new Command() {
+ @Override public void execute() {
+ assertThat(CustomArgs.NAME_VAL.get(), is(new Name("jane")));
+ }
+ }, "custom1", "jane");
+ test(CustomArgs.class,
+ new Command() {
+ @Override public void execute() {
+ assertThat(CustomArgs.MEANING_VAL.get(), is(new MeaningOfLife(42L)));
+ }
+ }, "custom2", "jim");
+ }
+
+ @Test
+ public void testHelp() {
+ assertFalse(parse(StandardArgs.class, "-h"));
+ assertFalse(parse(StandardArgs.class, "-help"));
+ }
+
+ @Test
+ public void testAllowsEmptyString() {
+ parse(StandardArgs.class, "-string=");
+ assertThat(StandardArgs.STRING_VAL.get(), is(""));
+
+ resetArgs(StandardArgs.class);
+
+ parse(StandardArgs.class, "-string=''");
+ assertThat(StandardArgs.STRING_VAL.get(), is(""));
+
+ resetArgs(StandardArgs.class);
+
+ parse(StandardArgs.class, "-string=\"\"");
+ assertThat(StandardArgs.STRING_VAL.get(), is(""));
+ }
+
+ public static class CollectionArgs {
+ @CmdLine(name = "stringList", help = "help")
+ static final Arg<List<String>> STRING_LIST = Arg.create(null);
+ @CmdLine(name = "intList", help = "help")
+ static final Arg<List<Integer>> INT_LIST = Arg.create(null);
+ @CmdLine(name = "stringSet", help = "help")
+ static final Arg<Set<String>> STRING_SET = Arg.create(null);
+ @CmdLine(name = "intSet", help = "help")
+ static final Arg<Set<Integer>> INT_SET = Arg.create(null);
+ @CmdLine(name = "stringStringMap", help = "help")
+ static final Arg<Map<String, String>> STRING_STRING_MAP = Arg.create(null);
+ @CmdLine(name = "intIntMap", help = "help")
+ static final Arg<Map<Integer, Integer>> INT_INT_MAP = Arg.create(null);
+ @CmdLine(name = "stringIntMap", help = "help")
+ static final Arg<Map<String, Integer>> STRING_INT_MAP = Arg.create(null);
+ @CmdLine(name = "intStringMap", help = "help")
+ static final Arg<Map<Integer, String>> INT_STRING_MAP = Arg.create(null);
+ @CmdLine(name = "stringStringPair", help = "help")
+ static final Arg<Pair<String, String>> STRING_STRING_PAIR = Arg.create(null);
+ @CmdLine(name = "intIntPair", help = "help")
+ static final Arg<Pair<Integer, Integer>> INT_INT_PAIR = Arg.create(null);
+ @CmdLine(name = "stringTimeAmountPair", help = "help")
+ static final Arg<Pair<String, Amount<Long, Time>>> STRING_TIME_AMOUNT_PAIR = Arg.create(null);
+ }
+
+ @Test
+ public void testCollectionArgs() {
+ test(CollectionArgs.class,
+ new Command() {
+ @Override public void execute() {
+ assertThat(CollectionArgs.STRING_LIST.get(), is(Arrays.asList("a", "b", "c", "d")));
+ }
+ },
+ "stringList", "a,b,c,d");
+ test(CollectionArgs.class,
+ new Command() {
+ @Override public void execute() {
+ assertThat(CollectionArgs.INT_LIST.get(), is(Arrays.asList(1, 2, 3, 4)));
+ }
+ },
+ "intList", "1, 2, 3, 4");
+ test(CollectionArgs.class,
+ new Command() {
+ @Override public void execute() {
+ Set<String> expected = ImmutableSet.of("a", "b", "c", "d");
+ assertThat(CollectionArgs.STRING_SET.get(), is(expected));
+ }
+ },
+ "stringSet", "a,b,c,d");
+ test(CollectionArgs.class,
+ new Command() {
+ @Override public void execute() {
+ Set<Integer> expected = ImmutableSet.of(1, 2, 3, 4);
+ assertThat(CollectionArgs.INT_SET.get(), is(expected));
+ }
+ },
+ "intSet", "1, 2, 3, 4");
+ test(CollectionArgs.class,
+ new Command() {
+ @Override public void execute() {
+ Map<String, String> expected = ImmutableMap.of("a", "b", "c", "d", "e", "f", "g", "h");
+ assertThat(CollectionArgs.STRING_STRING_MAP.get(), is(expected));
+ }
+ },
+ "stringStringMap", "a=b, c=d, e=f, g=h");
+ test(CollectionArgs.class,
+ new Command() {
+ @Override public void execute() {
+ Map<Integer, Integer> expected = ImmutableMap.of(1, 2, 3, 4, 5, 6, 7, 8);
+ assertThat(CollectionArgs.INT_INT_MAP.get(), is(expected));
+ }
+ },
+ "intIntMap", "1 = 2,3=4, 5=6 ,7=8");
+ test(CollectionArgs.class,
+ new Command() {
+ @Override public void execute() {
+ Map<String, Integer> expected = ImmutableMap.of("a", 1, "b", 2, "c", 3, "d", 4);
+ assertThat(CollectionArgs.STRING_INT_MAP.get(), is(expected));
+ }
+ },
+ "stringIntMap", "a=1 , b=2, c=3 ,d=4");
+ test(CollectionArgs.class,
+ new Command() {
+ @Override public void execute() {
+ Map<Integer, String> expected = ImmutableMap.of(1, "1", 2, "2", 3, "3", 4, "4");
+ assertThat(CollectionArgs.INT_STRING_MAP.get(), is(expected));
+ }
+ },
+ "intStringMap", " 1=1 , 2=2, 3=3,4=4");
+ test(CollectionArgs.class,
+ new Command() {
+ @Override public void execute() {
+ assertThat(CollectionArgs.STRING_STRING_PAIR.get(), is(Pair.of("foo", "bar")));
+ }
+ },
+ "stringStringPair", "foo , bar");
+ test(CollectionArgs.class,
+ new Command() {
+ @Override public void execute() {
+ assertThat(CollectionArgs.INT_INT_PAIR.get(), is(Pair.of(10, 20)));
+ }
+ },
+ "intIntPair", "10 ,20");
+ test(CollectionArgs.class,
+ new Command() {
+ @Override public void execute() {
+ assertThat(CollectionArgs.STRING_TIME_AMOUNT_PAIR.get(),
+ is(Pair.of("fred", Amount.of(42L, Time.MINUTES))));
+ }
+ },
+ "stringTimeAmountPair", "fred ,42mins");
+ test(CollectionArgs.class,
+ new Command() {
+ @Override public void execute() {
+ CollectionArgs.STRING_TIME_AMOUNT_PAIR.get();
+ }
+ },
+ true, "stringTimeAmountPair", "george,1MB");
+
+ }
+
+ static class Serializable1 implements Serializable { }
+ static class Serializable2 implements Serializable { }
+
+ public static class WildcardArgs {
+ @CmdLine(name = "class", help = "help")
+ static final Arg<? extends Class<? extends Serializable>> CLAZZ =
+ Arg.create(Serializable1.class);
+ @CmdLine(name = "classList1", help = "help")
+ static final Arg<List<Class<? extends Serializable>>> CLASS_LIST_1 = Arg.create(null);
+ @CmdLine(name = "classList2", help = "help")
+ static final Arg<List<? extends Class<? extends Serializable>>> CLASS_LIST_2 = Arg.create(null);
+ }
+
+ @Test
+ public void testWildcardArgs() {
+ test(WildcardArgs.class,
+ new Command() {
+ @Override public void execute() {
+ assertSame(Serializable2.class, WildcardArgs.CLAZZ.get());
+ }
+ },
+ "class", Serializable2.class.getName());
+
+ test(WildcardArgs.class,
+ new Command() {
+ @Override public void execute() {
+ WildcardArgs.CLAZZ.get();
+ }
+ },
+ true, "class", Runnable.class.getName());
+
+ test(WildcardArgs.class,
+ new Command() {
+ @Override public void execute() {
+ assertEquals(ImmutableList.of(Serializable1.class, Serializable2.class),
+ WildcardArgs.CLASS_LIST_1.get());
+ }
+ },
+ "classList1", Serializable1.class.getName() + "," + Serializable2.class.getName());
+
+ test(WildcardArgs.class,
+ new Command() {
+ @Override public void execute() {
+ assertEquals(ImmutableList.of(Serializable2.class), WildcardArgs.CLASS_LIST_2.get());
+ }
+ },
+ "classList2", Serializable2.class.getName());
+
+ test(WildcardArgs.class,
+ new Command() {
+ @Override public void execute() {
+ WildcardArgs.CLASS_LIST_2.get();
+ }
+ },
+ true, "classList2", Serializable1.class.getName() + "," + Runnable.class.getName());
+ }
+
+ @Target(FIELD)
+ @Retention(RUNTIME)
+ public static @interface Equals {
+ String value();
+ }
+
+ @VerifierFor(Equals.class)
+ public static class SameName implements Verifier<Name> {
+ @Override
+ public void verify(Name value, Annotation annotation) {
+ Preconditions.checkArgument(getValue(annotation).equals(value.getName()));
+ }
+
+ @Override
+ public String toString(Class<? extends Name> argType, Annotation annotation) {
+ return "name = " + getValue(annotation);
+ }
+
+ private String getValue(Annotation annotation) {
+ return ((Equals) annotation).value();
+ }
+ }
+
+ public static class VerifyArgs {
+ @Equals("jake") @CmdLine(name = "custom", help = "help")
+ static final Arg<Name> CUSTOM_VAL = Arg.create(new Name("jake"));
+ @NotEmpty @CmdLine(name = "string", help = "help")
+ static final Arg<String> STRING_VAL = Arg.create("string");
+ @NotEmpty @CmdLine(name = "optional_string", help = "help")
+ static final Arg<String> OPTIONAL_STRING_VAL = Arg.create(null);
+ @Positive @CmdLine(name = "int", help = "help")
+ static final Arg<Integer> INT_VAL = Arg.create(1);
+ @NotNegative @CmdLine(name = "long", help = "help")
+ static final Arg<Long> LONG_VAL = Arg.create(0L);
+ @Range(lower = 10, upper = 20) @CmdLine(name = "float", help = "help")
+ static final Arg<Float> FLOAT_VAL = Arg.create(10F);
+ @CmdLine(name = "double", help = "help")
+ static final Arg<Double> DOUBLE_VAL = Arg.create(0D);
+ @CmdLine(name = "bool", help = "help")
+ static final Arg<Boolean> BOOL = Arg.create(false);
+ @CmdLine(name = "arg_without_default", help = "help")
+ static final Arg<Boolean> ARG_WITHOUT_DEFAULT = Arg.create();
+ }
+
+ @Test
+ public void testEnforcesConstraints() {
+ test(VerifyArgs.class,
+ new Command() {
+ @Override public void execute() {
+ assertThat(VerifyArgs.STRING_VAL.get(), is("newstring"));
+ assertThat(VerifyArgs.OPTIONAL_STRING_VAL.get(), nullValue(String.class));
+ }
+ },
+ "string", "newstring");
+
+ testFails(VerifyArgs.class, "custom", "jane");
+ testFails(VerifyArgs.class, "string", "");
+ testFails(VerifyArgs.class, "optional_string", "");
+ testFails(VerifyArgs.class, "int", "0");
+ testFails(VerifyArgs.class, "long", "-1");
+
+ test(VerifyArgs.class,
+ new Command() {
+ @Override public void execute() {
+ assertThat(VerifyArgs.FLOAT_VAL.get(), is(10.5f));
+ }
+ },
+ "float", "10.5");
+ testFails(VerifyArgs.class, "float", "9");
+ }
+
+ @Test
+ public void testJoinKeysToValues() {
+ assertThat(ArgScanner.joinKeysToValues(Arrays.asList("")), is(Arrays.asList("")));
+ assertThat(ArgScanner.joinKeysToValues(Arrays.asList("-a", "b", "-c", "-d")),
+ is(Arrays.asList("-a=b", "-c", "-d")));
+ assertThat(ArgScanner.joinKeysToValues(Arrays.asList("-a='b'", "-c", "-d", "'e'")),
+ is(Arrays.asList("-a='b'", "-c", "-d='e'")));
+ assertThat(ArgScanner.joinKeysToValues(Arrays.asList("-a=-b", "c", "-d", "\"e\"")),
+ is(Arrays.asList("-a=-b", "c", "-d=\"e\"")));
+ }
+
+ public static class ShortHelpArg {
+ @CmdLine(name = "h", help = "help")
+ static final Arg<String> SHORT_HELP = Arg.create("string");
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testShortHelpReserved() {
+ parse(ShortHelpArg.class);
+ }
+
+ public static class LongHelpArg {
+ @CmdLine(name = "help", help = "help")
+ static final Arg<String> LONG_HELP = Arg.create("string");
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testLongHelpReserved() {
+ parse(LongHelpArg.class);
+ }
+
+ public static class DuplicateNames {
+ @CmdLine(name = "string", help = "help") static final Arg<String> STRING_1 = Arg.create();
+ @CmdLine(name = "string", help = "help") static final Arg<String> STRING_2 = Arg.create();
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testRejectsDuplicates() {
+ parse(DuplicateNames.class, "-string-str");
+ }
+
+ public static class OneRequired {
+ @CmdLine(name = "string1", help = "help")
+ static final Arg<String> STRING_1 = Arg.create(null);
+ @NotNull @CmdLine(name = "string2", help = "help")
+ static final Arg<String> STRING_2 = Arg.create(null);
+ }
+
+ @Test
+ public void testRequiredProvided() {
+ parse(OneRequired.class, "-string2=blah");
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testMissingRequired() {
+ parse(OneRequired.class, "-string1=blah");
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testUnrecognizedArg() {
+ parse(OneRequired.class, "-string2=blah", "-string3=blah");
+ }
+
+ public static class NameClashA {
+ @CmdLine(name = "string", help = "help")
+ static final Arg<String> STRING = Arg.create(null);
+ @CmdLine(name = "boolean", help = "help")
+ static final Arg<Boolean> BOOLEAN = Arg.create(true);
+ }
+
+ public static class NameClashB {
+ @CmdLine(name = "string", help = "help")
+ static final Arg<String> STRING_1 = Arg.create(null);
+ @CmdLine(name = "boolean", help = "help")
+ static final Arg<Boolean> BOOLEAN_1 = Arg.create(true);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testDisallowsShortNameOnArgCollision() {
+ parse(ImmutableList.of(NameClashA.class, NameClashB.class), "-string=blah");
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testDisallowsShortNegNameOnArgCollision() {
+ parse(ImmutableList.of(NameClashA.class, NameClashB.class), "-no_boolean");
+ }
+
+ @Test
+ public void testAllowsCanonicalNameOnArgCollision() {
+ // TODO(William Farner): Fix.
+ parse(ImmutableList.of(NameClashA.class, NameClashB.class),
+ "-" + NameClashB.class.getCanonicalName() + ".string=blah");
+ }
+
+ @Test
+ public void testAllowsCanonicalNegNameOnArgCollision() {
+ parse(ImmutableList.of(NameClashA.class, NameClashB.class),
+ "-" + NameClashB.class.getCanonicalName() + ".no_boolean");
+ }
+
+ public static class AmountContainer {
+ @CmdLine(name = "time_amount", help = "help")
+ static final Arg<Amount<Integer, Time>> TIME_AMOUNT = Arg.create(null);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testBadUnitType() {
+ parse(ImmutableList.of(AmountContainer.class), "-time_amount=1Mb");
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testUnrecognizedUnitType() {
+ parse(ImmutableList.of(AmountContainer.class), "-time_amount=1abcd");
+ }
+
+ static class Main1 {
+ @Positional(help = "halp")
+ static final Arg<List<String>> NAMES = Arg.create(null);
+ }
+
+ static class Main2 {
+ @Positional(help = "halp")
+ static final Arg<List<List<String>>> ROSTERS = Arg.create(null);
+ }
+
+ static class Main3 {
+ @Positional(help = "halp")
+ static final Arg<List<Double>> PERCENTILES = Arg.create(null);
+
+ @Positional(help = "halp")
+ static final Arg<List<File>> FILES = Arg.create(null);
+ }
+
+ private void resetMainArgs() {
+ resetArgs(Main1.class);
+ resetArgs(Main2.class);
+ resetArgs(Main3.class);
+ }
+
+ @Test
+ public void testMultiplePositionalsFails() {
+ // Indivdually these should work.
+
+ resetMainArgs();
+ assertTrue(parse(Main1.class, "jack,jill", "laurel,hardy"));
+ assertEquals(ImmutableList.of("jack,jill", "laurel,hardy"),
+ ImmutableList.copyOf(Main1.NAMES.get()));
+
+ resetMainArgs();
+ assertTrue(parse(Main2.class, "jack,jill", "laurel,hardy"));
+ assertEquals(
+ ImmutableList.of(
+ ImmutableList.of("jack", "jill"),
+ ImmutableList.of("laurel", "hardy")),
+ ImmutableList.copyOf(Main2.ROSTERS.get()));
+
+ // But if combined in the same class or across classes the @Positional is ambiguous and we
+ // should fail fast.
+
+ resetMainArgs();
+ try {
+ parse(ImmutableList.of(Main1.class, Main2.class), "jack,jill", "laurel,hardy");
+ fail("Expected more than 1 in-scope @Positional Arg List to trigger a failure.");
+ } catch (IllegalArgumentException e) {
+ // expected
+ }
+
+ resetMainArgs();
+ try {
+ parse(Main3.class, "50", "90", "99", "99.9");
+ fail("Expected more than 1 in-scope @Positional Arg List to trigger a failure.");
+ } catch (IllegalArgumentException e) {
+ // expected
+ }
+ }
+
+ // TODO(William Farner): Do we want to support nested parameterized args? If so, need to define a
+ // syntax for that and build it in.
+ // e.g. List<List<Integer>>, List<Pair<String, String>>
+
+ private static void testFails(Class<?> scope, String arg, String value) {
+ test(scope, null, true, arg, value);
+ }
+
+ private static void test(Class<?> scope, Command validate, String arg, String value) {
+ test(scope, validate, false, arg, value);
+ }
+
+ private static void test(Class<?> scope, Command validate, boolean expectFails, String arg,
+ String value) {
+ String canonicalName = scope.getCanonicalName() + "." + arg;
+
+ if (value.isEmpty()) {
+ testValidate(scope, validate, expectFails, String.format("-%s", arg));
+ testValidate(scope, validate, expectFails, String.format("-%s", canonicalName));
+ } else {
+ testValidate(scope, validate, expectFails, String.format("-%s=%s", arg, value));
+ testValidate(scope, validate, expectFails, String.format("-%s=%s", canonicalName, value));
+ testValidate(scope, validate, expectFails, String.format("-%s='%s'", arg, value));
+ testValidate(scope, validate, expectFails, String.format("-%s='%s'", canonicalName, value));
+ testValidate(scope, validate, expectFails, String.format("-%s=\"%s\"", arg, value));
+ testValidate(scope, validate, expectFails, String.format("-%s=\"%s\"", canonicalName, value));
+ testValidate(scope, validate, expectFails, String.format("-%s", arg), value);
+ testValidate(scope, validate, expectFails, String.format("-%s", canonicalName), value);
+ testValidate(scope, validate, expectFails,
+ String.format("-%s", arg), String.format("'%s'", value));
+ testValidate(scope, validate, expectFails,
+ String.format("-%s", canonicalName), String.format("'%s'", value));
+ testValidate(scope, validate, expectFails, String.format("-%s \"%s\"", arg, value));
+ testValidate(scope, validate, expectFails, String.format("-%s \"%s\"", canonicalName, value));
+ testValidate(scope, validate, expectFails,
+ String.format("-%s", arg), String.format("%s", value));
+ testValidate(scope, validate, expectFails,
+ String.format("-%s", canonicalName), String.format("%s", value));
+ }
+ }
+
+ private static void testValidate(Class<?> scope, Command validate, boolean expectFails,
+ String... args) {
+ resetArgs(scope);
+ IllegalArgumentException exception = null;
+ try {
+ assertTrue(parse(scope, args));
+ } catch (IllegalArgumentException e) {
+ exception = e;
+ }
+
+ if (!expectFails && exception != null) {
+ throw exception;
+ }
+ if (expectFails && exception == null) {
+ fail("Expected exception.");
+ }
+
+ if (validate != null) {
+ validate.execute();
+ }
+ resetArgs(scope);
+ }
+
+ private static void resetArgs(Class<?> scope) {
+ for (Field field : scope.getDeclaredFields()) {
+ if (Arg.class.isAssignableFrom(field.getType()) && Modifier.isStatic(field.getModifiers())) {
+ try {
+ ((Arg) field.get(null)).reset();
+ } catch (IllegalAccessException e) {
+ fail(e.getMessage());
+ }
+ }
+ }
+ }
+
+ private static boolean parse(final Class<?> scope, String... args) {
+ return parse(ImmutableList.of(scope), args);
+ }
+
+ private static boolean parse(Iterable<? extends Class<?>> scopes, String... args) {
+ Predicate<Field> filter = Predicates.or(Iterables.transform(scopes, TO_SCOPE_PREDICATE));
+ PrintStream devNull = new PrintStream(ByteStreams.nullOutputStream());
+ return new ArgScanner(devNull).parse(filter, Arrays.asList(args));
+ }
+}
http://git-wip-us.apache.org/repos/asf/aurora/blob/86a547b9/commons/src/test/java/com/twitter/common/args/ArgTest.java
----------------------------------------------------------------------
diff --git a/commons/src/test/java/com/twitter/common/args/ArgTest.java b/commons/src/test/java/com/twitter/common/args/ArgTest.java
new file mode 100644
index 0000000..3a20eca
--- /dev/null
+++ b/commons/src/test/java/com/twitter/common/args/ArgTest.java
@@ -0,0 +1,37 @@
+// =================================================================================================
+// 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.args;
+
+import org.junit.Test;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.fail;
+
+public class ArgTest {
+
+ @Test
+ public void testSetAfterGet() {
+ Arg<Boolean> arg = new Arg<Boolean>(false);
+ arg.get();
+ try {
+ arg.set(true);
+ fail("Expected set after get to throw");
+ } catch (IllegalStateException e) {
+ assertFalse(arg.get());
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/aurora/blob/86a547b9/commons/src/test/java/com/twitter/common/args/ArgsTest.java
----------------------------------------------------------------------
diff --git a/commons/src/test/java/com/twitter/common/args/ArgsTest.java b/commons/src/test/java/com/twitter/common/args/ArgsTest.java
new file mode 100644
index 0000000..d6a8b98
--- /dev/null
+++ b/commons/src/test/java/com/twitter/common/args/ArgsTest.java
@@ -0,0 +1,76 @@
+// =================================================================================================
+// 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.args;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.List;
+
+import com.google.common.collect.ImmutableList;
+
+import org.junit.Test;
+
+import com.twitter.common.args.constraints.NotEmpty;
+import com.twitter.common.args.constraints.Range;
+
+import static junit.framework.Assert.assertEquals;
+
+public class ArgsTest {
+ private static class App {
+ @CmdLine(name = "db", help = "help")
+ private static final Arg<File> DB = Arg.create();
+
+ @NotEmpty
+ @CmdLine(name = "name", help = "help")
+ private final Arg<String> name = Arg.create();
+
+ @Positional(help = "help")
+ private final Arg<List<Integer>> values = Arg.create();
+ }
+
+ @Test
+ public void testMixed() throws IOException {
+ App app = new App();
+
+ new ArgScanner().parse(Args.from(ArgFilters.selectClass(App.class), app),
+ ImmutableList.of("-name=bob", "-db=fred", "1", "137"));
+
+ assertEquals(new File("fred"), App.DB.get());
+ assertEquals("bob", app.name.get());
+ assertEquals(ImmutableList.of(1, 137), app.values.get());
+ }
+
+ @Test
+ public void testReentrance() throws IOException {
+ class InnerApp {
+ @Range(lower = 0.0, upper = 1.0)
+ @CmdLine(name = "level", help = "help")
+ private final Arg<Double> level = Arg.create();
+ }
+
+ InnerApp app1 = new InnerApp();
+ InnerApp app2 = new InnerApp();
+
+ new ArgScanner().parse(Args.from(ArgFilters.selectClass(InnerApp.class), app1),
+ ImmutableList.of("-level=0.5"));
+ new ArgScanner().parse(Args.from(ArgFilters.selectClass(InnerApp.class), app2),
+ ImmutableList.of("-level=0.00729"));
+
+ assertEquals(0.5, app1.level.get(), 0.00001);
+ assertEquals(0.00729, app2.level.get(), 0.00001);
+ }
+}
http://git-wip-us.apache.org/repos/asf/aurora/blob/86a547b9/commons/src/test/java/com/twitter/common/args/OptionInfoTest.java
----------------------------------------------------------------------
diff --git a/commons/src/test/java/com/twitter/common/args/OptionInfoTest.java b/commons/src/test/java/com/twitter/common/args/OptionInfoTest.java
new file mode 100644
index 0000000..166846d
--- /dev/null
+++ b/commons/src/test/java/com/twitter/common/args/OptionInfoTest.java
@@ -0,0 +1,111 @@
+// =================================================================================================
+// Copyright 2015 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.args;
+
+import java.io.File;
+import java.util.List;
+
+import com.google.common.base.Charsets;
+import com.google.common.collect.ImmutableList;
+import com.google.common.io.Files;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+
+import org.junit.rules.TemporaryFolder;
+
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertTrue;
+
+public class OptionInfoTest {
+ private static class App {
+ @CmdLine(name = "files", help = "help.", argFile = true)
+ private final Arg<List<File>> files = Arg.<List<File>>create(ImmutableList.<File>of());
+
+ @CmdLine(name = "flag", help = "help.")
+ private final Arg<Boolean> flag = Arg.create();
+ }
+
+ @Rule
+ public TemporaryFolder tmpDir = new TemporaryFolder();
+ private App app;
+
+ @Before
+ public void setUp() throws Exception {
+ app = new App();
+ }
+
+ @Test
+ public void testArgumentFilesCreateFromField() throws Exception {
+ OptionInfo optionInfo = OptionInfo.createFromField(App.class.getDeclaredField("files"), app);
+ assertEquals("files", optionInfo.getName());
+ assertEquals(
+ String.format(OptionInfo.ARG_FILE_HELP_TEMPLATE, "help.", "files", "files"),
+ optionInfo.getHelp());
+ assertTrue(optionInfo.argFile());
+ assertEquals("com.twitter.common.args.OptionInfoTest.App.files",
+ optionInfo.getCanonicalName());
+ }
+
+ @Test
+ public void testArgumentFilesRegularFormat() throws Exception {
+ new ArgScanner().parse(Args.from(ArgFilters.selectClass(App.class), app),
+ ImmutableList.of("-files=1.txt,2.txt"));
+ assertEquals(
+ ImmutableList.of(new File("1.txt"), new File("2.txt")),
+ app.files.get());
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testArgumentFilesArgFileFormatEmptyFileName() throws Exception {
+ new ArgScanner().parse(Args.from(ArgFilters.selectClass(App.class), app),
+ ImmutableList.of("-files=@"));
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testArgumentFilesArgFileFormatFileNotExist() throws Exception {
+ new ArgScanner().parse(Args.from(ArgFilters.selectClass(App.class), app),
+ ImmutableList.of("-files=@file_does_not_exist.txt"));
+ }
+
+ @Test
+ public void testArgumentFilesArgFileFormat() throws Exception {
+ File argfile = tmpDir.newFile();
+ // Note the '\n' at the end. Some editors auto add a newline at the end so
+ // make sure our arg scanner and parser can deal with this.
+ Files.write("1.txt,2.txt\n", argfile, Charsets.UTF_8);
+ new ArgScanner().parse(Args.from(ArgFilters.selectClass(App.class), app),
+ ImmutableList.of("-files=@" + argfile.getCanonicalPath()));
+ assertEquals(
+ ImmutableList.of(new File("1.txt"), new File("2.txt")),
+ app.files.get());
+ }
+
+ @Test
+ public void testArgumentFlagCreateFromField() throws Exception {
+ OptionInfo optionInfo = OptionInfo.createFromField(App.class.getDeclaredField("flag"), app);
+ assertEquals("flag", optionInfo.getName());
+ assertEquals("help.", optionInfo.getHelp());
+ assertFalse(optionInfo.argFile());
+ assertEquals("com.twitter.common.args.OptionInfoTest.App.flag", optionInfo.getCanonicalName());
+ assertEquals("no_flag", optionInfo.getNegatedName());
+ assertEquals(
+ "com.twitter.common.args.OptionInfoTest.App.no_flag", optionInfo.getCanonicalNegatedName());
+ }
+}
http://git-wip-us.apache.org/repos/asf/aurora/blob/86a547b9/commons/src/test/java/com/twitter/common/args/ParsersTest.java
----------------------------------------------------------------------
diff --git a/commons/src/test/java/com/twitter/common/args/ParsersTest.java b/commons/src/test/java/com/twitter/common/args/ParsersTest.java
new file mode 100644
index 0000000..f3da00d
--- /dev/null
+++ b/commons/src/test/java/com/twitter/common/args/ParsersTest.java
@@ -0,0 +1,84 @@
+// =================================================================================================
+// 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.args;
+
+import com.google.common.collect.ImmutableMap;
+import com.google.common.reflect.TypeToken;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import com.twitter.common.args.apt.Configuration.ParserInfo;
+import com.twitter.common.args.parsers.NonParameterizedTypeParser;
+import com.twitter.common.args.parsers.PairParser;
+import com.twitter.common.collections.Pair;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertSame;
+
+public class ParsersTest {
+
+ private Parsers defaultParsers;
+
+ @Before
+ public void setUp() {
+ defaultParsers =
+ new Parsers(ImmutableMap.<Class<?>, Parser<?>>of(
+ String.class, new StringParser(),
+ Pair.class, new PairParser()));
+ }
+
+ @Test
+ public void testParseTypeFamily() {
+ assertNotNull(defaultParsers.get(TypeToken.of(String.class)));
+
+ class Credentials extends Pair<String, String> {
+ public Credentials(String first, String second) {
+ super(first, second);
+ }
+ }
+ Parser parser = defaultParsers.get(TypeToken.of(Credentials.class));
+ assertNotNull(parser);
+ assertSame(parser, defaultParsers.get(TypeToken.of(Pair.class)));
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testNoParser() {
+ class NoParserForMe { }
+ assertNull(defaultParsers.get(TypeToken.of(NoParserForMe.class)));
+ }
+
+ static class StringParser extends NonParameterizedTypeParser<Integer> {
+ @Override public Integer doParse(String raw) throws IllegalArgumentException {
+ return raw.length();
+ }
+ }
+
+ @Test
+ public void testNonPublicParsers() {
+ @SuppressWarnings("unchecked")
+ Parser<Integer> parser = (Parser<Integer>)
+ Parsers.INFO_TO_PARSER.apply(
+ new ParserInfo(Integer.class.getName(), StringParser.class.getName()));
+
+ assertEquals(
+ Integer.valueOf(42),
+ parser.parse(null, null, "themeaningoflifeisfortytwointhebookbyadams"));
+ }
+}
http://git-wip-us.apache.org/repos/asf/aurora/blob/86a547b9/commons/src/test/java/com/twitter/common/args/argfilterstest/ArgsRoot.java
----------------------------------------------------------------------
diff --git a/commons/src/test/java/com/twitter/common/args/argfilterstest/ArgsRoot.java b/commons/src/test/java/com/twitter/common/args/argfilterstest/ArgsRoot.java
new file mode 100644
index 0000000..53b9abe
--- /dev/null
+++ b/commons/src/test/java/com/twitter/common/args/argfilterstest/ArgsRoot.java
@@ -0,0 +1,32 @@
+// =================================================================================================
+// 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.args.argfilterstest;
+
+import com.twitter.common.args.Arg;
+import com.twitter.common.args.CmdLine;
+
+/**
+ * @author John Sirois
+ */
+public final class ArgsRoot {
+ @CmdLine(name = "args_root", help = "")
+ static final Arg<String> ARGS_ROOT = Arg.create();
+
+ private ArgsRoot() {
+ // Test class.
+ }
+}
http://git-wip-us.apache.org/repos/asf/aurora/blob/86a547b9/commons/src/test/java/com/twitter/common/args/argfilterstest/subpackageA/ArgsA.java
----------------------------------------------------------------------
diff --git a/commons/src/test/java/com/twitter/common/args/argfilterstest/subpackageA/ArgsA.java b/commons/src/test/java/com/twitter/common/args/argfilterstest/subpackageA/ArgsA.java
new file mode 100644
index 0000000..b26895c
--- /dev/null
+++ b/commons/src/test/java/com/twitter/common/args/argfilterstest/subpackageA/ArgsA.java
@@ -0,0 +1,32 @@
+// =================================================================================================
+// 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.args.argfilterstest.subpackageA;
+
+import com.twitter.common.args.Arg;
+import com.twitter.common.args.CmdLine;
+
+/**
+ * @author John Sirois
+ */
+public final class ArgsA {
+ @CmdLine(name = "args_a", help = "")
+ static final Arg<String> ARGS_A = Arg.create();
+
+ private ArgsA() {
+ // Test class.
+ }
+}
http://git-wip-us.apache.org/repos/asf/aurora/blob/86a547b9/commons/src/test/java/com/twitter/common/args/argfilterstest/subpackageA/subsubpackage1/ArgsA1.java
----------------------------------------------------------------------
diff --git a/commons/src/test/java/com/twitter/common/args/argfilterstest/subpackageA/subsubpackage1/ArgsA1.java b/commons/src/test/java/com/twitter/common/args/argfilterstest/subpackageA/subsubpackage1/ArgsA1.java
new file mode 100644
index 0000000..9ec7686
--- /dev/null
+++ b/commons/src/test/java/com/twitter/common/args/argfilterstest/subpackageA/subsubpackage1/ArgsA1.java
@@ -0,0 +1,32 @@
+// =================================================================================================
+// 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.args.argfilterstest.subpackageA.subsubpackage1;
+
+import com.twitter.common.args.Arg;
+import com.twitter.common.args.CmdLine;
+
+/**
+ * @author John Sirois
+ */
+public final class ArgsA1 {
+ @CmdLine(name = "args_a1", help = "")
+ static final Arg<String> ARGS_A1 = Arg.create();
+
+ private ArgsA1() {
+ // Test class.
+ }
+}
http://git-wip-us.apache.org/repos/asf/aurora/blob/86a547b9/commons/src/test/java/com/twitter/common/args/argfilterstest/subpackageB/ArgsB.java
----------------------------------------------------------------------
diff --git a/commons/src/test/java/com/twitter/common/args/argfilterstest/subpackageB/ArgsB.java b/commons/src/test/java/com/twitter/common/args/argfilterstest/subpackageB/ArgsB.java
new file mode 100644
index 0000000..54a25ba
--- /dev/null
+++ b/commons/src/test/java/com/twitter/common/args/argfilterstest/subpackageB/ArgsB.java
@@ -0,0 +1,32 @@
+// =================================================================================================
+// 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.args.argfilterstest.subpackageB;
+
+import com.twitter.common.args.Arg;
+import com.twitter.common.args.CmdLine;
+
+/**
+ * @author John Sirois
+ */
+public final class ArgsB {
+ @CmdLine(name = "args_b", help = "")
+ static final Arg<String> ARGS_B = Arg.create();
+
+ private ArgsB() {
+ // Test class.
+ }
+}
http://git-wip-us.apache.org/repos/asf/aurora/blob/86a547b9/commons/src/test/java/com/twitter/common/args/argfilterstest/subpackageBwithSuffix/ArgsBWithSuffix.java
----------------------------------------------------------------------
diff --git a/commons/src/test/java/com/twitter/common/args/argfilterstest/subpackageBwithSuffix/ArgsBWithSuffix.java b/commons/src/test/java/com/twitter/common/args/argfilterstest/subpackageBwithSuffix/ArgsBWithSuffix.java
new file mode 100644
index 0000000..5c9f12b
--- /dev/null
+++ b/commons/src/test/java/com/twitter/common/args/argfilterstest/subpackageBwithSuffix/ArgsBWithSuffix.java
@@ -0,0 +1,32 @@
+// =================================================================================================
+// 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.args.argfilterstest.subpackageBwithSuffix;
+
+import com.twitter.common.args.Arg;
+import com.twitter.common.args.CmdLine;
+
+/**
+ * @author John Sirois
+ */
+public final class ArgsBWithSuffix {
+ @CmdLine(name = "args_b_with_suffix", help = "")
+ static final Arg<String> ARGS_B_WITH_SUFFIX = Arg.create();
+
+ private ArgsBWithSuffix() {
+ // Test class.
+ }
+}
http://git-wip-us.apache.org/repos/asf/aurora/blob/86a547b9/commons/src/test/java/com/twitter/common/base/CachingSupplierTest.java
----------------------------------------------------------------------
diff --git a/commons/src/test/java/com/twitter/common/base/CachingSupplierTest.java b/commons/src/test/java/com/twitter/common/base/CachingSupplierTest.java
new file mode 100644
index 0000000..627d30f
--- /dev/null
+++ b/commons/src/test/java/com/twitter/common/base/CachingSupplierTest.java
@@ -0,0 +1,67 @@
+// =================================================================================================
+// 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.base;
+
+import org.junit.Before;
+import org.junit.Test;
+
+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;
+import static org.junit.Assert.assertEquals;
+
+/**
+ * @author William Farner
+ */
+public class CachingSupplierTest extends EasyMockTest {
+
+ private static final Amount<Long, Time> ONE_SECOND = Amount.of(1L, Time.SECONDS);
+
+ private Supplier<String> supplier;
+ private FakeClock clock;
+ private Supplier<String> cache;
+
+ @Before
+ public void setUp() {
+ supplier = createMock(new Clazz<Supplier<String>>() { });
+ clock = new FakeClock();
+ cache = new CachingSupplier<String>(supplier, ONE_SECOND, clock);
+ }
+
+ @Test
+ public void testCaches() {
+ expect(supplier.get()).andReturn("foo");
+ expect(supplier.get()).andReturn("bar");
+
+ control.replay();
+
+ assertEquals("foo", cache.get());
+ assertEquals("foo", cache.get());
+
+ clock.advance(Amount.of(999L, Time.MILLISECONDS));
+ assertEquals("foo", cache.get());
+
+ clock.advance(Amount.of(1L, Time.MILLISECONDS));
+ assertEquals("foo", cache.get());
+
+ clock.advance(Amount.of(1L, Time.MILLISECONDS));
+ assertEquals("bar", cache.get());
+ }
+}
http://git-wip-us.apache.org/repos/asf/aurora/blob/86a547b9/commons/src/test/java/com/twitter/common/base/ClosuresTest.java
----------------------------------------------------------------------
diff --git a/commons/src/test/java/com/twitter/common/base/ClosuresTest.java b/commons/src/test/java/com/twitter/common/base/ClosuresTest.java
new file mode 100644
index 0000000..f2d0adb
--- /dev/null
+++ b/commons/src/test/java/com/twitter/common/base/ClosuresTest.java
@@ -0,0 +1,176 @@
+// =================================================================================================
+// 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.base;
+
+import java.io.IOException;
+
+import com.google.common.base.Function;
+import com.google.common.base.Predicate;
+
+import org.easymock.EasyMock;
+import org.junit.Test;
+
+import com.twitter.common.testing.easymock.EasyMockTest;
+
+import static org.easymock.EasyMock.expect;
+import static org.easymock.EasyMock.expectLastCall;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.fail;
+
+/**
+ * @author John Sirois
+ */
+public class ClosuresTest extends EasyMockTest {
+
+ private static final Clazz<Closure<Integer>> INT_CLOSURE_CLZ = new Clazz<Closure<Integer>>() { };
+ private static final Clazz<ExceptionalClosure<Integer, IOException>> EXC_INT_CLOSURE_CLZ =
+ new Clazz<ExceptionalClosure<Integer, IOException>>() { };
+
+ @Test(expected = NullPointerException.class)
+ public void testPreconditions() {
+ control.replay();
+
+ Closures.asFunction(null);
+ }
+
+ @Test
+ public void testApply() throws IOException {
+ ExceptionalClosure<Integer, IOException> work = createMock(EXC_INT_CLOSURE_CLZ);
+ work.execute(1);
+ control.replay();
+
+ Function<Integer, Void> workFunction = Closures.asFunction(work);
+ workFunction.apply(1);
+ }
+
+ static class Thrown extends RuntimeException { }
+
+ @Test
+ public void testApplyThrows() throws IOException {
+ ExceptionalClosure<Integer, IOException> work = createMock(EXC_INT_CLOSURE_CLZ);
+ work.execute(1);
+ RuntimeException runtimeException = new Thrown();
+ EasyMock.expectLastCall().andThrow(runtimeException);
+ control.replay();
+
+ Function<Integer, Void> workFunction = Closures.asFunction(work);
+ try {
+ workFunction.apply(1);
+ } catch (Thrown e) {
+ assertSame(runtimeException, e);
+ }
+ }
+
+ @Test
+ public void testApplyThrowsTransparent() throws IOException {
+ Closure<Integer> work = createMock(INT_CLOSURE_CLZ);
+ work.execute(1);
+ RuntimeException runtimeException = new Thrown();
+ EasyMock.expectLastCall().andThrow(runtimeException);
+ control.replay();
+
+ Function<Integer, Void> workFunction = Closures.asFunction(work);
+ try {
+ workFunction.apply(1);
+ } catch (Thrown e) {
+ assertSame(runtimeException, e);
+ }
+ }
+
+ @Test
+ public void testCombine() {
+ Closure<Integer> work1 = createMock(INT_CLOSURE_CLZ);
+ Closure<Integer> work2 = createMock(INT_CLOSURE_CLZ);
+
+ @SuppressWarnings("unchecked") // Needed because type information lost in vargs.
+ Closure<Integer> wrapper = Closures.combine(work1, work2);
+
+ work1.execute(1);
+ work2.execute(1);
+
+ work1.execute(2);
+ work2.execute(2);
+
+ control.replay();
+
+ wrapper.execute(1);
+ wrapper.execute(2);
+ }
+
+ @Test
+ public void testCombineOneThrows() {
+ Closure<Integer> work1 = createMock(INT_CLOSURE_CLZ);
+ Closure<Integer> work2 = createMock(INT_CLOSURE_CLZ);
+ Closure<Integer> work3 = createMock(INT_CLOSURE_CLZ);
+
+ @SuppressWarnings("unchecked") // Needed because type information lost in vargs.
+ Closure<Integer> wrapper = Closures.combine(work1, work2, work3);
+
+ work1.execute(1);
+ expectLastCall().andThrow(new Thrown());
+
+ work1.execute(2);
+ work2.execute(2);
+ expectLastCall().andThrow(new Thrown());
+
+ work1.execute(3);
+ work2.execute(3);
+ work3.execute(3);
+ expectLastCall().andThrow(new Thrown());
+
+ control.replay();
+
+ try {
+ wrapper.execute(1);
+ fail("Should have thrown.");
+ } catch (Thrown e) {
+ // Expected.
+ }
+
+ try {
+ wrapper.execute(2);
+ fail("Should have thrown.");
+ } catch (Thrown e) {
+ // Expected.
+ }
+
+ try {
+ wrapper.execute(3);
+ fail("Should have thrown.");
+ } catch (Thrown e) {
+ // Expected.
+ }
+ }
+
+ @Test
+ public void testFilter() {
+ Predicate<Integer> filter = createMock(new Clazz<Predicate<Integer>>() { });
+ Closure<Integer> work = createMock(INT_CLOSURE_CLZ);
+
+ expect(filter.apply(1)).andReturn(true);
+ work.execute(1);
+
+ expect(filter.apply(2)).andReturn(false);
+
+ Closure<Integer> filtered = Closures.filter(filter, work);
+
+ control.replay();
+
+ filtered.execute(1);
+ filtered.execute(2);
+ }
+}