e6269a9a3dce5cca4100fbdac2dd18961a308fad
[policy/drools-pdp.git] /
1 /*
2  * ============LICENSE_START=======================================================
3  * ONAP
4  * ================================================================================
5  * Copyright (C) 2018 AT&T Intellectual Property. All rights reserved.
6  * ================================================================================
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  * 
11  *      http://www.apache.org/licenses/LICENSE-2.0
12  * 
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  * ============LICENSE_END=========================================================
19  */
20
21 package org.onap.policy.drools.pooling.extractor;
22
23 import static org.junit.Assert.assertEquals;
24 import static org.junit.Assert.assertFalse;
25 import static org.junit.Assert.assertNull;
26 import java.util.Map;
27 import java.util.Properties;
28 import java.util.TreeMap;
29 import java.util.function.Function;
30 import org.junit.Before;
31 import org.junit.Test;
32
33 public class ClassExtractorsTest {
34
35     private static final int NTIMES = 5;
36
37     private static final String MY_TYPE = "theType";
38     private static final String PROP_PREFIX = "extractor." + MY_TYPE + ".";
39
40     private static final String VALUE = "a value";
41     private static final Integer INT_VALUE = 10;
42     private static final Integer INT_VALUE2 = 20;
43
44     private Properties props;
45     private ClassExtractors map;
46
47     @Before
48     public void setUp() {
49         props = new Properties();
50
51         props.setProperty(PROP_PREFIX + Simple.class.getName(), "${intValue}");
52         props.setProperty(PROP_PREFIX + WithString.class.getName(), "${strValue}");
53
54         map = new ClassExtractors(props, PROP_PREFIX, MY_TYPE);
55     }
56
57     @Test
58     public void testExtract() {
59         Simple obj = new Simple();
60         assertEquals(INT_VALUE, map.extract(obj));
61
62         // string value
63         assertEquals(VALUE, tryIt(Simple.class, "${strValue}", xxx -> new Simple()));
64
65         // null object
66         assertNull(map.extract(null));
67
68         // values from two different kinds of objects
69         props = new Properties();
70         props.setProperty(PROP_PREFIX + Simple.class.getName(), "${intValue}");
71         props.setProperty(PROP_PREFIX + WithString.class.getName(), "${strValue}");
72         map = new ClassExtractors(props, PROP_PREFIX, MY_TYPE);
73
74         assertEquals(INT_VALUE, map.extract(new Simple()));
75         assertEquals(VALUE, map.extract(new Sub()));
76
77         // values from a superclass method, but property defined for subclass
78         props = new Properties();
79         props.setProperty(PROP_PREFIX + Sub.class.getName(), "${strValue}");
80         map = new ClassExtractors(props, PROP_PREFIX, MY_TYPE);
81
82         assertEquals(VALUE, map.extract(new Sub()));
83
84         // values from a superclass field, but property defined for subclass
85         props = new Properties();
86         props.setProperty(PROP_PREFIX + Sub.class.getName(), "${intValue}");
87         map = new ClassExtractors(props, PROP_PREFIX, MY_TYPE);
88
89         assertEquals(INT_VALUE, map.extract(new Sub()));
90
91
92         // prefix includes trailing "."
93         props = new Properties();
94         props.setProperty(PROP_PREFIX + Simple.class.getName(), "${intValue}");
95         map = new ClassExtractors(props, PROP_PREFIX.substring(0, PROP_PREFIX.length() - 1), MY_TYPE);
96         assertEquals(INT_VALUE, map.extract(new Simple()));
97
98
99         // values from an class in a different file
100         props = new Properties();
101         props.setProperty(PROP_PREFIX + ClassExtractorsTestSupport.class.getName(), "${nested.theValue}");
102         map = new ClassExtractors(props, PROP_PREFIX, MY_TYPE);
103
104         assertEquals(ClassExtractorsTestSupport2.NESTED_VALUE, map.extract(new ClassExtractorsTestSupport()));
105     }
106
107     @Test
108     public void testGetExtractor() {
109         Simple obj = new Simple();
110
111         // repeat - shouldn't re-create the extractor
112         for (int x = 0; x < NTIMES; ++x) {
113             assertEquals("x=" + x, INT_VALUE, map.extract(obj));
114             assertEquals("x=" + x, 1, map.size());
115         }
116     }
117
118     @Test
119     public void testBuildExtractorClass_TopLevel() {
120         // extractor defined for top-level class
121         props = new Properties();
122         props.setProperty(PROP_PREFIX + Sub.class.getName(), "${strValue}");
123
124         map = new ClassExtractors(props, PROP_PREFIX, MY_TYPE);
125         assertEquals(VALUE, map.extract(new Sub()));
126
127         // one extractor for top-level class
128         assertEquals(1, map.size());
129     }
130
131     @Test
132     public void testBuildExtractorClass_SuperClass() {
133         // extractor defined for superclass (interface)
134         assertEquals(VALUE, map.extract(new Sub()));
135
136         // one extractor for top-level class and one for interface
137         assertEquals(2, map.size());
138     }
139
140     @Test
141     public void testBuildExtractorClass_NotDefined() {
142         // no extractor defined for "this" class
143         assertNull(map.extract(this));
144
145         // one NULL extractor for top-level class
146         assertEquals(1, map.size());
147     }
148
149     @Test
150     public void testBuildExtractorClassString() {
151         // no leading "${"
152         assertNull(tryIt(Simple.class, "intValue}", xxx -> new Simple()));
153
154         // no trailing "}"
155         assertNull(tryIt(Simple.class, "${intValue", xxx -> new Simple()));
156
157         // leading "."
158         assertNull(tryIt(Sub.class, "${.simple.strValue}", xxx -> new Sub()));
159
160         // trailing "."
161         assertNull(tryIt(Sub.class, "${simple.strValue.}", xxx -> new Sub()));
162
163         // one component
164         assertEquals(VALUE, tryIt(Sub.class, "${strValue}", xxx -> new Sub()));
165
166         // two components
167         assertEquals(VALUE, tryIt(Sub.class, "${simple.strValue}", xxx -> new Sub()));
168
169         // invalid component
170         assertNull(tryIt(Sub.class, "${unknown}", xxx -> new Sub()));
171     }
172
173     @Test
174     public void testGetClassExtractor_InSuper() {
175         // field in the superclass
176         assertEquals(INT_VALUE, tryIt(Super.class, "${intValue}", xxx -> new Sub()));
177     }
178
179     @Test
180     public void testGetClassExtractor_InInterface() {
181         // defined in the interface
182         assertEquals(VALUE, map.extract(new Sub()));
183     }
184
185     @Test
186     public void testNullExtractorExtract() {
187         // empty properties - should only create NullExtractor
188         map = new ClassExtractors(new Properties(), PROP_PREFIX, MY_TYPE);
189
190         Simple obj = new Simple();
191
192         // repeat - shouldn't re-create the extractor
193         for (int x = 0; x < NTIMES; ++x) {
194             assertNull("x=" + x, map.extract(obj));
195             assertEquals("x=" + x, 1, map.size());
196         }
197     }
198
199     @Test
200     public void testComponetizedExtractor() {
201         // one component
202         assertEquals(VALUE, tryIt(Sub.class, "${strValue}", xxx -> new Sub()));
203
204         // three components
205         assertEquals(VALUE, tryIt(Sub.class, "${cont.data.strValue}", xxx -> new Sub()));
206     }
207
208     @Test
209     public void testComponetizedExtractorBuildExtractor_Method() {
210         assertEquals(INT_VALUE, tryIt(Simple.class, "${intValue}", xxx -> new Simple()));
211     }
212
213     @Test
214     public void testComponetizedExtractorBuildExtractor_Field() {
215         assertEquals(VALUE, tryIt(Simple.class, "${strValue}", xxx -> new Simple()));
216     }
217
218     @Test
219     public void testComponetizedExtractorBuildExtractor_Map() {
220         Map<String, Object> inner = new TreeMap<>();
221         inner.put("inner1", "abc1");
222         inner.put("inner2", "abc2");
223
224         Map<String, Object> outer = new TreeMap<>();
225         outer.put("outer1", "def1");
226         outer.put("outer2", inner);
227
228         Simple obj = new Simple();
229
230         props.setProperty(PROP_PREFIX + Simple.class.getName(), "${mapValue}");
231         map = new ClassExtractors(props, PROP_PREFIX, MY_TYPE);
232         assertEquals(null, map.extract(obj));
233
234         obj.mapValue = outer;
235         props.setProperty(PROP_PREFIX + Simple.class.getName(), "${mapValue.outer2.inner2}");
236         map = new ClassExtractors(props, PROP_PREFIX, MY_TYPE);
237         assertEquals("abc2", map.extract(obj));
238     }
239
240     @Test
241     public void testComponetizedExtractorBuildExtractor_Unknown() {
242         assertNull(tryIt(Simple.class, "${unknown2}", xxx -> new Simple()));
243     }
244
245     @Test
246     public void testComponetizedExtractorExtract_MiddleNull() {
247         // data component is null
248         assertEquals(null, tryIt(Sub.class, "${cont.data.strValue}", xxx -> {
249             Sub obj = new Sub();
250             obj.cont.simpleValue = null;
251             return obj;
252         }));
253     }
254
255     @Test
256     public void testComponetizedExtractorGetMethodExtractor_VoidMethod() {
257         // tell it to use getVoidValue()
258         props.setProperty(PROP_PREFIX + Simple.class.getName(), "${voidValue}");
259         map = new ClassExtractors(props, PROP_PREFIX, MY_TYPE);
260
261         Simple obj = new Simple();
262         assertNull(map.extract(obj));
263
264         assertFalse(obj.voidInvoked);
265     }
266
267     @Test
268     public void testComponetizedExtractorGetMethodExtractor() {
269         assertEquals(INT_VALUE, map.extract(new Simple()));
270     }
271
272     @Test
273     public void testComponetizedExtractorGetFieldExtractor() {
274         // use a field
275         assertEquals(VALUE, tryIt(Simple.class, "${strValue}", xxx -> new Simple()));
276     }
277
278     @Test
279     public void testComponetizedExtractorGetMapExtractor() {
280         Map<String, Object> inner = new TreeMap<>();
281         inner.put("inner1", "abc1");
282         inner.put("inner2", "abc2");
283
284         Map<String, Object> outer = new TreeMap<>();
285         outer.put("outer1", "def1");
286         outer.put("outer2", inner);
287
288         Simple obj = new Simple();
289
290         obj.mapValue = outer;
291         props.setProperty(PROP_PREFIX + Simple.class.getName(), "${mapValue.outer2.inner2}");
292         map = new ClassExtractors(props, PROP_PREFIX, MY_TYPE);
293         assertEquals("abc2", map.extract(obj));
294     }
295
296     @Test
297     public void testComponetizedExtractorGetMapExtractor_MapSubclass() {
298         Map<String, Object> inner = new TreeMap<>();
299         inner.put("inner1", "abc1");
300         inner.put("inner2", "abc2");
301
302         MapSubclass outer = new MapSubclass();
303         outer.put("outer1", "def1");
304         outer.put("outer2", inner);
305
306         Simple obj = new Simple();
307
308         props.setProperty(PROP_PREFIX + Simple.class.getName(), "${mapValue}");
309         map = new ClassExtractors(props, PROP_PREFIX, MY_TYPE);
310         assertEquals(null, map.extract(obj));
311
312         obj.mapValue = outer;
313         props.setProperty(PROP_PREFIX + Simple.class.getName(), "${mapValue.outer2.inner2}");
314         map = new ClassExtractors(props, PROP_PREFIX, MY_TYPE);
315         assertEquals("abc2", map.extract(obj));
316     }
317
318     /**
319      * Sets a property for the given class, makes an object, and then returns
320      * the value extracted.
321      * 
322      * @param clazz class whose property is to be set
323      * @param propval value to which to set the property
324      * @param makeObj function to create the object whose data is to be
325      *        extracted
326      * @return the extracted data, or {@code null} if nothing was extracted
327      */
328     private Object tryIt(Class<?> clazz, String propval, Function<Void, Object> makeObj) {
329         Properties props = new Properties();
330         props.setProperty(PROP_PREFIX + clazz.getName(), propval);
331
332         map = new ClassExtractors(props, PROP_PREFIX, MY_TYPE);
333
334         return map.extract(makeObj.apply(null));
335     }
336
337     /**
338      * A Map subclass, used to verify that getMapExtractor() still handles it.
339      */
340     private static class MapSubclass extends TreeMap<String, Object> {
341         private static final long serialVersionUID = 1L;
342
343     }
344
345     /**
346      * A simple class.
347      */
348     private static class Simple {
349
350         /**
351          * This will not be used because getIntValue() will override it.
352          */
353         @SuppressWarnings("unused")
354         public final int intValue = INT_VALUE2;
355
356         /**
357          * Used to verify retrieval via a field name.
358          */
359         @SuppressWarnings("unused")
360         public final String strValue = VALUE;
361
362         /**
363          * Used to verify retrieval within maps.
364          */
365         @SuppressWarnings("unused")
366         public Map<String, Object> mapValue = null;
367
368         /**
369          * {@code True} if {@link #getVoidValue()} was invoked, {@code false}
370          * otherwise.
371          */
372         private boolean voidInvoked = false;
373
374         /**
375          * This function will supercede the value in the "intValue" field.
376          * 
377          * @return INT_VALUE
378          */
379         @SuppressWarnings("unused")
380         public Integer getIntValue() {
381             return INT_VALUE;
382         }
383
384         /**
385          * Used to verify that void functions are not invoked.
386          */
387         @SuppressWarnings("unused")
388         public void getVoidValue() {
389             voidInvoked = true;
390         }
391     }
392
393     /**
394      * Used to verify multi-component retrieval.
395      */
396     private static class Container {
397         public Simple simpleValue = new Simple();
398
399         @SuppressWarnings("unused")
400         public Simple getData() {
401             return simpleValue;
402         }
403     }
404
405     /**
406      * Used to verify extraction when the property refers to an interface.
407      */
408     private static interface WithString {
409
410         public String getStrValue();
411     }
412
413     /**
414      * Used to verify retrieval within a superclass.
415      */
416     private static class Super implements WithString {
417
418         @SuppressWarnings("unused")
419         public final int intValue = INT_VALUE;
420
421         @Override
422         public String getStrValue() {
423             return VALUE;
424         }
425     }
426
427     /**
428      * Used to verify retrieval within a subclass.
429      */
430     private static class Sub extends Super {
431
432         @SuppressWarnings("unused")
433         public final Simple simple = new Simple();
434
435         /**
436          * Used to verify multi-component retrieval.
437          */
438         public final Container cont = new Container();
439     }
440 }