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.junit.bct.BctAssertions.*;
20  import static org.junit.jupiter.api.Assertions.*;
21  
22  import java.util.*;
23  import java.util.stream.*;
24  
25  import org.apache.juneau.*;
26  import org.junit.jupiter.api.*;
27  
28  /**
29   * Unit tests for {@link Listifiers}.
30   */
31  class Listifiers_Test extends TestBase {
32  
33  	@Nested
34  	class A_collectionListifier extends TestBase {
35  
36  		@Test
37  		void a01_listifyList() {
38  			var listifier = Listifiers.collectionListifier();
39  			var input = List.of("a", "b", "c");
40  			var result = listifier.apply(null, input);
41  
42  			assertNotSame(input, result); // Should create new list
43  			assertList(result, "a", "b", "c");
44  		}
45  
46  		@Test
47  		void a02_listifySet() {
48  			var listifier = Listifiers.collectionListifier();
49  			var input = Set.of("z", "a", "m"); // Unordered input
50  			var result = listifier.apply(null, input);
51  
52  			// TreeSet conversion ensures natural ordering
53  			assertList(result, "a", "m", "z");
54  		}
55  
56  		@Test
57  		void a02a_listifySetTypes() {
58  			var listifier = Listifiers.collectionListifier();
59  
60  			// HashSet (unordered) -> converted to TreeSet for natural ordering
61  			var hashSet = new HashSet<>(Arrays.asList("z", "a", "m"));
62  			var hashResult = listifier.apply(null, hashSet);
63  			assertList(hashResult, "a", "m", "z");
64  
65  			// LinkedHashSet preserves insertion order
66  			var linkedSet = new LinkedHashSet<>(Arrays.asList("z", "a", "m"));
67  			var linkedResult = listifier.apply(null, linkedSet);
68  			assertList(linkedResult, "z", "a", "m"); // Insertion order
69  
70  			// TreeSet already sorted, preserves its order
71  			var treeSet = new TreeSet<>(Arrays.asList("z", "a", "m"));
72  			var treeResult = listifier.apply(null, treeSet);
73  			assertList(treeResult, "a", "m", "z"); // Natural order
74  		}
75  
76  		@Test
77  		void a03_listifyQueue() {
78  			var listifier = Listifiers.collectionListifier();
79  			var input = new LinkedList<>(List.of("first", "second"));
80  			var result = listifier.apply(null, input);
81  
82  			assertList(result, "first", "second");
83  		}
84  
85  		@Test
86  		void a04_listifyEmptyCollection() {
87  			var listifier = Listifiers.collectionListifier();
88  			var input = List.of();
89  			var result = listifier.apply(null, input);
90  
91  			assertTrue(result.isEmpty());
92  		}
93  
94  		@Test
95  		void a05_listifyWithConverter() {
96  			var converter = BasicBeanConverter.builder().defaultSettings().build();
97  			var listifier = Listifiers.collectionListifier();
98  			var input = List.of(1, 2, 3);
99  			var result = listifier.apply(converter, input);
100 
101 			assertList(result, 1, 2, 3);
102 		}
103 	}
104 
105 	@Nested
106 	class B_iterableListifier extends TestBase {
107 
108 		@Test
109 		void b01_listifyIterable() {
110 			var listifier = Listifiers.iterableListifier();
111 			Iterable<String> input = List.of("a", "b", "c");
112 			var result = listifier.apply(null, input);
113 
114 			assertList(result, "a", "b", "c");
115 		}
116 
117 		@Test
118 		void b02_listifyCustomIterable() {
119 			var listifier = Listifiers.iterableListifier();
120 			// Create a simple custom iterable
121 			Iterable<Integer> input = () -> IntStream.range(1, 4).iterator();
122 			var result = listifier.apply(null, input);
123 
124 			assertList(result, 1, 2, 3);
125 		}
126 
127 		@Test
128 		void b03_listifyEmptyIterable() {
129 			var listifier = Listifiers.iterableListifier();
130 			Iterable<String> input = List.of();
131 			var result = listifier.apply(null, input);
132 
133 			assertTrue(result.isEmpty());
134 		}
135 
136 		@Test
137 		void b04_listifyWithConverter() {
138 			var converter = BasicBeanConverter.builder().defaultSettings().build();
139 			var listifier = Listifiers.iterableListifier();
140 			Iterable<String> input = Set.of("x", "y");
141 			var result = listifier.apply(converter, input);
142 
143 			assertEquals(2, result.size());
144 			assertTrue(result.contains("x"));
145 			assertTrue(result.contains("y"));
146 		}
147 	}
148 
149 	@Nested
150 	class C_iteratorListifier extends TestBase {
151 
152 		@Test
153 		void c01_listifyIterator() {
154 			var listifier = Listifiers.iteratorListifier();
155 			var input = List.of("a", "b", "c").iterator();
156 			var result = listifier.apply(null, input);
157 
158 			assertList(result, "a", "b", "c");
159 			// Iterator should be exhausted
160 			assertFalse(input.hasNext());
161 		}
162 
163 		@Test
164 		void c02_listifyEmptyIterator() {
165 			var listifier = Listifiers.iteratorListifier();
166 			var input = List.of().iterator();
167 			var result = listifier.apply(null, input);
168 
169 			assertTrue(result.isEmpty());
170 		}
171 
172 		@Test
173 		void c03_listifyLargeIterator() {
174 			var listifier = Listifiers.iteratorListifier();
175 			var input = IntStream.range(0, 1000).iterator();
176 			var result = listifier.apply(null, input);
177 
178 			assertEquals(1000, result.size());
179 			assertEquals(0, result.get(0));
180 			assertEquals(999, result.get(999));
181 		}
182 
183 		@Test
184 		void c04_listifyWithConverter() {
185 			var converter = BasicBeanConverter.builder().defaultSettings().build();
186 			var listifier = Listifiers.iteratorListifier();
187 			var input = List.of("x", "y", "z").iterator();
188 			var result = listifier.apply(converter, input);
189 
190 			assertList(result, "x", "y", "z");
191 		}
192 	}
193 
194 	@Nested
195 	class D_enumerationListifier extends TestBase {
196 
197 		@Test
198 		void d01_listifyEnumeration() {
199 			var listifier = Listifiers.enumerationListifier();
200 			var vector = new Vector<>(List.of("a", "b", "c"));
201 			var input = vector.elements();
202 			var result = listifier.apply(null, input);
203 
204 			assertList(result, "a", "b", "c");
205 			// Enumeration should be exhausted
206 			assertFalse(input.hasMoreElements());
207 		}
208 
209 		@Test
210 		void d02_listifyEmptyEnumeration() {
211 			var listifier = Listifiers.enumerationListifier();
212 			var vector = new Vector<String>();
213 			var input = vector.elements();
214 			var result = listifier.apply(null, input);
215 
216 			assertTrue(result.isEmpty());
217 		}
218 
219 		@Test
220 		void d03_listifyHashtableKeys() {
221 			var listifier = Listifiers.enumerationListifier();
222 			var hashtable = new Hashtable<String, String>();
223 			hashtable.put("key1", "value1");
224 			hashtable.put("key2", "value2");
225 			var input = hashtable.keys();
226 			var result = listifier.apply(null, input);
227 
228 			assertEquals(2, result.size());
229 			assertTrue(result.contains("key1"));
230 			assertTrue(result.contains("key2"));
231 		}
232 
233 		@Test
234 		void d04_listifyWithConverter() {
235 			var converter = BasicBeanConverter.builder().defaultSettings().build();
236 			var listifier = Listifiers.enumerationListifier();
237 			var vector = new Vector<>(List.of(1, 2, 3));
238 			var input = vector.elements();
239 			var result = listifier.apply(converter, input);
240 
241 			assertList(result, 1, 2, 3);
242 		}
243 	}
244 
245 	@Nested
246 	class E_streamListifier extends TestBase {
247 
248 		@Test
249 		void e01_listifyStream() {
250 			var listifier = Listifiers.streamListifier();
251 			var input = Stream.of("a", "b", "c");
252 			var result = listifier.apply(null, input);
253 
254 			assertList(result, "a", "b", "c");
255 		}
256 
257 		@Test
258 		void e02_listifyEmptyStream() {
259 			var listifier = Listifiers.streamListifier();
260 			var input = Stream.empty();
261 			var result = listifier.apply(null, input);
262 
263 			assertTrue(result.isEmpty());
264 		}
265 
266 		@Test
267 		void e03_listifyFilteredStream() {
268 			var listifier = Listifiers.streamListifier();
269 			var input = IntStream.range(1, 10)
270 				.filter(n -> n % 2 == 0)
271 				.boxed();
272 			var result = listifier.apply(null, input);
273 
274 			assertList(result, 2, 4, 6, 8);
275 		}
276 
277 		@Test
278 		void e04_listifyMappedStream() {
279 			var listifier = Listifiers.streamListifier();
280 			var input = Stream.of("hello", "world")
281 				.map(String::toUpperCase);
282 			var result = listifier.apply(null, input);
283 
284 			assertList(result, "HELLO", "WORLD");
285 		}
286 
287 		@Test
288 		void e05_listifyWithConverter() {
289 			var converter = BasicBeanConverter.builder().defaultSettings().build();
290 			var listifier = Listifiers.streamListifier();
291 			var input = Stream.of(1, 2, 3);
292 			var result = listifier.apply(converter, input);
293 
294 			assertList(result, 1, 2, 3);
295 		}
296 	}
297 
298 	@Nested
299 	class F_mapListifier extends TestBase {
300 
301 		@Test
302 		void f01_listifyMap() {
303 			var listifier = Listifiers.mapListifier();
304 			var input = Map.of("z", "value1", "a", "value2"); // Unordered input
305 			var result = listifier.apply(null, input);
306 
307 			assertEquals(2, result.size());
308 			// Result should contain Map.Entry objects
309 			assertTrue(result.stream().allMatch(obj -> obj instanceof Map.Entry));
310 
311 			// TreeMap conversion ensures natural key ordering
312 			var entries = result.stream()
313 				.map(obj -> (Map.Entry<?, ?>) obj)
314 				.toList();
315 			assertEquals("a", entries.get(0).getKey());
316 			assertEquals("z", entries.get(1).getKey());
317 		}
318 
319 		@Test
320 		void f01a_listifyMapTypes() {
321 			var listifier = Listifiers.mapListifier();
322 
323 			// HashMap (unordered) -> converted to TreeMap for natural key ordering
324 			var hashMap = new HashMap<String, String>();
325 			hashMap.put("z", "value1");
326 			hashMap.put("a", "value2");
327 			hashMap.put("m", "value3");
328 			var hashResult = listifier.apply(null, hashMap);
329 			var hashEntries = hashResult.stream()
330 				.map(obj -> (Map.Entry<?, ?>) obj)
331 				.toList();
332 			assertEquals("a", hashEntries.get(0).getKey());
333 			assertEquals("m", hashEntries.get(1).getKey());
334 			assertEquals("z", hashEntries.get(2).getKey());
335 
336 			// LinkedHashMap preserves insertion order
337 			var linkedMap = new LinkedHashMap<String, String>();
338 			linkedMap.put("z", "value1");
339 			linkedMap.put("a", "value2");
340 			linkedMap.put("m", "value3");
341 			var linkedResult = listifier.apply(null, linkedMap);
342 			var linkedEntries = linkedResult.stream()
343 				.map(obj -> (Map.Entry<?, ?>) obj)
344 				.toList();
345 			assertEquals("z", linkedEntries.get(0).getKey()); // Insertion order
346 			assertEquals("a", linkedEntries.get(1).getKey());
347 			assertEquals("m", linkedEntries.get(2).getKey());
348 
349 			// TreeMap already sorted, preserves its order
350 			var treeMap = new TreeMap<String, String>();
351 			treeMap.put("z", "value1");
352 			treeMap.put("a", "value2");
353 			treeMap.put("m", "value3");
354 			var treeResult = listifier.apply(null, treeMap);
355 			var treeEntries = treeResult.stream()
356 				.map(obj -> (Map.Entry<?, ?>) obj)
357 				.toList();
358 			assertEquals("a", treeEntries.get(0).getKey()); // Natural order
359 			assertEquals("m", treeEntries.get(1).getKey());
360 			assertEquals("z", treeEntries.get(2).getKey());
361 		}
362 
363 		@Test
364 		void f02_listifyEmptyMap() {
365 			var listifier = Listifiers.mapListifier();
366 			var input = Map.of();
367 			var result = listifier.apply(null, input);
368 
369 			assertTrue(result.isEmpty());
370 		}
371 
372 		@Test
373 		void f03_listifyMapWithNullValues() {
374 			var listifier = Listifiers.mapListifier();
375 			var input = new HashMap<String, String>();
376 			input.put("key1", "value1");
377 			input.put("key2", null);
378 			var result = listifier.apply(null, input);
379 
380 			assertEquals(2, result.size());
381 			// Check that we have the expected entries
382 			var hasKey1Entry = result.stream()
383 				.filter(obj -> obj instanceof Map.Entry)
384 				.map(obj -> (Map.Entry<?, ?>) obj)
385 				.anyMatch(entry -> "key1".equals(entry.getKey()) && "value1".equals(entry.getValue()));
386 			assertTrue(hasKey1Entry);
387 
388 			var hasKey2Entry = result.stream()
389 				.filter(obj -> obj instanceof Map.Entry)
390 				.map(obj -> (Map.Entry<?, ?>) obj)
391 				.anyMatch(entry -> "key2".equals(entry.getKey()) && entry.getValue() == null);
392 			assertTrue(hasKey2Entry);
393 		}
394 
395 		@Test
396 		void f04_listifyWithConverter() {
397 			var converter = BasicBeanConverter.builder().defaultSettings().build();
398 			var listifier = Listifiers.mapListifier();
399 			var input = Map.of("a", 1, "b", 2);
400 			var result = listifier.apply(converter, input);
401 
402 			assertEquals(2, result.size());
403 			assertTrue(result.stream().allMatch(obj -> obj instanceof Map.Entry));
404 		}
405 	}
406 
407 	@Nested
408 	class G_integration extends TestBase {
409 
410 		@Test
411 		void g01_useInBasicBeanConverter() {
412 			// Test various listifiable objects
413 			assertList(List.of("a", "b"), "a", "b");
414 			assertList(new LinkedHashSet<>(Arrays.asList("x", "y")), "x", "y");
415 			assertSize(3, Stream.of(1, 2, 3));
416 			assertEmpty(Optional.empty());
417 		}
418 
419 		@Test
420 		void g02_customListifierRegistration() {
421 			// Test that custom registration works
422 			assertList(List.of("custom"), "custom");
423 		}
424 
425 		@Test
426 		void g03_listifierChaining() {
427 			// Test that listified objects can be processed by other listifiers
428 			// Stream of optionals
429 			var streamOfOptionals = Stream.of(Optional.of("a"), Optional.empty(), Optional.of("b"));
430 			assertSize(3, streamOfOptionals);
431 		}
432 	}
433 }