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