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