All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.eclipse.microprofile.fault.tolerance.tck.AsynchronousCSTest Maven / Gradle / Ivy

There is a newer version: 4.1
Show newest version
/*
 *******************************************************************************
 * Copyright (c) 2016-2018 Contributors to the Eclipse Foundation
 *
 * See the NOTICE file(s) distributed with this work for additional
 * information regarding copyright ownership.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * You may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *******************************************************************************/
package org.eclipse.microprofile.fault.tolerance.tck;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionException;
import java.util.concurrent.CompletionStage;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

import org.eclipse.microprofile.fault.tolerance.tck.asynchronous.AsyncClassLevelClient;
import org.eclipse.microprofile.fault.tolerance.tck.asynchronous.AsyncClient;
import org.eclipse.microprofile.fault.tolerance.tck.asynchronous.CompletableFutureHelper;
import org.eclipse.microprofile.fault.tolerance.tck.util.Connection;
import org.jboss.arquillian.container.test.api.Deployment;
import org.jboss.arquillian.testng.Arquillian;
import org.jboss.shrinkwrap.api.ShrinkWrap;
import org.jboss.shrinkwrap.api.asset.EmptyAsset;
import org.jboss.shrinkwrap.api.spec.JavaArchive;
import org.jboss.shrinkwrap.api.spec.WebArchive;
import org.testng.Assert;
import org.testng.Assert.ThrowingRunnable;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.Test;

import jakarta.inject.Inject;

/**
 * Verify the asynchronous invocation with CompletionStage
 *
 * @author Ondro Mihalyi
 */
public class AsynchronousCSTest extends Arquillian {

    private @Inject AsyncClient client;

    private @Inject AsyncClassLevelClient clientClass;

    private List> waitingFutures = new ArrayList<>();

    @Deployment
    public static WebArchive deploy() {
        JavaArchive testJar = ShrinkWrap
                .create(JavaArchive.class, "ftAsynchronous.jar")
                .addClasses(AsyncClient.class, AsyncClassLevelClient.class, Connection.class,
                        CompletableFutureHelper.class)
                .addAsManifestResource(EmptyAsset.INSTANCE, "beans.xml")
                .as(JavaArchive.class);

        WebArchive war = ShrinkWrap.create(WebArchive.class, "ftAsynchronous.war").addAsLibrary(testJar);
        return war;
    }

    /**
     * Test that the stage returned by calling an asynchronous method is not done if called right after the operation
     */
    @Test
    public void testAsyncIsNotFinished() {
        CompletableFuture waitingFuture = newWaitingFuture();

        CompletionStage resultFuture = client.serviceCS(waitingFuture);

        Assert.assertFalse(completesQuickly(resultFuture));

        complete(waitingFuture);

        try {
            waitUntilCompleted(resultFuture);
        } catch (CompletionException e) {
            handleCompletionException(e);
        }
    }

    /**
     * Test that the stage returned by calling an asynchronous method is done if called after waiting enough time to end
     * the operation
     */
    @Test
    public void testAsyncIsFinished() {
        CompletableFuture waitingFuture = newWaitingFuture();

        CompletionStage resultFuture = client.serviceCS(waitingFuture);
        complete(waitingFuture);

        Assert.assertTrue(completesQuickly(resultFuture));

        try {
            waitUntilCompleted(resultFuture);
        } catch (CompletionException e) {
            handleCompletionException(e);
        }
    }

    /**
     * Test that the stage returned by calling a method in an asynchronous class is not done if called right after the
     * operation
     */
    @Test
    public void testClassLevelAsyncIsNotFinished() {
        CompletableFuture waitingFuture = newWaitingFuture();

        CompletionStage resultFuture = clientClass.serviceCS(waitingFuture);

        Assert.assertFalse(completesQuickly(resultFuture));

        complete(waitingFuture);

        try {
            waitUntilCompleted(resultFuture);
        } catch (CompletionException e) {
            handleCompletionException(e);
        }
    }

    /**
     * Test that the stage returned by calling a method in an asynchronous class is done if called after waiting enough
     * time to end the operation
     */
    @Test
    public void testClassLevelAsyncIsFinished() {
        CompletableFuture waitingFuture = newWaitingFuture();

        CompletionStage resultFuture = clientClass.serviceCS(waitingFuture);
        complete(waitingFuture);

        Assert.assertTrue(completesQuickly(resultFuture));

        try {
            waitUntilCompleted(resultFuture);
        } catch (CompletionException e) {
            handleCompletionException(e);
        }
    }

    /**
     * Test that the callbacks added to the initial stage are executed after the stage returned by the asynchronous
     * method call is completed.
     * 
     * The callbacks added inside method invocation must be called first and then callbacks added to the result of the
     * call (on the calling thread) must be executed in the order they were added.
     */
    @Test
    public void testAsyncCallbacksChained() {
        CompletableFuture waitingFuture = newWaitingFuture();
        StringBuilder executionRecord = new StringBuilder();

        CompletableFuture innerFuture = new CompletableFuture();
        CompletableFuture returnedFuture = innerFuture
                .thenApply(v -> {
                    executionRecord.append("1");
                    return v;
                });
        CompletionStage resultFuture = client
                .serviceCS(waitingFuture, returnedFuture)
                .thenApply(v -> {
                    executionRecord.append("2");
                    return v;
                });
        complete(waitingFuture);
        resultFuture = resultFuture.thenApply(v -> {
            executionRecord.append("3");
            return v;
        });

        Assert.assertFalse(completesQuickly(resultFuture),
                "Stage returned by the method isn't completed yet so also the outer stage mustn't be completed");
        innerFuture.complete(new EmptyConnection());
        Assert.assertTrue(completesQuickly(resultFuture),
                "Stage returned by the method is completed so also the outer stage must be completed");
        Assert.assertEquals(executionRecord.toString(), "123",
                "The execution didn't happen in the expected order");

        try {
            waitUntilCompleted(resultFuture);
        } catch (CompletionException e) {
            handleCompletionException(e);
        }
    }

    /**
     * Test that the stage returned by calling an asynchronous method is completed exceptionally if the method throws an
     * exception
     */
    @Test
    public void testAsyncCompletesExceptionallyWhenExceptionThrown() {
        CompletableFuture waitingFuture = newWaitingFuture();

        CompletionStage resultFuture = client.serviceCS(waitingFuture, true);
        waitingFuture.completeExceptionally(new SimulatedException("completedExceptionally"));

        Assert.assertTrue(completesQuickly(resultFuture));
        Assert.assertTrue(isCompletedExceptionally(resultFuture));
        Assert.assertFalse(isCancelled(resultFuture));
        assertThrowsExecutionExceptionWithCause(SimulatedException.class,
                CompletableFutureHelper.toCompletableFuture(resultFuture)::get);
    }

    /**
     * Test that the stage returned by calling an asynchronous method is completed exceptionally if the method returns a
     * stage completed exceptionally
     */
    @Test
    public void testAsyncCompletesExceptionallyWhenCompletedExceptionally() {
        CompletableFuture waitingFuture = newWaitingFuture();

        CompletionStage resultFuture = client.serviceCS(waitingFuture, false);
        waitingFuture.completeExceptionally(new SimulatedException("completedExceptionally"));

        Assert.assertTrue(completesQuickly(resultFuture));
        Assert.assertTrue(isCompletedExceptionally(resultFuture));
        Assert.assertFalse(isCancelled(resultFuture));
        assertThrowsExecutionExceptionWithCause(SimulatedException.class,
                CompletableFutureHelper.toCompletableFuture(resultFuture)::get);
    }

    /**
     * Ensure that any waiting futures get completed at the end of each test
     * 

* Important in case tests end early due to an exception or failure. */ @AfterMethod public void completeWaitingFutures() { waitingFutures.forEach((future) -> { future.complete(null); }); waitingFutures.clear(); } /** * Use this method to obtain futures for passing to methods on {@link AsyncClient} *

* Using this factory method ensures they will be completed at the end of the test if your test fails. */ private CompletableFuture newWaitingFuture() { CompletableFuture result = new CompletableFuture<>(); waitingFutures.add(result); return result; } /** * A helper method to complete a waiting future with a more readable syntax */ private void complete(CompletableFuture future) { future.complete(null); } private void handleCompletionException(CompletionException e) throws AssertionError { throw new AssertionError("testAsync: unexpected Exception calling service: " + e.getCause().getMessage(), e); } private static Connection waitUntilCompleted(CompletionStage resultFuture) { return CompletableFutureHelper.toCompletableFuture(resultFuture).join(); } /** * Tests whether the given CompletionStage completes within 500ms *

* Used to avoid a race condition where a test wants to take some action which should cause the CompletionStage to * complete, but asynchronous execution means it may not happen immediately. */ private static boolean completesQuickly(CompletionStage resultFuture) { try { CompletableFutureHelper.toCompletableFuture(resultFuture).get(500, TimeUnit.MILLISECONDS); } catch (TimeoutException ex) { // Did not complete quickly return false; } catch (Exception ex) { // Completed with exception, fall through, we don't care about the result } return true; } private static boolean isCancelled(CompletionStage resultFuture) { return CompletableFutureHelper.toCompletableFuture(resultFuture).isCancelled(); } private static boolean isCompletedExceptionally(CompletionStage resultFuture) { return CompletableFutureHelper.toCompletableFuture(resultFuture).isCompletedExceptionally(); } private static void assertThrowsExecutionExceptionWithCause(Class causeClazz, ThrowingRunnable runnable) { try { runnable.run(); Assert.fail("ExecutionException not thrown"); } catch (ExecutionException ex) { Assert.assertTrue(causeClazz.isInstance(ex.getCause()), "Cause of ExecutionException was " + ex.getCause()); } catch (Throwable ex) { Assert.fail("Unexpected exception thrown", ex); } } private static class SimulatedException extends RuntimeException { public SimulatedException() { } public SimulatedException(String message) { super(message); } } private static class EmptyConnection implements Connection { @Override public String getData() { return null; } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy