View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      https://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.apache.commons.net.ftp;
18  
19  import static org.junit.jupiter.api.Assertions.assertArrayEquals;
20  import static org.junit.jupiter.api.Assertions.assertEquals;
21  import static org.junit.jupiter.api.Assertions.assertFalse;
22  import static org.junit.jupiter.api.Assertions.assertNotNull;
23  import static org.junit.jupiter.api.Assertions.assertTrue;
24  import static org.junit.jupiter.api.Assertions.fail;
25  
26  import java.io.IOException;
27  import java.net.SocketException;
28  import java.util.Arrays;
29  import java.util.Iterator;
30  import java.util.List;
31  import java.util.stream.Stream;
32  
33  import org.apache.commons.lang3.ArrayUtils;
34  import org.apache.commons.net.PrintCommandListener;
35  import org.junit.jupiter.api.AfterEach;
36  import org.junit.jupiter.params.ParameterizedTest;
37  import org.junit.jupiter.params.provider.MethodSource;
38  
39  /**
40   * A functional test suite for checking that site listings work.
41   */
42  class ListingFunctionalTest {
43      public static final class TestCase {
44          private final String hostName;
45          private final String invalidParserKey;
46          private final String invalidPath;
47          private final String validFilename;
48          private final String validParserKey;
49          private final String validPath;
50          private final String pwdPath;
51  
52          private TestCase(final String[] settings) {
53              invalidParserKey = settings[INVALID_PARSERKEY];
54              validParserKey = settings[VALID_PARSERKEY];
55              invalidPath = settings[INVALID_PATH];
56              validFilename = settings[VALID_FILENAME];
57              validPath = settings[VALID_PATH];
58              pwdPath = settings[PATH_PWD];
59              hostName = settings[HOSTNAME];
60          }
61  
62          @Override
63          public String toString() {
64              return validParserKey + " @ " + hostName;
65          }
66      }
67      // Offsets within testData below
68      static final int HOSTNAME = 0;
69      static final int VALID_PARSERKEY = 1;
70      static final int INVALID_PARSERKEY = 2;
71      static final int INVALID_PATH = 3;
72      static final int VALID_FILENAME = 4;
73      static final int VALID_PATH = 5;
74  
75      static final int PATH_PWD = 6; // response to PWD
76  
77      private static Stream<TestCase> testCases() {
78          final String[][] testData = { { "ftp.ibiblio.org", "unix", "vms", "HA!", "javaio.jar", "pub/languages/java/javafaq", "/pub/languages/java/javafaq", },
79                  { "apache.cs.utah.edu", "unix", "vms", "HA!", "HEADER.html", "apache.org", "/apache.org", },
80  //                { // not available
81  //                    "ftp.wacom.com", "windows", "VMS", "HA!",
82  //                    "wacom97.zip", "pub\\drivers"
83  //                },
84                  { "ftp.decuslib.com", "vms", "windows", // VMS OpenVMS V8.3
85                          "[.HA!]", "FREEWARE_SUBMISSION_INSTRUCTIONS.TXT;1", "[.FREEWAREV80.FREEWARE]", "DECUSLIB:[DECUS.FREEWAREV80.FREEWARE]" },
86  //                {  // VMS TCPware V5.7-2 does not return (RWED) permissions
87  //                    "ftp.process.com", "vms", "windows",
88  //                    "[.HA!]", "MESSAGE.;1",
89  //                    "[.VMS-FREEWARE.FREE-VMS]" //
90  //                },
91          };
92          return Arrays.stream(testData).map(TestCase::new);
93      }
94  
95      private FTPClient client;
96  
97      private FTPClient createFTPClient(final String hostName) {
98          try {
99              final FTPClient ftpClient = new FTPClient();
100             ftpClient.addProtocolCommandListener(new PrintCommandListener(System.out));
101             ftpClient.connect(hostName);
102             ftpClient.login("anonymous", "anonymous");
103             ftpClient.enterLocalPassiveMode();
104             ftpClient.setAutodetectUTF8(true);
105             ftpClient.opts("UTF-8", "NLST");
106             return ftpClient;
107         } catch (final SocketException e) {
108             return fail("Could not connect to FTP", e);
109         } catch (final IOException e) {
110             return fail(e);
111         }
112     }
113 
114     private boolean findByName(final List<?> fileList, final String string) {
115         boolean found = false;
116         final Iterator<?> iter = fileList.iterator();
117         while (iter.hasNext() && !found) {
118             final Object element = iter.next();
119             if (element instanceof FTPFile) {
120                 final FTPFile file = (FTPFile) element;
121                 found = file.getName().equals(string);
122             } else {
123                 final String fileName = (String) element;
124                 found = fileName.endsWith(string);
125             }
126         }
127         return found;
128     }
129 
130     @AfterEach
131     protected void tearDown() throws Exception {
132         if (client == null) {
133             return;
134         }
135         try {
136             client.logout();
137         } catch (final IOException e) {
138             e.printStackTrace();
139         }
140         if (client.isConnected()) {
141             client.disconnect();
142         }
143         client = null;
144     }
145 
146     /*
147      * Test for FTPListParseEngine initiateListParsing()
148      */
149     @ParameterizedTest(name = "hostname={0}")
150     @MethodSource("testCases")
151     void testInitiateListParsing(final TestCase testCase) throws IOException {
152         client = createFTPClient(testCase.hostName);
153         client.changeWorkingDirectory(testCase.validPath);
154         final FTPListParseEngine engine = client.initiateListParsing();
155         final List<FTPFile> files = Arrays.asList(engine.getNext(25));
156         assertTrue(findByName(files, testCase.validFilename), files.toString());
157     }
158 
159     /*
160      * Test for FTPListParseEngine initiateListParsing(String, String)
161      */
162     @ParameterizedTest(name = "hostname={0}")
163     @MethodSource("testCases")
164     void testInitiateListParsingWithPath(final TestCase testCase) throws IOException {
165         client = createFTPClient(testCase.hostName);
166         final FTPListParseEngine engine = client.initiateListParsing(testCase.validParserKey, testCase.validPath);
167         final List<FTPFile> files = Arrays.asList(engine.getNext(25));
168         assertTrue(findByName(files, testCase.validFilename), files.toString());
169     }
170 
171     /*
172      * Test for FTPListParseEngine initiateListParsing(String)
173      */
174     @ParameterizedTest(name = "hostname={0}")
175     @MethodSource("testCases")
176     void testInitiateListParsingWithPathAndAutodetection(final TestCase testCase) throws IOException {
177         client = createFTPClient(testCase.hostName);
178         final FTPListParseEngine engine = client.initiateListParsing(testCase.validPath);
179         final List<FTPFile> files = Arrays.asList(engine.getNext(25));
180         assertTrue(findByName(files, testCase.validFilename), files.toString());
181     }
182 
183     /*
184      * Test for FTPListParseEngine initiateListParsing(String)
185      */
186     @ParameterizedTest(name = "hostname={0}")
187     @MethodSource("testCases")
188     void testInitiateListParsingWithPathAndAutodetectionButEmpty(final TestCase testCase) throws IOException {
189         client = createFTPClient(testCase.hostName);
190         final FTPListParseEngine engine = client.initiateListParsing(testCase.invalidPath);
191         assertFalse(engine.hasNext());
192     }
193 
194     /*
195      * Test for FTPListParseEngine initiateListParsing(String, String)
196      */
197     @ParameterizedTest(name = "hostname={0}")
198     @MethodSource("testCases")
199     void testInitiateListParsingWithPathAndIncorrectParser(final TestCase testCase) throws IOException {
200         client = createFTPClient(testCase.hostName);
201         final FTPListParseEngine engine = client.initiateListParsing(testCase.invalidParserKey, testCase.invalidPath);
202         assertFalse(engine.hasNext());
203     }
204 
205     /*
206      * Test for FTPFile[] listFiles(String, String)
207      */
208     @ParameterizedTest(name = "hostname={0}")
209     @MethodSource("testCases")
210     void testListFiles(final TestCase testCase) throws IOException {
211         client = createFTPClient(testCase.hostName);
212         final FTPClientConfig config = new FTPClientConfig(testCase.validParserKey);
213         client.configure(config);
214         final List<FTPFile> files = Arrays.asList(client.listFiles(testCase.validPath));
215         assertTrue(findByName(files, testCase.validFilename), files.toString());
216     }
217 
218     @ParameterizedTest(name = "hostname={0}")
219     @MethodSource("testCases")
220     void testListFilesWithAutodection(final TestCase testCase) throws IOException {
221         client = createFTPClient(testCase.hostName);
222         client.changeWorkingDirectory(testCase.validPath);
223         final List<FTPFile> files = Arrays.asList(client.listFiles());
224         assertTrue(findByName(files, testCase.validFilename), files.toString());
225     }
226 
227     /*
228      * Test for FTPFile[] listFiles(String, String)
229      */
230     @ParameterizedTest(name = "hostname={0}")
231     @MethodSource("testCases")
232     void testListFilesWithIncorrectParser(final TestCase testCase) throws IOException {
233         client = createFTPClient(testCase.hostName);
234         final FTPClientConfig config = new FTPClientConfig(testCase.invalidParserKey);
235         client.configure(config);
236         final FTPFile[] files = client.listFiles(testCase.validPath);
237         assertNotNull(files);
238         // This may well fail, e.g. window parser for VMS listing
239         assertArrayEquals(new FTPFile[] {}, files, "Expected empty array: " + Arrays.toString(files));
240     }
241 
242     /*
243      * Test for FTPFile[] listFiles(String)
244      */
245     @ParameterizedTest(name = "hostname={0}")
246     @MethodSource("testCases")
247     void testListFilesWithPathAndAutodectionButEmpty(final TestCase testCase) throws IOException {
248         client = createFTPClient(testCase.hostName);
249         final FTPFile[] files = client.listFiles(testCase.invalidPath);
250         assertEquals(0, files.length);
251     }
252 
253     /*
254      * Test for FTPFile[] listFiles(String)
255      */
256     @ParameterizedTest(name = "hostname={0}")
257     @MethodSource("testCases")
258     void testListFilesWithPathAndAutodetection(final TestCase testCase) throws IOException {
259         client = createFTPClient(testCase.hostName);
260         final List<FTPFile> files = Arrays.asList(client.listFiles(testCase.validPath));
261         assertTrue(findByName(files, testCase.validFilename), files.toString());
262     }
263 
264     /*
265      * Test for String[] listNames()
266      */
267     @ParameterizedTest(name = "hostname={0}")
268     @MethodSource("testCases")
269     void testListNames(final TestCase testCase) throws IOException {
270         client = createFTPClient(testCase.hostName);
271         client.changeWorkingDirectory(testCase.validPath);
272         final String[] names = client.listNames();
273         assertNotNull(names);
274         final List<String> lnames = Arrays.asList(names);
275         assertTrue(lnames.contains(testCase.validFilename), lnames.toString());
276     }
277 
278     /*
279      * Test for String[] listNames(String)
280      */
281     @ParameterizedTest(name = "hostname={0}")
282     @MethodSource("testCases")
283     void testListNamesWithPath(final TestCase testCase) throws IOException {
284         client = createFTPClient(testCase.hostName);
285         final String[] listNames = client.listNames(testCase.validPath);
286         assertNotNull(listNames, "listNames not null");
287         final List<String> names = Arrays.asList(listNames);
288         assertTrue(findByName(names, testCase.validFilename), names.toString());
289     }
290 
291     @ParameterizedTest(name = "hostname={0}")
292     @MethodSource("testCases")
293     void testListNamesWithPathButEmpty(final TestCase testCase) throws IOException {
294         client = createFTPClient(testCase.hostName);
295         final String[] names = client.listNames(testCase.invalidPath);
296         assertTrue(ArrayUtils.isEmpty(names));
297     }
298 
299     @ParameterizedTest(name = "hostname={0}")
300     @MethodSource("testCases")
301     void testPrintWorkingDirectory(final TestCase testCase) throws IOException {
302         client = createFTPClient(testCase.hostName);
303         client.changeWorkingDirectory(testCase.validPath);
304         final String pwd = client.printWorkingDirectory();
305         assertEquals(testCase.pwdPath, pwd);
306     }
307 }