mirror of
https://github.com/Microsoft/calculator.git
synced 2025-08-22 22:23:29 -07:00
Add a robust way to start/stop the WinAppDriverService
* Add robust way of starting and stopping service
This commit is contained in:
parent
ea64f354ce
commit
45db18242f
4 changed files with 297 additions and 14 deletions
|
@ -15,6 +15,10 @@ jobs:
|
|||
steps:
|
||||
- checkout: none
|
||||
|
||||
- powershell: Set-DisplayResolution -Width 1920 -Height 1080 -Force
|
||||
displayName: Set resolution to 1920x1080
|
||||
continueOnError: true
|
||||
|
||||
- task: DownloadBuildArtifacts@0
|
||||
displayName: Download AppxBundle and CalculatorUITests
|
||||
inputs:
|
||||
|
@ -35,11 +39,6 @@ jobs:
|
|||
filePath: $(Build.ArtifactStagingDirectory)/drop/Release/${{ parameters.platform }}/Calculator/AppPackages/Calculator_$(Build.BuildNumber)_Test/Add-AppDevPackage.ps1
|
||||
arguments: -Force
|
||||
|
||||
- task: WinAppDriver.winappdriver-pipelines-task.winappdriver-pipelines-task.Windows Application Driver@0
|
||||
displayName: 'WinAppDriver - Start'
|
||||
inputs:
|
||||
AgentResolution: 1080p
|
||||
|
||||
- task: VSTest@2
|
||||
displayName: Run CalculatorUITests
|
||||
inputs:
|
||||
|
@ -47,9 +46,4 @@ jobs:
|
|||
vsTestVersion: 16.0
|
||||
runSettingsFile: $(Build.ArtifactStagingDirectory)/drop/Release/${{ parameters.platform }}/publish/CalculatorUITests.runsettings
|
||||
platform: ${{ parameters.platform }}
|
||||
configuration: Release
|
||||
|
||||
- task: WinAppDriver.winappdriver-pipelines-task.winappdriver-pipelines-task.Windows Application Driver@0
|
||||
displayName: 'WinAppDriver - Stop'
|
||||
inputs:
|
||||
OperationType: Stop
|
||||
configuration: Release
|
|
@ -4,14 +4,14 @@ using OpenQA.Selenium.Appium;
|
|||
using OpenQA.Selenium.Appium.Windows;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Text;
|
||||
|
||||
namespace CalculatorUITestFramework
|
||||
{
|
||||
public sealed class WinAppDriver
|
||||
{
|
||||
// Note: append /wd/hub to the URL if you're directing the test at Appium
|
||||
private const string windowsApplicationDriverUrl = "http://127.0.0.1:4723";
|
||||
private WindowsDriverLocalService windowsDriverService = null;
|
||||
private const string calculatorAppId = "Microsoft.WindowsCalculator.Dev_8wekyb3d8bbwe!App";
|
||||
private static WinAppDriver instance = null;
|
||||
public static WinAppDriver Instance
|
||||
|
@ -36,6 +36,18 @@ namespace CalculatorUITestFramework
|
|||
|
||||
public void SetupCalculatorSession(TestContext context)
|
||||
{
|
||||
windowsDriverService = new WindowsDriverServiceBuilder().Build();
|
||||
|
||||
windowsDriverService.OutputDataReceived += new DataReceivedEventHandler((sender, e) =>
|
||||
{
|
||||
if (!String.IsNullOrEmpty(e.Data))
|
||||
{
|
||||
Console.WriteLine(e.Data);
|
||||
}
|
||||
});
|
||||
|
||||
windowsDriverService.Start();
|
||||
|
||||
// Launch Calculator application if it is not yet launched
|
||||
if (CalculatorSession == null)
|
||||
{
|
||||
|
@ -44,7 +56,7 @@ namespace CalculatorUITestFramework
|
|||
var options = new AppiumOptions();
|
||||
options.AddAdditionalCapability("app", calculatorAppId);
|
||||
options.AddAdditionalCapability("deviceName", "WindowsPC");
|
||||
CalculatorSession = new WindowsDriver<WindowsElement>(new Uri(windowsApplicationDriverUrl), options);
|
||||
CalculatorSession = new WindowsDriver<WindowsElement>(windowsDriverService.ServiceUrl, options);
|
||||
CalculatorSession.Manage().Timeouts().ImplicitWait = TimeSpan.FromSeconds(10);
|
||||
Assert.IsNotNull(CalculatorSession);
|
||||
}
|
||||
|
@ -58,6 +70,12 @@ namespace CalculatorUITestFramework
|
|||
CalculatorSession.Quit();
|
||||
CalculatorSession = null;
|
||||
}
|
||||
|
||||
if (windowsDriverService != null)
|
||||
{
|
||||
windowsDriverService.Dispose();
|
||||
windowsDriverService = null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
178
src/CalculatorUITestFramework/WindowsDriverLocalService.cs
Normal file
178
src/CalculatorUITestFramework/WindowsDriverLocalService.cs
Normal file
|
@ -0,0 +1,178 @@
|
|||
using OpenQA.Selenium.Appium.Service;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Net;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Text;
|
||||
|
||||
namespace CalculatorUITestFramework
|
||||
{
|
||||
public class WindowsDriverLocalService : IDisposable
|
||||
{
|
||||
private FileInfo FileName;
|
||||
private string Arguments;
|
||||
private IPAddress IP;
|
||||
private int Port;
|
||||
private TimeSpan InitializationTimeout;
|
||||
private Process Service;
|
||||
|
||||
public event DataReceivedEventHandler OutputDataReceived;
|
||||
|
||||
internal WindowsDriverLocalService(
|
||||
FileInfo fileName,
|
||||
string arguments,
|
||||
IPAddress ip,
|
||||
int port,
|
||||
TimeSpan initializationTimeout)
|
||||
{
|
||||
FileName = fileName;
|
||||
Arguments = arguments;
|
||||
IP = ip;
|
||||
Port = port;
|
||||
InitializationTimeout = initializationTimeout;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.Synchronized)]
|
||||
public void Start()
|
||||
{
|
||||
if (IsRunning)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Service = new Process();
|
||||
Service.StartInfo.FileName = FileName.FullName;
|
||||
Service.StartInfo.Arguments = Arguments;
|
||||
Service.StartInfo.UseShellExecute = false;
|
||||
Service.StartInfo.CreateNoWindow = true;
|
||||
|
||||
Service.StartInfo.RedirectStandardOutput = true;
|
||||
Service.OutputDataReceived += (sender, e) => OutputDataReceived?.Invoke(this, e);
|
||||
|
||||
bool isLaunched = false;
|
||||
string msgTxt =
|
||||
$"The local WinAppDriver server has not been started: {FileName.FullName} Arguments: {Arguments}. " +
|
||||
"\n";
|
||||
|
||||
try
|
||||
{
|
||||
Service.Start();
|
||||
|
||||
Service.BeginOutputReadLine();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
DestroyProcess();
|
||||
throw new Exception(msgTxt, e);
|
||||
}
|
||||
|
||||
isLaunched = Ping();
|
||||
if (!isLaunched)
|
||||
{
|
||||
DestroyProcess();
|
||||
throw new Exception(
|
||||
msgTxt +
|
||||
$"Time {InitializationTimeout.TotalMilliseconds} ms for the service starting has been expired!");
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsRunning
|
||||
{
|
||||
get
|
||||
{
|
||||
if (Service == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var pid = Service.Id;
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return Ping();
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
DestroyProcess();
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
public Uri ServiceUrl
|
||||
{
|
||||
// Note: append /wd/hub to the URL if you're directing the test at Appium
|
||||
get { return new Uri($"http://{IP.ToString()}:{Convert.ToString(Port)}"); }
|
||||
}
|
||||
|
||||
private void DestroyProcess()
|
||||
{
|
||||
if (Service == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
Service.Kill();
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
}
|
||||
finally
|
||||
{
|
||||
Service.Close();
|
||||
}
|
||||
}
|
||||
|
||||
private bool Ping()
|
||||
{
|
||||
bool pinged = false;
|
||||
|
||||
Uri status;
|
||||
|
||||
Uri service = ServiceUrl;
|
||||
if (service.IsLoopback)
|
||||
{
|
||||
status = new Uri("http://localhost:" + Convert.ToString(Port) + "/status");
|
||||
}
|
||||
else
|
||||
{
|
||||
status = new Uri(service.ToString() + "/status");
|
||||
}
|
||||
|
||||
DateTime endTime = DateTime.Now.Add(this.InitializationTimeout);
|
||||
while (!pinged & DateTime.Now < endTime)
|
||||
{
|
||||
HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(status);
|
||||
HttpWebResponse response = null;
|
||||
try
|
||||
{
|
||||
using (response = (HttpWebResponse)request.GetResponse())
|
||||
{
|
||||
pinged = true;
|
||||
}
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
pinged = false;
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (response != null)
|
||||
{
|
||||
response.Close();
|
||||
}
|
||||
}
|
||||
}
|
||||
return pinged;
|
||||
}
|
||||
}
|
||||
}
|
93
src/CalculatorUITestFramework/WindowsDriverServiceBuilder.cs
Normal file
93
src/CalculatorUITestFramework/WindowsDriverServiceBuilder.cs
Normal file
|
@ -0,0 +1,93 @@
|
|||
using OpenQA.Selenium;
|
||||
using OpenQA.Selenium.Appium.Service;
|
||||
using OpenQA.Selenium.Appium.Service.Options;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Net;
|
||||
using System.Net.Sockets;
|
||||
using System.Text;
|
||||
|
||||
namespace CalculatorUITestFramework
|
||||
{
|
||||
public class WindowsDriverServiceBuilder
|
||||
{
|
||||
private string IpAddress = "127.0.0.1";
|
||||
private int Port = 4723;
|
||||
private TimeSpan StartUpTimeout = new TimeSpan(0, 2, 0);
|
||||
private FileInfo FileInfo;
|
||||
|
||||
public WindowsDriverLocalService Build()
|
||||
{
|
||||
if (FileInfo == null)
|
||||
{
|
||||
FileInfo = new FileInfo(@"c:\Program Files (x86)\Windows Application Driver\winappdriver.exe");
|
||||
}
|
||||
return new WindowsDriverLocalService(FileInfo, string.Empty, IPAddress.Parse(this.IpAddress), this.Port, StartUpTimeout);
|
||||
}
|
||||
|
||||
public WindowsDriverServiceBuilder WithFileInfo(FileInfo fileInfo)
|
||||
{
|
||||
if (fileInfo == null)
|
||||
{
|
||||
throw new ArgumentNullException("FileInfo should not be NULL");
|
||||
}
|
||||
FileInfo = fileInfo;
|
||||
return this;
|
||||
}
|
||||
|
||||
public WindowsDriverServiceBuilder WithStartUpTimeOut(TimeSpan startUpTimeout)
|
||||
{
|
||||
if (startUpTimeout == null)
|
||||
{
|
||||
throw new ArgumentNullException("A startup timeout should not be NULL");
|
||||
}
|
||||
StartUpTimeout = startUpTimeout;
|
||||
return this;
|
||||
}
|
||||
|
||||
public WindowsDriverServiceBuilder WithIPAddress(string ipAddress)
|
||||
{
|
||||
IpAddress = ipAddress;
|
||||
return this;
|
||||
}
|
||||
|
||||
public WindowsDriverServiceBuilder UsingPort(int port)
|
||||
{
|
||||
if (port < 0)
|
||||
{
|
||||
throw new ArgumentException("The port parameter should not be negative");
|
||||
}
|
||||
|
||||
if (port == 0)
|
||||
{
|
||||
return UsingAnyFreePort();
|
||||
}
|
||||
|
||||
Port = port;
|
||||
return this;
|
||||
}
|
||||
|
||||
public WindowsDriverServiceBuilder UsingAnyFreePort()
|
||||
{
|
||||
Socket sock = null;
|
||||
|
||||
try
|
||||
{
|
||||
sock = new Socket(AddressFamily.InterNetwork,
|
||||
SocketType.Stream, ProtocolType.Tcp);
|
||||
sock.Bind(new IPEndPoint(IPAddress.Any, 0));
|
||||
Port = ((IPEndPoint)sock.LocalEndPoint).Port;
|
||||
return this;
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (sock != null)
|
||||
{
|
||||
sock.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue