#define UseLocallyBuiltRCCService using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Diagnostics; using watrbx.wtfmon; using System.Net; using System.Net.Sockets; using System.Threading; using System.IO; namespace RCCService.Test { public static class RCCLauncher { private const string buildType = "Mixed Platforms"; private const string buildTarget = "Release"; #if UseLocallyBuiltRCCService // use for local tests: internal static string RCCServicePath = @"..\..\..\RccService\bin\" + buildTarget + @"\RccService.exe"; // use for automatic tfs build tests: //internal static string RCCServicePath = @"..\..\..\Binaries\Mixed Platforms\" + buildTarget + @"\RCCService.exe"; // use for local tests: internal static string RCCServiceArgs = "/console /content:..\\..\\..\\content\\ {0} "; // note: content for testing resides in: // "C:\Users\Administrator\AppData\Local\Temp\Doblox2\client_mixed\Binaries\Mixed Platforms\Debug" // test invoked from: // C:\Users\Administrator\AppData\Local\Temp\Doblox2\client_mixed\TestResults\Administrator_VOSTRO420 2009-07-01 11_45_00_Mixed Platforms_Debug\Out //internal static string RCCServiceArgs = "/console /content:..\\..\\..\\..\\..\\..\\..\\..\\..\\..\\share\\content\\ {0} "; #else private const string RCCServicePath = @"S:\Automation\RCCService\bin\" + buildTarget + @"\RCCService.exe"; private const string RCCServiceArgs = "/console {0} "; #endif internal static string RemoteRCCServicePath = RCCServicePath; internal static string RemoteRCCServiceArgs = RCCServiceArgs; private const int rigPort = 8000; private const int portBase = 64989; private const int portIncrement = -1; private static int nextAvailablePort = portBase; static private HashSet DeadServicesSet = new HashSet(); static private HashSet PendingServicesSet = new HashSet(); static private Dictionary RCCServicesMap = new Dictionary(); static public bool IsServiceAvailable(string host, int port, int retrycount) { // busy wait untill our service is ready to accept connections bool bAvailable = false; int waitcount = 0; while (!bAvailable) { System.Net.Sockets.TcpClient tcp = new TcpClient(); try { tcp.Connect(host, port); tcp.Close(); bAvailable = true; } catch (System.Net.Sockets.SocketException) { if (++waitcount > retrycount) { bAvailable = false; break; } Thread.Sleep(1000); } } return bAvailable; } static public Uri StartRCCService() { int port = nextAvailablePort; nextAvailablePort += portIncrement; /* * FIND THE CORRECT SERVICE AND ARGS PATH FOR LOCAL OR BUILD MACHINE RUN */ const string buildTarget = "Release"; string[] RCCSearchPaths = new string[] { System.Environment.GetEnvironmentVariable("RCCsHappyHome", EnvironmentVariableTarget.Machine) + @"\RccService\Win32\" + buildTarget + @"\RccService.exe", @"..\..\..\..\RccService\Win32\" + buildTarget + @"\RccService.exe", @"..\..\..\RccService\Win32\" + buildTarget + @"\RccService.exe", @"..\..\..\Binaries\Mixed Platforms\" + buildTarget + @"\RccService.exe" }; const string args_LOCAL = "/console /content:..\\..\\..\\content\\ {0} "; bool found = false; foreach (string path in RCCSearchPaths) { if (File.Exists(path)) { RCCLauncher.RCCServicePath = path; RCCLauncher.RCCServiceArgs = args_LOCAL; RCCLauncher.RemoteRCCServicePath = path; RCCLauncher.RemoteRCCServiceArgs = args_LOCAL; found = true; break; } } if (found) { Process proc = new Process(); proc.StartInfo.FileName = RCCServicePath; // AutoRCCTest.RCCServicePath; proc.StartInfo.Arguments = String.Format(RCCServiceArgs, port); string dirr = Directory.GetCurrentDirectory(); /* // write to a file the directory location System.IO.StreamWriter file = new System.IO.StreamWriter("c:\\dirr.txt"); file.WriteLine(dirr); file.Close(); */ proc.Start(); Uri result = new Uri("http://" + TestUtils.GetLocalIpAddress().ToString() + ":" + port.ToString()); if (!IsServiceAvailable(result.Host, result.Port, 40)) { throw new Exception("Unable to connect to Launched RCCService"); } RCCServicesMap.Add(result, proc); return result; } throw new Exception("Cannot find RccService"); } // for tests that don't care if they reuse a service. // (they should not close the service either, for this to be useful) static public Uri FindOrStartRCCService() { Uri result2 = new Uri("http://" + TestUtils.GetLocalIpAddress().ToString() + ":64989"); //Uri result2 = new Uri("http://localhost.:64989"); if (IsServiceAvailable(result2.Host, result2.Port, 20)) { return result2; } if (RCCServicesMap.Count == 0) { return StartRCCService(); } else { return RCCServicesMap.First().Key; } } delegate Uri getOneDelegate(); static public List FindNMoreRemoteRCCServices(int n) { List results = new List(); List urilist = new List(); getOneDelegate getOne = delegate() { return FindRemoteRCCService(); }; int pendingDelegates = 0; int bNeedMore = n - (urilist.Count + pendingDelegates); while (bNeedMore > 0) { // make sure we always have enough pending calls to cover our dept. while ((bNeedMore - pendingDelegates) > 0) { results.Add(getOne.BeginInvoke(null, null)); pendingDelegates++; } // process results. for (int i = results.Count - 1; i != -1; --i) { if (results[i].IsCompleted) { urilist.Add(getOne.EndInvoke(results[i])); results.RemoveAt(i); pendingDelegates--; } } lock (Lists.remoteServiceUris) { bNeedMore = n - urilist.Count; } } return urilist; } static public Uri FindRemoteRCCService() { for (int i = 0; i < Lists.remoteServiceUris.Count(); ++i) { Uri testinguri; lock (Lists.remoteServiceUris) { if (!RCCServicesMap.ContainsKey(Lists.remoteServiceUris[i]) && !DeadServicesSet.Contains(Lists.remoteServiceUris[i]) && !PendingServicesSet.Contains(Lists.remoteServiceUris[i])) { testinguri = Lists.remoteServiceUris[i]; PendingServicesSet.Add(testinguri); } else { continue; } } { if (IsServiceAvailable(testinguri.Host, testinguri.Port, 0)) { lock (Lists.remoteServiceUris) { RCCServicesMap.Add(testinguri, null); PendingServicesSet.Remove(testinguri); } return testinguri; } else if (IsServiceAvailable(testinguri.Host, rigPort, 0)) { // service is not up, but test rig is available. // spawn a rccservice. StartRemoteRCCService(testinguri); lock (Lists.remoteServiceUris) { RCCServicesMap.Add(testinguri, null); PendingServicesSet.Remove(testinguri); } return testinguri; } else { Console.WriteLine(testinguri + " is dead."); // skip pinging this one next time. lock (Lists.remoteServiceUris) { DeadServicesSet.Add(testinguri); PendingServicesSet.Remove(testinguri); } } } } throw new Exception("Not enough available services to fulfull request. Try adding/correcting the remoteServiceUris list"); } static public void StartRemoteRCCService(Uri uri) { string addressFormat = "http://{0}:8000/Roblox.Test/RunService"; string result; int pid = 0; Roblox.Test.Client.RunClient runclient = new Roblox.Test.Client.RunClient("BasicHttpBinding_IRun", String.Format(addressFormat, uri.Host)); result = runclient.SpawnProcess(out pid, RemoteRCCServicePath, String.Format(RemoteRCCServiceArgs, uri.Port)); Console.WriteLine(result); if (!IsServiceAvailable(uri.Host, uri.Port, 20)) { throw new Exception("Unable to connect to Launched RCCService"); } } static public void StopRCCService(Uri uri) { Process proc = RCCServicesMap[uri]; if (proc != null && !proc.HasExited) { proc.Kill(); } RCCServicesMap.Remove(uri); } static public void StopAllRCCServices() { foreach (System.Collections.Generic.KeyValuePair item in RCCServicesMap) { if (item.Value != null) { item.Value.Kill(); } } RCCServicesMap.Clear(); nextAvailablePort = portBase; } } }