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