diff --git a/src/main/java/com/test/junit/TestRunner.java b/src/main/java/com/test/junit/TestRunner.java index f1ead61..807e879 100644 --- a/src/main/java/com/test/junit/TestRunner.java +++ b/src/main/java/com/test/junit/TestRunner.java @@ -4,7 +4,10 @@ import com.test.junit.anotation.AfterMethod; import com.test.junit.anotation.BeforeAll; import com.test.junit.anotation.BeforeMethod; +import com.test.junit.anotation.Description; import com.test.junit.anotation.Test; +import com.test.junit.anotation.TimeUnit; +import com.test.junit.anotation.Timeout; import com.test.junit.assertion.AssertionsRuntimeException; import java.lang.annotation.Annotation; @@ -14,6 +17,9 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeoutException; public class TestRunner { @@ -78,7 +84,8 @@ private static void invokeTestMethods(Object instance, List beforeEachMe try { method.setAccessible(true); invokeMethods(instance, beforeEachMethods); - method.invoke(instance); + printDescription(method); + invokeMethodTimed(instance, method); invokeMethods(instance, afterEachMethods); handleSunnyDayScenario(method); } catch (InvocationTargetException e) { @@ -86,6 +93,8 @@ private static void invokeTestMethods(Object instance, List beforeEachMe AssertionsRuntimeException ae = (AssertionsRuntimeException) e.getCause(); handleAssertionException(method, ae); } + } catch (TimeoutException e) { + handleTimedOutException(method, e.getMessage()); } catch (IllegalAccessException e) { throw new RuntimeException(e); } @@ -98,9 +107,53 @@ private static void handleAssertionException(Method method, AssertionsRuntimeExc System.out.println(ConsoleColors.RESET); } + private static void handleTimedOutException(Method method, String message) { + System.out.println(ConsoleColors.RED); + System.out.println(String.format("[Test method %s] is failed. Timed out. %s", method.getName(), message)); + System.out.println(ConsoleColors.RESET); + } + private static void handleSunnyDayScenario(Method method) { System.out.println(ConsoleColors.GREEN); System.out.println(String.format("[Test method %s] is successful", method.getName())); System.out.println(ConsoleColors.RESET); } + + private static void printDescription(Method method) { + if (method.isAnnotationPresent(Description.class)) { + Description description = method.getAnnotation(Description.class); + System.out.print(String.format("[Test method %s] detailed description: %s", method.getName(), description.message())); + } + } + + private static void invokeMethodTimed(Object instance, Method method) throws InvocationTargetException, IllegalAccessException, TimeoutException { + if (!method.isAnnotationPresent(Timeout.class)) { + method.invoke(instance); + } else { + long targetTime = method.getAnnotation(Timeout.class).time(); + TimeUnit timeUnit = method.getAnnotation(Timeout.class).timeUnit(); + ExecutorService executor = Executors.newSingleThreadExecutor(); + try { + executor.submit(() -> { + try { + method.invoke(instance); + } catch (IllegalAccessException | InvocationTargetException e) { + throw new RuntimeException(e); + } + }); + if (!executor.awaitTermination(targetTime, timeUnit.getTimeUnit())) { + executor.shutdownNow(); + throw new TimeoutException("Expected running time is " + targetTime + " " + timeUnit.name()); + } + } catch (InterruptedException e) { + throw new TimeoutException("Expected running time is " + targetTime + " " + timeUnit.name()); + } catch (RuntimeException e){ + if (e.getCause() instanceof InvocationTargetException){ + throw new InvocationTargetException(e); + } else { + throw new RuntimeException(e); + } + } + } + } } diff --git a/src/main/java/com/test/junit/anotation/TimeUnit.java b/src/main/java/com/test/junit/anotation/TimeUnit.java index 84f3f58..9dc8357 100644 --- a/src/main/java/com/test/junit/anotation/TimeUnit.java +++ b/src/main/java/com/test/junit/anotation/TimeUnit.java @@ -1,8 +1,17 @@ package com.test.junit.anotation; public enum TimeUnit { - MILLISECOND, - SECONDS, + MILLISECOND (java.util.concurrent.TimeUnit.MILLISECONDS), + SECONDS(java.util.concurrent.TimeUnit.SECONDS), - MINUTES + MINUTES(java.util.concurrent.TimeUnit.MINUTES); + + java.util.concurrent.TimeUnit timeUnit; + TimeUnit(java.util.concurrent.TimeUnit timeUnit) { + this.timeUnit=timeUnit; + } + + public java.util.concurrent.TimeUnit getTimeUnit() { + return timeUnit; + } } diff --git a/src/main/java/com/test/junit/anotation/Timeout.java b/src/main/java/com/test/junit/anotation/Timeout.java index 5fc0c6c..bec9893 100644 --- a/src/main/java/com/test/junit/anotation/Timeout.java +++ b/src/main/java/com/test/junit/anotation/Timeout.java @@ -1,5 +1,12 @@ package com.test.junit.anotation; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) public @interface Timeout { int time();