static Dictionary <string, TestResult> ShardAndCollect(List <string> testNames, out List <Covered> covered) { var finished = 0; // all this just so we can make sure the subprocesses die when their parent does var _ = new Extern.SECURITY_ATTRIBUTES { }; var job = Extern.CreateJobObject(ref _, "LinqAF.TestRunner"); var info = new Extern.JOBOBJECT_BASIC_LIMIT_INFORMATION(); info.LimitFlags = 0x2000; // JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE var extendedInfo = new Extern.JOBOBJECT_EXTENDED_LIMIT_INFORMATION(); extendedInfo.BasicLimitInformation = info; int length = Marshal.SizeOf(typeof(Extern.JOBOBJECT_EXTENDED_LIMIT_INFORMATION)); var extendedInfoPtr = Marshal.AllocHGlobal(length); Marshal.StructureToPtr(extendedInfo, extendedInfoPtr, false); if (!Extern.SetInformationJobObject(job, Extern.JobObjectInfoType.ExtendedLimitInformation, extendedInfoPtr, (uint)length)) { throw new Exception("Wat"); } var results = new Dictionary <string, TestResult>(); var runningProcesses = new List <Tuple <Process, Thread, Thread> >(); var pendingProcessStarts = new Queue <ProcessStartInfo>(); string currentProcFile; using (var proc = Process.GetCurrentProcess()) { currentProcFile = proc.MainModule.FileName; } var coveredRefLock = new object(); IEnumerable <Covered> coveredRef = null; foreach (var name in testNames) { var procStart = new ProcessStartInfo { FileName = currentProcFile, Arguments = name, CreateNoWindow = true, RedirectStandardError = true, RedirectStandardOutput = true, UseShellExecute = false }; pendingProcessStarts.Enqueue(procStart); } Func <int> getPendingProcessStartsCount = () => { lock (pendingProcessStarts) { return(pendingProcessStarts.Count); } }; while (getPendingProcessStartsCount() > 0 || runningProcesses.Count > 0) { Thread.Sleep(TimeSpan.FromSeconds(1)); for (var i = runningProcesses.Count - 1; i >= 0; i--) { var t = runningProcesses[i]; var proc = t.Item1; var outThread = t.Item2; var errThread = t.Item3; if (!outThread.IsAlive && !errThread.IsAlive && proc.HasExited) { runningProcesses.RemoveAt(i); proc.Dispose(); } } if (runningProcesses.Count >= UseProceses()) { continue; } if (getPendingProcessStartsCount() > 0) { ProcessStartInfo next; lock (pendingProcessStarts) { next = pendingProcessStarts.Dequeue(); } var newProc = Process.Start(next); Extern.AssignProcessToJobObject(job, newProc.Handle); if (Debugger.IsAttached) { AttachDebugger(newProc); } var outThread = new Thread( () => { while (!newProc.HasExited) { // need to read it off, but needn't actually print it var toWrite = newProc.StandardOutput.ReadLine(); //Console.WriteLine($"[Proc: {newProc.Id}]: {toWrite}"); } } ); var errThread = new Thread( () => { var needsHandling = new StringBuilder(); while (!newProc.HasExited) { int c; while ((c = newProc.StandardError.Read()) != -1) { needsHandling.Append((char)c); } } var json = needsHandling.ToString(); dynamic data; try { data = JSON.DeserializeDynamic(json); } catch (Exception e) { Console.WriteLine($"[Proc: {newProc.Id}] ({next.Arguments}) ~~CRASHED~~"); Console.WriteLine(e.Message); Console.WriteLine(e.StackTrace); Console.WriteLine("Rescheduling"); lock (pendingProcessStarts) { pendingProcessStarts.Enqueue(next); } return; } foreach (var entry in data) { string name = entry.TestName; string errorMessage = entry.ErrorMessage; string testCoverageEncoded = entry.TestCoverageEncoded; lock (results) { results[name] = new TestResult { ErrorMessage = errorMessage, Coverage = null }; } double coverageRate; var parsedCoverage = Covered.DecodeList(testCoverageEncoded); if (parsedCoverage != null) { lock (coveredRefLock) { if (coveredRef == null) { coveredRef = parsedCoverage; coverageRate = double.NaN; } else { coveredRef = Covered.Merge(coveredRef.Concat(parsedCoverage).AsEnumerable()); } var total100 = coveredRef.Count(c => c.CoveragePercent >= 100); var total = coveredRef.Count(); coverageRate = Math.Round(((double)total100) / (double)total * 100.0, 1); } } else { coverageRate = double.NaN; } var num = Interlocked.Increment(ref finished); if (errorMessage == null) { if (!double.IsNaN(coverageRate)) { Console.WriteLine($"[Proc: {newProc.Id}] ({num}/{testNames.Count}): {name} PASSED (covered methods: {coverageRate}%)"); } else { Console.WriteLine($"[Proc: {newProc.Id}] ({num}/{testNames.Count}): {name} PASSED"); } } else { Console.WriteLine($"[Proc: {newProc.Id}] ({num}/{testNames.Count}): {name} !!FAILED!!"); } } } ); outThread.IsBackground = true; errThread.IsBackground = true; outThread.Start(); errThread.Start(); runningProcesses.Add(Tuple.Create(newProc, outThread, errThread)); } } covered = coveredRef?.ToList(); return(results); }