http://git-wip-us.apache.org/repos/asf/accumulo/blob/b2b985e2/core/src/test/java/org/apache/accumulo/core/util/shell/ShellConfigTest.java ---------------------------------------------------------------------- diff --git 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 new file mode 100644 index 0000000..43d2e12 --- /dev/null +++ b/core/src/test/java/org/apache/accumulo/core/util/shell/ShellConfigTest.java @@ -0,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), 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/b2b985e2/core/src/test/java/org/apache/accumulo/core/util/shell/ShellSetInstanceTest.java ---------------------------------------------------------------------- diff --git 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 new file mode 100644 index 0000000..d4c0aea --- /dev/null +++ b/core/src/test/java/org/apache/accumulo/core/util/shell/ShellSetInstanceTest.java @@ -0,0 +1,242 @@ +/* + * 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), 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); + } + + @SuppressWarnings("deprecation") + 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/b2b985e2/core/src/test/java/org/apache/accumulo/core/util/shell/ShellTest.java ---------------------------------------------------------------------- diff --git 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 new file mode 100644 index 0000000..3d9b89e --- /dev/null +++ b/core/src/test/java/org/apache/accumulo/core/util/shell/ShellTest.java @@ -0,0 +1,281 @@ +/* + * 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.assertEquals; +import static org.junit.Assert.assertTrue; + +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 java.util.TimeZone; + +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; + +public class ShellTest { + 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); + } + } + + public static class StringInputStream extends InputStream { + private String source = ""; + private int offset = 0; + + @Override + public int read() throws IOException { + if (offset == source.length()) + return '\n'; + else + return source.charAt(offset++); + } + + public void set(String other) { + source = other; + offset = 0; + } + } + + private StringInputStream input; + private TestOutputStream output; + private Shell shell; + + void exec(String cmd) throws IOException { + output.clear(); + shell.execCommand(cmd, true, true); + } + + void exec(String cmd, boolean expectGoodExit) throws IOException { + exec(cmd); + if (expectGoodExit) + assertGoodExit("", true); + else + assertBadExit("", true); + } + + void exec(String cmd, boolean expectGoodExit, String expectString) throws IOException { + exec(cmd, expectGoodExit, expectString, true); + } + + void exec(String cmd, boolean expectGoodExit, String expectString, boolean stringPresent) throws IOException { + exec(cmd); + if (expectGoodExit) + assertGoodExit(expectString, stringPresent); + else + assertBadExit(expectString, stringPresent); + } + + @Before + public void setup() throws IOException { + TimeZone.setDefault(TimeZone.getTimeZone("UTC")); + Shell.log.setLevel(Level.OFF); + output = new TestOutputStream(); + input = new StringInputStream(); + PrintWriter pw = new PrintWriter(new OutputStreamWriter(output)); + shell = new Shell(new ConsoleReader(input, output), pw); + shell.setLogErrorsToConsole(); + shell.config("--fake", "-u", "test", "-p", "secret"); + } + + @After + public void teardown() { + shell.shutdown(); + } + + void assertGoodExit(String s, boolean stringPresent) { + Shell.log.debug(output.get()); + assertEquals(shell.getExitCode(), 0); + if (s.length() > 0) + assertEquals(s + " present in " + output.get() + " was not " + stringPresent, stringPresent, output.get().contains(s)); + } + + void assertBadExit(String s, boolean stringPresent) { + Shell.log.debug(output.get()); + assertTrue(shell.getExitCode() > 0); + if (s.length() > 0) + assertEquals(s + " present in " + output.get() + " was not " + stringPresent, stringPresent, output.get().contains(s)); + shell.resetExitCode(); + } + + @Test + public void aboutTest() throws IOException { + Shell.log.debug("Starting about test -----------------------------------"); + exec("about", true, "Shell - Apache Accumulo Interactive Shell"); + exec("about -v", true, "Current user:"); + exec("about arg", false, "java.lang.IllegalArgumentException: Expected 0 arguments"); + } + + @Test + public void addGetSplitsTest() throws IOException { + Shell.log.debug("Starting addGetSplits test ----------------------------"); + exec("addsplits arg", false, "java.lang.IllegalStateException: Not in a table context"); + exec("createtable test", true); + exec("addsplits 1 \\x80", true); + exec("getsplits", true, "1\n\\x80"); + exec("deletetable test -f", true, "Table: [test] has been deleted"); + } + + @Test + public void insertDeleteScanTest() throws IOException { + Shell.log.debug("Starting insertDeleteScan test ------------------------"); + exec("insert r f q v", false, "java.lang.IllegalStateException: Not in a table context"); + exec("delete r f q", false, "java.lang.IllegalStateException: Not in a table context"); + exec("createtable test", true); + exec("insert r f q v", true); + exec("scan", true, "r f:q [] v"); + exec("delete r f q", true); + exec("scan", true, "r f:q [] v", false); + exec("insert \\x90 \\xa0 \\xb0 \\xc0\\xd0\\xe0\\xf0", true); + exec("scan", true, "\\x90 \\xA0:\\xB0 [] \\xC0\\xD0"); + exec("scan -f 2", true, "\\x90 \\xA0:\\xB0 [] \\xC0\\xD0"); + exec("scan -f 2", true, "\\x90 \\xA0:\\xB0 [] \\xC0\\xD0\\xE0", false); + exec("scan -b \\x90 -e \\x90 -c \\xA0", true, "\\x90 \\xA0:\\xB0 [] \\xC0"); + exec("scan -b \\x90 -e \\x90 -c \\xA0:\\xB0", true, "\\x90 \\xA0:\\xB0 [] \\xC0"); + exec("scan -b \\x90 -be", true, "\\x90 \\xA0:\\xB0 [] \\xC0", false); + exec("scan -e \\x90 -ee", true, "\\x90 \\xA0:\\xB0 [] \\xC0", false); + exec("scan -b \\x90\\x00", true, "\\x90 \\xA0:\\xB0 [] \\xC0", false); + exec("scan -e \\x8f", true, "\\x90 \\xA0:\\xB0 [] \\xC0", false); + exec("delete \\x90 \\xa0 \\xb0", true); + exec("scan", true, "\\x90 \\xA0:\\xB0 [] \\xC0", false); + exec("deletetable test -f", true, "Table: [test] has been deleted"); + } + + @Test + public void authsTest() throws Exception { + Shell.log.debug("Starting auths test --------------------------"); + exec("setauths x,y,z", false, "Missing required option"); + exec("setauths -s x,y,z -u notauser", false, "user does not exist"); + exec("setauths -s y,z,x", true); + exec("getauths -u notauser", false, "user does not exist"); + exec("getauths", true, "x,y,z"); + exec("addauths -u notauser", false, "Missing required option"); + exec("addauths -u notauser -s foo", false, "user does not exist"); + exec("addauths -s a", true); + exec("getauths", true, "a,x,y,z"); + exec("setauths -c", true); + } + + @Test + public void userTest() throws Exception { + Shell.log.debug("Starting user test --------------------------"); + // Test cannot be done via junit because createuser only prompts for password + // exec("createuser root", false, "user exists"); + } + + @Test + public void duContextTest() throws Exception { + Shell.log.debug("Starting du context test --------------------------"); + exec("createtable t", true); + exec("du", true, "0 [t]"); + exec("deletetable t -f", true, "Table: [t] has been deleted"); + } + + @Test + public void duTest() throws IOException { + Shell.log.debug("Starting DU test --------------------------"); + exec("createtable t", true); + exec("du t", true, "0 [t]"); + exec("deletetable t -f", true, "Table: [t] has been deleted"); + } + + @Test + public void duPatternTest() throws IOException { + Shell.log.debug("Starting DU with pattern test --------------------------"); + exec("createtable t", true); + exec("createtable tt", true); + exec("du -p t.*", true, "0 [t, tt]"); + exec("deletetable t -f", true, "Table: [t] has been deleted"); + exec("deletetable tt -f", true, "Table: [tt] has been deleted"); + } + + @Test + public void scanDateStringFormatterTest() throws IOException { + Shell.log.debug("Starting scan dateStringFormatter test --------------------------"); + exec("createtable t", true); + exec("insert r f q v -ts 0", true); + DateFormat dateFormat = new SimpleDateFormat(DateStringFormatter.DATE_FORMAT); + String expected = String.format("r f:q [] %s v", dateFormat.format(new Date(0))); + exec("scan -fm org.apache.accumulo.core.util.format.DateStringFormatter -st", true, expected); + exec("deletetable t -f", true, "Table: [t] has been deleted"); + } + + @Test + public void commentTest() throws IOException { + Shell.log.debug("Starting comment test --------------------------"); + exec("#", true, "Unknown command", false); + exec("# foo", true, "Unknown command", false); + exec("- foo", true, "Unknown command", true); + } + + @Test + public void execFileTest() throws IOException { + Shell.log.debug("Starting exec file test --------------------------"); + shell.config("--fake", "-u", "test", "-p", "secret", "-f", "src/test/resources/shelltest.txt"); + assertEquals(0, shell.start()); + assertGoodExit("Unknown command", false); + } + + @Test + public void setIterTest() throws IOException { + Shell.log.debug("Starting setiter test --------------------------"); + exec("createtable t", true); + + String cmdJustClass = "setiter -class VersioningIterator -p 1"; + exec(cmdJustClass, false, "java.lang.IllegalArgumentException", false); + exec(cmdJustClass, false, "fully qualified package name", true); + + String cmdFullPackage = "setiter -class o.a.a.foo -p 1"; + exec(cmdFullPackage, false, "java.lang.IllegalArgumentException", false); + exec(cmdFullPackage, false, "class not found", true); + + String cmdNoOption = "setiter -class java.lang.String -p 1"; + exec(cmdNoOption, false, "loaded successfully but does not implement SortedKeyValueIterator", true); + + input.set("\n\n"); + exec("setiter -scan -class org.apache.accumulo.core.iterators.ColumnFamilyCounter -p 30 -name foo", true); + + input.set("bar\nname value\n"); + exec("setiter -scan -class org.apache.accumulo.core.iterators.ColumnFamilyCounter -p 31", true); + + //TODO can't verify this as config -t fails, functionality verified in ShellServerIT + + exec("deletetable t -f", true, "Table: [t] has been deleted"); + } +} http://git-wip-us.apache.org/repos/asf/accumulo/blob/b2b985e2/core/src/test/java/org/apache/accumulo/core/util/shell/ShellUtilTest.java ---------------------------------------------------------------------- diff --git a/core/src/test/java/org/apache/accumulo/core/util/shell/ShellUtilTest.java b/core/src/test/java/org/apache/accumulo/core/util/shell/ShellUtilTest.java new file mode 100644 index 0000000..a5fd179 --- /dev/null +++ b/core/src/test/java/org/apache/accumulo/core/util/shell/ShellUtilTest.java @@ -0,0 +1,66 @@ +/* + * 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.*; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.util.List; + +import org.apache.commons.codec.binary.Base64; +import org.apache.commons.io.FileUtils; +import org.apache.hadoop.io.Text; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; + +import com.google.common.collect.ImmutableList; + +public class ShellUtilTest { + + @Rule + public TemporaryFolder folder = new TemporaryFolder(new File(System.getProperty("user.dir") + "/target")); + + // String with 3 lines, with one empty line + private static final String FILEDATA = "line1\n\nline2"; + + @Test + public void testWithoutDecode() throws IOException { + File testFile = new File(folder.getRoot(), "testFileNoDecode.txt"); + FileUtils.writeStringToFile(testFile, FILEDATA); + List<Text> output = ShellUtil.scanFile(testFile.getAbsolutePath(), false); + assertEquals(ImmutableList.of(new Text("line1"), new Text("line2")), output); + } + + @Test + public void testWithDecode() throws IOException { + File testFile = new File(folder.getRoot(), "testFileWithDecode.txt"); + FileUtils.writeStringToFile(testFile, FILEDATA); + List<Text> output = ShellUtil.scanFile(testFile.getAbsolutePath(), true); + assertEquals( + ImmutableList.of(new Text(Base64.decodeBase64("line1".getBytes(StandardCharsets.UTF_8))), new Text(Base64.decodeBase64("line2".getBytes(StandardCharsets.UTF_8)))), + output); + } + + @Test(expected = FileNotFoundException.class) + public void testWithMissingFile() throws FileNotFoundException { + ShellUtil.scanFile("missingFile.txt", false); + } +} http://git-wip-us.apache.org/repos/asf/accumulo/blob/b2b985e2/core/src/test/java/org/apache/accumulo/core/util/shell/command/FormatterCommandTest.java ---------------------------------------------------------------------- diff --git a/core/src/test/java/org/apache/accumulo/core/util/shell/command/FormatterCommandTest.java b/core/src/test/java/org/apache/accumulo/core/util/shell/command/FormatterCommandTest.java new file mode 100644 index 0000000..091ef75 --- /dev/null +++ b/core/src/test/java/org/apache/accumulo/core/util/shell/command/FormatterCommandTest.java @@ -0,0 +1,184 @@ +/* + * 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.command; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.Iterator; +import java.util.Map.Entry; + +import org.apache.accumulo.core.client.AccumuloException; +import org.apache.accumulo.core.client.AccumuloSecurityException; +import org.apache.accumulo.core.client.TableExistsException; +import org.apache.accumulo.core.client.mock.MockShell; +import org.apache.accumulo.core.data.Key; +import org.apache.accumulo.core.data.Value; +import org.apache.accumulo.core.util.format.Formatter; +import org.apache.accumulo.core.util.shell.Shell; +import org.apache.log4j.Level; +import org.apache.log4j.Logger; +import org.junit.Assert; +import org.junit.Test; + +/** + * Uses the MockShell to test the shell output with Formatters + */ +public class FormatterCommandTest { + ByteArrayOutputStream out = null; + InputStream in = null; + + @Test + public void test() throws IOException, AccumuloException, AccumuloSecurityException, TableExistsException, ClassNotFoundException { + // Keep the Shell AUDIT log off the test output + Logger.getLogger(Shell.class).setLevel(Level.WARN); + + final String[] args = new String[] {"--fake", "-u", "root", "-p", ""}; + + final String[] commands = createCommands(); + + in = MockShell.makeCommands(commands); + out = new ByteArrayOutputStream(); + + final MockShell shell = new MockShell(in, out); + shell.config(args); + + // Can't call createtable in the shell with MockAccumulo + shell.getConnector().tableOperations().create("test"); + + try { + shell.start(); + } catch (Exception e) { + Assert.fail("Exception while running commands: " + e.getMessage()); + } + + shell.getReader().flush(); + + final String[] output = new String(out.toByteArray()).split("\n\r"); + + boolean formatterOn = false; + + final String[] expectedDefault = new String[] {"row cf:cq [] 1234abcd", "row cf1:cq1 [] 9876fedc", "row2 cf:cq [] 13579bdf", + "row2 cf1:cq [] 2468ace"}; + + final String[] expectedFormatted = new String[] {"row cf:cq [] 0x31 0x32 0x33 0x34 0x61 0x62 0x63 0x64", + "row cf1:cq1 [] 0x39 0x38 0x37 0x36 0x66 0x65 0x64 0x63", "row2 cf:cq [] 0x31 0x33 0x35 0x37 0x39 0x62 0x64 0x66", + "row2 cf1:cq [] 0x32 0x34 0x36 0x38 0x61 0x63 0x65"}; + + int outputIndex = 0; + while (outputIndex < output.length) { + final String line = output[outputIndex]; + + if (line.startsWith("root@mock-instance")) { + if (line.contains("formatter")) { + formatterOn = true; + } + + outputIndex++; + } else if (line.startsWith("row")) { + int expectedIndex = 0; + String[] comparisonData; + + // Pick the type of data we expect (formatted or default) + if (formatterOn) { + comparisonData = expectedFormatted; + } else { + comparisonData = expectedDefault; + } + + // Ensure each output is what we expected + while (expectedIndex + outputIndex < output.length && expectedIndex < expectedFormatted.length) { + Assert.assertEquals(comparisonData[expectedIndex].trim(), output[expectedIndex + outputIndex].trim()); + expectedIndex++; + } + + outputIndex += expectedIndex; + } + } + } + + private String[] createCommands() { + return new String[] {"table test", "insert row cf cq 1234abcd", "insert row cf1 cq1 9876fedc", "insert row2 cf cq 13579bdf", "insert row2 cf1 cq 2468ace", + "scan", "formatter -t test -f org.apache.accumulo.core.util.shell.command.FormatterCommandTest$HexFormatter", "scan"}; + } + + /** + * <p> + * Simple <code>Formatter</code> that will convert each character in the Value from decimal to hexadecimal. Will automatically skip over characters in the + * value which do not fall within the [0-9,a-f] range. + * </p> + * + * <p> + * Example: <code>'0'</code> will be displayed as <code>'0x30'</code> + * </p> + */ + public static class HexFormatter implements Formatter { + private Iterator<Entry<Key,Value>> iter = null; + private boolean printTs = false; + + private final static String tab = "\t"; + private final static String newline = "\n"; + + public HexFormatter() {} + + @Override + public boolean hasNext() { + return this.iter.hasNext(); + } + + @Override + public String next() { + final Entry<Key,Value> entry = iter.next(); + + String key; + + // Observe the timestamps + if (printTs) { + key = entry.getKey().toString(); + } else { + key = entry.getKey().toStringNoTime(); + } + + final Value v = entry.getValue(); + + // Approximate how much space we'll need + final StringBuilder sb = new StringBuilder(key.length() + v.getSize() * 5); + + sb.append(key).append(tab); + + for (byte b : v.get()) { + if ((b >= 48 && b <= 57) || (b >= 97 || b <= 102)) { + sb.append(String.format("0x%x ", Integer.valueOf(b))); + } + } + + sb.append(newline); + + return sb.toString(); + } + + @Override + public void remove() {} + + @Override + public void initialize(final Iterable<Entry<Key,Value>> scanner, final boolean printTimestamps) { + this.iter = scanner.iterator(); + this.printTs = printTimestamps; + } + } + +} http://git-wip-us.apache.org/repos/asf/accumulo/blob/b2b985e2/core/src/test/resources/shelltest.txt ---------------------------------------------------------------------- diff --git a/core/src/test/resources/shelltest.txt b/core/src/test/resources/shelltest.txt new file mode 100644 index 0000000..19b6f61 --- /dev/null +++ b/core/src/test/resources/shelltest.txt @@ -0,0 +1,16 @@ +# 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. +exit +foo http://git-wip-us.apache.org/repos/asf/accumulo/blob/b2b985e2/examples/simple/pom.xml ---------------------------------------------------------------------- diff --git a/examples/simple/pom.xml b/examples/simple/pom.xml index 8390d01..752d952 100644 --- a/examples/simple/pom.xml +++ b/examples/simple/pom.xml @@ -61,10 +61,6 @@ </dependency> <dependency> <groupId>org.apache.accumulo</groupId> - <artifactId>accumulo-shell</artifactId> - </dependency> - <dependency> - <groupId>org.apache.accumulo</groupId> <artifactId>accumulo-trace</artifactId> </dependency> <dependency> http://git-wip-us.apache.org/repos/asf/accumulo/blob/b2b985e2/examples/simple/src/main/java/org/apache/accumulo/examples/simple/shell/DebugCommand.java ---------------------------------------------------------------------- diff --git a/examples/simple/src/main/java/org/apache/accumulo/examples/simple/shell/DebugCommand.java b/examples/simple/src/main/java/org/apache/accumulo/examples/simple/shell/DebugCommand.java index 6960898..cab78bd 100644 --- a/examples/simple/src/main/java/org/apache/accumulo/examples/simple/shell/DebugCommand.java +++ b/examples/simple/src/main/java/org/apache/accumulo/examples/simple/shell/DebugCommand.java @@ -19,8 +19,8 @@ package org.apache.accumulo.examples.simple.shell; import java.util.Set; import java.util.TreeSet; -import org.apache.accumulo.shell.Shell; -import org.apache.accumulo.shell.Shell.Command; +import org.apache.accumulo.core.util.shell.Shell; +import org.apache.accumulo.core.util.shell.Shell.Command; import org.apache.commons.cli.CommandLine; public class DebugCommand extends Command { http://git-wip-us.apache.org/repos/asf/accumulo/blob/b2b985e2/examples/simple/src/main/java/org/apache/accumulo/examples/simple/shell/MyAppShellExtension.java ---------------------------------------------------------------------- diff --git a/examples/simple/src/main/java/org/apache/accumulo/examples/simple/shell/MyAppShellExtension.java b/examples/simple/src/main/java/org/apache/accumulo/examples/simple/shell/MyAppShellExtension.java index 1ba5ad3..3a22f7b 100644 --- a/examples/simple/src/main/java/org/apache/accumulo/examples/simple/shell/MyAppShellExtension.java +++ b/examples/simple/src/main/java/org/apache/accumulo/examples/simple/shell/MyAppShellExtension.java @@ -16,8 +16,8 @@ */ package org.apache.accumulo.examples.simple.shell; -import org.apache.accumulo.shell.ShellExtension; -import org.apache.accumulo.shell.Shell.Command; +import org.apache.accumulo.core.util.shell.Shell.Command; +import org.apache.accumulo.core.util.shell.ShellExtension; public class MyAppShellExtension extends ShellExtension { http://git-wip-us.apache.org/repos/asf/accumulo/blob/b2b985e2/pom.xml ---------------------------------------------------------------------- diff --git a/pom.xml b/pom.xml index d6a552d..a829ba3 100644 --- a/pom.xml +++ b/pom.xml @@ -74,7 +74,6 @@ <modules> <module>trace</module> <module>core</module> - <module>shell</module> <module>fate</module> <module>start</module> <module>examples/simple</module> @@ -284,11 +283,6 @@ </dependency> <dependency> <groupId>org.apache.accumulo</groupId> - <artifactId>accumulo-shell</artifactId> - <version>${project.version}</version> - </dependency> - <dependency> - <groupId>org.apache.accumulo</groupId> <artifactId>accumulo-start</artifactId> <version>${project.version}</version> </dependency> http://git-wip-us.apache.org/repos/asf/accumulo/blob/b2b985e2/server/monitor/pom.xml ---------------------------------------------------------------------- diff --git a/server/monitor/pom.xml b/server/monitor/pom.xml index 411812c..10586f1 100644 --- a/server/monitor/pom.xml +++ b/server/monitor/pom.xml @@ -65,10 +65,6 @@ </dependency> <dependency> <groupId>org.apache.accumulo</groupId> - <artifactId>accumulo-shell</artifactId> - </dependency> - <dependency> - <groupId>org.apache.accumulo</groupId> <artifactId>accumulo-trace</artifactId> </dependency> <dependency> http://git-wip-us.apache.org/repos/asf/accumulo/blob/b2b985e2/server/monitor/src/main/java/org/apache/accumulo/monitor/servlets/ShellServlet.java ---------------------------------------------------------------------- diff --git a/server/monitor/src/main/java/org/apache/accumulo/monitor/servlets/ShellServlet.java b/server/monitor/src/main/java/org/apache/accumulo/monitor/servlets/ShellServlet.java index 25e9e33..1824840 100644 --- a/server/monitor/src/main/java/org/apache/accumulo/monitor/servlets/ShellServlet.java +++ b/server/monitor/src/main/java/org/apache/accumulo/monitor/servlets/ShellServlet.java @@ -34,7 +34,7 @@ import javax.servlet.http.HttpSession; import jline.console.ConsoleReader; -import org.apache.accumulo.shell.Shell; +import org.apache.accumulo.core.util.shell.Shell; public class ShellServlet extends BasicServlet { private static final long serialVersionUID = 1L; http://git-wip-us.apache.org/repos/asf/accumulo/blob/b2b985e2/shell/pom.xml ---------------------------------------------------------------------- diff --git a/shell/pom.xml b/shell/pom.xml deleted file mode 100644 index a5af4b8..0000000 --- a/shell/pom.xml +++ /dev/null @@ -1,123 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - 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. ---> -<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> - <modelVersion>4.0.0</modelVersion> - <parent> - <groupId>org.apache.accumulo</groupId> - <artifactId>accumulo-project</artifactId> - <version>1.7.0-SNAPSHOT</version> - </parent> - <artifactId>accumulo-shell</artifactId> - <name>Shell</name> - <description>An interactive shell for accessing Apache Accumulo via a command line interface.</description> - <dependencies> - <dependency> - <groupId>com.beust</groupId> - <artifactId>jcommander</artifactId> - </dependency> - <dependency> - <groupId>com.google.guava</groupId> - <artifactId>guava</artifactId> - </dependency> - <dependency> - <groupId>commons-cli</groupId> - <artifactId>commons-cli</artifactId> - </dependency> - <dependency> - <groupId>commons-codec</groupId> - <artifactId>commons-codec</artifactId> - </dependency> - <dependency> - <groupId>commons-collections</groupId> - <artifactId>commons-collections</artifactId> - </dependency> - <dependency> - <groupId>commons-configuration</groupId> - <artifactId>commons-configuration</artifactId> - </dependency> - <dependency> - <groupId>commons-io</groupId> - <artifactId>commons-io</artifactId> - </dependency> - <dependency> - <groupId>commons-lang</groupId> - <artifactId>commons-lang</artifactId> - </dependency> - <dependency> - <groupId>jline</groupId> - <artifactId>jline</artifactId> - </dependency> - <dependency> - <groupId>log4j</groupId> - <artifactId>log4j</artifactId> - </dependency> - <dependency> - <groupId>org.apache.accumulo</groupId> - <artifactId>accumulo-core</artifactId> - </dependency> - <dependency> - <groupId>org.apache.accumulo</groupId> - <artifactId>accumulo-fate</artifactId> - </dependency> - <dependency> - <groupId>org.apache.accumulo</groupId> - <artifactId>accumulo-start</artifactId> - </dependency> - <dependency> - <groupId>org.apache.accumulo</groupId> - <artifactId>accumulo-trace</artifactId> - </dependency> - <dependency> - <groupId>org.apache.commons</groupId> - <artifactId>commons-vfs2</artifactId> - </dependency> - <dependency> - <groupId>org.apache.hadoop</groupId> - <artifactId>hadoop-client</artifactId> - </dependency> - <dependency> - <groupId>org.apache.thrift</groupId> - <artifactId>libthrift</artifactId> - </dependency> - <dependency> - <groupId>junit</groupId> - <artifactId>junit</artifactId> - <scope>test</scope> - </dependency> - <dependency> - <groupId>org.easymock</groupId> - <artifactId>easymock</artifactId> - <scope>test</scope> - </dependency> - <dependency> - <groupId>org.powermock</groupId> - <artifactId>powermock-api-easymock</artifactId> - <scope>test</scope> - </dependency> - <dependency> - <groupId>org.powermock</groupId> - <artifactId>powermock-core</artifactId> - <scope>test</scope> - </dependency> - <dependency> - <groupId>org.powermock</groupId> - <artifactId>powermock-module-junit4</artifactId> - <scope>test</scope> - </dependency> - </dependencies> -</project>