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.io.*;
23  import java.nio.file.*;
24  import java.time.format.*;
25  import java.util.*;
26  
27  import org.apache.juneau.*;
28  import org.junit.jupiter.api.*;
29  import org.junit.jupiter.params.*;
30  import org.junit.jupiter.params.provider.*;
31  
32  /**
33   * Unit tests for {@link Stringifiers}.
34   */
35  class Stringifiers_Test extends TestBase {
36  
37  	@Nested
38  	class A_mapEntryStringifier extends TestBase {
39  
40  		@Test
41  		void a01_stringifySimpleEntry() {
42  			var converter = BasicBeanConverter.builder().defaultSettings().build();
43  			var stringifier = Stringifiers.mapEntryStringifier();
44  			var entry = Map.entry("key", "value");
45  			var result = stringifier.apply(converter, entry);
46  
47  			assertEquals("key=value", result);
48  		}
49  
50  		@Test
51  		void a02_stringifyWithNullKey() {
52  			var converter = BasicBeanConverter.builder().defaultSettings().build();
53  			var stringifier = Stringifiers.mapEntryStringifier();
54  			var entry = new AbstractMap.SimpleEntry<String, String>(null, "value");
55  			var result = stringifier.apply(converter, entry);
56  
57  			assertEquals("<null>=value", result);
58  		}
59  
60  		@Test
61  		void a03_stringifyWithNullValue() {
62  			var converter = BasicBeanConverter.builder().defaultSettings().build();
63  			var stringifier = Stringifiers.mapEntryStringifier();
64  			var entry = new AbstractMap.SimpleEntry<>("key", null);
65  			var result = stringifier.apply(converter, entry);
66  
67  			assertEquals("key=<null>", result);
68  		}
69  
70  		@Test
71  		void a04_stringifyWithCustomSeparator() {
72  			var converter = BasicBeanConverter.builder()
73  				.defaultSettings()
74  				.addSetting("mapEntrySeparator", ": ")
75  				.build();
76  			var stringifier = Stringifiers.mapEntryStringifier();
77  			var entry = Map.entry("name", "John");
78  			var result = stringifier.apply(converter, entry);
79  
80  			assertEquals("name: John", result);
81  		}
82  
83  		@Test
84  		void a05_stringifyWithComplexValues() {
85  			var converter = BasicBeanConverter.builder().defaultSettings().build();
86  			var stringifier = Stringifiers.mapEntryStringifier();
87  			var entry = Map.entry("list", List.of("a", "b"));
88  			var result = stringifier.apply(converter, entry);
89  
90  			assertEquals("list=[a,b]", result);
91  		}
92  	}
93  
94  	@Nested
95  	class B_calendarStringifier extends TestBase {
96  
97  		@Test
98  		void b01_stringifyGregorianCalendar() {
99  			var converter = BasicBeanConverter.builder().defaultSettings().build();
100 			var stringifier = Stringifiers.calendarStringifier();
101 			var calendar = new GregorianCalendar(2023, Calendar.JANUARY, 15, 10, 30, 0);
102 			calendar.setTimeZone(TimeZone.getTimeZone("UTC"));
103 			var result = stringifier.apply(converter, calendar);
104 
105 			assertMatchesGlob("2023-01-15T*", result);
106 		}
107 
108 		@Test
109 		void b02_stringifyWithCustomFormat() {
110 			var converter = BasicBeanConverter.builder()
111 				.defaultSettings()
112 				.addSetting("calendarFormat", DateTimeFormatter.ISO_LOCAL_DATE)
113 				.build();
114 			var stringifier = Stringifiers.calendarStringifier();
115 			var calendar = new GregorianCalendar(2023, Calendar.DECEMBER, 25);
116 			var result = stringifier.apply(converter, calendar);
117 
118 			assertMatchesGlob("2023-12-25", result);
119 		}
120 	}
121 
122 	@Nested
123 	class C_dateStringifier extends TestBase {
124 
125 		@Test
126 		void c01_stringifyDate() {
127 			var stringifier = Stringifiers.dateStringifier();
128 			var date = new Date(1673780400000L); // 2023-01-15T11:00:00Z
129 			var result = stringifier.apply(null, date);
130 
131 			assertEquals("2023-01-15T11:00:00Z", result);
132 		}
133 
134 		@Test
135 		void c02_stringifyDateWithConverter() {
136 			var converter = BasicBeanConverter.builder().defaultSettings().build();
137 			var stringifier = Stringifiers.dateStringifier();
138 			var date = new Date(0); // Epoch
139 			var result = stringifier.apply(converter, date);
140 
141 			assertEquals("1970-01-01T00:00:00Z", result);
142 		}
143 	}
144 
145 	@Nested
146 	class D_inputStreamStringifier extends TestBase {
147 
148 		@Test
149 		void d01_stringifyInputStream() {
150 			var stringifier = Stringifiers.inputStreamStringifier();
151 			var input = new ByteArrayInputStream(new byte[]{0x48, 0x65, 0x6C, 0x6C, 0x6F}); // "Hello"
152 			var result = stringifier.apply(null, input);
153 
154 			assertEquals("48656C6C6F", result);
155 		}
156 
157 		@Test
158 		void d02_stringifyEmptyInputStream() {
159 			var stringifier = Stringifiers.inputStreamStringifier();
160 			var input = new ByteArrayInputStream(new byte[0]);
161 			var result = stringifier.apply(null, input);
162 
163 			assertEquals("", result);
164 		}
165 
166 		@Test
167 		void d03_stringifyInputStreamWithZeros() {
168 			var stringifier = Stringifiers.inputStreamStringifier();
169 			var input = new ByteArrayInputStream(new byte[]{0x00, (byte)0xFF, 0x7F});
170 			var result = stringifier.apply(null, input);
171 
172 			assertEquals("00FF7F", result);
173 		}
174 	}
175 
176 	@Nested
177 	class E_byteArrayStringifier extends TestBase {
178 
179 		@Test
180 		void e01_stringifyByteArray() {
181 			var stringifier = Stringifiers.byteArrayStringifier();
182 			var input = new byte[]{0x48, 0x65, 0x6C, 0x6C, 0x6F}; // "Hello"
183 			var result = stringifier.apply(null, input);
184 
185 			assertEquals("48656C6C6F", result);
186 		}
187 
188 		@Test
189 		void e02_stringifyEmptyByteArray() {
190 			var stringifier = Stringifiers.byteArrayStringifier();
191 			var input = new byte[0];
192 			var result = stringifier.apply(null, input);
193 
194 			assertEquals("", result);
195 		}
196 
197 		@Test
198 		void e03_stringifyByteArrayWithAllValues() {
199 			var stringifier = Stringifiers.byteArrayStringifier();
200 			var input = new byte[]{0x00, (byte)0xFF, 0x7F, (byte)0x80};
201 			var result = stringifier.apply(null, input);
202 
203 			assertEquals("00FF7F80", result);
204 		}
205 	}
206 
207 	@Nested
208 	class F_readerStringifier extends TestBase {
209 
210 		@ParameterizedTest
211 		@ValueSource(strings = {
212 			"Hello World",
213 			"",
214 			"Line 1\nLine 2\nLine 3"
215 		})
216 		void f01_stringifyReader(String input) {
217 			var stringifier = Stringifiers.readerStringifier();
218 			var reader = new StringReader(input);
219 			var result = stringifier.apply(null, reader);
220 
221 			assertEquals(input, result);
222 		}
223 	}
224 
225 	@Nested
226 	class G_fileStringifier extends TestBase {
227 
228 		@Test
229 		void g01_stringifyTempFile() throws Exception {
230 			var stringifier = Stringifiers.fileStringifier();
231 			var tempFile = Files.createTempFile("test", ".txt");
232 			Files.writeString(tempFile, "Test content");
233 
234 			try {
235 				var result = stringifier.apply(null, tempFile.toFile());
236 				assertEquals("Test content", result);
237 			} finally {
238 				Files.deleteIfExists(tempFile);
239 			}
240 		}
241 
242 		@Test
243 		void g02_stringifyEmptyFile() throws Exception {
244 			var stringifier = Stringifiers.fileStringifier();
245 			var tempFile = Files.createTempFile("empty", ".txt");
246 
247 			try {
248 				var result = stringifier.apply(null, tempFile.toFile());
249 				assertEquals("", result);
250 			} finally {
251 				Files.deleteIfExists(tempFile);
252 			}
253 		}
254 	}
255 
256 	@Nested
257 	class H_enumStringifier extends TestBase {
258 
259 		enum TestEnum { VALUE1, VALUE2, LONG_VALUE_NAME }
260 
261 		@Test
262 		void h01_stringifyEnum() {
263 			var stringifier = Stringifiers.enumStringifier();
264 			var result = stringifier.apply(null, TestEnum.VALUE1);
265 
266 			assertEquals("VALUE1", result);
267 		}
268 
269 		@Test
270 		void h02_stringifyEnumWithLongName() {
271 			var stringifier = Stringifiers.enumStringifier();
272 			var result = stringifier.apply(null, TestEnum.LONG_VALUE_NAME);
273 
274 			assertEquals("LONG_VALUE_NAME", result);
275 		}
276 
277 		@Test
278 		void h03_stringifyStandardEnum() {
279 			var stringifier = Stringifiers.enumStringifier();
280 			var result = stringifier.apply(null, Thread.State.RUNNABLE);
281 
282 			assertEquals("RUNNABLE", result);
283 		}
284 	}
285 
286 	@Nested
287 	class I_classStringifier extends TestBase {
288 
289 		@Test
290 		void i01_stringifyClassSimple() {
291 			var converter = BasicBeanConverter.builder()
292 				.defaultSettings()
293 				.addSetting("classNameFormat", "simple")
294 				.build();
295 			var stringifier = Stringifiers.classStringifier();
296 			var result = stringifier.apply(converter, String.class);
297 
298 			assertEquals("String", result);
299 		}
300 
301 		@Test
302 		void i02_stringifyClassCanonical() {
303 			var converter = BasicBeanConverter.builder()
304 				.defaultSettings()
305 				.addSetting("classNameFormat", "canonical")
306 				.build();
307 			var stringifier = Stringifiers.classStringifier();
308 			var result = stringifier.apply(converter, ArrayList.class);
309 
310 			assertEquals("java.util.ArrayList", result);
311 		}
312 
313 		@Test
314 		void i03_stringifyClassFull() {
315 			var converter = BasicBeanConverter.builder()
316 				.defaultSettings()
317 				.addSetting("classNameFormat", "full")
318 				.build();
319 			var stringifier = Stringifiers.classStringifier();
320 			var result = stringifier.apply(converter, HashMap.class);
321 
322 			assertEquals("java.util.HashMap", result);
323 		}
324 
325 		@Test
326 		void i04_stringifyPrimitiveClass() {
327 			var converter = BasicBeanConverter.builder().defaultSettings().build();
328 			var stringifier = Stringifiers.classStringifier();
329 			var result = stringifier.apply(converter, int.class);
330 
331 			assertEquals("int", result);
332 		}
333 	}
334 
335 	@Nested
336 	class J_constructorStringifier extends TestBase {
337 
338 		@Test
339 		void j01_stringifyNoArgConstructor() throws Exception {
340 			var converter = BasicBeanConverter.builder().defaultSettings().build();
341 			var stringifier = Stringifiers.constructorStringifier();
342 			var constructor = ArrayList.class.getConstructor();
343 			var result = stringifier.apply(converter, constructor);
344 
345 			assertEquals("ArrayList()", result);
346 		}
347 
348 		@Test
349 		void j02_stringifyParameterizedConstructor() throws Exception {
350 			var converter = BasicBeanConverter.builder().defaultSettings().build();
351 			var stringifier = Stringifiers.constructorStringifier();
352 			var constructor = String.class.getConstructor(char[].class);
353 			var result = stringifier.apply(converter, constructor);
354 
355 			assertEquals("String(char[])", result);
356 		}
357 
358 		@Test
359 		void j03_stringifyMultiParameterConstructor() throws Exception {
360 			var converter = BasicBeanConverter.builder().defaultSettings().build();
361 			var stringifier = Stringifiers.constructorStringifier();
362 			var constructor = String.class.getConstructor(byte[].class, int.class, int.class);
363 			var result = stringifier.apply(converter, constructor);
364 
365 			assertEquals("String(byte[],int,int)", result);
366 		}
367 	}
368 
369 	@Nested
370 	class K_methodStringifier extends TestBase {
371 
372 		@Test
373 		void k01_stringifyNoArgMethod() throws Exception {
374 			var converter = BasicBeanConverter.builder().defaultSettings().build();
375 			var stringifier = Stringifiers.methodStringifier();
376 			var method = Object.class.getMethod("toString");
377 			var result = stringifier.apply(converter, method);
378 
379 			assertEquals("toString()", result);
380 		}
381 
382 		@Test
383 		void k02_stringifyParameterizedMethod() throws Exception {
384 			var converter = BasicBeanConverter.builder().defaultSettings().build();
385 			var stringifier = Stringifiers.methodStringifier();
386 			var method = String.class.getMethod("substring", int.class);
387 			var result = stringifier.apply(converter, method);
388 
389 			assertEquals("substring(int)", result);
390 		}
391 
392 		@Test
393 		void k03_stringifyMultiParameterMethod() throws Exception {
394 			var converter = BasicBeanConverter.builder().defaultSettings().build();
395 			var stringifier = Stringifiers.methodStringifier();
396 			var method = String.class.getMethod("substring", int.class, int.class);
397 			var result = stringifier.apply(converter, method);
398 
399 			assertEquals("substring(int,int)", result);
400 		}
401 	}
402 
403 	@Nested
404 	class L_listStringifier extends TestBase {
405 
406 		@Test
407 		void l01_stringifyList() {
408 			var converter = BasicBeanConverter.builder().defaultSettings().build();
409 			var stringifier = Stringifiers.listStringifier();
410 			var input = List.of("a", "b", "c");
411 			var result = stringifier.apply(converter, input);
412 
413 			assertEquals("[a,b,c]", result);
414 		}
415 
416 		@Test
417 		void l02_stringifyEmptyList() {
418 			var converter = BasicBeanConverter.builder().defaultSettings().build();
419 			var stringifier = Stringifiers.listStringifier();
420 			var input = List.of();
421 			var result = stringifier.apply(converter, input);
422 
423 			assertEquals("[]", result);
424 		}
425 
426 		@Test
427 		void l03_stringifyWithCustomDelimiters() {
428 			var converter = BasicBeanConverter.builder()
429 				.defaultSettings()
430 				.addSetting("fieldSeparator", "; ")
431 				.addSetting("collectionPrefix", "(")
432 				.addSetting("collectionSuffix", ")")
433 				.build();
434 			var stringifier = Stringifiers.listStringifier();
435 			var input = List.of("x", "y", "z");
436 			var result = stringifier.apply(converter, input);
437 
438 			assertEquals("(x; y; z)", result);
439 		}
440 
441 		@Test
442 		void l04_stringifyWithNullElements() {
443 			var converter = BasicBeanConverter.builder().defaultSettings().build();
444 			var stringifier = Stringifiers.listStringifier();
445 			var input = Arrays.asList("a", null, "c");
446 			var result = stringifier.apply(converter, input);
447 
448 			assertEquals("[a,<null>,c]", result);
449 		}
450 	}
451 
452 	@Nested
453 	class M_mapStringifier extends TestBase {
454 
455 		@Test
456 		void m01_stringifyMap() {
457 			var converter = BasicBeanConverter.builder().defaultSettings().build();
458 			var stringifier = Stringifiers.mapStringifier();
459 			var input = Map.of("key1", "value1", "key2", "value2");
460 			var result = stringifier.apply(converter, input);
461 
462 			// Order may vary, so check both possibilities
463 			assertTrue(result.equals("{key1=value1,key2=value2}") ||
464 				result.equals("{key2=value2,key1=value1}"));
465 		}
466 
467 		@Test
468 		void m02_stringifyEmptyMap() {
469 			var converter = BasicBeanConverter.builder().defaultSettings().build();
470 			var stringifier = Stringifiers.mapStringifier();
471 			var input = Map.of();
472 			var result = stringifier.apply(converter, input);
473 
474 			assertEquals("{}", result);
475 		}
476 
477 		@Test
478 		void m03_stringifyWithCustomDelimiters() {
479 			var converter = BasicBeanConverter.builder()
480 				.defaultSettings()
481 				.addSetting("fieldSeparator", "; ")
482 				.addSetting("mapPrefix", "<")
483 				.addSetting("mapSuffix", ">")
484 				.build();
485 			var stringifier = Stringifiers.mapStringifier();
486 			var input = Map.of("a", "1");
487 			var result = stringifier.apply(converter, input);
488 
489 			assertEquals("<a=1>", result);
490 		}
491 
492 		@Test
493 		void m04_stringifyWithNullValues() {
494 			var converter = BasicBeanConverter.builder().defaultSettings().build();
495 			var stringifier = Stringifiers.mapStringifier();
496 			var input = new HashMap<String, String>();
497 			input.put("key1", "value1");
498 			input.put("key2", null);
499 			var result = stringifier.apply(converter, input);
500 
501 			assertTrue(result.contains("key1=value1"));
502 			assertTrue(result.contains("key2=<null>"));
503 		}
504 	}
505 
506 	@Nested
507 	class N_integration extends TestBase {
508 
509 		@Test
510 		void n01_useInBasicBeanConverter() {
511 			// Test various stringifiable objects
512 			assertBean(Map.entry("k", "v"), "<self>", "k=v");
513 			assertBean(List.of("a", "b"), "<self>", "[a,b]");
514 			assertBean(Map.of("x", "y"), "<self>", "{x=y}");
515 			assertBean(TestEnum.VALUE1, "<self>", "VALUE1");
516 			assertBean(String.class, "<self>", "String");
517 		}
518 
519 		@Test
520 		void n02_customStringifierRegistration() {
521 			// Test that custom registration works
522 			assertBean(List.of("custom"), "<self>", "[custom]");
523 		}
524 
525 		@Test
526 		void n03_stringifierChaining() {
527 			// Map with list values
528 			var complexMap = Map.of("items", List.of("a", "b"));
529 			assertMatchesGlob("*items=[a,b]*", BasicBeanConverter.DEFAULT.stringify(complexMap));
530 		}
531 
532 		private enum TestEnum { VALUE1, VALUE2 }
533 	}
534 }