diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..269fa7e
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,6 @@
+BrowserStackLocal/BrowserStackLocal Unit Tests/bin
+BrowserStackLocal/BrowserStackLocal Unit Tests/obj
+BrowserStackLocal/BrowserStackLocal/bin
+BrowserStackLocal/BrowserStackLocal/obj
+BrowserStackLocal/packages
+BrowserStackLocal/.vs
\ No newline at end of file
diff --git a/.travis.yml b/.travis.yml
index d9270a0..4178183 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,6 +1,6 @@
language: csharp
-solution: BrowserStack/BrowserStack.sln
+solution: BrowserStackLocal/BrowserStackLocal.sln
before_install:
- sudo apt-get install nunit-console
after_script:
- - nunit-console BrowserStack/BrowserStack\ Unit\ Tests/bin/Release/BrowserStack\ Unit\ Tests.dll
+ - nunit-console BrowserStackLocal/BrowserStackLocal\ Unit\ Tests/bin/Release/BrowserStackLocal\ Unit\ Tests.dll
diff --git a/BrowserStack.dll b/BrowserStack.dll
deleted file mode 100644
index 01a56bd..0000000
Binary files a/BrowserStack.dll and /dev/null differ
diff --git a/BrowserStackLocal.0.1.0.0.nupkg b/BrowserStackLocal.0.1.0.0.nupkg
new file mode 100644
index 0000000..fc8f66b
Binary files /dev/null and b/BrowserStackLocal.0.1.0.0.nupkg differ
diff --git a/BrowserStack/BrowserStack Unit Tests/BrowserStack Unit Tests.csproj b/BrowserStackLocal/BrowserStackLocal Unit Tests/BrowserStackLocal Unit Tests.csproj
similarity index 96%
rename from BrowserStack/BrowserStack Unit Tests/BrowserStack Unit Tests.csproj
rename to BrowserStackLocal/BrowserStackLocal Unit Tests/BrowserStackLocal Unit Tests.csproj
index d459c6d..8434c68 100644
--- a/BrowserStack/BrowserStack Unit Tests/BrowserStack Unit Tests.csproj
+++ b/BrowserStackLocal/BrowserStackLocal Unit Tests/BrowserStackLocal Unit Tests.csproj
@@ -6,8 +6,8 @@
{FF1ABB6F-4A2C-48F4-B4DB-47DAD566D2F9}
Library
Properties
- BrowserStack_Unit_Tests
- BrowserStack Unit Tests
+ BrowserStackLocal_Unit_Tests
+ BrowserStackLocal Unit Tests
v4.5
512
{3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}
@@ -81,9 +81,9 @@
-
+
{f58e1c9a-5910-4da8-b531-9f4a6b0ae8c8}
- BrowserStack
+ BrowserStackLocal
diff --git a/BrowserStack/BrowserStack Unit Tests/BrowserStackTunnelTests.cs b/BrowserStackLocal/BrowserStackLocal Unit Tests/BrowserStackTunnelTests.cs
similarity index 100%
rename from BrowserStack/BrowserStack Unit Tests/BrowserStackTunnelTests.cs
rename to BrowserStackLocal/BrowserStackLocal Unit Tests/BrowserStackTunnelTests.cs
diff --git a/BrowserStack/BrowserStack Unit Tests/LocalTests.cs b/BrowserStackLocal/BrowserStackLocal Unit Tests/LocalTests.cs
similarity index 98%
rename from BrowserStack/BrowserStack Unit Tests/LocalTests.cs
rename to BrowserStackLocal/BrowserStackLocal Unit Tests/LocalTests.cs
index 2a90666..828a76e 100644
--- a/BrowserStack/BrowserStack Unit Tests/LocalTests.cs
+++ b/BrowserStackLocal/BrowserStackLocal Unit Tests/LocalTests.cs
@@ -177,7 +177,7 @@ public void TestWorksWithCustomOptions()
local.start(options);
tunnelMock.Verify(mock => mock.addBinaryPath(""), Times.Once);
tunnelMock.Verify(mock => mock.addBinaryArguments(
- It.IsRegex("-customBoolKey1.*customBoolKey2.*-customKey1.*'customValue1'.*-customKey2.*'customValue2'")
+ It.IsRegex("-customBoolKey1.*-customBoolKey2.*-customKey1.*customValue1.*-customKey2.*customValue2")
), Times.Once());
tunnelMock.Verify(mock => mock.Run("dummyKey", "", logAbsolute), Times.Once());
local.stop();
diff --git a/BrowserStack/BrowserStack Unit Tests/Properties/AssemblyInfo.cs b/BrowserStackLocal/BrowserStackLocal Unit Tests/Properties/AssemblyInfo.cs
similarity index 86%
rename from BrowserStack/BrowserStack Unit Tests/Properties/AssemblyInfo.cs
rename to BrowserStackLocal/BrowserStackLocal Unit Tests/Properties/AssemblyInfo.cs
index 5d161bf..deddef4 100644
--- a/BrowserStack/BrowserStack Unit Tests/Properties/AssemblyInfo.cs
+++ b/BrowserStackLocal/BrowserStackLocal Unit Tests/Properties/AssemblyInfo.cs
@@ -5,11 +5,11 @@
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
-[assembly: AssemblyTitle("BrowserStack Unit Tests")]
+[assembly: AssemblyTitle("BrowserStackLocal Unit Tests")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
-[assembly: AssemblyProduct("BrowserStack Unit Tests")]
+[assembly: AssemblyProduct("BrowserStackLocal Unit Tests")]
[assembly: AssemblyCopyright("Copyright © 2016")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
@@ -32,5 +32,5 @@
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
-[assembly: AssemblyVersion("1.0.0.0")]
-[assembly: AssemblyFileVersion("1.0.0.0")]
+[assembly: AssemblyVersion("0.1.0")]
+[assembly: AssemblyFileVersion("0.1.0")]
diff --git a/BrowserStack/BrowserStack Unit Tests/packages.config b/BrowserStackLocal/BrowserStackLocal Unit Tests/packages.config
similarity index 100%
rename from BrowserStack/BrowserStack Unit Tests/packages.config
rename to BrowserStackLocal/BrowserStackLocal Unit Tests/packages.config
diff --git a/BrowserStack/BrowserStack.sln b/BrowserStackLocal/BrowserStackLocal.sln
similarity index 85%
rename from BrowserStack/BrowserStack.sln
rename to BrowserStackLocal/BrowserStackLocal.sln
index 1bedd62..4a8b9b1 100644
--- a/BrowserStack/BrowserStack.sln
+++ b/BrowserStackLocal/BrowserStackLocal.sln
@@ -1,28 +1,28 @@
-
-Microsoft Visual Studio Solution File, Format Version 12.00
-# Visual Studio 14
-VisualStudioVersion = 14.0.23107.0
-MinimumVisualStudioVersion = 10.0.40219.1
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BrowserStack", "BrowserStack\BrowserStack.csproj", "{F58E1C9A-5910-4DA8-B531-9F4A6B0AE8C8}"
-EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BrowserStack Unit Tests", "BrowserStack Unit Tests\BrowserStack Unit Tests.csproj", "{FF1ABB6F-4A2C-48F4-B4DB-47DAD566D2F9}"
-EndProject
-Global
- GlobalSection(SolutionConfigurationPlatforms) = preSolution
- Debug|Any CPU = Debug|Any CPU
- Release|Any CPU = Release|Any CPU
- EndGlobalSection
- GlobalSection(ProjectConfigurationPlatforms) = postSolution
- {F58E1C9A-5910-4DA8-B531-9F4A6B0AE8C8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {F58E1C9A-5910-4DA8-B531-9F4A6B0AE8C8}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {F58E1C9A-5910-4DA8-B531-9F4A6B0AE8C8}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {F58E1C9A-5910-4DA8-B531-9F4A6B0AE8C8}.Release|Any CPU.Build.0 = Release|Any CPU
- {FF1ABB6F-4A2C-48F4-B4DB-47DAD566D2F9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {FF1ABB6F-4A2C-48F4-B4DB-47DAD566D2F9}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {FF1ABB6F-4A2C-48F4-B4DB-47DAD566D2F9}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {FF1ABB6F-4A2C-48F4-B4DB-47DAD566D2F9}.Release|Any CPU.Build.0 = Release|Any CPU
- EndGlobalSection
- GlobalSection(SolutionProperties) = preSolution
- HideSolutionNode = FALSE
- EndGlobalSection
-EndGlobal
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio 14
+VisualStudioVersion = 14.0.23107.0
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BrowserStackLocal", "BrowserStackLocal\BrowserStackLocal.csproj", "{F58E1C9A-5910-4DA8-B531-9F4A6B0AE8C8}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BrowserStackLocal Unit Tests", "BrowserStackLocal Unit Tests\BrowserStackLocal Unit Tests.csproj", "{FF1ABB6F-4A2C-48F4-B4DB-47DAD566D2F9}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {F58E1C9A-5910-4DA8-B531-9F4A6B0AE8C8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {F58E1C9A-5910-4DA8-B531-9F4A6B0AE8C8}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {F58E1C9A-5910-4DA8-B531-9F4A6B0AE8C8}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {F58E1C9A-5910-4DA8-B531-9F4A6B0AE8C8}.Release|Any CPU.Build.0 = Release|Any CPU
+ {FF1ABB6F-4A2C-48F4-B4DB-47DAD566D2F9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {FF1ABB6F-4A2C-48F4-B4DB-47DAD566D2F9}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {FF1ABB6F-4A2C-48F4-B4DB-47DAD566D2F9}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {FF1ABB6F-4A2C-48F4-B4DB-47DAD566D2F9}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+EndGlobal
diff --git a/BrowserStack/BrowserStack/BrowserStack.csproj b/BrowserStackLocal/BrowserStackLocal/BrowserStackLocal.csproj
similarity index 88%
rename from BrowserStack/BrowserStack/BrowserStack.csproj
rename to BrowserStackLocal/BrowserStackLocal/BrowserStackLocal.csproj
index d475640..83217af 100644
--- a/BrowserStack/BrowserStack/BrowserStack.csproj
+++ b/BrowserStackLocal/BrowserStackLocal/BrowserStackLocal.csproj
@@ -1,75 +1,71 @@
-
-
-
-
- Debug
- AnyCPU
- {F58E1C9A-5910-4DA8-B531-9F4A6B0AE8C8}
- Library
- Properties
- BrowserStack
- BrowserStack
- v4.5
- 512
-
-
- true
- full
- false
- bin\Debug\
- DEBUG;TRACE
- prompt
- 4
-
-
- pdbonly
- true
- bin\Release\
- TRACE
- prompt
- 4
- AnyCPU
-
-
-
- ..\packages\SharpZipLib.0.86.0\lib\20\ICSharpCode.SharpZipLib.dll
- True
-
-
- ..\packages\log4net.2.0.5\lib\net45-full\log4net.dll
- True
-
-
- ..\packages\NUnit.3.0.1\lib\net45\nunit.framework.dll
- True
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
+
+
+
+
+ Debug
+ AnyCPU
+ {F58E1C9A-5910-4DA8-B531-9F4A6B0AE8C8}
+ Library
+ Properties
+ BrowserStack
+ BrowserStackLocal
+ v4.5
+ 512
+
+
+ true
+ full
+ false
+ bin\Debug\
+ DEBUG;TRACE
+ prompt
+ 4
+
+
+ pdbonly
+ true
+ bin\Release\
+ TRACE
+ prompt
+ 4
+ AnyCPU
+
+
+
+ ..\packages\log4net.2.0.5\lib\net45-full\log4net.dll
+ True
+
+
+ ..\packages\NUnit.3.0.1\lib\net45\nunit.framework.dll
+ True
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/BrowserStack/BrowserStack/BrowserStackTunnel.cs b/BrowserStackLocal/BrowserStackLocal/BrowserStackTunnel.cs
similarity index 96%
rename from BrowserStack/BrowserStack/BrowserStackTunnel.cs
rename to BrowserStackLocal/BrowserStackLocal/BrowserStackTunnel.cs
index b7f2440..a957480 100644
--- a/BrowserStack/BrowserStack/BrowserStackTunnel.cs
+++ b/BrowserStackLocal/BrowserStackLocal/BrowserStackTunnel.cs
@@ -1,297 +1,297 @@
-using System;
-using System.IO;
-using System.Collections.Generic;
-using System.Diagnostics;
-using System.Text;
-using System.Text.RegularExpressions;
-using System.Net;
-
-using System.Security.AccessControl;
-using System.Security.Principal;
-using System.Threading;
-
-namespace BrowserStack
-{
- public enum LocalState { Idle, Connecting, Connected, Error, Disconnected };
-
- public class BrowserStackTunnel : IDisposable
- {
- static readonly string binaryName = "BrowserStackLocal.exe";
- static readonly string downloadURL = "https://s3.amazonaws.com/browserStack/browserstack-local/BrowserStackLocal.exe";
- public static readonly string[] basePaths = new string[] {
- Path.Combine(Environment.ExpandEnvironmentVariables("%HOMEDRIVE%%HOMEPATH%"), ".browserstack"),
- Directory.GetCurrentDirectory(),
- Path.GetTempPath() };
-
- int basePathsIndex = -1;
- protected string binaryAbsolute = "";
- protected string binaryArguments = "";
- public AutoResetEvent connectingEvent = new AutoResetEvent(false);
-
- protected StringBuilder output;
- public LocalState localState;
- protected string logFilePath = "";
- protected FileSystemWatcher logfileWatcher;
-
- Job job = null;
- Process process = null;
-
- private static Dictionary stateMatchers = new Dictionary() {
- { LocalState.Connected, new Regex(@"Press Ctrl-C to exit.*", RegexOptions.Multiline) },
- { LocalState.Error, new Regex(@"\s*\*\*\* Error:\s+(.*).*", RegexOptions.Multiline) }
- };
-
- public virtual void addBinaryPath(string binaryAbsolute)
- {
- if (binaryAbsolute == null || binaryAbsolute.Trim().Length == 0)
- {
- binaryAbsolute = Path.Combine(basePaths[++basePathsIndex], binaryName);
- }
- this.binaryAbsolute = binaryAbsolute;
- }
-
- public virtual void addBinaryArguments(string binaryArguments)
- {
- if (binaryArguments == null)
- {
- binaryArguments = "";
- }
- this.binaryArguments = binaryArguments;
- }
-
- public BrowserStackTunnel()
- {
- localState = LocalState.Idle;
- output = new StringBuilder();
- }
-
- public virtual void fallbackPaths()
- {
- if (basePathsIndex >= basePaths.Length - 1)
- {
- throw new Exception("No More Paths to try. Please specify a binary path in options.");
- }
- basePathsIndex++;
- binaryAbsolute = Path.Combine(basePaths[basePathsIndex], binaryName);
- }
- public void downloadBinary()
- {
- string binaryDirectory = Path.Combine(this.binaryAbsolute, "..");
- //string binaryAbsolute = Path.Combine(binaryDirectory, binaryName);
-
- Directory.CreateDirectory(binaryDirectory);
-
- using (var client = new WebClient())
- {
- Local.logger.Info("Downloading BrowserStackLocal..");
- client.DownloadFile(downloadURL, this.binaryAbsolute);
- Local.logger.Info("Binary Downloaded.");
- }
-
- if (!File.Exists(binaryAbsolute))
- {
- Local.logger.Error("Error accessing downloaded zip. Please check file permissions.");
- throw new Exception("Error accessing file " + binaryAbsolute);
- }
-
- DirectoryInfo dInfo = new DirectoryInfo(binaryAbsolute);
- DirectorySecurity dSecurity = dInfo.GetAccessControl();
- dSecurity.AddAccessRule(new FileSystemAccessRule(new SecurityIdentifier(WellKnownSidType.WorldSid, null), FileSystemRights.FullControl, InheritanceFlags.ObjectInherit | InheritanceFlags.ContainerInherit, PropagationFlags.NoPropagateInherit, AccessControlType.Allow));
- dInfo.SetAccessControl(dSecurity);
- }
-
- public virtual void Run(string accessKey, string folder, string logFilePath)
- {
- string arguments = "";
- if (folder != null && folder.Trim().Length != 0)
- {
- arguments = "-f " + accessKey + " " + folder + " " + binaryArguments;
- }
- else
- {
- arguments = accessKey + " " + binaryArguments;
- }
- if (!File.Exists(binaryAbsolute))
- {
- Local.logger.Warn("BrowserStackLocal binary was not found at " + binaryAbsolute);
- downloadBinary();
- }
-
- if (process != null)
- {
- process.Close();
- }
-
- if (File.Exists(logFilePath))
- {
- File.WriteAllText(logFilePath, string.Empty);
- }
- Local.logger.Info("BrowserStackLocal binary is located at " + binaryAbsolute);
- Local.logger.Info("Starting Binary with arguments " + arguments.Replace(accessKey, ""));
- ProcessStartInfo processStartInfo = new ProcessStartInfo()
- {
- FileName = binaryAbsolute,
- Arguments = arguments,
- CreateNoWindow = true,
- WindowStyle = ProcessWindowStyle.Hidden,
- RedirectStandardOutput = true,
- RedirectStandardError = true,
- RedirectStandardInput = true,
- UseShellExecute = false
- };
-
- process = new Process();
- process.StartInfo = processStartInfo;
- process.EnableRaisingEvents = true;
- DataReceivedEventHandler o = new DataReceivedEventHandler((s, e) =>
- {
- if (e.Data != null)
- {
- output.Append(e.Data);
-
- foreach (KeyValuePair kv in stateMatchers)
- {
- Match m = kv.Value.Match(e.Data);
- if (m.Success)
- {
- if (localState != kv.Key)
- TunnelStateChanged(localState, kv.Key);
-
- localState = kv.Key;
- output.Clear();
- Local.logger.Info("TunnelState: " + localState.ToString());
- break;
- }
- }
- }
- });
-
- process.OutputDataReceived += o;
- process.ErrorDataReceived += o;
- process.Exited += ((s, e) =>
- {
- Kill();
- process = null;
- });
-
- process.Start();
-
- job = new Job();
- job.AddProcess(process.Handle);
-
- process.BeginOutputReadLine();
- process.BeginErrorReadLine();
-
- TunnelStateChanged(LocalState.Idle, LocalState.Connecting);
-
- AppDomain.CurrentDomain.ProcessExit += new EventHandler((s, e) => Kill());
-
- new Thread(() =>
- {
- Thread.CurrentThread.IsBackground = true;
- readFile(logFilePath);
- }).Start();
- connectingEvent.WaitOne();
- }
-
- private void readFile(string filename)
- {
- logFilePath = filename;
- if (File.Exists(filename))
- {
- readAlreadyPresentFile(filename);
- }
- else
- {
- logfileWatcher = new FileSystemWatcher(new FileInfo(filename).Directory.FullName);
- logfileWatcher.Created += readAlreadyPresentFile;
- logfileWatcher.Changed += readAlreadyPresentFile;
- logfileWatcher.EnableRaisingEvents = true;
- }
- }
-
- private void readAlreadyPresentFile(object sender, FileSystemEventArgs e)
- {
- if (e.FullPath.Equals(logFilePath))
- {
- readAlreadyPresentFile(e.FullPath);
- }
- }
-
- private void readAlreadyPresentFile(string filename)
- {
- using (FileStream fs = File.Open(filename, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
- {
- byte[] bytes = new byte[1024];
- StringBuilder output = new StringBuilder();
- while (true)
- {
- fs.Read(bytes, 0, 1024);
- output.Append(Encoding.Default.GetString(bytes));
-
- foreach (KeyValuePair kv in stateMatchers)
- {
- Match m = kv.Value.Match(output.ToString());
- if (m.Success)
- {
- if (localState != kv.Key)
- TunnelStateChanged(localState, kv.Key);
-
- localState = kv.Key;
- output.Clear();
- break;
- }
- }
- }
- }
- }
-
- private void TunnelStateChanged(LocalState prevState, LocalState state)
- {
- if (state != LocalState.Connecting)
- {
- connectingEvent.Set();
- }
- }
-
- public bool IsConnected()
- {
- return (localState == LocalState.Connected);
- }
-
- public virtual void Kill()
- {
- try
- {
- if (process != null)
- process.Kill();
- process = null;
- if (job != null)
- job.Close();
- job = null;
- }
- catch (Exception e)
- {
- Local.logger.Error("Error killing: " + e.Message);
- }
- finally
- {
- if (localState != LocalState.Disconnected)
- TunnelStateChanged(localState, LocalState.Disconnected);
-
- localState = LocalState.Disconnected;
- }
- }
-
- public void Dispose()
- {
- Dispose(true);
- GC.SuppressFinalize(this);
- }
-
- protected virtual void Dispose(bool disposing)
- {
- Kill();
- }
- }
-}
+using System;
+using System.IO;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Text;
+using System.Text.RegularExpressions;
+using System.Net;
+
+using System.Security.AccessControl;
+using System.Security.Principal;
+using System.Threading;
+
+namespace BrowserStack
+{
+ public enum LocalState { Idle, Connecting, Connected, Error, Disconnected };
+
+ public class BrowserStackTunnel : IDisposable
+ {
+ static readonly string binaryName = "BrowserStackLocal.exe";
+ static readonly string downloadURL = "https://s3.amazonaws.com/browserStack/browserstack-local/BrowserStackLocal.exe";
+ public static readonly string[] basePaths = new string[] {
+ Path.Combine(Environment.ExpandEnvironmentVariables("%HOMEDRIVE%%HOMEPATH%"), ".browserstack"),
+ Directory.GetCurrentDirectory(),
+ Path.GetTempPath() };
+
+ int basePathsIndex = -1;
+ protected string binaryAbsolute = "";
+ protected string binaryArguments = "";
+ public AutoResetEvent connectingEvent = new AutoResetEvent(false);
+
+ protected StringBuilder output;
+ public LocalState localState;
+ protected string logFilePath = "";
+ protected FileSystemWatcher logfileWatcher;
+
+ Job job = null;
+ Process process = null;
+
+ private static Dictionary stateMatchers = new Dictionary() {
+ { LocalState.Connected, new Regex(@"Press Ctrl-C to exit.*", RegexOptions.Multiline) },
+ { LocalState.Error, new Regex(@"\s*\*\*\* Error:\s+(.*).*", RegexOptions.Multiline) }
+ };
+
+ public virtual void addBinaryPath(string binaryAbsolute)
+ {
+ if (binaryAbsolute == null || binaryAbsolute.Trim().Length == 0)
+ {
+ binaryAbsolute = Path.Combine(basePaths[++basePathsIndex], binaryName);
+ }
+ this.binaryAbsolute = binaryAbsolute;
+ }
+
+ public virtual void addBinaryArguments(string binaryArguments)
+ {
+ if (binaryArguments == null)
+ {
+ binaryArguments = "";
+ }
+ this.binaryArguments = binaryArguments;
+ }
+
+ public BrowserStackTunnel()
+ {
+ localState = LocalState.Idle;
+ output = new StringBuilder();
+ }
+
+ public virtual void fallbackPaths()
+ {
+ if (basePathsIndex >= basePaths.Length - 1)
+ {
+ throw new Exception("No More Paths to try. Please specify a binary path in options.");
+ }
+ basePathsIndex++;
+ binaryAbsolute = Path.Combine(basePaths[basePathsIndex], binaryName);
+ }
+ public void downloadBinary()
+ {
+ string binaryDirectory = Path.Combine(this.binaryAbsolute, "..");
+ //string binaryAbsolute = Path.Combine(binaryDirectory, binaryName);
+
+ Directory.CreateDirectory(binaryDirectory);
+
+ using (var client = new WebClient())
+ {
+ Local.logger.Info("Downloading BrowserStackLocal..");
+ client.DownloadFile(downloadURL, this.binaryAbsolute);
+ Local.logger.Info("Binary Downloaded.");
+ }
+
+ if (!File.Exists(binaryAbsolute))
+ {
+ Local.logger.Error("Error accessing downloaded zip. Please check file permissions.");
+ throw new Exception("Error accessing file " + binaryAbsolute);
+ }
+
+ DirectoryInfo dInfo = new DirectoryInfo(binaryAbsolute);
+ DirectorySecurity dSecurity = dInfo.GetAccessControl();
+ dSecurity.AddAccessRule(new FileSystemAccessRule(new SecurityIdentifier(WellKnownSidType.WorldSid, null), FileSystemRights.FullControl, InheritanceFlags.ObjectInherit | InheritanceFlags.ContainerInherit, PropagationFlags.NoPropagateInherit, AccessControlType.Allow));
+ dInfo.SetAccessControl(dSecurity);
+ }
+
+ public virtual void Run(string accessKey, string folder, string logFilePath)
+ {
+ string arguments = "";
+ if (folder != null && folder.Trim().Length != 0)
+ {
+ arguments = "-f " + accessKey + " " + folder + " " + binaryArguments;
+ }
+ else
+ {
+ arguments = accessKey + " " + binaryArguments;
+ }
+ if (!File.Exists(binaryAbsolute))
+ {
+ Local.logger.Warn("BrowserStackLocal binary was not found at " + binaryAbsolute);
+ downloadBinary();
+ }
+
+ if (process != null)
+ {
+ process.Close();
+ }
+
+ if (File.Exists(logFilePath))
+ {
+ File.WriteAllText(logFilePath, string.Empty);
+ }
+ Local.logger.Info("BrowserStackLocal binary is located at " + binaryAbsolute);
+ Local.logger.Info("Starting Binary with arguments " + arguments.Replace(accessKey, ""));
+ ProcessStartInfo processStartInfo = new ProcessStartInfo()
+ {
+ FileName = binaryAbsolute,
+ Arguments = arguments,
+ CreateNoWindow = true,
+ WindowStyle = ProcessWindowStyle.Hidden,
+ RedirectStandardOutput = true,
+ RedirectStandardError = true,
+ RedirectStandardInput = true,
+ UseShellExecute = false
+ };
+
+ process = new Process();
+ process.StartInfo = processStartInfo;
+ process.EnableRaisingEvents = true;
+ DataReceivedEventHandler o = new DataReceivedEventHandler((s, e) =>
+ {
+ if (e.Data != null)
+ {
+ output.Append(e.Data);
+
+ foreach (KeyValuePair kv in stateMatchers)
+ {
+ Match m = kv.Value.Match(e.Data);
+ if (m.Success)
+ {
+ if (localState != kv.Key)
+ TunnelStateChanged(localState, kv.Key);
+
+ localState = kv.Key;
+ output.Clear();
+ Local.logger.Info("TunnelState: " + localState.ToString());
+ break;
+ }
+ }
+ }
+ });
+
+ process.OutputDataReceived += o;
+ process.ErrorDataReceived += o;
+ process.Exited += ((s, e) =>
+ {
+ Kill();
+ process = null;
+ });
+
+ process.Start();
+
+ job = new Job();
+ job.AddProcess(process.Handle);
+
+ process.BeginOutputReadLine();
+ process.BeginErrorReadLine();
+
+ TunnelStateChanged(LocalState.Idle, LocalState.Connecting);
+
+ AppDomain.CurrentDomain.ProcessExit += new EventHandler((s, e) => Kill());
+
+ new Thread(() =>
+ {
+ Thread.CurrentThread.IsBackground = true;
+ readFile(logFilePath);
+ }).Start();
+ connectingEvent.WaitOne();
+ }
+
+ private void readFile(string filename)
+ {
+ logFilePath = filename;
+ if (File.Exists(filename))
+ {
+ readAlreadyPresentFile(filename);
+ }
+ else
+ {
+ logfileWatcher = new FileSystemWatcher(new FileInfo(filename).Directory.FullName);
+ logfileWatcher.Created += readAlreadyPresentFile;
+ logfileWatcher.Changed += readAlreadyPresentFile;
+ logfileWatcher.EnableRaisingEvents = true;
+ }
+ }
+
+ private void readAlreadyPresentFile(object sender, FileSystemEventArgs e)
+ {
+ if (e.FullPath.Equals(logFilePath))
+ {
+ readAlreadyPresentFile(e.FullPath);
+ }
+ }
+
+ private void readAlreadyPresentFile(string filename)
+ {
+ using (FileStream fs = File.Open(filename, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
+ {
+ byte[] bytes = new byte[1024];
+ StringBuilder output = new StringBuilder();
+ while (true)
+ {
+ fs.Read(bytes, 0, 1024);
+ output.Append(Encoding.Default.GetString(bytes));
+
+ foreach (KeyValuePair kv in stateMatchers)
+ {
+ Match m = kv.Value.Match(output.ToString());
+ if (m.Success)
+ {
+ if (localState != kv.Key)
+ TunnelStateChanged(localState, kv.Key);
+
+ localState = kv.Key;
+ output.Clear();
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ private void TunnelStateChanged(LocalState prevState, LocalState state)
+ {
+ if (state != LocalState.Connecting)
+ {
+ connectingEvent.Set();
+ }
+ }
+
+ public bool IsConnected()
+ {
+ return (localState == LocalState.Connected);
+ }
+
+ public virtual void Kill()
+ {
+ try
+ {
+ if (process != null)
+ process.Kill();
+ process = null;
+ if (job != null)
+ job.Close();
+ job = null;
+ }
+ catch (Exception e)
+ {
+ Local.logger.Error("Error killing: " + e.Message);
+ }
+ finally
+ {
+ if (localState != LocalState.Disconnected)
+ TunnelStateChanged(localState, LocalState.Disconnected);
+
+ localState = LocalState.Disconnected;
+ }
+ }
+
+ public void Dispose()
+ {
+ Dispose(true);
+ GC.SuppressFinalize(this);
+ }
+
+ protected virtual void Dispose(bool disposing)
+ {
+ Kill();
+ }
+ }
+}
diff --git a/BrowserStack/BrowserStack/Job.cs b/BrowserStackLocal/BrowserStackLocal/Job.cs
similarity index 96%
rename from BrowserStack/BrowserStack/Job.cs
rename to BrowserStackLocal/BrowserStackLocal/Job.cs
index 5ca0200..123c4c4 100644
--- a/BrowserStack/BrowserStack/Job.cs
+++ b/BrowserStackLocal/BrowserStackLocal/Job.cs
@@ -1,126 +1,126 @@
-using System;
-using System.Runtime.InteropServices;
-
-namespace BrowserStack
-{
- public class Job : IDisposable
- {
- [DllImport("kernel32.dll", CharSet = CharSet.Unicode)]
- static extern IntPtr CreateJobObject(IntPtr a, string lpName);
-
- [DllImport("kernel32.dll")]
- static extern bool SetInformationJobObject(IntPtr hJob, JobObjectInfoType infoType, IntPtr lpJobObjectInfo, UInt32 cbJobObjectInfoLength);
-
- [DllImport("kernel32.dll", SetLastError = true)]
- static extern bool AssignProcessToJobObject(IntPtr job, IntPtr process);
-
- [DllImport("kernel32.dll", SetLastError = true)]
- static extern bool CloseHandle(IntPtr hObject);
-
- private IntPtr handle;
- private bool disposed;
-
- public Job()
- {
- handle = CreateJobObject(IntPtr.Zero, null);
-
- var info = new JOBOBJECT_BASIC_LIMIT_INFORMATION
- {
- LimitFlags = 0x2000
- };
-
- var extendedInfo = new JOBOBJECT_EXTENDED_LIMIT_INFORMATION
- {
- BasicLimitInformation = info
- };
-
- int length = Marshal.SizeOf(typeof(JOBOBJECT_EXTENDED_LIMIT_INFORMATION));
- IntPtr extendedInfoPtr = Marshal.AllocHGlobal(length);
- Marshal.StructureToPtr(extendedInfo, extendedInfoPtr, false);
-
- if (!SetInformationJobObject(handle, JobObjectInfoType.ExtendedLimitInformation, extendedInfoPtr, (uint)length))
- throw new Exception(string.Format("Unable to set information. Error: {0}", Marshal.GetLastWin32Error()));
- }
-
- public void Dispose()
- {
- Dispose(true);
- GC.SuppressFinalize(this);
- }
-
- private void Dispose(bool disposing)
- {
- if (disposed)
- return;
-
- if (disposing) { }
-
- Close();
- disposed = true;
- }
-
- public void Close()
- {
- CloseHandle(handle);
- handle = IntPtr.Zero;
- }
-
- public bool AddProcess(IntPtr processHandle)
- {
- return AssignProcessToJobObject(handle, processHandle);
- }
-
- }
-
- #region Helper classes
-
- [StructLayout(LayoutKind.Sequential)]
- struct IO_COUNTERS
- {
- public UInt64 ReadOperationCount;
- public UInt64 WriteOperationCount;
- public UInt64 OtherOperationCount;
- public UInt64 ReadTransferCount;
- public UInt64 WriteTransferCount;
- public UInt64 OtherTransferCount;
- }
-
-
- [StructLayout(LayoutKind.Sequential)]
- struct JOBOBJECT_BASIC_LIMIT_INFORMATION
- {
- public Int64 PerProcessUserTimeLimit;
- public Int64 PerJobUserTimeLimit;
- public UInt32 LimitFlags;
- public UIntPtr MinimumWorkingSetSize;
- public UIntPtr MaximumWorkingSetSize;
- public UInt32 ActiveProcessLimit;
- public UIntPtr Affinity;
- public UInt32 PriorityClass;
- public UInt32 SchedulingClass;
- }
-
- [StructLayout(LayoutKind.Sequential)]
- struct JOBOBJECT_EXTENDED_LIMIT_INFORMATION
- {
- public JOBOBJECT_BASIC_LIMIT_INFORMATION BasicLimitInformation;
- public IO_COUNTERS IoInfo;
- public UIntPtr ProcessMemoryLimit;
- public UIntPtr JobMemoryLimit;
- public UIntPtr PeakProcessMemoryUsed;
- public UIntPtr PeakJobMemoryUsed;
- }
-
- public enum JobObjectInfoType
- {
- AssociateCompletionPortInformation = 7,
- BasicLimitInformation = 2,
- BasicUIRestrictions = 4,
- EndOfJobTimeInformation = 6,
- ExtendedLimitInformation = 9,
- SecurityLimitInformation = 5,
- GroupInformation = 11
- }
-
- #endregion
+using System;
+using System.Runtime.InteropServices;
+
+namespace BrowserStack
+{
+ public class Job : IDisposable
+ {
+ [DllImport("kernel32.dll", CharSet = CharSet.Unicode)]
+ static extern IntPtr CreateJobObject(IntPtr a, string lpName);
+
+ [DllImport("kernel32.dll")]
+ static extern bool SetInformationJobObject(IntPtr hJob, JobObjectInfoType infoType, IntPtr lpJobObjectInfo, UInt32 cbJobObjectInfoLength);
+
+ [DllImport("kernel32.dll", SetLastError = true)]
+ static extern bool AssignProcessToJobObject(IntPtr job, IntPtr process);
+
+ [DllImport("kernel32.dll", SetLastError = true)]
+ static extern bool CloseHandle(IntPtr hObject);
+
+ private IntPtr handle;
+ private bool disposed;
+
+ public Job()
+ {
+ handle = CreateJobObject(IntPtr.Zero, null);
+
+ var info = new JOBOBJECT_BASIC_LIMIT_INFORMATION
+ {
+ LimitFlags = 0x2000
+ };
+
+ var extendedInfo = new JOBOBJECT_EXTENDED_LIMIT_INFORMATION
+ {
+ BasicLimitInformation = info
+ };
+
+ int length = Marshal.SizeOf(typeof(JOBOBJECT_EXTENDED_LIMIT_INFORMATION));
+ IntPtr extendedInfoPtr = Marshal.AllocHGlobal(length);
+ Marshal.StructureToPtr(extendedInfo, extendedInfoPtr, false);
+
+ if (!SetInformationJobObject(handle, JobObjectInfoType.ExtendedLimitInformation, extendedInfoPtr, (uint)length))
+ throw new Exception(string.Format("Unable to set information. Error: {0}", Marshal.GetLastWin32Error()));
+ }
+
+ public void Dispose()
+ {
+ Dispose(true);
+ GC.SuppressFinalize(this);
+ }
+
+ private void Dispose(bool disposing)
+ {
+ if (disposed)
+ return;
+
+ if (disposing) { }
+
+ Close();
+ disposed = true;
+ }
+
+ public void Close()
+ {
+ CloseHandle(handle);
+ handle = IntPtr.Zero;
+ }
+
+ public bool AddProcess(IntPtr processHandle)
+ {
+ return AssignProcessToJobObject(handle, processHandle);
+ }
+
+ }
+
+ #region Helper classes
+
+ [StructLayout(LayoutKind.Sequential)]
+ struct IO_COUNTERS
+ {
+ public UInt64 ReadOperationCount;
+ public UInt64 WriteOperationCount;
+ public UInt64 OtherOperationCount;
+ public UInt64 ReadTransferCount;
+ public UInt64 WriteTransferCount;
+ public UInt64 OtherTransferCount;
+ }
+
+
+ [StructLayout(LayoutKind.Sequential)]
+ struct JOBOBJECT_BASIC_LIMIT_INFORMATION
+ {
+ public Int64 PerProcessUserTimeLimit;
+ public Int64 PerJobUserTimeLimit;
+ public UInt32 LimitFlags;
+ public UIntPtr MinimumWorkingSetSize;
+ public UIntPtr MaximumWorkingSetSize;
+ public UInt32 ActiveProcessLimit;
+ public UIntPtr Affinity;
+ public UInt32 PriorityClass;
+ public UInt32 SchedulingClass;
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ struct JOBOBJECT_EXTENDED_LIMIT_INFORMATION
+ {
+ public JOBOBJECT_BASIC_LIMIT_INFORMATION BasicLimitInformation;
+ public IO_COUNTERS IoInfo;
+ public UIntPtr ProcessMemoryLimit;
+ public UIntPtr JobMemoryLimit;
+ public UIntPtr PeakProcessMemoryUsed;
+ public UIntPtr PeakJobMemoryUsed;
+ }
+
+ public enum JobObjectInfoType
+ {
+ AssociateCompletionPortInformation = 7,
+ BasicLimitInformation = 2,
+ BasicUIRestrictions = 4,
+ EndOfJobTimeInformation = 6,
+ ExtendedLimitInformation = 9,
+ SecurityLimitInformation = 5,
+ GroupInformation = 11
+ }
+
+ #endregion
}
\ No newline at end of file
diff --git a/BrowserStack/BrowserStack/Local.cs b/BrowserStackLocal/BrowserStackLocal/Local.cs
similarity index 96%
rename from BrowserStack/BrowserStack/Local.cs
rename to BrowserStackLocal/BrowserStackLocal/Local.cs
index 130221e..276ace8 100644
--- a/BrowserStack/BrowserStack/Local.cs
+++ b/BrowserStackLocal/BrowserStackLocal/Local.cs
@@ -1,190 +1,190 @@
-using log4net;
-using log4net.Appender;
-using log4net.Core;
-using log4net.Filter;
-using log4net.Layout;
-using log4net.Repository.Hierarchy;
-using System;
-using System.Collections.Generic;
-using System.IO;
-using System.Text.RegularExpressions;
-
-namespace BrowserStack
-{
- public class Local
- {
- private Hierarchy hierarchy;
- private string folder = "";
- private string accessKey = "";
- private string customLogPath = "";
- private string argumentString = "";
- private string customBinaryPath = "";
- private PatternLayout patternLayout;
- protected BrowserStackTunnel tunnel = null;
- public static ILog logger = LogManager.GetLogger("Local");
- private static KeyValuePair emptyStringPair = new KeyValuePair();
-
- private static List> valueCommands = new List>() {
- new KeyValuePair("localIdentifier", "-localIdentifier"),
- new KeyValuePair("hosts", ""),
- new KeyValuePair("proxyHost", "-proxyHost"),
- new KeyValuePair("proxyPort", "-proxyPort"),
- new KeyValuePair("proxyUser", "-proxyUser"),
- new KeyValuePair("proxyPass", "-proxyPass"),
- };
-
- private static List> booleanCommands = new List>() {
- new KeyValuePair("v", "-vvv"),
- new KeyValuePair("force", "-force"),
- new KeyValuePair("forcelocal", "-forcelocal"),
- new KeyValuePair("forceproxy", "-forceproxy"),
- new KeyValuePair("onlyAutomate", "-onlyAutomate"),
- };
- private readonly string LOG4NET_CONFIG_FILE_PATH = Path.Combine(Directory.GetCurrentDirectory(), "log_config.xml");
-
- public bool isRunning()
- {
- if (tunnel == null) return false;
- return tunnel.IsConnected();
- }
-
- private void addArgs(string key, string value)
- {
- KeyValuePair result;
- key = key.Trim();
-
- if (key.Equals("key"))
- {
- accessKey = value;
- }
- else if (key.Equals("f"))
- {
- folder = value;
- }
- else if (key.Equals("binarypath"))
- {
- customBinaryPath = value;
- }
- else if (key.Equals("logfile"))
- {
- customLogPath = value;
- }
- else if (key.Equals("verbose"))
- {
-
- }
- else
- {
- result = valueCommands.Find(pair => pair.Key == key);
- if (!result.Equals(emptyStringPair))
- {
- argumentString += result.Value + " " + value + " ";
- return;
- }
-
- result = booleanCommands.Find(pair => pair.Key == key);
- if (!result.Equals(emptyStringPair))
- {
- if (value.Trim().ToLower() == "true")
- {
- argumentString += result.Value + " ";
- return;
- }
- }
-
- if (value.Trim().ToLower() == "true")
- {
- argumentString += "-" + key + " ";
- }
- else
- {
- argumentString += "-" + key + " " + value + " ";
- }
- }
- }
- private void setupLogging()
- {
- hierarchy = (Hierarchy)LogManager.GetRepository();
-
- patternLayout = new PatternLayout();
- patternLayout.ConversionPattern = "%date [%thread] %-5level %logger - %message%newline";
- patternLayout.ActivateOptions();
-
- ConsoleAppender consoleAppender = new ConsoleAppender();
- consoleAppender.Threshold = Level.Info;
- consoleAppender.Layout = patternLayout;
- consoleAppender.ActivateOptions();
-
- LoggerMatchFilter loggerMatchFilter = new LoggerMatchFilter();
- loggerMatchFilter.LoggerToMatch = "Local";
- loggerMatchFilter.AcceptOnMatch = true;
- consoleAppender.AddFilter(loggerMatchFilter);
- consoleAppender.AddFilter(new DenyAllFilter());
-
- hierarchy.Root.AddAppender(consoleAppender);
-
- hierarchy.Root.Level = Level.All;
- hierarchy.Configured = true;
- }
-
- public Local()
- {
- setupLogging();
- tunnel = new BrowserStackTunnel();
- }
- public void start(List> options)
- {
- foreach (KeyValuePair pair in options)
- {
- string key = pair.Key;
- string value = pair.Value;
- addArgs(key, value);
- }
-
- if (accessKey == null || accessKey.Trim().Length == 0)
- {
- accessKey = Environment.GetEnvironmentVariable("BROWSERSTACK_ACCESS_KEY");
- if (accessKey == null || accessKey.Trim().Length == 0)
- {
- throw new Exception("BROWSERSTACK_ACCESS_KEY cannot be empty. "+
- "Specify one by adding key to options or adding to the environment variable BROWSERSTACK_ACCESS_KEY.");
- }
- Regex.Replace(this.accessKey, @"\s+", "");
- }
-
- if (customLogPath == null || customLogPath.Trim().Length == 0)
- {
- customLogPath = Path.Combine(BrowserStackTunnel.basePaths[1], "local.log");
- }
-
- argumentString += "-logFile " + customLogPath;
- tunnel.addBinaryPath(customBinaryPath);
- tunnel.addBinaryArguments(argumentString);
- while (true) {
- bool except = false;
- try {
- tunnel.Run(accessKey, folder, customLogPath);
- } catch (Exception)
- {
- logger.Warn("Running Local failed. Falling back to backup path.");
- except = true;
- }
- if (except)
- {
- tunnel.fallbackPaths();
- } else
- {
- break;
- }
- }
- }
-
- public void stop()
- {
- if (tunnel != null)
- {
- tunnel.Kill();
- }
- }
- }
-}
+using log4net;
+using log4net.Appender;
+using log4net.Core;
+using log4net.Filter;
+using log4net.Layout;
+using log4net.Repository.Hierarchy;
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Text.RegularExpressions;
+
+namespace BrowserStack
+{
+ public class Local
+ {
+ private Hierarchy hierarchy;
+ private string folder = "";
+ private string accessKey = "";
+ private string customLogPath = "";
+ private string argumentString = "";
+ private string customBinaryPath = "";
+ private PatternLayout patternLayout;
+ protected BrowserStackTunnel tunnel = null;
+ public static ILog logger = LogManager.GetLogger("Local");
+ private static KeyValuePair emptyStringPair = new KeyValuePair();
+
+ private static List> valueCommands = new List>() {
+ new KeyValuePair("localIdentifier", "-localIdentifier"),
+ new KeyValuePair("hosts", ""),
+ new KeyValuePair("proxyHost", "-proxyHost"),
+ new KeyValuePair("proxyPort", "-proxyPort"),
+ new KeyValuePair("proxyUser", "-proxyUser"),
+ new KeyValuePair("proxyPass", "-proxyPass"),
+ };
+
+ private static List> booleanCommands = new List>() {
+ new KeyValuePair("v", "-vvv"),
+ new KeyValuePair("force", "-force"),
+ new KeyValuePair("forcelocal", "-forcelocal"),
+ new KeyValuePair("forceproxy", "-forceproxy"),
+ new KeyValuePair("onlyAutomate", "-onlyAutomate"),
+ };
+ private readonly string LOG4NET_CONFIG_FILE_PATH = Path.Combine(Directory.GetCurrentDirectory(), "log_config.xml");
+
+ public bool isRunning()
+ {
+ if (tunnel == null) return false;
+ return tunnel.IsConnected();
+ }
+
+ private void addArgs(string key, string value)
+ {
+ KeyValuePair result;
+ key = key.Trim();
+
+ if (key.Equals("key"))
+ {
+ accessKey = value;
+ }
+ else if (key.Equals("f"))
+ {
+ folder = value;
+ }
+ else if (key.Equals("binarypath"))
+ {
+ customBinaryPath = value;
+ }
+ else if (key.Equals("logfile"))
+ {
+ customLogPath = value;
+ }
+ else if (key.Equals("verbose"))
+ {
+
+ }
+ else
+ {
+ result = valueCommands.Find(pair => pair.Key == key);
+ if (!result.Equals(emptyStringPair))
+ {
+ argumentString += result.Value + " " + value + " ";
+ return;
+ }
+
+ result = booleanCommands.Find(pair => pair.Key == key);
+ if (!result.Equals(emptyStringPair))
+ {
+ if (value.Trim().ToLower() == "true")
+ {
+ argumentString += result.Value + " ";
+ return;
+ }
+ }
+
+ if (value.Trim().ToLower() == "true")
+ {
+ argumentString += "-" + key + " ";
+ }
+ else
+ {
+ argumentString += "-" + key + " " + value + " ";
+ }
+ }
+ }
+ private void setupLogging()
+ {
+ hierarchy = (Hierarchy)LogManager.GetRepository();
+
+ patternLayout = new PatternLayout();
+ patternLayout.ConversionPattern = "%date [%thread] %-5level %logger - %message%newline";
+ patternLayout.ActivateOptions();
+
+ ConsoleAppender consoleAppender = new ConsoleAppender();
+ consoleAppender.Threshold = Level.Info;
+ consoleAppender.Layout = patternLayout;
+ consoleAppender.ActivateOptions();
+
+ LoggerMatchFilter loggerMatchFilter = new LoggerMatchFilter();
+ loggerMatchFilter.LoggerToMatch = "Local";
+ loggerMatchFilter.AcceptOnMatch = true;
+ consoleAppender.AddFilter(loggerMatchFilter);
+ consoleAppender.AddFilter(new DenyAllFilter());
+
+ hierarchy.Root.AddAppender(consoleAppender);
+
+ hierarchy.Root.Level = Level.All;
+ hierarchy.Configured = true;
+ }
+
+ public Local()
+ {
+ setupLogging();
+ tunnel = new BrowserStackTunnel();
+ }
+ public void start(List> options)
+ {
+ foreach (KeyValuePair pair in options)
+ {
+ string key = pair.Key;
+ string value = pair.Value;
+ addArgs(key, value);
+ }
+
+ if (accessKey == null || accessKey.Trim().Length == 0)
+ {
+ accessKey = Environment.GetEnvironmentVariable("BROWSERSTACK_ACCESS_KEY");
+ if (accessKey == null || accessKey.Trim().Length == 0)
+ {
+ throw new Exception("BROWSERSTACK_ACCESS_KEY cannot be empty. "+
+ "Specify one by adding key to options or adding to the environment variable BROWSERSTACK_ACCESS_KEY.");
+ }
+ Regex.Replace(this.accessKey, @"\s+", "");
+ }
+
+ if (customLogPath == null || customLogPath.Trim().Length == 0)
+ {
+ customLogPath = Path.Combine(BrowserStackTunnel.basePaths[1], "local.log");
+ }
+
+ argumentString += "-logFile " + customLogPath;
+ tunnel.addBinaryPath(customBinaryPath);
+ tunnel.addBinaryArguments(argumentString);
+ while (true) {
+ bool except = false;
+ try {
+ tunnel.Run(accessKey, folder, customLogPath);
+ } catch (Exception)
+ {
+ logger.Warn("Running Local failed. Falling back to backup path.");
+ except = true;
+ }
+ if (except)
+ {
+ tunnel.fallbackPaths();
+ } else
+ {
+ break;
+ }
+ }
+ }
+
+ public void stop()
+ {
+ if (tunnel != null)
+ {
+ tunnel.Kill();
+ }
+ }
+ }
+}
diff --git a/BrowserStack/BrowserStack/Properties/AssemblyInfo.cs b/BrowserStackLocal/BrowserStackLocal/Properties/AssemblyInfo.cs
similarity index 79%
rename from BrowserStack/BrowserStack/Properties/AssemblyInfo.cs
rename to BrowserStackLocal/BrowserStackLocal/Properties/AssemblyInfo.cs
index 561509f..0b95849 100644
--- a/BrowserStack/BrowserStack/Properties/AssemblyInfo.cs
+++ b/BrowserStackLocal/BrowserStackLocal/Properties/AssemblyInfo.cs
@@ -1,36 +1,36 @@
-using System.Reflection;
-using System.Runtime.CompilerServices;
-using System.Runtime.InteropServices;
-
-// General Information about an assembly is controlled through the following
-// set of attributes. Change these attribute values to modify the information
-// associated with an assembly.
-[assembly: AssemblyTitle("BrowserStack")]
-[assembly: AssemblyDescription("")]
-[assembly: AssemblyConfiguration("")]
-[assembly: AssemblyCompany("")]
-[assembly: AssemblyProduct("BrowserStack")]
-[assembly: AssemblyCopyright("Copyright © 2016")]
-[assembly: AssemblyTrademark("")]
-[assembly: AssemblyCulture("")]
-
-// Setting ComVisible to false makes the types in this assembly not visible
-// to COM components. If you need to access a type in this assembly from
-// COM, set the ComVisible attribute to true on that type.
-[assembly: ComVisible(false)]
-
-// The following GUID is for the ID of the typelib if this project is exposed to COM
-[assembly: Guid("f58e1c9a-5910-4da8-b531-9f4a6b0ae8c8")]
-
-// Version information for an assembly consists of the following four values:
-//
-// Major Version
-// Minor Version
-// Build Number
-// Revision
-//
-// You can specify all the values or you can default the Build and Revision Numbers
-// by using the '*' as shown below:
-// [assembly: AssemblyVersion("1.0.*")]
-[assembly: AssemblyVersion("1.0.0.0")]
-[assembly: AssemblyFileVersion("1.0.0.0")]
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("BrowserStackLocal")]
+[assembly: AssemblyDescription("C# Bindings for BrowserStack Local")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("BrowserStack")]
+[assembly: AssemblyProduct("BrowserStackLocal")]
+[assembly: AssemblyCopyright("Copyright © 2016")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Setting ComVisible to false makes the types in this assembly not visible
+// to COM components. If you need to access a type in this assembly from
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid("f58e1c9a-5910-4da8-b531-9f4a6b0ae8c8")]
+
+// Version information for an assembly consists of the following four values:
+//
+// Major Version
+// Minor Version
+// Build Number
+// Revision
+//
+// You can specify all the values or you can default the Build and Revision Numbers
+// by using the '*' as shown below:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("0.1.0.0")]
+[assembly: AssemblyFileVersion("0.1.0.0")]
diff --git a/BrowserStack/BrowserStack/packages.config b/BrowserStackLocal/BrowserStackLocal/packages.config
similarity index 59%
rename from BrowserStack/BrowserStack/packages.config
rename to BrowserStackLocal/BrowserStackLocal/packages.config
index 411fa8c..011d0c6 100644
--- a/BrowserStack/BrowserStack/packages.config
+++ b/BrowserStackLocal/BrowserStackLocal/packages.config
@@ -1,5 +1,4 @@
-
-
\ No newline at end of file
+
diff --git a/BrowserStackExample/BrowserStackExample.sln b/BrowserStackLocalExample/BrowserStackExample.sln
similarity index 97%
rename from BrowserStackExample/BrowserStackExample.sln
rename to BrowserStackLocalExample/BrowserStackExample.sln
index a20b5bf..bc373c8 100644
--- a/BrowserStackExample/BrowserStackExample.sln
+++ b/BrowserStackLocalExample/BrowserStackExample.sln
@@ -1,22 +1,22 @@
-
-Microsoft Visual Studio Solution File, Format Version 12.00
-# Visual Studio 14
-VisualStudioVersion = 14.0.23107.0
-MinimumVisualStudioVersion = 10.0.40219.1
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BrowserStackExample", "BrowserStackExample\BrowserStackExample.csproj", "{9297DCCC-AE88-4E12-934A-B2BEA4864B49}"
-EndProject
-Global
- GlobalSection(SolutionConfigurationPlatforms) = preSolution
- Debug|Any CPU = Debug|Any CPU
- Release|Any CPU = Release|Any CPU
- EndGlobalSection
- GlobalSection(ProjectConfigurationPlatforms) = postSolution
- {9297DCCC-AE88-4E12-934A-B2BEA4864B49}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {9297DCCC-AE88-4E12-934A-B2BEA4864B49}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {9297DCCC-AE88-4E12-934A-B2BEA4864B49}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {9297DCCC-AE88-4E12-934A-B2BEA4864B49}.Release|Any CPU.Build.0 = Release|Any CPU
- EndGlobalSection
- GlobalSection(SolutionProperties) = preSolution
- HideSolutionNode = FALSE
- EndGlobalSection
-EndGlobal
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio 14
+VisualStudioVersion = 14.0.23107.0
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BrowserStackExample", "BrowserStackExample\BrowserStackExample.csproj", "{9297DCCC-AE88-4E12-934A-B2BEA4864B49}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {9297DCCC-AE88-4E12-934A-B2BEA4864B49}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {9297DCCC-AE88-4E12-934A-B2BEA4864B49}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {9297DCCC-AE88-4E12-934A-B2BEA4864B49}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {9297DCCC-AE88-4E12-934A-B2BEA4864B49}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+EndGlobal
diff --git a/BrowserStackExample/BrowserStackExample/BrowserStackExample.csproj b/BrowserStackLocalExample/BrowserStackExample/BrowserStackExample.csproj
similarity index 97%
rename from BrowserStackExample/BrowserStackExample/BrowserStackExample.csproj
rename to BrowserStackLocalExample/BrowserStackExample/BrowserStackExample.csproj
index 6c1c4a2..7748f1b 100644
--- a/BrowserStackExample/BrowserStackExample/BrowserStackExample.csproj
+++ b/BrowserStackLocalExample/BrowserStackExample/BrowserStackExample.csproj
@@ -1,94 +1,94 @@
-
-
-
-
- Debug
- AnyCPU
- {9297DCCC-AE88-4E12-934A-B2BEA4864B49}
- Exe
- Properties
- BrowserStackExample
- BrowserStackExample
- v4.5
- 512
- publish\
- true
- Disk
- false
- Foreground
- 7
- Days
- false
- false
- true
- 0
- 1.0.0.%2a
- false
- false
- true
-
-
- AnyCPU
- true
- full
- false
- bin\Debug\
- DEBUG;TRACE
- prompt
- 4
-
-
- AnyCPU
- pdbonly
- true
- bin\Release\
- TRACE
- prompt
- 4
-
-
-
- ..\..\BrowserStack\BrowserStack\bin\Debug\BrowserStack.dll
-
-
-
-
-
-
-
-
-
-
-
- ..\packages\Selenium.WebDriver.2.52.0\lib\net40\WebDriver.dll
- True
-
-
-
-
-
-
-
-
-
-
-
- False
- Microsoft .NET Framework 4.5 %28x86 and x64%29
- true
-
-
- False
- .NET Framework 3.5 SP1
- false
-
-
-
-
+
+
+
+
+ Debug
+ AnyCPU
+ {9297DCCC-AE88-4E12-934A-B2BEA4864B49}
+ Exe
+ Properties
+ BrowserStackExample
+ BrowserStackExample
+ v4.5
+ 512
+ publish\
+ true
+ Disk
+ false
+ Foreground
+ 7
+ Days
+ false
+ false
+ true
+ 0
+ 1.0.0.%2a
+ false
+ false
+ true
+
+
+ AnyCPU
+ true
+ full
+ false
+ bin\Debug\
+ DEBUG;TRACE
+ prompt
+ 4
+
+
+ AnyCPU
+ pdbonly
+ true
+ bin\Release\
+ TRACE
+ prompt
+ 4
+
+
+
+ ..\..\BrowserStack\BrowserStack\bin\Debug\BrowserStack.dll
+
+
+
+
+
+
+
+
+
+
+
+ ..\packages\Selenium.WebDriver.2.52.0\lib\net40\WebDriver.dll
+ True
+
+
+
+
+
+
+
+
+
+
+
+ False
+ Microsoft .NET Framework 4.5 %28x86 and x64%29
+ true
+
+
+ False
+ .NET Framework 3.5 SP1
+ false
+
+
+
+
\ No newline at end of file
diff --git a/BrowserStackExample/BrowserStackExample/Example.cs b/BrowserStackLocalExample/BrowserStackExample/Example.cs
similarity index 97%
rename from BrowserStackExample/BrowserStackExample/Example.cs
rename to BrowserStackLocalExample/BrowserStackExample/Example.cs
index f3cf910..89bd745 100644
--- a/BrowserStackExample/BrowserStackExample/Example.cs
+++ b/BrowserStackLocalExample/BrowserStackExample/Example.cs
@@ -1,53 +1,53 @@
-using System;
-using BrowserStack;
-using System.Collections.Generic;
-using OpenQA.Selenium;
-using OpenQA.Selenium.Remote;
-
-namespace BrowserStackExample
-{
- class Example
- {
- static void Main(string[] args)
- {
- Local local = new Local();
-
- List> options = new List>() {
- new KeyValuePair("key", BROWSERSTACK_ACCESS_KEY),
- new KeyValuePair("localIdentifier", "identifier"),
- //new KeyValuePair("f", "C:\\Users\\Admin\\Desktop\\"),
- new KeyValuePair("onlyAutomate", "true"),
- new KeyValuePair("verbose", "true"),
- new KeyValuePair("forcelocal", "true"),
- new KeyValuePair("binarypath", "C:\\Users\\Admin\\Desktop\\BrowserStackLocal.exe"),
- new KeyValuePair("logfile", "C:\\Users\\Admin\\Desktop\\local.log"),
- };
- local.start(options);
-
- // Run WebDriver Tests
- IWebDriver driver;
- DesiredCapabilities capability = DesiredCapabilities.Firefox();
- capability.SetCapability("browserstack.user", BROWSERSTACK_USERNAME);
- capability.SetCapability("browserstack.key", BROWSERSTACK_ACCESS_KEY);
- capability.SetCapability("browserstack.localIdentifier", "identifier");
- capability.SetCapability("browserstack.local", true);
- capability.SetCapability("build", "build");
-
- driver = new RemoteWebDriver(
- new Uri("http://hub.browserstack.com/wd/hub/"), capability
- );
- driver.Navigate().GoToUrl("http://www.google.com");
- Console.WriteLine(driver.Title);
-
- IWebElement query = driver.FindElement(By.Name("q"));
- query.SendKeys("Browserstack");
- query.Submit();
- Console.WriteLine(driver.Title);
-
- driver.Quit();
- local.stop();
- Console.WriteLine("Test Completed.");
- Console.ReadLine();
- }
- }
-}
+using System;
+using BrowserStack;
+using System.Collections.Generic;
+using OpenQA.Selenium;
+using OpenQA.Selenium.Remote;
+
+namespace BrowserStackExample
+{
+ class Example
+ {
+ static void Main(string[] args)
+ {
+ Local local = new Local();
+
+ List> options = new List>() {
+ new KeyValuePair("key", BROWSERSTACK_ACCESS_KEY),
+ new KeyValuePair("localIdentifier", "identifier"),
+ //new KeyValuePair("f", "C:\\Users\\Admin\\Desktop\\"),
+ new KeyValuePair("onlyAutomate", "true"),
+ new KeyValuePair("verbose", "true"),
+ new KeyValuePair("forcelocal", "true"),
+ new KeyValuePair("binarypath", "C:\\Users\\Admin\\Desktop\\BrowserStackLocal.exe"),
+ new KeyValuePair("logfile", "C:\\Users\\Admin\\Desktop\\local.log"),
+ };
+ local.start(options);
+
+ // Run WebDriver Tests
+ IWebDriver driver;
+ DesiredCapabilities capability = DesiredCapabilities.Firefox();
+ capability.SetCapability("browserstack.user", BROWSERSTACK_USERNAME);
+ capability.SetCapability("browserstack.key", BROWSERSTACK_ACCESS_KEY);
+ capability.SetCapability("browserstack.localIdentifier", "identifier");
+ capability.SetCapability("browserstack.local", true);
+ capability.SetCapability("build", "build");
+
+ driver = new RemoteWebDriver(
+ new Uri("http://hub.browserstack.com/wd/hub/"), capability
+ );
+ driver.Navigate().GoToUrl("http://www.google.com");
+ Console.WriteLine(driver.Title);
+
+ IWebElement query = driver.FindElement(By.Name("q"));
+ query.SendKeys("Browserstack");
+ query.Submit();
+ Console.WriteLine(driver.Title);
+
+ driver.Quit();
+ local.stop();
+ Console.WriteLine("Test Completed.");
+ Console.ReadLine();
+ }
+ }
+}
diff --git a/BrowserStackExample/BrowserStackExample/Properties/AssemblyInfo.cs b/BrowserStackLocalExample/BrowserStackExample/Properties/AssemblyInfo.cs
similarity index 97%
rename from BrowserStackExample/BrowserStackExample/Properties/AssemblyInfo.cs
rename to BrowserStackLocalExample/BrowserStackExample/Properties/AssemblyInfo.cs
index 39be242..b4a8a7c 100644
--- a/BrowserStackExample/BrowserStackExample/Properties/AssemblyInfo.cs
+++ b/BrowserStackLocalExample/BrowserStackExample/Properties/AssemblyInfo.cs
@@ -1,36 +1,36 @@
-using System.Reflection;
-using System.Runtime.CompilerServices;
-using System.Runtime.InteropServices;
-
-// General Information about an assembly is controlled through the following
-// set of attributes. Change these attribute values to modify the information
-// associated with an assembly.
-[assembly: AssemblyTitle("ConsoleApplication1")]
-[assembly: AssemblyDescription("")]
-[assembly: AssemblyConfiguration("")]
-[assembly: AssemblyCompany("")]
-[assembly: AssemblyProduct("ConsoleApplication1")]
-[assembly: AssemblyCopyright("Copyright © 2016")]
-[assembly: AssemblyTrademark("")]
-[assembly: AssemblyCulture("")]
-
-// Setting ComVisible to false makes the types in this assembly not visible
-// to COM components. If you need to access a type in this assembly from
-// COM, set the ComVisible attribute to true on that type.
-[assembly: ComVisible(false)]
-
-// The following GUID is for the ID of the typelib if this project is exposed to COM
-[assembly: Guid("9297dccc-ae88-4e12-934a-b2bea4864b49")]
-
-// Version information for an assembly consists of the following four values:
-//
-// Major Version
-// Minor Version
-// Build Number
-// Revision
-//
-// You can specify all the values or you can default the Build and Revision Numbers
-// by using the '*' as shown below:
-// [assembly: AssemblyVersion("1.0.*")]
-[assembly: AssemblyVersion("1.0.0.0")]
-[assembly: AssemblyFileVersion("1.0.0.0")]
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("ConsoleApplication1")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("ConsoleApplication1")]
+[assembly: AssemblyCopyright("Copyright © 2016")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Setting ComVisible to false makes the types in this assembly not visible
+// to COM components. If you need to access a type in this assembly from
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid("9297dccc-ae88-4e12-934a-b2bea4864b49")]
+
+// Version information for an assembly consists of the following four values:
+//
+// Major Version
+// Minor Version
+// Build Number
+// Revision
+//
+// You can specify all the values or you can default the Build and Revision Numbers
+// by using the '*' as shown below:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
diff --git a/BrowserStackExample/BrowserStackExample/packages.config b/BrowserStackLocalExample/BrowserStackExample/packages.config
similarity index 100%
rename from BrowserStackExample/BrowserStackExample/packages.config
rename to BrowserStackLocalExample/BrowserStackExample/packages.config
diff --git a/MIT-LICENSE.txt b/MIT-LICENSE.txt
new file mode 100644
index 0000000..e070d10
--- /dev/null
+++ b/MIT-LICENSE.txt
@@ -0,0 +1,22 @@
+The MIT License (MIT)
+
+Copyright (c) 2016 BrowserStack
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+
diff --git a/README.md b/README.md
index ffa1094..d1e7f82 100644
--- a/README.md
+++ b/README.md
@@ -2,7 +2,7 @@
[![Build Status](https://travis-ci.org/browserstack/browserstack-local-csharp.svg?branch=master)](https://travis-ci.org/browserstack/browserstack-local-csharp)
-A simple C-sharp wrapper for BrowserStack Local Binary.
+C# bindings for BrowserStack Local.
## Setup
@@ -111,6 +111,10 @@ bsLocalArgs.Add(new KeyValuePair("logfile", "/browserstack/logs.
To run the test suite run the nunit tests from Visual Studio.
+### Packaging
+
+To pack using nuget, run `nuget pack BrowserStackLocal\BrowserStackLocal\BrowserStackLocal.csproj -Prop Configuration=Release`
+
### Reporting bugs
You can submit bug reports either in the Github issue tracker.