Выполнение параллельных сборок с тестами WatiN на одном сервере CruiseControl.NET - PullRequest
1 голос
/ 05 сентября 2011

У нас есть тесты с использованием WatiN, которые мы хотим запустить на нашем сервере CruiseControl.NET. У нас это работает с одной единственной сборкой. Когда мы включаем эти тесты в других сборках, они не выполняются одновременно. Мы не хотим помещать все сборки, выполняющие эти тесты, в одну и ту же очередь cc.net, потому что эти тесты - это лишь малая часть всего времени сборки. Мы также хотели бы избежать создания отдельных проектов сборки для этих тестов, потому что это буквально удвоило бы количество сборок в нашей настройке cc.net.

Какие у нас есть варианты?

  1. Есть ли способ поместить эти тесты в свою собственную задачу cc.net и поместить только эту задачу в очередь?
  2. Есть ли какая-либо задача msbuild / nant / ccnet или что-то еще, что обрабатывает очередь?
  3. Существует ли какой-либо инструмент командной строки, который мы можем запустить из нашего сценария msbuild, который обрабатывает очереди задач командной строки, чтобы мы могли запускать наши тесты с помощью вызовов командной строки nunit?
  4. Есть ли другие умные решения этой проблемы?

Если мы не найдем какого-либо существующего решения этой проблемы, мы, вероятно, что-то создадим сами, если да, какое решение будет рекомендовано?

EDIT : Это была моя последняя реализация мьютекса:

public class SystemLevelLock : IDisposable
{
    private readonly string _id;
    private bool _isAquired;
    private Mutex _mutex;

    public SystemLevelLock(string id)
    {
        _id = id;
        Aquire();
    }

    public SystemLevelLock() : this(GetApplicationId()) { }

    private void Aquire()
    {
        try
        {
            var mutex = GetMutex();
            _isAquired = mutex.WaitOne(TimeSpan.FromMinutes(1), false);
            if (!_isAquired)
                throw new Exception("System level mutex could not be aquired");
        }
        catch (AbandonedMutexException)
        {
            // Mutex was abandoned by another process (it probably crashed)
            // Mutex was aquired by this process instead
        }
    }

    private Mutex GetMutex() { return _mutex ?? (_mutex = MakeMutex()); }

    private Mutex MakeMutex()
    {
        var mutexId = string.Format("Global\\{{{0}}}", _id);
        var allowEveryoneRule = new MutexAccessRule(new SecurityIdentifier(WellKnownSidType.WorldSid, null), MutexRights.FullControl, AccessControlType.Allow);
        var securitySettings = new MutexSecurity();
        securitySettings.AddAccessRule(allowEveryoneRule);
        var mutex = new Mutex(false, mutexId);
        mutex.SetAccessControl(securitySettings);
        return mutex;
    }

    private void Release()
    {
        if (_isAquired)
            _mutex.ReleaseMutex();
    }

    public void Dispose() { Release(); }

}

И это реализация класса Browser, использующая мьютекс. При этом также используется сценарий сценария SpecFlow для хранения экземпляра браузера и блокировки для этого сценария.

public static class Browser
{

    public static IE Current
    {
        get
        {
            if (!IsStarted())
                Start();
            return ScenarioBrowser;
        }
    }

    private static IE ScenarioBrowser
    {
        get
        {
            if (ScenarioContext.Current.ContainsKey("browser"))
                return ScenarioContext.Current["browser"] as IE;
            return null;
        }
        set
        {
            if (value == null)
            {
                if (ScenarioContext.Current.ContainsKey("browser"))
                    ScenarioContext.Current.Remove("browser");
            }
            else
                ScenarioContext.Current["browser"] = value;
        }
    }

    private static IDisposable BrowserLock
    {
        get
        {
            if (ScenarioContext.Current.ContainsKey("browserLock"))
                return ScenarioContext.Current["browserLock"] as IDisposable;
            return null;
        }
        set
        {
            if (value == null)
            {
                if (ScenarioContext.Current.ContainsKey("browserLock"))
                    ScenarioContext.Current.Remove("browserLock");
            }
            else
                ScenarioContext.Current["browserLock"] = value;
        }
    }

    private static void LockBrowser()
    {
        BrowserLock = MakeBrowserLock();
    }

    private static void ReleaseBrowser()
    {
        BrowserLock.Dispose();
        BrowserLock = null;
    }

    private static SystemLevelLock MakeBrowserLock() { return new SystemLevelLock("WatiNBrowserLock"); }

    private static void Start()
    {
        LockBrowser();
        var browser = new IE();
        ScenarioBrowser = browser;
    }

    public static bool IsStarted() { return ScenarioBrowser != null; }

    public static void Close()
    {
        try
        {
            var browser = ScenarioBrowser;
            ScenarioBrowser = null;
            browser.Close();
            browser.Dispose();
        }
        finally
        {
            ReleaseBrowser();
        }
    }

}

1 Ответ

1 голос
/ 05 сентября 2011

Похоже, ваши тесты не изолированы.Вы должны убедиться, что тесты настроены и запущены и находятся в собственных изолированных средах.Зависят ли тесты от общих ресурсов?Тесты / Серверы / URL и т. Д.

Поскольку ваш IE является общим ресурсом (вы уверены, что это не сайт).возможно, вы могли бы приостановить процесс с помощью пользовательской задачи, которая ожидает на системном уровне с именем mutex.

РЕДАКТИРОВАТЬ: Это была моя (MatteS) окончательная реализация мьютекса:

public class SystemLevelLock : IDisposable
{
    private readonly string _id;
    private bool _isAquired;
    private Mutex _mutex;

    public SystemLevelLock(string id)
    {
        _id = id;
        Aquire();
    }

    public SystemLevelLock() : this(GetApplicationId()) { }

    private void Aquire()
    {
        try
        {
            var mutex = GetMutex();
            _isAquired = mutex.WaitOne(TimeSpan.FromMinutes(1), false);
            if (!_isAquired)
                throw new Exception("System level mutex could not be aquired");
        }
        catch (AbandonedMutexException)
        {
            // Mutex was abandoned by another process (it probably crashed)
            // Mutex was aquired by this process instead
        }
    }

    private Mutex GetMutex() { return _mutex ?? (_mutex = MakeMutex()); }

    private Mutex MakeMutex()
    {
        var mutexId = string.Format("Global\\{{{0}}}", _id);
        var allowEveryoneRule = new MutexAccessRule(new SecurityIdentifier(WellKnownSidType.WorldSid, null), MutexRights.FullControl, AccessControlType.Allow);
        var securitySettings = new MutexSecurity();
        securitySettings.AddAccessRule(allowEveryoneRule);
        var mutex = new Mutex(false, mutexId);
        mutex.SetAccessControl(securitySettings);
        return mutex;
    }

    private void Release()
    {
        if (_isAquired)
            _mutex.ReleaseMutex();
    }

    public void Dispose() { Release(); }

}

Иэто реализация класса Browser, использующая мьютекс.При этом также используется сценарий сценария SpecFlow для хранения экземпляра браузера и блокировки для этого сценария.

public static class Browser
{

    public static IE Current
    {
        get
        {
            if (!IsStarted())
                Start();
            return ScenarioBrowser;
        }
    }

    private static IE ScenarioBrowser
    {
        get
        {
            if (ScenarioContext.Current.ContainsKey("browser"))
                return ScenarioContext.Current["browser"] as IE;
            return null;
        }
        set
        {
            if (value == null)
            {
                if (ScenarioContext.Current.ContainsKey("browser"))
                    ScenarioContext.Current.Remove("browser");
            }
            else
                ScenarioContext.Current["browser"] = value;
        }
    }

    private static IDisposable BrowserLock
    {
        get
        {
            if (ScenarioContext.Current.ContainsKey("browserLock"))
                return ScenarioContext.Current["browserLock"] as IDisposable;
            return null;
        }
        set
        {
            if (value == null)
            {
                if (ScenarioContext.Current.ContainsKey("browserLock"))
                    ScenarioContext.Current.Remove("browserLock");
            }
            else
                ScenarioContext.Current["browserLock"] = value;
        }
    }

    private static void LockBrowser()
    {
        BrowserLock = MakeBrowserLock();
    }

    private static void ReleaseBrowser()
    {
        BrowserLock.Dispose();
        BrowserLock = null;
    }

    private static SystemLevelLock MakeBrowserLock() { return new SystemLevelLock("WatiNBrowserLock"); }

    private static void Start()
    {
        LockBrowser();
        var browser = new IE();
        ScenarioBrowser = browser;
    }

    public static bool IsStarted() { return ScenarioBrowser != null; }

    public static void Close()
    {
        try
        {
            var browser = ScenarioBrowser;
            ScenarioBrowser = null;
            browser.Close();
            browser.Dispose();
        }
        finally
        {
            ReleaseBrowser();
        }
    }

}
...