Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Introduce the ability to run test methods on the vertx context #17143

Closed
wants to merge 6 commits into from

Conversation

geoand
Copy link
Contributor

@geoand geoand commented May 11, 2021

Relates to: #15131

@geoand
Copy link
Contributor Author

geoand commented May 11, 2021

@cescoffier @FroMage @stuartwdouglas I opened this as a draft to give you an idea of what problem I am trying to solve.
I am not sure if it's a horrible idea or not, so before doing any more work on making this presentable, I wanted to get your opinion.

@geoand
Copy link
Contributor Author

geoand commented May 13, 2021

@stuartwdouglas @FroMage do you think this is worth pursuing or not?
Personally I am on the fence...

@stuartwdouglas
Copy link
Member

Is this something we are going to need more generally given the threading model of Hibernate Reactive? If so maybe it should be a CDI interceptor

@geoand
Copy link
Contributor Author

geoand commented May 13, 2021

Yeah, that's what prompted me to start this.

Users often inject the repository into tests in order to assert the state of the database, but currently things just fall over in that case.

You are proposing that we introduce a CDI interceptor that essentially does the same thing?
That wouldn't work for Panache Active Record style however

@stuartwdouglas
Copy link
Member

I mean you would apply the interceptor to the test, but it means you could use it in your application as well. If you have done an operation that leaves you off the IO thread this would allow you to dispatch back to it. Not really sure if this is useful or not though.

@geoand
Copy link
Contributor Author

geoand commented May 13, 2021

Ah, I see what you mean.

So implicitly switching threads when there is an issue like the one in question. Sounds interesting, but I don't know if being explicit or implicit is better in this case.

Let's see what others think as well

@FroMage
Copy link
Member

FroMage commented May 21, 2021

Huh, I'm interested in this, because I'm looking at io.quarkus.it.panache.reactive.PanacheMockingTest which passes when run from Maven, and fails when run from Eclipse with:

java.lang.AssertionError: This needs to be run on the Vert.x event loop
	at org.hibernate.reactive.session.impl.ReactiveSessionImpl.<init>(ReactiveSessionImpl.java:136)
	at org.hibernate.reactive.mutiny.impl.MutinySessionFactoryImpl.openSession(MutinySessionFactoryImpl.java:69)
	at io.quarkus.hibernate.reactive.runtime.ReactiveSessionProducer.createMutinySession(ReactiveSessionProducer.java:32)
	at io.quarkus.hibernate.reactive.runtime.ReactiveSessionProducer_ProducerMethod_createMutinySession_1321d110ee9e92bda147899150401e0a136779c7_Bean.create(ReactiveSessionProducer_ProducerMethod_createMutinySession_1321d110ee9e92bda147899150401e0a136779c7_Bean.zig:235)
	at io.quarkus.hibernate.reactive.runtime.ReactiveSessionProducer_ProducerMethod_createMutinySession_1321d110ee9e92bda147899150401e0a136779c7_Bean.create(ReactiveSessionProducer_ProducerMethod_createMutinySession_1321d110ee9e92bda147899150401e0a136779c7_Bean.zig:273)
	at io.quarkus.arc.impl.RequestContext.getIfActive(RequestContext.java:68)
	at io.quarkus.arc.impl.ClientProxies.getDelegate(ClientProxies.java:33)
	at io.quarkus.hibernate.reactive.runtime.ReactiveSessionProducer_ProducerMethod_createMutinySession_1321d110ee9e92bda147899150401e0a136779c7_ClientProxy.arc$delegate(ReactiveSessionProducer_ProducerMethod_createMutinySession_1321d110ee9e92bda147899150401e0a136779c7_ClientProxy.zig:60)
	at io.quarkus.hibernate.reactive.runtime.ReactiveSessionProducer_ProducerMethod_createMutinySession_1321d110ee9e92bda147899150401e0a136779c7_ClientProxy.createQuery(ReactiveSessionProducer_ProducerMethod_createMutinySession_1321d110ee9e92bda147899150401e0a136779c7_ClientProxy.zig:519)
	at io.quarkus.hibernate.reactive.panache.common.runtime.AbstractJpaOperations.count(AbstractJpaOperations.java:249)
	at io.quarkus.it.panache.reactive.Person.count(Person.java)
	at io.quarkus.it.panache.reactive.PanacheMockingTest.testPanacheMocking(PanacheMockingTest.java:37)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.base/java.lang.reflect.Method.invoke(Method.java:566)
	at io.quarkus.test.junit.QuarkusTestExtension.runExtensionMethod(QuarkusTestExtension.java:991)
	at io.quarkus.test.junit.QuarkusTestExtension.interceptTestMethod(QuarkusTestExtension.java:868)
	at org.junit.jupiter.engine.execution.ExecutableInvoker$ReflectiveInterceptorCall.lambda$ofVoidMethod$0(ExecutableInvoker.java:115)
	at org.junit.jupiter.engine.execution.ExecutableInvoker.lambda$invoke$0(ExecutableInvoker.java:105)
	at org.junit.jupiter.engine.execution.InvocationInterceptorChain$InterceptedInvocation.proceed(InvocationInterceptorChain.java:106)
	at org.junit.jupiter.engine.extension.TimeoutExtension.intercept(TimeoutExtension.java:149)
	at org.junit.jupiter.engine.extension.TimeoutExtension.interceptTestableMethod(TimeoutExtension.java:140)
	at org.junit.jupiter.engine.extension.TimeoutExtension.interceptTestMethod(TimeoutExtension.java:84)
	at org.junit.jupiter.engine.execution.ExecutableInvoker$ReflectiveInterceptorCall.lambda$ofVoidMethod$0(ExecutableInvoker.java:115)
	at org.junit.jupiter.engine.execution.ExecutableInvoker.lambda$invoke$0(ExecutableInvoker.java:105)
	at org.junit.jupiter.engine.execution.InvocationInterceptorChain$InterceptedInvocation.proceed(InvocationInterceptorChain.java:106)
	at org.junit.jupiter.engine.execution.InvocationInterceptorChain.proceed(InvocationInterceptorChain.java:64)
	at org.junit.jupiter.engine.execution.InvocationInterceptorChain.chainAndInvoke(InvocationInterceptorChain.java:45)
	at org.junit.jupiter.engine.execution.InvocationInterceptorChain.invoke(InvocationInterceptorChain.java:37)
	at org.junit.jupiter.engine.execution.ExecutableInvoker.invoke(ExecutableInvoker.java:104)
	at org.junit.jupiter.engine.execution.ExecutableInvoker.invoke(ExecutableInvoker.java:98)
	at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.lambda$invokeTestMethod$6(TestMethodTestDescriptor.java:210)
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.invokeTestMethod(TestMethodTestDescriptor.java:206)
	at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:131)
	at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:65)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$5(NodeTestTask.java:139)
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$7(NodeTestTask.java:129)
	at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:127)
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:126)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:84)
	at java.base/java.util.ArrayList.forEach(ArrayList.java:1540)
	at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:38)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$5(NodeTestTask.java:143)
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$7(NodeTestTask.java:129)
	at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:127)
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:126)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:84)
	at java.base/java.util.ArrayList.forEach(ArrayList.java:1540)
	at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:38)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$5(NodeTestTask.java:143)
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$7(NodeTestTask.java:129)
	at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:127)
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:126)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:84)
	at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.submit(SameThreadHierarchicalTestExecutorService.java:32)
	at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor.execute(HierarchicalTestExecutor.java:57)
	at org.junit.platform.engine.support.hierarchical.HierarchicalTestEngine.execute(HierarchicalTestEngine.java:51)
	at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:108)
	at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:88)
	at org.junit.platform.launcher.core.EngineExecutionOrchestrator.lambda$execute$0(EngineExecutionOrchestrator.java:54)
	at org.junit.platform.launcher.core.EngineExecutionOrchestrator.withInterceptedStreams(EngineExecutionOrchestrator.java:67)
	at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:52)
	at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:96)
	at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:84)
	at org.eclipse.jdt.internal.junit5.runner.JUnit5TestReference.run(JUnit5TestReference.java:98)
	at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:41)
	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:542)
	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:770)
	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:464)
	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:210)

The issue is that I don't understand why it runs from the CLI.

@FroMage
Copy link
Member

FroMage commented May 21, 2021

I suspect something is not respecting the -ea flag when run from the CLI.

@FroMage
Copy link
Member

FroMage commented May 21, 2021

Indeed, surefire runs the tests like this:

[DEBUG] Forking command line: /bin/sh -c cd /home/stephane/src/java-eclipse/quarkus/integration-tests/hibernate-reactive-panache && /usr/lib/jvm/java-11-openjdk-amd64/bin/java -Xmx1500m -XX:MaxMetaspaceSize=1500m -Djava.io.tmpdir=/home/stephane/src/java-eclipse/quarkus/integration-tests/hibernate-reactive-panache/target -jar /home/stephane/src/java-eclipse/quarkus/integration-tests/hibernate-reactive-panache/target/surefire/surefirebooter4584890161105973706.jar /home/stephane/src/java-eclipse/quarkus/integration-tests/hibernate-reactive-panache/target/surefire 2021-05-21T17-02-51_604-jvmRun1 surefire2510599203574278173tmp surefire_012132620703708764493tmp

Even though I explicitely enabled assertions.

@geoand
Copy link
Contributor Author

geoand commented May 21, 2021

@FroMage how do you pass JVM args to surefire?

@cescoffier
Copy link
Member

cescoffier commented May 22, 2021

For sure fire, assertions are enabled from the configuration:

<enableAssertions>true</enableAssertions>

@geoand
Copy link
Contributor Author

geoand commented May 22, 2021 via email

@FroMage
Copy link
Member

FroMage commented May 26, 2021

I like this idea a lot. But I wonder if it's enough.

The Vert.x JUnit extension has an extra injected param available to make it easy to test async stuff: https://vertx.io/docs/vertx-junit5/java/

ATM we mostly call uni.await().forever() (or whatever the thing is) in our uni tests, which sorta depends on the test method not being on the IO thread, because otherwise it'd block it. So if we start running tests on the IO thread (which I think we should, especially for HR), then we'll need to think about this issue. I don't think the Vert.x JUnit extension does that, and I do wonder why not.

@geoand
Copy link
Contributor Author

geoand commented May 26, 2021

So do you think we should allow returning a Uni from the test and wait and assert no error occurred ourselves?

@gwenneg
Copy link
Member

gwenneg commented Jun 6, 2021

I'm also very interested in this, not only because we have tests failure from IntelliJ with 1.13 but also because we have similar issues with tests run from Maven with 2.0. All failing tests involve Hibernate Reactive queries executions.

@geoand
Copy link
Contributor Author

geoand commented Jun 6, 2021

So should I clean this one up an take it out of draft?

@geoand
Copy link
Contributor Author

geoand commented Jun 7, 2021

@gwenneg do you have an example I can use to test?

@geoand
Copy link
Contributor Author

geoand commented Jun 7, 2021

ATM we mostly call uni.await().forever() (or whatever the thing is) in our uni tests, which sorta depends on the test method not being on the IO thread, because otherwise it'd block it. So if we start running tests on the IO thread (which I think we should, especially for HR), then we'll need to think about this issue. I don't think the Vert.x JUnit extension does that, and I do wonder why not.

I just now see what you mean (after actually trying an example). So yeah, we will need some kind of way to make it easy for users to write assertions without blocking

@geoand
Copy link
Contributor Author

geoand commented Jun 7, 2021

I am thinking that perhaps we allow passing some kind of asserter object as a method param.

A sample test would then be written something like this:

@QuarkusTest
public class SessionTest {

    @Inject
    Mutiny.Session session;

    @RunOnVertxContext
    @ActivateRequestContext // should we enable this by default?
    @Test
    public void test(UniAsserter asserter) {
        asserter.assertItem(allFruits(), f -> { // the API of the asserter obviously needs to be worked out
           assertFalse(f.isEmpty());
       })
    }

    private Uni<List<Fruit>> allFruits() {
        return session.createNamedQuery("Fruits.findAll", Fruit.class).getResultList();
    }
}

What do you folks think?

@gwenneg
Copy link
Member

gwenneg commented Jun 7, 2021

@gwenneg do you have an example I can use to test?

@geoand Are you interested in 1.13 failures or 2.0 or both?

@geoand
Copy link
Contributor Author

geoand commented Jun 7, 2021

@gwenneg do you have an example I can use to test?

@geoand Are you interested in 1.13 failures or 2.0 or both?

Are they different? If so, both :)

@geoand
Copy link
Contributor Author

geoand commented Jun 7, 2021

With the latest commit I added one can write:

@QuarkusTest
public class SessionTest {

    @Inject
    Mutiny.SessionFactory sessionFactory;

    @RunOnVertxContext
    @Test
    public void test1(UniResult<List<Fruit>> asserter) {
        asserter.assertItem(allFruits(), fruits -> assertThat(fruits).hasSize(3));
    }


    private Uni<List<Fruit>> allFruits() {
        return sessionFactory.openSession().createNamedQuery("Fruits.findAll", Fruit.class).getResultList();
    }
}

The implementation is far from pretty (due to the CL and Thread juggling), but the end result should be pretty simple for users.

WDYT?

@FroMage
Copy link
Member

FroMage commented Jun 7, 2021

That looks nice. Why do we need to specify the type of thing being asserted? What if we have more than one assertion on different things? Lemme give you a real-world example from HR/Panache IT:

        Assertions.assertEquals(0, mockablePersonRepository.count().await().indefinitely());

        Mockito.when(mockablePersonRepository.count()).thenReturn(Uni.createFrom().item(23l));
        Assertions.assertEquals(23, mockablePersonRepository.count().await().indefinitely());

        Mockito.when(mockablePersonRepository.count()).thenReturn(Uni.createFrom().item(42l));
        Assertions.assertEquals(42, mockablePersonRepository.count().await().indefinitely());

        Mockito.when(mockablePersonRepository.count()).thenCallRealMethod();
        Assertions.assertEquals(0, mockablePersonRepository.count().await().indefinitely());

        Mockito.verify(mockablePersonRepository, Mockito.times(4)).count();

Which uses the .await().indefinitely() pixie dust, but notice how it allows for set-up code to be called in between assertions.

And:

         createBug7102()
                .flatMap(person -> {
                    return getBug7102(person.id)
                            .flatMap(person1 -> {
                                Assertions.assertEquals("pero", person1.name);
                                return updateBug7102(person.id);
                            })
                            .flatMap(v -> getBug7102(person.id))
                            .map(person2 -> {
                                Assertions.assertEquals("jozo", person2.name);
                                return null;
                            });
                }).flatMap(v -> Person.deleteAll())
                .await().indefinitely();

Which chains everything, for one last await().indefinitely().

@geoand
Copy link
Contributor Author

geoand commented Jun 7, 2021

Good point! We definitely don't need to use the type, I just added for simplicity.

So if we agree on the approach, what do you think the API of the asserter should look like?

@FroMage
Copy link
Member

FroMage commented Jun 7, 2021

lemme try some pseudo-code and get back to you

@FroMage
Copy link
Member

FroMage commented Jun 7, 2021

With:

interface Asserter {
    <T> void assertThat(Supplier<Uni<T>> uni, Consumer<T> asserter);
    <T> void execute(Supplier<Uni<T>> uni);
    <T> void execute(Runnable c);
}

I could rewrite the first one with:

        ass.assertThat(() -> mockablePersonRepository.count(), count -> Assertions.assertEquals(0, count));

        ass.execute(() -> Mockito.when(mockablePersonRepository.count()).thenReturn(Uni.createFrom().item(23l)));
        ass.assertThat(() -> mockablePersonRepository.count(), count -> Assertions.assertEquals(23, count));

        ass.execute(() -> Mockito.when(mockablePersonRepository.count()).thenReturn(Uni.createFrom().item(42l)));
        ass.assertThat(() -> mockablePersonRepository.count(), count -> Assertions.assertEquals(42, count));

        ass.execute(() -> Mockito.when(mockablePersonRepository.count()).thenCallRealMethod());
        ass.assertThat(() -> mockablePersonRepository.count(), count -> Assertions.assertEquals(0, count));

        ass.execute(() -> Mockito.verify(mockablePersonRepository, Mockito.times(4)).count());

        Person p = new Person();
        ass.execute(() -> Mockito.when(mockablePersonRepository.findById(12l)).thenReturn(Uni.createFrom().item(p)));
        ass.assertThat(() -> mockablePersonRepository.findById(12l), p2 -> Assertions.assertSame(p, p2));
        ass.assertThat(() -> mockablePersonRepository.findById(42l), p2 -> Assertions.assertNull(p2));

        ass.execute(() -> mockablePersonRepository.persist(p));
        ass.execute(() -> {
            Assertions.assertNull(p.id);
            Mockito.when(mockablePersonRepository.findById(12l)).thenThrow(new WebApplicationException());
            try {
                mockablePersonRepository.findById(12l);
                Assertions.fail();
            } catch (WebApplicationException x) {
            }

            Mockito.when(mockablePersonRepository.findOrdered()).thenReturn(Uni.createFrom().item(Collections.emptyList()));
        });

        ass.assertThat(() -> mockablePersonRepository.findOrdered(), v -> Assertions.assertTrue(v.isEmpty()));

        ass.execute(() -> {
            Mockito.verify(mockablePersonRepository).findOrdered();
            Mockito.verify(mockablePersonRepository, Mockito.atLeastOnce()).findById(Mockito.any());
            Mockito.verify(mockablePersonRepository).persist(Mockito.<Person> any());
            Mockito.verifyNoMoreInteractions(mockablePersonRepository);
        });

Doesn't really impress me. Relies on calls to Asserter serialising the lambdas we pass it to chain them, too.

The second looks better:

        ass.assertThat(() -> createBug7102(), person -> {
            ass.assertThat(() -> getBug7102(person.id), person1 -> Assertions.assertEquals("pero", person1.name));
            ass.assertThat(() -> updateBug7102(person.id), person2 -> Assertions.assertEquals("jozo", person2.name));
            ass.execute(() -> Person.deleteAll());
        });

@geoand
Copy link
Contributor Author

geoand commented Jun 7, 2021

Thanks for the great examples! I'll have a look tomorrow

@FroMage
Copy link
Member

FroMage commented Jun 9, 2021

And I noticed that due to this exception:

java.lang.IllegalStateException: The current thread cannot be blocked: vert.x-eventloop-thread-31
	at io.smallrye.mutiny.operators.uni.UniBlockingAwait.await(UniBlockingAwait.java:30)
	at io.smallrye.mutiny.groups.UniAwait.atMost(UniAwait.java:61)
	at io.smallrye.mutiny.groups.UniAwait.indefinitely(UniAwait.java:42)
	at io.quarkus.it.panache.reactive.PanacheFunctionalityTest.testBug7102(PanacheFunctionalityTest.java:185)
	at io.quarkus.it.panache.reactive.PanacheFunctionalityTest.testBug7102InOneTransaction(PanacheFunctionalityTest.java:166)
…

Which essentially means that in this code that looks like a normal test code:

    @DisabledOnNativeImage
    @ReactiveTransactional
    @Test
    void testTransaction() {
        Transaction transaction = Panache.currentTransaction().await().indefinitely();
        Assertions.assertNotNull(transaction);
    }

I can't actually call await because I'm suddenly on the Vert.x event loop thread and not allowed to block it. I also can't make the test method return a Uni to make my test method properly async (the interceptor could then know how to deal with that) because JUnit refuses to run non-void methods for some reason (claims the test passed, but didn't invoke it).

So, not sure how to proceed. I guess this would work with Asserter, but then we have to force users to use it when doing test methods on the Vert.x event loop thread. I guess your PR must have the same issue, that tests can't actually use await() when running on the Vert.x context, right?

@FroMage
Copy link
Member

FroMage commented Jun 9, 2021

Seems junit-team/junit5#2304 confirms non-void methods are just ignored by JUnit ATM.

@Override
public void handle(Void event) {
ManagedContext requestContext = Arc.container().requestContext();
if (requestContext.isActive()) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isn't this fishy? AFAIK the request context is always active on the test methods, no? If so, it should just be propagated, no?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's actually needed. Without it I am getting ContextNotActiveException in the Panache tests

@geoand
Copy link
Contributor Author

geoand commented Jun 9, 2021 via email

@geoand
Copy link
Contributor Author

geoand commented Jun 9, 2021 via email

@geoand
Copy link
Contributor Author

geoand commented Jun 9, 2021

Here is a crazy thought:

Would it be possible to have an event loop thread that is reserved for tests and where blocking is allowed?
I realize that this is farfetched, but it would be elegant from a user perspective.
WDYT @FroMage, @cescoffier ?

@FroMage
Copy link
Member

FroMage commented Jun 10, 2021

I don't think this would work : the idea around the async drivers is that they have thread affinity with the IO thread that they sent their queries from. So they can't block. Otherwise we'd be able to even just use a Vert.x worker thread with affinity.

@geoand
Copy link
Contributor Author

geoand commented Jun 10, 2021

Yeah, I talked with @cescoffier as well and he will also write why he thinks it will hard to do that

@cescoffier
Copy link
Member

There are two problems:

  1. Uni.await() detects that you can block and will throw an exception if you are on an I/O thread. This could be changed - so that should not be an issue
  2. The second issue is more complex. Because of the context affinity of Vert.x, it may lead to a stale system (nothing can make progress). If you block the I/O thread on which a callback is intended to be called, that call can't happen. If that call was required to unblock the "await", you will literally wait indefinitely.

@geoand
Copy link
Contributor Author

geoand commented Jun 11, 2021

@FroMage how do you want to proceed with this, especially WRT #17728?

@FroMage
Copy link
Member

FroMage commented Jun 11, 2021

Well, IMO I can only use @ReactiveTransaction on tests if we also use Asserter, I don't see how else to make it work. So I'l need this to be merged before I can merge #17728. Which means I need to rebase my branch on this and test it properly.

@geoand
Copy link
Contributor Author

geoand commented Jun 11, 2021

👍🏼

So what is missing from this to get you started?

@geoand
Copy link
Contributor Author

geoand commented Jun 16, 2021

👍🏼

So what is missing from this to get you started?

@FroMage ^

@FroMage
Copy link
Member

FroMage commented Jun 17, 2021

I should hopefully be ready to make my PR work with your PR by tomorrow. I had to fix a few things in your branch to get it to work, now I just have to verify that I have enough tests.

@geoand
Copy link
Contributor Author

geoand commented Jun 17, 2021

That's awesome! Should I close this PR then and have everything incorporated in yours?

@geoand
Copy link
Contributor Author

geoand commented Jun 23, 2021

That's awesome! Should I close this PR then and have everything incorporated in yours?

@FroMage ^

@FroMage
Copy link
Member

FroMage commented Jun 23, 2021

Probably yes, I've added some fixes there for you to check.

@FroMage FroMage closed this Jun 23, 2021
@quarkus-bot quarkus-bot bot added the triage/invalid This doesn't seem right label Jun 23, 2021
@gwenneg
Copy link
Member

gwenneg commented Jul 17, 2021

Hey @geoand, thanks for your work on @RunOnVertxContext! I am trying to play with UniAsserter using 2.1.0.CR1 and I'm not sure how to run something equivalent to JUnit's assertThrows. I managed to migrate an old test that used assertThrows to something combining UniAsserter and UniAssertSubscriber but it's not very elegant so I wonder if I missed something obvious.

@geoand
Copy link
Contributor Author

geoand commented Jul 17, 2021

Good point!

I don't think I added something to emulate assertThrows...

Can you open an issue for that?

@geoand geoand deleted the vertx-test branch July 17, 2021 12:36
@gwenneg
Copy link
Member

gwenneg commented Jul 17, 2021

Sure.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area/dependencies Pull requests that update a dependency file area/testing triage/invalid This doesn't seem right
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants