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 */
16package org.mockftpserver.stub.command;
17
18import org.apache.log4j.Logger;
19import org.easymock.MockControl;
20import org.mockftpserver.core.command.AbstractCommandHandler;
21import org.mockftpserver.core.command.Command;
22import org.mockftpserver.core.command.InvocationHistory;
23import org.mockftpserver.core.command.InvocationRecord;
24import org.mockftpserver.core.command.ReplyCodes;
25import org.mockftpserver.core.session.Session;
26import org.mockftpserver.test.AbstractTest;
27
28import java.text.MessageFormat;
29import java.util.ListResourceBundle;
30import java.util.ResourceBundle;
31
32/**
33 * Abstract superclass for CommandHandler tests
34 *
35 * @author Chris Mair
36 * @version $Revision$ - $Date$
37 */
38public abstract class AbstractCommandHandlerTest extends AbstractTest {
39
40    private static final Logger LOG = Logger.getLogger(AbstractCommandHandlerTest.class);
41
42    // Some common test constants
43    protected static final String DIR1 = "dir1";
44    protected static final String DIR2 = "dir2";
45    protected static final String FILENAME1 = "sample1.txt";
46    protected static final String FILENAME2 = "sample2.txt";
47
48    protected Session session;
49    protected ResourceBundle replyTextBundle;
50
51    /**
52     * Test the handleCommand() method, when one or more parameter is missing or invalid
53     *
54     * @param commandHandler - the CommandHandler to test
55     * @param commandName    - the name for the Command
56     * @param parameters     - the Command parameters
57     */
58    protected void testHandleCommand_InvalidParameters(AbstractCommandHandler commandHandler,
59                                                       String commandName, String[] parameters) throws Exception {
60        Command command = new Command(commandName, parameters);
61        session.sendReply(ReplyCodes.COMMAND_SYNTAX_ERROR, replyTextFor(ReplyCodes.COMMAND_SYNTAX_ERROR));
62        replay(session);
63
64        commandHandler.handleCommand(command, session);
65        verify(session);
66
67        verifyNumberOfInvocations(commandHandler, 1);
68        verifyNoDataElements(commandHandler.getInvocation(0));
69    }
70
71    /**
72     * Verify that the CommandHandler contains the specified number of invocation records
73     *
74     * @param commandHandler - the CommandHandler
75     * @param expected       - the expected number of invocations
76     */
77    protected void verifyNumberOfInvocations(InvocationHistory commandHandler, int expected) {
78        assertEquals("number of invocations", expected, commandHandler.numberOfInvocations());
79    }
80
81    /**
82     * Verify that the InvocationRecord contains no data elements
83     *
84     * @param invocationRecord - the InvocationRecord
85     */
86    protected void verifyNoDataElements(InvocationRecord invocationRecord) {
87        LOG.info("Verifying: " + invocationRecord);
88        assertEquals("number of data elements", 0, invocationRecord.keySet().size());
89    }
90
91    /**
92     * Verify that the InvocationRecord contains exactly one data element, with the specified key
93     * and value.
94     *
95     * @param invocationRecord - the InvocationRecord
96     * @param key              - the expected key
97     * @param value            - the expected value
98     */
99    protected void verifyOneDataElement(InvocationRecord invocationRecord, String key, Object value) {
100        LOG.info("Verifying: " + invocationRecord);
101        assertEquals("number of data elements", 1, invocationRecord.keySet().size());
102        assertEqualsAllTypes("value:" + value, value, invocationRecord.getObject(key));
103    }
104
105    /**
106     * Verify that the InvocationRecord contains exactly two data element, with the specified keys
107     * and values.
108     *
109     * @param invocationRecord - the InvocationRecord
110     * @param key1             - the expected key1
111     * @param value1           - the expected value1
112     * @param key2             - the expected key2
113     * @param value2-          the expected value2
114     */
115    protected void verifyTwoDataElements(InvocationRecord invocationRecord, String key1, Object value1,
116                                         String key2, Object value2) {
117
118        LOG.info("Verifying: " + invocationRecord);
119        assertEquals("number of data elements", 2, invocationRecord.keySet().size());
120        assertEqualsAllTypes("value1:" + value1, value1, invocationRecord.getObject(key1));
121        assertEqualsAllTypes("value2:" + value2, value2, invocationRecord.getObject(key2));
122    }
123
124    /**
125     * Assert that the actual is equal to the expected, using arrays equality comparison if
126     * necessary
127     *
128     * @param message  - the message, used if the comparison fails
129     * @param expected - the expected value
130     * @param actual   - the actual value
131     */
132    private void assertEqualsAllTypes(String message, Object expected, Object actual) {
133
134        if (expected instanceof byte[] || actual instanceof byte[]) {
135            assertEquals(message, (byte[]) expected, (byte[]) actual);
136        } else if (expected instanceof Object[] || actual instanceof Object[]) {
137            assertEquals(message, (Object[]) expected, (Object[]) actual);
138        } else {
139            assertEquals(message, expected, actual);
140        }
141    }
142
143    /**
144     * Perform setup before each test
145     *
146     * @see org.mockftpserver.test.AbstractTest#setUp()
147     */
148    protected void setUp() throws Exception {
149        super.setUp();
150
151        session = (Session) createMock(Session.class);
152        control(session).setDefaultMatcher(MockControl.ARRAY_MATCHER);
153        control(session).expectAndDefaultReturn(session.getClientHost(), DEFAULT_HOST);
154
155        replyTextBundle = new ListResourceBundle() {
156            protected Object[][] getContents() {
157                return new Object[][]{
158                        {"150", replyTextFor(150)},
159                        {"200", replyTextFor(200)},
160                        {"211", replyTextWithParameterFor(211)},
161                        {"213", replyTextWithParameterFor(213)},
162                        {"214", replyTextWithParameterFor(214)},
163                        {"215", replyTextWithParameterFor(215)},
164                        {"220", replyTextFor(220)},
165                        {"221", replyTextFor(221)},
166                        {"226", replyTextFor(226)},
167                        {"226.WithFilename", replyTextWithParameterFor("226.WithFilename")},
168                        {"227", replyTextWithParameterFor(227)},
169                        {"230", replyTextFor(230)},
170                        {"250", replyTextFor(250)},
171                        {"257", replyTextWithParameterFor(257)},
172                        {"331", replyTextFor(331)},
173                        {"350", replyTextFor(350)},
174                        {"501", replyTextFor(501)},
175                };
176            }
177        };
178    }
179
180    /**
181     * Return the test-specific reply text for the specified reply code
182     *
183     * @param replyCode - the reply code
184     * @return the reply text for the specified reply code
185     */
186    protected String replyTextFor(int replyCode) {
187        return "Reply for " + replyCode;
188    }
189
190    /**
191     * Return the test-specific parameterized reply text for the specified reply code
192     *
193     * @param replyCode - the reply code
194     * @return the reply text for the specified reply code
195     */
196    protected String replyTextWithParameterFor(int replyCode) {
197        return "Reply for " + replyCode + ":{0}";
198    }
199
200    /**
201     * Return the test-specific parameterized reply text for the specified messageKey
202     *
203     * @param messageKey - the messageKey
204     * @return the reply text for the specified messageKey
205     */
206    protected String replyTextWithParameterFor(String messageKey) {
207        return "Reply for " + messageKey + ":{0}";
208    }
209
210    /**
211     * Return the test-specific reply text for the specified reply code and message parameter
212     *
213     * @param replyCode - the reply code
214     * @param parameter - the message parameter value
215     * @return the reply text for the specified reply code
216     */
217    protected String formattedReplyTextFor(int replyCode, Object parameter) {
218        return MessageFormat.format(replyTextWithParameterFor(replyCode), objArray(parameter));
219    }
220
221    /**
222     * Return the test-specific reply text for the specified message key and message parameter
223     *
224     * @param messageKey - the messageKey
225     * @param parameter  - the message parameter value
226     * @return the reply text for the specified message key and parameter
227     */
228    protected String formattedReplyTextFor(String messageKey, Object parameter) {
229        return MessageFormat.format(replyTextWithParameterFor(messageKey), objArray(parameter));
230    }
231
232}
233