View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.apache.juneau.junit.bct;
18  
19  import static org.apache.juneau.commons.utils.CollectionUtils.*;
20  import static org.apache.juneau.junit.bct.BctAssertions.*;
21  import static org.junit.jupiter.api.Assertions.*;
22  
23  import java.util.*;
24  import java.util.function.*;
25  
26  import org.apache.juneau.*;
27  import org.junit.jupiter.api.*;
28  
29  /**
30   * Unit tests for the {@link Listifier} functional interface.
31   *
32   * <p>This test class verifies functional interface compliance, lambda compatibility,
33   * and edge case handling for Listifier implementations.</p>
34   */
35  class Listifier_Test extends TestBase {
36  
37  	// ====================================================================================================
38  	// Functional Interface Compliance Tests
39  	// ====================================================================================================
40  
41  	@Nested
42  	class A_functionalInterfaceCompliance extends TestBase {
43  
44  		@SuppressWarnings("cast")
45  		@Test
46  		void a01_functionalInterfaceContract() {
47  			// Verify it's a proper functional interface
48  			Listifier<String> listifier = (converter, obj) -> l(obj, obj.toUpperCase());
49  
50  			assertNotNull(listifier);
51  			assertTrue(listifier instanceof BiFunction);
52  			assertTrue(listifier instanceof Listifier);
53  		}
54  
55  		@Test
56  		void a02_lambdaExpressionCompatibility() {
57  			// Test lambda expression usage
58  			Listifier<String> lambda = (converter, str) -> l(str.toLowerCase(), str.toUpperCase());
59  
60  			var converter = BasicBeanConverter.DEFAULT;
61  			var result = lambda.apply(converter, "Test");
62  
63  			assertSize(2, result);
64  			assertEquals("test", result.get(0));
65  			assertEquals("TEST", result.get(1));
66  		}
67  
68  		@Test
69  		void a03_methodReferenceCompatibility() {
70  			// Test method reference usage
71  			Listifier<String> methodRef = ListifierMethods::splitToChars;
72  
73  			var converter = BasicBeanConverter.DEFAULT;
74  			var result = methodRef.apply(converter, "abc");
75  
76  			assertSize(3, result);
77  			assertEquals("a", result.get(0));
78  			assertEquals("b", result.get(1));
79  			assertEquals("c", result.get(2));
80  		}
81  
82  		@Test
83  		void a04_biFunctionInheritance() {
84  			// Verify BiFunction methods are inherited
85  			Listifier<String> listifier = (converter, str) -> l((Object[])str.split(""));
86  
87  			// Test BiFunction.apply method
88  			var converter = BasicBeanConverter.DEFAULT;
89  			var result = listifier.apply(converter, "xy");
90  			assertSize(2, result);
91  		}
92  	}
93  
94  	// ====================================================================================================
95  	// Lambda Composition Tests
96  	// ====================================================================================================
97  
98  	@Nested
99  	class B_lambdaComposition extends TestBase {
100 
101 		@Test
102 		void b01_andThenComposition() {
103 			Listifier<String> base = (converter, str) -> l(str.toLowerCase());
104 			Function<List<Object>, List<Object>> mapper = list -> {
105 				List<Object> result = new ArrayList<>(list);
106 				result.add("ADDED");
107 				return result;
108 			};
109 
110 			BiFunction<BeanConverter, String, List<Object>> composed = base.andThen(mapper);
111 
112 			var converter = BasicBeanConverter.DEFAULT;
113 			var result = composed.apply(converter, "TEST");
114 
115 			assertSize(2, result);
116 			assertEquals("test", result.get(0));
117 			assertEquals("ADDED", result.get(1));
118 		}
119 
120 		@Test
121 		void b02_functionalComposition() {
122 			// Compose multiple listifiers
123 			Listifier<String> splitter = (converter, str) -> l((Object[])str.split(","));
124 			Listifier<String> trimmer = (converter, str) -> l((Object[])str.trim().split("\\s+"));
125 
126 			var converter = BasicBeanConverter.DEFAULT;
127 
128 			var splitResult = splitter.apply(converter, "a,b,c");
129 			assertSize(3, splitResult);
130 
131 			var trimResult = trimmer.apply(converter, "  hello   world  ");
132 			assertSize(2, trimResult);
133 			assertEquals("hello", trimResult.get(0));
134 			assertEquals("world", trimResult.get(1));
135 		}
136 	}
137 
138 	// ====================================================================================================
139 	// Edge Case Tests
140 	// ====================================================================================================
141 
142 	@Nested
143 	class C_edgeCases extends TestBase {
144 
145 		@Test
146 		void c01_nullInputHandling() {
147 			Listifier<String> nullSafe = (converter, str) -> {
148 				if (str == null) return l("NULL_INPUT");
149 				return l(str);
150 			};
151 
152 			var converter = BasicBeanConverter.DEFAULT;
153 			var result = nullSafe.apply(converter, null);
154 
155 			assertSize(1, result);
156 			assertEquals("NULL_INPUT", result.get(0));
157 		}
158 
159 		@Test
160 		void c02_emptyResultHandling() {
161 			Listifier<String> emptyReturner = (converter, str) -> list();
162 
163 			var converter = BasicBeanConverter.DEFAULT;
164 			var result = emptyReturner.apply(converter, "anything");
165 
166 			assertNotNull(result);
167 			assertEmpty(result);
168 		}
169 
170 		@Test
171 		void c03_exceptionHandling() {
172 			Listifier<String> throwing = (converter, str) -> {
173 				if ("ERROR".equals(str)) {
174 					throw new RuntimeException("Intentional test exception");
175 				}
176 				return l(str);
177 			};
178 
179 			var converter = BasicBeanConverter.DEFAULT;
180 
181 			// Normal case should work
182 			var normalResult = throwing.apply(converter, "normal");
183 			assertSize(1, normalResult);
184 			assertEquals("normal", normalResult.get(0));
185 
186 			// Exception case should throw
187 			assertThrows(RuntimeException.class, () -> throwing.apply(converter, "ERROR"));
188 		}
189 
190 		@Test
191 		void c04_largeListHandling() {
192 			Listifier<Integer> largeListGenerator = (converter, count) -> {
193 				List<Object> result = list();
194 				for (var i = 0; i < count; i++) {
195 					result.add("item_" + i);
196 				}
197 				return result;
198 			};
199 
200 			var converter = BasicBeanConverter.DEFAULT;
201 			var result = largeListGenerator.apply(converter, 1000);
202 
203 			assertSize(1000, result);
204 			assertEquals("item_0", result.get(0));
205 			assertEquals("item_999", result.get(999));
206 		}
207 	}
208 
209 	// ====================================================================================================
210 	// Integration Tests
211 	// ====================================================================================================
212 
213 	@Nested
214 	class D_integration extends TestBase {
215 
216 		@Test
217 		void d01_converterIntegration() {
218 			// Test integration with custom converter
219 			var customConverter = BasicBeanConverter.builder()
220 				.defaultSettings()
221 				.addListifier(TestObject.class, (converter, obj) ->
222 				l(obj.name, obj.value, "LISTIFIED"))
223 				.build();
224 
225 			var test = new TestObject("test", 42);
226 			var result = customConverter.listify(test);
227 
228 			assertSize(3, result);
229 			assertEquals("test", result.get(0));
230 			assertEquals(42, result.get(1));
231 			assertEquals("LISTIFIED", result.get(2));
232 		}
233 
234 		@Test
235 		void d02_multipleListifierRegistration() {
236 			var customConverter = BasicBeanConverter.builder()
237 				.defaultSettings()
238 				.addListifier(String.class, (converter, str) -> l(str.toLowerCase()))
239 				.addListifier(Integer.class, (converter, num) -> l(num, num * 2))
240 				.build();
241 
242 			// Test string listifier
243 			var stringResult = customConverter.listify("TEST");
244 			assertSize(1, stringResult);
245 			assertEquals("test", stringResult.get(0));
246 
247 			// Test integer listifier
248 			var intResult = customConverter.listify(5);
249 			assertSize(2, intResult);
250 			assertEquals(5, intResult.get(0));
251 			assertEquals(10, intResult.get(1));
252 		}
253 
254 		@Test
255 		void d03_converterPassthrough() {
256 			// Test that converter parameter is properly passed
257 			Listifier<String> converterUser = (converter, str) -> {
258 				// Use the converter parameter to stringify something
259 				String stringified = converter.stringify(l("nested", "call"));
260 				return l(str, stringified);
261 			};
262 
263 			var converter = BasicBeanConverter.DEFAULT;
264 			var result = converterUser.apply(converter, "test");
265 
266 			assertSize(2, result);
267 			assertEquals("test", result.get(0));
268 			assertTrue(result.get(1).toString().contains("nested"));
269 		}
270 	}
271 
272 	// ====================================================================================================
273 	// Performance Tests
274 	// ====================================================================================================
275 
276 	@Nested
277 	class E_performance extends TestBase {
278 
279 		@Test
280 		void e01_performanceWithLargeLists() {
281 			Listifier<Integer> rangeGenerator = (converter, count) -> {
282 				List<Object> result = list();
283 				for (var i = 0; i < count; i++) {
284 					result.add(i);
285 				}
286 				return result;
287 			};
288 
289 			var converter = BasicBeanConverter.DEFAULT;
290 
291 			var start = System.currentTimeMillis();
292 			var result = rangeGenerator.apply(converter, 10000);
293 			var end = System.currentTimeMillis();
294 
295 			assertSize(10000, result);
296 			assertTrue(end - start < 1000, "Should complete within 1 second");
297 		}
298 
299 		@Test
300 		void e02_memoryEfficiency() {
301 			Listifier<String> memoryTest = (converter, str) -> {
302 				// Create a reasonably sized list
303 				List<Object> result = list();
304 				for (var i = 0; i < 1000; i++) {
305 					result.add(str + "_" + i);
306 				}
307 				return result;
308 			};
309 
310 			var converter = BasicBeanConverter.DEFAULT;
311 			var result = memoryTest.apply(converter, "test");
312 
313 			assertSize(1000, result);
314 			assertEquals("test_0", result.get(0));
315 			assertEquals("test_999", result.get(999));
316 		}
317 	}
318 
319 	// ====================================================================================================
320 	// Helper Classes and Methods
321 	// ====================================================================================================
322 
323 	static class ListifierMethods {
324 		static List<Object> splitToChars(BeanConverter converter, String str) {
325 			return l((Object[])str.split(""));
326 		}
327 	}
328 
329 	static class TestObject {
330 		final String name;
331 		final int value;
332 
333 		TestObject(String name, int value) {
334 			this.name = name;
335 			this.value = value;
336 		}
337 	}
338 }