1c1de24f1bfa6699e54b069e300af5e4246b34a34chrismair/* 2c1de24f1bfa6699e54b069e300af5e4246b34a34chrismair * Copyright 2007 the original author or authors. 3c1de24f1bfa6699e54b069e300af5e4246b34a34chrismair * 4c1de24f1bfa6699e54b069e300af5e4246b34a34chrismair * Licensed under the Apache License, Version 2.0 (the "License"); 5c1de24f1bfa6699e54b069e300af5e4246b34a34chrismair * you may not use this file except in compliance with the License. 6c1de24f1bfa6699e54b069e300af5e4246b34a34chrismair * You may obtain a copy of the License at 7c1de24f1bfa6699e54b069e300af5e4246b34a34chrismair * 8c1de24f1bfa6699e54b069e300af5e4246b34a34chrismair * http://www.apache.org/licenses/LICENSE-2.0 9c1de24f1bfa6699e54b069e300af5e4246b34a34chrismair * 10c1de24f1bfa6699e54b069e300af5e4246b34a34chrismair * Unless required by applicable law or agreed to in writing, software 11c1de24f1bfa6699e54b069e300af5e4246b34a34chrismair * distributed under the License is distributed on an "AS IS" BASIS, 12c1de24f1bfa6699e54b069e300af5e4246b34a34chrismair * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13c1de24f1bfa6699e54b069e300af5e4246b34a34chrismair * See the License for the specific language governing permissions and 14c1de24f1bfa6699e54b069e300af5e4246b34a34chrismair * limitations under the License. 15c1de24f1bfa6699e54b069e300af5e4246b34a34chrismair */ 16c1de24f1bfa6699e54b069e300af5e4246b34a34chrismairpackage org.mockftpserver.core.session; 17c1de24f1bfa6699e54b069e300af5e4246b34a34chrismair 18c1de24f1bfa6699e54b069e300af5e4246b34a34chrismairimport org.apache.log4j.Logger; 19c1de24f1bfa6699e54b069e300af5e4246b34a34chrismairimport org.mockftpserver.core.command.Command; 20c1de24f1bfa6699e54b069e300af5e4246b34a34chrismairimport org.mockftpserver.core.command.CommandHandler; 21c1de24f1bfa6699e54b069e300af5e4246b34a34chrismairimport org.mockftpserver.core.command.CommandNames; 22c1de24f1bfa6699e54b069e300af5e4246b34a34chrismairimport org.mockftpserver.core.command.ConnectCommandHandler; 23c1de24f1bfa6699e54b069e300af5e4246b34a34chrismairimport org.mockftpserver.core.command.InvocationRecord; 24c1de24f1bfa6699e54b069e300af5e4246b34a34chrismairimport org.mockftpserver.core.socket.StubSocket; 25c1de24f1bfa6699e54b069e300af5e4246b34a34chrismairimport org.mockftpserver.stub.command.AbstractStubCommandHandler; 26c1de24f1bfa6699e54b069e300af5e4246b34a34chrismairimport org.mockftpserver.test.AbstractTest; 27c1de24f1bfa6699e54b069e300af5e4246b34a34chrismair 28c1de24f1bfa6699e54b069e300af5e4246b34a34chrismairimport java.io.ByteArrayInputStream; 29c1de24f1bfa6699e54b069e300af5e4246b34a34chrismairimport java.io.ByteArrayOutputStream; 30c1de24f1bfa6699e54b069e300af5e4246b34a34chrismairimport java.io.InputStream; 31c1de24f1bfa6699e54b069e300af5e4246b34a34chrismairimport java.util.HashMap; 32c1de24f1bfa6699e54b069e300af5e4246b34a34chrismairimport java.util.ListResourceBundle; 33c1de24f1bfa6699e54b069e300af5e4246b34a34chrismairimport java.util.Map; 34c1de24f1bfa6699e54b069e300af5e4246b34a34chrismairimport java.util.ResourceBundle; 35c1de24f1bfa6699e54b069e300af5e4246b34a34chrismair 36c1de24f1bfa6699e54b069e300af5e4246b34a34chrismair/** 37c1de24f1bfa6699e54b069e300af5e4246b34a34chrismair * Tests for the DefaultSession class that require the session (thread) to be running/active. 38c1de24f1bfa6699e54b069e300af5e4246b34a34chrismair * 39c1de24f1bfa6699e54b069e300af5e4246b34a34chrismair * @author Chris Mair 40c1de24f1bfa6699e54b069e300af5e4246b34a34chrismair * @version $Revision$ - $Date$ 41c1de24f1bfa6699e54b069e300af5e4246b34a34chrismair */ 42c1de24f1bfa6699e54b069e300af5e4246b34a34chrismairpublic final class DefaultSession_RunTest extends AbstractTest { 43c1de24f1bfa6699e54b069e300af5e4246b34a34chrismair 44c1de24f1bfa6699e54b069e300af5e4246b34a34chrismair private static final Logger LOG = Logger.getLogger(DefaultSession_RunTest.class); 45c1de24f1bfa6699e54b069e300af5e4246b34a34chrismair private static final Command COMMAND = new Command("USER", EMPTY); 46c1de24f1bfa6699e54b069e300af5e4246b34a34chrismair private static final int REPLY_CODE = 100; 47c1de24f1bfa6699e54b069e300af5e4246b34a34chrismair private static final String REPLY_TEXT = "sample text description"; 48c1de24f1bfa6699e54b069e300af5e4246b34a34chrismair 49c1de24f1bfa6699e54b069e300af5e4246b34a34chrismair private DefaultSession session; 50c1de24f1bfa6699e54b069e300af5e4246b34a34chrismair private ByteArrayOutputStream outputStream; 51c1de24f1bfa6699e54b069e300af5e4246b34a34chrismair private Map commandHandlerMap; 52c1de24f1bfa6699e54b069e300af5e4246b34a34chrismair private StubSocket stubSocket; 53c1de24f1bfa6699e54b069e300af5e4246b34a34chrismair private boolean commandHandled = false; 54c1de24f1bfa6699e54b069e300af5e4246b34a34chrismair private String commandToRegister = COMMAND.getName(); 55c1de24f1bfa6699e54b069e300af5e4246b34a34chrismair 56c1de24f1bfa6699e54b069e300af5e4246b34a34chrismair protected void setUp() throws Exception { 57c1de24f1bfa6699e54b069e300af5e4246b34a34chrismair super.setUp(); 58c1de24f1bfa6699e54b069e300af5e4246b34a34chrismair commandHandlerMap = new HashMap(); 59c1de24f1bfa6699e54b069e300af5e4246b34a34chrismair outputStream = new ByteArrayOutputStream(); 60c1de24f1bfa6699e54b069e300af5e4246b34a34chrismair } 61c1de24f1bfa6699e54b069e300af5e4246b34a34chrismair 62c1de24f1bfa6699e54b069e300af5e4246b34a34chrismair public void testInvocationOfCommandHandler() throws Exception { 63c1de24f1bfa6699e54b069e300af5e4246b34a34chrismair AbstractStubCommandHandler commandHandler = new AbstractStubCommandHandler() { 64c1de24f1bfa6699e54b069e300af5e4246b34a34chrismair public void handleCommand(Command command, Session cmdSession, InvocationRecord invocationRecord) { 65c1de24f1bfa6699e54b069e300af5e4246b34a34chrismair assertEquals("command", COMMAND, command); 66c1de24f1bfa6699e54b069e300af5e4246b34a34chrismair assertSame("session", session, cmdSession); 67c1de24f1bfa6699e54b069e300af5e4246b34a34chrismair assertEquals("InvocationRecord: command", COMMAND, invocationRecord.getCommand()); 68c1de24f1bfa6699e54b069e300af5e4246b34a34chrismair assertEquals("InvocationRecord: clientHost", DEFAULT_HOST, invocationRecord.getClientHost()); 69c1de24f1bfa6699e54b069e300af5e4246b34a34chrismair commandHandled = true; 70c1de24f1bfa6699e54b069e300af5e4246b34a34chrismair } 71c1de24f1bfa6699e54b069e300af5e4246b34a34chrismair }; 72c1de24f1bfa6699e54b069e300af5e4246b34a34chrismair runCommandAndVerifyOutput(commandHandler, ""); 73c1de24f1bfa6699e54b069e300af5e4246b34a34chrismair } 74c1de24f1bfa6699e54b069e300af5e4246b34a34chrismair 75c1de24f1bfa6699e54b069e300af5e4246b34a34chrismair public void testClose() throws Exception { 76c1de24f1bfa6699e54b069e300af5e4246b34a34chrismair CommandHandler commandHandler = new AbstractStubCommandHandler() { 77c1de24f1bfa6699e54b069e300af5e4246b34a34chrismair public void handleCommand(Command command, Session session, InvocationRecord invocationRecord) { 78c1de24f1bfa6699e54b069e300af5e4246b34a34chrismair session.close(); 79c1de24f1bfa6699e54b069e300af5e4246b34a34chrismair commandHandled = true; 80c1de24f1bfa6699e54b069e300af5e4246b34a34chrismair } 81c1de24f1bfa6699e54b069e300af5e4246b34a34chrismair }; 82c1de24f1bfa6699e54b069e300af5e4246b34a34chrismair runCommandAndVerifyOutput(commandHandler, ""); 83c1de24f1bfa6699e54b069e300af5e4246b34a34chrismair assertFalse("socket should not be closed", stubSocket.isClosed()); 84c1de24f1bfa6699e54b069e300af5e4246b34a34chrismair } 85c1de24f1bfa6699e54b069e300af5e4246b34a34chrismair 86c1de24f1bfa6699e54b069e300af5e4246b34a34chrismair public void testGetClientHost() throws Exception { 87c1de24f1bfa6699e54b069e300af5e4246b34a34chrismair CommandHandler commandHandler = new AbstractStubCommandHandler() { 88c1de24f1bfa6699e54b069e300af5e4246b34a34chrismair public void handleCommand(Command command, Session session, InvocationRecord invocationRecord) { 89c1de24f1bfa6699e54b069e300af5e4246b34a34chrismair commandHandled = true; 90c1de24f1bfa6699e54b069e300af5e4246b34a34chrismair } 91c1de24f1bfa6699e54b069e300af5e4246b34a34chrismair }; 92c1de24f1bfa6699e54b069e300af5e4246b34a34chrismair runCommandAndVerifyOutput(commandHandler, ""); 93c1de24f1bfa6699e54b069e300af5e4246b34a34chrismair LOG.info("clientHost=" + session.getClientHost()); 94c1de24f1bfa6699e54b069e300af5e4246b34a34chrismair assertEquals("clientHost", DEFAULT_HOST, session.getClientHost()); 95c1de24f1bfa6699e54b069e300af5e4246b34a34chrismair } 96c1de24f1bfa6699e54b069e300af5e4246b34a34chrismair 97c1de24f1bfa6699e54b069e300af5e4246b34a34chrismair public void testSendReply_NullReplyText() throws Exception { 98c1de24f1bfa6699e54b069e300af5e4246b34a34chrismair CommandHandler commandHandler = new AbstractStubCommandHandler() { 99c1de24f1bfa6699e54b069e300af5e4246b34a34chrismair public void handleCommand(Command command, Session session, InvocationRecord invocationRecord) { 100c1de24f1bfa6699e54b069e300af5e4246b34a34chrismair session.sendReply(REPLY_CODE, null); 101c1de24f1bfa6699e54b069e300af5e4246b34a34chrismair commandHandled = true; 102c1de24f1bfa6699e54b069e300af5e4246b34a34chrismair } 103c1de24f1bfa6699e54b069e300af5e4246b34a34chrismair }; 104c1de24f1bfa6699e54b069e300af5e4246b34a34chrismair runCommandAndVerifyOutput(commandHandler, Integer.toString(REPLY_CODE)); 105c1de24f1bfa6699e54b069e300af5e4246b34a34chrismair } 106c1de24f1bfa6699e54b069e300af5e4246b34a34chrismair 107c1de24f1bfa6699e54b069e300af5e4246b34a34chrismair public void testSendReply_TrimReplyText() throws Exception { 108c1de24f1bfa6699e54b069e300af5e4246b34a34chrismair CommandHandler commandHandler = new AbstractStubCommandHandler() { 109c1de24f1bfa6699e54b069e300af5e4246b34a34chrismair public void handleCommand(Command command, Session session, InvocationRecord invocationRecord) { 110c1de24f1bfa6699e54b069e300af5e4246b34a34chrismair session.sendReply(REPLY_CODE, " " + REPLY_TEXT + " "); 111c1de24f1bfa6699e54b069e300af5e4246b34a34chrismair commandHandled = true; 112c1de24f1bfa6699e54b069e300af5e4246b34a34chrismair } 113c1de24f1bfa6699e54b069e300af5e4246b34a34chrismair }; 114c1de24f1bfa6699e54b069e300af5e4246b34a34chrismair runCommandAndVerifyOutput(commandHandler, REPLY_CODE + " " + REPLY_TEXT); 115c1de24f1bfa6699e54b069e300af5e4246b34a34chrismair } 116c1de24f1bfa6699e54b069e300af5e4246b34a34chrismair 117c1de24f1bfa6699e54b069e300af5e4246b34a34chrismair public void testSendReply_MultiLineText() throws Exception { 118c1de24f1bfa6699e54b069e300af5e4246b34a34chrismair final String MULTILINE_REPLY_TEXT = "abc\ndef\nghi\njkl"; 119c1de24f1bfa6699e54b069e300af5e4246b34a34chrismair final String FORMATTED_MULTILINE_REPLY_TEXT = "123-abc\ndef\nghi\n123 jkl"; 120c1de24f1bfa6699e54b069e300af5e4246b34a34chrismair 121c1de24f1bfa6699e54b069e300af5e4246b34a34chrismair CommandHandler commandHandler = new AbstractStubCommandHandler() { 122c1de24f1bfa6699e54b069e300af5e4246b34a34chrismair public void handleCommand(Command command, Session session, InvocationRecord invocationRecord) { 123c1de24f1bfa6699e54b069e300af5e4246b34a34chrismair session.sendReply(123, MULTILINE_REPLY_TEXT); 124c1de24f1bfa6699e54b069e300af5e4246b34a34chrismair commandHandled = true; 125c1de24f1bfa6699e54b069e300af5e4246b34a34chrismair } 126c1de24f1bfa6699e54b069e300af5e4246b34a34chrismair }; 127c1de24f1bfa6699e54b069e300af5e4246b34a34chrismair runCommandAndVerifyOutput(commandHandler, FORMATTED_MULTILINE_REPLY_TEXT); 128c1de24f1bfa6699e54b069e300af5e4246b34a34chrismair } 129c1de24f1bfa6699e54b069e300af5e4246b34a34chrismair 130c1de24f1bfa6699e54b069e300af5e4246b34a34chrismair public void testSendReply_ReplyText() throws Exception { 131c1de24f1bfa6699e54b069e300af5e4246b34a34chrismair CommandHandler commandHandler = new AbstractStubCommandHandler() { 132c1de24f1bfa6699e54b069e300af5e4246b34a34chrismair public void handleCommand(Command command, Session session, InvocationRecord invocationRecord) { 133c1de24f1bfa6699e54b069e300af5e4246b34a34chrismair session.sendReply(REPLY_CODE, REPLY_TEXT); 134c1de24f1bfa6699e54b069e300af5e4246b34a34chrismair commandHandled = true; 135c1de24f1bfa6699e54b069e300af5e4246b34a34chrismair } 136c1de24f1bfa6699e54b069e300af5e4246b34a34chrismair }; 137c1de24f1bfa6699e54b069e300af5e4246b34a34chrismair runCommandAndVerifyOutput(commandHandler, REPLY_CODE + " " + REPLY_TEXT); 138c1de24f1bfa6699e54b069e300af5e4246b34a34chrismair } 139c1de24f1bfa6699e54b069e300af5e4246b34a34chrismair 140c1de24f1bfa6699e54b069e300af5e4246b34a34chrismair public void testUnrecognizedCommand() throws Exception { 141c1de24f1bfa6699e54b069e300af5e4246b34a34chrismair // Register a handler for unsupported commands 142c1de24f1bfa6699e54b069e300af5e4246b34a34chrismair CommandHandler commandHandler = new AbstractStubCommandHandler() { 143c1de24f1bfa6699e54b069e300af5e4246b34a34chrismair public void handleCommand(Command command, Session session, InvocationRecord invocationRecord) { 144c1de24f1bfa6699e54b069e300af5e4246b34a34chrismair session.sendReply(502, "Unsupported"); 145c1de24f1bfa6699e54b069e300af5e4246b34a34chrismair commandHandled = true; 146c1de24f1bfa6699e54b069e300af5e4246b34a34chrismair } 147c1de24f1bfa6699e54b069e300af5e4246b34a34chrismair }; 148c1de24f1bfa6699e54b069e300af5e4246b34a34chrismair // Register the UNSUPPORTED command handler instead of the command that will be sent. So when we 149c1de24f1bfa6699e54b069e300af5e4246b34a34chrismair // send the regular command, it will trigger the handling for unsupported/unrecognized commands. 150c1de24f1bfa6699e54b069e300af5e4246b34a34chrismair commandToRegister = CommandNames.UNSUPPORTED; 151c1de24f1bfa6699e54b069e300af5e4246b34a34chrismair runCommandAndVerifyOutput(commandHandler, "502 Unsupported"); 152c1de24f1bfa6699e54b069e300af5e4246b34a34chrismair } 153c1de24f1bfa6699e54b069e300af5e4246b34a34chrismair 154c1de24f1bfa6699e54b069e300af5e4246b34a34chrismair // ------------------------------------------------------------------------- 155c1de24f1bfa6699e54b069e300af5e4246b34a34chrismair // Internal Helper Methods 156c1de24f1bfa6699e54b069e300af5e4246b34a34chrismair // ------------------------------------------------------------------------- 157c1de24f1bfa6699e54b069e300af5e4246b34a34chrismair 158c1de24f1bfa6699e54b069e300af5e4246b34a34chrismair /** 159c1de24f1bfa6699e54b069e300af5e4246b34a34chrismair * Create and return a DefaultSession and define the specified CommandHandler. Also, save the 160c1de24f1bfa6699e54b069e300af5e4246b34a34chrismair * StubSocket being used in the stubSocket attribute. 161c1de24f1bfa6699e54b069e300af5e4246b34a34chrismair * 162c1de24f1bfa6699e54b069e300af5e4246b34a34chrismair * @param commandHandler - define this CommandHandler within the commandHandlerMap 163c1de24f1bfa6699e54b069e300af5e4246b34a34chrismair * @return the DefaultSession 164c1de24f1bfa6699e54b069e300af5e4246b34a34chrismair */ 165c1de24f1bfa6699e54b069e300af5e4246b34a34chrismair private DefaultSession createDefaultSession(CommandHandler commandHandler) { 166c1de24f1bfa6699e54b069e300af5e4246b34a34chrismair stubSocket = createTestSocket(COMMAND.getName()); 167c1de24f1bfa6699e54b069e300af5e4246b34a34chrismair commandHandlerMap.put(commandToRegister, commandHandler); 168c1de24f1bfa6699e54b069e300af5e4246b34a34chrismair 169c1de24f1bfa6699e54b069e300af5e4246b34a34chrismair ConnectCommandHandler connectCommandHandler = new ConnectCommandHandler(); 170c1de24f1bfa6699e54b069e300af5e4246b34a34chrismair 171c1de24f1bfa6699e54b069e300af5e4246b34a34chrismair ResourceBundle replyTextBundle = new ListResourceBundle() { 172c1de24f1bfa6699e54b069e300af5e4246b34a34chrismair protected Object[][] getContents() { 173c1de24f1bfa6699e54b069e300af5e4246b34a34chrismair return new Object[][]{ 174c1de24f1bfa6699e54b069e300af5e4246b34a34chrismair {"220", "Reply for 220"}, 175c1de24f1bfa6699e54b069e300af5e4246b34a34chrismair }; 176c1de24f1bfa6699e54b069e300af5e4246b34a34chrismair } 177c1de24f1bfa6699e54b069e300af5e4246b34a34chrismair }; 178c1de24f1bfa6699e54b069e300af5e4246b34a34chrismair connectCommandHandler.setReplyTextBundle(replyTextBundle); 179c1de24f1bfa6699e54b069e300af5e4246b34a34chrismair commandHandlerMap.put(CommandNames.CONNECT, connectCommandHandler); 180c1de24f1bfa6699e54b069e300af5e4246b34a34chrismair 181c1de24f1bfa6699e54b069e300af5e4246b34a34chrismair return new DefaultSession(stubSocket, commandHandlerMap); 182c1de24f1bfa6699e54b069e300af5e4246b34a34chrismair } 183c1de24f1bfa6699e54b069e300af5e4246b34a34chrismair 184c1de24f1bfa6699e54b069e300af5e4246b34a34chrismair /** 185c1de24f1bfa6699e54b069e300af5e4246b34a34chrismair * Create and return a StubSocket that reads from an InputStream with the specified contents and 186c1de24f1bfa6699e54b069e300af5e4246b34a34chrismair * writes to the predefined outputStrean ByteArrayOutputStream. 187c1de24f1bfa6699e54b069e300af5e4246b34a34chrismair * 188c1de24f1bfa6699e54b069e300af5e4246b34a34chrismair * @param inputStreamContents - the contents of the input stream 189c1de24f1bfa6699e54b069e300af5e4246b34a34chrismair * @return the StubSocket 190c1de24f1bfa6699e54b069e300af5e4246b34a34chrismair */ 191c1de24f1bfa6699e54b069e300af5e4246b34a34chrismair private StubSocket createTestSocket(String inputStreamContents) { 192c1de24f1bfa6699e54b069e300af5e4246b34a34chrismair InputStream inputStream = new ByteArrayInputStream(inputStreamContents.getBytes()); 193c1de24f1bfa6699e54b069e300af5e4246b34a34chrismair return new StubSocket(DEFAULT_HOST, inputStream, outputStream); 194c1de24f1bfa6699e54b069e300af5e4246b34a34chrismair } 195c1de24f1bfa6699e54b069e300af5e4246b34a34chrismair 196c1de24f1bfa6699e54b069e300af5e4246b34a34chrismair /** 197c1de24f1bfa6699e54b069e300af5e4246b34a34chrismair * Run the command represented by the CommandHandler and verify that the session output from the 198c1de24f1bfa6699e54b069e300af5e4246b34a34chrismair * control socket contains the expected output text. 199c1de24f1bfa6699e54b069e300af5e4246b34a34chrismair * 200c1de24f1bfa6699e54b069e300af5e4246b34a34chrismair * @param commandHandler - the CommandHandler to invoke 201c1de24f1bfa6699e54b069e300af5e4246b34a34chrismair * @param expectedOutput - the text expected within the session output 202c1de24f1bfa6699e54b069e300af5e4246b34a34chrismair * @throws InterruptedException - if the thread sleep is interrupted 203c1de24f1bfa6699e54b069e300af5e4246b34a34chrismair */ 204c1de24f1bfa6699e54b069e300af5e4246b34a34chrismair private void runCommandAndVerifyOutput(CommandHandler commandHandler, String expectedOutput) 205c1de24f1bfa6699e54b069e300af5e4246b34a34chrismair throws InterruptedException { 206c1de24f1bfa6699e54b069e300af5e4246b34a34chrismair session = createDefaultSession(commandHandler); 207c1de24f1bfa6699e54b069e300af5e4246b34a34chrismair 208c1de24f1bfa6699e54b069e300af5e4246b34a34chrismair Thread thread = new Thread(session); 209c1de24f1bfa6699e54b069e300af5e4246b34a34chrismair thread.start(); 210c1de24f1bfa6699e54b069e300af5e4246b34a34chrismair 211c1de24f1bfa6699e54b069e300af5e4246b34a34chrismair for (int i = 0; !commandHandled && i < 10; i++) { 212c1de24f1bfa6699e54b069e300af5e4246b34a34chrismair Thread.sleep(50L); 213c1de24f1bfa6699e54b069e300af5e4246b34a34chrismair } 214c1de24f1bfa6699e54b069e300af5e4246b34a34chrismair 215c1de24f1bfa6699e54b069e300af5e4246b34a34chrismair session.close(); 216c1de24f1bfa6699e54b069e300af5e4246b34a34chrismair thread.join(); 217c1de24f1bfa6699e54b069e300af5e4246b34a34chrismair 218c1de24f1bfa6699e54b069e300af5e4246b34a34chrismair assertEquals("commandHandled", true, commandHandled); 219c1de24f1bfa6699e54b069e300af5e4246b34a34chrismair 220c1de24f1bfa6699e54b069e300af5e4246b34a34chrismair String output = outputStream.toString(); 221c1de24f1bfa6699e54b069e300af5e4246b34a34chrismair LOG.info("output=[" + output.trim() + "]"); 222c1de24f1bfa6699e54b069e300af5e4246b34a34chrismair assertTrue("line ends with \\r\\n", 223c1de24f1bfa6699e54b069e300af5e4246b34a34chrismair output.charAt(output.length() - 2) == '\r' && output.charAt(output.length() - 1) == '\n'); 224c1de24f1bfa6699e54b069e300af5e4246b34a34chrismair assertTrue("output: expected [" + expectedOutput + "]", output.indexOf(expectedOutput) != -1); 225c1de24f1bfa6699e54b069e300af5e4246b34a34chrismair } 226c1de24f1bfa6699e54b069e300af5e4246b34a34chrismair 227c1de24f1bfa6699e54b069e300af5e4246b34a34chrismair} 228