@@ -21,7 +21,10 @@ Licensed to the Apache Software Foundation (ASF) under one or more
2121import static org .junit .jupiter .api .Assertions .assertEquals ;
2222import static org .junit .jupiter .api .Assertions .assertNotNull ;
2323import static org .junit .jupiter .api .Assertions .assertNull ;
24+ import static org .junit .jupiter .api .Assertions .assertSame ;
25+ import static org .junit .jupiter .api .Assertions .assertTimeoutPreemptively ;
2426import static org .junit .jupiter .api .Assertions .assertThrows ;
27+ import static org .junit .jupiter .api .Assertions .assertTrue ;
2528import static org .mockito .Mockito .spy ;
2629import static org .mockito .Mockito .when ;
2730
@@ -31,6 +34,7 @@ Licensed to the Apache Software Foundation (ASF) under one or more
3134import java .io .PrintWriter ;
3235import java .io .StringWriter ;
3336import java .io .UncheckedIOException ;
37+ import java .time .Duration ;
3438import java .util .ArrayList ;
3539import java .util .List ;
3640import java .util .stream .Stream ;
@@ -172,6 +176,51 @@ void testDeprecatedPrintOptionsZeroWidth(final int width) {
172176 assertNotNull (result );
173177 }
174178
179+ @ Test
180+ void testAppendWrappedTextCutsLongWordAfterPadding () throws IOException {
181+ final StringBuilder sb = new StringBuilder ();
182+ final HelpFormatter formatter = new HelpFormatter ();
183+
184+ assertTimeoutPreemptively (Duration .ofMillis (200 ), () -> assertSame (sb , formatter .appendWrappedText (sb , 7 , 4 , "abc defghijk" )));
185+ assertEquals ("abc" + EOL + " def" + EOL + " ghi" + EOL + " jk" , sb .toString ());
186+ }
187+
188+ @ Test
189+ void testAppendWrappedTextResetsOversizedPadding () throws IOException {
190+ final StringBuilder sb = new StringBuilder ();
191+ final HelpFormatter formatter = new HelpFormatter ();
192+
193+ assertTimeoutPreemptively (Duration .ofMillis (200 ), () -> assertSame (sb , formatter .appendWrappedText (sb , 4 , 6 , "a b c d" )));
194+ assertEquals ("a b" + EOL + " c d" , sb .toString ());
195+ }
196+
197+ @ Test
198+ void testAppendWrappedTextResetsPaddingWhenItMatchesWidth () {
199+ final StringBuilder sb = new StringBuilder ();
200+ final HelpFormatter formatter = new HelpFormatter ();
201+
202+ assertTimeoutPreemptively (Duration .ofMillis (200 ), () -> assertSame (sb , formatter .appendWrappedText (sb , 5 , 5 , "a b c d" )));
203+ assertEquals ("a b c" + EOL + " d" , sb .toString ());
204+ }
205+
206+ @ Test
207+ void testAppendWrappedTextReturnsSameAppendableWhenNoWrapIsNeeded () throws IOException {
208+ final StringBuilder sb = new StringBuilder ();
209+ final HelpFormatter formatter = new HelpFormatter ();
210+
211+ assertSame (sb , formatter .appendWrappedText (sb , 20 , 4 , "value " ));
212+ assertEquals ("value" , sb .toString ());
213+ }
214+
215+ @ Test
216+ void testAppendWrappedTextZeroWidthReturnsImmediately () {
217+ final StringBuilder sb = new StringBuilder ("seed" );
218+ final HelpFormatter formatter = new HelpFormatter ();
219+
220+ assertTimeoutPreemptively (Duration .ofMillis (200 ), () -> assertSame (sb , formatter .appendWrappedText (sb , 0 , 4 , "value" )));
221+ assertEquals ("seed" , sb .toString ());
222+ }
223+
175224 @ Test
176225 void testFindWrapPos () {
177226 final HelpFormatter hf = new HelpFormatter ();
@@ -199,6 +248,11 @@ void testFindWrapPos() {
199248 assertEquals (7 , hf .findWrapPos (text , 6 , 0 ), "wrap position 6" );
200249 }
201250
251+ @ Test
252+ void testFindWrapPosDoesNotWrapAtWhitespaceAtStartPosition () {
253+ assertEquals (1 , new HelpFormatter ().findWrapPos (" word" , 1 , 0 ));
254+ }
255+
202256 @ Test
203257 void testHeaderStartingWithLineSeparator0 () {
204258 // related to Bugzilla #21215
@@ -544,6 +598,42 @@ void testPrintOptionGroupUsage() {
544598 assertEquals ("usage: app [-a | -b | -c]" + EOL , out .toString ());
545599 }
546600
601+ @ Test
602+ void testPrintOptionGroupUsagePreservesInsertionOrderWithNullComparator () {
603+ final OptionGroup optionGroup = new OptionGroup ();
604+ optionGroup .addOption (Option .builder ("c" ).get ());
605+ optionGroup .addOption (Option .builder ("a" ).get ());
606+ optionGroup .addOption (Option .builder ("b" ).get ());
607+
608+ final Options options = new Options ();
609+ options .addOptionGroup (optionGroup );
610+
611+ final StringWriter out = new StringWriter ();
612+ final HelpFormatter formatter = new HelpFormatter ();
613+ formatter .setOptionComparator (null );
614+ formatter .printUsage (new PrintWriter (out ), 80 , "app" , options );
615+
616+ assertEquals ("usage: app [-c | -a | -b]" + EOL , out .toString ());
617+ }
618+
619+ @ Test
620+ void testPrintOptionGroupUsageSortsWhenComparatorPresent () {
621+ final OptionGroup optionGroup = new OptionGroup ();
622+ optionGroup .addOption (Option .builder ("c" ).get ());
623+ optionGroup .addOption (Option .builder ("a" ).get ());
624+ optionGroup .addOption (Option .builder ("b" ).get ());
625+
626+ final Options options = new Options ();
627+ options .addOptionGroup (optionGroup );
628+
629+ final StringWriter out = new StringWriter ();
630+ final HelpFormatter formatter = new HelpFormatter ();
631+ formatter .setOptionComparator ((opt1 , opt2 ) -> opt2 .getKey ().compareToIgnoreCase (opt1 .getKey ()));
632+ formatter .printUsage (new PrintWriter (out ), 80 , "app" , options );
633+
634+ assertEquals ("usage: app [-c | -b | -a]" + EOL , out .toString ());
635+ }
636+
547637 @ Test
548638 void testPrintOptions () {
549639 final StringBuffer sb = new StringBuffer ();
@@ -557,7 +647,7 @@ void testPrintOptions() {
557647
558648 options = new Options ().addOption ("a" , false , "aaaa aaaa aaaa aaaa aaaa" );
559649 expected = lpad + "-a" + dpad + "aaaa aaaa aaaa aaaa aaaa" ;
560- hf .renderOptions (sb , 60 , options , leftPad , descPad );
650+ assertSame ( sb , hf .renderOptions (sb , 60 , options , leftPad , descPad ) );
561651 assertEquals (expected , sb .toString (), "simple non-wrapped option" );
562652
563653 int nextLineTabStop = leftPad + descPad + "-a" .length ();
@@ -586,6 +676,16 @@ void testPrintOptions() {
586676 assertEquals (expected , sb .toString (), "multiple wrapped options" );
587677 }
588678
679+ @ Test
680+ void testPrintOptionsShowSinceKeepsLongestPrefixAligned () {
681+ final Options options = new Options ().addOption (Option .builder ("a" ).longOpt ("alpha" ).since ("v1" ).desc ("desc" ).get ());
682+ final HelpFormatter formatter = HelpFormatter .builder ().setShowSince (true ).get ();
683+ final StringBuffer sb = new StringBuffer ();
684+
685+ assertSame (sb , formatter .renderOptions (sb , 80 , options , 2 , 2 ));
686+ assertEquals ("Options Since Description" + EOL + " -a,--alpha v1 desc" , sb .toString ());
687+ }
688+
589689 @ Test
590690 void testPrintOptionWithEmptyArgNameUsage () {
591691 final Option option = new Option ("f" , true , null );
@@ -674,6 +774,23 @@ void testPrintUsage() {
674774 assertEquals ("usage: app [-a] [-b] [-c]" + EOL , bytesOut .toString ());
675775 }
676776
777+ @ Test
778+ void testPrintUsageWrapsArgumentsUsingSyntaxIndentation () {
779+ final StringWriter out = new StringWriter ();
780+
781+ new HelpFormatter ().printUsage (new PrintWriter (out ), 12 , "cmd arg1 arg2" );
782+
783+ assertEquals ("usage: cmd" + EOL
784+ + " a" + EOL
785+ + " r" + EOL
786+ + " g" + EOL
787+ + " 1" + EOL
788+ + " a" + EOL
789+ + " r" + EOL
790+ + " g" + EOL
791+ + " 2" + EOL , out .toString ());
792+ }
793+
677794 @ Test
678795 void testRenderSince () throws IOException {
679796 final String [] expected = {"Options Since Description" , " -n,--no-since - Description for n" ,
@@ -704,7 +821,7 @@ void testRenderWrappedTextMultiLine() {
704821 //@formatter:on
705822
706823 final StringBuffer sb = new StringBuffer ();
707- new HelpFormatter ().renderWrappedText (sb , width , padding , expected );
824+ assertSame ( sb , new HelpFormatter ().renderWrappedText (sb , width , padding , expected ) );
708825 assertEquals (expected , sb .toString (), "multi line text" );
709826 }
710827
@@ -794,6 +911,8 @@ void testRtrim() {
794911
795912 assertNull (formatter .rtrim (null ));
796913 assertEquals ("" , formatter .rtrim ("" ));
914+ assertEquals ("" , formatter .rtrim (" " ));
915+ assertEquals ("foo" , formatter .rtrim ("foo " ));
797916 assertEquals (" foo" , formatter .rtrim (" foo " ));
798917 }
799918
@@ -813,4 +932,25 @@ void testUsageWithLongOptSeparator() {
813932
814933 assertEquals ("usage: create [--age=<arg>] [-f <arg>] [-s <SIZE>]" , out .toString ().trim ());
815934 }
935+
936+ @ Test
937+ void testPrintHelpFlushesConfiguredPrintWriter () {
938+ final class FlushAwareStringWriter extends StringWriter {
939+ private boolean flushed ;
940+
941+ @ Override
942+ public void flush () {
943+ flushed = true ;
944+ super .flush ();
945+ }
946+ }
947+
948+ final FlushAwareStringWriter out = new FlushAwareStringWriter ();
949+ final HelpFormatter formatter = HelpFormatter .builder ().setPrintWriter (new PrintWriter (out )).get ();
950+ final Options options = new Options ().addOption ("a" , false , "desc" );
951+
952+ formatter .printHelp (80 , "app" , "header" , options , "footer" , false );
953+
954+ assertTrue (out .flushed );
955+ }
816956}
0 commit comments