├── .directory
├── .gitignore
├── .gitmodules
├── .travis.yml
├── LICENSE
├── MSTestAllureAdapter.Console
├── MSTestAllureAdapter.Console.csproj
├── Program.cs
└── Properties
│ └── AssemblyInfo.cs
├── MSTestAllureAdapter.Tests
├── ConsoleTests.cs
├── GeneratedReportTests.cs
├── MSTestAllureAdapter.Tests.csproj
├── MSTestAllureAdapterTestBase.cs
├── TRXParserTests.cs
├── packages.config
├── sample-output
│ ├── Category1.xml
│ ├── Category2.xml
│ ├── Category3.xml
│ └── NoCategory.xml
├── trx
│ ├── InvalidFile.trx
│ └── sample.trx
└── xsd
│ └── allure.xsd
├── MSTestAllureAdapter.sln
├── MSTestAllureAdapter.vs2010.sln
├── MSTestAllureAdapter
├── AllureAdapter.cs
├── AllureEvents
│ ├── TestCaseFailureWithErrorInfoEvent.cs
│ ├── TestCaseFinishedWithTimeEvent.cs
│ ├── TestCaseStartedWithTimeEvent.cs
│ ├── TestSuiteFinishedWithTimeEvent.cs
│ └── TestSuiteStartedWithTimeEvent.cs
├── ITestResultProvider.cs
├── MSTestAllureAdapter.csproj
├── MSTestResult.cs
├── Properties
│ └── AssemblyInfo.cs
├── TRXParser
│ ├── ErrorInfo.cs
│ ├── TRXParser.cs
│ ├── XElementExtensions.cs
│ └── vstst
│ │ └── vstst.xsd
└── Utils
│ ├── AllureResultsUtils.cs
│ └── MSTestResultUtils.cs
├── README.md
└── SampleTestingProject
├── Properties
└── AssemblyInfo.cs
├── SampleTestingProject.csproj
├── TRXSample.vs2010.sln
├── TRXSample.vs2012.sln
├── UnitTest1.cs
└── UserData.csv
/.directory:
--------------------------------------------------------------------------------
1 | [Dolphin]
2 | Timestamp=2014,9,15,8,59,32
3 | Version=3
4 |
5 | [Settings]
6 | HiddenFilesShown=true
7 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | #User Specific
2 | *.userprefs
3 | *.usertasks
4 | *.suo
5 | *.user
6 |
7 | #Mono Project Files
8 | *.pidb
9 | *.resources
10 | test-results/
11 |
12 | # MSTest test Results
13 | [Tt]est[Rr]esult*/
14 | [Bb]uild[Ll]og.*
15 |
16 | # NuGet Packages Directory
17 | ## TODO: If you have NuGet Package Restore enabled, uncomment the next line
18 | packages/
19 |
20 | # Backup & report files from converting an old project file to a newer
21 | # Visual Studio version. Backup files are not needed, because we have git ;-)
22 | _UpgradeReport_Files/
23 | Backup*/
24 | UpgradeLog*.XML
25 | UpgradeLog*.htm
26 |
27 | # Windows image file caches
28 | Thumbs.db
29 | ehthumbs.db
30 |
31 | # Folder config file
32 | Desktop.ini
33 |
34 | MSTestAllureAdapter/bin
35 | MSTestAllureAdapter/obj
36 | MSTestAllureAdapter.Console/bin
37 | MSTestAllureAdapter.Console/obj
38 | MSTestAllureAdapter.Tests/bin
39 | MSTestAllureAdapter.Tests/obj
40 | SampleTestingProject/bin
41 | SampleTestingProject/obj
42 |
--------------------------------------------------------------------------------
/.gitmodules:
--------------------------------------------------------------------------------
1 |
2 | [submodule "allure-csharp-commons"]
3 | path = allure-csharp-commons
4 | url = https://github.com/allure-framework/allure-csharp-commons.git
5 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: c
2 |
3 | before_install:
4 | - sudo add-apt-repository -y ppa:ricotz/testing
5 | - sudo apt-get update -qq
6 | - sudo apt-get install -qq -y gtk+3.0 libgtk-3-dev
7 | - "export DISPLAY=:99.0"
8 | - sh -e /etc/init.d/xvfb start
9 |
10 | install:
11 | - sudo add-apt-repository -y ppa:directhex/monoxide
12 | - sudo apt-get update
13 | - sudo apt-get install mono-devel mono-gmcs nunit-console
14 |
15 | script:
16 | - mozroots --import --sync
17 | - wget http://nuget.org/nuget.exe
18 | - mono --runtime=v4.0 ./nuget.exe restore allure-csharp-commons/AllureCSharpCommons.sln
19 | - mono --runtime=v4.0 ./nuget.exe restore MSTestAllureAdapter.sln
20 | - xbuild MSTestAllureAdapter.sln
21 | - nunit-console ./MSTestAllureAdapter.Tests/bin/Debug/MSTestAllureAdapter.Tests.dll
22 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright 2014 Daniel Kugel
2 |
3 | Licensed under the Apache License, Version 2.0 (the "License");
4 | you may not use this file except in compliance with the License.
5 | You may obtain a copy of the License at
6 |
7 | http://www.apache.org/licenses/LICENSE-2.0
8 |
9 | Unless required by applicable law or agreed to in writing, software
10 | distributed under the License is distributed on an "AS IS" BASIS,
11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | See the License for the specific language governing permissions and
13 | limitations under the License.
14 |
--------------------------------------------------------------------------------
/MSTestAllureAdapter.Console/MSTestAllureAdapter.Console.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Debug
5 | AnyCPU
6 | {808195D1-424C-48E0-A40F-3F93B7CC2F3C}
7 | Exe
8 | MSTestAllureAdapter.Console
9 | MSTestAllureAdapter.Console
10 |
11 |
12 | true
13 | full
14 | false
15 | bin\Debug
16 | DEBUG;
17 | prompt
18 | 4
19 | false
20 | sample.trx results
21 |
22 |
23 | full
24 | true
25 | bin\Release
26 | prompt
27 | 4
28 | true
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 | {9CED0C38-BCEB-4BFC-9294-38455758A461}
41 | MSTestAllureAdapter
42 |
43 |
44 | {0C358997-150B-4752-8B69-8B174167D730}
45 | AllureCSharpCommons
46 |
47 |
48 |
49 |
50 | sample.trx
51 | PreserveNewest
52 |
53 |
54 |
--------------------------------------------------------------------------------
/MSTestAllureAdapter.Console/Program.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.IO;
3 | using System.Collections.Generic;
4 |
5 | namespace MSTestAllureAdapter.Console
6 | {
7 | internal class MainClass
8 | {
9 | const int ERROR = 1;
10 | const int OK = 0;
11 | const string DEFAULT_RESULT_DIR = "results";
12 |
13 | public static int Main(string[] args)
14 | {
15 | string resultsDir;
16 | string trxPath;
17 |
18 | if (!ParseCommandLineOptions(args, out trxPath, out resultsDir))
19 | {
20 | DisplayHelp();
21 | return ERROR;
22 | }
23 |
24 | if (!File.Exists(trxPath))
25 | {
26 | System.Console.WriteLine("The supplied TRX file: '" + trxPath + "' was not found.");
27 | return ERROR;
28 | }
29 |
30 | ITestResultProvider testResultProvider = new TRXParser();
31 |
32 | AllureAdapter adapter = new AllureAdapter();
33 |
34 | try {
35 |
36 | System.Console.Write("Generating allure files... ");
37 |
38 | IEnumerable testResults = testResultProvider.GetTestResults(trxPath);
39 |
40 | adapter.GenerateTestResults(testResults, resultsDir);
41 |
42 | System.Console.WriteLine("Done.");
43 |
44 | return OK;
45 | }
46 | catch (Exception e)
47 | {
48 | System.Console.WriteLine("There was an error generating allure files into '" + resultsDir + "' from the TRX file '" + trxPath + "'.");
49 | // perhaps in the future we'll use log4net.
50 | System.Console.WriteLine(e.ToString());
51 | return ERROR;
52 | }
53 | }
54 |
55 | ///
56 | /// Parses the command line options.
57 | /// Because the there are only 2 options the parsing is done manually instead of using a library.
58 | ///
59 | /// true, if command line options were parsed, false otherwise.
60 | /// The command line arguments.
61 | /// Trx path.
62 | /// Report path.
63 | private static bool ParseCommandLineOptions(string[] args, out string trxPath, out string outputPath)
64 | {
65 | outputPath = null;
66 | trxPath = null;
67 |
68 | if (args == null || args.Length == 0)
69 | return false;
70 |
71 | trxPath = args[0];
72 |
73 | outputPath = args.Length > 2 ? args[1] : DEFAULT_RESULT_DIR;
74 |
75 | if (!Directory.Exists(outputPath))
76 | {
77 | Directory.CreateDirectory(outputPath);
78 | }
79 |
80 | return true;
81 | }
82 |
83 | private static void DisplayHelp()
84 | {
85 | // if GetCommandLineArgs cannot obtain the program name then the first element of the array is
86 | // string.Empty but the array size will always be greater than zero.
87 | // Path.GetFileName handles string.Empty correctly.
88 |
89 | string programName = Path.GetFileName(Environment.GetCommandLineArgs()[0]);
90 |
91 | string help = String.Empty;
92 |
93 | help += "Usage: " + programName + " ";
94 |
95 | string targetDirDisplayName = "[output target dir]";
96 |
97 | help += " " + targetDirDisplayName;
98 | help += Environment.NewLine;
99 | help += "If '" + targetDirDisplayName + "' is missing the reslts are saved in the current directory in a folder named '" + DEFAULT_RESULT_DIR + "'.";
100 |
101 | System.Console.WriteLine(help);
102 | }
103 | }
104 | }
105 |
--------------------------------------------------------------------------------
/MSTestAllureAdapter.Console/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.CompilerServices;
3 |
4 | // Information about this assembly is defined by the following attributes.
5 | // Change them to the values specific to your project.
6 |
7 | [assembly: AssemblyTitle("MSTestAllureAdapter.Console")]
8 | [assembly: AssemblyDescription("")]
9 | [assembly: AssemblyConfiguration("")]
10 | [assembly: AssemblyCompany("")]
11 | [assembly: AssemblyProduct("")]
12 | [assembly: AssemblyCopyright("daniel")]
13 | [assembly: AssemblyTrademark("")]
14 | [assembly: AssemblyCulture("")]
15 |
16 | // The assembly version has the format "{Major}.{Minor}.{Build}.{Revision}".
17 | // The form "{Major}.{Minor}.*" will automatically update the build and revision,
18 | // and "{Major}.{Minor}.{Build}.*" will update just the revision.
19 |
20 | [assembly: AssemblyVersion("1.0.*")]
21 |
22 | // The following attributes are used to specify the signing key for the assembly,
23 | // if desired. See the Mono documentation for more information about signing.
24 |
25 | //[assembly: AssemblyDelaySign(false)]
26 | //[assembly: AssemblyKeyFile("")]
27 |
28 | [assembly: InternalsVisibleTo("MSTestAllureAdapter.Tests")]
--------------------------------------------------------------------------------
/MSTestAllureAdapter.Tests/ConsoleTests.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using MSTestAllureAdapter.Console;
3 | using NUnit.Framework;
4 | using System.IO;
5 |
6 | namespace MSTestAllureAdapter.Tests
7 | {
8 | [TestFixture]
9 | public class ConsoleTests : MSTestAllureAdapterTestBase
10 | {
11 | string mTargetDir = "results";
12 |
13 | [TearDown]
14 | public override void TearDown()
15 | {
16 | base.TearDown();
17 | if (Directory.Exists(mTargetDir))
18 | Directory.Delete(mTargetDir, true);
19 | }
20 |
21 | [Test]
22 | public void MissingArgumentsReturnError()
23 | {
24 | int expected = 1;
25 | int actual = MainClass.Main(new string[0]);
26 |
27 | Assert.AreEqual(expected, actual, "Main did not return the correct error code when no arguments were passed.");
28 | }
29 |
30 | [Test]
31 | public void MissingTrxReturnsError()
32 | {
33 | int expected = 1;
34 | int actual = MainClass.Main(new string[]{ "non-existing-trx-file.trx", mTargetDir });
35 |
36 | Assert.AreEqual(expected, actual, "Main did not return the correct error code on a missing TRX file.");
37 | }
38 |
39 | [Test]
40 | public void InvalidTrxReturnsError()
41 | {
42 | int expexted = 1;
43 |
44 | int actual = MainClass.Main(new string[]{ Path.Combine("trx", "InvalidFile.trx"), mTargetDir });
45 |
46 | Assert.AreEqual(expexted, actual);
47 | }
48 |
49 | [Test]
50 | public void ValidTrxReturnsOK()
51 | {
52 | int expexted = 0;
53 | int actual = MainClass.Main(new string[]{ Path.Combine("trx", "sample.trx"), mTargetDir });
54 |
55 | Assert.AreEqual(expexted, actual);
56 | }
57 | }
58 | }
59 |
60 |
--------------------------------------------------------------------------------
/MSTestAllureAdapter.Tests/GeneratedReportTests.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using NUnit.Framework;
3 | using System.IO;
4 | using System.Xml;
5 | using System.Xml.Schema;
6 | using System.Xml.Linq;
7 | using System.Xml.XPath;
8 | using System.Collections.Generic;
9 | using XmlUnit.Xunit;
10 |
11 | namespace MSTestAllureAdapter.Tests
12 | {
13 | [TestFixture]
14 | public class GeneratedReportTests
15 | {
16 | string mTargetDir = "results";
17 |
18 | string mValidTrxFile = Path.Combine("trx", "sample.trx");
19 |
20 | private void DeleteTargetDir()
21 | {
22 | if (Directory.Exists(mTargetDir))
23 | Directory.Delete(mTargetDir, true);
24 | }
25 |
26 | [SetUp]
27 | public void SetUp()
28 | {
29 | DeleteTargetDir();
30 | Directory.CreateDirectory(mTargetDir);
31 | }
32 |
33 | [Test]
34 | public void GeneratedFilesHaveCorrectSchema()
35 | {
36 | RunReport(mValidTrxFile);
37 |
38 | XmlReaderSettings readerSettings = new XmlReaderSettings();
39 | readerSettings.IgnoreWhitespace = true;
40 | readerSettings.Schemas.Add(null, Path.Combine("xsd", "allure.xsd"));
41 | readerSettings.ValidationType = ValidationType.Schema;
42 |
43 | string[] files = Directory.GetFiles(mTargetDir, "*.xml");
44 |
45 | if (files.Length == 0)
46 | {
47 | Assert.Fail("No generated files were found.");
48 | }
49 |
50 | foreach (string file in files)
51 | {
52 | // XmlDocument.Load will also work but we don't want to load the entire XML to memory.
53 | XmlReader xmlReader = XmlReader.Create(file, readerSettings);
54 | while (xmlReader.Read());
55 | }
56 | }
57 |
58 | [Test]
59 | public void CompareExpectedXMLs()
60 | {
61 | Dictionary expected = new Dictionary();
62 | Dictionary actual = new Dictionary();
63 |
64 | RunReport(mValidTrxFile);
65 |
66 | XmlNamespaceManager xmlNamespaceManager = new XmlNamespaceManager(new NameTable());
67 | xmlNamespaceManager.AddNamespace("prefix", "urn:model.allure.qatools.yandex.ru");
68 |
69 | DiffConfiguration diffConfiguration = new DiffConfiguration(String.Empty, false, WhitespaceHandling.None, true);
70 |
71 | FillCategoryToXmlMap("sample-output", expected);
72 | FillCategoryToXmlMap(mTargetDir, actual);
73 |
74 | if (expected.Keys.Count != actual.Keys.Count)
75 | {
76 | Assert.Fail("Expected {0} categories but found {1}.", expected.Keys.Count, actual.Keys.Count);
77 | }
78 |
79 | foreach (string category in actual.Keys)
80 | {
81 | if (!expected.ContainsKey(category))
82 | {
83 | Assert.Fail("The category " + category + " was not expected.");
84 | }
85 |
86 | string expectedFile = expected[category];
87 | string actualFile = actual[category];
88 |
89 | string expectedFileText = File.ReadAllText(expectedFile);
90 | string actualFileText = File.ReadAllText(actualFile);
91 |
92 | XmlInput control = new XmlInput(expectedFileText);
93 | XmlInput test = new XmlInput(actualFileText);
94 |
95 | XmlDiff xmlDiff = new XmlDiff(control, test, diffConfiguration);
96 |
97 | DiffResult diffResult = xmlDiff.Compare();
98 | if (!diffResult.Identical)
99 | {
100 | string failureMessage = String.Format("The expected file {0} was different from the actual file {1}", expectedFile, actualFile);
101 | failureMessage += Environment.NewLine;
102 | failureMessage += "Expected XML: ";
103 | failureMessage += expectedFileText;
104 | failureMessage += Environment.NewLine;
105 | failureMessage += "Actual XML: ";
106 | failureMessage += actualFileText;
107 | failureMessage += Environment.NewLine;
108 | failureMessage += "Difference: ";
109 | failureMessage += diffResult.Difference;
110 |
111 | Assert.Fail(failureMessage);
112 | }
113 | }
114 | }
115 |
116 | private void FillCategoryToXmlMap(string source, IDictionary map)
117 | {
118 | string[] files = Directory.GetFiles(source, "*.xml");
119 |
120 | foreach (string file in files)
121 | {
122 | string category = String.Empty;
123 | XDocument xDoc = XDocument.Load(file);
124 | XElement element = xDoc.Root.XPathSelectElement("./name");
125 |
126 | if (element != null)
127 | {
128 | category = element.Value;
129 | }
130 |
131 | map[category] = file;
132 | }
133 | }
134 |
135 | private void RunReport(string trxPath)
136 | {
137 | AllureAdapter adapter = new AllureAdapter();
138 |
139 | ITestResultProvider testResultsProvider = new TRXParser();
140 |
141 | IEnumerable testResults = testResultsProvider.GetTestResults(trxPath);
142 |
143 | adapter.GenerateTestResults(testResults, mTargetDir);
144 | }
145 | }
146 | }
147 |
148 |
--------------------------------------------------------------------------------
/MSTestAllureAdapter.Tests/MSTestAllureAdapter.Tests.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Debug
5 | AnyCPU
6 | {4B003B01-AA7F-4917-92B9-CCC278E84531}
7 | Library
8 | MSTestAllureAdapter.Tests
9 | MSTestAllureAdapter.Tests
10 |
11 |
12 | true
13 | full
14 | false
15 | bin\Debug
16 | DEBUG;
17 | prompt
18 | 4
19 | false
20 |
21 |
22 | full
23 | true
24 | bin\Release
25 | prompt
26 | 4
27 | false
28 |
29 |
30 |
31 |
32 | ..\packages\log4net.2.0.3\lib\net40-full\log4net.dll
33 |
34 |
35 |
36 |
37 | ..\packages\xunit.1.8.0.1545\lib\xunit.dll
38 |
39 |
40 | ..\packages\XmlUnit.Xunit.0.4\lib\net40\XmlUnit.Xunit.dll
41 |
42 |
43 | ..\packages\NUnit.2.6.3\lib\nunit.framework.dll
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 | {9CED0C38-BCEB-4BFC-9294-38455758A461}
56 | MSTestAllureAdapter
57 |
58 |
59 | {808195D1-424C-48E0-A40F-3F93B7CC2F3C}
60 | MSTestAllureAdapter.Console
61 |
62 |
63 |
64 |
65 |
66 | PreserveNewest
67 |
68 |
69 | PreserveNewest
70 |
71 |
72 | PreserveNewest
73 |
74 |
75 | PreserveNewest
76 |
77 |
78 | PreserveNewest
79 |
80 |
81 | PreserveNewest
82 |
83 |
84 | PreserveNewest
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
--------------------------------------------------------------------------------
/MSTestAllureAdapter.Tests/MSTestAllureAdapterTestBase.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.IO;
4 | using System.Linq;
5 | using NUnit.Framework;
6 |
7 | namespace MSTestAllureAdapter.Tests
8 | {
9 | public abstract class MSTestAllureAdapterTestBase
10 | {
11 | public MSTestAllureAdapterTestBase()
12 | {
13 | MSTestResult[] testResults = new [] {
14 | new MSTestResult("TestMethod1", TestOutcome.Passed, "Category1"),
15 | new MSTestResult("TestMethod2", TestOutcome.Passed, "Category1", "Category2"),
16 | new MSTestResult("TestMethod3", TestOutcome.Passed, "Category2"),
17 | new MSTestResult("Test_Without_Category", TestOutcome.Passed),
18 | new MSTestResult("SimpleFailingTest", TestOutcome.Failed),
19 | new MSTestResult("ExpectedException", TestOutcome.Passed),
20 | new MSTestResult("ExpectedExceptionWithNoExceptionMessage", TestOutcome.Passed),
21 | new MSTestResult("UnexpectedException", TestOutcome.Failed),
22 | new MSTestResult("CSVDataDrivenTest0", TestOutcome.Passed, "Category3"),
23 | new MSTestResult("CSVDataDrivenTest1", TestOutcome.Passed, "Category3"),
24 | new MSTestResult("CSVDataDrivenTest2", TestOutcome.Failed, "Category3"),
25 | new MSTestResult("TestMethod_With_Only_Missing_Result_File", TestOutcome.Passed),
26 | new MSTestResult("TestMethod_With_One_Missing_And_One_present_Result_File", TestOutcome.Passed),
27 | new MSTestResult("TestMethod_With_Multiple_Result_Files", TestOutcome.Passed)
28 | };
29 |
30 | ExpectedTestsResultsMap = CreateMap(testResults);
31 |
32 | ExpectedTestsResultsMap["TestMethod1"].Owner = "Owner1";
33 | ExpectedTestsResultsMap["TestMethod2"].Owner = "Owner1";
34 | ExpectedTestsResultsMap["TestMethod3"].Owner = "Owner1";
35 | ExpectedTestsResultsMap["UnexpectedException"].Owner = "Owner2";
36 | ExpectedTestsResultsMap["ExpectedExceptionWithNoExceptionMessage"].Owner = "Owner2";
37 | ExpectedTestsResultsMap["CSVDataDrivenTest0"].Owner = "Owner3";
38 | ExpectedTestsResultsMap["CSVDataDrivenTest1"].Owner = "Owner3";
39 | ExpectedTestsResultsMap["CSVDataDrivenTest2"].Owner = "Owner3";
40 | }
41 |
42 | private IDictionary CreateMap(IEnumerable testResults)
43 | {
44 | return testResults.ToDictionary(x => x.Name);
45 | }
46 |
47 | protected IDictionary ExpectedTestsResultsMap { get; set; }
48 |
49 | [SetUp]
50 | public virtual void SetUp()
51 | {
52 |
53 | }
54 |
55 | [TearDown]
56 | public virtual void TearDown()
57 | {
58 |
59 | }
60 |
61 | protected string GetRandomDir()
62 | {
63 | string randomDir = Guid.NewGuid().ToString();
64 |
65 | // not chance of that happening...
66 | while (Directory.Exists(randomDir))
67 | randomDir = Guid.NewGuid().ToString();
68 |
69 | return randomDir;
70 | }
71 |
72 | protected string CreateRandomDir()
73 | {
74 | string randomDir = GetRandomDir();
75 | Directory.CreateDirectory(randomDir);
76 | return randomDir;
77 | }
78 | }
79 | }
80 |
81 |
--------------------------------------------------------------------------------
/MSTestAllureAdapter.Tests/TRXParserTests.cs:
--------------------------------------------------------------------------------
1 | using NUnit.Framework;
2 | using System;
3 | using System.Collections.Generic;
4 | using System.Linq;
5 | using System.Text;
6 | using System.IO;
7 |
8 | namespace MSTestAllureAdapter.Tests
9 | {
10 | [TestFixture]
11 | public class TRXParserTests : MSTestAllureAdapterTestBase
12 | {
13 | private IEnumerable mTestResults;
14 |
15 | [SetUp]
16 | public override void SetUp()
17 | {
18 | base.SetUp();
19 |
20 | TRXParser parser = new TRXParser();
21 |
22 | mTestResults = parser.GetTestResults(Path.Combine("trx", "sample.trx")).EnumerateTestResults();
23 | }
24 |
25 | [Test]
26 | public void ExpectedNumberOfTestsWereFound()
27 | {
28 | Assert.AreEqual(ExpectedTestsResultsMap.Values.EnumerateTestResults().Count(), mTestResults.Count());
29 | }
30 |
31 | [Test]
32 | public void AllTestsWereFound()
33 | {
34 | IEnumerable expected = ExpectedTestsResultsMap.Keys;
35 | IEnumerable found = mTestResults.Select(testResult => testResult.Name);
36 |
37 | EnumerableDiffResult result = EnumerableDiff(expected, found);
38 |
39 | Assert.AreEqual(0, result.TotalOff, result.Message);
40 | }
41 |
42 | [Test]
43 | public void TestCategoriesWereFound()
44 | {
45 | foreach (MSTestResult testResult in mTestResults)
46 | {
47 | IEnumerable expected = ExpectedTestsResultsMap[testResult.Name].Suites;
48 | IEnumerable found = testResult.Suites;
49 |
50 | EnumerableDiffResult result = EnumerableDiff(expected, found);
51 |
52 | if (result.TotalOff > 0)
53 | {
54 | Assert.AreEqual(0, result.TotalOff, "Test '" + testResult.Name + "' : "+ Environment.NewLine + result.Message);
55 | }
56 | }
57 | }
58 |
59 | [Test]
60 | public void TestOutcomesMatch()
61 | {
62 | foreach (MSTestResult testResult in mTestResults)
63 | {
64 | TestOutcome expected = ExpectedTestsResultsMap[testResult.Name].Outcome;
65 | TestOutcome actual = testResult.Outcome;
66 |
67 | if (expected != actual)
68 | {
69 | Assert.Fail("Test '" + testResult.Name + "' was found to have an outcome of " + actual + " instead of the expected " + expected);
70 | }
71 | }
72 | }
73 |
74 | [Test]
75 | public void TestOwnersWereFound()
76 | {
77 | foreach (MSTestResult testResult in mTestResults)
78 | {
79 | string expected = ExpectedTestsResultsMap[testResult.Name].Owner;
80 | string actual = testResult.Owner;
81 |
82 | if (String.Compare(expected, actual, true) != 0)
83 | {
84 | Assert.Fail("Test '" + testResult.Name + "' owner was found to be '" + actual + "' instead of the expected '" + expected + "'");
85 | }
86 | }
87 | }
88 |
89 |
90 | private EnumerableDiffResult EnumerableDiff(IEnumerable expected, IEnumerable found)
91 | {
92 | if (expected == null)
93 | expected = Enumerable.Empty();
94 |
95 | if (found == null)
96 | found = Enumerable.Empty();
97 |
98 | HashSet missing = new HashSet(expected);
99 | missing.ExceptWith(found);
100 |
101 |
102 | HashSet notExpected = new HashSet(found);
103 | notExpected.ExceptWith(expected);
104 |
105 | string message = String.Empty;
106 |
107 | if (missing.Count > 0)
108 | {
109 | message += "The following items were not found: ";
110 | message += String.Join(", ", missing.Select( _ => "'" + _ + "'"));
111 | message += Environment.NewLine;
112 | }
113 |
114 | if (notExpected.Count > 0)
115 | {
116 | message += "The following items were not expected: ";
117 | message += String.Join(", ", notExpected.Select( _ => "'" + _ + "'"));
118 | message += Environment.NewLine;
119 | }
120 |
121 | return new EnumerableDiffResult(missing.Count + notExpected.Count, message);
122 | }
123 |
124 | private class EnumerableDiffResult
125 | {
126 | public EnumerableDiffResult(int totalOff, string message)
127 | {
128 | TotalOff = totalOff;
129 | Message = message;
130 | }
131 |
132 | public int TotalOff { get; private set; }
133 |
134 | public string Message { get; private set; }
135 | }
136 | }
137 |
138 |
139 | }
140 |
141 |
--------------------------------------------------------------------------------
/MSTestAllureAdapter.Tests/packages.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/MSTestAllureAdapter.Tests/sample-output/Category1.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | Category1
4 |
5 |
6 | TestMethod1
7 | Description of the TestMethod1 UnitTest.
8 | Test Owner: Owner1
9 |
10 |
11 |
12 |
13 |
14 | TestMethod2
15 |
16 | Test Owner: Owner1
17 |
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/MSTestAllureAdapter.Tests/sample-output/Category2.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | Category2
4 |
5 |
6 | TestMethod3
7 |
8 | Test Owner: Owner1
9 |
10 |
11 |
12 |
13 |
14 | TestMethod2
15 |
16 | Test Owner: Owner1
17 |
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/MSTestAllureAdapter.Tests/sample-output/Category3.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | Category3
4 |
5 |
6 | CSVDataDrivenTest0
7 | Description of the CSVDataDrivenTest data driven test.
8 | Test Owner: Owner3
9 |
10 |
11 |
12 |
13 |
14 | CSVDataDrivenTest1
15 | Description of the CSVDataDrivenTest data driven test.
16 | Test Owner: Owner3
17 |
18 |
19 |
20 |
21 |
22 | CSVDataDrivenTest2
23 | Description of the CSVDataDrivenTest data driven test.
24 | Test Owner: Owner3
25 |
26 | Test method SampleTestingProject.UnitTest1.CSVDataDrivenTest threw exception:
27 | System.DivideByZeroException: Attempted to divide by zero.
28 | at SampleTestingProject.UnitTest1.CSVDataDrivenTest() in C:\Documents and Settings\chen\allure-mstest-adapter\SampleTestingProject\UnitTest1.cs:line 119
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
--------------------------------------------------------------------------------
/MSTestAllureAdapter.Tests/sample-output/NoCategory.xml:
--------------------------------------------------------------------------------
1 | UnexpectedException
2 | Test Owner: Owner2Test method SampleTestingProject.UnitTest1.UnexpectedException threw exception:
3 | System.Exception: Exception of type 'System.Exception' was thrown. at SampleTestingProject.UnitTest1.UnexpectedException() in C:\Documents and Settings\chen\allure-mstest-adapter\SampleTestingProject\UnitTest1.cs:line 97
4 | ExpectedExceptionWithNoExceptionMessage
5 | Test Owner: Owner2ExpectedExceptionSimpleFailingTestAssert.AreEqual failed. Expected:<1>. Actual:<2>. The calculation failed. at SampleTestingProject.UnitTest1.SimpleFailingTest() in C:\Documents and Settings\chen\allure-mstest-adapter\SampleTestingProject\UnitTest1.cs:line 75
6 | TestMethod_With_Only_Missing_Result_FileTest_Without_CategoryTestMethod_With_One_Missing_And_One_present_Result_FileTestMethod_With_Multiple_Result_Files
--------------------------------------------------------------------------------
/MSTestAllureAdapter.Tests/trx/InvalidFile.trx:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/MSTestAllureAdapter.Tests/trx/sample.trx:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 | Description of the TestMethod1 UnitTest.
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 | Description of the CSVDataDrivenTest data driven test.
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
133 |
134 |
135 |
136 |
137 |
138 |
139 |
140 |
141 |
148 |
149 |
150 |
151 |
152 |
153 |
154 |
162 |
163 |
164 |
165 |
166 |
167 |
168 |
169 |
177 |
178 |
179 |
180 |
181 |
186 |
187 |
188 |
189 |
190 |
191 |
196 |
197 |
198 |
--------------------------------------------------------------------------------
/MSTestAllureAdapter.Tests/xsd/allure.xsd:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 |
136 |
137 |
138 |
139 |
140 |
141 |
142 |
143 |
144 |
145 |
146 |
147 |
148 |
149 |
150 |
151 |
152 |
153 |
154 |
155 |
156 |
157 |
158 |
159 |
160 |
161 |
162 |
163 |
164 |
165 |
166 |
167 |
168 |
169 |
170 |
171 |
172 |
173 |
--------------------------------------------------------------------------------
/MSTestAllureAdapter.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio 2012
4 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MSTestAllureAdapter", "MSTestAllureAdapter\MSTestAllureAdapter.csproj", "{9CED0C38-BCEB-4BFC-9294-38455758A461}"
5 | EndProject
6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AllureCSharpCommons", "allure-csharp-commons\AllureCSharpCommons\AllureCSharpCommons.csproj", "{0C358997-150B-4752-8B69-8B174167D730}"
7 | EndProject
8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MSTestAllureAdapter.Tests", "MSTestAllureAdapter.Tests\MSTestAllureAdapter.Tests.csproj", "{4B003B01-AA7F-4917-92B9-CCC278E84531}"
9 | EndProject
10 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MSTestAllureAdapter.Console", "MSTestAllureAdapter.Console\MSTestAllureAdapter.Console.csproj", "{808195D1-424C-48E0-A40F-3F93B7CC2F3C}"
11 | EndProject
12 | Global
13 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
14 | Debug|Any CPU = Debug|Any CPU
15 | Release|Any CPU = Release|Any CPU
16 | EndGlobalSection
17 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
18 | {0C358997-150B-4752-8B69-8B174167D730}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
19 | {0C358997-150B-4752-8B69-8B174167D730}.Debug|Any CPU.Build.0 = Debug|Any CPU
20 | {0C358997-150B-4752-8B69-8B174167D730}.Release|Any CPU.ActiveCfg = Release|Any CPU
21 | {0C358997-150B-4752-8B69-8B174167D730}.Release|Any CPU.Build.0 = Release|Any CPU
22 | {4B003B01-AA7F-4917-92B9-CCC278E84531}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
23 | {4B003B01-AA7F-4917-92B9-CCC278E84531}.Debug|Any CPU.Build.0 = Debug|Any CPU
24 | {4B003B01-AA7F-4917-92B9-CCC278E84531}.Release|Any CPU.ActiveCfg = Release|Any CPU
25 | {4B003B01-AA7F-4917-92B9-CCC278E84531}.Release|Any CPU.Build.0 = Release|Any CPU
26 | {808195D1-424C-48E0-A40F-3F93B7CC2F3C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
27 | {808195D1-424C-48E0-A40F-3F93B7CC2F3C}.Debug|Any CPU.Build.0 = Debug|Any CPU
28 | {808195D1-424C-48E0-A40F-3F93B7CC2F3C}.Release|Any CPU.ActiveCfg = Release|Any CPU
29 | {808195D1-424C-48E0-A40F-3F93B7CC2F3C}.Release|Any CPU.Build.0 = Release|Any CPU
30 | {9CED0C38-BCEB-4BFC-9294-38455758A461}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
31 | {9CED0C38-BCEB-4BFC-9294-38455758A461}.Debug|Any CPU.Build.0 = Debug|Any CPU
32 | {9CED0C38-BCEB-4BFC-9294-38455758A461}.Release|Any CPU.ActiveCfg = Release|Any CPU
33 | {9CED0C38-BCEB-4BFC-9294-38455758A461}.Release|Any CPU.Build.0 = Release|Any CPU
34 | EndGlobalSection
35 | GlobalSection(MonoDevelopProperties) = preSolution
36 | StartupItem = MSTestAllureAdapter.Console\MSTestAllureAdapter.Console.csproj
37 | EndGlobalSection
38 | EndGlobal
39 |
--------------------------------------------------------------------------------
/MSTestAllureAdapter.vs2010.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 11.00
3 | # Visual Studio 2010
4 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MSTestAllureAdapter", "MSTestAllureAdapter\MSTestAllureAdapter.csproj", "{9CED0C38-BCEB-4BFC-9294-38455758A461}"
5 | EndProject
6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MSTestAllureAdapter.Console", "MSTestAllureAdapter.Console\MSTestAllureAdapter.Console.csproj", "{808195D1-424C-48E0-A40F-3F93B7CC2F3C}"
7 | EndProject
8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MSTestAllureAdapter.Tests", "MSTestAllureAdapter.Tests\MSTestAllureAdapter.Tests.csproj", "{4B003B01-AA7F-4917-92B9-CCC278E84531}"
9 | EndProject
10 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AllureCSharpCommons", "allure-csharp-commons\AllureCSharpCommons\AllureCSharpCommons.csproj", "{0C358997-150B-4752-8B69-8B174167D730}"
11 | EndProject
12 | Global
13 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
14 | Debug|Any CPU = Debug|Any CPU
15 | Release|Any CPU = Release|Any CPU
16 | EndGlobalSection
17 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
18 | {9CED0C38-BCEB-4BFC-9294-38455758A461}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
19 | {9CED0C38-BCEB-4BFC-9294-38455758A461}.Debug|Any CPU.Build.0 = Debug|Any CPU
20 | {9CED0C38-BCEB-4BFC-9294-38455758A461}.Release|Any CPU.ActiveCfg = Release|Any CPU
21 | {9CED0C38-BCEB-4BFC-9294-38455758A461}.Release|Any CPU.Build.0 = Release|Any CPU
22 | {808195D1-424C-48E0-A40F-3F93B7CC2F3C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
23 | {808195D1-424C-48E0-A40F-3F93B7CC2F3C}.Debug|Any CPU.Build.0 = Debug|Any CPU
24 | {808195D1-424C-48E0-A40F-3F93B7CC2F3C}.Release|Any CPU.ActiveCfg = Release|Any CPU
25 | {808195D1-424C-48E0-A40F-3F93B7CC2F3C}.Release|Any CPU.Build.0 = Release|Any CPU
26 | {4B003B01-AA7F-4917-92B9-CCC278E84531}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
27 | {4B003B01-AA7F-4917-92B9-CCC278E84531}.Debug|Any CPU.Build.0 = Debug|Any CPU
28 | {4B003B01-AA7F-4917-92B9-CCC278E84531}.Release|Any CPU.ActiveCfg = Release|Any CPU
29 | {4B003B01-AA7F-4917-92B9-CCC278E84531}.Release|Any CPU.Build.0 = Release|Any CPU
30 | {0C358997-150B-4752-8B69-8B174167D730}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
31 | {0C358997-150B-4752-8B69-8B174167D730}.Debug|Any CPU.Build.0 = Debug|Any CPU
32 | {0C358997-150B-4752-8B69-8B174167D730}.Release|Any CPU.ActiveCfg = Release|Any CPU
33 | {0C358997-150B-4752-8B69-8B174167D730}.Release|Any CPU.Build.0 = Release|Any CPU
34 | EndGlobalSection
35 | GlobalSection(SolutionProperties) = preSolution
36 | HideSolutionNode = FALSE
37 | EndGlobalSection
38 | EndGlobal
39 |
--------------------------------------------------------------------------------
/MSTestAllureAdapter/AllureAdapter.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using AllureCSharpCommons;
3 | using AllureCSharpCommons.Events;
4 | using System.Collections.Generic;
5 | using System.Linq;
6 | using System.Text;
7 | using AllureCSharpCommons.AllureModel;
8 |
9 | namespace MSTestAllureAdapter
10 | {
11 | ///
12 | /// The base class for the Allure adapter.
13 | ///
14 | public class AllureAdapter
15 | {
16 | private static readonly string EMPTY_SUITE_CATEGORY_NAME = "NO_CATEGORY";
17 |
18 | static AllureAdapter()
19 | {
20 | AllureConfig.AllowEmptySuites = true;
21 | }
22 |
23 | protected virtual void HandleTestResult(MSTestResult testResult)
24 | {
25 | switch (testResult.Outcome)
26 | {
27 | case TestOutcome.Failed:
28 | TestFailed(testResult);
29 | break;
30 | }
31 | }
32 |
33 | ///
34 | /// Generates the test results from the supplied TRX file to be used by the allure framework.
35 | ///
36 | /// Trx file.
37 | /// Results path where the files shuold be saved.
38 | public void GenerateTestResults(IEnumerable testResults, string resultsPath)
39 | {
40 | string originalResultsPath = AllureConfig.ResultsPath;
41 |
42 | if (!resultsPath.EndsWith(System.IO.Path.DirectorySeparatorChar.ToString()))
43 | {
44 | resultsPath += System.IO.Path.DirectorySeparatorChar;
45 | }
46 |
47 | AllureConfig.ResultsPath = resultsPath;
48 |
49 | try
50 | {
51 | IDictionary> testsMap = CreateSuitToTestsMap(testResults);
52 |
53 | foreach (KeyValuePair> testResultBySuit in testsMap)
54 | {
55 | string suitUid = Guid.NewGuid().ToString();
56 | string suitName = testResultBySuit.Key;
57 | ICollection tests = testResultBySuit.Value;
58 |
59 | DateTime start = tests.Min(x => x.Start);
60 | DateTime end = tests.Max(x => x.End);
61 |
62 | if (suitName == EMPTY_SUITE_CATEGORY_NAME)
63 | {
64 | suitName = null;
65 | }
66 |
67 | TestSuitStarted(suitUid, suitName, start);
68 |
69 | foreach (MSTestResult testResult in testResultBySuit.Value)
70 | {
71 | if (testResult.InnerTests == null || !testResult.InnerTests.Any())
72 | {
73 | HandleAllureTestCaseResult(suitUid, testResult);
74 | }
75 | else
76 | {
77 | foreach (MSTestResult innerTestResult in testResult.InnerTests)
78 | {
79 | HandleAllureTestCaseResult(suitUid, innerTestResult);
80 | }
81 | }
82 | }
83 |
84 | TestSuitFinished(suitUid, end);
85 | }
86 | }
87 | finally
88 | {
89 | AllureConfig.ResultsPath = originalResultsPath;
90 | }
91 | }
92 |
93 | private void HandleAllureTestCaseResult(string suitUid, MSTestResult testResult)
94 | {
95 | TestStarted(suitUid, testResult);
96 |
97 | HandleTestResult(testResult);
98 |
99 | TestFinished(testResult);
100 | }
101 |
102 | private IDictionary> CreateSuitToTestsMap(IEnumerable testResults )
103 | {
104 | // use MultiValueDictionary from the Microsoft Experimental Collections when it becomes available.
105 | // https://www.nuget.org/packages/Microsoft.Experimental.Collections/
106 | IDictionary> testsMap = new Dictionary>();
107 |
108 | foreach (MSTestResult testResult in testResults)
109 | {
110 | IEnumerable suits = testResult.Suites;
111 |
112 | if (!suits.Any())
113 | {
114 | suits = new string[]{ EMPTY_SUITE_CATEGORY_NAME };
115 | }
116 |
117 | foreach (string suit in suits)
118 | {
119 | ICollection tests = null;
120 |
121 | if (!testsMap.TryGetValue(suit, out tests))
122 | {
123 | tests = new List();
124 | testsMap[suit] = tests;
125 | }
126 |
127 | tests.Add(testResult);
128 | }
129 | }
130 |
131 | return testsMap;
132 | }
133 |
134 | protected virtual void TestStarted(string suitId, MSTestResult testResult)
135 | {
136 | TestCaseStartedWithTimeEvent testCase = new TestCaseStartedWithTimeEvent(suitId, testResult.Name, testResult.Start);
137 |
138 | if (testResult.Owner != null)
139 | {
140 | label ownerLabel = new label("Owner", testResult.Owner);
141 |
142 | testCase.Labels = new label[]{ ownerLabel };
143 |
144 | // allure doesnt support custom labels so until issue #394 is solved
145 | // the test description is used.
146 | //
147 | // https://github.com/allure-framework/allure-core/issues/394
148 |
149 | string description = FormatDescription(testResult);
150 |
151 | testCase.Description = new description(descriptiontype.text, description);
152 | }
153 |
154 | Allure.Lifecycle.Fire(testCase);
155 | }
156 |
157 | protected virtual void TestFinished(MSTestResult testResult)
158 | {
159 | Allure.Lifecycle.Fire(new TestCaseFinishedWithTimeEvent(testResult.End));
160 | }
161 |
162 | protected virtual void TestFailed(MSTestResult testResult)
163 | {
164 | Allure.Lifecycle.Fire(new TestCaseFailureWithErrorInfoEvent(testResult.ErrorInfo));
165 | }
166 |
167 | protected virtual void TestSuitStarted(string uid, string name, DateTime start)
168 | {
169 | Allure.Lifecycle.Fire(new TestSuiteStartedWithTimeEvent(uid, name, start));
170 | }
171 |
172 | protected virtual void TestSuitFinished(string uid, DateTime finished)
173 | {
174 | Allure.Lifecycle.Fire(new TestSuiteFinishedWithTimeEvent(uid, finished));
175 | }
176 |
177 | private string FormatDescription(MSTestResult testResult)
178 | {
179 | string description = testResult.Description;
180 | description += Environment.NewLine;
181 | description += "Test Owner: " + testResult.Owner;
182 | return description;
183 | }
184 | }
185 | }
186 |
--------------------------------------------------------------------------------
/MSTestAllureAdapter/AllureEvents/TestCaseFailureWithErrorInfoEvent.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using AllureCSharpCommons.Events;
3 | using AllureCSharpCommons.AllureModel;
4 |
5 | namespace MSTestAllureAdapter
6 | {
7 | public class TestCaseFailureWithErrorInfoEvent : TestCaseFailureEvent
8 | {
9 | ErrorInfo mErrorInfo;
10 |
11 | public TestCaseFailureWithErrorInfoEvent(ErrorInfo errorInfo)
12 | {
13 | // this assignment is safe because ErrorInfo is immutable
14 | mErrorInfo = errorInfo;
15 | }
16 |
17 | public override void Process(AllureCSharpCommons.AllureModel.testcaseresult context)
18 | {
19 | context.status = status.failed;
20 | context.failure = new failure
21 | {
22 | message = mErrorInfo.Message,
23 | stacktrace = mErrorInfo.StackTrace
24 | };
25 | }
26 | }
27 | }
28 |
29 |
--------------------------------------------------------------------------------
/MSTestAllureAdapter/AllureEvents/TestCaseFinishedWithTimeEvent.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using AllureCSharpCommons.Events;
3 |
4 | namespace MSTestAllureAdapter
5 | {
6 | public class TestCaseFinishedWithTimeEvent : TestCaseFinishedEvent
7 | {
8 | public TestCaseFinishedWithTimeEvent(DateTime finished)
9 | {
10 | Finished = finished;
11 | }
12 |
13 | public DateTime Finished { get; private set; }
14 |
15 | public override void Process(AllureCSharpCommons.AllureModel.testcaseresult context)
16 | {
17 | base.Process(context);
18 | context.stop = Finished.ToUnixEpochTime();
19 | }
20 | }
21 | }
22 |
23 |
--------------------------------------------------------------------------------
/MSTestAllureAdapter/AllureEvents/TestCaseStartedWithTimeEvent.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using AllureCSharpCommons.Events;
3 |
4 | namespace MSTestAllureAdapter
5 | {
6 | public class TestCaseStartedWithTimeEvent : TestCaseStartedEvent
7 | {
8 | public TestCaseStartedWithTimeEvent(string suitId, string name, DateTime time)
9 | : base(suitId, name)
10 | {
11 | Started = time;
12 | }
13 |
14 | public DateTime Started { get; private set; }
15 |
16 | public override void Process(AllureCSharpCommons.AllureModel.testcaseresult context)
17 | {
18 | base.Process(context);
19 | context.start = Started.ToUnixEpochTime();
20 | }
21 | }
22 | }
23 |
24 |
--------------------------------------------------------------------------------
/MSTestAllureAdapter/AllureEvents/TestSuiteFinishedWithTimeEvent.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using AllureCSharpCommons.Events;
3 |
4 | namespace MSTestAllureAdapter
5 | {
6 | public class TestSuiteFinishedWithTimeEvent : TestSuiteFinishedEvent
7 | {
8 | public TestSuiteFinishedWithTimeEvent(string uid, DateTime finished)
9 | : base(uid)
10 | {
11 | Finished = finished;
12 | }
13 |
14 | public DateTime Finished { get; private set; }
15 |
16 | public override void Process(AllureCSharpCommons.AllureModel.testsuiteresult context)
17 | {
18 | base.Process(context);
19 | context.stop = Finished.ToUnixEpochTime();
20 | }
21 | }
22 | }
23 |
24 |
--------------------------------------------------------------------------------
/MSTestAllureAdapter/AllureEvents/TestSuiteStartedWithTimeEvent.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using AllureCSharpCommons.Events;
3 |
4 | namespace MSTestAllureAdapter
5 | {
6 | public class TestSuiteStartedWithTimeEvent : TestSuiteStartedEvent
7 | {
8 | public TestSuiteStartedWithTimeEvent(string uid, string name, DateTime started)
9 | : base(uid, name)
10 | {
11 | Started = started;
12 | }
13 |
14 | public DateTime Started { get; private set; }
15 |
16 | public override void Process(AllureCSharpCommons.AllureModel.testsuiteresult context)
17 | {
18 | base.Process(context);
19 | context.start = Started.ToUnixEpochTime();
20 | }
21 | }
22 | }
23 |
24 |
--------------------------------------------------------------------------------
/MSTestAllureAdapter/ITestResultProvider.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 |
4 | namespace MSTestAllureAdapter
5 | {
6 | ///
7 | /// An abstraction on the way test results are provided.
8 | ///
9 | public interface ITestResultProvider
10 | {
11 | ///
12 | /// Gets the test results form the supplied file.
13 | ///
14 | /// The test results.
15 | /// The file containing the test results.
16 | IEnumerable GetTestResults(string filePath);
17 | }
18 | }
19 |
20 |
--------------------------------------------------------------------------------
/MSTestAllureAdapter/MSTestAllureAdapter.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Debug
5 | AnyCPU
6 | {9CED0C38-BCEB-4BFC-9294-38455758A461}
7 | Library
8 | MSTestAllureAdapter
9 | MSTestAllureAdapter
10 |
11 |
12 | true
13 | full
14 | false
15 | bin\Debug
16 | DEBUG;
17 | prompt
18 | 4
19 | false
20 |
21 |
22 | full
23 | true
24 | bin\Release
25 | prompt
26 | 4
27 | false
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 | {0C358997-150B-4752-8B69-8B174167D730}
63 | AllureCSharpCommons
64 |
65 |
66 |
--------------------------------------------------------------------------------
/MSTestAllureAdapter/MSTestResult.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 |
5 | namespace MSTestAllureAdapter
6 | {
7 | ///
8 | /// Test outcome.
9 | /// From the TestOutcome type in the vstst.xsd
10 | ///
11 | public enum TestOutcome {
12 | Unknown,
13 | Error,
14 | Failed,
15 | Timeout,
16 | Aborted,
17 | Inconclusive,
18 | PassedButRunAborted,
19 | NotRunnable,
20 | NotExecuted,
21 | Disconnected,
22 | Warning,
23 | Passed,
24 | Completed,
25 | InProgress,
26 | Pending
27 | }
28 |
29 | ///
30 | /// Represents a test result extracted from the trx file.
31 | ///
32 | public class MSTestResult
33 | {
34 | private IEnumerable mSuits;
35 |
36 | ///
37 | /// Copy constructor.
38 | ///
39 | /// Source MSTestResult.
40 | private MSTestResult(MSTestResult other)
41 | : this(other.Name, other.Outcome, other.Start, other.End, other.mSuits, other.InnerTests)
42 | {
43 | Owner = other.Owner;
44 | ErrorInfo = other.ErrorInfo; // ErrorInfo is immutable
45 | Description = other.Description;
46 | }
47 |
48 | ///
49 | /// Initializes a new instance of the class.
50 | ///
51 | /// Name.
52 | /// Outcome.
53 | /// Start time.
54 | /// End time.
55 | /// List of test suits this test belongs to.
56 | /// List of inner test results this test might have.
57 | public MSTestResult(string name, TestOutcome outcome, DateTime start, DateTime end, IEnumerable suits, IEnumerable innerResults)
58 | {
59 | Name = name;
60 |
61 | mSuits = suits ?? Enumerable.Empty();
62 |
63 | Outcome = outcome;
64 | Start = start;
65 | End = end;
66 |
67 | List results = new List();
68 | if (innerResults != null)
69 | {
70 | foreach (MSTestResult result in innerResults)
71 | {
72 | results.Add(result.Clone());
73 | }
74 | }
75 |
76 | InnerTests = results;
77 | }
78 |
79 | public MSTestResult(string name, TestOutcome outcome, DateTime start, DateTime end, string[] suits)
80 | : this(name, outcome, start, end, suits, Enumerable.Empty()) { }
81 |
82 |
83 | public MSTestResult (string testName, TestOutcome outcome, DateTime start, DateTime end, string suite)
84 | : this(testName, outcome, start, end, new string[] { suite }) { }
85 |
86 | public MSTestResult(string testName, TestOutcome outcome, string[] suits, IEnumerable innerResults)
87 | : this(testName, outcome, default(DateTime), default(DateTime), suits, innerResults) { }
88 |
89 |
90 | public MSTestResult(string testName, TestOutcome outcome, string suit, IEnumerable innerResults)
91 | : this(testName, outcome, default(DateTime), default(DateTime), new string[] { suit }, innerResults) { }
92 |
93 | public MSTestResult (string testName, TestOutcome outcome, params string[] suits)
94 | : this(testName, outcome, default(DateTime), default(DateTime), suits) { }
95 |
96 | public MSTestResult (string testName, TestOutcome outcome, string suite)
97 | : this(testName, outcome, default(DateTime), default(DateTime), suite) { }
98 |
99 | public MSTestResult (string testName, TestOutcome outcome)
100 | : this(testName, outcome, default(DateTime), default(DateTime), (string[])null) { }
101 |
102 | public string Name { get; private set; }
103 |
104 | public IEnumerable Suites
105 | {
106 | get { return mSuits; }
107 | }
108 |
109 | public TestOutcome Outcome { get; private set; }
110 |
111 | public DateTime Start { get; private set; }
112 |
113 | public DateTime End { get; private set; }
114 |
115 | public ErrorInfo ErrorInfo { get; set; }
116 |
117 | ///
118 | /// Gets or sets the test owner.
119 | /// According to the vstst.xsd currently only one owner is supported.
120 | ///
121 | /// The test owner.
122 | public string Owner { get; set; }
123 |
124 | public string Description { get; set; }
125 |
126 | public IEnumerable InnerTests { get; private set; }
127 |
128 | public MSTestResult Clone()
129 | {
130 | return new MSTestResult(this);
131 | }
132 |
133 | public override string ToString()
134 | {
135 | return string.Format("[MSTestResult: Name={0}, Suites=[{1}], Outcome={2}, Start={3}, End={4}, ErrorInfo={5}]", Name, String.Join(",", Suites), Outcome, Start, End, ErrorInfo);
136 | }
137 | }
138 | }
139 |
140 |
--------------------------------------------------------------------------------
/MSTestAllureAdapter/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.CompilerServices;
3 |
4 | // Information about this assembly is defined by the following attributes.
5 | // Change them to the values specific to your project.
6 |
7 | [assembly: AssemblyTitle ("MSTestAllureAdapter")]
8 | [assembly: AssemblyDescription ("")]
9 | [assembly: AssemblyConfiguration ("")]
10 | [assembly: AssemblyCompany ("")]
11 | [assembly: AssemblyProduct ("")]
12 | [assembly: AssemblyCopyright ("daniel")]
13 | [assembly: AssemblyTrademark ("")]
14 | [assembly: AssemblyCulture ("")]
15 |
16 | // The assembly version has the format "{Major}.{Minor}.{Build}.{Revision}".
17 | // The form "{Major}.{Minor}.*" will automatically update the build and revision,
18 | // and "{Major}.{Minor}.{Build}.*" will update just the revision.
19 |
20 | [assembly: AssemblyVersion ("1.0.*")]
21 |
22 | // The following attributes are used to specify the signing key for the assembly,
23 | // if desired. See the Mono documentation for more information about signing.
24 |
25 | //[assembly: AssemblyDelaySign(false)]
26 | //[assembly: AssemblyKeyFile("")]
27 |
28 | [assembly: InternalsVisibleTo("MSTestAllureAdapter.Tests")]
29 |
--------------------------------------------------------------------------------
/MSTestAllureAdapter/TRXParser/ErrorInfo.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Xml.Serialization;
4 | using System.IO;
5 | using System.Linq;
6 | using System.Xml.Linq;
7 | using System.Xml.XPath;
8 | using System.Xml;
9 |
10 | namespace MSTestAllureAdapter
11 | {
12 | ///
13 | /// Represents an ErrorInfo element in the trx file.
14 | ///
15 | /// >This class is immutable.
16 | public class ErrorInfo
17 | {
18 | public ErrorInfo(string message)
19 | : this(message, null) { }
20 |
21 | public ErrorInfo(string message, string stackTrace)
22 | : this(message, stackTrace, null) { }
23 |
24 | public ErrorInfo(string message, string stackTrace, string stdOut)
25 | {
26 | Message = message;
27 | StackTrace = stackTrace;
28 | StdOut = stdOut;
29 | }
30 |
31 | ///
32 | /// Gets or sets the message.
33 | ///
34 | /// The message.
35 | public string Message { get; private set; }
36 |
37 | ///
38 | /// Gets or sets the stack trace.
39 | ///
40 | /// The stack trace.
41 | public string StackTrace { get; private set; }
42 |
43 | ///
44 | /// Gets or sets the StdOut.
45 | ///
46 | /// The StdOut.
47 | public string StdOut { get; private set; }
48 |
49 | public override string ToString()
50 | {
51 | return string.Format("[ErrorInfo: Message={0}, StackTrace={1}, StdOut={2}]", Message, StackTrace, StdOut);
52 | }
53 |
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/MSTestAllureAdapter/TRXParser/TRXParser.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Xml.Serialization;
4 | using System.IO;
5 | using System.Linq;
6 | using System.Xml.Linq;
7 | using System.Xml.XPath;
8 | using System.Xml;
9 |
10 | namespace MSTestAllureAdapter
11 | {
12 | ///
13 | /// MSTest TRX parser.
14 | ///
15 | public class TRXParser : ITestResultProvider
16 | {
17 | // this namespace is required whenever using linq2xml on the trx.
18 | // for aesthetic reasons the naming convention was violated.
19 | private static readonly XNamespace ns = "http://microsoft.com/schemas/VisualStudio/TeamTest/2010";
20 |
21 | ///
22 | /// Parses the test results from the supplied trx file.
23 | ///
24 | /// The parsed test results.
25 | /// File path to the trx file.
26 | public IEnumerable GetTestResults(string filePath)
27 | {
28 | XDocument doc = XDocument.Load(filePath);
29 |
30 | IEnumerable unitTests = doc.Descendants(ns + "UnitTest");
31 |
32 | IEnumerable unitTestResults = doc.Descendants(ns + "UnitTestResult");
33 |
34 | Func outerKeySelector = _ => _.Element(ns + "Execution").Attribute("id").Value;
35 | Func innerKeySelector = _ => _.Attribute("executionId").Value;
36 | Func resultSelector = CreateMSTestResult;
37 |
38 | IEnumerable result = unitTests.Join(unitTestResults, outerKeySelector, innerKeySelector, resultSelector);
39 |
40 | // this will return the flat list of the tests with the inner tests.
41 | // here a test 'parent' that holds other tests will be discarded (such as the data driven tests).
42 | // result = result.EnumerateTestResults();
43 |
44 | return result;
45 | }
46 |
47 | private ErrorInfo ParseErrorInfo(XElement errorInfoXmlElement)
48 | {
49 | XmlNamespaceManager xmlNamespaceManager = new XmlNamespaceManager(new NameTable());
50 | xmlNamespaceManager.AddNamespace("prefix", ns.NamespaceName);
51 |
52 | errorInfoXmlElement = errorInfoXmlElement.Element(ns + "Output");
53 |
54 | XElement messageElement = errorInfoXmlElement.XPathSelectElement("prefix:ErrorInfo/prefix:Message", xmlNamespaceManager);
55 |
56 | string message = (messageElement != null) ? messageElement.Value : null;
57 |
58 | XElement stackTraceElement = errorInfoXmlElement.XPathSelectElement("prefix:ErrorInfo/prefix:StackTrace", xmlNamespaceManager);
59 |
60 | string stackTrace = (stackTraceElement != null) ? stackTraceElement.Value : null;
61 |
62 | XElement stdOutElement = errorInfoXmlElement.XPathSelectElement("prefix:StdOut", xmlNamespaceManager);
63 |
64 | string stdOut = (stdOutElement != null) ? stdOutElement.Value : null;
65 |
66 | return new ErrorInfo(message, stackTrace, stdOut);
67 | }
68 |
69 | private class UnitTestData
70 | {
71 | public string Name { get; set; }
72 | public string Owner { get; set; }
73 | public string Description { get; set; }
74 | public IEnumerable Suits { get; set; }
75 |
76 | }
77 |
78 | private MSTestResult CreateMSTestResult(XElement unitTest, XElement unitTestResult)
79 | {
80 | UnitTestData unitTestData = new UnitTestData();
81 | unitTestData.Name = unitTest.GetSafeAttributeValue(ns + "TestMethod", "name");
82 | unitTestData.Owner = GetOwner(unitTest);
83 | unitTestData.Description = GetDescription(unitTest);
84 | unitTestData.Suits = (from testCategory in unitTest.Descendants(ns + "TestCategoryItem")
85 | select testCategory.GetSafeAttributeValue("TestCategory")).ToList();
86 |
87 | return CreateMSTestResultInternal(unitTestData, unitTestResult);
88 | }
89 |
90 | private MSTestResult CreateMSTestResultInternal(UnitTestData unitTestData, XElement unitTestResult)
91 | {
92 | string dataRowInfo = unitTestResult.GetSafeAttributeValue("dataRowInfo");
93 |
94 | // in data driven tests this appends the input row number to the test name
95 | string unitTestName = unitTestData.Name;
96 |
97 | unitTestName += dataRowInfo;
98 |
99 | TestOutcome outcome = (TestOutcome)Enum.Parse(typeof(TestOutcome), unitTestResult.Attribute("outcome").Value);
100 |
101 | DateTime start = DateTime.Parse(unitTestResult.Attribute("startTime").Value);
102 |
103 | DateTime end = DateTime.Parse(unitTestResult.Attribute("endTime").Value);
104 |
105 | /*
106 | if (categories.Length == 0)
107 | categories = new string[]{ DEFAULT_CATEGORY };
108 | */
109 |
110 | IEnumerable innerTestResults = ParseInnerTestResults(unitTestData, unitTestResult);
111 |
112 | MSTestResult testResult = new MSTestResult(unitTestName, outcome, start, end, unitTestData.Suits, innerTestResults);
113 |
114 | bool containsInnerTestResults = unitTestResult.Element(ns + "InnerResults") == null;
115 | if ((outcome == TestOutcome.Error || outcome == TestOutcome.Failed) && containsInnerTestResults)
116 | {
117 | testResult.ErrorInfo = ParseErrorInfo(unitTestResult);
118 | }
119 |
120 | testResult.Owner = unitTestData.Owner;
121 |
122 | testResult.Description = unitTestData.Description;
123 |
124 | return testResult;
125 | }
126 |
127 | private IEnumerable ParseInnerTestResults(UnitTestData unitTestData, XElement unitTestResult)
128 | {
129 | IEnumerable innerResultsElements = unitTestResult.Descendants(ns + "InnerResults");
130 |
131 | if (!innerResultsElements.Any())
132 | return null;
133 |
134 | // the schema for the trx states there can be multiple 'InnerResults' elements but
135 | // until we see it we take the first.
136 | // In the future if it will be required to handle multiple 'InnerResults' elements
137 | // one can wrap the comming loop in another loop that loops over them.
138 | XElement innerResultsElement = innerResultsElements.FirstOrDefault();
139 |
140 | IList result = new List();
141 |
142 | foreach (XElement innerUnitTestResult in innerResultsElement.Descendants(ns + "UnitTestResult"))
143 | {
144 | result.Add(CreateMSTestResultInternal(unitTestData, innerUnitTestResult));
145 | }
146 |
147 | return result;
148 | }
149 |
150 | private string GetOwner(XElement unitTestElement)
151 | {
152 | string owner = null;
153 |
154 | XElement ownerElement = unitTestElement.Descendants(ns + "Owner").FirstOrDefault();
155 |
156 | if (ownerElement != null)
157 | {
158 | XAttribute ownerAttribute = ownerElement.Attribute("name");
159 | owner = ownerAttribute.Value;
160 | }
161 |
162 | return owner;
163 | }
164 |
165 | private string GetDescription(XElement unitTestElement)
166 | {
167 | string description = null;
168 |
169 | XElement descriptionElement = unitTestElement.Element(ns + "Description");
170 |
171 | if (descriptionElement != null)
172 | {
173 | description = descriptionElement.Value;
174 | }
175 |
176 | return description;
177 | }
178 | }
179 | }
180 |
181 |
--------------------------------------------------------------------------------
/MSTestAllureAdapter/TRXParser/XElementExtensions.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Xml.Linq;
3 |
4 | namespace MSTestAllureAdapter
5 | {
6 | ///
7 | /// Convenience methods for XElement
8 | ///
9 | internal static class XElementExtensions
10 | {
11 | ///
12 | /// Gets the value of the element with the given name.
13 | ///
14 | /// The value if found, String.Empty otherwise.
15 | /// Element.
16 | /// Name.
17 | public static string GetSafeValue(this XElement element, XName name)
18 | {
19 | string result = String.Empty;
20 |
21 | element = element.Element(name);
22 |
23 | if (element != null)
24 | {
25 | result = element.Value;
26 | }
27 |
28 | return result;
29 | }
30 |
31 | ///
32 | /// Gets the attribute value with the given attributeName of the element with the given descendantElement name.
33 | ///
34 | /// The attribute value if found, String.Empty otherwise.
35 | /// Element.
36 | /// Descendant element.
37 | /// Attribute name.
38 | public static string GetSafeAttributeValue(this XElement element, XName descendantElement, XName attributeName)
39 | {
40 | string result = String.Empty;
41 |
42 | element = element.Element(descendantElement);
43 |
44 | if (element != null && element.Attribute(attributeName) != null)
45 | {
46 | result = element.Attribute(attributeName).Value;
47 | }
48 |
49 | return result;
50 | }
51 |
52 | ///
53 | /// Gets the attribute value with the given attributeName.
54 | ///
55 | /// The attribute value if found, String.Empty otherwise.
56 | /// Element.
57 | /// Attribute name.
58 | public static string GetSafeAttributeValue(this XElement element, XName attributeName)
59 | {
60 | string result = String.Empty;
61 |
62 | if (element != null && element.Attribute(attributeName) != null)
63 | {
64 | result = element.Attribute(attributeName).Value;
65 | }
66 |
67 | return result;
68 | }
69 | }
70 | }
71 |
72 |
--------------------------------------------------------------------------------
/MSTestAllureAdapter/Utils/AllureResultsUtils.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Linq;
3 | using System.Collections.Generic;
4 |
5 | namespace MSTestAllureAdapter
6 | {
7 | ///
8 | /// Utility method
9 | /// Allure results utils.
10 | ///
11 | public static class AllureResultsUtils
12 | {
13 | public static long ToUnixEpochTime(this DateTime time)
14 | {
15 | return (long) (time.ToUniversalTime() - new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc)).TotalMilliseconds;
16 | }
17 | }
18 | }
19 |
20 |
--------------------------------------------------------------------------------
/MSTestAllureAdapter/Utils/MSTestResultUtils.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Linq;
3 | using System.Collections.Generic;
4 |
5 | namespace MSTestAllureAdapter
6 | {
7 | public static class MSTestResultUtils
8 | {
9 | public static IEnumerable EnumerateTestResults(this IEnumerable results)
10 | {
11 | foreach (MSTestResult testReslt in results)
12 | {
13 | if (testReslt.InnerTests != null && testReslt.InnerTests.Any())
14 | {
15 | foreach (MSTestResult innerTestResult in testReslt.InnerTests)
16 | {
17 | yield return innerTestResult;
18 | }
19 | }
20 | else
21 | {
22 | yield return testReslt;
23 | }
24 | }
25 | }
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | MSTestAllureAdapter
2 | ===================
3 |
4 | [](https://travis-ci.org/allure-framework/allure-mstest-adapter)
5 |
6 | **MSTestAllureAdapter** allows you convert an MSTest trx file to the XMLs from which an Allure report can be generated.
7 |
8 | Because MSTest does not have an extension or hook mechanism this adapter cannot run as part of MSTest but instead it converts the resulting trx file to the xml format expected by allure.
9 |
10 | It is a .NET/Mono based console application.
11 |
12 |
13 | # Usage
14 | ```bash
15 | MSTestAllureAdapter.Console.exe [output target dir]
16 | ```
17 |
18 | If '[output target dir]' is missing the reslts are saved in the current directory in a folder named 'results'.
19 |
20 | ```bash
21 | $ mono MSTestAllureAdapter.Console.exe sample.trx
22 | ```
23 |
24 | This will generate the xml files from which the allure report can be created based upon the 'sample.trx' file.
25 |
26 | ```bash
27 | $ mono MSTestAllureAdapter.Console.exe sample.trx output-xmls
28 | ```
29 |
30 | This will generate the xml files from which the allure report can be created in a folder named 'output-xmls' based upon the 'sample.trx' file.
31 |
32 | If the target directory does not exists it is created.
33 |
34 |
35 |
36 | To generate a report using [allure-cli](https://github.com/allure-framework/allure-cli/releases/tag/allure-cli-2.1):
37 | ```bash
38 | $ allure generate output-xmls -v 1.4.0
39 | ```
40 |
--------------------------------------------------------------------------------
/SampleTestingProject/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.CompilerServices;
3 | using System.Runtime.InteropServices;
4 |
5 | // General Information about an assembly is controlled through the following
6 | // set of attributes. Change these attribute values to modify the information
7 | // associated with an assembly.
8 | [assembly: AssemblyTitle("SampleTestingProject")]
9 | [assembly: AssemblyDescription("")]
10 | [assembly: AssemblyConfiguration("")]
11 | [assembly: AssemblyCompany("")]
12 | [assembly: AssemblyProduct("SampleTestingProject")]
13 | [assembly: AssemblyCopyright("Copyright © 2014")]
14 | [assembly: AssemblyTrademark("")]
15 | [assembly: AssemblyCulture("")]
16 |
17 | // Setting ComVisible to false makes the types in this assembly not visible
18 | // to COM components. If you need to access a type in this assembly from
19 | // COM, set the ComVisible attribute to true on that type.
20 | [assembly: ComVisible(false)]
21 |
22 | // The following GUID is for the ID of the typelib if this project is exposed to COM
23 | [assembly: Guid("b4a29084-c495-4254-a2ff-d2d71911c4a6")]
24 |
25 | // Version information for an assembly consists of the following four values:
26 | //
27 | // Major Version
28 | // Minor Version
29 | // Build Number
30 | // Revision
31 | //
32 | // You can specify all the values or you can default the Build and Revision Numbers
33 | // by using the '*' as shown below:
34 | // [assembly: AssemblyVersion("1.0.*")]
35 | [assembly: AssemblyVersion("1.0.0.0")]
36 | [assembly: AssemblyFileVersion("1.0.0.0")]
37 |
--------------------------------------------------------------------------------
/SampleTestingProject/SampleTestingProject.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Debug
5 | AnyCPU
6 | {A9A6251A-937F-4E43-AB49-BF21E3E04124}
7 | Library
8 | Properties
9 | SampleTestingProject
10 | SampleTestingProject
11 | v4.0
12 | 512
13 | {3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}
14 | 10.0
15 | $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)
16 | $(ProgramFiles)\Common Files\microsoft shared\VSTT\$(VisualStudioVersion)\UITestExtensionPackages
17 | False
18 | UnitTest
19 |
20 |
21 | true
22 | full
23 | false
24 | bin\Debug\
25 | DEBUG;TRACE
26 | prompt
27 | 4
28 |
29 |
30 | pdbonly
31 | true
32 | bin\Release\
33 | TRACE
34 | prompt
35 | 4
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 | PreserveNewest
60 |
61 |
62 |
63 |
64 |
65 |
66 | False
67 |
68 |
69 | False
70 |
71 |
72 | False
73 |
74 |
75 | False
76 |
77 |
78 |
79 |
80 |
81 |
82 |
89 |
--------------------------------------------------------------------------------
/SampleTestingProject/TRXSample.vs2010.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 11.00
3 | # Visual Studio 2010
4 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SampleTestingProject", "SampleTestingProject.csproj", "{A9A6251A-937F-4E43-AB49-BF21E3E04124}"
5 | EndProject
6 | Global
7 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
8 | Debug|Any CPU = Debug|Any CPU
9 | Release|Any CPU = Release|Any CPU
10 | EndGlobalSection
11 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
12 | {A9A6251A-937F-4E43-AB49-BF21E3E04124}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
13 | {A9A6251A-937F-4E43-AB49-BF21E3E04124}.Debug|Any CPU.Build.0 = Debug|Any CPU
14 | {A9A6251A-937F-4E43-AB49-BF21E3E04124}.Release|Any CPU.ActiveCfg = Release|Any CPU
15 | {A9A6251A-937F-4E43-AB49-BF21E3E04124}.Release|Any CPU.Build.0 = Release|Any CPU
16 | EndGlobalSection
17 | GlobalSection(SolutionProperties) = preSolution
18 | HideSolutionNode = FALSE
19 | EndGlobalSection
20 | EndGlobal
21 |
--------------------------------------------------------------------------------
/SampleTestingProject/TRXSample.vs2012.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio 2013
4 | VisualStudioVersion = 12.0.30501.0
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SampleTestingProject", "SampleTestingProject.csproj", "{A9A6251A-937F-4E43-AB49-BF21E3E04124}"
7 | EndProject
8 | Global
9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
10 | Debug|Any CPU = Debug|Any CPU
11 | Release|Any CPU = Release|Any CPU
12 | EndGlobalSection
13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
14 | {A9A6251A-937F-4E43-AB49-BF21E3E04124}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
15 | {A9A6251A-937F-4E43-AB49-BF21E3E04124}.Debug|Any CPU.Build.0 = Debug|Any CPU
16 | {A9A6251A-937F-4E43-AB49-BF21E3E04124}.Release|Any CPU.ActiveCfg = Release|Any CPU
17 | {A9A6251A-937F-4E43-AB49-BF21E3E04124}.Release|Any CPU.Build.0 = Release|Any CPU
18 | EndGlobalSection
19 | GlobalSection(SolutionProperties) = preSolution
20 | HideSolutionNode = FALSE
21 | EndGlobalSection
22 | EndGlobal
23 |
--------------------------------------------------------------------------------
/SampleTestingProject/UnitTest1.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using Microsoft.VisualStudio.TestTools.UnitTesting;
3 | using System.IO;
4 |
5 | namespace SampleTestingProject
6 | {
7 | [TestClass]
8 | public class UnitTest1
9 | {
10 | [TestMethod]
11 | [TestCategory("Category1")]
12 | [Owner("Owner1")]
13 | [Description("Description of the TestMethod1 UnitTest.")]
14 | public void TestMethod1()
15 | {
16 | Assert.IsTrue(true);
17 | }
18 |
19 | [TestMethod]
20 | [TestCategory("Category1")]
21 | [TestCategory("Category2")]
22 | [Owner("Owner1")]
23 | public void TestMethod2()
24 | {
25 | Assert.IsTrue(true);
26 | }
27 |
28 | [TestMethod]
29 | [TestCategory("Category2")]
30 | [Owner("Owner1")]
31 | public void TestMethod3()
32 | {
33 | Assert.IsTrue(true);
34 | }
35 |
36 | [TestMethod]
37 | public void TestMethod_With_One_Missing_And_One_present_Result_File()
38 | {
39 | File.WriteAllText("file.xxx", "file.xxx contents.");
40 | TestContext.AddResultFile("file.xxx");
41 |
42 | TestContext.AddResultFile("missing.zzz");
43 | Assert.IsTrue(true);
44 | }
45 |
46 | [TestMethod]
47 | public void TestMethod_With_Only_Missing_Result_File()
48 | {
49 | TestContext.AddResultFile("missing.zzz");
50 | Assert.IsTrue(true);
51 | }
52 |
53 | [TestMethod]
54 | public void TestMethod_With_Multiple_Result_Files()
55 | {
56 | File.WriteAllText("file.xxx", "file.xxx contents.");
57 | TestContext.AddResultFile("file.xxx");
58 |
59 | File.WriteAllText("file.yyy", "file.yyy contents.");
60 | TestContext.AddResultFile("file.yyy");
61 |
62 | TestContext.AddResultFile("missing.zzz");
63 | Assert.IsTrue(true);
64 | }
65 |
66 | [TestMethod]
67 | public void Test_Without_Category()
68 | {
69 | Assert.IsTrue(true);
70 | }
71 |
72 | [TestMethod]
73 | public void SimpleFailingTest()
74 | {
75 | Assert.AreEqual(1, 2, "The calculation failed.");
76 | }
77 |
78 | [TestMethod]
79 | [ExpectedException(typeof(ArgumentOutOfRangeException))]
80 | public void ExpectedException()
81 | {
82 | throw new ArgumentOutOfRangeException("value", "This value cannot be set to the specified range.");
83 | }
84 |
85 | [Owner("Owner2")]
86 | [TestMethod]
87 | [ExpectedException(typeof(ArgumentOutOfRangeException), "Expected ArgumentOutOfRangeException was not fired.")]
88 | public void ExpectedExceptionWithNoExceptionMessage()
89 | {
90 | throw new ArgumentOutOfRangeException("value", "This value cannot be set to the specified range.");
91 | }
92 |
93 | [Owner("Owner2")]
94 | [TestMethod]
95 | public void UnexpectedException()
96 | {
97 | throw new Exception();
98 | }
99 |
100 | public TestContext TestContext
101 | {
102 | get;
103 | set;
104 | }
105 |
106 | //[DataSource("Microsoft.Visualstudio.TestTools.DataSource.CSV", "|DataDirectory|\\UserData.csv", "UserData#csv", Microsoft.VisualStudio.TestTools.UnitTesting.DataAccessMethod.Sequential)]
107 | [DataSource("System.Data.OleDb", "Provider=Microsoft.Jet.OLEDB.4.0;Data Source='|DataDirectory|';Extended Properties=\"text;HDR=Yes;FMT=Delimited\"", "UserData#csv", Microsoft.VisualStudio.TestTools.UnitTesting.DataAccessMethod.Sequential)]
108 | [DeploymentItem("UserData.csv")]
109 | [TestCategory("Category3")]
110 | [Owner("Owner3")]
111 | [TestMethod]
112 | [Description("Description of the CSVDataDrivenTest data driven test.")]
113 | public void CSVDataDrivenTest()
114 | {
115 | int a = Convert.ToInt32(TestContext.DataRow["Operator 1"]);
116 | int b = Convert.ToInt32(TestContext.DataRow["Operator 2"]);
117 | int expected = Convert.ToInt32(TestContext.DataRow["Outcome"]);
118 |
119 | int actual = a / b;
120 | Assert.AreEqual(expected, actual);
121 | }
122 | }
123 | }
124 |
--------------------------------------------------------------------------------
/SampleTestingProject/UserData.csv:
--------------------------------------------------------------------------------
1 | Operator 1, Operator 2, Outcome
2 | 9, 3, 3
3 | 2, 2, 1
4 | 1, 0, -1
--------------------------------------------------------------------------------