Repository: netty/netty
File: transport/src/test/java/io/netty/channel/ManualIoEventLoopTest.java (Java)

Code before:
            });
        } finally {
            eventLoop.shutdownGracefully(0, 0, TimeUnit.MILLISECONDS);
            while (!eventLoop.isTerminated()) {
                eventLoop.runNow();
            }
            assertTrue(eventLoop.terminationFuture().isSuccess());
        }
    }

    @Test
    public void testDelayOwningThread() throws ExecutionException, InterruptedException {
        Semaphore semaphore = new Semaphore(0);
        ManualIoEventLoop eventLoop = new ManualIoEventLoop(null, executor ->
                new TestIoHandler(semaphore));
        Thread thread = new Thread(() -> {
            eventLoop.setOwningThread(Thread.currentThread());
            assertTrue(eventLoop.inEventLoop());
            while (!eventLoop.isTerminated()) {
                eventLoop.runNow();
            }
        });

        assertFalse(eventLoop.inEventLoop());

        CompletableFuture<Void> cf = new CompletableFuture<>();
        eventLoop.execute(() -> {
            assertTrue(eventLoop.inEventLoop());
            cf.complete(null);
        });

        thread.start();
        eventLoop.shutdownGracefully();

        cf.get();
    }

    private static final class TestRunnable implements Runnable {
        private boolean done;
        @Override
        public void run() {
            done = true;
        }

        boolean isDone() {
            return done;
        }
    }

    private static class TestIoHandler implements IoHandler {
        private final Semaphore semaphore;

Review comment (suggestion):
verify that `eventLoop::run` is throwing an exception too (you can add a separate test for it)

Code after:
            });
        } finally {
            eventLoop.shutdownGracefully(0, 0, TimeUnit.MILLISECONDS);
            while (!eventLoop.isTerminated()) {
                eventLoop.runNow();
            }
            assertTrue(eventLoop.terminationFuture().isSuccess());
        }
    }

    @Test
    public void testDelayOwningThread() throws ExecutionException, InterruptedException {
        Semaphore semaphore = new Semaphore(0);
        ManualIoEventLoop eventLoop = new ManualIoEventLoop(null, executor ->
                new TestIoHandler(semaphore));
        Thread thread = new Thread(() -> {
            eventLoop.setOwningThread(Thread.currentThread());
            assertTrue(eventLoop.inEventLoop());
            while (!eventLoop.isTerminated()) {
                eventLoop.runNow();
            }
        });

        assertFalse(eventLoop.inEventLoop());

        CompletableFuture<Void> cf = new CompletableFuture<>();
        eventLoop.execute(() -> {
            assertTrue(eventLoop.inEventLoop());
            cf.complete(null);
        });

        thread.start();
        cf.get();

        eventLoop.shutdownGracefully();
        thread.join();
    }

    @Test
    public void testRunWithoutOwner() throws ExecutionException, InterruptedException {
        ManualIoEventLoop eventLoop = new ManualIoEventLoop(null, executor ->
                new TestIoHandler(new Semaphore(0)));

        // prior to setOwningThread, runNow is forbidden
        assertThrows(IllegalStateException.class, eventLoop::runNow);

        eventLoop.setOwningThread(Thread.currentThread());

        eventLoop.runNow(); // runs fine

        eventLoop.shutdownGracefully();