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.io;
18  
19  import static org.junit.jupiter.api.Assertions.assertEquals;
20  import static org.junit.jupiter.api.Assertions.assertFalse;
21  import static org.junit.jupiter.api.Assertions.assertNotNull;
22  import static org.junit.jupiter.api.Assertions.assertThrows;
23  import static org.junit.jupiter.api.Assertions.assertTrue;
24  
25  import java.io.BufferedReader;
26  import java.io.File;
27  import java.io.IOException;
28  import java.io.Reader;
29  import java.io.StringReader;
30  import java.nio.charset.StandardCharsets;
31  import java.nio.charset.UnsupportedCharsetException;
32  import java.nio.file.Files;
33  import java.nio.file.NoSuchFileException;
34  import java.util.ArrayList;
35  import java.util.List;
36  import java.util.NoSuchElementException;
37  
38  import org.junit.jupiter.api.Test;
39  import org.junit.jupiter.api.io.TempDir;
40  
41  /**
42   * Tests {@link LineIterator}.
43   */
44  class LineIteratorTest {
45  
46      private static final String UTF_8 = StandardCharsets.UTF_8.name();
47  
48      @TempDir
49      public File temporaryFolder;
50  
51      private void assertLines(final List<String> lines, final LineIterator iterator) {
52          try {
53              for (int i = 0; i < lines.size(); i++) {
54                  final String line = iterator.nextLine();
55                  assertEquals(lines.get(i), line, "nextLine() line " + i);
56              }
57              assertFalse(iterator.hasNext(), "No more expected");
58          } finally {
59              IOUtils.closeQuietly(iterator);
60          }
61      }
62  
63      /**
64       * Creates a test file with a specified number of lines.
65       *
66       * @param file target file
67       * @param lineCount number of lines to create
68       * @throws IOException If an I/O error occurs
69       */
70      private List<String> createLinesFile(final File file, final int lineCount) throws IOException {
71          final List<String> lines = createStringLines(lineCount);
72          FileUtils.writeLines(file, lines);
73          return lines;
74      }
75  
76      /**
77       * Creates a test file with a specified number of lines.
78       *
79       * @param file target file
80       * @param encoding the encoding to use while writing the lines
81       * @param lineCount number of lines to create
82       * @throws IOException If an I/O error occurs
83       */
84      private List<String> createLinesFile(final File file, final String encoding, final int lineCount) throws IOException {
85          final List<String> lines = createStringLines(lineCount);
86          FileUtils.writeLines(file, encoding, lines);
87          return lines;
88      }
89  
90      /**
91       * Creates String data lines.
92       *
93       * @param lineCount number of lines to create
94       * @return a new lines list.
95       */
96      private List<String> createStringLines(final int lineCount) {
97          final List<String> lines = new ArrayList<>();
98          for (int i = 0; i < lineCount; i++) {
99              lines.add("LINE " + i);
100         }
101         return lines;
102     }
103 
104     /**
105      * Utility method to create and test a file with a specified number of lines.
106      *
107      * @param lineCount the lines to create in the test file
108      * @throws IOException If an I/O error occurs while creating the file
109      */
110     private void doTestFileWithSpecifiedLines(final int lineCount) throws IOException {
111         final String encoding = UTF_8;
112 
113         final String fileName = "LineIterator-" + lineCount + "-test.txt";
114         final File testFile = new File(temporaryFolder, fileName);
115         final List<String> lines = createLinesFile(testFile, encoding, lineCount);
116 
117         try (LineIterator iterator = FileUtils.lineIterator(testFile, encoding)) {
118             assertThrows(UnsupportedOperationException.class, iterator::remove);
119 
120             int idx = 0;
121             while (iterator.hasNext()) {
122                 final String line = iterator.next();
123                 assertEquals(lines.get(idx), line, "Comparing line " + idx);
124                 assertTrue(idx < lines.size(), "Exceeded expected idx=" + idx + " size=" + lines.size());
125                 idx++;
126             }
127             assertEquals(idx, lines.size(), "Line Count doesn't match");
128 
129             // try calling next() after file processed
130             assertThrows(NoSuchElementException.class, iterator::next);
131             assertThrows(NoSuchElementException.class, iterator::nextLine);
132         }
133     }
134 
135     @Test
136     void testCloseEarly() throws Exception {
137         final String encoding = UTF_8;
138 
139         final File testFile = new File(temporaryFolder, "LineIterator-closeEarly.txt");
140         createLinesFile(testFile, encoding, 3);
141 
142         try (LineIterator iterator = FileUtils.lineIterator(testFile, encoding)) {
143             // get
144             assertNotNull("Line expected", iterator.next());
145             assertTrue(iterator.hasNext(), "More expected");
146 
147             // close
148             iterator.close();
149             assertFalse(iterator.hasNext(), "No more expected");
150             assertThrows(NoSuchElementException.class, iterator::next);
151             assertThrows(NoSuchElementException.class, iterator::nextLine);
152             // try closing again
153             iterator.close();
154             assertThrows(NoSuchElementException.class, iterator::next);
155             assertThrows(NoSuchElementException.class, iterator::nextLine);
156         }
157     }
158 
159     @Test
160     void testConstructor() {
161         assertThrows(NullPointerException.class, () -> new LineIterator(null));
162     }
163 
164     private void testFiltering(final List<String> lines, final Reader reader) throws IOException {
165         try (LineIterator iterator = new LineIterator(reader) {
166             @Override
167             protected boolean isValidLine(final String line) {
168                 final char c = line.charAt(line.length() - 1);
169                 return (c - 48) % 3 != 1;
170             }
171         }) {
172             assertThrows(UnsupportedOperationException.class, iterator::remove);
173 
174             int idx = 0;
175             int actualLines = 0;
176             while (iterator.hasNext()) {
177                 final String line = iterator.next();
178                 actualLines++;
179                 assertEquals(lines.get(idx), line, "Comparing line " + idx);
180                 assertTrue(idx < lines.size(), "Exceeded expected idx=" + idx + " size=" + lines.size());
181                 idx++;
182                 if (idx % 3 == 1) {
183                     idx++;
184                 }
185             }
186             assertEquals(9, lines.size(), "Line Count doesn't match");
187             assertEquals(9, idx, "Line Count doesn't match");
188             assertEquals(6, actualLines, "Line Count doesn't match");
189 
190             // try calling next() after file processed
191             assertThrows(NoSuchElementException.class, iterator::next);
192             assertThrows(NoSuchElementException.class, iterator::nextLine);
193         }
194     }
195 
196     @Test
197     void testFilteringBufferedReader() throws Exception {
198         final String encoding = UTF_8;
199 
200         final String fileName = "LineIterator-Filter-test.txt";
201         final File testFile = new File(temporaryFolder, fileName);
202         final List<String> lines = createLinesFile(testFile, encoding, 9);
203 
204         final Reader reader = new BufferedReader(Files.newBufferedReader(testFile.toPath()));
205         testFiltering(lines, reader);
206     }
207 
208     @Test
209     void testFilteringFileReader() throws Exception {
210         final String encoding = UTF_8;
211 
212         final String fileName = "LineIterator-Filter-test.txt";
213         final File testFile = new File(temporaryFolder, fileName);
214         final List<String> lines = createLinesFile(testFile, encoding, 9);
215 
216         final Reader reader = Files.newBufferedReader(testFile.toPath());
217         testFiltering(lines, reader);
218     }
219 
220     @Test
221     void testInvalidEncoding() throws Exception {
222         final String encoding = "XXXXXXXX";
223 
224         final File testFile = new File(temporaryFolder, "LineIterator-invalidEncoding.txt");
225         createLinesFile(testFile, UTF_8, 3);
226 
227         assertThrows(UnsupportedCharsetException.class, () -> FileUtils.lineIterator(testFile, encoding));
228     }
229 
230     @Test
231     void testMissingFile() throws Exception {
232         final File testFile = new File(temporaryFolder, "dummy-missing-file.txt");
233         assertThrows(NoSuchFileException.class, () -> FileUtils.lineIterator(testFile, UTF_8));
234     }
235 
236     @Test
237     void testNextLineOnlyDefaultEncoding() throws Exception {
238         final File testFile = new File(temporaryFolder, "LineIterator-nextOnly.txt");
239         final List<String> lines = createLinesFile(testFile, 3);
240 
241         final LineIterator iterator = FileUtils.lineIterator(testFile);
242         assertLines(lines, iterator);
243     }
244 
245     @Test
246     void testNextLineOnlyNullEncoding() throws Exception {
247         final String encoding = null;
248 
249         final File testFile = new File(temporaryFolder, "LineIterator-nextOnly.txt");
250         final List<String> lines = createLinesFile(testFile, encoding, 3);
251 
252         final LineIterator iterator = FileUtils.lineIterator(testFile, encoding);
253         assertLines(lines, iterator);
254     }
255 
256     @Test
257     void testNextLineOnlyUtf8Encoding() throws Exception {
258         final String encoding = UTF_8;
259 
260         final File testFile = new File(temporaryFolder, "LineIterator-nextOnly.txt");
261         final List<String> lines = createLinesFile(testFile, encoding, 3);
262 
263         final LineIterator iterator = FileUtils.lineIterator(testFile, encoding);
264         assertLines(lines, iterator);
265     }
266 
267     @Test
268     void testNextOnly() throws Exception {
269         final String encoding = null;
270 
271         final File testFile = new File(temporaryFolder, "LineIterator-nextOnly.txt");
272         final List<String> lines = createLinesFile(testFile, encoding, 3);
273 
274         try (LineIterator iterator = FileUtils.lineIterator(testFile, encoding)) {
275             for (int i = 0; i < lines.size(); i++) {
276                 final String line = iterator.next();
277                 assertEquals(lines.get(i), line, "next() line " + i);
278             }
279             assertFalse(iterator.hasNext(), "No more expected");
280         }
281     }
282 
283     @Test
284     void testNextWithException() throws Exception {
285         final Reader reader = new BufferedReader(new StringReader("")) {
286             @Override
287             public String readLine() throws IOException {
288                 throw new IOException("hasNext");
289             }
290         };
291         try (LineIterator li = new LineIterator(reader)) {
292             assertThrows(IllegalStateException.class, li::hasNext);
293         }
294     }
295 
296     @Test
297     void testOneLines() throws Exception {
298         doTestFileWithSpecifiedLines(1);
299     }
300 
301     @Test
302     void testThreeLines() throws Exception {
303         doTestFileWithSpecifiedLines(3);
304     }
305 
306     @Test
307     void testTwoLines() throws Exception {
308         doTestFileWithSpecifiedLines(2);
309     }
310 
311     @Test
312     void testValidEncoding() throws Exception {
313         final String encoding = UTF_8;
314 
315         final File testFile = new File(temporaryFolder, "LineIterator-validEncoding.txt");
316         createLinesFile(testFile, encoding, 3);
317 
318         try (LineIterator iterator = FileUtils.lineIterator(testFile, encoding)) {
319             int count = 0;
320             while (iterator.hasNext()) {
321                 assertNotNull(iterator.next());
322                 count++;
323             }
324             assertEquals(3, count);
325         }
326     }
327 
328     @Test
329     void testZeroLines() throws Exception {
330         doTestFileWithSpecifiedLines(0);
331     }
332 
333 }