|
44 | 44 | import java.sql.Types; |
45 | 45 | import java.util.*; |
46 | 46 | import java.util.concurrent.Callable; |
| 47 | +import java.util.concurrent.CancellationException; |
| 48 | +import java.util.concurrent.CountDownLatch; |
47 | 49 | import java.util.concurrent.ExecutionException; |
48 | 50 | import java.util.concurrent.ExecutorService; |
49 | 51 | import java.util.concurrent.Future; |
| 52 | +import java.util.concurrent.TimeUnit; |
| 53 | +import java.util.concurrent.TimeoutException; |
50 | 54 | import java.util.regex.Pattern; |
51 | 55 | import org.junit.jupiter.api.BeforeEach; |
52 | 56 | import org.junit.jupiter.api.Test; |
@@ -3308,4 +3312,125 @@ public void testMetadataAndResultSetMetadataTypeMappingConsistency(StandardSQLTy |
3308 | 3312 | assertEquals( |
3309 | 3313 | metadataTypeInfo.jdbcType, (int) resultSetType, "Type mapping mismatch for " + type); |
3310 | 3314 | } |
| 3315 | + |
| 3316 | + @Test |
| 3317 | + public void testWrapThread_NullThread() { |
| 3318 | + assertNull(BigQueryDatabaseMetaData.wrapThread(null)); |
| 3319 | + } |
| 3320 | + |
| 3321 | + @Test |
| 3322 | + public void testWrapThread_BasicLifecycle() throws Exception { |
| 3323 | + CountDownLatch startLatch = new CountDownLatch(1); |
| 3324 | + CountDownLatch finishLatch = new CountDownLatch(1); |
| 3325 | + Thread t = |
| 3326 | + new Thread( |
| 3327 | + () -> { |
| 3328 | + try { |
| 3329 | + startLatch.countDown(); |
| 3330 | + finishLatch.await(); |
| 3331 | + } catch (InterruptedException e) { |
| 3332 | + // ignore |
| 3333 | + } |
| 3334 | + }); |
| 3335 | + |
| 3336 | + Future<?>[] futures = BigQueryDatabaseMetaData.wrapThread(t); |
| 3337 | + assertNotNull(futures); |
| 3338 | + assertEquals(1, futures.length); |
| 3339 | + Future<?> f = futures[0]; |
| 3340 | + |
| 3341 | + // Thread is NEW (not started yet). |
| 3342 | + assertFalse(f.isDone()); |
| 3343 | + assertFalse(f.isCancelled()); |
| 3344 | + |
| 3345 | + t.start(); |
| 3346 | + startLatch.await(); |
| 3347 | + |
| 3348 | + // Thread is running. |
| 3349 | + assertFalse(f.isDone()); |
| 3350 | + assertFalse(f.isCancelled()); |
| 3351 | + |
| 3352 | + finishLatch.countDown(); |
| 3353 | + t.join(); |
| 3354 | + |
| 3355 | + // Thread is terminated. |
| 3356 | + assertTrue(f.isDone()); |
| 3357 | + assertFalse(f.isCancelled()); |
| 3358 | + assertNull(f.get()); |
| 3359 | + } |
| 3360 | + |
| 3361 | + @Test |
| 3362 | + public void testWrapThread_CancelBeforeStart() throws Exception { |
| 3363 | + Thread t = |
| 3364 | + new Thread( |
| 3365 | + () -> { |
| 3366 | + try { |
| 3367 | + Thread.sleep(1000); |
| 3368 | + } catch (InterruptedException e) { |
| 3369 | + // ignore |
| 3370 | + } |
| 3371 | + }); |
| 3372 | + |
| 3373 | + Future<?> f = BigQueryDatabaseMetaData.wrapThread(t)[0]; |
| 3374 | + assertTrue(f.cancel(true)); |
| 3375 | + assertTrue(f.isCancelled()); |
| 3376 | + assertTrue(f.isDone()); |
| 3377 | + |
| 3378 | + // cancel on already cancelled should return false |
| 3379 | + assertFalse(f.cancel(true)); |
| 3380 | + |
| 3381 | + assertThrows(CancellationException.class, () -> f.get()); |
| 3382 | + assertThrows(CancellationException.class, () -> f.get(1, TimeUnit.SECONDS)); |
| 3383 | + } |
| 3384 | + |
| 3385 | + @Test |
| 3386 | + public void testWrapThread_CancelRunningWithInterrupt() throws Exception { |
| 3387 | + CountDownLatch startLatch = new CountDownLatch(1); |
| 3388 | + CountDownLatch interruptedLatch = new CountDownLatch(1); |
| 3389 | + Thread t = |
| 3390 | + new Thread( |
| 3391 | + () -> { |
| 3392 | + startLatch.countDown(); |
| 3393 | + try { |
| 3394 | + Thread.sleep(10000); |
| 3395 | + } catch (InterruptedException e) { |
| 3396 | + interruptedLatch.countDown(); |
| 3397 | + } |
| 3398 | + }); |
| 3399 | + |
| 3400 | + t.start(); |
| 3401 | + startLatch.await(); |
| 3402 | + |
| 3403 | + Future<?> f = BigQueryDatabaseMetaData.wrapThread(t)[0]; |
| 3404 | + assertTrue(f.cancel(true)); |
| 3405 | + assertTrue(f.isCancelled()); |
| 3406 | + assertTrue(f.isDone()); |
| 3407 | + |
| 3408 | + assertTrue(interruptedLatch.await(5, TimeUnit.SECONDS)); |
| 3409 | + assertThrows(CancellationException.class, () -> f.get()); |
| 3410 | + } |
| 3411 | + |
| 3412 | + @Test |
| 3413 | + public void testWrapThread_GetTimeout() throws Exception { |
| 3414 | + CountDownLatch startLatch = new CountDownLatch(1); |
| 3415 | + Thread t = |
| 3416 | + new Thread( |
| 3417 | + () -> { |
| 3418 | + startLatch.countDown(); |
| 3419 | + try { |
| 3420 | + Thread.sleep(10000); |
| 3421 | + } catch (InterruptedException e) { |
| 3422 | + // ignore |
| 3423 | + } |
| 3424 | + }); |
| 3425 | + |
| 3426 | + t.start(); |
| 3427 | + startLatch.await(); |
| 3428 | + |
| 3429 | + Future<?> f = BigQueryDatabaseMetaData.wrapThread(t)[0]; |
| 3430 | + assertThrows(TimeoutException.class, () -> f.get(100, TimeUnit.MILLISECONDS)); |
| 3431 | + |
| 3432 | + // Cleanup: stop the thread |
| 3433 | + t.interrupt(); |
| 3434 | + t.join(); |
| 3435 | + } |
3311 | 3436 | } |
0 commit comments