Merge branch '1.5.2-SNAPSHOT' into 1.6.0-SNAPSHOT Conflicts: core/src/main/java/org/apache/accumulo/core/util/shell/Shell.java core/src/test/java/org/apache/accumulo/core/util/shell/ShellTest.java proxy/src/test/java/org/apache/accumulo/proxy/SimpleProxyIT.java test/src/test/java/org/apache/accumulo/test/ShellServerIT.java
Project: http://git-wip-us.apache.org/repos/asf/accumulo/repo Commit: http://git-wip-us.apache.org/repos/asf/accumulo/commit/da7f937b Tree: http://git-wip-us.apache.org/repos/asf/accumulo/tree/da7f937b Diff: http://git-wip-us.apache.org/repos/asf/accumulo/diff/da7f937b Branch: refs/heads/ACCUMULO-2061 Commit: da7f937b1d7d88ba7508b22a92ff7d0d110954ae Parents: 4223d66 4cc25ca Author: Josh Elser <els...@apache.org> Authored: Mon Mar 10 18:15:28 2014 -0400 Committer: Josh Elser <els...@apache.org> Committed: Mon Mar 10 18:15:28 2014 -0400 ---------------------------------------------------------------------- .../apache/accumulo/core/util/shell/Shell.java | 20 +-- .../core/util/shell/ShellConfigTest.java | 5 +- .../core/util/shell/ShellSetInstanceTest.java | 3 +- .../accumulo/core/util/shell/ShellTest.java | 5 +- .../apache/accumulo/proxy/SimpleProxyIT.java | 98 +++++++++---- .../org/apache/accumulo/test/ShellServerIT.java | 145 ++++++++++++++----- 6 files changed, 196 insertions(+), 80 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/accumulo/blob/da7f937b/core/src/main/java/org/apache/accumulo/core/util/shell/Shell.java ---------------------------------------------------------------------- diff --cc core/src/main/java/org/apache/accumulo/core/util/shell/Shell.java index 420d465,6943b37..0c2cf3c --- a/core/src/main/java/org/apache/accumulo/core/util/shell/Shell.java +++ b/core/src/main/java/org/apache/accumulo/core/util/shell/Shell.java @@@ -219,111 -200,114 +219,109 @@@ public class Shell extends ShellOption private boolean logErrorsToConsole = false; private PrintWriter writer = null; private boolean masking = false; - + public Shell() throws IOException { - this(new ConsoleReader()); + this(new ConsoleReader(), new PrintWriter( + new OutputStreamWriter(System.out, + System.getProperty("jline.WindowsTerminal.output.encoding", System.getProperty("file.encoding"))))); } - + - public Shell(ConsoleReader reader) { + public Shell(ConsoleReader reader, PrintWriter writer) { super(); this.reader = reader; - } - - public Shell(ConsoleReader reader, PrintWriter writer) { - this(reader); this.writer = writer; } - + // Not for client use public boolean config(String... args) { - - CommandLine cl; + ShellOptionsJC options = new ShellOptionsJC(); + JCommander jc = new JCommander(); + + jc.setProgramName("accumulo shell"); + jc.addObject(options); try { - cl = new BasicParser().parse(opts, args); - if (cl.getArgs().length > 0) - throw new ParseException("Unrecognized arguments: " + cl.getArgList()); - - if (cl.hasOption(helpOpt.getOpt())) { - configError = true; - printHelp("shell", SHELL_DESCRIPTION, opts); - return true; - } - - setDebugging(cl.hasOption(debugOption.getLongOpt())); - authTimeout = Integer.parseInt(cl.getOptionValue(authTimeoutOpt.getLongOpt(), DEFAULT_AUTH_TIMEOUT)) * 60 * 1000; - disableAuthTimeout = cl.hasOption(disableAuthTimeoutOpt.getLongOpt()); - - if (cl.hasOption(zooKeeperInstance.getOpt()) && cl.getOptionValues(zooKeeperInstance.getOpt()).length != 2) - throw new MissingArgumentException(zooKeeperInstance); - - } catch (Exception e) { + jc.parse(args); + } catch (ParameterException e) { configError = true; - printException(e); - printHelp("shell", SHELL_DESCRIPTION, opts); + } + + if (options.isHelpEnabled()) { + configError = true; + } + + if (!configError && options.getUnrecognizedOptions() != null) { + configError = true; + logError("Unrecognized Options: " + options.getUnrecognizedOptions().toString()); + } + + if (configError) { + jc.usage(); return true; } - + + setDebugging(options.isDebugEnabled()); + authTimeout = options.getAuthTimeout() * 60 * 1000; // convert minutes to milliseconds + disableAuthTimeout = options.isAuthTimeoutDisabled(); + // get the options that were parsed - String sysUser = System.getProperty("user.name"); - if (sysUser == null) - sysUser = "root"; - String user = cl.getOptionValue(usernameOption.getOpt(), sysUser); - - String passw = cl.getOptionValue(passwOption.getOpt(), null); - tabCompletion = !cl.hasOption(tabCompleteOption.getLongOpt()); - String[] loginOptions = cl.getOptionValues(loginOption.getOpt()); - + String user = options.getUsername(); + String password = options.getPassword(); + + tabCompletion = !options.isTabCompletionDisabled(); + // Use a fake (Mock), ZK, or HdfsZK Accumulo instance - setInstance(cl); - + setInstance(options); + + // AuthenticationToken options + token = options.getAuthenticationToken(); + Map<String,String> loginOptions = options.getTokenProperties(); + // process default parameters if unspecified try { - if (loginOptions != null && !cl.hasOption(tokenOption.getOpt())) - throw new IllegalArgumentException("Must supply '-" + tokenOption.getOpt() + "' option with '-" + loginOption.getOpt() + "' option"); - - if (loginOptions == null && cl.hasOption(tokenOption.getOpt())) - throw new IllegalArgumentException("Must supply '-" + loginOption.getOpt() + "' option with '-" + tokenOption.getOpt() + "' option"); - - if (passw != null && cl.hasOption(tokenOption.getOpt())) - throw new IllegalArgumentException("Can not supply '-" + passwOption.getOpt() + "' option with '-" + tokenOption.getOpt() + "' option"); - - if (user == null) - throw new MissingArgumentException(usernameOption); - - if (loginOptions != null && cl.hasOption(tokenOption.getOpt())) { - Properties props = new Properties(); - for (String loginOption : loginOptions) - for (String lo : loginOption.split(",")) { - String[] split = lo.split("="); - props.put(split[0], split[1]); - } - - this.token = Class.forName(cl.getOptionValue(tokenOption.getOpt())).asSubclass(AuthenticationToken.class).newInstance(); - this.token.init(props); - } - - if (!cl.hasOption(fakeOption.getLongOpt())) { - DistributedTrace.enable(instance, new ZooReader(instance.getZooKeepers(), instance.getZooKeepersSessionTimeOut()), "shell", InetAddress.getLocalHost() - .getHostName()); + boolean hasToken = (token != null); + boolean hasTokenOptions = !loginOptions.isEmpty(); + + if (hasToken && password != null) { + throw new ParameterException("Can not supply '--pass' option with '--tokenClass' option"); } - + Runtime.getRuntime().addShutdownHook(new Thread() { @Override - public void start() { - reader.getTerminal().enableEcho(); + public void run() { + reader.getTerminal().setEchoEnabled(true); } }); - - if (passw != null) { - this.token = new PasswordToken(passw); + + // Need either both a token and options, or neither, but not just one. + if (hasToken != hasTokenOptions) { + throw new ParameterException("Must supply either both or neither of '--tokenClass' and '--tokenProperty'"); + } else if (hasToken) { // implied hasTokenOptions + // Fully qualified name so we don't shadow java.util.Properties + org.apache.accumulo.core.client.security.tokens.AuthenticationToken.Properties props; + // and line wrap it because the package name is so long + props = new org.apache.accumulo.core.client.security.tokens.AuthenticationToken.Properties(); + + props.putAllStrings(loginOptions); + token.init(props); + } else { + // Read password if the user explicitly asked for it, or didn't specify anything at all + if ("stdin".equals(password) || password == null) { + password = reader.readLine("Password: ", '*'); + } + + if (password == null) { + // User cancel, e.g. Ctrl-D pressed + throw new ParameterException("No password or token option supplied"); + } else { + this.token = new PasswordToken(password); + } } - - if (this.token == null) { - passw = readMaskedLine("Password: ", '*'); - if (passw != null) - this.token = new PasswordToken(passw); + + if (!options.isFake()) { + ZooReader zr = new ZooReader(instance.getZooKeepers(), instance.getZooKeepersSessionTimeOut()); + DistributedTrace.enable(instance, zr, "shell", InetAddress.getLocalHost().getHostName()); } - - if (this.token == null) { - reader.printNewline(); - throw new MissingArgumentException("No password or token option supplied"); - } // user canceled - + this.setTableName(""); this.principal = user; connector = instance.getConnector(this.principal, token); @@@ -1053,17 -939,13 +1051,13 @@@ private final void printHelp(String usage, String description, Options opts) { printHelp(usage, description, opts, Integer.MAX_VALUE); } - + private final void printHelp(String usage, String description, Options opts, int width) { - PrintWriter pw = new PrintWriter(new OutputStreamWriter(System.err, Constants.UTF8)); - new HelpFormatter().printHelp(pw, width, usage, description, opts, 2, 5, null, true); - pw.flush(); - if (logErrorsToConsole && writer != null) { - new HelpFormatter().printHelp(writer, width, usage, description, opts, 2, 5, null, true); - writer.flush(); - } + // TODO Use the OutputStream from the JLine ConsoleReader if we can ever get access to it + new HelpFormatter().printHelp(writer, width, usage, description, opts, 2, 5, null, true); + writer.flush(); } - + public int getExitCode() { return exitCode; } http://git-wip-us.apache.org/repos/asf/accumulo/blob/da7f937b/core/src/test/java/org/apache/accumulo/core/util/shell/ShellConfigTest.java ---------------------------------------------------------------------- diff --cc core/src/test/java/org/apache/accumulo/core/util/shell/ShellConfigTest.java index c9914da,0000000..43d2e12 mode 100644,000000..100644 --- a/core/src/test/java/org/apache/accumulo/core/util/shell/ShellConfigTest.java +++ b/core/src/test/java/org/apache/accumulo/core/util/shell/ShellConfigTest.java @@@ -1,89 -1,0 +1,90 @@@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.accumulo.core.util.shell; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import java.io.FileDescriptor; +import java.io.FileInputStream; +import java.io.PrintStream; ++import java.io.PrintWriter; + +import jline.console.ConsoleReader; + +import org.apache.accumulo.core.client.security.tokens.PasswordToken; +import org.apache.accumulo.core.util.shell.ShellTest.TestOutputStream; +import org.apache.log4j.Level; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import com.beust.jcommander.ParameterException; + +public class ShellConfigTest { + TestOutputStream output; + Shell shell; + PrintStream out; + + @Before + public void setUp() throws Exception { + Shell.log.setLevel(Level.ERROR); + + out = System.out; + output = new TestOutputStream(); + System.setOut(new PrintStream(output)); - - shell = new Shell(new ConsoleReader(new FileInputStream(FileDescriptor.in), output)); ++ ++ shell = new Shell(new ConsoleReader(new FileInputStream(FileDescriptor.in), output), new PrintWriter(output)); + shell.setLogErrorsToConsole(); + } + + @After + public void teardown() throws Exception { + shell.shutdown(); + output.clear(); + System.setOut(out); + } + + @Test + public void testHelp() { + assertTrue(shell.config("--help")); + assertTrue("Did not print usage", output.get().startsWith("Usage")); + } + + @Test + public void testBadArg() { + assertTrue(shell.config("--bogus")); + assertTrue("Did not print usage", output.get().startsWith("Usage")); + } + + @Test + public void testToken() { + assertTrue(shell.config("--fake", "-tc", PasswordToken.class.getCanonicalName())); + assertTrue(output.get().contains(ParameterException.class.getCanonicalName())); + } + + @Test + public void testTokenAndOption() { + assertFalse(shell.config("--fake", "-tc", PasswordToken.class.getCanonicalName(), "-u", "foo", "-l", "password=foo")); + } + + @Test + public void testTokenAndOptionAndPassword() { + assertTrue(shell.config("--fake", "-tc", PasswordToken.class.getCanonicalName(), "-l", "password=foo", "-p", "bar")); + assertTrue(output.get().contains(ParameterException.class.getCanonicalName())); + } +} http://git-wip-us.apache.org/repos/asf/accumulo/blob/da7f937b/core/src/test/java/org/apache/accumulo/core/util/shell/ShellSetInstanceTest.java ---------------------------------------------------------------------- diff --cc core/src/test/java/org/apache/accumulo/core/util/shell/ShellSetInstanceTest.java index 2929b04,0000000..ad24e6d mode 100644,000000..100644 --- a/core/src/test/java/org/apache/accumulo/core/util/shell/ShellSetInstanceTest.java +++ b/core/src/test/java/org/apache/accumulo/core/util/shell/ShellSetInstanceTest.java @@@ -1,240 -1,0 +1,241 @@@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.accumulo.core.util.shell; + +import static org.easymock.EasyMock.anyObject; +import static org.easymock.EasyMock.expect; +import static org.powermock.api.easymock.PowerMock.createMock; +import static org.powermock.api.easymock.PowerMock.expectLastCall; +import static org.powermock.api.easymock.PowerMock.expectNew; +import static org.powermock.api.easymock.PowerMock.mockStatic; +import static org.powermock.api.easymock.PowerMock.replay; +import static org.powermock.api.easymock.PowerMock.verify; + +import java.io.FileDescriptor; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.OutputStream; ++import java.io.PrintWriter; +import java.util.Collections; +import java.util.List; +import java.util.UUID; + +import jline.console.ConsoleReader; + +import org.apache.accumulo.core.client.ClientConfiguration; +import org.apache.accumulo.core.client.ClientConfiguration.ClientProperty; +import org.apache.accumulo.core.client.ZooKeeperInstance; +import org.apache.accumulo.core.client.mock.MockInstance; +import org.apache.accumulo.core.conf.AccumuloConfiguration; +import org.apache.accumulo.core.conf.ConfigSanityCheck; +import org.apache.accumulo.core.conf.Property; +import org.apache.accumulo.core.conf.SiteConfiguration; +import org.apache.accumulo.core.zookeeper.ZooUtil; +import org.apache.hadoop.fs.Path; +import org.apache.log4j.Level; +import org.easymock.EasyMock; +import org.junit.After; +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.powermock.core.classloader.annotations.PrepareForTest; +import org.powermock.modules.junit4.PowerMockRunner; + +@RunWith(PowerMockRunner.class) +@PrepareForTest({Shell.class, ZooUtil.class, ConfigSanityCheck.class}) +public class ShellSetInstanceTest { + public static class TestOutputStream extends OutputStream { + StringBuilder sb = new StringBuilder(); + + @Override + public void write(int b) throws IOException { + sb.append((char) (0xff & b)); + } + + public String get() { + return sb.toString(); + } + + public void clear() { + sb.setLength(0); + } + } + + @BeforeClass + public static void setupClass() { + // This is necessary because PowerMock messes with Hadoop's ability to + // determine the current user (see security.UserGroupInformation). + System.setProperty("HADOOP_USER_NAME", "test"); + } + @AfterClass + public static void teardownClass() { + System.clearProperty("HADOOP_USER_NAME"); + } + + private TestOutputStream output; + private Shell shell; + + @Before + public void setup() throws IOException { + Shell.log.setLevel(Level.OFF); + output = new TestOutputStream(); - shell = new Shell(new ConsoleReader(new FileInputStream(FileDescriptor.in), output)); ++ shell = new Shell(new ConsoleReader(new FileInputStream(FileDescriptor.in), output), new PrintWriter(output)); + shell.setLogErrorsToConsole(); + } + @After + public void tearDown() { + shell.shutdown(); + SiteConfiguration.clearInstance(); + } + + @Test + public void testSetInstance_Fake() throws Exception { + ShellOptionsJC opts = createMock(ShellOptionsJC.class); + expect(opts.isFake()).andReturn(true); + replay(opts); + MockInstance theInstance = createMock(MockInstance.class); + expectNew(MockInstance.class, "fake").andReturn(theInstance); + replay(theInstance, MockInstance.class); + + shell.setInstance(opts); + verify(theInstance, MockInstance.class); + } + @Test + public void testSetInstance_HdfsZooInstance_Explicit() throws Exception { + testSetInstance_HdfsZooInstance(true, false, false); + } + @Test + public void testSetInstance_HdfsZooInstance_InstanceGiven() throws Exception { + testSetInstance_HdfsZooInstance(false, true, false); + } + @Test + public void testSetInstance_HdfsZooInstance_HostsGiven() throws Exception { + testSetInstance_HdfsZooInstance(false, false, true); + } + @Test + public void testSetInstance_HdfsZooInstance_Implicit() throws Exception { + testSetInstance_HdfsZooInstance(false, false, false); + } + + private void testSetInstance_HdfsZooInstance(boolean explicitHdfs, boolean onlyInstance, boolean onlyHosts) + throws Exception { + ClientConfiguration clientConf = createMock(ClientConfiguration.class); + ShellOptionsJC opts = createMock(ShellOptionsJC.class); + expect(opts.isFake()).andReturn(false); + expect(opts.getClientConfiguration()).andReturn(clientConf); + expect(opts.isHdfsZooInstance()).andReturn(explicitHdfs); + if (!explicitHdfs) { + expect(opts.getZooKeeperInstance()) + .andReturn(Collections.<String>emptyList()); + if (onlyInstance) { + expect(opts.getZooKeeperInstanceName()).andReturn("instance"); + expect(clientConf.withInstance("instance")).andReturn(clientConf); + } else { + expect(opts.getZooKeeperInstanceName()).andReturn(null); + } + if (onlyHosts) { + expect(opts.getZooKeeperHosts()).andReturn("host3,host4"); + expect(clientConf.withZkHosts("host3,host4")).andReturn(clientConf); + } else { + expect(opts.getZooKeeperHosts()).andReturn(null); + } + } + replay(opts); + + if (!onlyInstance) { + expect(clientConf.get(ClientProperty.INSTANCE_NAME)).andReturn(null); + } + + mockStatic(ConfigSanityCheck.class); + ConfigSanityCheck.validate(EasyMock.<AccumuloConfiguration>anyObject()); + expectLastCall().atLeastOnce(); + replay(ConfigSanityCheck.class); + + if (!onlyHosts) { + expect(clientConf.containsKey(Property.INSTANCE_ZK_HOST.getKey())).andReturn(true).atLeastOnce(); + expect(clientConf.getString(Property.INSTANCE_ZK_HOST.getKey())).andReturn("host1,host2").atLeastOnce(); + expect(clientConf.withZkHosts("host1,host2")).andReturn(clientConf); + } + if (!onlyInstance) { + expect(clientConf.containsKey(Property.INSTANCE_VOLUMES.getKey())).andReturn(false).atLeastOnce(); + expect(clientConf.containsKey(Property.INSTANCE_DFS_DIR.getKey())).andReturn(true).atLeastOnce(); + expect(clientConf.containsKey(Property.INSTANCE_DFS_URI.getKey())).andReturn(true).atLeastOnce(); + expect(clientConf.getString(Property.INSTANCE_DFS_URI.getKey())).andReturn("hdfs://nn1").atLeastOnce(); + expect(clientConf.getString(Property.INSTANCE_DFS_DIR.getKey())).andReturn("/dfs").atLeastOnce(); + } + + UUID randomUUID = null; + if (!onlyInstance) { + mockStatic(ZooUtil.class); + randomUUID = UUID.randomUUID(); + expect(ZooUtil.getInstanceIDFromHdfs(anyObject(Path.class), anyObject(AccumuloConfiguration.class))) + .andReturn(randomUUID.toString()); + replay(ZooUtil.class); + expect(clientConf.withInstance(randomUUID)).andReturn(clientConf); + } + replay(clientConf); + + ZooKeeperInstance theInstance = createMock(ZooKeeperInstance.class); + + expectNew(ZooKeeperInstance.class, clientConf).andReturn(theInstance); + replay(theInstance, ZooKeeperInstance.class); + + shell.setInstance(opts); + verify(theInstance, ZooKeeperInstance.class); + } + @Test + public void testSetInstance_ZKInstance_DashZ() throws Exception { + testSetInstance_ZKInstance(true); + } + @Test + public void testSetInstance_ZKInstance_DashZIandZH() throws Exception { + testSetInstance_ZKInstance(false); + } + private void testSetInstance_ZKInstance(boolean dashZ) throws Exception { + ClientConfiguration clientConf = createMock(ClientConfiguration.class); + ShellOptionsJC opts = createMock(ShellOptionsJC.class); + expect(opts.isFake()).andReturn(false); + expect(opts.getClientConfiguration()).andReturn(clientConf); + expect(opts.isHdfsZooInstance()).andReturn(false); + if (dashZ) { + expect(clientConf.withInstance("foo")).andReturn(clientConf); + expect(clientConf.withZkHosts("host1,host2")).andReturn(clientConf); + List<String> zl = new java.util.ArrayList<String>(); + zl.add("foo"); + zl.add("host1,host2"); + expect(opts.getZooKeeperInstance()).andReturn(zl); + expectLastCall().anyTimes(); + } else { + expect(clientConf.withInstance("bar")).andReturn(clientConf); + expect(clientConf.withZkHosts("host3,host4")).andReturn(clientConf); + expect(opts.getZooKeeperInstance()).andReturn(Collections.<String>emptyList()); + expect(opts.getZooKeeperInstanceName()).andReturn("bar"); + expect(opts.getZooKeeperHosts()).andReturn("host3,host4"); + } + replay(clientConf); + replay(opts); + + ZooKeeperInstance theInstance = createMock(ZooKeeperInstance.class); + expectNew(ZooKeeperInstance.class, clientConf).andReturn(theInstance); + replay(theInstance, ZooKeeperInstance.class); + + shell.setInstance(opts); + verify(theInstance, ZooKeeperInstance.class); + } +} http://git-wip-us.apache.org/repos/asf/accumulo/blob/da7f937b/core/src/test/java/org/apache/accumulo/core/util/shell/ShellTest.java ---------------------------------------------------------------------- diff --cc core/src/test/java/org/apache/accumulo/core/util/shell/ShellTest.java index 4771f2c,8505370..82d1f34 --- a/core/src/test/java/org/apache/accumulo/core/util/shell/ShellTest.java +++ b/core/src/test/java/org/apache/accumulo/core/util/shell/ShellTest.java @@@ -19,18 -19,16 +19,20 @@@ package org.apache.accumulo.core.util.s import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; -import java.io.FileDescriptor; -import java.io.FileInputStream; import java.io.IOException; +import java.io.InputStream; import java.io.OutputStream; + import java.io.OutputStreamWriter; + import java.io.PrintWriter; +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.Date; -import jline.ConsoleReader; +import jline.console.ConsoleReader; +import org.apache.accumulo.core.util.format.DateStringFormatter; import org.apache.log4j.Level; +import org.junit.After; import org.junit.Before; import org.junit.Test; @@@ -103,8 -82,8 +105,9 @@@ public class ShellTest public void setup() throws IOException { Shell.log.setLevel(Level.OFF); output = new TestOutputStream(); - PrintWriter pw = new PrintWriter( new OutputStreamWriter(output)); - shell = new Shell(new ConsoleReader(new FileInputStream(FileDescriptor.in), new OutputStreamWriter(output)), pw); + input = new StringInputStream(); - shell = new Shell(new ConsoleReader(input, output)); ++ PrintWriter pw = new PrintWriter(new OutputStreamWriter(output)); ++ shell = new Shell(new ConsoleReader(input, output), pw); shell.setLogErrorsToConsole(); shell.config("--fake", "-u", "test", "-p", "secret"); }