1 /* 2 * Copyright 2007 the original author or authors. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 package org.mockftpserver.core.session; 17 18 import org.slf4j.Logger; 19 import org.slf4j.LoggerFactory; 20 import org.mockftpserver.core.MockFtpServerException; 21 import org.mockftpserver.core.command.Command; 22 import org.mockftpserver.core.socket.StubServerSocket; 23 import org.mockftpserver.core.socket.StubServerSocketFactory; 24 import org.mockftpserver.core.socket.StubSocket; 25 import org.mockftpserver.core.socket.StubSocketFactory; 26 import org.mockftpserver.core.util.AssertFailedException; 27 import org.mockftpserver.test.AbstractTestCase; 28 29 import java.io.ByteArrayInputStream; 30 import java.io.ByteArrayOutputStream; 31 import java.io.IOException; 32 import java.io.InputStream; 33 import java.net.InetAddress; 34 import java.net.SocketTimeoutException; 35 import java.util.Collections; 36 import java.util.HashMap; 37 import java.util.Map; 38 39 /** 40 * Tests for the DefaultSession class 41 * 42 * @version $Revision$ - $Date$ 43 * 44 * @author Chris Mair 45 */ 46 public final class DefaultSessionTest extends AbstractTestCase { 47 48 private static final Logger LOG = LoggerFactory.getLogger(DefaultSessionTest.class); 49 private static final String DATA = "sample data 123"; 50 private static final int PORT = 197; 51 private static final String NAME1 = "name1"; 52 private static final String NAME2 = "name2"; 53 private static final Object VALUE = "value"; 54 55 private DefaultSession session; 56 private ByteArrayOutputStream outputStream; 57 private Map commandHandlerMap; 58 private StubSocket stubSocket; 59 private InetAddress clientHost; 60 61 /** 62 * Perform initialization before each test 63 * 64 * @see org.mockftpserver.test.AbstractTestCase#setUp() 65 */ 66 protected void setUp() throws Exception { 67 super.setUp(); 68 69 commandHandlerMap = new HashMap(); 70 outputStream = new ByteArrayOutputStream(); 71 session = createDefaultSession(""); 72 clientHost = InetAddress.getLocalHost(); 73 } 74 75 /** 76 * @see org.mockftpserver.test.AbstractTestCase#tearDown() 77 */ 78 protected void tearDown() throws Exception { 79 super.tearDown(); 80 } 81 82 /** 83 * Test the Constructor when the control socket is null 84 */ 85 public void testConstructor_NullControlSocket() { 86 try { 87 new DefaultSession(null, commandHandlerMap); 88 fail("Expected AssertFailedException"); 89 } 90 catch (AssertFailedException expected) { 91 LOG.info("Expected: " + expected); 92 } 93 } 94 95 /** 96 * Test the Constructor when the command handler Map is null 97 */ 98 public void testConstructor_NullCommandHandlerMap() { 99 try { 100 new DefaultSession(stubSocket, null); 101 fail("Expected AssertFailedException"); 102 } 103 catch (AssertFailedException expected) { 104 LOG.info("Expected: " + expected); 105 } 106 } 107 108 /** 109 * Test the setClientDataPort() method 110 */ 111 public void testSetClientDataPort() { 112 StubSocket stubSocket = createTestSocket(""); 113 StubSocketFactory stubSocketFactory = new StubSocketFactory(stubSocket); 114 session.socketFactory = stubSocketFactory; 115 session.setClientDataPort(PORT); 116 session.setClientDataHost(clientHost); 117 session.openDataConnection(); 118 assertEquals("data port", PORT, stubSocketFactory.requestedDataPort); 119 } 120 121 /** 122 * Test the setClientDataPort() method after the session was in passive data mode 123 */ 124 public void testSetClientDataPort_AfterPassiveConnectionMode() throws IOException { 125 StubServerSocket stubServerSocket = new StubServerSocket(PORT); 126 StubServerSocketFactory stubServerSocketFactory = new StubServerSocketFactory(stubServerSocket); 127 session.serverSocketFactory = stubServerSocketFactory; 128 129 session.switchToPassiveMode(); 130 assertFalse("server socket closed", stubServerSocket.isClosed()); 131 assertNotNull("passiveModeDataSocket", session.passiveModeDataSocket); 132 session.setClientDataPort(PORT); 133 134 // Make sure that any passive mode connection info is cleared out 135 assertTrue("server socket closed", stubServerSocket.isClosed()); 136 assertNull("passiveModeDataSocket should be null", session.passiveModeDataSocket); 137 } 138 139 /** 140 * Test the setClientHost() method 141 */ 142 public void testSetClientHost() throws Exception { 143 StubSocket stubSocket = createTestSocket(""); 144 StubSocketFactory stubSocketFactory = new StubSocketFactory(stubSocket); 145 session.socketFactory = stubSocketFactory; 146 session.setClientDataHost(clientHost); 147 session.openDataConnection(); 148 assertEquals("client host", clientHost, stubSocketFactory.requestedHost); 149 } 150 151 /** 152 * Test the openDataConnection(), setClientDataPort() and setClientDataHost() methods 153 */ 154 public void testOpenDataConnection() { 155 StubSocket stubSocket = createTestSocket(""); 156 StubSocketFactory stubSocketFactory = new StubSocketFactory(stubSocket); 157 session.socketFactory = stubSocketFactory; 158 159 // Use default client data port 160 session.setClientDataHost(clientHost); 161 session.openDataConnection(); 162 assertEquals("data port", DefaultSession.DEFAULT_CLIENT_DATA_PORT, stubSocketFactory.requestedDataPort); 163 assertEquals("client host", clientHost, stubSocketFactory.requestedHost); 164 165 // Set client data port explicitly 166 session.setClientDataPort(PORT); 167 session.setClientDataHost(clientHost); 168 session.openDataConnection(); 169 assertEquals("data port", PORT, stubSocketFactory.requestedDataPort); 170 assertEquals("client host", clientHost, stubSocketFactory.requestedHost); 171 } 172 173 /** 174 * Test the OpenDataConnection method, when in passive mode and no incoming connection is 175 * initiated 176 */ 177 public void testOpenDataConnection_PassiveMode_NoConnection() throws IOException { 178 179 StubServerSocket stubServerSocket = new StubServerSocket(PORT); 180 StubServerSocketFactory stubServerSocketFactory = new StubServerSocketFactory(stubServerSocket); 181 session.serverSocketFactory = stubServerSocketFactory; 182 183 session.switchToPassiveMode(); 184 185 try { 186 session.openDataConnection(); 187 fail("Expected MockFtpServerException"); 188 } 189 catch (MockFtpServerException expected) { 190 LOG.info("Expected: " + expected); 191 assertSame("cause", SocketTimeoutException.class, expected.getCause().getClass()); 192 } 193 } 194 195 /** 196 * Test the OpenDataConnection method, when the clientHost has not been set 197 */ 198 public void testOpenDataConnection_NullClientHost() { 199 try { 200 session.openDataConnection(); 201 fail("Expected AssertFailedException"); 202 } 203 catch (AssertFailedException expected) { 204 LOG.info("Expected: " + expected); 205 } 206 } 207 208 /** 209 * Test the readData() method 210 */ 211 public void testReadData() { 212 StubSocket stubSocket = createTestSocket(DATA); 213 session.socketFactory = new StubSocketFactory(stubSocket); 214 session.setClientDataHost(clientHost); 215 216 session.openDataConnection(); 217 byte[] data = session.readData(); 218 LOG.info("data=[" + new String(data) + "]"); 219 assertEquals("data", DATA.getBytes(), data); 220 } 221 222 /** 223 * Test the readData() method after switching to passive mode 224 */ 225 public void testReadData_PassiveMode() throws IOException { 226 StubSocket stubSocket = createTestSocket(DATA); 227 StubServerSocket stubServerSocket = new StubServerSocket(PORT, stubSocket); 228 StubServerSocketFactory stubServerSocketFactory = new StubServerSocketFactory(stubServerSocket); 229 session.serverSocketFactory = stubServerSocketFactory; 230 231 session.switchToPassiveMode(); 232 session.openDataConnection(); 233 byte[] data = session.readData(); 234 LOG.info("data=[" + new String(data) + "]"); 235 assertEquals("data", DATA.getBytes(), data); 236 } 237 238 /** 239 * Test the readData(int) method 240 */ 241 public void testReadData_NumBytes() { 242 final int NUM_BYTES = 5; 243 final String EXPECTED_DATA = DATA.substring(0, NUM_BYTES); 244 StubSocket stubSocket = createTestSocket(DATA); 245 session.socketFactory = new StubSocketFactory(stubSocket); 246 session.setClientDataHost(clientHost); 247 248 session.openDataConnection(); 249 byte[] data = session.readData(NUM_BYTES); 250 LOG.info("data=[" + new String(data) + "]"); 251 assertEquals("data", EXPECTED_DATA.getBytes(), data); 252 } 253 254 public void testReadData_NumBytes_AskForMoreBytesThanThereAre() { 255 StubSocket stubSocket = createTestSocket(DATA); 256 session.socketFactory = new StubSocketFactory(stubSocket); 257 session.setClientDataHost(clientHost); 258 259 session.openDataConnection(); 260 byte[] data = session.readData(10000); 261 LOG.info("data=[" + new String(data) + "]"); 262 assertEquals("data", DATA.getBytes(), data); 263 } 264 265 /** 266 * Test the closeDataConnection() method 267 */ 268 public void testCloseDataConnection() { 269 StubSocket stubSocket = createTestSocket(DATA); 270 session.socketFactory = new StubSocketFactory(stubSocket); 271 272 session.setClientDataHost(clientHost); 273 session.openDataConnection(); 274 session.closeDataConnection(); 275 assertTrue("client data socket should be closed", stubSocket.isClosed()); 276 } 277 278 /** 279 * Test the switchToPassiveMode() method 280 */ 281 public void testSwitchToPassiveMode() throws IOException { 282 StubServerSocket stubServerSocket = new StubServerSocket(PORT); 283 StubServerSocketFactory stubServerSocketFactory = new StubServerSocketFactory(stubServerSocket); 284 session.serverSocketFactory = stubServerSocketFactory; 285 286 assertNull("passiveModeDataSocket starts out null", session.passiveModeDataSocket); 287 int port = session.switchToPassiveMode(); 288 assertSame("passiveModeDataSocket", stubServerSocket, session.passiveModeDataSocket); 289 assertEquals("port", PORT, port); 290 } 291 292 /** 293 * Test the getServerHost() method 294 */ 295 public void testGetServerHost() { 296 assertEquals("host", DEFAULT_HOST, session.getServerHost()); 297 } 298 299 /** 300 * Test the getClientHost() method when the session is not yet started 301 */ 302 public void testGetClientHost_NotRunning() { 303 assertNull("null", session.getClientHost()); 304 } 305 306 /** 307 * Test the parseCommand() method 308 */ 309 public void testParseCommand() { 310 Command command = session.parseCommand("LIST"); 311 assertEquals("command name", "LIST", command.getName()); 312 assertEquals("command parameters", EMPTY, command.getParameters()); 313 314 command = session.parseCommand("USER user123"); 315 assertEquals("command name", "USER", command.getName()); 316 assertEquals("command parameters", array("user123"), command.getParameters()); 317 318 command = session.parseCommand("PORT 127,0,0,1,17,37"); 319 assertEquals("command name", "PORT", command.getName()); 320 assertEquals("command parameters", new String[] { "127", "0", "0", "1", "17", "37" }, command 321 .getParameters()); 322 } 323 324 /** 325 * Test the parseCommand() method, passing in an empty command String 326 */ 327 public void testParseCommand_EmptyCommandString() { 328 try { 329 session.parseCommand(""); 330 fail("Expected AssertFailedException"); 331 } 332 catch (AssertFailedException expected) { 333 LOG.info("Expected: " + expected); 334 } 335 } 336 337 /** 338 * Test the sendData() method, as well as the openDataConnection() and closeDataConnection() 339 */ 340 public void testSendData() { 341 StubSocket stubSocket = createTestSocket("1234567890 abcdef"); 342 session.socketFactory = new StubSocketFactory(stubSocket); 343 344 session.setClientDataHost(clientHost); 345 session.openDataConnection(); 346 session.sendData(DATA.getBytes(), DATA.length()); 347 LOG.info("output=[" + outputStream.toString() + "]"); 348 assertEquals("output", DATA, outputStream.toString()); 349 } 350 351 /** 352 * Test the SendData() method, passing in a null byte[] 353 */ 354 public void testSendData_Null() { 355 356 try { 357 session.sendData(null, 1); 358 fail("Expected AssertFailedException"); 359 } 360 catch (AssertFailedException expected) { 361 LOG.info("Expected: " + expected); 362 } 363 } 364 365 /** 366 * Test the SendReply(int,String) method, passing in an invalid reply code 367 */ 368 public void testSendReply_InvalidReplyCode() { 369 370 try { 371 session.sendReply(-66, "text"); 372 fail("Expected AssertFailedException"); 373 } 374 catch (AssertFailedException expected) { 375 LOG.info("Expected: " + expected); 376 } 377 } 378 379 /** 380 * Test the getAttribute() and setAttribute() methods 381 */ 382 public void testGetAndSetAttribute() { 383 assertNull("name does not exist yet", session.getAttribute(NAME1)); 384 session.setAttribute(NAME1, VALUE); 385 session.setAttribute(NAME2, null); 386 assertEquals("NAME1", VALUE, session.getAttribute(NAME1)); 387 assertNull("NAME2", session.getAttribute(NAME2)); 388 assertNull("no such name", session.getAttribute("noSuchName")); 389 } 390 391 /** 392 * Test the getAttribute() method, passing in a null name 393 */ 394 public void testGetAttribute_Null() { 395 try { 396 session.getAttribute(null); 397 fail("Expected AssertFailedException"); 398 } 399 catch (AssertFailedException expected) { 400 LOG.info("Expected: " + expected); 401 } 402 } 403 404 /** 405 * Test the setAttribute() method, passing in a null name 406 */ 407 public void testSetAttribute_NullName() { 408 try { 409 session.setAttribute(null, VALUE); 410 fail("Expected AssertFailedException"); 411 } 412 catch (AssertFailedException expected) { 413 LOG.info("Expected: " + expected); 414 } 415 } 416 417 /** 418 * Test the removeAttribute() 419 */ 420 public void testRemoveAttribute() { 421 session.removeAttribute("noSuchName"); // do nothing 422 session.setAttribute(NAME1, VALUE); 423 session.removeAttribute(NAME1); 424 assertNull("NAME1", session.getAttribute(NAME1)); 425 } 426 427 /** 428 * Test the removeAttribute() method, passing in a null name 429 */ 430 public void testRemoveAttribute_Null() { 431 try { 432 session.removeAttribute(null); 433 fail("Expected AssertFailedException"); 434 } 435 catch (AssertFailedException expected) { 436 LOG.info("Expected: " + expected); 437 } 438 } 439 440 /** 441 * Test the getAttributeNames() 442 */ 443 public void testGetAttributeNames() { 444 assertEquals("No names yet", Collections.EMPTY_SET, session.getAttributeNames()); 445 session.setAttribute(NAME1, VALUE); 446 assertEquals("1", Collections.singleton(NAME1), session.getAttributeNames()); 447 session.setAttribute(NAME2, VALUE); 448 assertEquals("2", set(NAME1, NAME2), session.getAttributeNames()); 449 } 450 451 // ------------------------------------------------------------------------- 452 // Internal Helper Methods 453 // ------------------------------------------------------------------------- 454 455 /** 456 * Create and return a DefaultSession object that reads from an InputStream with the specified 457 * contents and writes to the predefined outputStrean ByteArrayOutputStream. Also, save the 458 * StubSocket being used in the stubSocket attribute. 459 * 460 * @param inputStreamContents - the contents of the input stream 461 * @return the DefaultSession 462 */ 463 private DefaultSession createDefaultSession(String inputStreamContents) { 464 stubSocket = createTestSocket(inputStreamContents); 465 return new DefaultSession(stubSocket, commandHandlerMap); 466 } 467 468 /** 469 * Create and return a StubSocket that reads from an InputStream with the specified contents and 470 * writes to the predefined outputStrean ByteArrayOutputStream. 471 * 472 * @param inputStreamContents - the contents of the input stream 473 * @return the StubSocket 474 */ 475 private StubSocket createTestSocket(String inputStreamContents) { 476 InputStream inputStream = new ByteArrayInputStream(inputStreamContents.getBytes()); 477 StubSocket stubSocket = new StubSocket(inputStream, outputStream); 478 stubSocket._setLocalAddress(DEFAULT_HOST); 479 return stubSocket; 480 } 481 482 } 483