diff --git a/src/FlaUI.WebDriver.UITests/SessionTests.cs b/src/FlaUI.WebDriver.UITests/SessionTests.cs index f7f8cdd..2d5940d 100644 --- a/src/FlaUI.WebDriver.UITests/SessionTests.cs +++ b/src/FlaUI.WebDriver.UITests/SessionTests.cs @@ -129,6 +129,10 @@ public void NewSession_AppTopLevelWindow_IsSupported() var title = driver.Title; Assert.That(title, Is.EqualTo("FlaUI WPF Test App")); + + driver.Quit(); + + Assert.That(testAppProcess.Process.HasExited, Is.False); } [Test] @@ -166,6 +170,10 @@ public void NewSession_AppTopLevelWindowTitleMatch_IsSupported(string match) var title = driver.Title; Assert.That(title, Is.EqualTo("FlaUI WPF Test App")); + + driver.Quit(); + + Assert.That(testAppProcess.Process.HasExited, Is.False); } [Test, Ignore("Sometimes multiple processes are left open")] @@ -315,6 +323,35 @@ public void NewCommandTimeout_NotExpired_DoesNotEndSession() Assert.That(() => driver.Title, Throws.Nothing); } + [Test, Explicit(("Sometimes multiple processes are left open"))] + public void NewCommandTimeout_SessionWithAppTopLevelWindowTitleMatch_ClosesSessionButDoesNotCloseApp() + { + using var testAppProcess = new TestAppProcess(); + var driverOptions = FlaUIDriverOptions.AppTopLevelWindowTitleMatch("FlaUI WPF Test App"); + driverOptions.AddAdditionalOption("appium:newCommandTimeout", 1); + using var driver = new RemoteWebDriver(WebDriverFixture.WebDriverUrl, driverOptions); + + System.Threading.Thread.Sleep(TimeSpan.FromSeconds(1) + WebDriverFixture.SessionCleanupInterval * 2); + + Assert.That(testAppProcess.Process.HasExited, Is.False); + Assert.That(() => driver.Title, Throws.TypeOf().With.Message.Matches("No active session with ID '.*'")); + } + + [Test] + public void NewCommandTimeout_SessionWithAppTopLevelWindow_ClosesSessionButDoesNotCloseApp() + { + using var testAppProcess = new TestAppProcess(); + var windowHandle = string.Format("0x{0:x}", testAppProcess.Process.MainWindowHandle); + var driverOptions = FlaUIDriverOptions.AppTopLevelWindow(windowHandle); + driverOptions.AddAdditionalOption("appium:newCommandTimeout", 1); + using var driver = new RemoteWebDriver(WebDriverFixture.WebDriverUrl, driverOptions); + + System.Threading.Thread.Sleep(TimeSpan.FromSeconds(1) + WebDriverFixture.SessionCleanupInterval * 2); + + Assert.That(testAppProcess.Process.HasExited, Is.False); + Assert.That(() => driver.Title, Throws.TypeOf().With.Message.Matches("No active session with ID '.*'")); + } + [TestCase("123")] [TestCase(false)] [TestCase("not a number")] diff --git a/src/FlaUI.WebDriver/Controllers/SessionController.cs b/src/FlaUI.WebDriver/Controllers/SessionController.cs index 1b94db3..4759dda 100644 --- a/src/FlaUI.WebDriver/Controllers/SessionController.cs +++ b/src/FlaUI.WebDriver/Controllers/SessionController.cs @@ -30,6 +30,7 @@ public async Task CreateNewSession([FromBody] CreateSessionRequest .Select(capabillities => matchedCapabilities!); Core.Application? app; + var isAppOwnedBySession = false; var capabilities = matchingCapabilities.FirstOrDefault(); if (capabilities == null) { @@ -69,6 +70,8 @@ public async Task CreateNewSession([FromBody] CreateSessionRequest throw WebDriverResponseException.InvalidArgument($"Starting app '{appPath}' with arguments '{appArguments}' threw an exception: {e.Message}"); } } + + isAppOwnedBySession = true; } else if (TryGetStringCapability(capabilities, "appium:appTopLevelWindow", out var appTopLevelWindowString)) { @@ -84,7 +87,7 @@ public async Task CreateNewSession([FromBody] CreateSessionRequest { throw WebDriverResponseException.InvalidArgument("One of appium:app, appium:appTopLevelWindow or appium:appTopLevelWindowTitleMatch must be passed as a capability"); } - var session = new Session(app); + var session = new Session(app, isAppOwnedBySession); if(TryGetNumberCapability(capabilities, "appium:newCommandTimeout", out var newCommandTimeout)) { session.NewCommandTimeout = TimeSpan.FromSeconds(newCommandTimeout); diff --git a/src/FlaUI.WebDriver/Session.cs b/src/FlaUI.WebDriver/Session.cs index 75309b8..9991fd9 100644 --- a/src/FlaUI.WebDriver/Session.cs +++ b/src/FlaUI.WebDriver/Session.cs @@ -6,13 +6,14 @@ namespace FlaUI.WebDriver { public class Session : IDisposable { - public Session(Application? app) + public Session(Application? app, bool isAppOwnedBySession) { App = app; SessionId = Guid.NewGuid().ToString(); Automation = new UIA3Automation(); InputState = new InputState(); TimeoutsConfiguration = new TimeoutsConfiguration(); + IsAppOwnedBySession = isAppOwnedBySession; if (app != null) { @@ -30,6 +31,7 @@ public Session(Application? app) public TimeSpan ImplicitWaitTimeout => TimeSpan.FromMilliseconds(TimeoutsConfiguration.ImplicitWaitTimeoutMs); public TimeSpan PageLoadTimeout => TimeSpan.FromMilliseconds(TimeoutsConfiguration.PageLoadTimeoutMs); public TimeSpan? ScriptTimeout => TimeoutsConfiguration.ScriptTimeoutMs.HasValue ? TimeSpan.FromMilliseconds(TimeoutsConfiguration.ScriptTimeoutMs.Value) : null; + public bool IsAppOwnedBySession { get; } public TimeoutsConfiguration TimeoutsConfiguration { get; set; } @@ -124,7 +126,7 @@ public void RemoveKnownWindow(Window window) public void Dispose() { - if (App != null && !App.HasExited) + if (IsAppOwnedBySession && App != null && !App.HasExited) { App.Close(); }