1   package com.lexicalscope.jewel.cli;
2   
3   import static com.lexicalscope.fluentreflection.FluentReflection.*;
4   import static org.hamcrest.Matchers.contains;
5   import static org.junit.Assert.*;
6   
7   import java.lang.reflect.Method;
8   import java.util.List;
9   
10  import org.junit.Rule;
11  import org.junit.Test;
12  import org.junit.rules.ExpectedException;
13  
14  import com.lexicalscope.jewel.cli.specification.ParsedOptionSpecification;
15  
16  public class TestOptionSpecificationImpl {
17      @Rule public ExpectedException exception = ExpectedException.none();
18  
19      public interface HasShortName {
20          @Option(shortName = "n") String getName1();
21  
22          @Option String getName2();
23      }
24  
25      public interface ShortName {
26          @Option(shortName = "") String getName0();
27  
28          @Option(shortName = "n") String getName1();
29  
30          @Option(shortName = "excessive") String getName2();
31      }
32  
33      public interface LongName {
34          @Option() String getName0();
35  
36          @Option(longName = "totallyDifferent") String getName1();
37  
38          @Option(longName = "name2") String getName2();
39      }
40  
41      public interface MultipleLongNames {
42          @Option(longName = { "name", "alternativename" }) String getName();
43      }
44  
45      public interface InvalidLongName {
46          @Option(longName = "") String getName0();
47      }
48  
49      public interface Value {
50          @Option String getName();
51  
52          @Option boolean getDebug();
53      }
54  
55      public interface Name {
56          @Option String getName();
57  
58          @Option String name();
59  
60          @Option boolean isDebug();
61  
62          @Option boolean debug();
63      }
64  
65      public interface Type {
66          @Option String getString();
67  
68          @Option Integer getInteger();
69  
70          @Option int getInt();
71  
72          @Option List<String> getStringList();
73  
74          @SuppressWarnings("rawtypes") @Option List getList();
75      }
76  
77      public interface MultiValued {
78          @Option List<String> getStringList();
79  
80          @SuppressWarnings("rawtypes") @Option List getList();
81      }
82  
83      public interface HasOptionalOption {
84          @Option String getName1();
85  
86          @Option String getName2();
87  
88          boolean isName2();
89      }
90  
91      public interface ToString {
92          @Option(longName = "aLongName") String getLongName();
93  
94          @Option(longName = { "aLongName", "alternativeLongName" }) String getMultipleLongNames();
95  
96          @Option(shortName = "s") String getShortName();
97  
98          @Option String getOptional();
99          boolean isOptional();
100 
101         @Option List<String> getOptionalMulti();
102         boolean isOptionalMulti();
103 
104         @Option(description = "this is a description") List<String> getDescription();
105 
106         @Option(description = "this is a description", shortName = "a") List<String> getAll();
107         boolean isAll();
108     }
109 
110     public interface Summary {
111         @Option(shortName = "s") String getShortName();
112 
113         @Option(longName = "aLongName") String getLongName();
114 
115         @Option(description = "this is a description") String getWithDescription();
116 
117         @Option(longName = "aLongName", shortName = "s") String getLongNameShortName();
118 
119         @Option(pattern = "[a-z]") char getPattern();
120 
121         @Option String getOptional();
122         boolean isOptional();
123 
124         @Option List<String> getList();
125 
126         @Option List<String> getOptionalList();
127         boolean isOptionalList();
128 
129         @Option(longName = "aLongName", shortName = "s") List<String> getOptionalListShortNameLongName();
130         boolean isOptionalListShortNameLongName();
131     }
132 
133     @Test public void testSumary() throws NoSuchMethodException {
134         checkSummary("getShortName", "--shortName -s value");
135         checkSummary("getLongName", "--aLongName value");
136         checkSummary("getWithDescription", "--withDescription value : this is a description");
137         checkSummary("getLongNameShortName", "--aLongName -s value");
138         checkSummary("getOptional", "[--optional value]");
139         checkSummary("getList", "--list value...");
140         checkSummary("getOptionalList", "[--optionalList value...]");
141         checkSummary("getOptionalListShortNameLongName", "[--aLongName -s value...]");
142         checkSummary("getPattern", "--pattern /[a-z]/");
143 
144         // TODO[tim]: test summary
145         // TODO[tim]: test option specifications (plural) summary.
146     }
147 
148     private void checkSummary(final String method, final String expectedSummary) throws NoSuchMethodException {
149         assertEquals(expectedSummary, new ParsedOptionSummary(createOption(Summary.class, method)).toString());
150     }
151 
152     @Test public void testToString() throws NoSuchMethodException {
153         assertEquals("--aLongName value", createOption(ToString.class, "getLongName").toString());
154         assertEquals("--aLongName --alternativeLongName value", createOption(ToString.class, "getMultipleLongNames")
155                 .toString());
156         assertEquals("--shortName -s value", createOption(ToString.class, "getShortName").toString());
157         assertEquals("[--optional value]", createOption(ToString.class, "getOptional").toString());
158         assertEquals("[--optionalMulti value...]", createOption(ToString.class, "getOptionalMulti").toString());
159         assertEquals("--description value... : this is a description", createOption(ToString.class, "getDescription")
160                 .toString());
161         assertEquals("[--all -a value...] : this is a description", createOption(ToString.class, "getAll").toString());
162     }
163 
164     @Test public void testIsMultiValued() throws NoSuchMethodException {
165         assertTrue(createOption(MultiValued.class, "getStringList").isMultiValued());
166         assertTrue(createOption(MultiValued.class, "getList").isMultiValued());
167     }
168 
169     @Test public void testGetType() throws NoSuchMethodException {
170         assertEquals(String.class, createOption(Type.class, "getString").getType());
171         assertEquals(Integer.class, createOption(Type.class, "getInteger").getType());
172         assertEquals(int.class, createOption(Type.class, "getInt").getType());
173         assertEquals(String.class, createOption(Type.class, "getStringList").getType());
174         assertEquals(String.class, createOption(Type.class, "getList").getType());
175     }
176 
177     @Test public void testGetName() throws SecurityException, NoSuchMethodException {
178         assertThat(createOption(Name.class, "getName").getLongName(), contains("name"));
179         assertThat(createOption(Name.class, "name").getLongName(), contains("name"));
180         assertThat(createOption(Name.class, "isDebug").getLongName(), contains("debug"));
181         assertThat(createOption(Name.class, "debug").getLongName(), contains("debug"));
182     }
183 
184     @Test public void testGetShortName() throws SecurityException, NoSuchMethodException {
185         assertEquals(0, createOption(ShortName.class, "getName0").getShortNames().size());
186         assertEquals("n", createOption(ShortName.class, "getName1").getShortNames().get(0));
187         assertEquals("e", createOption(ShortName.class, "getName2").getShortNames().get(0));
188     }
189 
190     @Test public void testGetLongName() throws SecurityException, NoSuchMethodException {
191         assertThat(createOption(LongName.class, "getName0").getLongName(), contains("name0"));
192         assertThat(createOption(LongName.class, "getName1").getLongName(), contains("totallyDifferent"));
193         assertThat(createOption(LongName.class, "getName2").getLongName(), contains("name2"));
194     }
195 
196     @Test public void testMultipleLongNames() throws SecurityException, NoSuchMethodException {
197         assertThat(createOption(MultipleLongNames.class, "getName").getLongName(), contains("name", "alternativename"));
198     }
199 
200     @Test public void testEmptyLongNameIsNotAllowed() throws SecurityException, NoSuchMethodException {
201         exception.expect(InvalidOptionSpecificationException.class);
202         exception.expectMessage("option public java.lang.String getName0() long name cannot be blank");
203 
204         createOption(InvalidLongName.class, "getName0");
205     }
206 
207     @Test public void testHasValue() throws SecurityException, NoSuchMethodException {
208         assertTrue(createOption(Value.class, "getName").hasValue());
209         assertFalse(createOption(Value.class, "getDebug").hasValue());
210     }
211 
212     @Test public void testHasShortName() throws SecurityException, NoSuchMethodException {
213         assertTrue(createOption(HasShortName.class, "getName1").hasShortName());
214         assertFalse(createOption(HasShortName.class, "getName2").hasShortName());
215     }
216 
217     @Test public void testIsOptional() throws SecurityException, NoSuchMethodException {
218         assertFalse(createOption(HasOptionalOption.class, "getName1").isOptional());
219         assertTrue(createOption(HasOptionalOption.class, "getName2").isOptional());
220     }
221 
222     private ParsedOptionSpecification createOption(final Class<?> klass, final String methodName)
223             throws NoSuchMethodException {
224         final Method method = klass.getMethod(methodName, (Class[]) null);
225 
226         return new ConvertGetterMethodToParsedOptionSpecification(type(klass)).convert(method(method));
227     }
228 }