/* * Copyright (C) 2013 Ustream Inc. * author chaotx <lombai.ferenc@ustream.tv> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ package com.robin.testcase; import java.lang.reflect.Method; import java.util.ArrayList; import org.testng.Assert; import com.robin.device.DevicePool; import com.robin.testcase.annotations.MultiDevice; import com.robin.testcase.annotations.Sequential; /** * Manages method starts when multi-device and sequential conditions must be * met. * * @author ChaotX */ public class TestExecutionManager { /** * Stores list of methods currently running. */ private static ArrayList<Method> runningMethods = new ArrayList<Method>(); /** * Suspend the actual thread that is going to run the next test method till * the conditions defined by runtime annotations \@MultiDevice and * \@Sequential are met for the method to start. * @param method the Method that is going to be run * @param deviceSelector device selector regexp for the method */ public static void registerMethod(final Method method, final String deviceSelector) { final int methodMaxDevice = getMethodMaxDeviceUsage(method); final boolean sequentialMethod = isMethodSequential(method); final boolean globallySequentialMethod = isMethodGloballySequential(method); synchronized (runningMethods) { while (true) { int matchingDeviceNum = DevicePool.getMatchingDeviceList(deviceSelector).size(); if (matchingDeviceNum >= methodMaxDevice && !isOtherMultiDeviceRunning() && (!sequentialMethod || !globallySequentialMethod && !isMethodAlreadyRunning(method) && !isOtherSequentialClassGroupRunning(method) || !isOtherSequentialMethodRunning())) { runningMethods.add(method); return; } else { Assert.assertTrue( methodMaxDevice <= matchingDeviceNum, "Method " + method.getDeclaringClass().getName() + "." + method.getName() + " need more device (" + methodMaxDevice + ") than available (" + matchingDeviceNum + ") for " + deviceSelector + " device selector expression!"); try { runningMethods.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } } } } private static boolean isOtherMultiDeviceRunning() { synchronized (runningMethods) { for (Method runningMethod : runningMethods) { if (getMethodMaxDeviceUsage(runningMethod) > 1) { return true; } } } return false; } /** * Removes the finisher method running from the runningMethods list and * notifies other threads to try re-register their methods. * @param method the Method object that is going to be removed from * currently running method list */ public static void unRegisterMethod(final Method method) { synchronized (runningMethods) { for (int i = 0; i < runningMethods.size(); i++) { if (runningMethods.get(i).equals(method)) { runningMethods.remove(i); runningMethods.notifyAll(); return; } } runningMethods.notifyAll(); } } private static boolean isMethodAlreadyRunning(final Method method) { synchronized (runningMethods) { for (Method runningMethod : runningMethods) { if (runningMethod.equals(method)) { return true; } } return false; } } private static boolean isOtherSequentialMethodRunning() { synchronized (runningMethods) { for (Method runningMethod : runningMethods) { if (isMethodSequential(runningMethod)) { return true; } } return false; } } private static boolean isOtherSequentialClassGroupRunning( final Method method) { synchronized (runningMethods) { if (isMethodSequential(method)) { Class<?>[] classGroups = method.getAnnotation(Sequential.class).groupClasses(); for (int i = 0; i < classGroups.length; i++) { for (Method runningMethod : runningMethods) { if (isMethodSequential(runningMethod)) { Class<?>[] runningGroups = method .getAnnotation(Sequential.class) .groupClasses(); for (int j = 0; j < runningGroups.length; j++) { if (runningGroups[j].equals(classGroups[i])) { return true; } } } } } } return false; } } private static int getMethodMaxDeviceUsage(final Method method) { MultiDevice annotation = method.getAnnotation(MultiDevice.class); if (annotation != null) { return annotation.maxDeviceUsed(); } return 1; } private static boolean isMethodSequential(final Method method) { return method.getAnnotation(Sequential.class) != null; } private static boolean isMethodGloballySequential(final Method method) { Sequential annotation = method.getAnnotation(Sequential.class); if (annotation != null) { return annotation.globally(); } return false; } }