From f07421897b5521157915e21a7d5483e48bcd1932 Mon Sep 17 00:00:00 2001 From: Sven Jost Date: Wed, 19 Apr 2017 16:06:41 +0200 Subject: [PATCH 01/12] Support for selenium 3.3 --- .../GetSessionCapabilitiesExecutor.cs | 20 +++++++++++++++++++ .../CommandExecutors/SetTimeoutExecutor.cs | 20 +++++++++++++++++++ .../Winium.Desktop.Driver.csproj | 4 +++- src/Winium.Desktop.Driver/packages.config | 2 +- 4 files changed, 44 insertions(+), 2 deletions(-) create mode 100644 src/Winium.Desktop.Driver/CommandExecutors/GetSessionCapabilitiesExecutor.cs create mode 100644 src/Winium.Desktop.Driver/CommandExecutors/SetTimeoutExecutor.cs diff --git a/src/Winium.Desktop.Driver/CommandExecutors/GetSessionCapabilitiesExecutor.cs b/src/Winium.Desktop.Driver/CommandExecutors/GetSessionCapabilitiesExecutor.cs new file mode 100644 index 0000000..09a7756 --- /dev/null +++ b/src/Winium.Desktop.Driver/CommandExecutors/GetSessionCapabilitiesExecutor.cs @@ -0,0 +1,20 @@ +namespace Winium.Desktop.Driver.CommandExecutors +{ + #region using + + using Winium.StoreApps.Common; + + #endregion + + internal class GetSessionCapabilitiesExecutor : CommandExecutorBase + { + #region Methods + + protected override string DoImpl() + { + return this.JsonResponse(ResponseStatus.Success, this.Automator.ActualCapabilities); + } + + #endregion + } +} diff --git a/src/Winium.Desktop.Driver/CommandExecutors/SetTimeoutExecutor.cs b/src/Winium.Desktop.Driver/CommandExecutors/SetTimeoutExecutor.cs new file mode 100644 index 0000000..cf6209e --- /dev/null +++ b/src/Winium.Desktop.Driver/CommandExecutors/SetTimeoutExecutor.cs @@ -0,0 +1,20 @@ +namespace Winium.Desktop.Driver.CommandExecutors +{ + #region using + + using Winium.StoreApps.Common; + + #endregion + + internal class SetTimeoutExecutor : CommandExecutorBase + { + #region Methods + + protected override string DoImpl() + { + return this.JsonResponse(ResponseStatus.Success, null); + } + + #endregion + } +} \ No newline at end of file diff --git a/src/Winium.Desktop.Driver/Winium.Desktop.Driver.csproj b/src/Winium.Desktop.Driver/Winium.Desktop.Driver.csproj index 1ad8823..9238a25 100644 --- a/src/Winium.Desktop.Driver/Winium.Desktop.Driver.csproj +++ b/src/Winium.Desktop.Driver/Winium.Desktop.Driver.csproj @@ -72,6 +72,7 @@ + @@ -85,6 +86,7 @@ + @@ -163,4 +165,4 @@ - \ No newline at end of file + diff --git a/src/Winium.Desktop.Driver/packages.config b/src/Winium.Desktop.Driver/packages.config index 0486d04..9b60155 100644 --- a/src/Winium.Desktop.Driver/packages.config +++ b/src/Winium.Desktop.Driver/packages.config @@ -5,6 +5,6 @@ - + \ No newline at end of file From 164385104d47759dfe31fd713b239ad1b0e42ebd Mon Sep 17 00:00:00 2001 From: Sven Jost Date: Wed, 19 Apr 2017 17:25:10 +0200 Subject: [PATCH 02/12] Update selenium version in tests --- .../WindowsFormsTestApplication.Tests/packages.config | 4 ++-- src/TestApps.Tests/WpfTestApplication.Tests/packages.config | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/TestApps.Tests/WindowsFormsTestApplication.Tests/packages.config b/src/TestApps.Tests/WindowsFormsTestApplication.Tests/packages.config index c5c2577..7518043 100644 --- a/src/TestApps.Tests/WindowsFormsTestApplication.Tests/packages.config +++ b/src/TestApps.Tests/WindowsFormsTestApplication.Tests/packages.config @@ -1,5 +1,5 @@  - - \ No newline at end of file + + diff --git a/src/TestApps.Tests/WpfTestApplication.Tests/packages.config b/src/TestApps.Tests/WpfTestApplication.Tests/packages.config index c5c2577..7518043 100644 --- a/src/TestApps.Tests/WpfTestApplication.Tests/packages.config +++ b/src/TestApps.Tests/WpfTestApplication.Tests/packages.config @@ -1,5 +1,5 @@  - - \ No newline at end of file + + From 43f0bdc5f3c639e09d178c7eb8c0f3d2003b46bd Mon Sep 17 00:00:00 2001 From: Sven Jost Date: Wed, 19 Apr 2017 17:37:12 +0200 Subject: [PATCH 03/12] Update Winium.Desktop.Driver.csproj --- src/Winium.Desktop.Driver/Winium.Desktop.Driver.csproj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Winium.Desktop.Driver/Winium.Desktop.Driver.csproj b/src/Winium.Desktop.Driver/Winium.Desktop.Driver.csproj index 9238a25..b70cae3 100644 --- a/src/Winium.Desktop.Driver/Winium.Desktop.Driver.csproj +++ b/src/Winium.Desktop.Driver/Winium.Desktop.Driver.csproj @@ -52,8 +52,8 @@ - - ..\packages\Selenium.WebDriver.2.53.1\lib\net40\WebDriver.dll + + ..\packages\Selenium.WebDriver.3.3.0\lib\net40\WebDriver.dll True From 1f7f7e979bef05c904223620245e4b5ccffaeba2 Mon Sep 17 00:00:00 2001 From: Sven Jost Date: Wed, 19 Apr 2017 17:40:32 +0200 Subject: [PATCH 04/12] Fix include reference versions for tests --- .../WindowsFormsTestApplication.Tests.csproj | 6 +++--- .../WpfTestApplication.Tests.csproj | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/TestApps.Tests/WindowsFormsTestApplication.Tests/WindowsFormsTestApplication.Tests.csproj b/src/TestApps.Tests/WindowsFormsTestApplication.Tests/WindowsFormsTestApplication.Tests.csproj index c709295..76d2c9e 100644 --- a/src/TestApps.Tests/WindowsFormsTestApplication.Tests/WindowsFormsTestApplication.Tests.csproj +++ b/src/TestApps.Tests/WindowsFormsTestApplication.Tests/WindowsFormsTestApplication.Tests.csproj @@ -41,8 +41,8 @@ - - ..\..\packages\Selenium.WebDriver.2.53.1\lib\net40\WebDriver.dll + + ..\..\packages\Selenium.WebDriver.3.3.0\lib\net40\WebDriver.dll True @@ -92,4 +92,4 @@ --> - \ No newline at end of file + diff --git a/src/TestApps.Tests/WpfTestApplication.Tests/WpfTestApplication.Tests.csproj b/src/TestApps.Tests/WpfTestApplication.Tests/WpfTestApplication.Tests.csproj index a7ee778..9d48552 100644 --- a/src/TestApps.Tests/WpfTestApplication.Tests/WpfTestApplication.Tests.csproj +++ b/src/TestApps.Tests/WpfTestApplication.Tests/WpfTestApplication.Tests.csproj @@ -40,8 +40,8 @@ - - ..\..\packages\Selenium.WebDriver.2.53.1\lib\net40\WebDriver.dll + + ..\..\packages\Selenium.WebDriver.3.3.0\lib\net40\WebDriver.dll True @@ -96,4 +96,4 @@ --> - \ No newline at end of file + From 2b8a6de002e1cd3b7d259ffaf9dca23b22dfc38f Mon Sep 17 00:00:00 2001 From: Sven Jost Date: Fri, 8 Sep 2017 15:44:10 +0200 Subject: [PATCH 05/12] Return unique id for each session --- src/Winium.Desktop.Driver/Automator/Automator.cs | 12 +++--------- .../CommandExecutors/NewSessionExecutor.cs | 3 +++ 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/src/Winium.Desktop.Driver/Automator/Automator.cs b/src/Winium.Desktop.Driver/Automator/Automator.cs index 88f8d25..03371cc 100644 --- a/src/Winium.Desktop.Driver/Automator/Automator.cs +++ b/src/Winium.Desktop.Driver/Automator/Automator.cs @@ -21,9 +21,8 @@ internal class Automator #region Constructors and Destructors - public Automator(string session) + public Automator() { - this.Session = session; this.ElementsRegistry = new ElementsRegistry(); } @@ -37,7 +36,7 @@ public Automator(string session) public ElementsRegistry ElementsRegistry { get; private set; } - public string Session { get; private set; } + public string Session { get; set; } public WiniumKeyboard WiniumKeyboard { get; set; } @@ -61,13 +60,8 @@ public static Automator InstanceForSession(string sessionId) { if (instance == null) { - if (sessionId == null) - { - sessionId = "AwesomeSession"; - } - // TODO: Add actual support for sessions. Temporary return single Automator for any season - instance = new Automator(sessionId); + instance = new Automator(); } } } diff --git a/src/Winium.Desktop.Driver/CommandExecutors/NewSessionExecutor.cs b/src/Winium.Desktop.Driver/CommandExecutors/NewSessionExecutor.cs index e9b448a..d53946f 100644 --- a/src/Winium.Desktop.Driver/CommandExecutors/NewSessionExecutor.cs +++ b/src/Winium.Desktop.Driver/CommandExecutors/NewSessionExecutor.cs @@ -2,6 +2,7 @@ { #region using + using System; using System.Threading; using Newtonsoft.Json; @@ -20,6 +21,8 @@ internal class NewSessionExecutor : CommandExecutorBase protected override string DoImpl() { + this.Automator.Session = Guid.NewGuid().ToString(); + // It is easier to reparse desired capabilities as JSON instead of re-mapping keys to attributes and calling type conversions, // so we will take possible one time performance hit by serializing Dictionary and deserializing it as Capabilities object var serializedCapability = From b6fd595449e6616b9acb508f1851d46527366b88 Mon Sep 17 00:00:00 2001 From: Sven Jost Date: Fri, 8 Sep 2017 15:47:00 +0200 Subject: [PATCH 06/12] Implement NodeRegistrar to register on selenium grid --- .../Automator/Capabilities.cs | 9 ++ .../CommandLineOptions.cs | 10 ++- src/Winium.Desktop.Driver/Listener.cs | 87 +++++++++++-------- src/Winium.Desktop.Driver/Program.cs | 23 ++--- .../Winium.Desktop.Driver.csproj | 2 + 5 files changed, 84 insertions(+), 47 deletions(-) diff --git a/src/Winium.Desktop.Driver/Automator/Capabilities.cs b/src/Winium.Desktop.Driver/Automator/Capabilities.cs index 8a6b2d7..3ee52c7 100644 --- a/src/Winium.Desktop.Driver/Automator/Capabilities.cs +++ b/src/Winium.Desktop.Driver/Automator/Capabilities.cs @@ -27,6 +27,15 @@ internal Capabilities() #region Public Properties + [JsonProperty("platformName")] + public static string PlatformName + { + get + { + return "Windows"; + } + } + [JsonProperty("app")] public string App { get; set; } diff --git a/src/Winium.Desktop.Driver/CommandLineOptions.cs b/src/Winium.Desktop.Driver/CommandLineOptions.cs index 666ae9d..840db8c 100644 --- a/src/Winium.Desktop.Driver/CommandLineOptions.cs +++ b/src/Winium.Desktop.Driver/CommandLineOptions.cs @@ -15,8 +15,8 @@ internal class CommandLineOptions HelpText = "write server log to file instead of stdout, increases log level to INFO")] public string LogPath { get; set; } - [Option("port", Required = false, HelpText = "port to listen on")] - public int? Port { get; set; } + [Option("port", Required = false, DefaultValue = 9999, HelpText = "port to listen on")] + public int Port { get; set; } [Option("url-base", Required = false, HelpText = "base URL path prefix for commands, e.g. wd/url")] public string UrlBase { get; set; } @@ -24,6 +24,12 @@ internal class CommandLineOptions [Option("verbose", Required = false, HelpText = "log verbosely")] public bool Verbose { get; set; } + [Option("version", Required = false, HelpText = "print version number and exit")] + public bool Version { get; set; } + + [Option("nodeconfig", Required = false, HelpText = "configuration JSON file to register driver with selenium grid")] + public string NodeConfig { get; set; } + [Option("silent", Required = false, HelpText = "log nothing")] public bool Silent { get; set; } diff --git a/src/Winium.Desktop.Driver/Listener.cs b/src/Winium.Desktop.Driver/Listener.cs index c628dfb..561607e 100644 --- a/src/Winium.Desktop.Driver/Listener.cs +++ b/src/Winium.Desktop.Driver/Listener.cs @@ -16,7 +16,9 @@ public class Listener { #region Static Fields - private static string urnPrefix; + private readonly Uri baseAddress; + + private readonly NodeRegistrar nodeRegistrar; #endregion @@ -32,35 +34,33 @@ public class Listener #region Constructors and Destructors - public Listener(int listenerPort) + public Listener(int listenerPort, string urlBase, string nodeConfigFile) { + urlBase = NormalizePrefix(urlBase); this.Port = listenerPort; - } - - #endregion - - #region Public Properties - - public static string UrnPrefix - { - get - { - return urnPrefix; - } - set + if (!string.IsNullOrWhiteSpace(nodeConfigFile)) { - if (!string.IsNullOrEmpty(value)) + if (!urlBase.Equals("wd/hub")) { - // Normalize prefix - urnPrefix = "/" + value.Trim('/'); + Logger.Warn( + "--url-base '{0}' will be overriden and set to 'wd/hub' because --nodeconfig option was specified", + urlBase); } + + urlBase = "wd/hub"; + + this.nodeRegistrar = new NodeRegistrar(nodeConfigFile, "localhost", this.Port); } + + this.baseAddress = new UriBuilder("http", "localhost", this.Port, urlBase).Uri; } - public int Port { get; private set; } + #endregion + + #region Public Properties - public Uri Prefix { get; private set; } + public int Port { get; private set; } #endregion @@ -71,13 +71,17 @@ public void StartListening() try { this.listener = new TcpListener(IPAddress.Any, this.Port); - - this.Prefix = new Uri(string.Format(CultureInfo.InvariantCulture, "http://localhost:{0}", this.Port)); - this.dispatcher = new UriDispatchTables(new Uri(this.Prefix, UrnPrefix)); + this.dispatcher = new UriDispatchTables(this.baseAddress); this.executorDispatcher = new CommandExecutorDispatchTable(); // Start listening for client requests. this.listener.Start(); + Logger.Info("RemoteWebDriver instances should connect to: {0}", this.baseAddress); + + if (this.nodeRegistrar != null) + { + this.nodeRegistrar.Register(); + } // Enter the listening loop while (true) @@ -125,12 +129,12 @@ public void StartListening() } catch (SocketException ex) { - Logger.Error("SocketException occurred while trying to start listner: {0}", ex); + Logger.Error("SocketException occurred while trying to start listener: {0}", ex); throw; } catch (ArgumentException ex) { - Logger.Error("ArgumentException occurred while trying to start listner: {0}", ex); + Logger.Error("ArgumentException occurred while trying to start listener: {0}", ex); throw; } finally @@ -149,13 +153,18 @@ public void StopListening() #region Methods + private static string NormalizePrefix(string prefix) + { + return string.IsNullOrWhiteSpace(prefix) ? string.Empty : prefix.Trim('/'); + } + private string HandleRequest(HttpRequest acceptedRequest) { var firstHeaderTokens = acceptedRequest.StartingLine.Split(' '); var method = firstHeaderTokens[0]; var resourcePath = firstHeaderTokens[1]; - var uriToMatch = new Uri(this.Prefix, resourcePath); + var uriToMatch = new Uri(this.baseAddress, resourcePath); var matched = this.dispatcher.Match(method, uriToMatch); if (matched == null) @@ -165,14 +174,22 @@ private string HandleRequest(HttpRequest acceptedRequest) } var commandName = matched.Data.ToString(); - var commandToExecute = new Command(commandName, acceptedRequest.MessageBody); - foreach (string variableName in matched.BoundVariables.Keys) + try { - commandToExecute.Parameters[variableName] = matched.BoundVariables[variableName]; - } + var commandToExecute = new Command(commandName, acceptedRequest.MessageBody); + foreach (string variableName in matched.BoundVariables.Keys) + { + commandToExecute.Parameters[variableName] = matched.BoundVariables[variableName]; + } - var commandResponse = this.ProcessCommand(commandToExecute); - return HttpResponseHelper.ResponseString(commandResponse.HttpStatusCode, commandResponse.Content); + var commandResponse = this.ProcessCommand(commandToExecute); + return HttpResponseHelper.ResponseString(commandResponse.HttpStatusCode, commandResponse.Content); + } + catch (Newtonsoft.Json.JsonReaderException exception) + { + Logger.Error("{0}\r\nRAW REQUEST BODY:\r\n{1}", exception.ToString(), acceptedRequest.MessageBody); + return HttpResponseHelper.ResponseString(HttpStatusCode.BadRequest, exception.ToString()); + } } private CommandResponse ProcessCommand(Command command) @@ -180,10 +197,10 @@ private CommandResponse ProcessCommand(Command command) Logger.Info("COMMAND {0}\r\n{1}", command.Name, command.Parameters.ToString()); var executor = this.executorDispatcher.GetExecutor(command.Name); executor.ExecutedCommand = command; - var respnose = executor.Do(); - Logger.Debug("RESPONSE:\r\n{0}", respnose); + var response = executor.Do(); + Logger.Debug("RESPONSE:\r\n{0}", response); - return respnose; + return response; } #endregion diff --git a/src/Winium.Desktop.Driver/Program.cs b/src/Winium.Desktop.Driver/Program.cs index 4dd5bf5..a343750 100644 --- a/src/Winium.Desktop.Driver/Program.cs +++ b/src/Winium.Desktop.Driver/Program.cs @@ -3,6 +3,7 @@ #region using using System; + using Winium.Desktop.Driver.CommandHelpers; #endregion @@ -13,15 +14,16 @@ internal class Program [STAThread] private static void Main(string[] args) { - var listeningPort = 9999; - var options = new CommandLineOptions(); - if (CommandLine.Parser.Default.ParseArguments(args, options)) + CommandLine.Parser.Default.ParseArgumentsStrict(args, options); + + var appName = typeof(Program).Assembly.GetName().Name; + var versionInfo = string.Format("{0}, {1}", appName, new BuildInfo()); + + if (options.Version) { - if (options.Port.HasValue) - { - listeningPort = options.Port.Value; - } + Console.WriteLine(versionInfo); + Environment.Exit(0); } if (options.LogPath != null) @@ -37,12 +39,13 @@ private static void Main(string[] args) Logger.TargetNull(); } + Logger.Info(versionInfo); + try { - var listener = new Listener(listeningPort); - Listener.UrnPrefix = options.UrlBase; + var listener = new Listener(options.Port, options.UrlBase, options.NodeConfig); - Console.WriteLine("Starting Windows Desktop Driver on port {0}\n", listeningPort); + Console.WriteLine("Starting {0} on port {1}\n", appName, listener.Port); listener.StartListening(); } diff --git a/src/Winium.Desktop.Driver/Winium.Desktop.Driver.csproj b/src/Winium.Desktop.Driver/Winium.Desktop.Driver.csproj index b70cae3..8442f6a 100644 --- a/src/Winium.Desktop.Driver/Winium.Desktop.Driver.csproj +++ b/src/Winium.Desktop.Driver/Winium.Desktop.Driver.csproj @@ -38,6 +38,7 @@ ..\packages\CommandLineParser.1.9.71\lib\net45\CommandLine.dll + ..\packages\Newtonsoft.Json.6.0.8\lib\net45\Newtonsoft.Json.dll @@ -128,6 +129,7 @@ + From d60c09836f449512fce67cbfa2bdac22efcf48f5 Mon Sep 17 00:00:00 2001 From: Sven Jost Date: Mon, 11 Sep 2017 11:08:59 +0200 Subject: [PATCH 07/12] Be able to reset a certain directory on app start --- .../Automator/Capabilities.cs | 4 ++++ .../CommandExecutors/NewSessionExecutor.cs | 16 ++++++++++++++++ 2 files changed, 20 insertions(+) diff --git a/src/Winium.Desktop.Driver/Automator/Capabilities.cs b/src/Winium.Desktop.Driver/Automator/Capabilities.cs index 3ee52c7..5d00e75 100644 --- a/src/Winium.Desktop.Driver/Automator/Capabilities.cs +++ b/src/Winium.Desktop.Driver/Automator/Capabilities.cs @@ -17,6 +17,7 @@ internal Capabilities() { this.App = string.Empty; this.Arguments = string.Empty; + this.ResetDirectory = string.Empty; this.LaunchDelay = 0; this.DebugConnectToRunningApp = false; this.InnerPort = 9998; @@ -42,6 +43,9 @@ public static string PlatformName [JsonProperty("args")] public string Arguments { get; set; } + [JsonProperty("resetDirectory")] + public string ResetDirectory { get; set; } + [JsonProperty("debugConnectToRunningApp")] public bool DebugConnectToRunningApp { get; set; } diff --git a/src/Winium.Desktop.Driver/CommandExecutors/NewSessionExecutor.cs b/src/Winium.Desktop.Driver/CommandExecutors/NewSessionExecutor.cs index d53946f..22760f8 100644 --- a/src/Winium.Desktop.Driver/CommandExecutors/NewSessionExecutor.cs +++ b/src/Winium.Desktop.Driver/CommandExecutors/NewSessionExecutor.cs @@ -12,6 +12,7 @@ using Winium.Desktop.Driver.Automator; using Winium.Desktop.Driver.Input; using Winium.StoreApps.Common; + using System.IO; #endregion @@ -29,6 +30,7 @@ protected override string DoImpl() JsonConvert.SerializeObject(this.ExecutedCommand.Parameters["desiredCapabilities"]); this.Automator.ActualCapabilities = Capabilities.CapabilitiesFromJsonString(serializedCapability); + this.ResetDirectory(this.Automator.ActualCapabilities.ResetDirectory); this.InitializeApplication(this.Automator.ActualCapabilities.DebugConnectToRunningApp); this.InitializeKeyboardEmulator(this.Automator.ActualCapabilities.KeyboardSimulator); @@ -38,6 +40,20 @@ protected override string DoImpl() return this.JsonResponse(ResponseStatus.Success, this.Automator.ActualCapabilities); } + private void ResetDirectory(string resetDirectory) + { + DirectoryInfo directory = new DirectoryInfo(resetDirectory); + + foreach (FileInfo file in directory.GetFiles()) + { + file.Delete(); + } + foreach (DirectoryInfo subDirectory in directory.GetDirectories()) + { + subDirectory.Delete(true); + } + } + private void InitializeApplication(bool debugDoNotDeploy = false) { var appPath = this.Automator.ActualCapabilities.App; From ad7945407185c035588220e71e83a31b0ecc66e9 Mon Sep 17 00:00:00 2001 From: Sven Jost Date: Mon, 11 Sep 2017 15:24:37 +0200 Subject: [PATCH 08/12] Only reset directory if capability is set --- .../CommandExecutors/NewSessionExecutor.cs | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/src/Winium.Desktop.Driver/CommandExecutors/NewSessionExecutor.cs b/src/Winium.Desktop.Driver/CommandExecutors/NewSessionExecutor.cs index 22760f8..b739b9e 100644 --- a/src/Winium.Desktop.Driver/CommandExecutors/NewSessionExecutor.cs +++ b/src/Winium.Desktop.Driver/CommandExecutors/NewSessionExecutor.cs @@ -42,15 +42,18 @@ protected override string DoImpl() private void ResetDirectory(string resetDirectory) { - DirectoryInfo directory = new DirectoryInfo(resetDirectory); - - foreach (FileInfo file in directory.GetFiles()) - { - file.Delete(); - } - foreach (DirectoryInfo subDirectory in directory.GetDirectories()) + if (!string.IsNullOrEmpty(resetDirectory)) { - subDirectory.Delete(true); + DirectoryInfo directory = new DirectoryInfo(resetDirectory); + + foreach (FileInfo file in directory.GetFiles()) + { + file.Delete(); + } + foreach (DirectoryInfo subDirectory in directory.GetDirectories()) + { + subDirectory.Delete(true); + } } } From 458dd295a499425a9d098adb26b0ec40abd9a69f Mon Sep 17 00:00:00 2001 From: Sven Jost Date: Mon, 11 Sep 2017 17:38:14 +0200 Subject: [PATCH 09/12] Allow NuGet to download missing packages --- src/.nuget/NuGet.Config | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/.nuget/NuGet.Config b/src/.nuget/NuGet.Config index 67f8ea0..1a2e3dc 100644 --- a/src/.nuget/NuGet.Config +++ b/src/.nuget/NuGet.Config @@ -1,6 +1,9 @@ - + + + + - \ No newline at end of file + From 3a70ae1ad875e36593f191dae1817ff7b555cb56 Mon Sep 17 00:00:00 2001 From: Sven Jost Date: Thu, 14 Sep 2017 15:07:58 +0200 Subject: [PATCH 10/12] Forgot to add NodeRegistrar.cs --- NodeRegistrar.cs | 243 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 243 insertions(+) create mode 100644 NodeRegistrar.cs diff --git a/NodeRegistrar.cs b/NodeRegistrar.cs new file mode 100644 index 0000000..c71072d --- /dev/null +++ b/NodeRegistrar.cs @@ -0,0 +1,243 @@ +namespace Winium.Desktop.Driver +{ + #region + + using System; + using System.IO; + using System.Net; + using System.Threading; + using System.Timers; + + using Newtonsoft.Json; + using Newtonsoft.Json.Linq; + + using Timer = System.Timers.Timer; + + #endregion + + public class NodeRegistrar + { + #region Fields + + private readonly string configFilePath; + + private readonly string defaultHost; + + private readonly int defaultPort; + + private Timer autoRegisterTimer; + + private string data; + + private NodeRegistrarConfiguration registrarConfiguration; + + #endregion + + #region Constructors and Destructors + + public NodeRegistrar(string configFilePath, string defaultHost, int defaultPort) + { + this.configFilePath = configFilePath; + this.defaultHost = defaultHost; + this.defaultPort = defaultPort; + } + + #endregion + + #region Public Methods and Operators + + public void Register() + { + new Thread( + () => + { + Thread.CurrentThread.IsBackground = true; + this.RegisterNode(); + }).Start(); + } + + #endregion + + #region Methods + + private void AutoRegisterEvent(object source, ElapsedEventArgs e) + { + if (!this.IsAlreadyRegistered()) + { + this.PostData(); + } + } + + private bool IsAlreadyRegistered() + { + try + { + using (var client = new WebClient()) + { + client.Headers[HttpRequestHeader.ContentType] = "application/json"; + var uri = new Uri(this.registrarConfiguration.HubUri, "/grid/api/proxy"); + client.QueryString.Add("id", this.registrarConfiguration.Id); + dynamic jo = JObject.Parse(client.DownloadString(uri)); + return jo.success; + } + } + catch (WebException webException) + { + Logger.Error("Selenium Grid hub down or not responding: {0}.", webException.Message); + } + + return false; + } + + private bool LoadConfiguration() + { + try + { + dynamic jo = JObject.Parse(File.ReadAllText(this.configFilePath)); + + if (jo.configuration.host == null || jo.configuration.port == null || jo.configuration.url == null) + { + jo.configuration.host = this.defaultHost; + jo.configuration.port = this.defaultPort; + jo.configuration.url = new UriBuilder("http", this.defaultHost, this.defaultPort).ToString(); + Logger.Warn( + "Some of required node options (host, port or url) are not set, Winium set them to: host={0}, port={1}, url={2}." + + " Note that this will not work if your node and grid aren't in the same place.", + jo.configuration.host, + jo.configuration.port, + jo.configuration.url); + } + + this.registrarConfiguration = jo.configuration.ToObject(); + + this.data = JsonConvert.SerializeObject(jo, Formatting.Indented); + + return true; + } + catch (JsonReaderException ex) + { + Logger.Error("Syntax error in node configuration file: {0}", ex.Message); + return false; + } + catch (Exception ex) + { + Logger.Error("Error reading node configuration: {0}", ex.Message); + return false; + } + } + + private void PostData() + { + try + { + using (var client = new WebClient()) + { + client.Headers[HttpRequestHeader.ContentType] = "application/json"; + var uri = new Uri(this.registrarConfiguration.HubUri, "grid/register/"); + var response = client.UploadString(uri, this.data); + + Logger.Debug( + "Winium successfully registered with the grid on {0}, grid responded with: '{1}'.", + this.registrarConfiguration.HubUri, + response); + } + } + catch (WebException webException) + { + var webResponse = webException.Response as HttpWebResponse; + if (webResponse != null) + { + Logger.Error("Selenium Grid refused to register hub. {0}", webResponse.StatusDescription); + } + else + { + Logger.Error("Selenium Grid hub down or not responding: {0}.", webException.Message); + } + } + } + + private void RegisterNode() + { + if (!this.LoadConfiguration()) + { + return; + } + + if (!this.registrarConfiguration.Register) + { + Logger.Debug("Node registration data was not send to Selenium Grid."); + return; + } + + this.PostData(); + + if (!(this.registrarConfiguration.RegisterCycle > 0)) + { + return; + } + + Logger.Debug( + "Starting auto register for grid. Will try to register every {0} ms", + this.registrarConfiguration.RegisterCycle); + this.autoRegisterTimer = new Timer(); + this.autoRegisterTimer.Elapsed += this.AutoRegisterEvent; + this.autoRegisterTimer.Interval = this.registrarConfiguration.RegisterCycle; + this.autoRegisterTimer.Enabled = true; + } + + #endregion + + private class NodeRegistrarConfiguration + { + #region Fields + + private Uri hubUri; + + private string id; + + #endregion + + #region Public Properties + + public Uri HubUri + { + get + { + return this.hubUri ?? (this.hubUri = new UriBuilder("http", this.HubHost, this.HubPort).Uri); + } + } + + public string Id + { + get + { + return this.id ?? (this.id = new UriBuilder("http", this.Host, this.Port).ToString()); + } + } + + [JsonProperty("register")] + public bool Register { get; set; } + + [JsonProperty("registerCycle")] + public double RegisterCycle { get; set; } + + #endregion + + #region Properties + + [JsonProperty("host")] + private string Host { get; set; } + + [JsonProperty("hubHost")] + private string HubHost { get; set; } + + [JsonProperty("hubPort")] + private int HubPort { get; set; } + + [JsonProperty("port")] + private int Port { get; set; } + + #endregion + } + } +} From 52a07985233e5aff853522701d5780c6996d662a Mon Sep 17 00:00:00 2001 From: Sven Jost Date: Thu, 14 Sep 2017 15:11:34 +0200 Subject: [PATCH 11/12] Move NodeRegistrar.cs into correct directory --- NodeRegistrar.cs => src/Winium.Desktop.Driver/NodeRegistrar.cs | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename NodeRegistrar.cs => src/Winium.Desktop.Driver/NodeRegistrar.cs (100%) diff --git a/NodeRegistrar.cs b/src/Winium.Desktop.Driver/NodeRegistrar.cs similarity index 100% rename from NodeRegistrar.cs rename to src/Winium.Desktop.Driver/NodeRegistrar.cs From de1245841089b251db59896c79b4c453bdd31874 Mon Sep 17 00:00:00 2001 From: Sven Jost Date: Fri, 15 Sep 2017 14:08:02 +0200 Subject: [PATCH 12/12] Better session handling in grid + resetProcesses capability --- .../Automator/Capabilities.cs | 4 ++ .../CommandExecutors/CommandExecutorBase.cs | 14 +++- .../CommandExecutors/NewSessionExecutor.cs | 66 +++++++++++++++---- .../Exceptions/SessionNotCreatedException.cs | 35 ++++++++++ .../Winium.Desktop.Driver.csproj | 3 +- 5 files changed, 108 insertions(+), 14 deletions(-) create mode 100644 src/Winium.Desktop.Driver/Exceptions/SessionNotCreatedException.cs diff --git a/src/Winium.Desktop.Driver/Automator/Capabilities.cs b/src/Winium.Desktop.Driver/Automator/Capabilities.cs index 5d00e75..554d125 100644 --- a/src/Winium.Desktop.Driver/Automator/Capabilities.cs +++ b/src/Winium.Desktop.Driver/Automator/Capabilities.cs @@ -18,6 +18,7 @@ internal Capabilities() this.App = string.Empty; this.Arguments = string.Empty; this.ResetDirectory = string.Empty; + this.ResetProcesses = false; this.LaunchDelay = 0; this.DebugConnectToRunningApp = false; this.InnerPort = 9998; @@ -46,6 +47,9 @@ public static string PlatformName [JsonProperty("resetDirectory")] public string ResetDirectory { get; set; } + [JsonProperty("resetProcesses")] + public bool ResetProcesses { get; set; } + [JsonProperty("debugConnectToRunningApp")] public bool DebugConnectToRunningApp { get; set; } diff --git a/src/Winium.Desktop.Driver/CommandExecutors/CommandExecutorBase.cs b/src/Winium.Desktop.Driver/CommandExecutors/CommandExecutorBase.cs index caa8503..83d926e 100644 --- a/src/Winium.Desktop.Driver/CommandExecutors/CommandExecutorBase.cs +++ b/src/Winium.Desktop.Driver/CommandExecutors/CommandExecutorBase.cs @@ -8,6 +8,7 @@ using Newtonsoft.Json; using Winium.Desktop.Driver.Automator; + using Winium.Desktop.Driver.Exceptions; using Winium.StoreApps.Common; using Winium.StoreApps.Common.Exceptions; @@ -52,6 +53,12 @@ public CommandResponse Do() HttpStatusCode.NotImplemented, this.JsonResponse(ResponseStatus.UnknownCommand, exception)); } + catch (SessionNotCreatedException exception) + { + return CommandResponse.Create( + HttpStatusCode.InternalServerError, + this.JsonResponse(ResponseStatus.SessionNotCreatedException, exception.GetBaseException())); + } catch (Exception exception) { return CommandResponse.Create( @@ -76,8 +83,13 @@ protected string JsonResponse() protected string JsonResponse(ResponseStatus status, object value) { + var session = this.Automator.Session; + if (status == ResponseStatus.SessionNotCreatedException) + { + this.Automator.Session = null; + } return JsonConvert.SerializeObject( - new JsonResponse(this.Automator.Session, status, value), + new JsonResponse(session, status, value), Formatting.Indented); } diff --git a/src/Winium.Desktop.Driver/CommandExecutors/NewSessionExecutor.cs b/src/Winium.Desktop.Driver/CommandExecutors/NewSessionExecutor.cs index b739b9e..bf172c3 100644 --- a/src/Winium.Desktop.Driver/CommandExecutors/NewSessionExecutor.cs +++ b/src/Winium.Desktop.Driver/CommandExecutors/NewSessionExecutor.cs @@ -3,6 +3,8 @@ #region using using System; + using System.IO; + using System.Diagnostics; using System.Threading; using Newtonsoft.Json; @@ -10,9 +12,9 @@ using Winium.Cruciatus; using Winium.Cruciatus.Settings; using Winium.Desktop.Driver.Automator; + using Winium.Desktop.Driver.Exceptions; using Winium.Desktop.Driver.Input; using Winium.StoreApps.Common; - using System.IO; #endregion @@ -22,24 +24,64 @@ internal class NewSessionExecutor : CommandExecutorBase protected override string DoImpl() { - this.Automator.Session = Guid.NewGuid().ToString(); + try + { + this.Automator.Session = Guid.NewGuid().ToString(); + Logger.Debug("session for command: " + this.Automator.Session); - // It is easier to reparse desired capabilities as JSON instead of re-mapping keys to attributes and calling type conversions, - // so we will take possible one time performance hit by serializing Dictionary and deserializing it as Capabilities object - var serializedCapability = - JsonConvert.SerializeObject(this.ExecutedCommand.Parameters["desiredCapabilities"]); - this.Automator.ActualCapabilities = Capabilities.CapabilitiesFromJsonString(serializedCapability); + // It is easier to reparse desired capabilities as JSON instead of re-mapping keys to attributes and calling type conversions, + // so we will take possible one time performance hit by serializing Dictionary and deserializing it as Capabilities object + var serializedCapability = + JsonConvert.SerializeObject(this.ExecutedCommand.Parameters["desiredCapabilities"]); + this.Automator.ActualCapabilities = Capabilities.CapabilitiesFromJsonString(serializedCapability); - this.ResetDirectory(this.Automator.ActualCapabilities.ResetDirectory); - this.InitializeApplication(this.Automator.ActualCapabilities.DebugConnectToRunningApp); - this.InitializeKeyboardEmulator(this.Automator.ActualCapabilities.KeyboardSimulator); + if (this.Automator.ActualCapabilities.ResetProcesses) + { + this.ResetProcesses(this.Automator.ActualCapabilities.App); + } + this.ResetDirectory(this.Automator.ActualCapabilities.ResetDirectory); + this.InitializeApplication(this.Automator.ActualCapabilities.DebugConnectToRunningApp); + this.InitializeKeyboardEmulator(this.Automator.ActualCapabilities.KeyboardSimulator); - // Gives sometime to load visuals (needed only in case of slow emulation) - Thread.Sleep(this.Automator.ActualCapabilities.LaunchDelay); + // Gives sometime to load visuals (needed only in case of slow emulation) + Thread.Sleep(this.Automator.ActualCapabilities.LaunchDelay); + } catch(Exception e) + { + throw new SessionNotCreatedException(e.Message, e); + } return this.JsonResponse(ResponseStatus.Success, this.Automator.ActualCapabilities); } + private void ResetProcesses(string app) + { + string executable = app.Substring(app.LastIndexOf("\\") + 1); + string processName = executable.Substring(0, executable.LastIndexOf(".")); + foreach (Process process in Process.GetProcesses()) + { + Logger.Debug("Running process: " + process.ProcessName); + } + foreach (Process process in Process.GetProcessesByName(processName)) + { + Logger.Debug("Killing process: " + process.ProcessName); + try + { + process.Kill(); + } + catch (Exception e) + { + Logger.Info("Could not kill process: " + e.Message); + } + try + { + process.WaitForExit(10000); + } catch(Exception e) + { + Logger.Info("Could not wait for process to exit: " + e.Message); + } + } + } + private void ResetDirectory(string resetDirectory) { if (!string.IsNullOrEmpty(resetDirectory)) diff --git a/src/Winium.Desktop.Driver/Exceptions/SessionNotCreatedException.cs b/src/Winium.Desktop.Driver/Exceptions/SessionNotCreatedException.cs new file mode 100644 index 0000000..52177d9 --- /dev/null +++ b/src/Winium.Desktop.Driver/Exceptions/SessionNotCreatedException.cs @@ -0,0 +1,35 @@ +namespace Winium.Desktop.Driver.Exceptions +{ + #region + + using System; + + #endregion + + public class SessionNotCreatedException : Exception + { + + #region Constructors and Destructors + + public SessionNotCreatedException() + { + } + + public SessionNotCreatedException(string message) + : base(message) + { + } + + public SessionNotCreatedException(string message, params object[] args) + : base(string.Format(message, args)) + { + } + + public SessionNotCreatedException(string message, Exception innerException) + : base(message, innerException) + { + } + + #endregion + } +} diff --git a/src/Winium.Desktop.Driver/Winium.Desktop.Driver.csproj b/src/Winium.Desktop.Driver/Winium.Desktop.Driver.csproj index 8442f6a..500e741 100644 --- a/src/Winium.Desktop.Driver/Winium.Desktop.Driver.csproj +++ b/src/Winium.Desktop.Driver/Winium.Desktop.Driver.csproj @@ -120,6 +120,7 @@ + @@ -167,4 +168,4 @@ - + \ No newline at end of file