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