Page 1 of 1

Parsing JUnit/XML report file to email results

Posted: Tue Apr 10, 2018 6:37 pm
by Darwin
Just sharing a parser I wrote to email the results of our nightly automation suite runs to our team. Not everyone who gets the email will necessarily have or want the Ranorex report viewer on their PC (or other device) they use to check email, but I wanted them all to see the basic results and stats from the nightly run.

I have our test suite run and generate both the original Ranorex report and the Junit/XML report, then run this utility to parse the XML report and write a summary to an HTM file. Next, I run this freeware SendItQuiet utility to grab the HTM file as the email body and distribute the email. The report looks like the abbreviated screenshot below.
ResultsUploader.png

Code: Select all

using System;
using System.Xml;

namespace RanorexResultUploader
{
    class Program
    {
        /// <summary>
        /// Reads and parses the Ranorex XML/JUnit report to get stats for overall test suite execution and
        /// individual test case results, then writes this information to an HTM file to be included in the 
        /// body of the results email (a subsequent call to the SendItQuiet email utility grabs the file).
        /// </summary>
        /// <param name="args">
        /// [0] - The original Ranorex report file, e.g.: HOSTNAME_YYYY_MM_DD_HH_NN_SUITENAME.rxlog
        /// [1] - The build number of the installed AUT, e.g.: 1.4.2.32
        /// </param>
        static void Main(string[] args)
        {
            XmlDocument resultsFile = new XmlDocument();

#if DEBUG
            // For local debugging (replace with actual file)
            string reportFile = @"\\<share>\<hostname>_2018_01_22_9_09_SelectedTest.rxlog";
            string buildNumber = "1.4.2.X";
            resultsFile.Load(reportFile + ".junit.xml");
#endif
            // ********************************
#if !DEBUG

            // For Release -- run-time parameters
            string reportFile = args[0];
            string buildNumber = args[1];
            resultsFile.Load(@"C:\Test\Reports\" + reportFile + ".junit.xml");
#endif

            using (System.IO.StreamWriter file = new System.IO.StreamWriter(@"C:\Test\Reports\EmailBody.htm"))
            {
                TestSuite currentTestSuite = new TestSuite();

                XmlNode testSuite = resultsFile.DocumentElement;
                currentTestSuite.Name = testSuite.Attributes["name"].Value;
                currentTestSuite.TestsRun = testSuite.Attributes["tests"].Value;
                currentTestSuite.Failures = testSuite.Attributes["failures"].Value;
                currentTestSuite.ExecutionTime = testSuite.Attributes["time"].Value;
                currentTestSuite.Skipped = testSuite.Attributes["skipped"].Value;
                currentTestSuite.TimeStamp = testSuite.Attributes["timestamp"].Value;
                currentTestSuite.HostName = testSuite.Attributes["hostname"].Value;

                file.WriteLine("<table border=\"1\">");
                file.WriteLine("<caption><b>{0}</b></caption>", currentTestSuite.Name);
                file.WriteLine("<tr><th>Build<td>{0}", buildNumber);
                file.WriteLine("<tr><th>Tests Run<td>{0}", currentTestSuite.TestsRun);

                if (currentTestSuite.Failures != "0")
                    file.WriteLine("<tr><th>Failed<td bgcolor=\"Red\">{0}", currentTestSuite.Failures);
                else
                    file.WriteLine("<tr><th>Failed<td bgcolor=\"Lime\">0");

                if (currentTestSuite.Skipped != "0")
                    file.WriteLine("<tr><th>Skipped<td bgcolor=\"Yellow\">{0}", currentTestSuite.Skipped);
                else
                    file.WriteLine("<tr><th>Skipped<td>0");

                file.WriteLine("<tr><th>Execution Time<td>{0}", currentTestSuite.ExecutionTime);
                file.WriteLine("<tr><th>Timestamp<td>{0}", currentTestSuite.TimeStamp);
                file.WriteLine("<tr><th>Host<td>{0}", currentTestSuite.HostName);
                file.WriteLine("</table><br>");

                Console.ForegroundColor = ConsoleColor.White;
                Console.WriteLine("Test Suite:");
                Console.WriteLine("\tName: {0}", currentTestSuite.Name);
                Console.WriteLine("\tTests Run: {0}", currentTestSuite.TestsRun);
                Console.WriteLine("\tFailures: {0}", currentTestSuite.Failures);
                Console.WriteLine("\tTests Skipped: {0}", currentTestSuite.Skipped);
                Console.WriteLine("\tExecution Time: {0}", currentTestSuite.ExecutionTime);
                Console.WriteLine();

                file.WriteLine("<table border=\"1\">");
                file.WriteLine("<tr><th>Test Case<th>Result<th>Duration<th>Error Msg");

                XmlNodeList testCases = resultsFile.DocumentElement.SelectNodes("/testsuite/testcase");
                foreach (XmlNode currentNode in testCases)
                {
                    TestCase testCase = new TestCase();

                    testCase.Name = currentNode.Attributes["name"].Value;
                    testCase.ExecutionTime = currentNode.Attributes["time"].Value;
                    testCase.ClassName = currentNode.Attributes["classname"].Value;

                    XmlNode skippedNode = currentNode.SelectSingleNode("skipped");
                    if (skippedNode != null)
                    {
                        testCase.SkippedMsg = skippedNode.Attributes["message"].Value;
                    }

                    XmlNode errorNode = currentNode.SelectSingleNode("error");
                    if (errorNode != null)
                    {
                        testCase.ErrorMsg = errorNode.Attributes["message"].Value;

                        XmlNode errorDesc = currentNode.SelectSingleNode("system-err");
                        if (errorDesc != null)
                        {
                            if (errorDesc.InnerText.Length > 1000)
                                testCase.ErrorDescription = errorDesc.InnerText.Substring(0, 1000);
                            else
                                testCase.ErrorDescription = errorDesc.InnerText;
                        }                       
                    }

                    Console.WriteLine("Test Case: {0}", testCase.Name);
                    Console.WriteLine("\tTime: {0}", testCase.ExecutionTime);

                    file.Write("<tr><td>{0}", testCase.Name);

                    if (testCase.Failed)
                    {
                        Console.ForegroundColor = ConsoleColor.Red;
                        Console.WriteLine("\tFailed: {0}", testCase.ErrorMsg);
                        Console.WriteLine(testCase.ErrorDescription);
                        file.Write("<td bgcolor=\"Red\">FAILED");
                    }
                    else if (testCase.Skipped)
                    {
                        Console.ForegroundColor = ConsoleColor.Yellow;
                        Console.WriteLine("\tSkipped: {0}", testCase.SkippedMsg);

                        file.Write("<td bgcolor=\"Yellow\">SKIPPED");
                    }
                    else
                    {
                        Console.ForegroundColor = ConsoleColor.Green;
                        Console.WriteLine("\tPASSED!");

                        file.Write("<td bgcolor=\"Lime\">PASSED");
                    }
                    Console.ForegroundColor = ConsoleColor.White;
                    Console.WriteLine();

                    file.Write("<td>{0}", testCase.ExecutionTime);
                    file.Write("<td>{0}", testCase.ErrorDescription);
                    file.WriteLine();
                }
                file.WriteLine("</table>");
                file.WriteLine("<br><br>Complete Log File at:<br>");
                file.WriteLine(@"\\{0}\Reports\{1}", currentTestSuite.HostName, reportFile);
                file.WriteLine("</html></body>");
            }

#if DEBUG
            Console.ReadLine();
#endif
        }
    }

    class TestCase
    {
        public string Name;
        public string ClassName;
        public bool Skipped;

        private string _skippedMsg;
        public string SkippedMsg
        {
            get { return _skippedMsg; }
            set
            {
                Skipped = true;
                _skippedMsg = value;
            }
        }

        public bool Failed;

        private string _errorMsg = "";
        public string ErrorMsg
        {
            get { return _errorMsg;}
            set
            {
                Failed = true;
                _errorMsg = value;
            }
        }

        private string _errorDescription = "";

        public string ErrorDescription
        {
            get { return _errorDescription; }
            set
            {
                Failed = true;
                _errorDescription = value;
            }
        }

        public string ExecutionTime
        {
            get
            {
                int hours = _executionInSeconds / 3600;
                int minutes = (_executionInSeconds % 3600) / 60;
                int seconds = (_executionInSeconds % 3600) % 60;

                string returnValue = (hours > 0 ? hours.ToString() + "h " : "") + minutes.ToString() + "m " + seconds.ToString() + "s";
                return returnValue;
            }
            set { _executionInSeconds = Int32.Parse(value); }
        }

        private int _executionInSeconds = 0;


        public TestCase()
        {
            Skipped = false;
            Failed = false;
        }
    }

    class TestSuite
    {
        public string Name;
        public string TestsRun;
        public string Failures;
        public string Skipped;
        public string TimeStamp;
        public string HostName;

        public string ExecutionTime
        {
            get
            {
                int hours = _executionInSeconds / 3600;
                int minutes = (_executionInSeconds % 3600) / 60;
                int seconds = (_executionInSeconds % 3600) % 60;

                string returnValue = (hours > 0 ? hours.ToString() + "h " : "") + minutes.ToString() + "m " + seconds.ToString() + "s";
                return returnValue;
            }
            set { _executionInSeconds = Int32.Parse(value); }
        }

        private int _executionInSeconds = 0;

        
    }
}
Subsequent call to SendItQuiet from our overall test execution BAT file:

Code: Select all

call \Test\SendItQuiet\SendItQuiet.exe -s <sender> -port <port> -u %fromEmail% -protocol normal -p -f %fromEmail% -t %recipientList% -subject %emailSubject% -bodyfile %baseReportPath%\EmailBody.htm

Re: Parsing JUnit/XML report file to email results

Posted: Wed Apr 11, 2018 7:21 am
by odklizec
Hi,

This is very cool! Thanks for sharing your code! ;)