@@ -28,7 +28,7 @@ namespace Microsoft.VisualStudio.TestPlatform.Extensions.TrxLogger
2828 /// </summary>
2929 [ FriendlyName ( TrxLogger . FriendlyName ) ]
3030 [ ExtensionUri ( TrxLogger . ExtensionUri ) ]
31- internal class TrxLogger : ITestLogger
31+ internal class TrxLogger : ITestLoggerWithParameters
3232 {
3333 #region Constants
3434
@@ -47,14 +47,19 @@ internal class TrxLogger : ITestLogger
4747 /// </summary>
4848 public const string DataCollectorUriPrefix = "dataCollector://" ;
4949
50+ /// <summary>
51+ /// Log file parameter key
52+ /// </summary>
53+ public const string LogFileNameKey = "LogFileName" ;
54+
5055 #endregion
5156
5257 #region Fields
5358
5459 /// <summary>
55- /// Cache the TRX filename
60+ /// Cache the TRX file path
5661 /// </summary>
57- private static string trxFileName ;
62+ private string trxFilePath ;
5863
5964 private TrxLoggerObjectModel . TestRun testRun ;
6065 private List < TrxLoggerObjectModel . UnitTestResult > results ;
@@ -75,47 +80,59 @@ internal class TrxLogger : ITestLogger
7580
7681 private DateTime testRunStartTime ;
7782
78- #endregion
83+ /// <summary>
84+ /// Parameters dictionary for logger. Ex: {"LogFileName":"TestResults.trx"}.
85+ /// </summary>
86+ private Dictionary < string , string > parametersDictionary ;
7987
8088 /// <summary>
81- /// Gets the directory under which trx file should be saved.
89+ /// Gets the directory under which default trx file and test results attachements should be saved.
8290 /// </summary>
83- [ SuppressMessage ( "StyleCop.CSharp.DocumentationRules" , "SA1650:ElementDocumentationMustBeSpelledCorrectly" , Justification = "Reviewed. Suppression is OK here." ) ]
84- public static string TrxFileDirectory
85- {
86- get ;
87- internal set ;
88- }
91+ private string testResultsDirPath ;
92+
93+ #endregion
8994
9095 #region ITestLogger
9196
92- /// <summary>
93- /// Initializes the Test Logger.
94- /// </summary>
95- /// <param name="events">Events that can be registered for.</param>
96- /// <param name="testRunDirectory">Test Run Directory</param>
97- public void Initialize ( TestLoggerEvents events , string testRunDirectory )
97+ /// <inheritdoc/>
98+ public void Initialize ( TestLoggerEvents events , string testResultsDirPath )
9899 {
99100 if ( events == null )
100101 {
101102 throw new ArgumentNullException ( nameof ( events ) ) ;
102103 }
103104
104- if ( string . IsNullOrEmpty ( testRunDirectory ) )
105+ if ( string . IsNullOrEmpty ( testResultsDirPath ) )
105106 {
106- throw new ArgumentNullException ( nameof ( testRunDirectory ) ) ;
107+ throw new ArgumentNullException ( nameof ( testResultsDirPath ) ) ;
107108 }
108109
109110 // Register for the events.
110111 events . TestRunMessage += this . TestMessageHandler ;
111112 events . TestResult += this . TestResultHandler ;
112113 events . TestRunComplete += this . TestRunCompleteHandler ;
113114
114- TrxFileDirectory = testRunDirectory ;
115+ this . testResultsDirPath = testResultsDirPath ;
115116
116117 this . InitializeInternal ( ) ;
117118 }
118119
120+ /// <inheritdoc/>
121+ public void Initialize ( TestLoggerEvents events , Dictionary < string , string > parameters )
122+ {
123+ if ( parameters == null )
124+ {
125+ throw new ArgumentNullException ( nameof ( parameters ) ) ;
126+ }
127+
128+ if ( parameters . Count == 0 )
129+ {
130+ throw new ArgumentException ( "No default parameters added" , nameof ( parameters ) ) ;
131+ }
132+
133+ this . parametersDictionary = parameters ;
134+ this . Initialize ( events , this . parametersDictionary [ DefaultLoggerParameterNames . TestRunDirectory ] ) ;
135+ }
119136 #endregion
120137
121138 #region ForTesting
@@ -257,7 +274,7 @@ internal void TestResultHandler(object sender, ObjectModel.Logging.TestResultEve
257274
258275 // Conver the rocksteady result to MSTest result
259276 TrxLoggerObjectModel . TestOutcome testOutcome = Converter . ToOutcome ( e . Result . Outcome ) ;
260- TrxLoggerObjectModel . UnitTestResult testResult = Converter . ToUnitTestResult ( e . Result , testElement , testOutcome , this . testRun , TrxFileDirectory ) ;
277+ TrxLoggerObjectModel . UnitTestResult testResult = Converter . ToUnitTestResult ( e . Result , testElement , testOutcome , this . testRun , this . testResultsDirPath ) ;
261278
262279 // Set various counts (passtests, failed tests, total tests)
263280 this . totalTests ++ ;
@@ -331,8 +348,8 @@ internal void TestRunCompleteHandler(object sender, TestRunCompleteEventArgs e)
331348 }
332349
333350 List < string > errorMessages = new List < string > ( ) ;
334- List < CollectorDataEntry > collectorEntries = Converter . ToCollectionEntries ( e . AttachmentSets , this . testRun , TrxFileDirectory ) ;
335- IList < String > resultFiles = Converter . ToResultFiles ( e . AttachmentSets , this . testRun , TrxFileDirectory , errorMessages ) ;
351+ List < CollectorDataEntry > collectorEntries = Converter . ToCollectionEntries ( e . AttachmentSets , this . testRun , this . testResultsDirPath ) ;
352+ IList < String > resultFiles = Converter . ToResultFiles ( e . AttachmentSets , this . testRun , this . testResultsDirPath , errorMessages ) ;
336353
337354 if ( errorMessages . Count > 0 )
338355 {
@@ -358,19 +375,9 @@ internal void TestRunCompleteHandler(object sender, TestRunCompleteEventArgs e)
358375
359376 helper . SaveObject ( runSummary , rootElement , "ResultSummary" , parameters ) ;
360377
361- if ( Directory . Exists ( TrxFileDirectory ) == false )
362- {
363- Directory . CreateDirectory ( TrxFileDirectory ) ;
364- }
365-
366- if ( string . IsNullOrEmpty ( trxFileName ) )
367- {
368- // save the xml to file in testResultsFolder
369- // [RunDeploymentRootDirectory] Replace white space with underscore from trx file name to make it command line friendly
370- trxFileName = this . GetTrxFileName ( TrxFileDirectory , this . testRun . RunConfiguration . RunDeploymentRootDirectory . Replace ( ' ' , '_' ) ) ;
371- }
372-
373- this . PopulateTrxFile ( trxFileName , rootElement ) ;
378+ //Save results to Trx file
379+ this . DeriveTrxFilePath ( ) ;
380+ this . PopulateTrxFile ( this . trxFilePath , rootElement ) ;
374381 }
375382 }
376383
@@ -387,35 +394,34 @@ internal virtual void PopulateTrxFile(string trxFileName, XmlElement rootElement
387394 {
388395 try
389396 {
390- FileStream fs = File . OpenWrite ( trxFileName ) ;
391- rootElement . OwnerDocument . Save ( fs ) ;
397+ var trxFileDirPath = Path . GetDirectoryName ( trxFilePath ) ;
398+ if ( Directory . Exists ( trxFileDirPath ) == false )
399+ {
400+ Directory . CreateDirectory ( trxFileDirPath ) ;
401+ }
402+
403+ if ( File . Exists ( trxFilePath ) )
404+ {
405+ var overwriteWarningMsg = string . Format ( CultureInfo . CurrentCulture ,
406+ TrxLoggerResources . TrxLoggerResultsFileOverwriteWarning , trxFileName ) ;
407+ Console . WriteLine ( overwriteWarningMsg ) ;
408+ EqtTrace . Warning ( overwriteWarningMsg ) ;
409+ }
410+
411+ using ( var fs = File . Open ( trxFileName , FileMode . Create ) )
412+ {
413+ rootElement . OwnerDocument . Save ( fs ) ;
414+ }
392415 String resultsFileMessage = String . Format ( CultureInfo . CurrentCulture , TrxLoggerResources . TrxLoggerResultsFile , trxFileName ) ;
393416 Console . WriteLine ( resultsFileMessage ) ;
417+ EqtTrace . Info ( resultsFileMessage ) ;
394418 }
395419 catch ( System . UnauthorizedAccessException fileWriteException )
396420 {
397421 Console . WriteLine ( fileWriteException . Message ) ;
398422 }
399423 }
400424
401- /// <summary>
402- /// Get full path to trx file
403- /// </summary>
404- /// <param name="baseDirectory">
405- /// The base Directory.
406- /// </param>
407- /// <param name="trxFileName">
408- /// The trx File Name.
409- /// </param>
410- /// <returns>
411- /// trx file name.
412- /// </returns>
413- [ SuppressMessage ( "StyleCop.CSharp.DocumentationRules" , "SA1650:ElementDocumentationMustBeSpelledCorrectly" , Justification = "Reviewed. Suppression is OK here." ) ]
414- private string GetTrxFileName ( string baseDirectory , string trxFileName )
415- {
416- return FileHelper . GetNextIterationFileName ( baseDirectory , trxFileName + ".trx" , false ) ;
417- }
418-
419425 // Initializes trx logger cache.
420426 private void InitializeInternal ( )
421427 {
@@ -454,6 +460,36 @@ private void HandleSkippedTest(ObjectModel.TestResult rsTestResult)
454460 this . AddRunLevelInformationalMessage ( message ) ;
455461 }
456462
463+ private void DeriveTrxFilePath ( )
464+ {
465+ if ( this . parametersDictionary != null )
466+ {
467+ var isLogFileNameParameterExists = this . parametersDictionary . TryGetValue ( TrxLogger . LogFileNameKey , out string logFileNameValue ) ;
468+ if ( isLogFileNameParameterExists && ! string . IsNullOrWhiteSpace ( logFileNameValue ) )
469+ {
470+ this . trxFilePath = Path . Combine ( this . testResultsDirPath , logFileNameValue ) ;
471+ }
472+ else
473+ {
474+ this . SetDefaultTrxFilePath ( ) ;
475+ }
476+ }
477+ else
478+ {
479+ this . SetDefaultTrxFilePath ( ) ;
480+ }
481+ }
482+
483+ /// <summary>
484+ /// Sets auto generated Trx file name under test results directory.
485+ /// </summary>
486+ private void SetDefaultTrxFilePath ( )
487+ {
488+ // [RunDeploymentRootDirectory] Replace white space with underscore from trx file name to make it command line friendly
489+ var defaultTrxFileName = this . testRun . RunConfiguration . RunDeploymentRootDirectory . Replace ( ' ' , '_' ) + ".trx" ;
490+ this . trxFilePath = FileHelper . GetNextIterationFileName ( this . testResultsDirPath , defaultTrxFileName , false ) ;
491+ }
492+
457493 #endregion
458494 }
459495}
0 commit comments