/// <summary>
        /// Removes the cached runner for the specified run and configuration.
        /// </summary>
        /// <param name="run">The run.</param>
        /// <param name="configurationSubstitutions">The configuration substitutions.</param>
        public void Remove(TestRun run, DistributedConfigurationSubstitutions configurationSubstitutions = null)
        {
            ConcurrentDictionary<int, TestRunnerMetadata> byParametersCache;
            if (configurationSubstitutions == null
                ? cache.TryRemove(GetKey(run), out byParametersCache)
                : cache.TryGetValue(GetKey(run), out byParametersCache))
            {
                IEnumerable<TestRunnerMetadata> toFree = new TestRunnerMetadata[0];
                if (configurationSubstitutions != null)
                {
                    TestRunnerMetadata metadata;
                    if (byParametersCache.TryRemove(GetKeyForSubstitutions(configurationSubstitutions), out metadata))
                    {
                        toFree = new[] { metadata };
                    }
                }
                else
                    toFree = byParametersCache.Values;

                foreach (var metadata in toFree)
                {
                    metadata.Runner.Unload();
                    metadata.Runner.Dispose();
                }
            }
        }
        /// <summary>
        /// Gets the or load.
        /// </summary>
        /// <param name="testRun">The test run.</param>
        /// <param name="substitutions"></param>
        /// <param name="loadRunner">The action.</param>
        /// <returns></returns>
        public NDistribUnitProcessRunner GetOrLoad(TestRun testRun, DistributedConfigurationSubstitutions substitutions, Func<NDistribUnitProcessRunner> loadRunner)
        {
            var key = GetKey(testRun);
            //var timeSpan = TimeSpan.FromHours(4);
            var cacheByParameters = cache.GetOrAdd(key, guid => new ConcurrentDictionary<int, TestRunnerMetadata>());
            int hashCode = GetKeyForSubstitutions(substitutions);

            var metadata = cacheByParameters.GetOrAdd(hashCode, hash =>
                                                 {
                                                     var runner = loadRunner();
            //                                                     var timer = new Timer(obj=>
            //                                                                               {
            //                                                                                   TestRunnerMetadata removed;
            //                                                                                   if (cacheByParameters.TryRemove(hashCode, out removed))
            //                                                                                       removed.Runner.Unload();
            //                                                                               }, null, timeSpan, TimeSpan.FromMilliseconds(-1));
                                                     return new TestRunnerMetadata
                                                                {
                                                                    Runner = runner,
            //                                                                    Timer = timer
                                                                };
                                                 });
            //            metadata.Timer.Change(timeSpan, TimeSpan.FromMilliseconds(-1));
            return metadata.Runner;
        }
        /// <summary>
        /// Gets the substituted configuration file.
        /// </summary>
        /// <param name="project">The project.</param>
        /// <param name="nUnitParameters">The n unit parameters.</param>
        /// <param name="configurationSubstitutions">The configuration substitutions.</param>
        /// <returns></returns>
        public string GetSubstitutedConfigurationFile(TestProject project, NUnitParameters nUnitParameters, DistributedConfigurationSubstitutions configurationSubstitutions)
        {
            var distributedConfigurationFileNames = GetDistributedConfigurationFileNames(project, nUnitParameters);

            if (configurationSubstitutions == null || configurationSubstitutions.Variables.Count == 0)
                return distributedConfigurationFileNames.FirstOrDefault();

            string finalConfigurationFile = null;
            int hashCode = configurationSubstitutions.GetHashCode();
            foreach (var configurationFileName in distributedConfigurationFileNames)
            {
                var configurationDocument = XDocument.Load(configurationFileName);
                SubstitutePlaceHolders(configurationDocument, configurationSubstitutions);
                var substitutedFileName = Path.ChangeExtension(configurationFileName,
                                     string.Format(".{0}.config", hashCode));
                configurationDocument.Save(substitutedFileName);

                if (finalConfigurationFile == null)
                    finalConfigurationFile = substitutedFileName;
            }

            return finalConfigurationFile;
        }
 private static int GetKeyForSubstitutions(DistributedConfigurationSubstitutions substitutions)
 {
     return substitutions == null ? 0 : substitutions.GetHashCode();
 }
 /// <summary>
 /// Runs tests on agent
 /// </summary>
 /// <param name="test">The test.</param>
 /// <param name="configurationSubstitutions">The configuration substitutions.</param>
 /// <returns></returns>
 public TestResult RunTests(TestUnit test, DistributedConfigurationSubstitutions configurationSubstitutions)
 {
     throw new CommunicationException();
 }
        internal void SubstitutePlaceHolders(XDocument doc, DistributedConfigurationSubstitutions configurationSubstitutions)
        {
            var descendantComments = doc.DescendantNodes().OfType<XComment>();

            var comments = (from comment in descendantComments
                            let match = TypeIdentifier.Match(comment.Value)
                            where match.Success
                            select new { XComment = comment, Match = match }).ToList();

            foreach (var comment in comments)
            {
                if (!comment.Match.Groups["selector"].Value.Equals("Replace"))
                    continue;

                var parameters = new JavaScriptSerializer().DeserializeObject(comment.Match.Groups["object"].Value) as Dictionary<string, object>;
                var xPath = parameters["XPath"] as string;
                var value = parameters["Value"] as string;

                var node = (comment.XComment.XPathEvaluate(xPath) as IEnumerable<object>).FirstOrDefault();
                if (node is XElement)
                    ((XElement)node).SetValue(value);
                else if (node is XAttribute)
                    ((XAttribute)node).SetValue(value);
                comment.XComment.Remove();
            }

            var xmlAsString = doc.ToString(); // not efficient but clear and ok for usually small configs
            xmlAsString = Variable.Replace(xmlAsString, match =>
                                              {
                                                  var variableName = match.Groups["varName"].Value;
                                                  var variable = configurationSubstitutions.Variables
                                                      .FirstOrDefault(v => v.Name.Equals(variableName));

                                                  if (variable == null)
                                                     return match.Value;

                                                  return variable.Value;
                                              });
            xmlAsString = Escaping.Replace(xmlAsString, string.Empty);
            var doc2 = XDocument.Parse(xmlAsString);
            doc.ReplaceNodes(doc2.Nodes());
        }
        /// <summary>
        /// Gets the NUnit test result.
        /// </summary>
        /// <param name="test">The test.</param>
        /// <param name="project">The project.</param>
        /// <param name="configurationSubstitutions">The configuration substitutions.</param>
        /// <param name="isChild">if set to <c>true</c> [is child].</param>
        /// <returns></returns>
        public TestResult GetNUnitTestResult(TestUnit test, TestProject project, DistributedConfigurationSubstitutions configurationSubstitutions, bool isChild = false)
        {
            var nativeRunner = runnerCache.GetOrLoad(test.Run, configurationSubstitutions,
                                                     () =>
                                                         {
                                                             initializer.Initialize();

                                                             string configurationFileName =
                                                                 configurationOperator.GetSubstitutedConfigurationFile(project,
                                                                                                            test.Run.
                                                                                                                NUnitParameters,
                                                                                                            configurationSubstitutions);

                                                             var mappedAssemblyFile = Path.Combine(project.Path,
                                                                                                   Path.GetFileName(
                                                                                                       test.Run.NUnitParameters.
                                                                                                           AssembliesToTest[0]));

                                                             TestPackage package;
                                                             if (!NUnitProject.IsNUnitProjectFile(mappedAssemblyFile))
                                                                 package = new TestPackage(mappedAssemblyFile);
                                                             else
                                                             {
                                                                 var nunitProject = new NUnitProject(mappedAssemblyFile);
                                                                 nunitProject.Load();
                                                                 if (!string.IsNullOrEmpty(test.Run.NUnitParameters.Configuration))
                                                                     nunitProject.SetActiveConfig(test.Run.NUnitParameters.Configuration);

                                                                 package = nunitProject.ActiveConfig.MakeTestPackage();
                                                             }

                                                             package.Settings["ShadowCopyFiles"] = true;
                                                             package.AutoBinPath = true;
                                                             if (!string.IsNullOrEmpty(configurationFileName))
                                                             {
                                                                 package.ConfigurationFile = configurationFileName;
                                                             }

                                                             var nativeTestRunner = new NDistribUnitProcessRunner(log);
                                                             nativeTestRunner.Load(package);
                                                             return nativeTestRunner;
                                                         });

            var testOptions = test.Run.NUnitParameters;
            TestResult testResult = null;

            try
            {
                Action runTest = ()=>
                                     {
                                         try
                                         {
                                             testResult = nativeRunner.Run(new NullListener(),
                                                                           new NUnitTestsFilter(
                                                                               testOptions.IncludeCategories,
                                                                               testOptions.ExcludeCategories,
                                                                               test.UniqueTestId).NativeFilter);
                                             nativeRunner.CleanupAfterRun();
                                         }
                                         //TODO: remove this. This is for tracking purposes only
                                         catch(AppDomainUnloadedException ex)
                                         {
                                             log.Warning("AppDomainUnloadedException is still being thrown", ex);
                                             if (!isChild)
                                             {
                                                 runnerCache.Remove(test.Run, configurationSubstitutions);
                                                 testResult = GetNUnitTestResult(test, project,
                                                                                 configurationSubstitutions,
                                                                                 isChild: true);
                                             }
                                             else
                                                 throw;
                                         }
                                     };

                if (Thread.CurrentThread.GetApartmentState() != ApartmentState.STA
                    && !Thread.CurrentThread.TrySetApartmentState(ApartmentState.STA))
                {
                    var thread = new Thread(()=> exceptionCatcher.Run(runTest));
                    thread.SetApartmentState(ApartmentState.STA);
                    thread.Start();
                    thread.Join();
                }
                else
                {
                    runTest();
                }
            }
            catch (Exception ex)
            {
                log.Error("Error while running test on agent", ex);
                throw;
            }
            return testResult;
        }
        /// <summary>
        /// Runs the specified test.
        /// </summary>
        /// <param name="test">The test.</param>
        /// <param name="configurationSubstitutions"></param>
        /// <returns></returns>
        public TestResult Run(TestUnit test, DistributedConfigurationSubstitutions configurationSubstitutions)
        {
            var project = projects.Get(test.Run);
            if (project == null)
            {
                log.Info("Project file was not found. Throwing exception");
                return TestResultFactory.GetProjectRetrievalFailure(test);
            }

            log.BeginActivity("Starting test execution...");
            var nUnitTestResult = GetNUnitTestResult(test, project, configurationSubstitutions);
            log.EndActivity("Test execution was finished");

            return nUnitTestResult;
        }
        private DistributedConfigurationSubstitutions GetConfigurationValues(DistributedConfigurationSetup configurationSetup,
                                                                      AgentMetadata agentToRun,
                                                                      TestUnitWithMetadata testToRun)
        {
            //BUG: add dependency on test run!
            return configurations.GetOrAdd(agentToRun.Name,
                                    key =>
                                        {
                                            var distributedConfigurationValues = new DistributedConfigurationSubstitutions();

                                            foreach (var variable in configurationSetup.Variables)
                                            {
                                                distributedConfigurationValues.Variables.Add(
                                                    new DistributedConfigurationVariablesValue(variable.Name, variable.GetNextValue()));
                                            }

                                            return distributedConfigurationValues;
                                        });
        }
Beispiel #10
0
        /// <summary>
        /// Runs tests on agent
        /// </summary>
        /// <param name="test">The test.</param>
        /// <param name="configurationSubstitutions">The configuration substitutions.</param>
        /// <returns></returns>
        public TestResult RunTests(TestUnit test, DistributedConfigurationSubstitutions configurationSubstitutions)
        {
            if (IsStarted)
                return TestRunner.RunTests(test, configurationSubstitutions);

            throw new CommunicationException("Agent seems to be not available");
        }
        // Should be started in a separate thread
        private void Run(TestUnitWithMetadata test, AgentMetadata agent, DistributedConfigurationSubstitutions configurationSubstitutions)
        {
            log.BeginActivity(string.Format("[{0}] Started test {1}", test.Test.Run, test.Test.Info.TestName.FullName));
            var request = requests.GetBy(test.Test.Run);
            if (request != null)
                request.Status = TestRunRequestStatus.Pending;
            try
            {
                log.BeginActivity(string.Format("Connecting to agent [{0}]...", agent));
                var testRunnerAgent = connectionProvider.GetConnection<IAgent>(agent.Address);
                log.BeginActivity(string.Format("Connected to agent [{0}]", agent));

                log.BeginActivity(string.Format("Checking project existence ('{0}') on agent {1}", test.Test.Run, agent));
                if (!testRunnerAgent.HasProject(test.Test.Run))
                {
                    log.EndActivity(string.Format("Project '{0}' doesn't exist on agent {1}", test.Test.Run, agent));

                    log.BeginActivity(string.Format("Sending project ('{0}') to agent {1}", test.Test.Run, agent));
                    using (Stream project = projects.GetStreamToPacked(test.Test.Run) ?? new MemoryStream(1))
                    {
                        testRunnerAgent.ReceiveProject(new ProjectMessage
                                                           {
                                                               TestRun = test.Test.Run,
                                                               Project = project
                                                           });
                    }
                    log.EndActivity(string.Format("Sent project ('{0}') to agent {1}", test.Test.Run, agent));
                }
                else
                    log.EndActivity(string.Format("Project '{0}' exist on agent {1}", test.Test.Run, agent));

                var reprocessedCount = request.Statistics.ReprocessedCount;
                log.BeginActivity(string.Format("[{3}/{4}]: Running {0} on {1} with variables ({2})...", test.Test.UniqueTestId, agent, configurationSubstitutions,
                    request.Statistics.GetCountAndIncrement(), reprocessedCount == 0
                    ? request.Statistics.Total.ToString(CultureInfo.InvariantCulture)
                    : string.Format("{2}({0}+{1})", request.Statistics.Total, reprocessedCount, request.Statistics.Total + reprocessedCount)));
                TestResult result = testRunnerAgent.RunTests(test.Test, configurationSubstitutions);
                log.EndActivity(string.Format("Finished running {0} on {1}...", test.Test.UniqueTestId, agent));

                if (result == null)
                    throw new FaultException("Result is not available");

                ProcessResult(test, agent, result);
            }
            catch (FaultException ex)
            {
                log.Error(string.Format("Exception while running test {0} on {1}", test.Test.UniqueTestId, agent), ex);
                agents.MarkAsFailure(agent);
                tests.Add(test);
            }
            catch (CommunicationException ex)
            {
                log.Error(string.Format("Exception in communication while running test {0} on {1}", test.Test.UniqueTestId, agent), ex);
                agents.MarkAsDisconnected(agent);
                tests.Add(test);
            }
            catch(Exception ex)
            {
                log.Error(string.Format("Something bad and unhandled happened while running test {0} on {1}", test.Test.UniqueTestId, agent), ex);
            }
        }