diff --git a/src/main/java/com/zebrunner/carina/core/registrar/ownership/MethodOwner.java b/src/main/java/com/zebrunner/carina/core/registrar/ownership/MethodOwner.java index 0f2257bd3..9d6a634fe 100644 --- a/src/main/java/com/zebrunner/carina/core/registrar/ownership/MethodOwner.java +++ b/src/main/java/com/zebrunner/carina/core/registrar/ownership/MethodOwner.java @@ -22,13 +22,19 @@ import java.lang.annotation.Target; /** - * This defines the 'MethodOwner' annotation used to specify the - * TestNG methods owners. - * + * Allows to specify the owner of the test method/test class.
+ * The order of priorities when choosing the owner of the test method:
+ * 1. The annotation on a test method containing a suitable plaftorm.
+ * 2. The annotation on a test class containing a suitable platform.
+ * 3. The annotation on a test method without the specified platform.
+ * 4 The annotation on a test class withot the specified platform.
+ *
+ * + * The expected platform is determined based on the "capabilities.platformName" in the {@link com.zebrunner.carina.utils.R#CONFIG} (case-insensitive). */ @Repeatable(MethodOwner.List.class) @Retention(value = RetentionPolicy.RUNTIME) -@Target(value = ElementType.METHOD) +@Target(value = { ElementType.METHOD, ElementType.TYPE }) public @interface MethodOwner { String owner() default ""; diff --git a/src/main/java/com/zebrunner/carina/core/registrar/ownership/Ownership.java b/src/main/java/com/zebrunner/carina/core/registrar/ownership/Ownership.java index e7d838714..ed5dc3a9d 100644 --- a/src/main/java/com/zebrunner/carina/core/registrar/ownership/Ownership.java +++ b/src/main/java/com/zebrunner/carina/core/registrar/ownership/Ownership.java @@ -15,67 +15,111 @@ *******************************************************************************/ package com.zebrunner.carina.core.registrar.ownership; +import java.lang.invoke.MethodHandles; import java.lang.reflect.Method; +import java.util.Optional; + +import org.openqa.selenium.remote.CapabilityType; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; -import com.zebrunner.carina.utils.Configuration; import com.zebrunner.agent.core.registrar.maintainer.MaintainerResolver; -import org.apache.commons.lang3.StringUtils; +import com.zebrunner.carina.utils.R; public class Ownership implements MaintainerResolver { + // todo think about adding validation for the platform duplicates in method/class annotations + + private static final Logger LOGGER = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); + // todo move to AbstractCapabilities + private static final String CAPABILITIES_CONFIG_PREFIX = "capabilities."; @Override public String resolve(Class clazz, Method method) { - String owner = StringUtils.EMPTY; - // Get a handle to the class and method - // We can't use getMethod() because we may have parameterized tests - // for which we don't know the matching signature - String methodName = method.getName(); - Method testMethod = null; - Method[] possibleMethods = clazz.getMethods(); - for (Method possibleMethod : possibleMethods) { - if (possibleMethod.getName().equals(methodName)) { - testMethod = possibleMethod; - break; - } - } - - // do a scan for single Methodowner annotation as well) - if (testMethod != null && testMethod.isAnnotationPresent(MethodOwner.class)) { - MethodOwner methodAnnotation = testMethod.getAnnotation(MethodOwner.class); - owner = methodAnnotation.owner(); + // In order not to check for null further + if (method == null || clazz == null) { + return null; } - - // scan all MethodOwner annotations to find default ownership without any platform - if (testMethod != null && testMethod.isAnnotationPresent(MethodOwner.List.class)) { - MethodOwner.List methodAnnotation = testMethod.getAnnotation(MethodOwner.List.class); - for (MethodOwner methodOwner : methodAnnotation.value()) { - String actualPlatform = methodOwner.platform(); - if (actualPlatform.isEmpty()) { - owner = methodOwner.owner(); - break; - } + + String expectedPlatform = R.CONFIG.get(CAPABILITIES_CONFIG_PREFIX + CapabilityType.PLATFORM_NAME); + Optional possibleMethodOwner = getMethodOwner(method, expectedPlatform); + Optional possibleClassOwner = getClassOwner(clazz, expectedPlatform); + + // resolve platform-specific method owner + if (possibleMethodOwner.isPresent()) { + MethodOwner methodOwner = possibleMethodOwner.get(); + if (!methodOwner.platform().isEmpty()) { + return methodOwner.owner(); } } - - //do one more scan using platform ownership filter if any to override default owner value - if (testMethod != null && testMethod.isAnnotationPresent(MethodOwner.List.class)) { - MethodOwner.List methodAnnotation = testMethod.getAnnotation(MethodOwner.List.class); - for (MethodOwner methodOwner : methodAnnotation.value()) { - String actualPlatform = methodOwner.platform(); - String expectedPlatform = Configuration.getPlatform(); - - if (!actualPlatform.isEmpty() && isValidPlatform(actualPlatform, expectedPlatform)) { - owner = methodOwner.owner(); - } + // resolve platform-specific class owner + if (possibleClassOwner.isPresent()) { + MethodOwner classOwner = possibleClassOwner.get(); + if (!classOwner.platform().isEmpty()) { + return classOwner.owner(); } } - return owner; + String suitableAnyPlatformOwner = null; + // resolve all-other-platforms method/class owner + if (possibleMethodOwner.isPresent()) { + suitableAnyPlatformOwner = possibleMethodOwner.get().owner(); + } else if (possibleClassOwner.isPresent()) { + suitableAnyPlatformOwner = possibleClassOwner.get().owner(); + } + + return suitableAnyPlatformOwner; } - - private static boolean isValidPlatform(String actualPlatform, String expectedPlatform) { - return actualPlatform.equalsIgnoreCase(expectedPlatform); + + /** + * Get suitable method owner + * + * @param method test method + * @param expectedPlatform expected platform + * @return {@link Optional} of {@link MethodOwner} + */ + private static Optional getMethodOwner(Method method, String expectedPlatform) { + Optional suitableMethodOwner = Optional.empty(); + if (method.isAnnotationPresent(MethodOwner.class)) { + MethodOwner[] owners = new MethodOwner[1]; + owners[0] = method.getAnnotation(MethodOwner.class); + suitableMethodOwner = getOwner(owners, expectedPlatform); + } else if (method.isAnnotationPresent(MethodOwner.List.class)) { + suitableMethodOwner = getOwner(method.getAnnotation(MethodOwner.List.class).value(), expectedPlatform); + } + return suitableMethodOwner; + } + + /** + * Get suitable class owner + * + * @param clazz test class + * @param expectedPlatform expected platform + * @return {@link Optional} of {@link MethodOwner} + */ + private static Optional getClassOwner(Class clazz, String expectedPlatform) { + Optional suitableMethodOwner = Optional.empty(); + if (clazz.isAnnotationPresent(MethodOwner.class)) { + MethodOwner[] owners = new MethodOwner[1]; + owners[0] = clazz.getAnnotation(MethodOwner.class); + suitableMethodOwner = getOwner(owners, expectedPlatform); + } else if (clazz.isAnnotationPresent(MethodOwner.List.class)) { + suitableMethodOwner = getOwner(clazz.getAnnotation(MethodOwner.List.class).value(), expectedPlatform); + } + return suitableMethodOwner; } -} \ No newline at end of file + private static Optional getOwner(MethodOwner[] owners, String expectedPlatform) { + MethodOwner suitableOwner = null; + for (MethodOwner owner : owners) { + if (owner.platform().isEmpty()) { + suitableOwner = owner; + } else if (owner.platform().equalsIgnoreCase(expectedPlatform)) { + suitableOwner = owner; + // If an annotation was found suitable for a specific platform, there is no point in continuing to search further. + break; + } + } + return Optional.ofNullable(suitableOwner); + } +}