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.commons.reflect;
18  
19  import static org.apache.juneau.commons.utils.CollectionUtils.*;
20  import static org.apache.juneau.commons.utils.Utils.*;
21  import static org.apache.juneau.junit.bct.BctAssertions.*;
22  import static org.junit.jupiter.api.Assertions.*;
23  
24  import java.util.*;
25  
26  import org.apache.juneau.*;
27  import org.apache.juneau.commons.function.*;
28  import org.junit.jupiter.api.*;
29  
30  /**
31   * Unit tests for the {@link ThrowingSupplier} functional interface.
32   *
33   * <p>This test class verifies functional interface compliance, exception handling,
34   * and integration with the safe() method.</p>
35   */
36  class ThrowingSupplier_Test extends TestBase {
37  
38  	// ====================================================================================================
39  	// Functional Interface Compliance Tests
40  	// ====================================================================================================
41  
42  	@Nested
43  	class A_functionalInterfaceCompliance extends TestBase {
44  
45  		@SuppressWarnings("cast")
46  		@Test
47  		void a01_functionalInterfaceContract() {
48  			// Verify it's a proper functional interface
49  			ThrowingSupplier<String> supplier = () -> "test_value";
50  
51  			assertNotNull(supplier);
52  			assertTrue(supplier instanceof ThrowingSupplier);
53  		}
54  
55  		@Test
56  		void a02_lambdaExpressionCompatibility() throws Exception {
57  			// Test lambda expression usage
58  			ThrowingSupplier<Integer> lambda = () -> 42;
59  
60  			var result = lambda.get();
61  			assertEquals(42, result);
62  		}
63  
64  		@Test
65  		void a03_methodReferenceCompatibility() throws Exception {
66  			// Test method reference usage
67  			ThrowingSupplier<String> methodRef = SupplierMethods::getValue;
68  
69  			var result = methodRef.get();
70  			assertEquals("METHOD_VALUE", result);
71  		}
72  
73  		@Test
74  		void a04_exceptionThrowingCapability() {
75  			// Test that it can throw checked exceptions
76  			ThrowingSupplier<String> throwingSupplier = () -> {
77  				throw new Exception("Test checked exception");
78  			};
79  
80  			// Should be able to throw checked exceptions
81  			assertThrows(Exception.class, throwingSupplier::get);
82  		}
83  	}
84  
85  	// ====================================================================================================
86  	// Exception Handling Tests
87  	// ====================================================================================================
88  
89  	@Nested
90  	class B_exceptionHandling extends TestBase {
91  
92  		@Test
93  		void b01_checkedExceptionHandling() {
94  			ThrowingSupplier<String> supplier = () -> {
95  				throw new java.io.IOException("IO error");
96  			};
97  
98  			assertThrows(java.io.IOException.class, supplier::get);
99  		}
100 
101 		@Test
102 		void b02_runtimeExceptionHandling() {
103 			ThrowingSupplier<String> supplier = () -> {
104 				throw new RuntimeException("Runtime error");
105 			};
106 
107 			assertThrows(RuntimeException.class, supplier::get);
108 		}
109 
110 		@Test
111 		void b03_multipleExceptionTypes() {
112 			ThrowingSupplier<String> ioSupplier = () -> {
113 				throw new java.io.IOException("IO exception");
114 			};
115 
116 			ThrowingSupplier<String> parseSupplier = () -> {
117 				throw new java.text.ParseException("Parse exception", 0);
118 			};
119 
120 			assertThrows(java.io.IOException.class, ioSupplier::get);
121 			assertThrows(java.text.ParseException.class, parseSupplier::get);
122 		}
123 
124 		@Test
125 		void b04_noExceptionCase() throws Exception {
126 			ThrowingSupplier<String> normalSupplier = () -> "normal_value";
127 
128 			var result = normalSupplier.get();
129 			assertEquals("normal_value", result);
130 		}
131 	}
132 
133 	// ====================================================================================================
134 	// Integration with safe() Tests
135 	// ====================================================================================================
136 
137 	@Nested
138 	class C_utilsSafeIntegration extends TestBase {
139 
140 		@Test
141 		void c01_safeExecutionWithoutException() {
142 			ThrowingSupplier<String> supplier = () -> "safe_value";
143 
144 			var result = safe(supplier);
145 			assertEquals("safe_value", result);
146 		}
147 
148 		@Test
149 		void c02_safeExecutionWithCheckedException() {
150 			ThrowingSupplier<String> supplier = () -> {
151 				throw new java.io.IOException("Checked exception");
152 			};
153 
154 			// safe should wrap checked exceptions in RuntimeException
155 			var exception = assertThrows(RuntimeException.class, () -> safe(supplier));
156 			assertTrue(exception.getCause() instanceof java.io.IOException);
157 			assertEquals("Checked exception", exception.getCause().getMessage());
158 		}
159 
160 		@Test
161 		void c03_safeExecutionWithRuntimeException() {
162 			ThrowingSupplier<String> supplier = () -> {
163 				throw new RuntimeException("Runtime exception");
164 			};
165 
166 			// safe should re-throw RuntimeExceptions as-is
167 			var exception = assertThrows(RuntimeException.class, () -> safe(supplier));
168 			assertEquals("Runtime exception", exception.getMessage());
169 			assertNull(exception.getCause()); // Should not be wrapped
170 		}
171 
172 		@Test
173 		void c04_safeExecutionWithNullReturn() {
174 			ThrowingSupplier<String> supplier = () -> null;
175 
176 			var result = safe(supplier);
177 			assertNull(result);
178 		}
179 	}
180 
181 	// ====================================================================================================
182 	// Complex Scenario Tests
183 	// ====================================================================================================
184 
185 	@Nested
186 	class D_complexScenarios extends TestBase {
187 
188 		@Test
189 		void d01_fileOperationSimulation() {
190 			// Simulate file reading that might throw IOException
191 			ThrowingSupplier<String> fileReader = () -> {
192 				// Simulate file not found
193 				throw new java.io.FileNotFoundException("File not found");
194 			};
195 
196 			var exception = assertThrows(RuntimeException.class, () -> safe(fileReader));
197 			assertTrue(exception.getCause() instanceof java.io.FileNotFoundException);
198 		}
199 
200 		@Test
201 		void d02_networkOperationSimulation() {
202 			// Simulate network operation that might timeout
203 			ThrowingSupplier<String> networkCall = () -> {
204 				// Simulate network timeout
205 				throw new java.net.SocketTimeoutException("Connection timeout");
206 			};
207 
208 			var exception = assertThrows(RuntimeException.class, () -> safe(networkCall));
209 			assertTrue(exception.getCause() instanceof java.net.SocketTimeoutException);
210 		}
211 
212 		@Test
213 		void d03_databaseOperationSimulation() {
214 			// Simulate database operation that might throw SQLException
215 			ThrowingSupplier<List<String>> dbQuery = () -> {
216 				// Simulate SQL exception
217 				throw new java.sql.SQLException("Database connection failed");
218 			};
219 
220 			var exception = assertThrows(RuntimeException.class, () -> safe(dbQuery));
221 			assertTrue(exception.getCause() instanceof java.sql.SQLException);
222 		}
223 
224 		@Test
225 		void d04_successfulComplexOperation() {
226 			// Simulate successful complex operation
227 			ThrowingSupplier<Map<String, Object>> complexOperation = () -> {
228 				var result = new HashMap<String, Object>();
229 				result.put("status", "success");
230 				result.put("data", l("item1", "item2", "item3"));
231 				result.put("timestamp", System.currentTimeMillis());
232 				return result;
233 			};
234 
235 			var result = safe(complexOperation);
236 			assertNotNull(result);
237 			assertEquals("success", result.get("status"));
238 			assertNotNull(result.get("data"));
239 			assertNotNull(result.get("timestamp"));
240 		}
241 	}
242 
243 	// ====================================================================================================
244 	// Performance and Resource Tests
245 	// ====================================================================================================
246 
247 	@Nested
248 	class E_performanceAndResources extends TestBase {
249 
250 		@Test
251 		void e01_quickExecution() {
252 			ThrowingSupplier<String> fastSupplier = () -> "quick_result";
253 
254 			var start = System.nanoTime();
255 			var result = safe(fastSupplier);
256 			var end = System.nanoTime();
257 
258 			assertEquals("quick_result", result);
259 			assertTrue((end - start) < 1_000_000); // Less than 1ms
260 		}
261 
262 		@Test
263 		void e02_multipleExecutions() {
264 			var counter = new ThrowingSupplier<Integer>() {
265 				private int count = 0;
266 				@Override
267 				public Integer get() {
268 					return ++count;
269 				}
270 			};
271 
272 			assertEquals(1, safe(counter));
273 			assertEquals(2, safe(counter));
274 			assertEquals(3, safe(counter));
275 		}
276 
277 		@Test
278 		void e03_resourceCleanupSimulation() {
279 			// Simulate resource that needs cleanup
280 			var resourceClosed = booleans(false);
281 
282 			ThrowingSupplier<String> resourceUser = () -> {
283 				try {
284 					// Simulate resource usage
285 					return "resource_data";
286 				} finally {
287 					// Simulate cleanup
288 					resourceClosed[0] = true;
289 				}
290 			};
291 
292 			var result = safe(resourceUser);
293 			assertEquals("resource_data", result);
294 			assertTrue(resourceClosed[0], "Resource should be cleaned up");
295 		}
296 
297 		@Test
298 		void e04_exceptionInFinallyBlock() {
299 			// Test behavior when exception occurs in finally block
300 			ThrowingSupplier<String> problematicResource = () -> {
301 				try {
302 					return "success";
303 				} finally {
304 					// This could happen in real resource cleanup
305 					// but won't affect the result since it's in finally
306 					System.gc(); // Safe operation for test
307 				}
308 			};
309 
310 			var result = safe(problematicResource);
311 			assertEquals("success", result);
312 		}
313 	}
314 
315 	// ====================================================================================================
316 	// Concurrency Tests
317 	// ====================================================================================================
318 
319 	@Nested
320 	class F_concurrency extends TestBase {
321 
322 		@Test
323 		void f01_threadSafeExecution() {
324 			ThrowingSupplier<String> supplier = () -> "thread_safe_value";
325 
326 			// Test concurrent execution
327 			var futures = new ArrayList<java.util.concurrent.CompletableFuture<String>>();
328 			for (var i = 0; i < 10; i++) {
329 				futures.add(java.util.concurrent.CompletableFuture.supplyAsync(() -> safe(supplier)));
330 			}
331 
332 			// All should complete successfully
333 			for (var future : futures) {
334 				assertEquals("thread_safe_value", future.join());
335 			}
336 		}
337 
338 		@Test
339 		void f02_concurrentExceptionHandling() {
340 			ThrowingSupplier<String> throwingSupplier = () -> {
341 				throw new RuntimeException("Concurrent exception");
342 			};
343 
344 			// Test concurrent exception handling
345 			var futures = new ArrayList<java.util.concurrent.CompletableFuture<RuntimeException>>();
346 			for (var i = 0; i < 5; i++) {
347 				futures.add(java.util.concurrent.CompletableFuture.supplyAsync(() -> {
348 					try {
349 						safe(throwingSupplier);
350 						return null; // Should not reach here
351 					} catch (RuntimeException e) {
352 						return e;
353 					}
354 				}));
355 			}
356 
357 			// All should throw the same exception type
358 			for (var future : futures) {
359 				var exception = future.join();
360 				assertNotNull(exception);
361 				assertEquals("Concurrent exception", exception.getMessage());
362 			}
363 		}
364 	}
365 
366 	// ====================================================================================================
367 	// Generic Type Tests
368 	// ====================================================================================================
369 
370 	@Nested
371 	class G_genericTypes extends TestBase {
372 
373 		@Test
374 		void g01_stringTypeSupplier() throws Exception {
375 			ThrowingSupplier<String> stringSupplier = () -> "string_value";
376 			assertEquals("string_value", stringSupplier.get());
377 		}
378 
379 		@Test
380 		void g02_integerTypeSupplier() throws Exception {
381 			ThrowingSupplier<Integer> intSupplier = () -> 42;
382 			assertEquals(42, intSupplier.get());
383 		}
384 
385 		@Test
386 		void g03_listTypeSupplier() throws Exception {
387 			ThrowingSupplier<List<String>> listSupplier = () -> l("a", "b", "c");
388 			var result = listSupplier.get();
389 			assertSize(3, result);
390 			assertEquals("a", result.get(0));
391 		}
392 
393 		@Test
394 		void g04_mapTypeSupplier() throws Exception {
395 			ThrowingSupplier<Map<String, Integer>> mapSupplier = () -> {
396 				var map = new HashMap<String, Integer>();
397 				map.put("key1", 1);
398 				map.put("key2", 2);
399 				return map;
400 			};
401 
402 			var result = mapSupplier.get();
403 			assertSize(2, result);
404 			assertEquals(1, result.get("key1"));
405 		}
406 
407 		@Test
408 		void g05_customObjectTypeSupplier() throws Exception {
409 			ThrowingSupplier<TestResult> objectSupplier = () -> new TestResult("success", 100);
410 
411 			var result = objectSupplier.get();
412 			assertEquals("success", result.status);
413 			assertEquals(100, result.value);
414 		}
415 	}
416 
417 	// ====================================================================================================
418 	// Helper Classes and Methods
419 	// ====================================================================================================
420 
421 	static class SupplierMethods {
422 		static String getValue() {
423 			return "METHOD_VALUE";
424 		}
425 
426 		static String getValueWithException() throws Exception {
427 			throw new Exception("Method exception");
428 		}
429 	}
430 
431 	static class TestResult {
432 		final String status;
433 		final int value;
434 
435 		TestResult(String status, int value) {
436 			this.status = status;
437 			this.value = value;
438 		}
439 	}
440 }