Skip to content

Commit

Permalink
Merge pull request #282 from smadala/trx-logger-enhancement
Browse files Browse the repository at this point in the history
fixes #243
  • Loading branch information
smadala authored Dec 26, 2016
2 parents 055287c + 1fb09a7 commit fc1f7b9
Show file tree
Hide file tree
Showing 12 changed files with 338 additions and 126 deletions.
57 changes: 37 additions & 20 deletions scripts/test.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -63,15 +63,18 @@ $Script:TPT_SkipProjects = @("testhost.UnitTests", "vstest.console.UnitTests")
$Script:TPT_Pattern = $Pattern
$Script:TPT_FailFast = $FailFast
$Script:TPT_Parallel = $Parallel
$Script:TPT_TestResultsDir = Join-Path $env:TP_ROOT_DIR "TestResults"
$Script:TPT_DefaultTrxFileName = "TrxLogResults.trx"
$Script:TPT_ErrorMsgColor = "Red"

#
# Capture error state in any step globally to modify return code
$Script:ScriptFailed = $false

function Write-Log ([string] $message)
function Write-Log ([string] $message, $messageColor = "Green")
{
$currentColor = $Host.UI.RawUI.ForegroundColor
$Host.UI.RawUI.ForegroundColor = "Green"
$Host.UI.RawUI.ForegroundColor = $messageColor
if ($message)
{
Write-Output "... $message"
Expand All @@ -84,6 +87,19 @@ function Write-VerboseLog([string] $message)
Write-Verbose $message
}

function Print-FailedTests($TrxFilePath)
{
if(![System.IO.File]::Exists($TrxFilePath)){
Write-Log "TrxFile: $TrxFilePath doesn't exists"
return
}
$xdoc = [xml] (get-content $TrxFilePath)
$FailedTestIds = $xdoc.TestRun.Results.UnitTestResult |?{$_.GetAttribute("outcome") -eq "Failed"} | %{$_.testId}
if ($FailedTestIds) {
Write-Log (".. .. . " + ($xdoc.TestRun.TestDefinitions.UnitTest | ?{ $FailedTestIds.Contains($_.GetAttribute("id")) } | %{ "$($_.TestMethod.className).$($_.TestMethod.name)"})) $Script:TPT_ErrorMsgColor
}
}

function Invoke-Test
{
$timer = Start-Timer
Expand Down Expand Up @@ -147,16 +163,16 @@ function Invoke-Test
if ($TPT_Parallel) {
# Fill in the framework in test containers
$testContainerSet = $testContainers | % { [System.String]::Format($_, $fx) }

$trxLogFileName = [System.String]::Format("Parallel_{0}_{1}", $fx, $Script:TPT_DefaultTrxFileName)
Set-TestEnvironment
if($fx -eq $TPT_TargetFrameworkFullCLR){
if($fx -eq $TPT_TargetFrameworkFullCLR) {

Write-Verbose "$vstestConsolePath $testContainerSet /platform:$testArchitecture /framework:$testFrameWork /testAdapterPath:$testAdapterPath /parallel"
$output = & $vstestConsolePath $testContainerSet /platform:$testArchitecture /framework:$testFrameWork /testAdapterPath:"$testAdapterPath" /parallel
}else{
Write-Verbose "$vstestConsolePath $testContainerSet /platform:$testArchitecture /framework:$testFrameWork /testAdapterPath:$testAdapterPath /parallel /logger:`"trx;LogFileName=$trxLogFileName`""
$output = & $vstestConsolePath $testContainerSet /platform:$testArchitecture /framework:$testFrameWork /testAdapterPath:"$testAdapterPath" /parallel /logger:"trx;LogFileName=$trxLogFileName"
} else {

Write-Verbose "$dotNetPath $vstestConsolePath $testContainerSet /platform:$testArchitecture /framework:$testFrameWork /testAdapterPath:$testAdapterPath /parallel"
$output = & $dotNetPath $vstestConsolePath $testContainerSet /platform:$testArchitecture /framework:$testFrameWork /testAdapterPath:"$testAdapterPath" /parallel
Write-Verbose "$dotNetPath $vstestConsolePath $testContainerSet /platform:$testArchitecture /framework:$testFrameWork /testAdapterPath:$testAdapterPath /parallel /logger:`"trx;LogFileName=$trxLogFileName`""
$output = & $dotNetPath $vstestConsolePath $testContainerSet /platform:$testArchitecture /framework:$testFrameWork /testAdapterPath:"$testAdapterPath" /parallel /logger:"trx;LogFileName=$trxLogFileName"
}

Reset-TestEnvironment
Expand All @@ -165,8 +181,8 @@ function Invoke-Test
Write-Log ".. . $($output[-3])"
} else {
Write-Log ".. . $($output[-2])"
Write-Log ".. . Failed tests:"
Write-Log ".. . $($output -match '^Failed')"
Write-Log ".. . Failed tests:" $Script:TPT_ErrorMsgColor
Print-FailedTests (Join-Path $Script:TPT_TestResultsDir $trxLogFileName)

Set-ScriptFailed

Expand All @@ -179,28 +195,29 @@ function Invoke-Test
$testContainers | % {
# Fill in the framework in test containers
$testContainer = [System.String]::Format($_, $fx)
$trxLogFileName = [System.String]::Format("{0}_{1}_{2}", ($(Get-ChildItem $testContainer).Name), $fx, $Script:TPT_DefaultTrxFileName)

Write-Log ".. Container: $testContainer"

Set-TestEnvironment

if($fx -eq $TPT_TargetFrameworkFullCLR){
if($fx -eq $TPT_TargetFrameworkFullCLR) {

Write-Verbose "$vstestConsolePath $testContainer /platform:$testArchitecture /framework:$testFrameWork /testAdapterPath:$testAdapterPath"
$output = & $vstestConsolePath $testContainer /platform:$testArchitecture /framework:$testFrameWork /testAdapterPath:"$testAdapterPath"
}else{
Write-Verbose "$vstestConsolePath $testContainer /platform:$testArchitecture /framework:$testFrameWork /testAdapterPath:$testAdapterPath /logger:`"trx;LogFileName=$trxLogFileName`""
$output = & $vstestConsolePath $testContainer /platform:$testArchitecture /framework:$testFrameWork /testAdapterPath:"$testAdapterPath" /logger:"trx;LogFileName=$trxLogFileName"
} else {

Write-Verbose "$dotNetPath $vstestConsolePath $testContainer /platform:$testArchitecture /framework:$testFrameWork /testAdapterPath:$testAdapterPath"
$output = & $dotNetPath $vstestConsolePath $testContainer /platform:$testArchitecture /framework:$testFrameWork /testAdapterPath:"$testAdapterPath"
Write-Verbose "$dotNetPath $vstestConsolePath $testContainer /platform:$testArchitecture /framework:$testFrameWork /testAdapterPath:$testAdapterPath /logger:`"trx;LogFileName=$trxLogFileName`""
$output = & $dotNetPath $vstestConsolePath $testContainer /platform:$testArchitecture /framework:$testFrameWork /testAdapterPath:"$testAdapterPath" /logger:"trx;LogFileName=$trxLogFileName"
}

Reset-TestEnvironment

if ($output[-2].Contains("Test Run Successful.")) {
Write-Log ".. . $($output[-3])"
} else {
Write-Log ".. . $($output[-2])"
Write-Log ".. . Failed tests:"
Write-Log ".. . $($output -match '^Failed')"
Write-Log ".. . Failed tests:" $Script:TPT_ErrorMsgColor
Print-FailedTests (Join-Path $Script:TPT_TestResultsDir $trxLogFileName)

Set-ScriptFailed

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -385,8 +385,7 @@ private Dictionary<string, string> UpdateLoggerParamters(Dictionary<string, stri
}

// Add default logger parameters...
// todo Read Output Directory from RunSettings
loggerParams[DefaultLoggerParameterNames.TestRunDirectory] = null;
loggerParams[DefaultLoggerParameterNames.TestRunDirectory] = this.GetResultsDirectory(RunSettingsManager.Instance.ActiveRunSettings);
return loggerParams;
}

Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -208,4 +208,7 @@ Error Details: {1}:{2}</value>
<data name="TS_UncategorizedResults" xml:space="preserve">
<value>Results Not in a List</value>
</data>
<data name="TrxLoggerResultsFileOverwriteWarning" xml:space="preserve">
<value>WARNING: Overwriting results file: {0}</value>
</data>
</root>
148 changes: 92 additions & 56 deletions src/Microsoft.TestPlatform.Extensions.TrxLogger/TrxLogger.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ namespace Microsoft.VisualStudio.TestPlatform.Extensions.TrxLogger
/// </summary>
[FriendlyName(TrxLogger.FriendlyName)]
[ExtensionUri(TrxLogger.ExtensionUri)]
internal class TrxLogger : ITestLogger
internal class TrxLogger : ITestLoggerWithParameters
{
#region Constants

Expand All @@ -47,14 +47,19 @@ internal class TrxLogger : ITestLogger
/// </summary>
public const string DataCollectorUriPrefix = "dataCollector://";

/// <summary>
/// Log file parameter key
/// </summary>
public const string LogFileNameKey = "LogFileName";

#endregion

#region Fields

/// <summary>
/// Cache the TRX filename
/// Cache the TRX file path
/// </summary>
private static string trxFileName;
private string trxFilePath;

private TrxLoggerObjectModel.TestRun testRun;
private List<TrxLoggerObjectModel.UnitTestResult> results;
Expand All @@ -75,47 +80,59 @@ internal class TrxLogger : ITestLogger

private DateTime testRunStartTime;

#endregion
/// <summary>
/// Parameters dictionary for logger. Ex: {"LogFileName":"TestResults.trx"}.
/// </summary>
private Dictionary<string, string> parametersDictionary;

/// <summary>
/// Gets the directory under which trx file should be saved.
/// Gets the directory under which default trx file and test results attachements should be saved.
/// </summary>
[SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1650:ElementDocumentationMustBeSpelledCorrectly", Justification = "Reviewed. Suppression is OK here.")]
public static string TrxFileDirectory
{
get;
internal set;
}
private string testResultsDirPath;

#endregion

#region ITestLogger

/// <summary>
/// Initializes the Test Logger.
/// </summary>
/// <param name="events">Events that can be registered for.</param>
/// <param name="testRunDirectory">Test Run Directory</param>
public void Initialize(TestLoggerEvents events, string testRunDirectory)
/// <inheritdoc/>
public void Initialize(TestLoggerEvents events, string testResultsDirPath)
{
if (events == null)
{
throw new ArgumentNullException(nameof(events));
}

if (string.IsNullOrEmpty(testRunDirectory))
if (string.IsNullOrEmpty(testResultsDirPath))
{
throw new ArgumentNullException(nameof(testRunDirectory));
throw new ArgumentNullException(nameof(testResultsDirPath));
}

// Register for the events.
events.TestRunMessage += this.TestMessageHandler;
events.TestResult += this.TestResultHandler;
events.TestRunComplete += this.TestRunCompleteHandler;

TrxFileDirectory = testRunDirectory;
this.testResultsDirPath = testResultsDirPath;

this.InitializeInternal();
}

/// <inheritdoc/>
public void Initialize(TestLoggerEvents events, Dictionary<string, string> parameters)
{
if (parameters == null)
{
throw new ArgumentNullException(nameof(parameters));
}

if (parameters.Count == 0)
{
throw new ArgumentException("No default parameters added", nameof(parameters));
}

this.parametersDictionary = parameters;
this.Initialize(events, this.parametersDictionary[DefaultLoggerParameterNames.TestRunDirectory]);
}
#endregion

#region ForTesting
Expand Down Expand Up @@ -257,7 +274,7 @@ internal void TestResultHandler(object sender, ObjectModel.Logging.TestResultEve

// Conver the rocksteady result to MSTest result
TrxLoggerObjectModel.TestOutcome testOutcome = Converter.ToOutcome(e.Result.Outcome);
TrxLoggerObjectModel.UnitTestResult testResult = Converter.ToUnitTestResult(e.Result, testElement, testOutcome, this.testRun, TrxFileDirectory);
TrxLoggerObjectModel.UnitTestResult testResult = Converter.ToUnitTestResult(e.Result, testElement, testOutcome, this.testRun, this.testResultsDirPath);

// Set various counts (passtests, failed tests, total tests)
this.totalTests++;
Expand Down Expand Up @@ -331,8 +348,8 @@ internal void TestRunCompleteHandler(object sender, TestRunCompleteEventArgs e)
}

List<string> errorMessages = new List<string>();
List<CollectorDataEntry> collectorEntries = Converter.ToCollectionEntries(e.AttachmentSets, this.testRun, TrxFileDirectory);
IList<String> resultFiles = Converter.ToResultFiles(e.AttachmentSets, this.testRun, TrxFileDirectory, errorMessages);
List<CollectorDataEntry> collectorEntries = Converter.ToCollectionEntries(e.AttachmentSets, this.testRun, this.testResultsDirPath);
IList<String> resultFiles = Converter.ToResultFiles(e.AttachmentSets, this.testRun, this.testResultsDirPath, errorMessages);

if (errorMessages.Count > 0)
{
Expand All @@ -358,19 +375,9 @@ internal void TestRunCompleteHandler(object sender, TestRunCompleteEventArgs e)

helper.SaveObject(runSummary, rootElement, "ResultSummary", parameters);

if (Directory.Exists(TrxFileDirectory) == false)
{
Directory.CreateDirectory(TrxFileDirectory);
}

if (string.IsNullOrEmpty(trxFileName))
{
// save the xml to file in testResultsFolder
// [RunDeploymentRootDirectory] Replace white space with underscore from trx file name to make it command line friendly
trxFileName = this.GetTrxFileName(TrxFileDirectory, this.testRun.RunConfiguration.RunDeploymentRootDirectory.Replace(' ', '_'));
}

this.PopulateTrxFile(trxFileName, rootElement);
//Save results to Trx file
this.DeriveTrxFilePath();
this.PopulateTrxFile(this.trxFilePath, rootElement);
}
}

Expand All @@ -387,35 +394,34 @@ internal virtual void PopulateTrxFile(string trxFileName, XmlElement rootElement
{
try
{
FileStream fs = File.OpenWrite(trxFileName);
rootElement.OwnerDocument.Save(fs);
var trxFileDirPath = Path.GetDirectoryName(trxFilePath);
if (Directory.Exists(trxFileDirPath) == false)
{
Directory.CreateDirectory(trxFileDirPath);
}

if (File.Exists(trxFilePath))
{
var overwriteWarningMsg = string.Format(CultureInfo.CurrentCulture,
TrxLoggerResources.TrxLoggerResultsFileOverwriteWarning, trxFileName);
Console.WriteLine(overwriteWarningMsg);
EqtTrace.Warning(overwriteWarningMsg);
}

using (var fs = File.Open(trxFileName, FileMode.Create))
{
rootElement.OwnerDocument.Save(fs);
}
String resultsFileMessage = String.Format(CultureInfo.CurrentCulture, TrxLoggerResources.TrxLoggerResultsFile, trxFileName);
Console.WriteLine(resultsFileMessage);
EqtTrace.Info(resultsFileMessage);
}
catch (System.UnauthorizedAccessException fileWriteException)
{
Console.WriteLine(fileWriteException.Message);
}
}

/// <summary>
/// Get full path to trx file
/// </summary>
/// <param name="baseDirectory">
/// The base Directory.
/// </param>
/// <param name="trxFileName">
/// The trx File Name.
/// </param>
/// <returns>
/// trx file name.
/// </returns>
[SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1650:ElementDocumentationMustBeSpelledCorrectly", Justification = "Reviewed. Suppression is OK here.")]
private string GetTrxFileName(string baseDirectory, string trxFileName)
{
return FileHelper.GetNextIterationFileName(baseDirectory, trxFileName + ".trx", false);
}

// Initializes trx logger cache.
private void InitializeInternal()
{
Expand Down Expand Up @@ -454,6 +460,36 @@ private void HandleSkippedTest(ObjectModel.TestResult rsTestResult)
this.AddRunLevelInformationalMessage(message);
}

private void DeriveTrxFilePath()
{
if (this.parametersDictionary != null)
{
var isLogFileNameParameterExists = this.parametersDictionary.TryGetValue(TrxLogger.LogFileNameKey, out string logFileNameValue);
if (isLogFileNameParameterExists && !string.IsNullOrWhiteSpace(logFileNameValue))
{
this.trxFilePath = Path.Combine(this.testResultsDirPath, logFileNameValue);
}
else
{
this.SetDefaultTrxFilePath();
}
}
else
{
this.SetDefaultTrxFilePath();
}
}

/// <summary>
/// Sets auto generated Trx file name under test results directory.
/// </summary>
private void SetDefaultTrxFilePath()
{
// [RunDeploymentRootDirectory] Replace white space with underscore from trx file name to make it command line friendly
var defaultTrxFileName = this.testRun.RunConfiguration.RunDeploymentRootDirectory.Replace(' ', '_') + ".trx";
this.trxFilePath = FileHelper.GetNextIterationFileName(this.testResultsDirPath, defaultTrxFileName, false);
}

#endregion
}
}
Loading

0 comments on commit fc1f7b9

Please sign in to comment.