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.cli.help;
18  
19  import static org.junit.jupiter.api.Assertions.assertEquals;
20  import static org.junit.jupiter.api.Assertions.assertThrows;
21  
22  import java.io.IOException;
23  import java.io.StringReader;
24  import java.util.ArrayList;
25  import java.util.Arrays;
26  import java.util.Collections;
27  import java.util.LinkedList;
28  import java.util.List;
29  import java.util.Queue;
30  
31  import org.apache.commons.io.IOUtils;
32  import org.junit.jupiter.api.BeforeEach;
33  import org.junit.jupiter.api.Test;
34  import org.junit.jupiter.params.ParameterizedTest;
35  import org.junit.jupiter.params.provider.MethodSource;
36  
37  /**
38   * Tests {@link TextHelpAppendable}.
39   */
40  public final class TextHelpAppendableTest {
41  
42      private StringBuilder sb;
43      private TextHelpAppendable underTest;
44  
45      @BeforeEach
46      public void setUp() {
47          sb = new StringBuilder();
48          underTest = new TextHelpAppendable(sb);
49      }
50  
51      @Test
52      void testAdjustTableFormat() {
53          // test width smaller than header
54          // @formatter:off
55          final TableDefinition tableDefinition = TableDefinition.from("Testing",
56                  Collections.singletonList(TextStyle.builder().setMaxWidth(3).get()),
57                  Collections.singletonList("header"),
58                  // "data" shorter than "header"
59                  Collections.singletonList(Collections.singletonList("data"))
60          );
61          // @formatter:on
62          final TableDefinition actual = underTest.adjustTableFormat(tableDefinition);
63          assertEquals("header".length(), actual.columnTextStyles().get(0).getMaxWidth());
64          assertEquals("header".length(), actual.columnTextStyles().get(0).getMinWidth());
65      }
66  
67      @Test
68      void testAppend() throws IOException {
69          final char c = (char) 0x1F44D;
70          underTest.append(c);
71          assertEquals(1, sb.length());
72          assertEquals(String.valueOf(c), sb.toString());
73  
74          sb.setLength(0);
75          underTest.append("Hello");
76          assertEquals("Hello", sb.toString());
77      }
78  
79      @Test
80      void testAppendHeader() throws IOException {
81          final String[] expected = { " Hello World", " ===========", "" };
82  
83          sb.setLength(0);
84          underTest.appendHeader(1, "Hello World");
85          List<String> actual = IOUtils.readLines(new StringReader(sb.toString()));
86          assertEquals(Arrays.asList(expected), actual, "header 1 failed");
87  
88          sb.setLength(0);
89          underTest.appendHeader(2, "Hello World");
90          actual = IOUtils.readLines(new StringReader(sb.toString()));
91          expected[1] = " %%%%%%%%%%%";
92          assertEquals(Arrays.asList(expected), actual, "header 2 failed");
93  
94          sb.setLength(0);
95          underTest.appendHeader(3, "Hello World");
96          actual = IOUtils.readLines(new StringReader(sb.toString()));
97          expected[1] = " +++++++++++";
98          assertEquals(Arrays.asList(expected), actual, "header 3 failed");
99  
100         sb.setLength(0);
101         underTest.appendHeader(4, "Hello World");
102         actual = IOUtils.readLines(new StringReader(sb.toString()));
103         expected[1] = " ___________";
104         assertEquals(Arrays.asList(expected), actual, "header 4 failed");
105 
106         sb.setLength(0);
107         underTest.appendHeader(5, "Hello World");
108         actual = IOUtils.readLines(new StringReader(sb.toString()));
109         assertEquals(Arrays.asList(expected), actual, "header 5 failed");
110 
111         sb.setLength(0);
112         assertThrows(IllegalArgumentException.class, () -> underTest.appendHeader(0, "Hello World"));
113 
114         sb.setLength(0);
115         underTest.appendHeader(5, "");
116         assertEquals(0, sb.length(), "empty string test failed");
117 
118         sb.setLength(0);
119         underTest.appendHeader(5, null);
120         assertEquals(0, sb.length(), "null test failed");
121     }
122 
123     @Test
124     void testAppendList() throws IOException {
125         final List<String> expected = new ArrayList<>();
126         final String[] entries = { "one", "two", "three" };
127         for (int i = 0; i < entries.length; i++) {
128             expected.add(String.format("  %s. %s", i + 1, entries[i]));
129         }
130         expected.add("");
131 
132         sb.setLength(0);
133         underTest.appendList(true, Arrays.asList(entries));
134         List<String> actual = IOUtils.readLines(new StringReader(sb.toString()));
135         assertEquals(expected, actual, "ordered list failed");
136 
137         sb.setLength(0);
138         expected.clear();
139         for (final String entry : entries) {
140             expected.add(String.format("  * %s", entry));
141         }
142         expected.add("");
143         underTest.appendList(false, Arrays.asList(entries));
144         actual = IOUtils.readLines(new StringReader(sb.toString()));
145         assertEquals(expected, actual, "unordered list failed");
146 
147         sb.setLength(0);
148         expected.clear();
149         underTest.appendList(false, Collections.emptyList());
150         actual = IOUtils.readLines(new StringReader(sb.toString()));
151         assertEquals(expected, actual, "empty list failed");
152 
153         sb.setLength(0);
154         expected.clear();
155         underTest.appendList(false, null);
156         actual = IOUtils.readLines(new StringReader(sb.toString()));
157         assertEquals(expected, actual, "null list failed");
158     }
159 
160     @Test
161     void testAppendParagraph() throws IOException {
162         final String[] expected = { " Hello World", "" };
163 
164         sb.setLength(0);
165         underTest.appendParagraph("Hello World");
166         final List<String> actual = IOUtils.readLines(new StringReader(sb.toString()));
167         assertEquals(Arrays.asList(expected), actual);
168 
169         sb.setLength(0);
170         underTest.appendParagraph("");
171         assertEquals(0, sb.length(), "empty string test failed");
172 
173         sb.setLength(0);
174         underTest.appendParagraph(null);
175         assertEquals(0, sb.length(), "null test failed");
176     }
177 
178     @Test
179     void testAppendParagraphFormat() throws IOException {
180         final String[] expected = { " Hello Joe World 309", "" };
181 
182         sb.setLength(0);
183         underTest.appendParagraphFormat("Hello %s World %,d", "Joe", 309);
184         final List<String> actual = IOUtils.readLines(new StringReader(sb.toString()));
185         assertEquals(Arrays.asList(expected), actual);
186 
187         sb.setLength(0);
188         underTest.appendParagraphFormat("");
189         assertEquals(0, sb.length(), "empty string test failed");
190     }
191 
192     @Test
193     void testAppendTable() throws IOException {
194         final TextStyle.Builder styleBuilder = TextStyle.builder();
195         final List<TextStyle> styles = new ArrayList<>();
196         styles.add(styleBuilder.setIndent(2).get());
197         styles.add(styleBuilder.setIndent(0).setLeftPad(5).setAlignment(TextStyle.Alignment.RIGHT).get());
198         final String[] headers = { "fox", "time" };
199         // @formatter:off
200         final List<List<String>> rows = Arrays.asList(
201                 Arrays.asList("The quick brown fox jumps over the lazy dog",
202                         "Now is the time for all good people to come to the aid of their country"),
203                 Arrays.asList("Léimeann an sionnach donn gasta thar an madra leisciúil",
204                         "Anois an t-am do na daoine maithe go léir teacht i gcabhair ar a dtír")
205         );
206         // @formatter:on
207 
208         List<String> expected = new ArrayList<>();
209         expected.add(" Common Phrases");
210         expected.add("");
211         expected.add("               fox                                       time                   ");
212         expected.add(" The quick brown fox jumps over           Now is the time for all good people to");
213         expected.add("   the lazy dog                                 come to the aid of their country");
214         expected.add(" Léimeann an sionnach donn gasta       Anois an t-am do na daoine maithe go léir");
215         expected.add("   thar an madra leisciúil                           teacht i gcabhair ar a dtír");
216         expected.add("");
217 
218         TableDefinition table = TableDefinition.from("Common Phrases", styles, Arrays.asList(headers), rows);
219         sb.setLength(0);
220         underTest.setMaxWidth(80);
221         underTest.appendTable(table);
222         List<String> actual = IOUtils.readLines(new StringReader(sb.toString()));
223         assertEquals(expected, actual, "full table failed");
224 
225         table = TableDefinition.from(null, styles, Arrays.asList(headers), rows);
226         expected.remove(1);
227         expected.remove(0);
228         sb.setLength(0);
229         underTest.appendTable(table);
230         actual = IOUtils.readLines(new StringReader(sb.toString()));
231         assertEquals(expected, actual);
232 
233         table = TableDefinition.from(null, styles, Arrays.asList(headers), Collections.emptyList());
234         expected = new ArrayList<>();
235         expected.add(" fox     time");
236         expected.add("");
237         sb.setLength(0);
238         underTest.appendTable(table);
239         actual = IOUtils.readLines(new StringReader(sb.toString()));
240         assertEquals(expected, actual, "no rows test failed");
241     }
242 
243     @Test
244     void testAppendTitle() throws IOException {
245         final String[] expected = { " Hello World", " ###########", "" };
246 
247         sb.setLength(0);
248         underTest.appendTitle("Hello World");
249         final List<String> actual = IOUtils.readLines(new StringReader(sb.toString()));
250         assertEquals(Arrays.asList(expected), actual);
251 
252         sb.setLength(0);
253         underTest.appendTitle("");
254         assertEquals(0, sb.length(), "empty string test failed");
255 
256         sb.setLength(0);
257         underTest.appendTitle(null);
258         assertEquals(0, sb.length(), "null test failed");
259 
260     }
261 
262     @Test
263     void testGetStyleBuilder() {
264         final TextStyle.Builder builder = underTest.getTextStyleBuilder();
265         assertEquals(TextHelpAppendable.DEFAULT_INDENT, builder.getIndent(), "Default indent value was changed, some tests may fail");
266         assertEquals(TextHelpAppendable.DEFAULT_LEFT_PAD, builder.getLeftPad(), "Default left pad value was changed, some tests may fail");
267         assertEquals(TextHelpAppendable.DEFAULT_WIDTH, builder.getMaxWidth(), "Default width value was changed, some tests may fail");
268     }
269 
270     @Test
271     void testindexOfWrapPos() {
272         final String testString = "The quick brown fox jumps over\tthe lazy dog";
273 
274         assertEquals(9, TextHelpAppendable.indexOfWrap(testString, 10, 0), "did not find end of word");
275         assertEquals(9, TextHelpAppendable.indexOfWrap(testString, 14, 0), "did not backup to end of word");
276         assertEquals(15, TextHelpAppendable.indexOfWrap(testString, 15, 0), "did not find word at 15");
277         assertEquals(15, TextHelpAppendable.indexOfWrap(testString, 16, 0));
278         assertEquals(30, TextHelpAppendable.indexOfWrap(testString, 15, 20), "did not find break character");
279         assertEquals(30, TextHelpAppendable.indexOfWrap(testString, 150, 0), "did not handle text shorter than width");
280 
281         assertThrows(IllegalArgumentException.class, () -> TextHelpAppendable.indexOfWrap("", 0, 0));
282         assertEquals(3, TextHelpAppendable.indexOfWrap("Hello", 4, 0));
283     }
284 
285     @ParameterizedTest
286     @MethodSource("org.apache.commons.cli.help.UtilTest#charArgs")
287     void testindexOfWrapPosWithWhitespace(final Character c, final boolean isWhitespace) {
288         final String text = String.format("Hello%cWorld", c);
289         assertEquals(isWhitespace ? 5 : 6, TextHelpAppendable.indexOfWrap(text, 7, 0));
290     }
291 
292     @Test
293     void testMakeColumnQueue() {
294         final String text = "The quick brown fox jumps over the lazy dog";
295         final TextStyle.Builder styleBuilder = TextStyle.builder().setMaxWidth(10).setIndent(0).setLeftPad(0);
296 
297         Queue<String> expected = new LinkedList<>();
298         expected.add("The quick ");
299         expected.add("brown fox ");
300         expected.add("jumps over");
301         expected.add("the lazy  ");
302         expected.add("dog       ");
303 
304         Queue<String> result = underTest.makeColumnQueue(text, styleBuilder.get());
305         assertEquals(expected, result, "left aligned failed");
306 
307         expected.clear();
308         expected.add(" The quick");
309         expected.add(" brown fox");
310         expected.add("jumps over");
311         expected.add("  the lazy");
312         expected.add("       dog");
313         styleBuilder.setAlignment(TextStyle.Alignment.RIGHT);
314 
315         result = underTest.makeColumnQueue(text, styleBuilder.get());
316         assertEquals(expected, result, "right aligned failed");
317 
318         expected.clear();
319         expected.add("The quick ");
320         expected.add("brown fox ");
321         expected.add("jumps over");
322         expected.add(" the lazy ");
323         expected.add("   dog    ");
324         styleBuilder.setAlignment(TextStyle.Alignment.CENTER);
325 
326         result = underTest.makeColumnQueue(text, styleBuilder.get());
327         assertEquals(expected, result, "center aligned failed");
328 
329         expected = new LinkedList<>();
330         expected.add("      The quick");
331         expected.add("          brown");
332         expected.add("            fox");
333         expected.add("          jumps");
334         expected.add("       over the");
335         expected.add("       lazy dog");
336         styleBuilder.setAlignment(TextStyle.Alignment.RIGHT).setLeftPad(5).setIndent(2);
337 
338         result = underTest.makeColumnQueue(text, styleBuilder.get());
339         assertEquals(expected, result, "right aligned failed");
340     }
341 
342     @Test
343     void testMakeColumnQueueWithMultipleTrailingLineBreaks() {
344         // this test should work with any n>1 multiple trailing characters from TestHelpAppendable.BREAK_CHAR_SET.
345         final String text = "Header\t" + (char) Character.PARAGRAPH_SEPARATOR;
346         final TextStyle.Builder styleBuilder = TextStyle.builder().setMaxWidth(10).setIndent(0).setLeftPad(0);
347 
348         final Queue<String> expected = new LinkedList<>();
349         expected.add("Header    ");
350         expected.add("          ");
351 
352         final Queue<String> result = underTest.makeColumnQueue(text, styleBuilder.get());
353         assertEquals(expected, result, "left aligned failed");
354     }
355 
356     @Test
357     void testPrintWrapped() throws IOException {
358         String text = "The quick brown fox jumps over the lazy dog";
359         final TextStyle.Builder styleBuilder = TextStyle.builder().setMaxWidth(10).setIndent(0).setLeftPad(0);
360 
361         final List<String> expected = new ArrayList<>();
362         expected.add("The quick");
363         expected.add("brown fox");
364         expected.add("jumps over");
365         expected.add("the lazy");
366         expected.add("dog");
367         underTest.printWrapped(text, styleBuilder.get());
368         List<String> actual = IOUtils.readLines(new StringReader(sb.toString()));
369         assertEquals(expected, actual, "left aligned failed");
370 
371         sb.setLength(0);
372         expected.clear();
373         expected.add(" The quick");
374         expected.add(" brown fox");
375         expected.add("jumps over");
376         expected.add("  the lazy");
377         expected.add("       dog");
378         styleBuilder.setAlignment(TextStyle.Alignment.RIGHT);
379 
380         underTest.printWrapped(text, styleBuilder.get());
381         actual = IOUtils.readLines(new StringReader(sb.toString()));
382         assertEquals(expected, actual, "right aligned failed");
383 
384         sb.setLength(0);
385         expected.clear();
386         expected.add("The quick");
387         expected.add("brown fox");
388         expected.add("jumps over");
389         expected.add(" the lazy");
390         expected.add("   dog");
391         styleBuilder.setAlignment(TextStyle.Alignment.CENTER);
392 
393         underTest.printWrapped(text, styleBuilder.get());
394         actual = IOUtils.readLines(new StringReader(sb.toString()));
395         assertEquals(expected, actual, "center aligned failed");
396 
397         sb.setLength(0);
398         expected.clear();
399         expected.add(" The quick brown fox jumps over the lazy dog");
400 
401         assertEquals(1, underTest.getLeftPad(), "unexpected page left pad");
402         assertEquals(3, underTest.getIndent(), "unexpected page indent");
403         assertEquals(74, underTest.getMaxWidth(), "unexpected page width");
404         underTest.printWrapped(text);
405         actual = IOUtils.readLines(new StringReader(sb.toString()));
406         assertEquals(expected, actual, "default format aligned failed");
407 
408         sb.setLength(0);
409         text += ".\nNow is the time for all good people to come to the aid of their country.";
410         expected.clear();
411         expected.add(" The quick brown fox jumps over the lazy dog.");
412         expected.add("    Now is the time for all good people to come to the aid of their");
413         expected.add("    country.");
414         underTest.printWrapped(text);
415         actual = IOUtils.readLines(new StringReader(sb.toString()));
416         assertEquals(expected, actual, "default format aligned failed");
417     }
418 
419     @Test
420     void testResize() {
421         TextStyle.Builder tsBuilder = TextStyle.builder().setIndent(2).setMaxWidth(3);
422         underTest.resize(tsBuilder, 0.5);
423         assertEquals(0, tsBuilder.getIndent());
424 
425         tsBuilder = TextStyle.builder().setIndent(4).setMaxWidth(6);
426         underTest.resize(tsBuilder, 0.5);
427         assertEquals(1, tsBuilder.getIndent());
428     }
429 
430     @Test
431     void testResizeTableFormat() {
432         underTest.setMaxWidth(150);
433         final TableDefinition tableDefinition = TableDefinition.from("Caption",
434                 Collections.singletonList(TextStyle.builder().setMinWidth(20).setMaxWidth(100).get()), Collections.singletonList("header"),
435                 Collections.singletonList(Collections.singletonList("one")));
436         final TableDefinition result = underTest.adjustTableFormat(tableDefinition);
437         assertEquals(20, result.columnTextStyles().get(0).getMinWidth(), "Minimum width should not be reset");
438         assertEquals(100, result.columnTextStyles().get(0).getMaxWidth(), "Maximum width should not be reset");
439     }
440 
441     @Test
442     void testSetIndent() {
443         assertEquals(TextHelpAppendable.DEFAULT_INDENT, underTest.getIndent(), "Default indent value was changed, some tests may fail");
444         underTest.setIndent(TextHelpAppendable.DEFAULT_INDENT + 2);
445         assertEquals(underTest.getIndent(), TextHelpAppendable.DEFAULT_INDENT + 2);
446     }
447 
448     @Test
449     void testWriteColumnQueues() throws IOException {
450         final List<Queue<String>> queues = new ArrayList<>();
451 
452         Queue<String> queue = new LinkedList<>();
453         queue.add("The quick ");
454         queue.add("brown fox ");
455         queue.add("jumps over");
456         queue.add("the lazy  ");
457         queue.add("dog       ");
458 
459         queues.add(queue);
460 
461         queue = new LinkedList<>();
462         queue.add("     Now is the");
463         queue.add("     time for  ");
464         queue.add("     all good  ");
465         queue.add("     people to ");
466         queue.add("     come to   ");
467         queue.add("     the aid of");
468         queue.add("     their     ");
469         queue.add("     country   ");
470 
471         queues.add(queue);
472 
473         final TextStyle.Builder styleBuilder = TextStyle.builder().setMaxWidth(10).setIndent(0).setLeftPad(0);
474 
475         final List<TextStyle> columns = new ArrayList<>();
476         columns.add(styleBuilder.get());
477         columns.add(styleBuilder.setLeftPad(5).get());
478 
479         final List<String> expected = new ArrayList<>();
480         expected.add(" The quick      Now is the");
481         expected.add(" brown fox      time for  ");
482         expected.add(" jumps over     all good  ");
483         expected.add(" the lazy       people to ");
484         expected.add(" dog            come to   ");
485         expected.add("                the aid of");
486         expected.add("                their     ");
487         expected.add("                country   ");
488 
489         sb.setLength(0);
490         underTest.writeColumnQueues(queues, columns);
491         final List<String> actual = IOUtils.readLines(new StringReader(sb.toString()));
492         assertEquals(expected, actual);
493     }
494 }