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