View Javadoc
1   package org.davidmoten.text.utils;
2   
3   import static org.junit.Assert.assertEquals;
4   import static org.junit.Assert.assertTrue;
5   
6   import java.io.ByteArrayInputStream;
7   import java.io.ByteArrayOutputStream;
8   import java.io.File;
9   import java.io.FileWriter;
10  import java.io.IOException;
11  import java.io.OutputStreamWriter;
12  import java.io.Reader;
13  import java.io.StringReader;
14  import java.io.Writer;
15  import java.nio.charset.StandardCharsets;
16  import java.nio.file.Files;
17  import java.util.ArrayList;
18  import java.util.Arrays;
19  import java.util.List;
20  
21  import org.junit.FixMethodOrder;
22  import org.junit.Test;
23  import org.junit.runners.MethodSorters;
24  
25  import com.github.davidmoten.junit.Asserts;
26  
27  @FixMethodOrder(MethodSorters.NAME_ASCENDING)
28  public class WordWrapTest {
29  
30      ////////////////////////////////////////////
31      // Word wrap tests
32      ////////////////////////////////////////////
33  
34      @Test
35      public void longLineSplitsOnWhiteSpace() {
36          check("hello there", "hello\nthere");
37      }
38  
39      @Test
40      public void longLineWithTwoSpacesInMiddle() {
41          check("hello  there", "hello\nthere");
42      }
43  
44      @Test
45      public void longLineALotOfWhiteSpaceInMiddle() {
46          check("hello          there", "hello\nthere");
47      }
48  
49      @Test
50      public void precedingWhitespaceConserved() {
51          check("  he", "  he");
52      }
53  
54      @Test
55      public void precedingWhitespaceLongWord() {
56          check("  helloyou", "  hel-\nloyou");
57      }
58  
59      @Test
60      public void whitespacePreservedAfterNewLine() {
61          check("hello\n  the", "hello\n  the");
62      }
63  
64      @Test
65      public void shortLineNoWhitespace() {
66          check("hello", "hello");
67      }
68  
69      @Test
70      public void shortLineHasWhitespace() {
71          check("hi bo", "hi bo");
72      }
73  
74      @Test
75      public void emptyText() {
76          check("", "");
77      }
78  
79      @Test
80      public void oneLetter() {
81          check("a", "a");
82      }
83  
84      @Test
85      public void spaceThenOneLetter() {
86          check(" a", " a");
87      }
88  
89      @Test
90      public void newLineCharacterPreserved() {
91          check("hello\nthere", "hello\nthere");
92      }
93  
94      @Test
95      public void carriageReturnRemoved() {
96          check("hello\r\nthere", "hello\nthere");
97      }
98  
99      @Test
100     public void whitespaceConservedAfterNewLine() {
101         check("hello\n there", "hello\n there");
102     }
103 
104     @Test
105     public void wrapRightTrimsWhitespaceBeforeNewLine() {
106         check("abc    \ncde   ", "abc\ncde   ");
107     }
108 
109     @Test
110     public void longWordForcesBreak() {
111         check("hellothere", "hello-\nthere");
112     }
113 
114     @Test
115     public void longWordForcesBreakNoHyphens() {
116         assertEquals("hellot\nhere",
117                 WordWrap.from("hellothere").maxWidth(6).insertHyphens(false).wrap());
118     }
119 
120     @Test
121     public void breakOnCommaDoesNotHappenWithoutSpaceAfter() {
122         check("hi,there", "hi,th-\nere");
123     }
124 
125     @Test
126     public void noHyphenAfterDigits() {
127         check("1234567890", "123456\n7890");
128     }
129 
130     @Test
131     public void breakOnCommasWithDigits() {
132         check("1,2,3,4,5,6,7,8,9", "1,2,3,\n4,5,6,\n7,8,9");
133     }
134 
135     @Test
136     public void longThenShort() {
137         check("hellothere\n  boo", "hello-\nthere\n  boo");
138     }
139 
140     @Test
141     public void longThenShortWithMoreLines() {
142         check("hellothere\n  boo\n  hi", "hello-\nthere\n  boo\n  hi");
143     }
144 
145     @Test
146     public void endWithNewLine() {
147         check("a\n", "a\n");
148     }
149 
150     @Test
151     public void spaceAndQuestionMark() {
152         check("  ?", "  ?");
153     }
154 
155     @Test
156     public void dontBreakOnQuestionMark() {
157         check("ab cde?", "ab\ncde?");
158     }
159 
160     @Test
161     public void breakOnQuote() {
162         check("says 'helo'", "says\n'helo'");
163     }
164 
165     @Test
166     public void breakQuoteInMiddle() {
167         check("why he's nasty", "why\nhe's\nnasty");
168     }
169 
170     @Test
171     public void shortThenLong() {
172         check("hi mygoodnessme", "hi\nmygoo-\ndness-\nme");
173     }
174 
175     @Test
176     public void longWhitespaceThenWord() {
177         check("        a", "\na");
178     }
179 
180     @Test
181     public void longWhitespaceLastLine() {
182         check("          ", "");
183     }
184 
185     @Test
186     public void longWhitespaceThenNewLine() {
187         check("          \n", "\n");
188     }
189 
190     @Test
191     public void conserveWhitespace() {
192         check("  ab\n   cd\n  ef\n\nhi", "  ab\n   cd\n  ef\n\nhi");
193     }
194 
195     ////////////////////////////////////////////
196     // Left trim tests
197     ////////////////////////////////////////////
198 
199     @Test
200     public void testLeftTrimLeadingEmpty() {
201         StringBuilder2 s = new StringBuilder2("");
202         WordWrap.leftTrim(s);
203         assertEquals("", s.toString());
204     }
205 
206     @Test
207     public void testLeftTrimLeadingSpaces0() {
208         StringBuilder2 s = new StringBuilder2("abc");
209         WordWrap.leftTrim(s);
210         assertEquals("abc", s.toString());
211     }
212 
213     @Test
214     public void testLeftTrimLeadingSpaces1() {
215         StringBuilder2 s = new StringBuilder2(" abc");
216         WordWrap.leftTrim(s);
217         assertEquals("abc", s.toString());
218     }
219 
220     @Test
221     public void testLeftTrimLeadingSpaces3() {
222         StringBuilder2 s = new StringBuilder2("   abc");
223         WordWrap.leftTrim(s);
224         assertEquals("abc", s.toString());
225     }
226 
227     ////////////////////////////////////////////
228     // Right trim tests
229     ////////////////////////////////////////////
230 
231     @Test
232     public void testRightTrimAtEnd() {
233         assertEquals("abc", WordWrap.rightTrim("abc  "));
234     }
235 
236     @Test
237     public void testRightTrimNoSpace() {
238         assertEquals("abc", WordWrap.rightTrim("abc"));
239     }
240 
241     @Test
242     public void testRightTrimEmpty() {
243         assertEquals("", WordWrap.rightTrim(""));
244     }
245 
246     @Test
247     public void testRightTrimOnlySpace() {
248         assertEquals("", WordWrap.rightTrim("  "));
249     }
250 
251     ////////////////////////////////////////////
252     // Builder tests
253     ////////////////////////////////////////////
254 
255     @Test
256     public void testStringWidth() {
257         String text = WordWrap.from("abc").maxWidth(4).stringWidth(s -> s.length() * 2).wrap();
258         assertEquals("a-\nbc", text);
259     }
260 
261     @Test
262     public void testNewLineOverride() {
263         String text = WordWrap.from("abc").maxWidth(2).newLine("\r\n").wrap();
264         assertEquals("a-\r\nbc", text);
265     }
266 
267     @Test
268     public void testSetWordChars() {
269         String text = WordWrap.from("abc").maxWidth(2).extraWordChars("abc").wrap();
270         assertEquals("a-\nbc", text);
271     }
272 
273     @Test
274     public void testIncludeWordChars() {
275         String text = WordWrap.from("abc").maxWidth(2).extraWordChars("")
276                 .includeExtraWordChars("abc").wrap();
277         assertEquals("a-\nbc", text);
278     }
279 
280     @Test
281     public void testExcludeWordChars() {
282         String text = WordWrap.from("abc").maxWidth(2).extraWordChars("abc")
283                 .excludeExtraWordChars("abc").wrap();
284         assertEquals("a-\nbc", text);
285     }
286 
287     @Test
288     public void testDontBreakLongWords() {
289         String s = WordWrap.from("hello jonathon").maxWidth(6).breakWords(false).wrap();
290         assertEquals("hello\njonathon", s);
291     }
292 
293     @Test
294     public void testDontBreakLongWords2() {
295         String s = WordWrap.from("hell jonathon").maxWidth(6).breakWords(false).wrap();
296         assertEquals("hell\njonathon", s);
297     }
298 
299     @Test
300     public void testFromInputStream() {
301         ByteArrayInputStream bytes = new ByteArrayInputStream(
302                 "hi".getBytes(StandardCharsets.UTF_8));
303         assertEquals("hi", WordWrap.fromUtf8(bytes).maxWidth(6).wrap());
304     }
305 
306     @Test
307     public void testFromFile() throws IOException {
308         Files.write(new File("target/test1.txt").toPath(), "hi".getBytes(StandardCharsets.UTF_8));
309         assertEquals("hi", WordWrap.from(new File("target/test1.txt"), StandardCharsets.UTF_8)
310                 .maxWidth(6).wrap());
311     }
312 
313     @Test(expected = IORuntimeException.class)
314     public void testWriterThrows() {
315         WordWrap.from("abc").wrap(new Writer() {
316 
317             @Override
318             public void write(char[] cbuf, int off, int len) throws IOException {
319                 throw new IOException("boo");
320             }
321 
322             @Override
323             public void flush() throws IOException {
324 
325             }
326 
327             @Override
328             public void close() throws IOException {
329 
330             }
331         });
332     }
333 
334     @Test(expected = IORuntimeException.class)
335     public void testWrapToFileThrows() {
336         WordWrap.from("abc").wrap(new File("target/doesNoExist/temp.txt"), StandardCharsets.UTF_8);
337     }
338 
339     @Test(expected = IORuntimeException.class)
340     public void testCloseReaderThrows() {
341         WordWrap.close(new Reader() {
342 
343             @Override
344             public int read(char[] cbuf, int off, int len) throws IOException {
345                 return 0;
346             }
347 
348             @Override
349             public void close() throws IOException {
350                 throw new IOException("boo");
351             }
352         });
353     }
354 
355     @Test(expected = IORuntimeException.class)
356     public void testFromFileDoesNotExist() throws IOException {
357         WordWrap.from(new File("target/doesNotExist"), StandardCharsets.UTF_8).maxWidth(6).wrap();
358     }
359     
360     @Test
361     public void testToList() {
362         List<String> list = WordWrap.from("hello there how are you").maxWidth(10).wrapToList();
363         assertEquals(Arrays.asList("hello", "there how", "are you"), list);
364     }
365     
366     @Test
367     public void testToListWithNewLines() {
368         List<String> list = WordWrap.from("hello\n\n\n\nhow how\n").maxWidth(10).wrapToList();
369         assertEquals(Arrays.asList("hello", "", "","", "how how"), list);
370     }
371     
372     @Test
373     public void testToListFinishWithNewLines() {
374         List<String> list = WordWrap.from("hello\n\n\n\n").maxWidth(10).wrapToList();
375         assertEquals(Arrays.asList("hello", "", "",""), list);
376     }
377     
378     @Test(expected=IORuntimeException.class)
379     public void testLineConsumerThrows() {
380         WordWrap.from("hello there how are you").maxWidth(10).wrap(new LineConsumer() {
381 
382             @Override
383             public void write(char[] chars, int offset, int length) throws IOException {
384                 throw new IOException("problem");
385             }
386 
387             @Override
388             public void writeNewLine() throws IOException {
389                 throw new IOException("problem");
390             }});
391     }
392     
393     @Test(expected=IORuntimeException.class)
394     public void testLineConsumerThrowsDontCloseReader() {
395         StringReader reader = new StringReader("hello there how are you");
396         WordWrap.from(reader, false).maxWidth(10).wrap(new LineConsumer() {
397 
398             @Override
399             public void write(char[] chars, int offset, int length) throws IOException {
400                 throw new IOException("problem");
401             }
402 
403             @Override
404             public void writeNewLine() throws IOException {
405                 throw new IOException("problem");
406             }});
407     }
408 
409     @Test(expected = IllegalArgumentException.class)
410     public void testMaxWidthZero() {
411         WordWrap.from("abc").maxWidth(0);
412     }
413 
414     @Test
415     public void testWrapToFile() {
416         File file = new File("target/testWrap.txt");
417         WordWrap.from("abc").wrap(file.getAbsolutePath(), StandardCharsets.UTF_8);
418         assertTrue(file.exists());
419     }
420     
421     @Test
422     public void testDontWrapNumbers() {
423         assertEquals("hello\n123", WordWrap.from("hello 123").extraWordChars("0123456789").breakWords(false).maxWidth(8).wrap());
424     }
425 
426     @Test
427     public void testNumbersWrapByDefault() {
428         assertEquals("hello 12\n3", WordWrap.from("hello 123").breakWords(false).maxWidth(8).wrap());
429     }
430 
431     @Test
432     public void testStatelessness() {
433         assertEquals("hello super-\ncool", WordWrap.from("hello super-cool").breakWords(false).maxWidth(12).wrap());
434         assertEquals("hello\nsuper-cool", WordWrap.from("hello super-cool").breakWords(false).maxWidth(12).includeExtraWordChars("-").wrap());
435         assertEquals("hello super-\ncool", WordWrap.from("hello super-cool").breakWords(false).maxWidth(12).wrap());
436     }
437     
438     @Test
439     public void testIssue49() {
440         assertEquals("CELESTAN Depot\n2,7mg/ml", //
441                 WordWrap //
442                 .from("CELESTAN Depot 2,7mg/ml") //
443                 .includeExtraWordChars("0123456789") //
444                 .breakWords(false) //
445                 .maxWidth(17) //
446                 .wrap());
447     }
448     
449     ////////////////////////////////////////////
450     // Novel wrapping tests
451     ////////////////////////////////////////////
452 
453     @Test
454     public void testImportanceOfBeingEarnest() throws IOException {
455         WordWrap.fromClasspathUtf8("/the-importance-of-being-earnest.txt") //
456                 .maxWidth(20) //
457                 .wrapUtf8("target/the-importance-of-being-earnest.txt");
458     }
459 
460     @Test
461     public void testTheBlackGang() throws IOException {
462         WordWrap.fromClasspathUtf8("/the-black-gang.txt") //
463                 .maxWidth(20) //
464                 .wrapUtf8("target/the-black-gang.txt");
465     }
466 
467     @Test
468     public void testTreasureIsland() throws IOException {
469         WordWrap.fromClasspathUtf8("/treasure-island-fragment.txt") //
470                 .maxWidth(80) //
471                 .wrapUtf8("target/treasure-island-fragment.txt");
472     }
473     
474     @Test
475     public void testWrapWithAtLeastLineLongWhitespaceFollowedByParenthesis() {
476         int maxWidth = 25;
477         assertEquals("(abc", WordWrap.from(repeat(" ", maxWidth) + "(abc") //
478             .maxWidth(maxWidth).wrap());
479     }
480     
481     private static String repeat(String s, int n) {
482         StringBuilder b = new StringBuilder();
483         for (int i = 0; i < n; i++) {
484             b.append(s);
485         }
486         return b.toString();
487     }
488 
489     @Test
490     public void testIsUtilityClass() {
491         Asserts.assertIsUtilityClass(WordWrap.class);
492     }
493     
494     private static final class Check {
495         final String input;
496         final String output;
497         final String name;
498 
499         Check(String input, String output, String name) {
500             this.input = input;
501             this.output = output;
502             this.name = name;
503         }
504 
505     }
506 
507     private static List<Check> checks = new ArrayList<>();
508 
509     private static void check(String text, String expected) {
510         String s = WordWrap.from(text).maxWidth(6).wrap();
511         // System.out.println(s.replace(" ","\u2423"));
512         assertEquals(expected, s);
513         String name = Thread.currentThread().getStackTrace()[2].getMethodName();
514         checks.add(new Check(text, expected, name));
515         int i = 0;
516         try (FileWriter out = new FileWriter("src/docs/rules.md")) {
517             out.append("## Wrapping rules\n");
518             out.append(
519                     "The rules below are generated from the unit tests in `WordWrapTest.java`. The default wrapping configuration used is a line length of 6 characters. The $ symbol is used to represent a new line character.\n\n");
520             for (Check check : checks) {
521                 i++;
522                 out.append(i + ". **" + check.name + "**\n\n");
523                 out.append("Input:\n");
524                 out.append("```\n");
525                 out.append(prettify(check.input));
526                 out.append("\n```\n");
527                 out.append("Output:\n");
528                 out.append("```\n");
529                 out.append(prettify(check.output));
530                 out.append("\n```\n\n");
531             }
532         } catch (IOException e) {
533             throw new RuntimeException(e);
534         }
535     }
536 
537     private static String prettify(String s) {
538         return s.replace(" ", "\u2423").replace("\n", "$\n");
539     }
540 
541     public static void main(String[] args) throws IOException {
542         int i = 0;
543         long t = 0;
544         ByteArrayOutputStream b = new ByteArrayOutputStream(128 * 1024);
545         byte[] bytes = Files
546                 .readAllBytes(new File("src/test/resources/treasure-island-fragment.txt").toPath());
547         String text = new String(bytes, StandardCharsets.UTF_8);
548         while (true) {
549             b.reset();
550             Writer out = new OutputStreamWriter(b, StandardCharsets.UTF_8);
551             WordWrap.from(text) //
552                     .maxWidth(80) //
553                     .wrap(out);
554             i++;
555             if (i % 100 == 0) {
556                 if (i > 1000 && t == 0) {
557                     t = System.currentTimeMillis();
558                     i = 0;
559                 }
560                 long dt = System.currentTimeMillis() - t;
561                 if (dt != 0) {
562                     System.out.println("avgPerSecond=" + (i * 1000 / dt));
563                 }
564             }
565         }
566     }
567 
568 }