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

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

There is a newer version: 4.1.1
Show newest version
/*
 *******************************************************************************
 * Copyright (c) 2016-2017 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 static org.eclipse.microprofile.fault.tolerance.tck.util.TCKConfig.getConfig;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.greaterThanOrEqualTo;
import static org.hamcrest.Matchers.lessThan;

import java.time.Duration;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

import org.eclipse.microprofile.fault.tolerance.tck.asynctimeout.clientserver.AsyncClassLevelTimeoutClient;
import org.eclipse.microprofile.fault.tolerance.tck.asynctimeout.clientserver.AsyncTimeoutClient;
import org.eclipse.microprofile.fault.tolerance.tck.config.ConfigAnnotationAsset;
import org.eclipse.microprofile.fault.tolerance.tck.util.Connection;
import org.eclipse.microprofile.faulttolerance.Timeout;
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.Asset;
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.annotations.Test;

import jakarta.inject.Inject;

/**
 * Test the combination of the @Asynchronous and @Timeout annotations.
 * 
 * @author Neil Young
 *
 */
public class AsyncTimeoutTest extends Arquillian {

    private @Inject AsyncTimeoutClient clientForAsyncTimeout;
    private @Inject AsyncClassLevelTimeoutClient clientForClassLevelAsyncTimeout;

    // Used to detect Futures that return too slowly
    private static final Duration TEST_FUTURE_THRESHOLD = getConfig().getTimeoutInDuration(2000);
    // The @Timeout specified on serviceA
    private static final Duration TEST_TIMEOUT_SERVICEA = getConfig().getTimeoutInDuration(2000);
    private static final Duration TEST_TIMEOUT_SERVICEB = getConfig().getTimeoutInDuration(2000);
    // One second unit
    private static final Duration TEST_TIME_UNIT = getConfig().getTimeoutInDuration(1000);

    @Deployment
    public static WebArchive deploy() {

        final Asset config = new ConfigAnnotationAsset()
                .setValue(AsyncTimeoutClient.class, "serviceA", Timeout.class,
                        String.valueOf(TEST_TIMEOUT_SERVICEA.toMillis()))
                .setValue(AsyncTimeoutClient.class, "serviceB", Timeout.class,
                        String.valueOf(TEST_TIMEOUT_SERVICEB.toMillis()))
                .setValue(AsyncClassLevelTimeoutClient.class, null, Timeout.class, getConfig().getTimeoutInStr(2000));

        JavaArchive testJar = ShrinkWrap
                .create(JavaArchive.class, "ftAsyncTimeout.jar")
                .addClasses(AsyncTimeoutClient.class, AsyncClassLevelTimeoutClient.class, Connection.class)
                .addAsManifestResource(config, "microprofile-config.properties")
                .addAsManifestResource(EmptyAsset.INSTANCE, "beans.xml")
                .as(JavaArchive.class);

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

    /**
     * Test that an Asynchronous Service times out as expected where the service is annotated with both
     * the @Asynchronous and @Timeout annotations.
     * 
     * A timeout is configured for serviceA but serviceA has a 5 second sleep so that, in this case, the service should
     * generate Timeout exceptions.
     */
    @Test
    public void testAsyncTimeout() {

        // Call serviceA. As it is annotated @Asynchronous, serviceA should return a future straight away even though
        // the method has a 5s sleep in it
        long start = System.nanoTime();

        Future future = null;
        try {
            future = clientForAsyncTimeout.serviceA();
        } catch (InterruptedException e1) {
            throw new AssertionError("testAsyncTimeout: unexpected InterruptedException calling serviceA");
        }

        long end = System.nanoTime();

        Duration duration = Duration.ofNanos(end - start);
        // should have returned almost instantly, if it takes TEST_FUTURE_THRESHOLD then there is something wrong
        assertThat("Method did not return quickly enough", duration, lessThan(TEST_FUTURE_THRESHOLD));

        // serviceA is slow (5 second sleep) but is configured with a 2 second Timeout. It should complete after 2
        // seconds
        // throwing a wrapped TimeoutException.

        // First check that the future hasn't completed prematurely
        if (future.isDone()) {
            throw new AssertionError("testAsyncTimeout: Future completed too fast");
        }

        // Call future.get() with a timeout (3 seconds) that is longer than the annotated timeout (2 seconds) specified
        // on
        // the service but shorter than the overall service duration (5 seconds sleep)
        try {
            future.get(TEST_TIMEOUT_SERVICEA.plus(TEST_TIME_UNIT).toMillis(), TimeUnit.MILLISECONDS);
            throw new AssertionError("testAsyncTimeout: Future not interrupted");
        } catch (ExecutionException e) {
            Assert.assertSame(e.getCause().getClass(),
                    org.eclipse.microprofile.faulttolerance.exceptions.TimeoutException.class,
                    "Should be a wrapped TimeoutException");
        } catch (InterruptedException e) {
            throw new AssertionError("testAsyncTimeout: unexpected InterruptedException on future.get()", e);
        } catch (TimeoutException e) {
            throw new AssertionError("testAsyncTimeout: unexpected TimeoutException on future.get()", e);
        }
        end = System.nanoTime();

        duration = Duration.ofNanos(end - start);
        // duration should be greater than the timeout configured on the service
        assertThat("the service duration was less than the configured timeout", duration,
                greaterThanOrEqualTo(TEST_TIMEOUT_SERVICEA));
    }

    /**
     * Test that an Asynchronous Service does not throw a TimeoutException where the service completes more quickly than
     * the specified time out. The service is annotated with both @Asynchronous and @Timeout.
     * 
     * A 2 second timeout is configured for serviceB but serviceB has a 0.5 second sleep so that, in this case, the
     * service should NOT generate Timeout exceptions.
     */
    @Test
    public void testAsyncNoTimeout() {
        // Call serviceB. As it is annotated @Asynchronous, serviceB should return a future straight away even though
        // the method has a 0.5s sleep in it
        long start = System.nanoTime();

        Future future = null;
        try {
            future = clientForAsyncTimeout.serviceB();
        } catch (InterruptedException e) {
            throw new AssertionError("testAsyncNoTimeout: unexpected InterruptedException calling serviceB");
        }
        long end = System.nanoTime();

        Duration duration = Duration.ofNanos(end - start);
        // should have returned almost instantly, if it takes TEST_FUTURE_THRESHOLD then there is something wrong
        assertThat("Method did not return quickly enough", duration, lessThan(TEST_FUTURE_THRESHOLD));

        // serviceB is fast and should return normally after 0.5 seconds but check for premature
        if (future.isDone()) {
            throw new AssertionError("testAsyncNoTimeout: Future completed too fast");
        }

        // The service should complete normally, there should be no FT TimeoutException
        try {
            Connection conn = future.get(TEST_TIME_UNIT.toMillis(), TimeUnit.MILLISECONDS);
        } catch (Exception t) {
            // Not Expected
            Assert.fail("serviceB should not throw an Exception in testAsyncNoTimeout");
        }
    }

    /**
     * Analogous to testAsyncTimeout but using Class level rather than method level annotations.
     * 
     * A timeout is configured for serviceA but serviceA has a 5 second sleep so that, in this case, the service should
     * generate Timeout exceptions.
     */
    @Test
    public void testAsyncClassLevelTimeout() {
        // Call serviceA. As it is annotated @Asynchronous, serviceA should return a future straight away even though
        // the method has a 5s sleep in it
        long start = System.nanoTime();

        Future future = null;
        try {
            future = clientForClassLevelAsyncTimeout.serviceA();
        } catch (InterruptedException e1) {
            throw new AssertionError("testAsyncClassLevelTimeout: unexpected InterruptedException calling serviceA");
        }
        long end = System.nanoTime();

        Duration duration = Duration.ofNanos(end - start);
        // should have returned almost instantly, if it takes TEST_FUTURE_THRESHOLD then there is something wrong
        assertThat("Method did not return quickly enough", duration, lessThan(TEST_FUTURE_THRESHOLD));

        // serviceA is slow (5 second sleep) but is configured with a 2 second Timeout. It should complete after 2
        // seconds
        // throwing a wrapped TimeoutException.

        // First check that the future hasn't completed prematurely
        if (future.isDone()) {
            throw new AssertionError("testAsyncClassLevelTimeout: Future completed too fast");
        }

        // Call future.get() with a timeout (3 seconds) that is longer than the annotated timeout (2 seconds) specified
        // on
        // the service but shorter than the overall service duration (5 seconds sleep)
        try {
            future.get(TEST_TIMEOUT_SERVICEA.plus(TEST_TIME_UNIT).toMillis(), TimeUnit.MILLISECONDS);
            throw new AssertionError("testAsyncClassLevelTimeout: Future not interrupted");
        } catch (ExecutionException e) {
            Assert.assertSame(e.getCause().getClass(),
                    org.eclipse.microprofile.faulttolerance.exceptions.TimeoutException.class,
                    "Should be a wrapped TimeoutException");
        } catch (InterruptedException e) {
            throw new AssertionError("testAsyncClassLevelTimeout: unexpected InterruptedException on future.get()");
        } catch (TimeoutException e) {
            throw new AssertionError("testAsyncClassLevelTimeout: unexpected TimeoutException on future.get()");
        }
        end = System.nanoTime();

        duration = Duration.ofNanos(end - start);
        // duration should be greater than the timeout configured on the service
        assertThat("the service duration was less than the configured timeout", duration,
                greaterThanOrEqualTo(TEST_TIMEOUT_SERVICEA));
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy