JUnits for receiveUntil method 49/30849/4
authorTomek Kaminski <tomasz.kaminski@nokia.com>
Thu, 8 Feb 2018 11:38:56 +0000 (12:38 +0100)
committerPatrick Brady <pb071s@att.com>
Wed, 14 Feb 2018 20:14:13 +0000 (20:14 +0000)
Test for fonctionallity of receiveUntil method in SshJcraftWrapper.java

Issue-ID: APPC-560
Change-Id: Ifb23b5cbc8b74ddea77ab9cfd5eb26f84c521a92
Signed-off-by: Tomek Kaminski <tomasz.kaminski@nokia.com>
appc-config/appc-config-adaptor/provider/src/main/java/org/onap/appc/ccadaptor/SshJcraftWrapper.java
appc-config/appc-config-adaptor/provider/src/test/java/org/onap/appc/ccadaptor/SshJcraftWrapperTest.java

index acdb87b..07eb431 100644 (file)
@@ -55,24 +55,24 @@ import java.text.SimpleDateFormat;
 import java.util.Calendar;
 import java.util.Date;
 import java.util.StringTokenizer;
+import java.util.concurrent.TimeUnit;
 import javax.annotation.Nonnull;
 import org.apache.commons.lang.StringUtils;
 
 public class SshJcraftWrapper {
 
     private static final EELFLogger log = EELFManager.getInstance().getLogger(SshJcraftWrapper.class);
-
-    private static final int BUFFER_SIZE = 512000;
     static final int DEFAULT_PORT = 22;
+    static final String EOL = "\n";
     static final String CHANNEL_SHELL_TYPE = "shell";
     static final String CHANNEL_SUBSYSTEM_TYPE = "subsystem";
     private static final String TERMINAL_BASIC_MODE = "vt102";
     static final String STRICT_HOST_CHECK_KEY = "StrictHostKeyChecking";
     static final String STRICT_HOST_CHECK_VALUE = "no";
+    static final String DELIMITERS_SEPARATOR = "|";
+
     private TelnetListener listener = null;
     private String routerLogFileName = null;
-    private String routerName = null;
-    private char[] charBuffer = new char[BUFFER_SIZE];
     private BufferedReader reader = null;
     private BufferedWriter out = null;
     private File tmpFile = null;
@@ -84,18 +84,25 @@ public class SshJcraftWrapper {
     private String routerFileName = null;
     private File jcraftReadSwConfigFileFromDisk = new File("/tmp/jcraftReadSwConfigFileFromDisk");
     private String equipNameCode = null;
+    private String routerName = null;
     private String hostName = null;
     private String userName = null;
     private String passWord = null;
+    private int readIntervalMs = 500;
+    private int readBufferSizeBytes = 512_000;
+    private char[] charBuffer;
     private Runtime runtime = Runtime.getRuntime();
 
-
     public SshJcraftWrapper() {
         this.jsch = new JSch();
+        this.charBuffer = new char[readBufferSizeBytes];
     }
 
-    SshJcraftWrapper(JSch jsch) {
+    SshJcraftWrapper(JSch jsch, int readIntervalMs, int readBufferSizeBytes) {
+        this.readIntervalMs = readIntervalMs;
         this.jsch = jsch;
+        this.readBufferSizeBytes = readBufferSizeBytes;
+        this.charBuffer = new char[readBufferSizeBytes];
     }
 
     public void connect(String hostname, String username, String password, String prompt, int timeOut)
@@ -109,7 +116,7 @@ public class SshJcraftWrapper {
         try {
             channel = provideSessionChannel(CHANNEL_SHELL_TYPE, DEFAULT_PORT, timeOut);
             ((ChannelShell) channel).setPtyType(TERMINAL_BASIC_MODE);
-            reader = new BufferedReader(new InputStreamReader(new DataInputStream(channel.getInputStream())), BUFFER_SIZE);
+            reader = new BufferedReader(new InputStreamReader(new DataInputStream(channel.getInputStream())), readBufferSizeBytes);
             channel.connect();
             log.info("Successfully connected. Flushing input buffer.");
             try {
@@ -135,7 +142,7 @@ public class SshJcraftWrapper {
         try {
             channel = provideSessionChannel(CHANNEL_SHELL_TYPE, portNum, timeOut);
             ((ChannelShell) channel).setPtyType(TERMINAL_BASIC_MODE);
-            reader = new BufferedReader(new InputStreamReader(new DataInputStream(channel.getInputStream())), BUFFER_SIZE);
+            reader = new BufferedReader(new InputStreamReader(new DataInputStream(channel.getInputStream())), readBufferSizeBytes);
             channel.connect();
             log.info("Successfully connected. Flushing input buffer.");
             try {
@@ -155,6 +162,7 @@ public class SshJcraftWrapper {
 
 
     public String receiveUntil(String delimeters, int timeout, String cmdThatWasSent) throws IOException {
+        checkConnection();
         boolean match = false;
         boolean cliPromptCmd = false;
         StringBuilder sb = new StringBuilder();
@@ -173,12 +181,8 @@ public class SshJcraftWrapper {
                     log.error("Routine has timed out: routerName={0} CmdThatWasSent={1}", routerName, formattedCmd);
                     throw new TimedOutException("Routine has timed out");
                 }
-                try {
-                    Thread.sleep(500);
-                } catch (java.lang.InterruptedException ee) {
-                    Thread.currentThread().interrupt();
-                }
-                int len = reader.read(charBuffer, 0, BUFFER_SIZE);
+                sleep(readIntervalMs);
+                int len = reader.read(charBuffer, 0, readBufferSizeBytes);
                 log.trace("After reader. Read command len={0}", len);
                 if (len <= 0) {
                     log.error("Reader failed to read any bytes. Suspected socket timeout, router={0}", routerName);
@@ -271,12 +275,30 @@ public class SshJcraftWrapper {
         return stripOffCmdFromRouterResponse(sbReceive.toString());
     }
 
+    private void sleep(long timeoutMs) {
+        try {
+            TimeUnit.MILLISECONDS.sleep(timeoutMs);
+        } catch (java.lang.InterruptedException ee) {
+            Thread.currentThread().interrupt();
+        }
+    }
+
+    private void checkConnection() {
+        try {
+            if (!isConnected() || !reader.ready()) {
+                throw new IllegalStateException("Connection not established. Cannot perform action.");
+            }
+        } catch (IOException e) {
+            throw new IllegalStateException("Reader stream is closed. Cannot perform action.", e);
+        }
+    }
+
     public boolean checkIfReceivedStringMatchesDelimeter(String delimeters, String receivedString,
         String cmdThatWasSent) {
         // The delimeters are in a '|' seperated string. Return true on the first match.
         log.debug("Entered checkIfReceivedStringMatchesDelimeter: delimeters={0} cmdThatWasSent={1} receivedString={2}",
             delimeters, cmdThatWasSent, receivedString);
-        StringTokenizer st = new StringTokenizer(delimeters, "|");
+        StringTokenizer st = new StringTokenizer(delimeters, DELIMITERS_SEPARATOR);
 
         if ((delimeters.contains("#$")) || ("CLI".equals(routerCmdType)))  // This would be an IOS XR, CLI command.
         {
@@ -500,7 +522,7 @@ public class SshJcraftWrapper {
 
     public String removeWhiteSpaceAndNewLineCharactersAroundString(String str) {
         if (str != null && !StringUtils.EMPTY.equals(str)) {
-            StringTokenizer strTok = new StringTokenizer(str, "\n");
+            StringTokenizer strTok = new StringTokenizer(str, EOL);
             StringBuilder sb = new StringBuilder();
 
             while (strTok.hasMoreTokens()) {
@@ -516,17 +538,9 @@ public class SshJcraftWrapper {
         // The session of SSH will echo the command sent to the router, in the router's response.
         // Since all our commands are terminated by a '\n', strip off the first line
         // of the response from the router. This first line contains the orginal command.
-        StringTokenizer rr = new StringTokenizer(routerResponse, "\n");
-        StringBuilder sb = new StringBuilder();
 
-        int numTokens = rr.countTokens();
-        if (numTokens > 1) {
-            rr.nextToken(); //Skip the first line.
-            while (rr.hasMoreTokens()) {
-                sb.append(rr.nextToken()).append("\n");
-            }
-        }
-        return sb.toString();
+        String[] responseTokens = routerResponse.split(EOL, 2);
+        return responseTokens[responseTokens.length-1];
     }
 
     public void setRouterCommandType(String type) {
@@ -595,7 +609,7 @@ public class SshJcraftWrapper {
                         ncharsTotalReceived);
                     throw new TimedOutException("Routine has timed out");
                 }
-                ncharsRead = reader.read(charBuffer, 0, BUFFER_SIZE);
+                ncharsRead = reader.read(charBuffer, 0, readBufferSizeBytes);
                 if (listener != null) {
                     listener.receivedString(String.copyValueOf(charBuffer, 0, ncharsRead));
                 }
@@ -736,7 +750,7 @@ public class SshJcraftWrapper {
             channel = provideSessionChannel(CHANNEL_SUBSYSTEM_TYPE, portNum, timeOut);
             ((ChannelSubsystem) channel).setSubsystem(subsystem);
             ((ChannelSubsystem) channel).setPty(true); //expected ptyType vt102
-            reader = new BufferedReader(new InputStreamReader(new DataInputStream(channel.getInputStream())), BUFFER_SIZE);
+            reader = new BufferedReader(new InputStreamReader(new DataInputStream(channel.getInputStream())), readBufferSizeBytes);
             channel.connect(5000);
         } catch (JSchException e) {
             log.error("JschException occurred ", e);
@@ -753,7 +767,7 @@ public class SshJcraftWrapper {
         try {
             channel = provideSessionChannel(CHANNEL_SHELL_TYPE, DEFAULT_PORT, 30000);
             ((ChannelShell) channel).setPtyType(TERMINAL_BASIC_MODE);
-            reader = new BufferedReader(new InputStreamReader(new DataInputStream(channel.getInputStream())), BUFFER_SIZE);
+            reader = new BufferedReader(new InputStreamReader(new DataInputStream(channel.getInputStream())), readBufferSizeBytes);
             channel.connect();
             try {
                 receiveUntil(":~#", 9000, "No cmd was sent, just waiting, but we can stop on a '~#'");
@@ -916,7 +930,7 @@ public class SshJcraftWrapper {
     private String enhanceCommandWithEOL(@Nonnull String originalCommand) {
         char commandEnding = originalCommand.charAt(originalCommand.length() - 1);
         if (commandEnding != '\n' && commandEnding != '\r') {
-            return originalCommand + "\n";
+            return originalCommand + EOL;
         }
         return originalCommand;
     }
@@ -931,4 +945,5 @@ public class SshJcraftWrapper {
             0); // If this is not set to '0', then socket timeout on all reads will not work!!!!
         return session.openChannel(channelType);
     }
+
 }
index 2495c64..76b4c62 100644 (file)
@@ -25,6 +25,7 @@
 package org.onap.appc.ccadaptor;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.fail;
@@ -68,8 +69,12 @@ public class SshJcraftWrapperTest {
     private static final String HOST = "hostname";
     private static final String SUBSYSTEM = "netconf";
     private static final String PROMPT = "]]>]]>";
+    private static final String SEARCH_STR = "</rpc-reply>";
+    private static final int READ_TIMEOUT = 180_000;
     private static final int PORT_NUM = 23;
     private static final int SESSION_TIMEOUT = 30_000;
+    private static final int READ_INTERVAL_MS = 1;
+    private static final int READ_BUFFER_SIZE = 10;
 
     private SshJcraftWrapper cut;
     @Mock
@@ -91,7 +96,7 @@ public class SshJcraftWrapperTest {
         given(session.openChannel(SshJcraftWrapper.CHANNEL_SHELL_TYPE)).willReturn(channelShell);
         given(session.openChannel(SshJcraftWrapper.CHANNEL_SUBSYSTEM_TYPE)).willReturn(channelSubsystem);
         given(jSchMock.getSession(anyString(), anyString(), anyInt())).willReturn(session);
-        cut = new SshJcraftWrapper(jSchMock);
+        cut = new SshJcraftWrapper(jSchMock, READ_INTERVAL_MS, READ_BUFFER_SIZE);
     }
 
     @Ignore
@@ -451,4 +456,228 @@ public class SshJcraftWrapperTest {
         assertTrue(cut.isConnected());
     }
 
+    //receiveUntil tests begin
+    @Test(expected = IllegalStateException.class)
+    public void receiveUntil_shouldThrowIllegalStateException_whenInstanceIsNotConnected() throws Exception {
+        //given
+        assertFalse(cut.isConnected());
+
+        //when
+        cut.receiveUntil(SEARCH_STR, READ_TIMEOUT, "");
+
+        //then
+        fail("IllegalStateException should be thrown");
+    }
+
+    @Test(expected = IllegalStateException.class)
+    public void receiveUntil_shouldThrowIllegalStateException_whenJschReaderStreamIsNotAvailable() throws Exception {
+        //given
+        provideConnectedSubsystemInstance();
+        given(channelIs.available()).willReturn(0);
+
+        //when
+        cut.receiveUntil(SEARCH_STR, READ_TIMEOUT, "");
+
+        //then
+        fail("IllegalStateException should be thrown");
+    }
+
+    @Test(expected = TimedOutException.class)
+    public void receiveUntil_shouldThrowTimedOutException_whenSessionFails() throws Exception {
+        //given
+        given(channelSubsystem.getInputStream()).willReturn(IOUtils.toInputStream("test input stream:~#", "UTF-8"));
+        cut.connect(HOST, USER, PASS, SESSION_TIMEOUT, PORT_NUM, SUBSYSTEM);
+        assertTrue(cut.isConnected());
+        doThrow(new JSchException("Session is not available")).when(session).setTimeout(anyInt());
+
+        //when
+        cut.receiveUntil(SEARCH_STR, READ_TIMEOUT, "");
+
+        //then
+        fail("TimedOutException should be thrown");
+    }
+
+    @Test(expected = TimedOutException.class)
+    public void receiveUntil_shouldThrowTimedOutException_whenReadFails() throws Exception {
+        //given
+        provideConnectedSubsystemInstance();
+        given(channelIs.available()).willReturn(1);
+        given(channelIs.read(any(), anyInt(), anyInt())).willThrow(new IOException("Could not read stream"));
+
+        //when
+        cut.receiveUntil(SEARCH_STR, READ_TIMEOUT, "");
+
+        //then
+        fail("TimedOutException should be thrown");
+    }
+
+    @Test(expected = TimedOutException.class)
+    public void receiveUntil_shouldThrowException_whenTimeoutIsReached() throws Exception {
+        //given
+        String streamContent = "test input stream:~#";
+        provideConnectedSubsystemInstanceWithStreamContent(streamContent);
+
+        //when
+        cut.receiveUntil(SEARCH_STR, -1000, " Some fake command\n");
+
+        //then
+        fail("TimedOutException should be thrown");
+    }
+
+    @Test(expected = TimedOutException.class)
+    public void receiveUntil_shouldThrowException_whenReachedEndOfStream_andCouldNotReadMoreBytes() throws Exception {
+        //given
+        provideConnectedSubsystemInstance();
+        given(channelIs.available()).willReturn(1);
+        given(channelIs.read(any(), anyInt(), anyInt())).willReturn(-1);
+
+        //when
+        cut.receiveUntil(SEARCH_STR, READ_TIMEOUT, "");
+
+        //then
+        fail("TimedOutException should be thrown");
+    }
+
+    @Test
+    public void receiveUntil_shouldReadUnderlyingStream_andStripOffFirstLine() throws Exception {
+        //given
+        String command = "Command"+SshJcraftWrapper.EOL;
+        String reply = "Reply"+SshJcraftWrapper.EOL;
+        String streamContent = command+reply+PROMPT;
+        provideConnectedSubsystemInstanceWithStreamContent(streamContent);
+
+        //when
+        String result = cut.receiveUntil(PROMPT, SESSION_TIMEOUT, command);
+
+        //then
+        assertEquals(reply+PROMPT, result);
+    }
+
+    @Test
+    public void receiveUntil_shouldReadUnderlyingStream_andReturnWholeReadString() throws Exception {
+        //given
+        String streamContent = "Command and Reply in just one line"+PROMPT;
+        provideConnectedSubsystemInstanceWithStreamContent(streamContent);
+
+        //when
+        String result = cut.receiveUntil(PROMPT, SESSION_TIMEOUT, streamContent);
+
+        //then
+        assertEquals(streamContent, result);
+    }
+
+    @Test
+    public void receiveUntil_shouldCutOffSpecialCharactersFromStream() throws Exception {
+        //given
+        char special1 = Character.UNASSIGNED;
+        char special2 = Character.ENCLOSING_MARK;
+        char special3 = Character.LINE_SEPARATOR;
+        char special4 = Character.MODIFIER_SYMBOL;
+        StringBuilder sb = new StringBuilder("Command");
+        sb.append(special1).append("With").append(special2).append("Special")
+            .append(special3).append("Characters").append(special4).append("Set").append(PROMPT);
+
+        provideConnectedSubsystemInstanceWithStreamContent(sb.toString());
+
+        //when
+        String result = cut.receiveUntil(PROMPT, SESSION_TIMEOUT, "");
+
+        //then
+        assertEquals("CommandWithSpecialCharactersSet"+PROMPT, result);
+    }
+
+    @Test
+    public void receiveUntil_shouldReadUnderlyingStream_untilCLIDelimiterFound_whenProperDelimiterSet() throws Exception {
+        //given
+        String cliDelimiter = "#$";
+        String delimiters = PROMPT+SshJcraftWrapper.DELIMITERS_SEPARATOR+cliDelimiter;
+        String streamContent = "Command for CLI invocation #";
+        provideConnectedSubsystemInstanceWithStreamContent(streamContent);
+
+        //when
+        String result = cut.receiveUntil(delimiters, SESSION_TIMEOUT, streamContent);
+
+        //then
+        assertEquals(streamContent, result);
+    }
+
+    @Test
+    public void receiveUntil_shouldReadUnderlyingStream_untilCLIDelimiterFound_whenCLICommandSet() throws Exception {
+        //given
+        String streamContent = "Command for CLI invocation #";
+        provideConnectedSubsystemInstanceWithStreamContent(streamContent);
+        cut.setRouterCommandType("CLI");
+
+        //when
+        String result = cut.receiveUntil("", SESSION_TIMEOUT, streamContent);
+
+        //then
+        assertEquals(streamContent, result);
+    }
+
+    @Test
+    public void receiveUntil_shouldReadUnderlyingStream_untilCLIDelimiterFound_forShowConfigCommand() throws Exception {
+        //given
+        String streamContent = "show config\nconfig content#";
+        provideConnectedSubsystemInstanceWithStreamContent(streamContent);
+
+        //when
+        String result = cut.receiveUntil("#", SESSION_TIMEOUT, streamContent);
+
+        //then
+        assertEquals("config content#", result);
+    }
+
+    @Test
+    public void receiveUntil_shouldWriteOutputToRouterFile_whenReadingIOSXRswConfigFile_confirmFromFile() throws Exception {
+        receiveUntil_shouldWriteOutputToRouterFile_whenReadingIOSXRswConfigFile();
+    }
+
+    @Test
+    public void receiveUntil_shouldWriteOutputToRouterFile_whenReadingIOSXRswConfigFile_confirmFromBuffer() throws Exception {
+        //given
+        int biggerBufferSize = 32;
+        cut = new SshJcraftWrapper(jSchMock, READ_INTERVAL_MS, biggerBufferSize);
+
+        receiveUntil_shouldWriteOutputToRouterFile_whenReadingIOSXRswConfigFile();
+    }
+
+    private void receiveUntil_shouldWriteOutputToRouterFile_whenReadingIOSXRswConfigFile() throws Exception {
+        //given
+        String routerName = "router";
+        String command = "RP/0/RP0/CPU0: "+routerName+" #IOS_XR_uploadedSwConfigCmd";
+        String configFileEnding = "\nXML>";
+        String streamContent = "Config file\ncontent"+configFileEnding;
+        provideConnectedSubsystemInstanceWithStreamContent(streamContent);
+
+        //when
+        String result = cut.receiveUntil("", SESSION_TIMEOUT, command);
+
+        //then
+        assertNull(result); //TO-DO: it would be better to return empty string in this situation
+        assertFileExist(routerName);
+
+        //after
+        teardownFile(routerName);
+    }
+
+    private void provideConnectedSubsystemInstanceWithStreamContent( String streamContent) throws Exception {
+        given(channelSubsystem.getInputStream()).willReturn(IOUtils.toInputStream(streamContent, "UTF-8"));
+        cut.connect(HOST, USER, PASS, SESSION_TIMEOUT, PORT_NUM, SUBSYSTEM);
+        assertTrue(cut.isConnected());
+    }
+
+    private void teardownFile(String routerName) {
+        File file = new File(routerName);
+        if(file.exists() && file.isFile()) {
+            file.delete();
+        }
+    }
+
+    private void assertFileExist(String fileName) {
+        File file = new File(fileName);
+        assertTrue(file.exists());
+        assertTrue(file.isFile());
+    }
+
 }