Помогите с синхронизацией из юнит-теста в C # - PullRequest
2 голосов
/ 12 апреля 2011

Я тестирую класс, который оборачивает BackgroundWorker для выполнения операции вне потока пользовательского интерфейса в моем приложении.

Приведенный ниже тест не пройден, если превышен тайм-аут, и проходит, если progressEventCount достигает ожидаемого числа событий дозатем.

Мой вопрос касается синхронизации.asyncExecutor.Progressed запускается из потока пула потоков, который используется BackgroundWorker, и тестовый поток считывает его обратно в цикле while.

Правильно ли я использую блокировку?

    [Test]
    [Timeout(1250)]
    public void Execute()
    {
        var locker = new object();
        const int numberOfEvents = 10;
        const int frequencyOfEvents = 100;
        var start = DateTime.Now;
        int progressEventCount = 0;

        IGradualOperation tester = new TestGradualOperation(numberOfEvents, frequencyOfEvents);

        var asyncExecutor = new AsynchronousOperationExecutor();

        asyncExecutor.Progressed += (s, e) => { lock (locker) progressEventCount++; };

        asyncExecutor.Execute(tester);

        while (true)
        {
            int count;
            lock (locker)
            {
                count = progressEventCount;
            }
            if (count < numberOfEvents) continue;
            Assert.Pass("Succeeded after {0} milliseconds", (DateTime.Now - start).TotalMilliseconds);
        }
    }


//  Implementation
public class AsynchronousOperationExecutor
{
    public void Execute(IGradualOperation gradualOperation)
    {
        var backgroundWorker = new BackgroundWorker {WorkerReportsProgress = true};

        backgroundWorker.DoWork += BackgroundWorkerDoWork;
        backgroundWorker.ProgressChanged += BackgroundWorkerProgressChanged;
        backgroundWorker.RunWorkerAsync(gradualOperation);
    }

    private void BackgroundWorkerProgressChanged(object sender, ProgressChangedEventArgs e)
    {
        var myArgs = e.UserState as ProgressEventArgs;
        OnProgressed(myArgs);
    }

    static void BackgroundWorkerDoWork(object sender, DoWorkEventArgs e)
    {
        var workerThis = sender as BackgroundWorker;
        var operation = e.Argument as IGradualOperation;

        if (workerThis == null || operation == null) return;

        operation.Progressed += (s, e1) => workerThis.ReportProgress((int)e1.Percentage, e1);

        operation.Run();
    }

    private void OnProgressed(ProgressEventArgs e)
    {
        if (Progressed != null)
            Progressed(this, e);
    }

    public event EventHandler<ProgressEventArgs> Progressed;
}


//   Test Helper Class
public class TestGradualOperation : IGradualOperation
{
    private readonly int _numberOfEvents;
    private readonly int _frequencyMilliseconds;

    public TestGradualOperation(int numberOfEvents, int frequencyMilliseconds)
    {
        _numberOfEvents = numberOfEvents;
        _frequencyMilliseconds = frequencyMilliseconds;
    }

    public void Run()
    {
        for (int i = 0; i < _numberOfEvents; i++)
        {
            Thread.Sleep(_frequencyMilliseconds);
            OnProgressed(new ProgressEventArgs(i, _numberOfEvents));
        }
    }

    private void OnProgressed(ProgressEventArgs e)
    {
        if (Progressed != null)
            Progressed(this, e);            
    }

    public event EventHandler<ProgressEventArgs> Progressed;
}

1 Ответ

1 голос
/ 13 апреля 2011

Я думаю, что эта ревизия является улучшением, блокируя тестовый поток и сигнализируя с помощью AutoResetEvent. Не набрав ни одного очка брауни за читаемость теста.

    [Test]
    [Timeout(1250)]
    public void Execute()
    {
        var locker = new object();
        EventWaitHandle waitHandle = new AutoResetEvent(false);// <--
        const int numberOfEvents = 10;
        const int frequencyOfEvents = 100;
        var start = DateTime.Now;
        int progressEventCount = 0;

        IGradualOperation tester = new TestGradualOperation(numberOfEvents, frequencyOfEvents);

        var asyncExecutor = new AsynchronousOperationExecutor();

        asyncExecutor.Progressed += (s, e) =>
        {
            lock (locker)
            {
                progressEventCount++;
                waitHandle.Set();// <--
            }
        };

        asyncExecutor.Execute(tester);

        while (true)
        {
            waitHandle.WaitOne();// <--
            if (progressEventCount < numberOfEvents) continue;
            Assert.Pass("Succeeded after {0} milliseconds", (DateTime.Now - start).TotalMilliseconds);
        }
    }
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...