sources/Google.Solutions.Testing.Application/Views/WindowTestFixtureBase.cs (137 lines of code) (raw):
//
// Copyright 2020 Google LLC
//
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
//
using Google.Solutions.IapDesktop.Application.Profile;
using Google.Solutions.IapDesktop.Application.Profile.Settings;
using Google.Solutions.IapDesktop.Application.Windows;
using Google.Solutions.IapDesktop.Application.Windows.Dialog;
using Google.Solutions.IapDesktop.Core.ObjectModel;
using Google.Solutions.IapDesktop.Core.ProjectModel;
using Google.Solutions.Settings.Collection;
using Google.Solutions.Testing.Apis.Platform;
using Google.Solutions.Testing.Application.Test;
using NUnit.Framework;
using System;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace Google.Solutions.Testing.Application.Views
{
[TestFixture]
[Apartment(ApartmentState.STA)]
[Timeout(10 * 60 * 1000)]
public class WindowTestFixtureBase : ApplicationFixtureBase
{
private MockExceptionDialog? exceptionDialog;
private ServiceRegistry? serviceRegistry;
private IServiceProvider? serviceProvider;
private IMainWindow? mainWindow;
private IEventQueue? eventService;
protected ServiceRegistry ServiceRegistry
{
get => this.serviceRegistry ?? throw new InvalidOperationException();
}
protected IServiceProvider ServiceProvider
{
get => this.serviceProvider ?? throw new InvalidOperationException();
}
protected IMainWindow MainWindow
{
get => this.mainWindow ?? throw new InvalidOperationException();
}
protected IEventQueue EventService
{
get => this.eventService ?? throw new InvalidOperationException();
}
protected Exception? ExceptionShown => this.exceptionDialog?.ExceptionShown;
private class MockExceptionDialog : IExceptionDialog
{
public Exception? ExceptionShown { get; private set; }
public void Show(IWin32Window? parent, string caption, Exception e)
{
this.ExceptionShown = e;
}
}
[SetUp]
public void SetUp()
{
var settingsKeyPath = RegistryKeyPath.ForCurrentTest();
var mainForm = new TestMainForm();
this.eventService = new EventQueue(mainForm);
var registry = new ServiceRegistry();
registry.AddSingleton<IProjectRepository>(new ProjectRepository(settingsKeyPath.CreateKey()));
registry.AddSingleton(new ToolWindowStateRepository(settingsKeyPath.CreateKey()));
registry.AddSingleton<IRepository<IApplicationSettings>>(new ApplicationSettingsRepository(
settingsKeyPath.CreateKey(),
null,
null,
UserProfile.SchemaVersion.Current));
registry.AddSingleton<IMainWindow>(mainForm);
registry.AddSingleton<IJobService>(mainForm);
registry.AddSingleton<ISessionBroker, SessionBroker>();
registry.AddSingleton<IEventQueue>(this.EventService);
this.exceptionDialog = new MockExceptionDialog();
registry.AddSingleton<IExceptionDialog>(this.exceptionDialog);
this.mainWindow = mainForm;
this.serviceRegistry = registry;
this.serviceProvider = registry;
mainForm.Show();
PumpWindowMessages();
}
[TearDown]
public void TearDown()
{
PumpWindowMessages();
this.MainWindow.Close();
}
protected static void PumpWindowMessages()
=> System.Windows.Forms.Application.DoEvents();
protected async Task<TEvent> AssertRaisesEventAsync<TEvent>(
Func<Task> action,
TimeSpan timeout) where TEvent : class
{
var deadline = DateTime.Now.Add(timeout);
//
// Set up event handler.
//
TEvent? deliveredEvent = null;
this.EventService.Subscribe<TEvent>(e =>
{
deliveredEvent = e;
});
//
// Invoke the action - it can either synchrounously
// or asynchronously deliver the event.
//
await action().ConfigureAwait(true);
//
// Wait for event in case it has not been delivered yet.
//
var lastLog = DateTime.Now;
for (var i = 0; deliveredEvent == null; i++)
{
if (deadline < DateTime.Now)
{
throw new TimeoutException(
$"Timeout waiting for event {typeof(TEvent).Name} elapsed");
}
//
// Print out a message once per second.
//
if (DateTime.Now.Subtract(lastLog).TotalSeconds >= 1)
{
Console.WriteLine($"Still waiting for {typeof(TEvent).Name} (until {deadline})");
lastLog = DateTime.Now;
}
//
// Let the SynchronizationContext pump.
//
await Task.Yield();
//
// Let Windows pump.
//
PumpWindowMessages();
}
return deliveredEvent;
}
protected Task<TEvent> AssertRaisesEventAsync<TEvent>(
Func<Task> action) where TEvent : class
=> AssertRaisesEventAsync<TEvent>(
action,
TimeSpan.FromSeconds(45));
protected Task<TEvent> AssertRaisesEventAsync<TEvent>(
Action action) where TEvent : class
=> AssertRaisesEventAsync<TEvent>(() =>
{
action();
return Task.CompletedTask;
});
protected static void Delay(TimeSpan timeout)
{
var deadline = DateTime.Now.Add(timeout);
while (DateTime.Now < deadline)
{
PumpWindowMessages();
}
}
}
}