コード例 #1
0
        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);
        }