aboutsummaryrefslogtreecommitdiffstats
path: root/feature-pooling-dmaap/src/test/java/org/onap/policy/drools/pooling/extractor/ClassExtractorsTest.java
blob: 0bf087a39952a780bf2ad2c5fb141277061979db (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
/*
 * ============LICENSE_START=======================================================
 * ONAP
 * ================================================================================
 * Copyright (C) 2018-2019 AT&T Intellectual Property. All rights reserved.
 * ================================================================================
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * ============LICENSE_END=========================================================
 */

package org.onap.policy.drools.pooling.extractor;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;

import java.util.Map;
import java.util.Properties;
import java.util.TreeMap;
import java.util.function.Function;
import org.junit.Before;
import org.junit.Test;

public class ClassExtractorsTest {

    private static final int NTIMES = 5;

    private static final String MY_TYPE = "theType";
    private static final String PROP_PREFIX = "extractor." + MY_TYPE + ".";

    private static final String VALUE = "a value";
    private static final Integer INT_VALUE = 10;
    private static final Integer INT_VALUE2 = 20;

    private Properties props;
    private ClassExtractors map;

    /**
     * Setup.
     * 
     */
    @Before
    public void setUp() {
        props = new Properties();

        props.setProperty(PROP_PREFIX + Simple.class.getName(), "${intValue}");
        props.setProperty(PROP_PREFIX + WithString.class.getName(), "${strValue}");

        map = new ClassExtractors(props, PROP_PREFIX, MY_TYPE);
    }

    @Test
    public void testExtract() {
        Simple obj = new Simple();
        assertEquals(INT_VALUE, map.extract(obj));

        // string value
        assertEquals(VALUE, tryIt(Simple.class, "${strValue}", xxx -> new Simple()));

        // null object
        assertNull(map.extract(null));

        // values from two different kinds of objects
        props = new Properties();
        props.setProperty(PROP_PREFIX + Simple.class.getName(), "${intValue}");
        props.setProperty(PROP_PREFIX + WithString.class.getName(), "${strValue}");
        map = new ClassExtractors(props, PROP_PREFIX, MY_TYPE);

        assertEquals(INT_VALUE, map.extract(new Simple()));
        assertEquals(VALUE, map.extract(new Sub()));

        // values from a superclass method, but property defined for subclass
        props = new Properties();
        props.setProperty(PROP_PREFIX + Sub.class.getName(), "${strValue}");
        map = new ClassExtractors(props, PROP_PREFIX, MY_TYPE);

        assertEquals(VALUE, map.extract(new Sub()));

        // values from a superclass field, but property defined for subclass
        props = new Properties();
        props.setProperty(PROP_PREFIX + Sub.class.getName(), "${intValue}");
        map = new ClassExtractors(props, PROP_PREFIX, MY_TYPE);

        assertEquals(INT_VALUE, map.extract(new Sub()));


        // prefix includes trailing "."
        props = new Properties();
        props.setProperty(PROP_PREFIX + Simple.class.getName(), "${intValue}");
        map = new ClassExtractors(props, PROP_PREFIX.substring(0, PROP_PREFIX.length() - 1), MY_TYPE);
        assertEquals(INT_VALUE, map.extract(new Simple()));


        // values from an class in a different file
        props = new Properties();
        props.setProperty(PROP_PREFIX + ClassExtractorsTestSupport.class.getName(), "${nested.theValue}");
        map = new ClassExtractors(props, PROP_PREFIX, MY_TYPE);

        assertEquals(ClassExtractorsTestSupport2.NESTED_VALUE, map.extract(new ClassExtractorsTestSupport()));
    }

    @Test
    public void testGetExtractor() {
        Simple obj = new Simple();

        // repeat - shouldn't re-create the extractor
        for (int x = 0; x < NTIMES; ++x) {
            assertEquals("x=" + x, INT_VALUE, map.extract(obj));
            assertEquals("x=" + x, 1, map.size());
        }
    }

    @Test
    public void testBuildExtractorClass_TopLevel() {
        // extractor defined for top-level class
        props = new Properties();
        props.setProperty(PROP_PREFIX + Sub.class.getName(), "${strValue}");

        map = new ClassExtractors(props, PROP_PREFIX, MY_TYPE);
        assertEquals(VALUE, map.extract(new Sub()));

        // one extractor for top-level class
        assertEquals(1, map.size());
    }

    @Test
    public void testBuildExtractorClass_SuperClass() {
        // extractor defined for superclass (interface)
        assertEquals(VALUE, map.extract(new Sub()));

        // one extractor for top-level class and one for interface
        assertEquals(2, map.size());
    }

    @Test
    public void testBuildExtractorClass_NotDefined() {
        // no extractor defined for "this" class
        assertNull(map.extract(this));

        // one NULL extractor for top-level class
        assertEquals(1, map.size());
    }

    @Test
    public void testBuildExtractorClassString() {
        // no leading "${"
        assertNull(tryIt(Simple.class, "intValue}", xxx -> new Simple()));

        // no trailing "}"
        assertNull(tryIt(Simple.class, "${intValue", xxx -> new Simple()));

        // leading "."
        assertNull(tryIt(Sub.class, "${.simple.strValue}", xxx -> new Sub()));

        // trailing "."
        assertNull(tryIt(Sub.class, "${simple.strValue.}", xxx -> new Sub()));

        // one component
        assertEquals(VALUE, tryIt(Sub.class, "${strValue}", xxx -> new Sub()));

        // two components
        assertEquals(VALUE, tryIt(Sub.class, "${simple.strValue}", xxx -> new Sub()));

        // invalid component
        assertNull(tryIt(Sub.class, "${unknown}", xxx -> new Sub()));
    }

    @Test
    public void testGetClassExtractor_InSuper() {
        // field in the superclass
        assertEquals(INT_VALUE, tryIt(Super.class, "${intValue}", xxx -> new Sub()));
    }

    @Test
    public void testGetClassExtractor_InInterface() {
        // defined in the interface
        assertEquals(VALUE, map.extract(new Sub()));
    }

    @Test
    public void testNullExtractorExtract() {
        // empty properties - should only create NullExtractor
        map = new ClassExtractors(new Properties(), PROP_PREFIX, MY_TYPE);

        Simple obj = new Simple();

        // repeat - shouldn't re-create the extractor
        for (int x = 0; x < NTIMES; ++x) {
            assertNull("x=" + x, map.extract(obj));
            assertEquals("x=" + x, 1, map.size());
        }
    }

    @Test
    public void testComponetizedExtractor() {
        // one component
        assertEquals(VALUE, tryIt(Sub.class, "${strValue}", xxx -> new Sub()));

        // three components
        assertEquals(VALUE, tryIt(Sub.class, "${cont.data.strValue}", xxx -> new Sub()));
    }

    @Test
    public void testComponetizedExtractorBuildExtractor_Method() {
        assertEquals(INT_VALUE, tryIt(Simple.class, "${intValue}", xxx -> new Simple()));
    }

    @Test
    public void testComponetizedExtractorBuildExtractor_Field() {
        assertEquals(VALUE, tryIt(Simple.class, "${strValue}", xxx -> new Simple()));
    }

    @Test
    public void testComponetizedExtractorBuildExtractor_Map() {
        Map<String, Object> inner = new TreeMap<>();
        inner.put("inner1", "abc1");
        inner.put("inner2", "abc2");

        Map<String, Object> outer = new TreeMap<>();
        outer.put("outer1", "def1");
        outer.put("outer2", inner);

        Simple obj = new Simple();

        props.setProperty(PROP_PREFIX + Simple.class.getName(), "${mapValue}");
        map = new ClassExtractors(props, PROP_PREFIX, MY_TYPE);
        assertEquals(null, map.extract(obj));

        obj.mapValue = outer;
        props.setProperty(PROP_PREFIX + Simple.class.getName(), "${mapValue.outer2.inner2}");
        map = new ClassExtractors(props, PROP_PREFIX, MY_TYPE);
        assertEquals("abc2", map.extract(obj));
    }

    @Test
    public void testComponetizedExtractorBuildExtractor_Unknown() {
        assertNull(tryIt(Simple.class, "${unknown2}", xxx -> new Simple()));
    }

    @Test
    public void testComponetizedExtractorExtract_MiddleNull() {
        // data component is null
        assertEquals(null, tryIt(Sub.class, "${cont.data.strValue}", xxx -> {
            Sub obj = new Sub();
            obj.cont.simpleValue = null;
            return obj;
        }));
    }

    @Test
    public void testComponetizedExtractorGetMethodExtractor_VoidMethod() {
        // tell it to use getVoidValue()
        props.setProperty(PROP_PREFIX + Simple.class.getName(), "${voidValue}");
        map = new ClassExtractors(props, PROP_PREFIX, MY_TYPE);

        Simple obj = new Simple();
        assertNull(map.extract(obj));

        assertFalse(obj.voidInvoked);
    }

    @Test
    public void testComponetizedExtractorGetMethodExtractor() {
        assertEquals(INT_VALUE, map.extract(new Simple()));
    }

    @Test
    public void testComponetizedExtractorGetFieldExtractor() {
        // use a field
        assertEquals(VALUE, tryIt(Simple.class, "${strValue}", xxx -> new Simple()));
    }

    @Test
    public void testComponetizedExtractorGetMapExtractor() {
        Map<String, Object> inner = new TreeMap<>();
        inner.put("inner1", "abc1");
        inner.put("inner2", "abc2");

        Map<String, Object> outer = new TreeMap<>();
        outer.put("outer1", "def1");
        outer.put("outer2", inner);

        Simple obj = new Simple();

        obj.mapValue = outer;
        props.setProperty(PROP_PREFIX + Simple.class.getName(), "${mapValue.outer2.inner2}");
        map = new ClassExtractors(props, PROP_PREFIX, MY_TYPE);
        assertEquals("abc2", map.extract(obj));
    }

    @Test
    public void testComponetizedExtractorGetMapExtractor_MapSubclass() {
        Map<String, Object> inner = new TreeMap<>();
        inner.put("inner1", "abc1");
        inner.put("inner2", "abc2");

        MapSubclass outer = new MapSubclass();
        outer.put("outer1", "def1");
        outer.put("outer2", inner);

        Simple obj = new Simple();

        props.setProperty(PROP_PREFIX + Simple.class.getName(), "${mapValue}");
        map = new ClassExtractors(props, PROP_PREFIX, MY_TYPE);
        assertEquals(null, map.extract(obj));

        obj.mapValue = outer;
        props.setProperty(PROP_PREFIX + Simple.class.getName(), "${mapValue.outer2.inner2}");
        map = new ClassExtractors(props, PROP_PREFIX, MY_TYPE);
        assertEquals("abc2", map.extract(obj));
    }

    /**
     * Sets a property for the given class, makes an object, and then returns
     * the value extracted.
     * 
     * @param clazz class whose property is to be set
     * @param propval value to which to set the property
     * @param makeObj function to create the object whose data is to be
     *        extracted
     * @return the extracted data, or {@code null} if nothing was extracted
     */
    private Object tryIt(Class<?> clazz, String propval, Function<Void, Object> makeObj) {
        Properties props = new Properties();
        props.setProperty(PROP_PREFIX + clazz.getName(), propval);

        map = new ClassExtractors(props, PROP_PREFIX, MY_TYPE);

        return map.extract(makeObj.apply(null));
    }

    /**
     * A Map subclass, used to verify that getMapExtractor() still handles it.
     */
    private static class MapSubclass extends TreeMap<String, Object> {
        private static final long serialVersionUID = 1L;

    }

    /**
     * A simple class.
     */
    private static class Simple {

        /**
         * This will not be used because getIntValue() will override it.
         */
        @SuppressWarnings("unused")
        public final int intValue = INT_VALUE2;

        /**
         * Used to verify retrieval via a field name.
         */
        @SuppressWarnings("unused")
        public final String strValue = VALUE;

        /**
         * Used to verify retrieval within maps.
         */
        @SuppressWarnings("unused")
        public Map<String, Object> mapValue = null;

        /**
         * {@code True} if {@link #getVoidValue()} was invoked, {@code false}
         * otherwise.
         */
        private boolean voidInvoked = false;

        /**
         * This function will supercede the value in the "intValue" field.
         * 
         * @return INT_VALUE
         */
        @SuppressWarnings("unused")
        public Integer getIntValue() {
            return INT_VALUE;
        }

        /**
         * Used to verify that void functions are not invoked.
         */
        @SuppressWarnings("unused")
        public void getVoidValue() {
            voidInvoked = true;
        }
    }

    /**
     * Used to verify multi-component retrieval.
     */
    private static class Container {
        public Simple simpleValue = new Simple();

        @SuppressWarnings("unused")
        public Simple getData() {
            return simpleValue;
        }
    }

    /**
     * Used to verify extraction when the property refers to an interface.
     */
    private static interface WithString {

        String getStrValue();
    }

    /**
     * Used to verify retrieval within a superclass.
     */
    private static class Super implements WithString {

        @SuppressWarnings("unused")
        public final int intValue = INT_VALUE;

        @Override
        public String getStrValue() {
            return VALUE;
        }
    }

    /**
     * Used to verify retrieval within a subclass.
     */
    private static class Sub extends Super {

        @SuppressWarnings("unused")
        public final Simple simple = new Simple();

        /**
         * Used to verify multi-component retrieval.
         */
        public final Container cont = new Container();
    }
}