From 468d4b23277860bb4b878e22b872b45a7212db6f Mon Sep 17 00:00:00 2001 From: Harsh Jain Date: Thu, 7 Sep 2017 19:15:50 +0530 Subject: [PATCH] Added Event Log Data Collector for Windows OS (#1002) * Initial commit for Event Log Data Collector. * Added Tests. * Saving state. * Added Tests. * Added E2E tests. * Added localized resources. * Added entry for Microsoft.TestPlatform.Extensions.EventLogCollector.dll for signing * Removed System.Diagnostics.Debugger.Launch(); * Minor changes. * PR feedback. * PR feedback * PR feedback. * PR feedback. * Merged EventLogContainerContextData and DataCollectorContainer. * PR feedback. * Changed code to have only one EventLog per eventLogName configured. * Added Filter for EventLogEnteries. * Added verification for test case level "Event Log.xml" file. * Fixed failing tests. * PR feedback. * Added more tests, minor changes. * Added logging for EventLogs handling. --- TestPlatform.sln | 56 +- scripts/build.ps1 | 10 +- .../CollectorNameValueConfigurationManager.cs | 130 ++++ .../EventLogCollectorException.cs | 23 + .../EventLogConstants.cs | 23 + .../EventLogContainer.cs | 271 +++++++ .../EventLogDataCollector.cs | 683 ++++++++++++++++++ .../EventLogSessionContext.cs | 65 ++ .../EventLogXmlWriter.cs | 89 +++ .../Friends.cs | 12 + .../IEventLogContainer.cs | 36 + ...atform.Extensions.EventLogCollector.csproj | 51 ++ .../Resources/Resources.Designer.cs | 126 ++++ .../Resources/Resources.resx | 141 ++++ .../Resources/xlf/Resources.cs.xlf | 60 ++ .../Resources/xlf/Resources.de.xlf | 60 ++ .../Resources/xlf/Resources.es.xlf | 60 ++ .../Resources/xlf/Resources.fr.xlf | 60 ++ .../Resources/xlf/Resources.it.xlf | 60 ++ .../Resources/xlf/Resources.ja.xlf | 60 ++ .../Resources/xlf/Resources.ko.xlf | 60 ++ .../Resources/xlf/Resources.pl.xlf | 60 ++ .../Resources/xlf/Resources.pt-BR.xlf | 60 ++ .../Resources/xlf/Resources.ru.xlf | 60 ++ .../Resources/xlf/Resources.tr.xlf | 60 ++ .../Resources/xlf/Resources.xlf | 42 ++ .../Resources/xlf/Resources.zh-Hans.xlf | 60 ++ .../Resources/xlf/Resources.zh-Hant.xlf | 60 ++ .../TestPlatformDataCollectionSink.cs | 2 - .../Helpers/FileHelper.cs | 37 +- .../Helpers/Interfaces/IFileHelper.cs | 30 + .../DataCollectionLauncherFactory.cs | 2 +- .../Events/DataRequestEventArgs.cs | 10 +- .../DataCollector/Events/TestCaseEvents.cs | 2 +- .../FileTransferInformation.cs | 34 +- .../Friends.cs | 1 + src/package/sign/sign.proj | 3 +- ...ectorNameValueConfigurationManagerTests.cs | 55 ++ .../EventLogContainerTests.cs | 134 ++++ .../EventLogDataCollectorTests.cs | 481 ++++++++++++ .../EventLogSessionContextTests.cs | 68 ++ .../EventLogXmlWriterTests.cs | 62 ++ ...ensions.EventLogCollector.UnitTests.csproj | 31 + .../EventLogCollectorTests.cs | 89 +++ .../EventLogUnitTestProject.csproj | 22 + .../EventLogUnitTestProject/UnitTest1.cs | 40 + 46 files changed, 3622 insertions(+), 19 deletions(-) create mode 100644 src/DataCollectors/Microsoft.TestPlatform.Extensions.EventLogCollector/CollectorNameValueConfigurationManager.cs create mode 100644 src/DataCollectors/Microsoft.TestPlatform.Extensions.EventLogCollector/EventLogCollectorException.cs create mode 100644 src/DataCollectors/Microsoft.TestPlatform.Extensions.EventLogCollector/EventLogConstants.cs create mode 100644 src/DataCollectors/Microsoft.TestPlatform.Extensions.EventLogCollector/EventLogContainer.cs create mode 100644 src/DataCollectors/Microsoft.TestPlatform.Extensions.EventLogCollector/EventLogDataCollector.cs create mode 100644 src/DataCollectors/Microsoft.TestPlatform.Extensions.EventLogCollector/EventLogSessionContext.cs create mode 100644 src/DataCollectors/Microsoft.TestPlatform.Extensions.EventLogCollector/EventLogXmlWriter.cs create mode 100644 src/DataCollectors/Microsoft.TestPlatform.Extensions.EventLogCollector/Friends.cs create mode 100644 src/DataCollectors/Microsoft.TestPlatform.Extensions.EventLogCollector/IEventLogContainer.cs create mode 100644 src/DataCollectors/Microsoft.TestPlatform.Extensions.EventLogCollector/Microsoft.TestPlatform.Extensions.EventLogCollector.csproj create mode 100644 src/DataCollectors/Microsoft.TestPlatform.Extensions.EventLogCollector/Resources/Resources.Designer.cs create mode 100644 src/DataCollectors/Microsoft.TestPlatform.Extensions.EventLogCollector/Resources/Resources.resx create mode 100644 src/DataCollectors/Microsoft.TestPlatform.Extensions.EventLogCollector/Resources/xlf/Resources.cs.xlf create mode 100644 src/DataCollectors/Microsoft.TestPlatform.Extensions.EventLogCollector/Resources/xlf/Resources.de.xlf create mode 100644 src/DataCollectors/Microsoft.TestPlatform.Extensions.EventLogCollector/Resources/xlf/Resources.es.xlf create mode 100644 src/DataCollectors/Microsoft.TestPlatform.Extensions.EventLogCollector/Resources/xlf/Resources.fr.xlf create mode 100644 src/DataCollectors/Microsoft.TestPlatform.Extensions.EventLogCollector/Resources/xlf/Resources.it.xlf create mode 100644 src/DataCollectors/Microsoft.TestPlatform.Extensions.EventLogCollector/Resources/xlf/Resources.ja.xlf create mode 100644 src/DataCollectors/Microsoft.TestPlatform.Extensions.EventLogCollector/Resources/xlf/Resources.ko.xlf create mode 100644 src/DataCollectors/Microsoft.TestPlatform.Extensions.EventLogCollector/Resources/xlf/Resources.pl.xlf create mode 100644 src/DataCollectors/Microsoft.TestPlatform.Extensions.EventLogCollector/Resources/xlf/Resources.pt-BR.xlf create mode 100644 src/DataCollectors/Microsoft.TestPlatform.Extensions.EventLogCollector/Resources/xlf/Resources.ru.xlf create mode 100644 src/DataCollectors/Microsoft.TestPlatform.Extensions.EventLogCollector/Resources/xlf/Resources.tr.xlf create mode 100644 src/DataCollectors/Microsoft.TestPlatform.Extensions.EventLogCollector/Resources/xlf/Resources.xlf create mode 100644 src/DataCollectors/Microsoft.TestPlatform.Extensions.EventLogCollector/Resources/xlf/Resources.zh-Hans.xlf create mode 100644 src/DataCollectors/Microsoft.TestPlatform.Extensions.EventLogCollector/Resources/xlf/Resources.zh-Hant.xlf create mode 100644 test/DataCollectors/Microsoft.TestPlatform.Extensions.EventLogCollector.UnitTests/CollectorNameValueConfigurationManagerTests.cs create mode 100644 test/DataCollectors/Microsoft.TestPlatform.Extensions.EventLogCollector.UnitTests/EventLogContainerTests.cs create mode 100644 test/DataCollectors/Microsoft.TestPlatform.Extensions.EventLogCollector.UnitTests/EventLogDataCollectorTests.cs create mode 100644 test/DataCollectors/Microsoft.TestPlatform.Extensions.EventLogCollector.UnitTests/EventLogSessionContextTests.cs create mode 100644 test/DataCollectors/Microsoft.TestPlatform.Extensions.EventLogCollector.UnitTests/EventLogXmlWriterTests.cs create mode 100644 test/DataCollectors/Microsoft.TestPlatform.Extensions.EventLogCollector.UnitTests/Microsoft.TestPlatform.Extensions.EventLogCollector.UnitTests.csproj create mode 100644 test/Microsoft.TestPlatform.AcceptanceTests/EventLogCollectorTests.cs create mode 100644 test/TestAssets/EventLogUnitTestProject/EventLogUnitTestProject.csproj create mode 100644 test/TestAssets/EventLogUnitTestProject/UnitTest1.cs diff --git a/TestPlatform.sln b/TestPlatform.sln index 6026dafc1e..15002fe6db 100644 --- a/TestPlatform.sln +++ b/TestPlatform.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 15 -VisualStudioVersion = 15.0.26507.0 +VisualStudioVersion = 15.0.26703.0 MinimumVisualStudioVersion = 10.0.40219.1 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{ED0C35EB-7F31-4841-A24F-8EB708FFA959}" EndProject @@ -159,6 +159,16 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BlameUnitTestProject", "tes EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SampleProjectWithOldTestHost", "test\TestAssets\SampleProjectWithOldTestHost\SampleProjectWithOldTestHost.csproj", "{DFF82C76-9498-4E8B-8B5E-D12E2B92FA46}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "DataCollectors", "DataCollectors", "{B705537C-B82C-4A30-AFA5-6244D9A7DAEB}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.TestPlatform.Extensions.EventLogCollector", "src\DataCollectors\Microsoft.TestPlatform.Extensions.EventLogCollector\Microsoft.TestPlatform.Extensions.EventLogCollector.csproj", "{65A25D6E-C9CC-4F45-8925-04087AC82634}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "DataCollectors", "DataCollectors", "{D9A30E32-D466-4EC5-B4F2-62E17562279B}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.TestPlatform.Extensions.EventLogCollector.UnitTests", "test\DataCollectors\Microsoft.TestPlatform.Extensions.EventLogCollector.UnitTests\Microsoft.TestPlatform.Extensions.EventLogCollector.UnitTests.csproj", "{21DB138B-85B7-479E-91FE-01E0F972EC56}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EventLogUnitTestProject", "test\TestAssets\EventLogUnitTestProject\EventLogUnitTestProject.csproj", "{826CD5AF-44FA-40F6-B731-3980CADED8C0}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -841,6 +851,42 @@ Global {DFF82C76-9498-4E8B-8B5E-D12E2B92FA46}.Release|x64.Build.0 = Release|Any CPU {DFF82C76-9498-4E8B-8B5E-D12E2B92FA46}.Release|x86.ActiveCfg = Release|Any CPU {DFF82C76-9498-4E8B-8B5E-D12E2B92FA46}.Release|x86.Build.0 = Release|Any CPU + {65A25D6E-C9CC-4F45-8925-04087AC82634}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {65A25D6E-C9CC-4F45-8925-04087AC82634}.Debug|Any CPU.Build.0 = Debug|Any CPU + {65A25D6E-C9CC-4F45-8925-04087AC82634}.Debug|x64.ActiveCfg = Debug|Any CPU + {65A25D6E-C9CC-4F45-8925-04087AC82634}.Debug|x64.Build.0 = Debug|Any CPU + {65A25D6E-C9CC-4F45-8925-04087AC82634}.Debug|x86.ActiveCfg = Debug|Any CPU + {65A25D6E-C9CC-4F45-8925-04087AC82634}.Debug|x86.Build.0 = Debug|Any CPU + {65A25D6E-C9CC-4F45-8925-04087AC82634}.Release|Any CPU.ActiveCfg = Release|Any CPU + {65A25D6E-C9CC-4F45-8925-04087AC82634}.Release|Any CPU.Build.0 = Release|Any CPU + {65A25D6E-C9CC-4F45-8925-04087AC82634}.Release|x64.ActiveCfg = Release|Any CPU + {65A25D6E-C9CC-4F45-8925-04087AC82634}.Release|x64.Build.0 = Release|Any CPU + {65A25D6E-C9CC-4F45-8925-04087AC82634}.Release|x86.ActiveCfg = Release|Any CPU + {65A25D6E-C9CC-4F45-8925-04087AC82634}.Release|x86.Build.0 = Release|Any CPU + {21DB138B-85B7-479E-91FE-01E0F972EC56}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {21DB138B-85B7-479E-91FE-01E0F972EC56}.Debug|Any CPU.Build.0 = Debug|Any CPU + {21DB138B-85B7-479E-91FE-01E0F972EC56}.Debug|x64.ActiveCfg = Debug|Any CPU + {21DB138B-85B7-479E-91FE-01E0F972EC56}.Debug|x64.Build.0 = Debug|Any CPU + {21DB138B-85B7-479E-91FE-01E0F972EC56}.Debug|x86.ActiveCfg = Debug|Any CPU + {21DB138B-85B7-479E-91FE-01E0F972EC56}.Debug|x86.Build.0 = Debug|Any CPU + {21DB138B-85B7-479E-91FE-01E0F972EC56}.Release|Any CPU.ActiveCfg = Release|Any CPU + {21DB138B-85B7-479E-91FE-01E0F972EC56}.Release|Any CPU.Build.0 = Release|Any CPU + {21DB138B-85B7-479E-91FE-01E0F972EC56}.Release|x64.ActiveCfg = Release|Any CPU + {21DB138B-85B7-479E-91FE-01E0F972EC56}.Release|x64.Build.0 = Release|Any CPU + {21DB138B-85B7-479E-91FE-01E0F972EC56}.Release|x86.ActiveCfg = Release|Any CPU + {21DB138B-85B7-479E-91FE-01E0F972EC56}.Release|x86.Build.0 = Release|Any CPU + {826CD5AF-44FA-40F6-B731-3980CADED8C0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {826CD5AF-44FA-40F6-B731-3980CADED8C0}.Debug|Any CPU.Build.0 = Debug|Any CPU + {826CD5AF-44FA-40F6-B731-3980CADED8C0}.Debug|x64.ActiveCfg = Debug|Any CPU + {826CD5AF-44FA-40F6-B731-3980CADED8C0}.Debug|x64.Build.0 = Debug|Any CPU + {826CD5AF-44FA-40F6-B731-3980CADED8C0}.Debug|x86.ActiveCfg = Debug|Any CPU + {826CD5AF-44FA-40F6-B731-3980CADED8C0}.Debug|x86.Build.0 = Debug|Any CPU + {826CD5AF-44FA-40F6-B731-3980CADED8C0}.Release|Any CPU.ActiveCfg = Release|Any CPU + {826CD5AF-44FA-40F6-B731-3980CADED8C0}.Release|Any CPU.Build.0 = Release|Any CPU + {826CD5AF-44FA-40F6-B731-3980CADED8C0}.Release|x64.ActiveCfg = Release|Any CPU + {826CD5AF-44FA-40F6-B731-3980CADED8C0}.Release|x64.Build.0 = Release|Any CPU + {826CD5AF-44FA-40F6-B731-3980CADED8C0}.Release|x86.ActiveCfg = Release|Any CPU + {826CD5AF-44FA-40F6-B731-3980CADED8C0}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -911,5 +957,13 @@ Global {488675EC-C8BB-40E0-AD4F-91F623D548B3} = {B27FAFDF-2DBA-4AB0-BA85-FD5F21D359D6} {6B2B841C-CCFF-469A-9939-EB07EA0401AE} = {8DA7CBD9-F17E-41B6-90C4-CFF55848A25A} {DFF82C76-9498-4E8B-8B5E-D12E2B92FA46} = {8DA7CBD9-F17E-41B6-90C4-CFF55848A25A} + {B705537C-B82C-4A30-AFA5-6244D9A7DAEB} = {ED0C35EB-7F31-4841-A24F-8EB708FFA959} + {65A25D6E-C9CC-4F45-8925-04087AC82634} = {B705537C-B82C-4A30-AFA5-6244D9A7DAEB} + {D9A30E32-D466-4EC5-B4F2-62E17562279B} = {B27FAFDF-2DBA-4AB0-BA85-FD5F21D359D6} + {21DB138B-85B7-479E-91FE-01E0F972EC56} = {D9A30E32-D466-4EC5-B4F2-62E17562279B} + {826CD5AF-44FA-40F6-B731-3980CADED8C0} = {8DA7CBD9-F17E-41B6-90C4-CFF55848A25A} + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {0541B30C-FF51-4E28-B172-83F5F3934BCD} EndGlobalSection EndGlobal diff --git a/scripts/build.ps1 b/scripts/build.ps1 index 79b7526708..a3a96d5d2f 100644 --- a/scripts/build.ps1 +++ b/scripts/build.ps1 @@ -296,7 +296,15 @@ function Publish-Package Copy-Item $blameDataCollectorNetFull\Microsoft.TestPlatform.Extensions.BlameDataCollector.pdb $fullCLRExtensionsDir -Force Copy-Item $blameDataCollectorNetStandard\Microsoft.TestPlatform.Extensions.BlameDataCollector.dll $coreCLRExtensionsDir -Force Copy-Item $blameDataCollectorNetStandard\Microsoft.TestPlatform.Extensions.BlameDataCollector.pdb $coreCLRExtensionsDir -Force - + +# Copy Event Log Datacollector to Extensions folder. + $eventLogDataCollector = Join-Path $env:TP_ROOT_DIR "src\DataCollectors\Microsoft.TestPlatform.Extensions.EventLogCollector\bin\$TPB_Configuration" + $eventLogDataCollectorNetFull = Join-Path $eventLogDataCollector $TPB_TargetFramework + Copy-Item $eventLogDataCollectorNetFull\Microsoft.TestPlatform.Extensions.EventLogCollector.dll $fullCLRExtensionsDir -Force + Copy-Item $eventLogDataCollectorNetFull\Microsoft.TestPlatform.Extensions.EventLogCollector.pdb $fullCLRExtensionsDir -Force + Copy-Item $eventLogDataCollectorNetFull\Microsoft.TestPlatform.Extensions.EventLogCollector.dll $coreCLRExtensionsDir -Force + Copy-Item $eventLogDataCollectorNetFull\Microsoft.TestPlatform.Extensions.EventLogCollector.pdb $coreCLRExtensionsDir -Force + # Note Note: If there are some dependencies for the TestHostRuntimeProvider assemblies, those need to be moved too. $runtimeproviders = @("Microsoft.TestPlatform.TestHostRuntimeProvider.dll", "Microsoft.TestPlatform.TestHostRuntimeProvider.pdb") foreach($file in $runtimeproviders) { diff --git a/src/DataCollectors/Microsoft.TestPlatform.Extensions.EventLogCollector/CollectorNameValueConfigurationManager.cs b/src/DataCollectors/Microsoft.TestPlatform.Extensions.EventLogCollector/CollectorNameValueConfigurationManager.cs new file mode 100644 index 0000000000..5358acd119 --- /dev/null +++ b/src/DataCollectors/Microsoft.TestPlatform.Extensions.EventLogCollector/CollectorNameValueConfigurationManager.cs @@ -0,0 +1,130 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace Microsoft.TestPlatform.Extensions.EventLogCollector +{ + using System.Collections.Generic; + using System.Xml; + using Microsoft.VisualStudio.TestPlatform.ObjectModel; + + /// + /// Utility class that collectors can use to read name/value configuration information from + /// the XML element sent to them + /// + internal class CollectorNameValueConfigurationManager + { + #region Private constants + // Configuration XML constants + private const string SettingNameAttributeName = "name"; + + private const string SettingValueAttributeName = "value"; + + #endregion + + #region Private fields + + /// + /// The name/value pairs loaded from the configuration XML element + /// + private IDictionary nameValuePairs = new Dictionary(); + + #endregion + + #region Constructor + + /// + /// Initializes a new instance of the class. + /// Loads the configuration name/value information from the provided XML element into a dictionary + /// + /// + /// XML element containing the configuration + /// + public CollectorNameValueConfigurationManager(XmlElement configurationElement) + { + if (configurationElement == null) + { + // There is no configuration + return; + } + + // Iterate through top-level XML elements within the configuration element and store + // name/value information for elements that have name/value attributes. + foreach (XmlNode settingNode in configurationElement.ChildNodes) + { + // Skip all non-elements + var settingElement = settingNode as XmlElement; + if (settingElement == null) + { + continue; + } + + // Get the setting name + string settingName = settingElement.GetAttribute(SettingNameAttributeName); + if (string.IsNullOrWhiteSpace(settingName)) + { + if (EqtTrace.IsWarningEnabled) + { + EqtTrace.Warning("Skipping configuration setting due to missing setting name"); + } + + continue; + } + + // Get the setting value + string settingValue = settingElement.GetAttribute(SettingValueAttributeName); + if (string.IsNullOrWhiteSpace(settingValue)) + { + if (EqtTrace.IsWarningEnabled) + { + EqtTrace.Warning("Skipping configuration setting '{0}' due to missing value", settingName); + } + + continue; + } + + // Save the name/value pair in the dictionary. Note that duplicate settings are + // overwritten with the last occurrance's value. + if (this.nameValuePairs.ContainsKey(settingName)) + { + if (EqtTrace.IsVerboseEnabled) + { + EqtTrace.Verbose( + "Duplicate configuration setting found for '{0}'. Using the last setting.", + settingName); + } + } + + this.nameValuePairs[settingName] = settingValue; + } + } + + #endregion + + #region Public properties + + internal IDictionary NameValuePairs => this.nameValuePairs; + + /// + /// Gets the value of the setting specified by name, or null if it was not found + /// + /// The setting name + /// The setting value, or null if the setting was not found + public string this[string name] + { + get + { + if (name == null) + { + return null; + } + + string settingValue; + this.nameValuePairs.TryGetValue(name, out settingValue); + return settingValue; + } + + set => this.nameValuePairs[name] = value; + } + #endregion + } +} diff --git a/src/DataCollectors/Microsoft.TestPlatform.Extensions.EventLogCollector/EventLogCollectorException.cs b/src/DataCollectors/Microsoft.TestPlatform.Extensions.EventLogCollector/EventLogCollectorException.cs new file mode 100644 index 0000000000..169a7a3442 --- /dev/null +++ b/src/DataCollectors/Microsoft.TestPlatform.Extensions.EventLogCollector/EventLogCollectorException.cs @@ -0,0 +1,23 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace Microsoft.TestPlatform.Extensions.EventLogCollector +{ + using System; + + /// + /// Private Exception class used for event log exceptions + /// + internal class EventLogCollectorException : Exception + { + /// + /// Initializes a new instance of the class. + /// + /// the localized exception message + /// the inner exception + public EventLogCollectorException(string localizedMessage, Exception innerException) + : base(localizedMessage, innerException) + { + } + } +} diff --git a/src/DataCollectors/Microsoft.TestPlatform.Extensions.EventLogCollector/EventLogConstants.cs b/src/DataCollectors/Microsoft.TestPlatform.Extensions.EventLogCollector/EventLogConstants.cs new file mode 100644 index 0000000000..1f74f8c00c --- /dev/null +++ b/src/DataCollectors/Microsoft.TestPlatform.Extensions.EventLogCollector/EventLogConstants.cs @@ -0,0 +1,23 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace Microsoft.TestPlatform.Extensions.EventLogCollector +{ + /// + /// Constants used by Event Log Data Collector. + /// + internal static class EventLogConstants + { + // Supported configuration setting names + public const string SettingEventLogs = "EventLogs"; + public const string SettingEventSources = "EventSources"; + public const string SettingEntryTypes = "EntryTypes"; + public const string SettingMaxEntries = "MaxEventLogEntriesToCollect"; + + // default values + public const int DefaultMaxEntries = 50000; + + public const int TypeColumnMaxLength = 64; + public const int SourceColumnMaxLength = 212; + } +} \ No newline at end of file diff --git a/src/DataCollectors/Microsoft.TestPlatform.Extensions.EventLogCollector/EventLogContainer.cs b/src/DataCollectors/Microsoft.TestPlatform.Extensions.EventLogCollector/EventLogContainer.cs new file mode 100644 index 0000000000..a95bcbd8b7 --- /dev/null +++ b/src/DataCollectors/Microsoft.TestPlatform.Extensions.EventLogCollector/EventLogContainer.cs @@ -0,0 +1,271 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace Microsoft.TestPlatform.Extensions.EventLogCollector +{ + using System; + using System.Collections.Generic; + using System.Diagnostics; + using System.Globalization; + + using Microsoft.VisualStudio.TestPlatform.ObjectModel; + using Microsoft.VisualStudio.TestPlatform.ObjectModel.DataCollection; + using Microsoft.VisualStudio.TestPlatform.Utilities.Helpers.Interfaces; + + using Resource = Microsoft.TestPlatform.Extensions.EventLogCollector.Resources.Resources; + + /// + /// The event log container. + /// + internal class EventLogContainer : IEventLogContainer + { + private ISet eventSources; + + private ISet entryTypes; + + private EventLog eventLog; + + private int nextEntryIndexToCollect; + + private int maxLogEntries; + + private DataCollectionLogger dataCollectionLogger; + + private DataCollectionContext dataCollectionContext; + + private bool limitReached; + + private List eventLogEntries; + + /// + /// Keeps track of if we are disposed. + /// + private bool isDisposed; + + /// + /// Initializes a new instance of the class. + /// + /// + /// Event Log Name for which logs has to be collected. + /// + /// + /// The event Sources. + /// + /// + /// The entry Types. + /// + /// + /// Max entries to store + /// + /// + /// Data Collection Logger + /// + /// + /// Data Collection Context + /// + public EventLogContainer(string eventLogName, ISet eventSources, ISet entryTypes, int maxLogEntries, DataCollectionLogger dataCollectionLogger, DataCollectionContext dataCollectionContext) + { + this.CreateEventLog(eventLogName); + this.eventSources = eventSources; + this.entryTypes = entryTypes; + this.maxLogEntries = maxLogEntries; + this.dataCollectionLogger = dataCollectionLogger; + this.dataCollectionContext = dataCollectionContext; + + this.eventLogEntries = new List(); + } + + /// + public List EventLogEntries => this.eventLogEntries; + + /// + public EventLog EventLog => this.eventLog; + + internal int NextEntryIndexToCollect + { + get => this.nextEntryIndexToCollect; + + set => this.nextEntryIndexToCollect = value; + } + + /// + /// Gets or sets a value indicating whether limit reached. + /// + internal bool LimitReached + { + get => this.limitReached; + + set => this.limitReached = value; + } + + public void Dispose() + { + this.Dispose(true); + + // Use SupressFinalize in case a subclass + // of this type implements a finalizer. + GC.SuppressFinalize(this); + } + + /// + /// This is the event handler for the EntryWritten event of the System.Diagnostics.EventLog class. + /// Note that the documentation for the EntryWritten event includes these remarks: + /// "The system responds to WriteEntry only if the last write event occurred at least five seconds previously. + /// This implies you will only receive one EntryWritten event notification within a five-second interval, even if more + /// than one event log change occurs. If you insert a sufficiently long sleep interval (around 10 seconds) between calls + /// to WriteEntry, no events will be lost. However, if write events occur more frequently, the most recent write events + /// could be lost." + /// This complicates this data collector because we don't want to sleep to wait for all events or lose the most recent events. + /// To workaround, the implementation does several things: + /// 1. We get the EventLog entries to collect from the EventLog.Entries collection and ignore the EntryWrittenEventArgs. + /// 2. When event log collection ends for a data collection context, this method is called explicitly by the EventLogDataCollector + /// passing null for EntryWrittenEventArgs (which is fine since the argument is ignored. + /// 3. We keep track of which EventLogEntry object in the EventLog.Entries we still need to collect. We do this by inspecting + /// the value of the EventLogEntry.Index property. The value of this property is an integer that is incremented for each entry + /// that is written to the event log, but is reset to 0 if the entire event log is cleared. + /// Another behavior of event logs that we need to account for is that if the event log reaches a size limit, older events are + /// automatically deleted. In this case the collection EventLog.Entries contains only the entries remaining in the log, + /// and the value of the EventLog.Entries[0].Index will not be 0; it will be the index of the oldest entry still in the log. + /// For example, if the first 1000 entries written to an event log (since it was last completely cleared) are deleted because + /// of the size limitation, then EventLog.Entries[0].Index would have a value of 1000 (this value is saved in the local variable + /// "firstIndexInLog" in the method implementation. Similarly "mostRecentIndexInLog" is the index of the last entry written + /// to the log at the time we examine it. + /// + /// Source + /// The System.Diagnostics.EntryWrittenEventArgs object describing the entry that was written. + public void OnEventLogEntryWritten(object source, EntryWrittenEventArgs e) + { + while (!this.limitReached) + { + try + { + lock (this.eventLogEntries) + { + int currentCount = this.eventLog.Entries.Count; + if (currentCount == 0) + { + break; + } + + int firstIndexInLog = this.eventLog.Entries[0].Index; + int mostRecentIndexInLog = this.eventLog.Entries[currentCount - 1].Index; + + if (mostRecentIndexInLog == this.nextEntryIndexToCollect - 1) + { + // We've already collected the most recent entry in the log + break; + } + + if (mostRecentIndexInLog < this.nextEntryIndexToCollect - 1) + { + if (EqtTrace.IsWarningEnabled) + { + EqtTrace.Warning( + string.Format( + CultureInfo.InvariantCulture, + "EventLogDataContainer: OnEventLogEntryWritten: Handling clearing of log (mostRecentIndexInLog < eventLogContainer.NextEntryIndex): firstIndexInLog: {0}:, mostRecentIndexInLog: {1}, NextEntryIndex: {2}", + firstIndexInLog, + mostRecentIndexInLog, + this.nextEntryIndexToCollect)); + } + + // Send warning; event log must have been cleared. + this.dataCollectionLogger.LogWarning( + this.dataCollectionContext, + string.Format( + CultureInfo.InvariantCulture, + Resource.EventsLostWarning, + this.eventLog.Log)); + + this.nextEntryIndexToCollect = 0; + firstIndexInLog = 0; + } + + for (; + this.nextEntryIndexToCollect <= mostRecentIndexInLog; + this.nextEntryIndexToCollect = this.nextEntryIndexToCollect + 1) + { + int nextEntryIndexInCurrentLog = this.nextEntryIndexToCollect - firstIndexInLog; + EventLogEntry nextEntry = this.eventLog.Entries[nextEntryIndexInCurrentLog]; + + // If an explicit list of event sources was provided, only report log entries from those sources + if (this.eventSources != null && this.eventSources.Count > 0) + { + if (!this.eventSources.Contains(nextEntry.Source)) + { + continue; + } + } + + if (!this.entryTypes.Contains(nextEntry.EntryType)) + { + continue; + } + + if (this.eventLogEntries.Count < this.maxLogEntries) + { + this.eventLogEntries.Add(nextEntry); + + if (EqtTrace.IsVerboseEnabled) + { + EqtTrace.Verbose( + string.Format( + CultureInfo.InvariantCulture, + "EventLogDataContainer.OnEventLogEntryWritten() add event with Id {0} from position {1} in the current {2} log", + nextEntry.Index, + nextEntryIndexInCurrentLog, + this.eventLog.Log)); + } + } + else + { + this.LimitReached = true; + break; + } + } + } + } + catch (Exception exception) + { + this.dataCollectionLogger.LogError( + this.dataCollectionContext, + string.Format( + CultureInfo.InvariantCulture, + Resource.EventsLostError, + this.eventLog.Log), exception); + } + } + } + + /// + /// The dispose. + /// + /// + /// The disposing. + /// + protected virtual void Dispose(bool disposing) + { + if (!this.isDisposed) + { + if (disposing) + { + this.eventLog.EnableRaisingEvents = false; + this.eventLog.EntryWritten -= this.OnEventLogEntryWritten; + this.eventLog.Dispose(); + } + + this.isDisposed = true; + } + } + + private void CreateEventLog(string eventLogName) + { + this.eventLog = new EventLog(eventLogName); + this.eventLog.EnableRaisingEvents = true; + this.eventLog.EntryWritten += this.OnEventLogEntryWritten; + int currentCount = this.eventLog.Entries.Count; + this.nextEntryIndexToCollect = + (currentCount == 0) ? 0 : this.eventLog.Entries[currentCount - 1].Index + 1; + } + } +} diff --git a/src/DataCollectors/Microsoft.TestPlatform.Extensions.EventLogCollector/EventLogDataCollector.cs b/src/DataCollectors/Microsoft.TestPlatform.Extensions.EventLogCollector/EventLogDataCollector.cs new file mode 100644 index 0000000000..7b0a08e881 --- /dev/null +++ b/src/DataCollectors/Microsoft.TestPlatform.Extensions.EventLogCollector/EventLogDataCollector.cs @@ -0,0 +1,683 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace Microsoft.TestPlatform.Extensions.EventLogCollector +{ + using System; + using System.Collections.Generic; + using System.Diagnostics; + using System.Globalization; + using System.IO; + using System.Linq; + using System.Xml; + + using Microsoft.VisualStudio.TestPlatform.ObjectModel; + using Microsoft.VisualStudio.TestPlatform.ObjectModel.DataCollection; + using Microsoft.VisualStudio.TestPlatform.Utilities.Helpers; + using Microsoft.VisualStudio.TestPlatform.Utilities.Helpers.Interfaces; + + using Resource = Resources.Resources; + + /// + /// A data collector that collects event log data + /// + [DataCollectorTypeUri(DefaultUri)] + [DataCollectorFriendlyName("Event Log")] + public class EventLogDataCollector : DataCollector + { + #region Constants + + /// + /// The event log file name. + /// + private const string EventLogFileName = "Event Log"; + + /// + /// DataCollector URI. + /// + private const string DefaultUri = @"datacollector://Microsoft/EventLog/2.0"; + + #endregion + + #region Private fields + + /// + /// Event handler delegate for the SessionStart event + /// + private readonly EventHandler sessionStartEventHandler; + + /// + /// Event handler delegate for the SessionEnd event + /// + private readonly EventHandler sessionEndEventHandler; + + /// + /// Event handler delegate for the TestCaseStart event + /// + private readonly EventHandler testCaseStartEventHandler; + + /// + /// Event handler delegate for the TestCaseEnd event + /// + private readonly EventHandler testCaseEndEventHandler; + + /// + /// The event log directories. + /// + private readonly List eventLogDirectories; + + /// + /// Object containing the execution events the data collector registers for + /// + private DataCollectionEvents events; + + /// + /// The sink used by the data collector to send its data + /// + private DataCollectionSink dataSink; + + /// + /// The data collector context. + /// + private DataCollectionContext dataCollectorContext; + + /// + /// Used by the data collector to send warnings, errors, or other messages + /// + private DataCollectionLogger logger; + + /// + /// The event log names. + /// + private ISet eventLogNames; + + /// + /// The event sources. + /// + private ISet eventSources; + + /// + /// The entry types. + /// + private ISet entryTypes; + + /// + /// The max entries. + /// + private int maxEntries; + + /// + /// The file helper. + /// + private IFileHelper fileHelper; + + /// + /// The event log map. + /// + private IDictionary eventLogContainerMap = new Dictionary(); + + #endregion + + #region Constructor + + /// + /// Initializes a new instance of the class. + /// + public EventLogDataCollector() + : this(new FileHelper()) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// + /// File Helper. + /// + internal EventLogDataCollector(IFileHelper fileHelper) + { + this.sessionStartEventHandler = this.OnSessionStart; + this.sessionEndEventHandler = this.OnSessionEnd; + this.testCaseStartEventHandler = this.OnTestCaseStart; + this.testCaseEndEventHandler = this.OnTestCaseEnd; + + this.eventLogDirectories = new List(); + this.ContextMap = new Dictionary(); + this.fileHelper = fileHelper; + } + + #endregion + + #region Internal Fields + + internal int MaxEntries + { + get + { + return this.maxEntries; + } + } + + internal ISet EventSources + { + get + { + return this.eventSources; + } + } + + internal ISet EntryTypes + { + get + { + return this.entryTypes; + } + } + + internal ISet EventLogNames + { + get + { + return this.eventLogNames; + } + } + + /// + /// Gets the context data. + /// + internal Dictionary ContextMap { get; private set; } + + #endregion + + #region DataCollector Members + + /// + /// Initializes the data collector + /// + /// + /// The XML element containing configuration information for the data collector. Currently, + /// this data collector does not have any configuration, so we ignore this parameter. + /// + /// + /// Object containing the execution events the data collector registers for + /// + /// The sink used by the data collector to send its data + /// + /// Used by the data collector to send warnings, errors, or other messages + /// + /// Provides contextual information about the agent environment + public override void Initialize( + XmlElement configurationElement, + DataCollectionEvents events, + DataCollectionSink dataSink, + DataCollectionLogger logger, + DataCollectionEnvironmentContext dataCollectionEnvironmentContext) + { + ValidateArg.NotNull(events, nameof(events)); + ValidateArg.NotNull(dataSink, nameof(dataSink)); + ValidateArg.NotNull(logger, nameof(logger)); + + this.events = events; + this.dataSink = dataSink; + this.logger = logger; + this.dataCollectorContext = dataCollectionEnvironmentContext.SessionDataCollectionContext; + + // Load the configuration + CollectorNameValueConfigurationManager nameValueSettings = + new CollectorNameValueConfigurationManager(configurationElement); + + // Apply the configuration + this.ConfigureEventSources(nameValueSettings); + this.ConfigureEntryTypes(nameValueSettings); + this.ConfigureMaxEntries(nameValueSettings); + this.ConfigureEventLogNames(nameValueSettings); + + // Register for events + events.SessionStart += this.sessionStartEventHandler; + events.SessionEnd += this.sessionEndEventHandler; + events.TestCaseStart += this.testCaseStartEventHandler; + events.TestCaseEnd += this.testCaseEndEventHandler; + } + + #endregion + + #region Internal + + /// + /// The write event logs. + /// + /// + /// The event log entries. + /// + /// + /// Max Log Entries. + /// + /// + /// The data collection context. + /// + /// + /// The requested duration. + /// + /// + /// The time request received. + /// + /// + /// The . + /// + internal string WriteEventLogs(List eventLogEntries, int maxLogEntries, DataCollectionContext dataCollectionContext, TimeSpan requestedDuration, DateTime timeRequestReceived) + { + // Generate a unique but friendly Directory name in the temp directory + string eventLogDirName = string.Format( + CultureInfo.InvariantCulture, + "{0}-{1}-{2:yyyy}{2:MM}{2:dd}-{2:HH}{2:mm}{2:ss}.{2:fff}", + "Event Log", + Environment.MachineName, + DateTime.Now); + + string eventLogDirPath = Path.Combine(Path.GetTempPath(), eventLogDirName); + + // Create the directory + this.fileHelper.CreateDirectory(eventLogDirPath); + + string eventLogBasePath = Path.Combine(eventLogDirPath, EventLogFileName); + bool unusedFilenameFound = false; + + string eventLogPath = eventLogBasePath + ".xml"; + + if (this.fileHelper.Exists(eventLogPath)) + { + for (int i = 1; !unusedFilenameFound; i++) + { + eventLogPath = eventLogBasePath + "-" + i.ToString(CultureInfo.InvariantCulture) + ".xml"; + + if (!this.fileHelper.Exists(eventLogPath)) + { + unusedFilenameFound = true; + } + } + } + + DateTime minDate = DateTime.MinValue; + + // Limit entries to a certain time range if requested + if (requestedDuration < TimeSpan.MaxValue) + { + try + { + minDate = timeRequestReceived - requestedDuration; + } + catch (ArgumentOutOfRangeException) + { + minDate = DateTime.MinValue; + } + } + + Stopwatch stopwatch = new Stopwatch(); + stopwatch.Start(); + EventLogXmlWriter.WriteEventLogEntriesToXmlFile( + eventLogPath, + eventLogEntries.Where( + entry => entry.TimeGenerated > minDate && entry.TimeGenerated < DateTime.MaxValue).OrderBy(x => x.TimeGenerated).ToList().Take(maxLogEntries).ToList(), + this.fileHelper); + + stopwatch.Stop(); + + if (EqtTrace.IsVerboseEnabled) + { + EqtTrace.Verbose( + string.Format( + CultureInfo.InvariantCulture, + "EventLogDataContainer: Wrote {0} event log entries to file '{1}' in {2} seconds", + eventLogEntries.Count, + eventLogPath, + stopwatch.Elapsed.TotalSeconds.ToString(CultureInfo.InvariantCulture))); + } + + // Write the event log file + FileTransferInformation fileTransferInformation = + new FileTransferInformation(dataCollectionContext, eventLogPath, true, this.fileHelper); + this.dataSink.SendFileAsync(fileTransferInformation); + + if (EqtTrace.IsVerboseEnabled) + { + EqtTrace.Verbose( + "EventLogDataContainer: Event log successfully sent for data collection context '{0}'.", + dataCollectionContext.ToString()); + } + + return eventLogPath; + } + #endregion + + #region IDisposable Members + + /// + /// Cleans up resources allocated by the data collector + /// + /// Not used since this class does not have a finaliser. + protected override void Dispose(bool disposing) + { + // Unregister events + this.events.SessionStart -= this.sessionStartEventHandler; + this.events.SessionEnd -= this.sessionEndEventHandler; + this.events.TestCaseStart -= this.testCaseStartEventHandler; + this.events.TestCaseEnd -= this.testCaseEndEventHandler; + + // Unregister EventLogEntry Written. + foreach (var eventLogContainer in this.eventLogContainerMap.Values) + { + eventLogContainer.Dispose(); + } + + // Delete all the temp event log directories + this.RemoveTempEventLogDirs(this.eventLogDirectories); + GC.SuppressFinalize(this); + } + + #endregion + + private static ISet ParseCommaSeparatedList(string commaSeparatedList) + { + ISet strings = new HashSet(); + string[] items = commaSeparatedList.Split(new char[] { ',' }); + foreach (string item in items) + { + strings.Add(item.Trim()); + } + + return strings; + } + + #region Event Handlers + + private void OnSessionStart(object sender, SessionStartEventArgs e) + { + ValidateArg.NotNull(e, "SessionStartEventArgs"); + ValidateArg.NotNull(e.Context, "SessionStartEventArgs.Context"); + + if (EqtTrace.IsVerboseEnabled) + { + EqtTrace.Verbose("EventLogDataCollector: SessionStart received"); + } + + this.StartCollectionForContext(e.Context, true); + } + + private void OnSessionEnd(object sender, SessionEndEventArgs e) + { + ValidateArg.NotNull(e, "SessionEndEventArgs"); + ValidateArg.NotNull(e.Context, "SessionEndEventArgs.Context"); + + if (EqtTrace.IsVerboseEnabled) + { + EqtTrace.Verbose("EventLogDataCollector: SessionEnd received"); + } + + this.WriteCollectedEventLogEntries(e.Context, true, TimeSpan.MaxValue, DateTime.Now); + } + + private void OnTestCaseStart(object sender, TestCaseStartEventArgs e) + { + ValidateArg.NotNull(e, "TestCaseStartEventArgs"); + ValidateArg.NotNull(e.Context, "TestCaseStartEventArgs.Context"); + + if (!e.Context.HasTestCase) + { + Debug.Fail("Context is not for a test case"); + throw new ArgumentNullException("TestCaseStartEventArgs.Context.HasTestCase"); + } + + if (EqtTrace.IsVerboseEnabled) + { + EqtTrace.Verbose("EventLogDataCollector: TestCaseStart received for test '{0}'.", e.TestCaseName); + } + + this.StartCollectionForContext(e.Context, false); + } + + private void OnTestCaseEnd(object sender, TestCaseEndEventArgs e) + { + ValidateArg.NotNull(e, "TestCaseEndEventArgs"); + + Debug.Assert(e.Context != null, "Context is null"); + Debug.Assert(e.Context.HasTestCase, "Context is not for a test case"); + + if (EqtTrace.IsVerboseEnabled) + { + EqtTrace.Verbose( + "EventLogDataCollector: TestCaseEnd received for test '{0}' with Test Outcome: {1}.", + e.TestCaseName, + e.TestOutcome); + } + + this.WriteCollectedEventLogEntries(e.Context, false, TimeSpan.MaxValue, DateTime.Now); + } + + #endregion + + #region Private methods + + private void RemoveTempEventLogDirs(List tempDirs) + { + if (tempDirs != null) + { + foreach (string dir in tempDirs) + { + // Delete only if the directory is empty + this.fileHelper.DeleteEmptyDirectroy(dir); + } + } + } + + private void StartCollectionForContext(DataCollectionContext dataCollectionContext, bool isSessionContext) + { + EventLogSessionContext eventLogSessionContext = null; + lock (this.ContextMap) + { + eventLogSessionContext = + new EventLogSessionContext(this.eventLogContainerMap); + this.ContextMap.Add(dataCollectionContext, eventLogSessionContext); + } + } + + private void WriteCollectedEventLogEntries( + DataCollectionContext dataCollectionContext, + bool isSessionEnd, + TimeSpan requestedDuration, + DateTime timeRequestReceived) + { + var context = this.GetEventLogSessionContext(dataCollectionContext); + context.CreateEventLogContainerEndIndexMap(); + + List eventLogEntries = new List(); + foreach (KeyValuePair kvp in this.eventLogContainerMap) + { + try + { + if (isSessionEnd) + { + kvp.Value.EventLog.EnableRaisingEvents = false; + } + + for (int i = context.EventLogContainerStartIndexMap[kvp.Key]; i <= context.EventLogContainerEndIndexMap[kvp.Key]; i++) + { + eventLogEntries.Add(kvp.Value.EventLogEntries[i]); + } + } + catch (Exception e) + { + this.logger.LogWarning( + dataCollectionContext, + string.Format( + CultureInfo.InvariantCulture, + Resource.CleanupException, + kvp.Value.EventLog, + e.ToString())); + } + } + + var fileName = this.WriteEventLogs(eventLogEntries, isSessionEnd ? int.MaxValue : this.maxEntries, dataCollectionContext, requestedDuration, timeRequestReceived); + + // Add the directory to the list + this.eventLogDirectories.Add(Path.GetDirectoryName(fileName)); + + lock (this.ContextMap) + { + this.ContextMap.Remove(dataCollectionContext); + } + } + + private void ConfigureEventLogNames(CollectorNameValueConfigurationManager collectorNameValueConfigurationManager) + { + this.eventLogNames = new HashSet(); + string eventLogs = collectorNameValueConfigurationManager[EventLogConstants.SettingEventLogs]; + if (eventLogs != null) + { + this.eventLogNames = ParseCommaSeparatedList(eventLogs); + if (EqtTrace.IsVerboseEnabled) + { + EqtTrace.Verbose( + "EventLogDataCollector configuration: " + EventLogConstants.SettingEventLogs + "=" + eventLogs); + } + } + else + { + // Default to collecting these standard logs + this.eventLogNames.Add("System"); + this.eventLogNames.Add("Security"); + this.eventLogNames.Add("Application"); + } + + foreach (string eventLogName in this.eventLogNames) + { + try + { + // Create an EventLog object and add it to the eventLogContext if one does not already exist + if (!this.eventLogContainerMap.ContainsKey(eventLogName)) + { + IEventLogContainer eventLogContainer = new EventLogContainer( + eventLogName, + this.eventSources, + this.entryTypes, + int.MaxValue, + this.logger, + this.dataCollectorContext); + this.eventLogContainerMap.Add(eventLogName, eventLogContainer); + } + + if (EqtTrace.IsVerboseEnabled) + { + EqtTrace.Verbose(string.Format( + CultureInfo.InvariantCulture, + "EventLogDataCollector: Created EventSource '{0}'", + eventLogName)); + } + } + catch (Exception ex) + { + this.logger.LogError( + null, + new EventLogCollectorException(string.Format(CultureInfo.InvariantCulture, Resource.ReadError, eventLogName, Environment.MachineName), ex)); + } + } + } + + private void ConfigureEventSources(CollectorNameValueConfigurationManager collectorNameValueConfigurationManager) + { + string eventSourcesStr = collectorNameValueConfigurationManager[EventLogConstants.SettingEventSources]; + if (!string.IsNullOrEmpty(eventSourcesStr)) + { + this.eventSources = ParseCommaSeparatedList(eventSourcesStr); + if (EqtTrace.IsVerboseEnabled) + { + EqtTrace.Verbose( + "EventLogDataCollector configuration: " + EventLogConstants.SettingEventSources + "=" + + this.eventSources); + } + } + } + + private void ConfigureEntryTypes(CollectorNameValueConfigurationManager collectorNameValueConfigurationManager) + { + this.entryTypes = new HashSet(); + string entryTypesStr = collectorNameValueConfigurationManager[EventLogConstants.SettingEntryTypes]; + if (entryTypesStr != null) + { + foreach (string entryTypestring in ParseCommaSeparatedList(entryTypesStr)) + { + this.entryTypes.Add( + (EventLogEntryType)Enum.Parse(typeof(EventLogEntryType), entryTypestring, true)); + } + + if (EqtTrace.IsVerboseEnabled) + { + EqtTrace.Verbose( + "EventLogDataCollector configuration: " + EventLogConstants.SettingEntryTypes + "=" + + this.entryTypes); + } + } + else + { + this.entryTypes.Add(EventLogEntryType.Error); + this.entryTypes.Add(EventLogEntryType.Warning); + this.entryTypes.Add(EventLogEntryType.FailureAudit); + } + } + + private void ConfigureMaxEntries(CollectorNameValueConfigurationManager collectorNameValueConfigurationManager) + { + string maxEntriesstring = collectorNameValueConfigurationManager[EventLogConstants.SettingMaxEntries]; + if (maxEntriesstring != null) + { + try + { + this.maxEntries = int.Parse(maxEntriesstring, CultureInfo.InvariantCulture); + + // A negative or 0 value means no maximum + if (this.maxEntries <= 0) + { + this.maxEntries = int.MaxValue; + } + } + catch (FormatException) + { + this.maxEntries = EventLogConstants.DefaultMaxEntries; + } + + if (EqtTrace.IsVerboseEnabled) + { + EqtTrace.Verbose( + "EventLogDataCollector configuration: " + EventLogConstants.SettingMaxEntries + "=" + + this.maxEntries); + } + } + else + { + this.maxEntries = EventLogConstants.DefaultMaxEntries; + } + } + + private EventLogSessionContext GetEventLogSessionContext(DataCollectionContext dataCollectionContext) + { + EventLogSessionContext eventLogSessionContext; + bool eventLogContainerFound; + lock (this.ContextMap) + { + eventLogContainerFound = this.ContextMap.TryGetValue(dataCollectionContext, out eventLogSessionContext); + } + + if (!eventLogContainerFound) + { + string msg = string.Format( + CultureInfo.InvariantCulture, + Resource.ContextNotFoundException, + dataCollectionContext.ToString()); + throw new EventLogCollectorException(msg, null); + } + + return eventLogSessionContext; + } + + #endregion + } +} diff --git a/src/DataCollectors/Microsoft.TestPlatform.Extensions.EventLogCollector/EventLogSessionContext.cs b/src/DataCollectors/Microsoft.TestPlatform.Extensions.EventLogCollector/EventLogSessionContext.cs new file mode 100644 index 0000000000..878dea9873 --- /dev/null +++ b/src/DataCollectors/Microsoft.TestPlatform.Extensions.EventLogCollector/EventLogSessionContext.cs @@ -0,0 +1,65 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace Microsoft.TestPlatform.Extensions.EventLogCollector +{ + using System.Collections.Generic; + + /// + /// Stores the start and end index for EventLogEntries correspoinding to a data collection session. + /// + internal class EventLogSessionContext + { + private IDictionary eventLogContainerMap; + + /// + /// Initializes a new instance of the class. + /// + /// + /// Event Log container map. + /// + public EventLogSessionContext(IDictionary eventLogContainerMap) + { + this.eventLogContainerMap = eventLogContainerMap; + this.CreateEventLogContainerStartIndexMap(); + } + + /// + /// Gets the start index for EventLogs Entries. + /// + internal Dictionary EventLogContainerStartIndexMap { get; private set; } + + /// + /// Gets the end index for EventLogs Entries + /// + internal Dictionary EventLogContainerEndIndexMap { get; private set; } + + /// + /// Creates the end index map for EventLogs Entries + /// + public void CreateEventLogContainerEndIndexMap() + { + this.EventLogContainerEndIndexMap = new Dictionary(this.eventLogContainerMap.Count); + + foreach (KeyValuePair kvp in this.eventLogContainerMap) + { + kvp.Value.OnEventLogEntryWritten(kvp.Value.EventLog, null); + + this.EventLogContainerEndIndexMap.Add(kvp.Key, kvp.Value.EventLogEntries.Count == 0 ? 0 : kvp.Value.EventLogEntries.Count - 1); + } + } + + /// + /// Creates the start index map for EventLogs Entries + /// + public void CreateEventLogContainerStartIndexMap() + { + this.EventLogContainerStartIndexMap = new Dictionary(this.eventLogContainerMap.Count); + + foreach (KeyValuePair kvp in this.eventLogContainerMap) + { + this.EventLogContainerStartIndexMap.Add(kvp.Key, kvp.Value.EventLogEntries.Count == 0 ? 0 : kvp.Value.EventLogEntries.Count - 1); + } + } + } +} \ No newline at end of file diff --git a/src/DataCollectors/Microsoft.TestPlatform.Extensions.EventLogCollector/EventLogXmlWriter.cs b/src/DataCollectors/Microsoft.TestPlatform.Extensions.EventLogCollector/EventLogXmlWriter.cs new file mode 100644 index 0000000000..1446d31295 --- /dev/null +++ b/src/DataCollectors/Microsoft.TestPlatform.Extensions.EventLogCollector/EventLogXmlWriter.cs @@ -0,0 +1,89 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace Microsoft.TestPlatform.Extensions.EventLogCollector +{ + using System; + using System.Collections.Generic; + using System.Data; + using System.Diagnostics; + using System.Globalization; + using System.IO; + using System.Text; + + using Microsoft.VisualStudio.TestPlatform.Utilities.Helpers.Interfaces; + + /// + /// This class writes event log entries to an XML file in a format that can be retrieved into a DataSet + /// + internal static class EventLogXmlWriter + { + #region Public methods + + /// + /// The write event log entries to xml file. + /// + /// + /// The xml file path. + /// + /// + /// The event log entries. + /// + /// + /// The file Helper. + /// + public static void WriteEventLogEntriesToXmlFile(string xmlFilePath, List eventLogEntries, IFileHelper fileHelper) + { + using (DataTable dataTable = new DataTable()) + { + dataTable.Locale = CultureInfo.InvariantCulture; + + // The MaxLength of the Type and Source columns must be set to allow indices to be created on them + DataColumn typeColumn = new DataColumn("Type", typeof(string)); + typeColumn.MaxLength = EventLogConstants.TypeColumnMaxLength; + dataTable.Columns.Add(typeColumn); + + dataTable.Columns.Add(new DataColumn("DateTime", typeof(DateTime))); + + DataColumn sourceColumn = new DataColumn("Source", typeof(string)); + sourceColumn.MaxLength = EventLogConstants.SourceColumnMaxLength; + dataTable.Columns.Add(sourceColumn); + + dataTable.Columns.Add(new DataColumn("Category", typeof(string))); + dataTable.Columns.Add(new DataColumn("EventID", typeof(long))); + dataTable.Columns.Add(new DataColumn("Description", typeof(string))); + dataTable.Columns.Add(new DataColumn("User", typeof(string))); + dataTable.Columns.Add(new DataColumn("Computer", typeof(string))); + dataTable.ExtendedProperties.Add("TimestampColumnName", "DateTime"); + dataTable.ExtendedProperties.Add("IndexColumnNames", "Source,Type"); + + foreach (EventLogEntry entry in eventLogEntries) + { + DataRow row = dataTable.NewRow(); + row["Type"] = entry.EntryType.ToString(); + row["DateTime"] = entry.TimeGenerated; + row["Source"] = entry.Source; + row["Category"] = entry.Category; + row["EventID"] = entry.InstanceId; + row["Description"] = entry.Message; + row["User"] = entry.UserName; + row["Computer"] = entry.MachineName; + dataTable.Rows.Add(row); + } + + DataSet dataSet = new DataSet(); + dataSet.Locale = CultureInfo.InvariantCulture; + dataSet.Tables.Add(dataTable); + + // Use UTF-16 encoding + StringBuilder stringBuilder = new StringBuilder(); + using (StringWriter stringWriter = new StringWriter(stringBuilder)) + { + dataSet.WriteXml(stringWriter, XmlWriteMode.WriteSchema); + fileHelper.WriteAllTextToFile(xmlFilePath, stringBuilder.ToString()); + } + } + } + } + #endregion +} diff --git a/src/DataCollectors/Microsoft.TestPlatform.Extensions.EventLogCollector/Friends.cs b/src/DataCollectors/Microsoft.TestPlatform.Extensions.EventLogCollector/Friends.cs new file mode 100644 index 0000000000..01d8b6658c --- /dev/null +++ b/src/DataCollectors/Microsoft.TestPlatform.Extensions.EventLogCollector/Friends.cs @@ -0,0 +1,12 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System.Runtime.CompilerServices; + +using Castle.Core.Internal; + +#region Test Assemblies + +[assembly: InternalsVisibleTo("Microsoft.TestPlatform.Extensions.EventLogCollector.UnitTests, PublicKey=002400000480000094000000060200000024000052534131000400000100010007d1fa57c4aed9f0a32e84aa0faefd0de9e8fd6aec8f87fb03766c834c99921eb23be79ad9d5dcc1dd9ad236132102900b723cf980957fc4e177108fc607774f29e8320e92ea05ece4e821c0a5efe8f1645c4c0c93c1ab99285d622caa652c1dfad63d745d6f2de5f17e5eaf0fc4963d261c8a12436518206dc093344d5ad293")] +[assembly: InternalsVisibleTo(InternalsVisible.ToDynamicProxyGenAssembly2)] +#endregion \ No newline at end of file diff --git a/src/DataCollectors/Microsoft.TestPlatform.Extensions.EventLogCollector/IEventLogContainer.cs b/src/DataCollectors/Microsoft.TestPlatform.Extensions.EventLogCollector/IEventLogContainer.cs new file mode 100644 index 0000000000..71166a0576 --- /dev/null +++ b/src/DataCollectors/Microsoft.TestPlatform.Extensions.EventLogCollector/IEventLogContainer.cs @@ -0,0 +1,36 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace Microsoft.TestPlatform.Extensions.EventLogCollector +{ + using System; + using System.Collections.Generic; + using System.Diagnostics; + + /// + /// Event log container interface + /// + internal interface IEventLogContainer : IDisposable + { + /// + /// Gets the event log. + /// + EventLog EventLog { get; } + + /// + /// Gets the event log entries. + /// + List EventLogEntries { get; } + + /// + /// Event Handler for handling log entries. + /// + /// + /// The source object that raised EventLog entry event. + /// + /// + /// Contains data related to EventLog entry. + /// + void OnEventLogEntryWritten(object source, EntryWrittenEventArgs e); + } +} diff --git a/src/DataCollectors/Microsoft.TestPlatform.Extensions.EventLogCollector/Microsoft.TestPlatform.Extensions.EventLogCollector.csproj b/src/DataCollectors/Microsoft.TestPlatform.Extensions.EventLogCollector/Microsoft.TestPlatform.Extensions.EventLogCollector.csproj new file mode 100644 index 0000000000..bff550e1a6 --- /dev/null +++ b/src/DataCollectors/Microsoft.TestPlatform.Extensions.EventLogCollector/Microsoft.TestPlatform.Extensions.EventLogCollector.csproj @@ -0,0 +1,51 @@ + + + + ..\..\..\ + + + + Microsoft.TestPlatform.Extensions.EventLogCollector + net451 + true + true + + + + + + + + + + + ResXFileCodeGenerator + Resources.Designer.cs + + + + + ..\..\..\packages\castle.core\4.1.0\lib\net45\Castle.Core.dll + + + + + + + + + + + True + True + Resources.resx + + + + + ResXFileCodeGenerator + Resources.Designer.cs + + + + diff --git a/src/DataCollectors/Microsoft.TestPlatform.Extensions.EventLogCollector/Resources/Resources.Designer.cs b/src/DataCollectors/Microsoft.TestPlatform.Extensions.EventLogCollector/Resources/Resources.Designer.cs new file mode 100644 index 0000000000..b6c5add47f --- /dev/null +++ b/src/DataCollectors/Microsoft.TestPlatform.Extensions.EventLogCollector/Resources/Resources.Designer.cs @@ -0,0 +1,126 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace Microsoft.TestPlatform.Extensions.EventLogCollector.Resources { + using System; + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "15.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class Resources { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal Resources() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Microsoft.TestPlatform.Extensions.EventLogCollector.Resources.Resources", typeof(Resources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// + /// Looks up a localized string similar to An exception occurred while collecting final entries from the event log '{0}': {1}. + /// + internal static string CleanupException { + get { + return ResourceManager.GetString("CleanupException", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Event Log DataCollector did not find eventLogContext for DataCollectionContext: {0}. + /// + internal static string ContextNotFoundException { + get { + return ResourceManager.GetString("ContextNotFoundException", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The '{0}' event log has encountered an exception, some events might get lost. Error : {1}. + /// + internal static string EventsLostError { + get { + return ResourceManager.GetString("EventsLostError", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The '{0}' event log may have been cleared during collection; some events may not have been collected. + /// + internal static string EventsLostWarning { + get { + return ResourceManager.GetString("EventsLostWarning", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The Event Log DataCollector encountered an invalid value for 'EntryTypes' in its configuration: {0}. + /// + internal static string InvalidEntryTypeInConfig { + get { + return ResourceManager.GetString("InvalidEntryTypeInConfig", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The Event Log DataCollector encountered an invalid value for 'MaxEventLogEntriesToCollect' in its configuration: {0}. + /// + internal static string InvalidMaxEntriesInConfig { + get { + return ResourceManager.GetString("InvalidMaxEntriesInConfig", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Unable to read event log '{0}' from computer '{1}'. + /// + internal static string ReadError { + get { + return ResourceManager.GetString("ReadError", resourceCulture); + } + } + } +} diff --git a/src/DataCollectors/Microsoft.TestPlatform.Extensions.EventLogCollector/Resources/Resources.resx b/src/DataCollectors/Microsoft.TestPlatform.Extensions.EventLogCollector/Resources/Resources.resx new file mode 100644 index 0000000000..1ac31055bf --- /dev/null +++ b/src/DataCollectors/Microsoft.TestPlatform.Extensions.EventLogCollector/Resources/Resources.resx @@ -0,0 +1,141 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + An exception occurred while collecting final entries from the event log '{0}': {1} + + + Event Log DataCollector did not find eventLogContext for DataCollectionContext: {0} + + + The '{0}' event log has encountered an exception, some events might get lost. Error : {1} + + + The '{0}' event log may have been cleared during collection; some events may not have been collected + + + The Event Log DataCollector encountered an invalid value for 'EntryTypes' in its configuration: {0} + + + The Event Log DataCollector encountered an invalid value for 'MaxEventLogEntriesToCollect' in its configuration: {0} + + + Unable to read event log '{0}' from computer '{1}' + + \ No newline at end of file diff --git a/src/DataCollectors/Microsoft.TestPlatform.Extensions.EventLogCollector/Resources/xlf/Resources.cs.xlf b/src/DataCollectors/Microsoft.TestPlatform.Extensions.EventLogCollector/Resources/xlf/Resources.cs.xlf new file mode 100644 index 0000000000..d6df232087 --- /dev/null +++ b/src/DataCollectors/Microsoft.TestPlatform.Extensions.EventLogCollector/Resources/xlf/Resources.cs.xlf @@ -0,0 +1,60 @@ + + + +
+ + 27 + 22.95 + 22.95 + 0 + 0 + + + 0 + 0 + 0 + 0 + 0 + 27 + 0 + +
+ + + An exception occurred while collecting final entries from the event log '{0}': {1} + An exception occurred while collecting final entries from the event log '{0}': {1} + + + + Event Log DataCollector did not find eventLogContext for DataCollectionContext: {0} + Event Log DataCollector did not find eventLogContext for DataCollectionContext: {0} + + + + The '{0}' event log may have been cleared during collection; some events may not have been collected + The '{0}' event log may have been cleared during collection; some events may not have been collected + + + + The Event Log DataCollector encountered an invalid value for 'EntryTypes' in its configuration: {0} + The Event Log DataCollector encountered an invalid event log entry type in its configuration: {0} + + + + The Event Log DataCollector encountered an invalid value for 'MaxEventLogEntriesToCollect' in its configuration: {0} + The Event Log DataCollector encountered an invalid value for 'MaxEventLogEntriesToCollect' in its configuration: {0} + + + + Unable to read event log '{0}' from computer '{1}' + Unable to read event log '{0}' from computer '{1}' + + + + The '{0}' event log has encountered an exception, some events might get lost. Error : {1} + The '{0}' event log has encountered an exception, some events might get lost. Error : {1} + + + +
+
\ No newline at end of file diff --git a/src/DataCollectors/Microsoft.TestPlatform.Extensions.EventLogCollector/Resources/xlf/Resources.de.xlf b/src/DataCollectors/Microsoft.TestPlatform.Extensions.EventLogCollector/Resources/xlf/Resources.de.xlf new file mode 100644 index 0000000000..2eff621e26 --- /dev/null +++ b/src/DataCollectors/Microsoft.TestPlatform.Extensions.EventLogCollector/Resources/xlf/Resources.de.xlf @@ -0,0 +1,60 @@ + + + +
+ + 27 + 22.95 + 22.95 + 0 + 0 + + + 0 + 0 + 0 + 0 + 0 + 27 + 0 + +
+ + + An exception occurred while collecting final entries from the event log '{0}': {1} + An exception occurred while collecting final entries from the event log '{0}': {1} + + + + Event Log DataCollector did not find eventLogContext for DataCollectionContext: {0} + Event Log DataCollector did not find eventLogContext for DataCollectionContext: {0} + + + + The '{0}' event log may have been cleared during collection; some events may not have been collected + The '{0}' event log may have been cleared during collection; some events may not have been collected + + + + The Event Log DataCollector encountered an invalid value for 'EntryTypes' in its configuration: {0} + The Event Log DataCollector encountered an invalid event log entry type in its configuration: {0} + + + + The Event Log DataCollector encountered an invalid value for 'MaxEventLogEntriesToCollect' in its configuration: {0} + The Event Log DataCollector encountered an invalid value for 'MaxEventLogEntriesToCollect' in its configuration: {0} + + + + Unable to read event log '{0}' from computer '{1}' + Unable to read event log '{0}' from computer '{1}' + + + + The '{0}' event log has encountered an exception, some events might get lost. Error : {1} + The '{0}' event log has encountered an exception, some events might get lost. Error : {1} + + + +
+
\ No newline at end of file diff --git a/src/DataCollectors/Microsoft.TestPlatform.Extensions.EventLogCollector/Resources/xlf/Resources.es.xlf b/src/DataCollectors/Microsoft.TestPlatform.Extensions.EventLogCollector/Resources/xlf/Resources.es.xlf new file mode 100644 index 0000000000..64533dfe3c --- /dev/null +++ b/src/DataCollectors/Microsoft.TestPlatform.Extensions.EventLogCollector/Resources/xlf/Resources.es.xlf @@ -0,0 +1,60 @@ + + + +
+ + 27 + 22.95 + 22.95 + 0 + 0 + + + 0 + 0 + 0 + 0 + 0 + 27 + 0 + +
+ + + An exception occurred while collecting final entries from the event log '{0}': {1} + An exception occurred while collecting final entries from the event log '{0}': {1} + + + + Event Log DataCollector did not find eventLogContext for DataCollectionContext: {0} + Event Log DataCollector did not find eventLogContext for DataCollectionContext: {0} + + + + The '{0}' event log may have been cleared during collection; some events may not have been collected + The '{0}' event log may have been cleared during collection; some events may not have been collected + + + + The Event Log DataCollector encountered an invalid value for 'EntryTypes' in its configuration: {0} + The Event Log DataCollector encountered an invalid event log entry type in its configuration: {0} + + + + The Event Log DataCollector encountered an invalid value for 'MaxEventLogEntriesToCollect' in its configuration: {0} + The Event Log DataCollector encountered an invalid value for 'MaxEventLogEntriesToCollect' in its configuration: {0} + + + + Unable to read event log '{0}' from computer '{1}' + Unable to read event log '{0}' from computer '{1}' + + + + The '{0}' event log has encountered an exception, some events might get lost. Error : {1} + The '{0}' event log has encountered an exception, some events might get lost. Error : {1} + + + +
+
\ No newline at end of file diff --git a/src/DataCollectors/Microsoft.TestPlatform.Extensions.EventLogCollector/Resources/xlf/Resources.fr.xlf b/src/DataCollectors/Microsoft.TestPlatform.Extensions.EventLogCollector/Resources/xlf/Resources.fr.xlf new file mode 100644 index 0000000000..617a770f11 --- /dev/null +++ b/src/DataCollectors/Microsoft.TestPlatform.Extensions.EventLogCollector/Resources/xlf/Resources.fr.xlf @@ -0,0 +1,60 @@ + + + +
+ + 27 + 22.95 + 22.95 + 0 + 0 + + + 0 + 0 + 0 + 0 + 0 + 27 + 0 + +
+ + + An exception occurred while collecting final entries from the event log '{0}': {1} + An exception occurred while collecting final entries from the event log '{0}': {1} + + + + Event Log DataCollector did not find eventLogContext for DataCollectionContext: {0} + Event Log DataCollector did not find eventLogContext for DataCollectionContext: {0} + + + + The '{0}' event log may have been cleared during collection; some events may not have been collected + The '{0}' event log may have been cleared during collection; some events may not have been collected + + + + The Event Log DataCollector encountered an invalid value for 'EntryTypes' in its configuration: {0} + The Event Log DataCollector encountered an invalid event log entry type in its configuration: {0} + + + + The Event Log DataCollector encountered an invalid value for 'MaxEventLogEntriesToCollect' in its configuration: {0} + The Event Log DataCollector encountered an invalid value for 'MaxEventLogEntriesToCollect' in its configuration: {0} + + + + Unable to read event log '{0}' from computer '{1}' + Unable to read event log '{0}' from computer '{1}' + + + + The '{0}' event log has encountered an exception, some events might get lost. Error : {1} + The '{0}' event log has encountered an exception, some events might get lost. Error : {1} + + + +
+
\ No newline at end of file diff --git a/src/DataCollectors/Microsoft.TestPlatform.Extensions.EventLogCollector/Resources/xlf/Resources.it.xlf b/src/DataCollectors/Microsoft.TestPlatform.Extensions.EventLogCollector/Resources/xlf/Resources.it.xlf new file mode 100644 index 0000000000..5e30fc214d --- /dev/null +++ b/src/DataCollectors/Microsoft.TestPlatform.Extensions.EventLogCollector/Resources/xlf/Resources.it.xlf @@ -0,0 +1,60 @@ + + + +
+ + 27 + 22.95 + 22.95 + 0 + 0 + + + 0 + 0 + 0 + 0 + 0 + 27 + 0 + +
+ + + An exception occurred while collecting final entries from the event log '{0}': {1} + An exception occurred while collecting final entries from the event log '{0}': {1} + + + + Event Log DataCollector did not find eventLogContext for DataCollectionContext: {0} + Event Log DataCollector did not find eventLogContext for DataCollectionContext: {0} + + + + The '{0}' event log may have been cleared during collection; some events may not have been collected + The '{0}' event log may have been cleared during collection; some events may not have been collected + + + + The Event Log DataCollector encountered an invalid value for 'EntryTypes' in its configuration: {0} + The Event Log DataCollector encountered an invalid event log entry type in its configuration: {0} + + + + The Event Log DataCollector encountered an invalid value for 'MaxEventLogEntriesToCollect' in its configuration: {0} + The Event Log DataCollector encountered an invalid value for 'MaxEventLogEntriesToCollect' in its configuration: {0} + + + + Unable to read event log '{0}' from computer '{1}' + Unable to read event log '{0}' from computer '{1}' + + + + The '{0}' event log has encountered an exception, some events might get lost. Error : {1} + The '{0}' event log has encountered an exception, some events might get lost. Error : {1} + + + +
+
\ No newline at end of file diff --git a/src/DataCollectors/Microsoft.TestPlatform.Extensions.EventLogCollector/Resources/xlf/Resources.ja.xlf b/src/DataCollectors/Microsoft.TestPlatform.Extensions.EventLogCollector/Resources/xlf/Resources.ja.xlf new file mode 100644 index 0000000000..d694544084 --- /dev/null +++ b/src/DataCollectors/Microsoft.TestPlatform.Extensions.EventLogCollector/Resources/xlf/Resources.ja.xlf @@ -0,0 +1,60 @@ + + + +
+ + 27 + 22.95 + 22.95 + 0 + 0 + + + 0 + 0 + 0 + 0 + 0 + 27 + 0 + +
+ + + An exception occurred while collecting final entries from the event log '{0}': {1} + An exception occurred while collecting final entries from the event log '{0}': {1} + + + + Event Log DataCollector did not find eventLogContext for DataCollectionContext: {0} + Event Log DataCollector did not find eventLogContext for DataCollectionContext: {0} + + + + The '{0}' event log may have been cleared during collection; some events may not have been collected + The '{0}' event log may have been cleared during collection; some events may not have been collected + + + + The Event Log DataCollector encountered an invalid value for 'EntryTypes' in its configuration: {0} + The Event Log DataCollector encountered an invalid event log entry type in its configuration: {0} + + + + The Event Log DataCollector encountered an invalid value for 'MaxEventLogEntriesToCollect' in its configuration: {0} + The Event Log DataCollector encountered an invalid value for 'MaxEventLogEntriesToCollect' in its configuration: {0} + + + + Unable to read event log '{0}' from computer '{1}' + Unable to read event log '{0}' from computer '{1}' + + + + The '{0}' event log has encountered an exception, some events might get lost. Error : {1} + The '{0}' event log has encountered an exception, some events might get lost. Error : {1} + + + +
+
\ No newline at end of file diff --git a/src/DataCollectors/Microsoft.TestPlatform.Extensions.EventLogCollector/Resources/xlf/Resources.ko.xlf b/src/DataCollectors/Microsoft.TestPlatform.Extensions.EventLogCollector/Resources/xlf/Resources.ko.xlf new file mode 100644 index 0000000000..abfe05a23c --- /dev/null +++ b/src/DataCollectors/Microsoft.TestPlatform.Extensions.EventLogCollector/Resources/xlf/Resources.ko.xlf @@ -0,0 +1,60 @@ + + + +
+ + 27 + 22.95 + 22.95 + 0 + 0 + + + 0 + 0 + 0 + 0 + 0 + 27 + 0 + +
+ + + An exception occurred while collecting final entries from the event log '{0}': {1} + An exception occurred while collecting final entries from the event log '{0}': {1} + + + + Event Log DataCollector did not find eventLogContext for DataCollectionContext: {0} + Event Log DataCollector did not find eventLogContext for DataCollectionContext: {0} + + + + The '{0}' event log may have been cleared during collection; some events may not have been collected + The '{0}' event log may have been cleared during collection; some events may not have been collected + + + + The Event Log DataCollector encountered an invalid value for 'EntryTypes' in its configuration: {0} + The Event Log DataCollector encountered an invalid event log entry type in its configuration: {0} + + + + The Event Log DataCollector encountered an invalid value for 'MaxEventLogEntriesToCollect' in its configuration: {0} + The Event Log DataCollector encountered an invalid value for 'MaxEventLogEntriesToCollect' in its configuration: {0} + + + + Unable to read event log '{0}' from computer '{1}' + Unable to read event log '{0}' from computer '{1}' + + + + The '{0}' event log has encountered an exception, some events might get lost. Error : {1} + The '{0}' event log has encountered an exception, some events might get lost. Error : {1} + + + +
+
\ No newline at end of file diff --git a/src/DataCollectors/Microsoft.TestPlatform.Extensions.EventLogCollector/Resources/xlf/Resources.pl.xlf b/src/DataCollectors/Microsoft.TestPlatform.Extensions.EventLogCollector/Resources/xlf/Resources.pl.xlf new file mode 100644 index 0000000000..e854b2eab0 --- /dev/null +++ b/src/DataCollectors/Microsoft.TestPlatform.Extensions.EventLogCollector/Resources/xlf/Resources.pl.xlf @@ -0,0 +1,60 @@ + + + +
+ + 27 + 22.95 + 22.95 + 0 + 0 + + + 0 + 0 + 0 + 0 + 0 + 27 + 0 + +
+ + + An exception occurred while collecting final entries from the event log '{0}': {1} + An exception occurred while collecting final entries from the event log '{0}': {1} + + + + Event Log DataCollector did not find eventLogContext for DataCollectionContext: {0} + Event Log DataCollector did not find eventLogContext for DataCollectionContext: {0} + + + + The '{0}' event log may have been cleared during collection; some events may not have been collected + The '{0}' event log may have been cleared during collection; some events may not have been collected + + + + The Event Log DataCollector encountered an invalid value for 'EntryTypes' in its configuration: {0} + The Event Log DataCollector encountered an invalid event log entry type in its configuration: {0} + + + + The Event Log DataCollector encountered an invalid value for 'MaxEventLogEntriesToCollect' in its configuration: {0} + The Event Log DataCollector encountered an invalid value for 'MaxEventLogEntriesToCollect' in its configuration: {0} + + + + Unable to read event log '{0}' from computer '{1}' + Unable to read event log '{0}' from computer '{1}' + + + + The '{0}' event log has encountered an exception, some events might get lost. Error : {1} + The '{0}' event log has encountered an exception, some events might get lost. Error : {1} + + + +
+
\ No newline at end of file diff --git a/src/DataCollectors/Microsoft.TestPlatform.Extensions.EventLogCollector/Resources/xlf/Resources.pt-BR.xlf b/src/DataCollectors/Microsoft.TestPlatform.Extensions.EventLogCollector/Resources/xlf/Resources.pt-BR.xlf new file mode 100644 index 0000000000..f69c7c523c --- /dev/null +++ b/src/DataCollectors/Microsoft.TestPlatform.Extensions.EventLogCollector/Resources/xlf/Resources.pt-BR.xlf @@ -0,0 +1,60 @@ + + + +
+ + 27 + 22.95 + 22.95 + 0 + 0 + + + 0 + 0 + 0 + 0 + 0 + 27 + 0 + +
+ + + An exception occurred while collecting final entries from the event log '{0}': {1} + An exception occurred while collecting final entries from the event log '{0}': {1} + + + + Event Log DataCollector did not find eventLogContext for DataCollectionContext: {0} + Event Log DataCollector did not find eventLogContext for DataCollectionContext: {0} + + + + The '{0}' event log may have been cleared during collection; some events may not have been collected + The '{0}' event log may have been cleared during collection; some events may not have been collected + + + + The Event Log DataCollector encountered an invalid value for 'EntryTypes' in its configuration: {0} + The Event Log DataCollector encountered an invalid event log entry type in its configuration: {0} + + + + The Event Log DataCollector encountered an invalid value for 'MaxEventLogEntriesToCollect' in its configuration: {0} + The Event Log DataCollector encountered an invalid value for 'MaxEventLogEntriesToCollect' in its configuration: {0} + + + + Unable to read event log '{0}' from computer '{1}' + Unable to read event log '{0}' from computer '{1}' + + + + The '{0}' event log has encountered an exception, some events might get lost. Error : {1} + The '{0}' event log has encountered an exception, some events might get lost. Error : {1} + + + +
+
\ No newline at end of file diff --git a/src/DataCollectors/Microsoft.TestPlatform.Extensions.EventLogCollector/Resources/xlf/Resources.ru.xlf b/src/DataCollectors/Microsoft.TestPlatform.Extensions.EventLogCollector/Resources/xlf/Resources.ru.xlf new file mode 100644 index 0000000000..bf1b874b36 --- /dev/null +++ b/src/DataCollectors/Microsoft.TestPlatform.Extensions.EventLogCollector/Resources/xlf/Resources.ru.xlf @@ -0,0 +1,60 @@ + + + +
+ + 27 + 22.95 + 22.95 + 0 + 0 + + + 0 + 0 + 0 + 0 + 0 + 27 + 0 + +
+ + + An exception occurred while collecting final entries from the event log '{0}': {1} + An exception occurred while collecting final entries from the event log '{0}': {1} + + + + Event Log DataCollector did not find eventLogContext for DataCollectionContext: {0} + Event Log DataCollector did not find eventLogContext for DataCollectionContext: {0} + + + + The '{0}' event log may have been cleared during collection; some events may not have been collected + The '{0}' event log may have been cleared during collection; some events may not have been collected + + + + The Event Log DataCollector encountered an invalid value for 'EntryTypes' in its configuration: {0} + The Event Log DataCollector encountered an invalid event log entry type in its configuration: {0} + + + + The Event Log DataCollector encountered an invalid value for 'MaxEventLogEntriesToCollect' in its configuration: {0} + The Event Log DataCollector encountered an invalid value for 'MaxEventLogEntriesToCollect' in its configuration: {0} + + + + Unable to read event log '{0}' from computer '{1}' + Unable to read event log '{0}' from computer '{1}' + + + + The '{0}' event log has encountered an exception, some events might get lost. Error : {1} + The '{0}' event log has encountered an exception, some events might get lost. Error : {1} + + + +
+
\ No newline at end of file diff --git a/src/DataCollectors/Microsoft.TestPlatform.Extensions.EventLogCollector/Resources/xlf/Resources.tr.xlf b/src/DataCollectors/Microsoft.TestPlatform.Extensions.EventLogCollector/Resources/xlf/Resources.tr.xlf new file mode 100644 index 0000000000..e26b195284 --- /dev/null +++ b/src/DataCollectors/Microsoft.TestPlatform.Extensions.EventLogCollector/Resources/xlf/Resources.tr.xlf @@ -0,0 +1,60 @@ + + + +
+ + 27 + 22.95 + 22.95 + 0 + 0 + + + 0 + 0 + 0 + 0 + 0 + 27 + 0 + +
+ + + An exception occurred while collecting final entries from the event log '{0}': {1} + An exception occurred while collecting final entries from the event log '{0}': {1} + + + + Event Log DataCollector did not find eventLogContext for DataCollectionContext: {0} + Event Log DataCollector did not find eventLogContext for DataCollectionContext: {0} + + + + The '{0}' event log may have been cleared during collection; some events may not have been collected + The '{0}' event log may have been cleared during collection; some events may not have been collected + + + + The Event Log DataCollector encountered an invalid value for 'EntryTypes' in its configuration: {0} + The Event Log DataCollector encountered an invalid event log entry type in its configuration: {0} + + + + The Event Log DataCollector encountered an invalid value for 'MaxEventLogEntriesToCollect' in its configuration: {0} + The Event Log DataCollector encountered an invalid value for 'MaxEventLogEntriesToCollect' in its configuration: {0} + + + + Unable to read event log '{0}' from computer '{1}' + Unable to read event log '{0}' from computer '{1}' + + + + The '{0}' event log has encountered an exception, some events might get lost. Error : {1} + The '{0}' event log has encountered an exception, some events might get lost. Error : {1} + + + +
+
\ No newline at end of file diff --git a/src/DataCollectors/Microsoft.TestPlatform.Extensions.EventLogCollector/Resources/xlf/Resources.xlf b/src/DataCollectors/Microsoft.TestPlatform.Extensions.EventLogCollector/Resources/xlf/Resources.xlf new file mode 100644 index 0000000000..03643df81f --- /dev/null +++ b/src/DataCollectors/Microsoft.TestPlatform.Extensions.EventLogCollector/Resources/xlf/Resources.xlf @@ -0,0 +1,42 @@ + + + + + + An exception occurred while collecting final entries from the event log '{0}': {1} + An exception occurred while collecting final entries from the event log '{0}': {1} + + + + Event Log DataCollector did not find eventLogContext for DataCollectionContext: {0} + Event Log DataCollector did not find eventLogContext for DataCollectionContext: {0} + + + + The '{0}' event log may have been cleared during collection; some events may not have been collected + The '{0}' event log may have been cleared during collection; some events may not have been collected + + + + The Event Log DataCollector encountered an invalid value for 'EntryTypes' in its configuration: {0} + The Event Log DataCollector encountered an invalid event log entry type in its configuration: {0} + + + + The Event Log DataCollector encountered an invalid value for 'MaxEventLogEntriesToCollect' in its configuration: {0} + The Event Log DataCollector encountered an invalid value for 'MaxEventLogEntriesToCollect' in its configuration: {0} + + + + Unable to read event log '{0}' from computer '{1}' + Unable to read event log '{0}' from computer '{1}' + + + + The '{0}' event log has encountered an exception, some events might get lost. Error : {1} + The '{0}' event log has encountered an exception, some events might get lost. Error : {1} + + + + + \ No newline at end of file diff --git a/src/DataCollectors/Microsoft.TestPlatform.Extensions.EventLogCollector/Resources/xlf/Resources.zh-Hans.xlf b/src/DataCollectors/Microsoft.TestPlatform.Extensions.EventLogCollector/Resources/xlf/Resources.zh-Hans.xlf new file mode 100644 index 0000000000..e39d33ff0a --- /dev/null +++ b/src/DataCollectors/Microsoft.TestPlatform.Extensions.EventLogCollector/Resources/xlf/Resources.zh-Hans.xlf @@ -0,0 +1,60 @@ + + + +
+ + 27 + 22.95 + 22.95 + 0 + 0 + + + 0 + 0 + 0 + 0 + 0 + 27 + 0 + +
+ + + An exception occurred while collecting final entries from the event log '{0}': {1} + An exception occurred while collecting final entries from the event log '{0}': {1} + + + + Event Log DataCollector did not find eventLogContext for DataCollectionContext: {0} + Event Log DataCollector did not find eventLogContext for DataCollectionContext: {0} + + + + The '{0}' event log may have been cleared during collection; some events may not have been collected + The '{0}' event log may have been cleared during collection; some events may not have been collected + + + + The Event Log DataCollector encountered an invalid value for 'EntryTypes' in its configuration: {0} + The Event Log DataCollector encountered an invalid event log entry type in its configuration: {0} + + + + The Event Log DataCollector encountered an invalid value for 'MaxEventLogEntriesToCollect' in its configuration: {0} + The Event Log DataCollector encountered an invalid value for 'MaxEventLogEntriesToCollect' in its configuration: {0} + + + + Unable to read event log '{0}' from computer '{1}' + Unable to read event log '{0}' from computer '{1}' + + + + The '{0}' event log has encountered an exception, some events might get lost. Error : {1} + The '{0}' event log has encountered an exception, some events might get lost. Error : {1} + + + +
+
\ No newline at end of file diff --git a/src/DataCollectors/Microsoft.TestPlatform.Extensions.EventLogCollector/Resources/xlf/Resources.zh-Hant.xlf b/src/DataCollectors/Microsoft.TestPlatform.Extensions.EventLogCollector/Resources/xlf/Resources.zh-Hant.xlf new file mode 100644 index 0000000000..e25f9f90b3 --- /dev/null +++ b/src/DataCollectors/Microsoft.TestPlatform.Extensions.EventLogCollector/Resources/xlf/Resources.zh-Hant.xlf @@ -0,0 +1,60 @@ + + + +
+ + 27 + 22.95 + 22.95 + 0 + 0 + + + 0 + 0 + 0 + 0 + 0 + 27 + 0 + +
+ + + An exception occurred while collecting final entries from the event log '{0}': {1} + An exception occurred while collecting final entries from the event log '{0}': {1} + + + + Event Log DataCollector did not find eventLogContext for DataCollectionContext: {0} + Event Log DataCollector did not find eventLogContext for DataCollectionContext: {0} + + + + The '{0}' event log may have been cleared during collection; some events may not have been collected + The '{0}' event log may have been cleared during collection; some events may not have been collected + + + + The Event Log DataCollector encountered an invalid value for 'EntryTypes' in its configuration: {0} + The Event Log DataCollector encountered an invalid event log entry type in its configuration: {0} + + + + The Event Log DataCollector encountered an invalid value for 'MaxEventLogEntriesToCollect' in its configuration: {0} + The Event Log DataCollector encountered an invalid value for 'MaxEventLogEntriesToCollect' in its configuration: {0} + + + + Unable to read event log '{0}' from computer '{1}' + Unable to read event log '{0}' from computer '{1}' + + + + The '{0}' event log has encountered an exception, some events might get lost. Error : {1} + The '{0}' event log has encountered an exception, some events might get lost. Error : {1} + + + +
+
\ No newline at end of file diff --git a/src/Microsoft.TestPlatform.Common/DataCollection/TestPlatformDataCollectionSink.cs b/src/Microsoft.TestPlatform.Common/DataCollection/TestPlatformDataCollectionSink.cs index dea3529ccf..cde5cf40c9 100644 --- a/src/Microsoft.TestPlatform.Common/DataCollection/TestPlatformDataCollectionSink.cs +++ b/src/Microsoft.TestPlatform.Common/DataCollection/TestPlatformDataCollectionSink.cs @@ -63,8 +63,6 @@ public override void SendFileAsync(FileTransferInformation fileTransferInformati { ValidateArg.NotNull(fileTransferInformation, "fileTransferInformation"); - Debug.Assert(System.IO.File.Exists(fileTransferInformation.Path), "DataCollector file '" + fileTransferInformation.Path + "' does not exist!"); - this.AttachmentManager.AddAttachment(fileTransferInformation, this.SendFileCompleted, this.DataCollectorConfig.TypeUri, this.DataCollectorConfig.FriendlyName); } } diff --git a/src/Microsoft.TestPlatform.CoreUtilities/Helpers/FileHelper.cs b/src/Microsoft.TestPlatform.CoreUtilities/Helpers/FileHelper.cs index fffc0740c5..ba0070cae9 100644 --- a/src/Microsoft.TestPlatform.CoreUtilities/Helpers/FileHelper.cs +++ b/src/Microsoft.TestPlatform.CoreUtilities/Helpers/FileHelper.cs @@ -46,7 +46,10 @@ public Stream GetStream(string filePath, FileMode mode, FileAccess access = File } /// - public IEnumerable EnumerateFiles(string directory, SearchOption searchOption, params string[] endsWithSearchPatterns) + public IEnumerable EnumerateFiles( + string directory, + SearchOption searchOption, + params string[] endsWithSearchPatterns) { if (endsWithSearchPatterns == null || endsWithSearchPatterns.Length == 0) { @@ -56,7 +59,8 @@ public IEnumerable EnumerateFiles(string directory, SearchOption searchO var files = Directory.EnumerateFiles(directory, "*", searchOption); return files.Where( - file => endsWithSearchPatterns.Any(pattern => file.EndsWith(pattern, StringComparison.OrdinalIgnoreCase))); + file => endsWithSearchPatterns.Any( + pattern => file.EndsWith(pattern, StringComparison.OrdinalIgnoreCase))); } /// @@ -76,5 +80,34 @@ public void MoveFile(string sourcePath, string destinationPath) { File.Move(sourcePath, destinationPath); } + + /// + public void WriteAllTextToFile(string filePath, string content) + { + File.WriteAllText(filePath, content); + } + + /// + public string GetFullPath(string path) + { + return Path.GetFullPath(path); + } + + /// + public void DeleteEmptyDirectroy(string dirPath) + { + try + { + if (Directory.Exists(dirPath) && Directory.GetFiles(dirPath).Length == 0 + && Directory.GetDirectories(dirPath).Length == 0) + { + Directory.Delete(dirPath, true); + } + } + catch + { + // ignored + } + } } } diff --git a/src/Microsoft.TestPlatform.CoreUtilities/Helpers/Interfaces/IFileHelper.cs b/src/Microsoft.TestPlatform.CoreUtilities/Helpers/Interfaces/IFileHelper.cs index 4c33b271a9..8127237c83 100644 --- a/src/Microsoft.TestPlatform.CoreUtilities/Helpers/Interfaces/IFileHelper.cs +++ b/src/Microsoft.TestPlatform.CoreUtilities/Helpers/Interfaces/IFileHelper.cs @@ -76,5 +76,35 @@ public interface IFileHelper /// Full path of the file. /// Target path for the file. void MoveFile(string sourcePath, string destinationPath); + + /// + /// The write all text to file. + /// + /// + /// The file Path. + /// + /// + /// The content. + /// + void WriteAllTextToFile(string filePath, string content); + + /// + /// Gets full path if relative path is specified. + /// + /// + /// The path. + /// + /// + /// Full path. + /// + string GetFullPath(string path); + + /// + /// Helper for deleting a directory. It deletes the directory only if its empty. + /// + /// + /// The directory path. + /// + void DeleteEmptyDirectroy(string directoryPath); } } diff --git a/src/Microsoft.TestPlatform.CrossPlatEngine/DataCollection/DataCollectionLauncherFactory.cs b/src/Microsoft.TestPlatform.CrossPlatEngine/DataCollection/DataCollectionLauncherFactory.cs index cebe3108cb..d54482a909 100644 --- a/src/Microsoft.TestPlatform.CrossPlatEngine/DataCollection/DataCollectionLauncherFactory.cs +++ b/src/Microsoft.TestPlatform.CrossPlatEngine/DataCollection/DataCollectionLauncherFactory.cs @@ -28,7 +28,7 @@ internal static IDataCollectionLauncher GetDataCollectorLauncher(IProcessHelper var dataCollectionRunSettings = XmlRunSettingsUtilities.GetDataCollectionRunSettings(settingsXml); foreach (var dataCollectorSettings in dataCollectionRunSettings.DataCollectorSettingsList) { - if (dataCollectorSettings.FriendlyName.ToLower().Equals("code coverage")) + if (dataCollectorSettings.FriendlyName.ToLower().Equals("code coverage") || dataCollectorSettings.FriendlyName.ToLower().Equals("event log")) { return new DefaultDataCollectionLauncher(); } diff --git a/src/Microsoft.TestPlatform.ObjectModel/DataCollector/Events/DataRequestEventArgs.cs b/src/Microsoft.TestPlatform.ObjectModel/DataCollector/Events/DataRequestEventArgs.cs index ee00d7b47a..2285270b0a 100644 --- a/src/Microsoft.TestPlatform.ObjectModel/DataCollector/Events/DataRequestEventArgs.cs +++ b/src/Microsoft.TestPlatform.ObjectModel/DataCollector/Events/DataRequestEventArgs.cs @@ -44,9 +44,9 @@ internal DataRequestEventArgs(DataCollectionContext context, TimeSpan requestedD : this(context, DefaultTestCaseId, DefaultTestCaseName, DefaultIsChildTestCase, requestedDuration) { Debug.Assert( - !context.HasTestCase, - "This constructor overload is to be used only for a session data request" - ); + !context.HasTestCase, + "This constructor overload is to be used only for a session data request" + ); } /// @@ -141,7 +141,7 @@ public TimeSpan RequestedDuration /// indicate that all the data has been flushed into the result sink. /// #if NET451 - [Serializable] + [Serializable] #endif internal sealed class FlushDataEventArgs : DataCollectionEventArgs { @@ -158,4 +158,4 @@ internal FlushDataEventArgs(DataCollectionContext context) #endregion } -} +} \ No newline at end of file diff --git a/src/Microsoft.TestPlatform.ObjectModel/DataCollector/Events/TestCaseEvents.cs b/src/Microsoft.TestPlatform.ObjectModel/DataCollector/Events/TestCaseEvents.cs index aecc8de48d..ae0392817b 100644 --- a/src/Microsoft.TestPlatform.ObjectModel/DataCollector/Events/TestCaseEvents.cs +++ b/src/Microsoft.TestPlatform.ObjectModel/DataCollector/Events/TestCaseEvents.cs @@ -434,4 +434,4 @@ public TestResult TestResult } #endregion } -} +} \ No newline at end of file diff --git a/src/Microsoft.TestPlatform.ObjectModel/DataCollector/TransferInformation/FileTransferInformation.cs b/src/Microsoft.TestPlatform.ObjectModel/DataCollector/TransferInformation/FileTransferInformation.cs index c5c7ae2c3b..cb7cad2ae1 100644 --- a/src/Microsoft.TestPlatform.ObjectModel/DataCollector/TransferInformation/FileTransferInformation.cs +++ b/src/Microsoft.TestPlatform.ObjectModel/DataCollector/TransferInformation/FileTransferInformation.cs @@ -6,13 +6,16 @@ namespace Microsoft.VisualStudio.TestPlatform.ObjectModel.DataCollection using System; using System.IO; + using Microsoft.VisualStudio.TestPlatform.Utilities.Helpers.Interfaces; + /// /// Represents required and optional information needed for requesting a file transfer. - /// + /// public class FileTransferInformation : BasicTransferInformation { - #region Constructor + private readonly IFileHelper fileHelper; + #region Constructor /// /// Initializes a new instance of the class. /// @@ -26,21 +29,42 @@ public class FileTransferInformation : BasicTransferInformation /// True to automatically have the file removed after sending it. /// public FileTransferInformation(DataCollectionContext context, string path, bool deleteFile) + : this(context, path, deleteFile, new TestPlatform.Utilities.Helpers.FileHelper()) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// + /// The context in which the file is being sent. Cannot be null. + /// + /// + /// The path to the file on the local file system + /// + /// + /// True to automatically have the file removed after sending it. + /// + /// + /// The file Helper. + /// + [CLSCompliant(false)] + public FileTransferInformation(DataCollectionContext context, string path, bool deleteFile, IFileHelper fileHelper) : base(context) { - // EqtAssert.StringNotNullOrEmpty(path, "path"); + this.fileHelper = fileHelper; // Expand environment variables in the path path = Environment.ExpandEnvironmentVariables(path); // Make sure the file exists. - if (!File.Exists(path)) + if (!this.fileHelper.Exists(path)) { throw new FileNotFoundException(string.Format(Resources.Resources.Common_FileNotExist, new object[] { path }), path); } // Make sure the path we have is a full path (not relative). - this.Path = System.IO.Path.GetFullPath(path); + this.Path = this.fileHelper.GetFullPath(path); this.PerformCleanup = deleteFile; } diff --git a/src/Microsoft.TestPlatform.ObjectModel/Friends.cs b/src/Microsoft.TestPlatform.ObjectModel/Friends.cs index 9bcec7aca2..ea3b21f6f1 100644 --- a/src/Microsoft.TestPlatform.ObjectModel/Friends.cs +++ b/src/Microsoft.TestPlatform.ObjectModel/Friends.cs @@ -11,3 +11,4 @@ [assembly: InternalsVisibleTo("Microsoft.TestPlatform.ObjectModel.UnitTests, PublicKey=002400000480000094000000060200000024000052534131000400000100010007d1fa57c4aed9f0a32e84aa0faefd0de9e8fd6aec8f87fb03766c834c99921eb23be79ad9d5dcc1dd9ad236132102900b723cf980957fc4e177108fc607774f29e8320e92ea05ece4e821c0a5efe8f1645c4c0c93c1ab99285d622caa652c1dfad63d745d6f2de5f17e5eaf0fc4963d261c8a12436518206dc093344d5ad293")] [assembly: InternalsVisibleTo("datacollector.UnitTests, PublicKey=002400000480000094000000060200000024000052534131000400000100010007d1fa57c4aed9f0a32e84aa0faefd0de9e8fd6aec8f87fb03766c834c99921eb23be79ad9d5dcc1dd9ad236132102900b723cf980957fc4e177108fc607774f29e8320e92ea05ece4e821c0a5efe8f1645c4c0c93c1ab99285d622caa652c1dfad63d745d6f2de5f17e5eaf0fc4963d261c8a12436518206dc093344d5ad293")] +[assembly: InternalsVisibleTo("Microsoft.TestPlatform.Extensions.EventLogCollector.UnitTests, PublicKey=002400000480000094000000060200000024000052534131000400000100010007d1fa57c4aed9f0a32e84aa0faefd0de9e8fd6aec8f87fb03766c834c99921eb23be79ad9d5dcc1dd9ad236132102900b723cf980957fc4e177108fc607774f29e8320e92ea05ece4e821c0a5efe8f1645c4c0c93c1ab99285d622caa652c1dfad63d745d6f2de5f17e5eaf0fc4963d261c8a12436518206dc093344d5ad293")] diff --git a/src/package/sign/sign.proj b/src/package/sign/sign.proj index faac82a38f..c7ce09bfed 100644 --- a/src/package/sign/sign.proj +++ b/src/package/sign/sign.proj @@ -57,7 +57,6 @@ - @@ -102,6 +101,7 @@ + @@ -137,6 +137,7 @@ + diff --git a/test/DataCollectors/Microsoft.TestPlatform.Extensions.EventLogCollector.UnitTests/CollectorNameValueConfigurationManagerTests.cs b/test/DataCollectors/Microsoft.TestPlatform.Extensions.EventLogCollector.UnitTests/CollectorNameValueConfigurationManagerTests.cs new file mode 100644 index 0000000000..1ef1d485b6 --- /dev/null +++ b/test/DataCollectors/Microsoft.TestPlatform.Extensions.EventLogCollector.UnitTests/CollectorNameValueConfigurationManagerTests.cs @@ -0,0 +1,55 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace Microsoft.TestPlatform.Extensions.EventLogCollector.UnitTests +{ + using System.IO; + using System.Xml; + + using Microsoft.TestPlatform.Extensions.EventLogCollector; + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class CollectorNameValueConfigurationManagerTests + { + private const string ConfigurationString = + @""; + + private const string EmptyConfigurationString = + @""; + + [TestMethod] + public void ConstructorShouldInitializeNameValuePairDictionary() + { + XmlDocument xmlDocument = new XmlDocument(); + using (XmlReader reader = XmlReader.Create(new StringReader(ConfigurationString))) + { + xmlDocument.Load(reader); + } + + var configManager = new CollectorNameValueConfigurationManager(xmlDocument.DocumentElement); + + Assert.AreEqual("value", configManager["key"]); + } + + [TestMethod] + public void ConstructorShouldNotInitializeNameValuePairIfEmptyXmlElementIsPassed() + { + XmlDocument xmlDocument = new XmlDocument(); + using (XmlReader reader = XmlReader.Create(new StringReader(EmptyConfigurationString))) + { + xmlDocument.Load(reader); + } + + var configManager = new CollectorNameValueConfigurationManager(xmlDocument.DocumentElement); + Assert.AreEqual(configManager.NameValuePairs.Count, 0); + } + + [TestMethod] + public void ConstructorShouldNotInitializeNameValuePairNullIsPassed() + { + var configManager = new CollectorNameValueConfigurationManager(null); + Assert.AreEqual(configManager.NameValuePairs.Count, 0); + } + } +} diff --git a/test/DataCollectors/Microsoft.TestPlatform.Extensions.EventLogCollector.UnitTests/EventLogContainerTests.cs b/test/DataCollectors/Microsoft.TestPlatform.Extensions.EventLogCollector.UnitTests/EventLogContainerTests.cs new file mode 100644 index 0000000000..6ad8df9b30 --- /dev/null +++ b/test/DataCollectors/Microsoft.TestPlatform.Extensions.EventLogCollector.UnitTests/EventLogContainerTests.cs @@ -0,0 +1,134 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace Microsoft.TestPlatform.Extensions.EventLogCollector.UnitTests +{ + using System; + using System.Collections.Generic; + using System.Diagnostics; + + using Microsoft.VisualStudio.TestPlatform.ObjectModel.DataCollection; + using Microsoft.VisualStudio.TestTools.UnitTesting; + + using Moq; + + using Resource = Microsoft.TestPlatform.Extensions.EventLogCollector.Resources.Resources; + using System.Globalization; + + [TestClass] + public class EventLogContainerTests + { + private HashSet eventSources; + + private HashSet entryTypes; + + private Mock logger; + + private DataCollectionContext dataCollectionContext; + + private EventLog eventLog; + + private EventLogContainer eventLogContainer; + + private EntryWrittenEventArgs entryWrittenEventArgs; + + + private string eventLogName = "Application"; + + + public EventLogContainerTests() + { + this.eventSources = new HashSet(); + this.eventSources.Add("Application"); + this.entryTypes = new HashSet(); + this.entryTypes.Add(EventLogEntryType.Error); + + this.logger = new Mock(); + this.eventLog = new EventLog("Application"); + this.entryWrittenEventArgs = new EntryWrittenEventArgs(this.eventLog.Entries[this.eventLog.Entries.Count - 1]); + + this.dataCollectionContext = new DataCollectionContext(new SessionId(Guid.NewGuid())); + + this.eventLogContainer = new EventLogContainer( + this.eventLogName, + this.eventSources, + this.entryTypes, + int.MaxValue, + this.logger.Object, + this.dataCollectionContext); + } + + [TestMethod] + public void OnEventLogEntryWrittenShouldAddLogs() + { + EventLog.WriteEntry("Application", "Application", EventLogEntryType.Error, 234); + this.eventLogContainer.OnEventLogEntryWritten(this.eventLog, this.entryWrittenEventArgs); + var newCount = this.eventLogContainer.EventLogEntries.Count; + + Assert.IsTrue(newCount > 0); + } + + [TestMethod] + public void OnEventLogEntryWrittenShouldNotAddLogsIfNoNewEntryIsPresent() + { + this.eventLogContainer.OnEventLogEntryWritten(this.eventLog, this.entryWrittenEventArgs); + var newCount = this.eventLogContainer.EventLogEntries.Count; + + Assert.AreEqual(0, newCount); + } + + [TestMethod] + public void OnEventLogEntryWrittenShoulFilterLogsBasedOnEventTypeAndEventSource() + { + this.entryTypes.Add(EventLogEntryType.Warning); + this.eventSources.Add("Application"); + + EventLog.WriteEntry("Application", "Application", EventLogEntryType.Warning, 234); + this.eventLogContainer.OnEventLogEntryWritten(this.eventLog, this.entryWrittenEventArgs); + var newCount = this.eventLogContainer.EventLogEntries.Count; + + Assert.AreEqual(1, newCount); + } + + [TestMethod] + public void OnEventLogEntryWrittenShoulNotAddLogsIfEventSourceIsDifferent() + { + this.eventSources.Clear(); + this.eventSources.Add("Application1"); + this.eventLogContainer = new EventLogContainer( + this.eventLogName, + this.eventSources, + this.entryTypes, + int.MaxValue, + this.logger.Object, + this.dataCollectionContext); + EventLog.WriteEntry("Application", "Application", EventLogEntryType.Warning, 234); + this.eventLogContainer.OnEventLogEntryWritten(this.eventLog, this.entryWrittenEventArgs); + var newCount = this.eventLogContainer.EventLogEntries.Count; + + Assert.AreEqual(0, newCount); + } + + [TestMethod] + public void OnEventLogEntryWrittenShoulNotAddLogsIfEventTypeIsDifferent() + { + this.entryTypes.Clear(); + this.entryTypes.Add(EventLogEntryType.FailureAudit); + + this.eventSources.Add("Application1"); + this.eventLogContainer = new EventLogContainer( + this.eventLogName, + this.eventSources, + this.entryTypes, + int.MaxValue, + this.logger.Object, + this.dataCollectionContext); + + EventLog.WriteEntry("Application", "Application", EventLogEntryType.Warning, 234); + this.eventLogContainer.OnEventLogEntryWritten(this.eventLog, this.entryWrittenEventArgs); + var newCount = this.eventLogContainer.EventLogEntries.Count; + + Assert.AreEqual(0, newCount); + } + } +} diff --git a/test/DataCollectors/Microsoft.TestPlatform.Extensions.EventLogCollector.UnitTests/EventLogDataCollectorTests.cs b/test/DataCollectors/Microsoft.TestPlatform.Extensions.EventLogCollector.UnitTests/EventLogDataCollectorTests.cs new file mode 100644 index 0000000000..aeb47af173 --- /dev/null +++ b/test/DataCollectors/Microsoft.TestPlatform.Extensions.EventLogCollector.UnitTests/EventLogDataCollectorTests.cs @@ -0,0 +1,481 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace Microsoft.TestPlatform.Extensions.EventLogCollector.UnitTests +{ + using System; + using System.Collections.Generic; + using System.ComponentModel; + using System.Diagnostics; + using System.Linq; + using System.Xml; + + using Microsoft.VisualStudio.TestPlatform.ObjectModel; + using Microsoft.VisualStudio.TestPlatform.ObjectModel.DataCollection; + using Microsoft.VisualStudio.TestPlatform.Utilities.Helpers.Interfaces; + using Microsoft.VisualStudio.TestTools.UnitTesting; + + using Moq; + + [TestClass] + public class EventLogDataCollectorTests + { + private const string ConfigurationString = + @""; + + private Mock mockDataCollectionEvents; + + private TestableDataCollectionSink mockDataCollectionSink; + + private Mock mockDataCollectionLogger; + + private DataCollectionEnvironmentContext dataCollectionEnvironmentContext; + + private EventLogDataCollector eventLogDataCollector; + + private Mock mockFileHelper; + + public EventLogDataCollectorTests() + { + this.mockDataCollectionEvents = new Mock(); + this.mockDataCollectionSink = new TestableDataCollectionSink(); + this.mockFileHelper = new Mock(); + TestCase tc = new TestCase(); + DataCollectionContext dataCollectionContext = + new DataCollectionContext(new SessionId(Guid.NewGuid())); + this.dataCollectionEnvironmentContext = new DataCollectionEnvironmentContext(dataCollectionContext); + this.mockDataCollectionLogger = new Mock(); + this.eventLogDataCollector = new EventLogDataCollector(this.mockFileHelper.Object); + } + + [TestMethod] + public void InitializeShouldThrowExceptionIfEventsIsNull() + { + Assert.ThrowsException( + () => + { + this.eventLogDataCollector.Initialize( + null, + null, + this.mockDataCollectionSink, + this.mockDataCollectionLogger.Object, + this.dataCollectionEnvironmentContext); + }); + } + + [TestMethod] + public void InitializeShouldThrowExceptionIfCollectionSinkIsNull() + { + Assert.ThrowsException( + () => + { + this.eventLogDataCollector.Initialize( + null, + this.mockDataCollectionEvents.Object, + null, + this.mockDataCollectionLogger.Object, + this.dataCollectionEnvironmentContext); + }); + } + + [TestMethod] + public void InitializeShouldThrowExceptionIfLoggerIsNull() + { + Assert.ThrowsException( + () => + { + this.eventLogDataCollector.Initialize( + null, + this.mockDataCollectionEvents.Object, + this.mockDataCollectionSink, + null, + this.dataCollectionEnvironmentContext); + }); + } + + [TestMethod] + public void InitializeShouldInitializeDefaultEventLogNames() + { + List eventLogNames = new List(); + eventLogNames.Add("System"); + eventLogNames.Add("Security"); + eventLogNames.Add("Application"); + + this.eventLogDataCollector.Initialize(null, this.mockDataCollectionEvents.Object, this.mockDataCollectionSink, this.mockDataCollectionLogger.Object, this.dataCollectionEnvironmentContext); + + CollectionAssert.AreEqual(eventLogNames, this.eventLogDataCollector.EventLogNames.ToList()); + } + + [TestMethod] + public void InitializeShouldInitializeCustomEventLogNamesIfSpecifiedInConfiguration() + { + string configurationString = + @""; + + List eventLogNames = new List(); + eventLogNames.Add("MyEventName"); + eventLogNames.Add("MyEventName2"); + + XmlDocument expectedXmlDoc = new XmlDocument(); + expectedXmlDoc.LoadXml(configurationString); + + this.eventLogDataCollector.Initialize(expectedXmlDoc.DocumentElement, this.mockDataCollectionEvents.Object, this.mockDataCollectionSink, this.mockDataCollectionLogger.Object, this.dataCollectionEnvironmentContext); + + CollectionAssert.AreEqual(eventLogNames, this.eventLogDataCollector.EventLogNames.ToList()); + } + + [TestMethod] + public void InitializeShouldInitializeDefaultLogEntryTypes() + { + List entryTypes = new List(); + entryTypes.Add(EventLogEntryType.Error); + entryTypes.Add(EventLogEntryType.Warning); + entryTypes.Add(EventLogEntryType.FailureAudit); + + this.eventLogDataCollector.Initialize(null, this.mockDataCollectionEvents.Object, this.mockDataCollectionSink, this.mockDataCollectionLogger.Object, this.dataCollectionEnvironmentContext); + + CollectionAssert.AreEqual(entryTypes, this.eventLogDataCollector.EntryTypes.ToList()); + } + + [TestMethod] + public void InitializeShouldInitializeEntryTypesIfSpecifiedInConfiguration() + { + string configurationString = + @""; + + List entryTypes = new List(); + entryTypes.Add(EventLogEntryType.Error); + + XmlDocument expectedXmlDoc = new XmlDocument(); + expectedXmlDoc.LoadXml(configurationString); + this.eventLogDataCollector.Initialize(expectedXmlDoc.DocumentElement, this.mockDataCollectionEvents.Object, this.mockDataCollectionSink, this.mockDataCollectionLogger.Object, this.dataCollectionEnvironmentContext); + + CollectionAssert.AreEqual(entryTypes, this.eventLogDataCollector.EntryTypes.ToList()); + } + + [TestMethod] + public void InitializeShouldInitializeEventSourcesIfSpecifiedInConfiguration() + { + string configurationString = + @""; + + List eventSources = new List(); + eventSources.Add("MyEventSource"); + + XmlDocument expectedXmlDoc = new XmlDocument(); + expectedXmlDoc.LoadXml(configurationString); + this.eventLogDataCollector.Initialize(expectedXmlDoc.DocumentElement, this.mockDataCollectionEvents.Object, this.mockDataCollectionSink, this.mockDataCollectionLogger.Object, this.dataCollectionEnvironmentContext); + + CollectionAssert.AreEqual(eventSources, this.eventLogDataCollector.EventSources.ToList()); + } + + [TestMethod] + public void InitializeShouldNotInitializeEventSourcesByDefault() + { + this.eventLogDataCollector.Initialize(null, this.mockDataCollectionEvents.Object, this.mockDataCollectionSink, this.mockDataCollectionLogger.Object, this.dataCollectionEnvironmentContext); + + Assert.IsNull(this.eventLogDataCollector.EventSources); + } + + [TestMethod] + public void InitializeShouldInitializeMaxEntriesIfSpecifiedInConfiguration() + { + string configurationString = + @""; + + XmlDocument expectedXmlDoc = new XmlDocument(); + expectedXmlDoc.LoadXml(configurationString); + this.eventLogDataCollector.Initialize(expectedXmlDoc.DocumentElement, this.mockDataCollectionEvents.Object, this.mockDataCollectionSink, this.mockDataCollectionLogger.Object, this.dataCollectionEnvironmentContext); + + Assert.AreEqual(20, this.eventLogDataCollector.MaxEntries); + } + + [TestMethod] + public void InitializeShouldSetDefaultMaxEntries() + { + this.eventLogDataCollector.Initialize(null, this.mockDataCollectionEvents.Object, this.mockDataCollectionSink, this.mockDataCollectionLogger.Object, this.dataCollectionEnvironmentContext); + + Assert.AreEqual(50000, this.eventLogDataCollector.MaxEntries); + } + + [TestMethod] + public void InitializeShouldSubscribeToDataCollectionEvents() + { + var testableDataCollectionEvents = new TestableDataCollectionEvents(); + this.eventLogDataCollector.Initialize(null, testableDataCollectionEvents, this.mockDataCollectionSink, this.mockDataCollectionLogger.Object, this.dataCollectionEnvironmentContext); + Assert.AreEqual(1, testableDataCollectionEvents.GetTestCaseStartInvocationList().Length); + Assert.AreEqual(1, testableDataCollectionEvents.GetTestCaseEndInvocationList().Length); + Assert.AreEqual(1, testableDataCollectionEvents.GetTestSessionEndInvocationList().Length); + Assert.AreEqual(1, testableDataCollectionEvents.GetTestSessionStartInvocationList().Length); + } + + [TestMethod] + public void TestSessionStartEventShouldCreateEventLogContainer() + { + var eventLogDataCollector = new EventLogDataCollector(); + Assert.AreEqual(eventLogDataCollector.ContextMap.Count, 0); + eventLogDataCollector.Initialize(null, this.mockDataCollectionEvents.Object, this.mockDataCollectionSink, this.mockDataCollectionLogger.Object, this.dataCollectionEnvironmentContext); + this.mockDataCollectionEvents.Raise(x => x.SessionStart += null, new SessionStartEventArgs()); + Assert.AreEqual(eventLogDataCollector.ContextMap.Count, 1); + } + + [TestMethod] + public void TestCaseStartEventShouldCreateEventLogContainer() + { + var eventLogDataCollector = new EventLogDataCollector(); + Assert.AreEqual(eventLogDataCollector.ContextMap.Count, 0); + + eventLogDataCollector.Initialize(null, this.mockDataCollectionEvents.Object, this.mockDataCollectionSink, this.mockDataCollectionLogger.Object, this.dataCollectionEnvironmentContext); + this.mockDataCollectionEvents.Raise(x => x.TestCaseStart += null, new TestCaseStartEventArgs(new DataCollectionContext(new SessionId(Guid.NewGuid()), new TestExecId(Guid.NewGuid())), new TestCase())); + Assert.AreEqual(eventLogDataCollector.ContextMap.Count, 1); + } + + [TestMethod] + + public void TestCaseEndEventShouldWriteEventLogEntriesAndSendFile() + { + var eventLogDataCollector = new EventLogDataCollector(); + eventLogDataCollector.Initialize(null, this.mockDataCollectionEvents.Object, this.mockDataCollectionSink, this.mockDataCollectionLogger.Object, this.dataCollectionEnvironmentContext); + var tc = new TestCase(); + var context = new DataCollectionContext(new SessionId(Guid.NewGuid()), new TestExecId(Guid.NewGuid())); + this.mockDataCollectionEvents.Raise(x => x.TestCaseStart += null, new TestCaseStartEventArgs(context, tc)); + this.mockDataCollectionEvents.Raise(x => x.TestCaseEnd += null, new TestCaseEndEventArgs(context, tc, TestOutcome.Passed)); + Assert.IsTrue(this.mockDataCollectionSink.IsSendFileAsyncInvoked); + } + + public void TestCaseEndEventShouldInvokeSendFileAsync() + { + var eventLogDataCollector = new EventLogDataCollector(); + eventLogDataCollector.Initialize(null, this.mockDataCollectionEvents.Object, this.mockDataCollectionSink, this.mockDataCollectionLogger.Object, this.dataCollectionEnvironmentContext); + var tc = new TestCase(); + var context = new DataCollectionContext(new SessionId(Guid.NewGuid()), new TestExecId(Guid.NewGuid())); + this.mockDataCollectionEvents.Raise(x => x.TestCaseStart += null, new TestCaseStartEventArgs(context, tc)); + this.mockDataCollectionEvents.Raise(x => x.TestCaseEnd += null, new TestCaseEndEventArgs(context, tc, TestOutcome.Passed)); + Assert.IsTrue(this.mockDataCollectionSink.IsSendFileAsyncInvoked); + } + + [TestMethod] + public void TestCaseEndEventShouldThrowIfTestCaseStartIsNotInvoked() + { + var eventLogDataCollector = new EventLogDataCollector(); + eventLogDataCollector.Initialize(null, this.mockDataCollectionEvents.Object, this.mockDataCollectionSink, this.mockDataCollectionLogger.Object, this.dataCollectionEnvironmentContext); + var tc = new TestCase(); + var context = new DataCollectionContext(new SessionId(Guid.NewGuid()), new TestExecId(Guid.NewGuid())); + + Assert.ThrowsException(() => + { + this.mockDataCollectionEvents.Raise(x => x.TestCaseEnd += null, new TestCaseEndEventArgs(context, tc, TestOutcome.Passed)); + }); + } + + public void SessionEndEventShouldThrowIfSessionStartEventtIsNotInvoked() + { + var eventLogDataCollector = new EventLogDataCollector(); + eventLogDataCollector.Initialize(null, this.mockDataCollectionEvents.Object, this.mockDataCollectionSink, this.mockDataCollectionLogger.Object, this.dataCollectionEnvironmentContext); + var tc = new TestCase(); + + Assert.ThrowsException(() => + { + this.mockDataCollectionEvents.Raise(x => x.SessionEnd += null, new SessionEndEventArgs(this.dataCollectionEnvironmentContext.SessionDataCollectionContext)); + }); + } + + [TestMethod] + public void TestSessionEndEventShouldWriteEventLogEntriesAndSendFile() + { + var eventLogDataCollector = new EventLogDataCollector(); + eventLogDataCollector.Initialize(null, this.mockDataCollectionEvents.Object, this.mockDataCollectionSink, this.mockDataCollectionLogger.Object, this.dataCollectionEnvironmentContext); + var testcase = new TestCase() { Id = Guid.NewGuid() }; + this.mockDataCollectionEvents.Raise(x => x.SessionStart += null, new SessionStartEventArgs(this.dataCollectionEnvironmentContext.SessionDataCollectionContext)); + this.mockDataCollectionEvents.Raise(x => x.SessionEnd += null, new SessionEndEventArgs(this.dataCollectionEnvironmentContext.SessionDataCollectionContext)); + Assert.IsTrue(this.mockDataCollectionSink.IsSendFileAsyncInvoked); + } + + [TestMethod] + public void WriteEventLogsShouldCreateXmlFile() + { + string configurationString = + @""; + + XmlDocument expectedXmlDoc = new XmlDocument(); + expectedXmlDoc.LoadXml(configurationString); + + this.mockFileHelper.SetupSequence(x => x.Exists(It.IsAny())).Returns(false).Returns(true); + this.eventLogDataCollector.Initialize(expectedXmlDoc.DocumentElement, this.mockDataCollectionEvents.Object, this.mockDataCollectionSink, this.mockDataCollectionLogger.Object, this.dataCollectionEnvironmentContext); + this.eventLogDataCollector.WriteEventLogs( + new List(), + 20, + this.dataCollectionEnvironmentContext.SessionDataCollectionContext, + TimeSpan.MaxValue, + DateTime.Now); + + this.mockFileHelper.Verify(x => x.WriteAllTextToFile(It.IsAny(), It.IsAny()), Times.Once); + } + + [TestMethod] + public void WriteEventLogsShouldThrowExceptionIfThrownByFileHelper() + { + string configurationString = + @""; + + XmlDocument expectedXmlDoc = new XmlDocument(); + expectedXmlDoc.LoadXml(configurationString); + this.mockFileHelper.Setup(x => x.Exists(It.IsAny())).Throws(); + Assert.ThrowsException( + () => + { + this.eventLogDataCollector.WriteEventLogs( + new List(), + 20, + this.dataCollectionEnvironmentContext.SessionDataCollectionContext, + TimeSpan.MaxValue, + DateTime.Now); + }); + } + + [TestMethod] + public void WriteEventLogsShouldFilterTestsBasedOnTimeAndMaxValue() + { + string configurationString = + @""; + + XmlDocument expectedXmlDoc = new XmlDocument(); + expectedXmlDoc.LoadXml(configurationString); + + this.mockFileHelper.SetupSequence(x => x.Exists(It.IsAny())).Returns(false).Returns(true); + this.eventLogDataCollector.Initialize(expectedXmlDoc.DocumentElement, this.mockDataCollectionEvents.Object, this.mockDataCollectionSink, this.mockDataCollectionLogger.Object, this.dataCollectionEnvironmentContext); + + var entries = new List(); + + var eventLog = new EventLog("Application"); + int endIndex = eventLog.Entries[eventLog.Entries.Count - 1].Index; + int firstIndexInLog = eventLog.Entries[0].Index; + for (int i = endIndex; i > endIndex - 10; i--) + { + entries.Add(eventLog.Entries[i - firstIndexInLog]); + } + + var filteredEntries = entries.Where(entry => entry.TimeGenerated > DateTime.MinValue && entry.TimeGenerated < DateTime.MaxValue) + .OrderBy(x => x.TimeGenerated).ToList().Take(5).ToList(); + + this.eventLogDataCollector.WriteEventLogs( + entries, + 5, + this.dataCollectionEnvironmentContext.SessionDataCollectionContext, + TimeSpan.MaxValue, + DateTime.Now); + + this.mockFileHelper.Verify( + x => x.WriteAllTextToFile( + It.IsAny(), + It.Is( + str => str.Contains(filteredEntries[0].InstanceId.ToString()) + && str.Contains(filteredEntries[1].InstanceId.ToString()) + && str.Contains(filteredEntries[2].InstanceId.ToString()) + && str.Contains(filteredEntries[3].InstanceId.ToString()) + && str.Contains(filteredEntries[4].InstanceId.ToString())))); + } + + [TestMethod] + public void WriteEventLogsShouldFilterTestIfMaxValueExceedsEntries() + { + string configurationString = + @""; + + XmlDocument expectedXmlDoc = new XmlDocument(); + expectedXmlDoc.LoadXml(configurationString); + + this.mockFileHelper.SetupSequence(x => x.Exists(It.IsAny())).Returns(false).Returns(true); + this.eventLogDataCollector.Initialize(expectedXmlDoc.DocumentElement, this.mockDataCollectionEvents.Object, this.mockDataCollectionSink, this.mockDataCollectionLogger.Object, this.dataCollectionEnvironmentContext); + + var entries = new List(); + + var eventLog = new EventLog("Application"); + int endIndex = eventLog.Entries[eventLog.Entries.Count - 1].Index; + int firstIndexInLog = eventLog.Entries[0].Index; + for (int i = endIndex; i > endIndex - 5; i--) + { + entries.Add(eventLog.Entries[i - firstIndexInLog]); + } + + var filteredEntries = entries.Where(entry => entry.TimeGenerated > DateTime.MinValue && entry.TimeGenerated < DateTime.MaxValue) + .OrderBy(x => x.TimeGenerated).ToList().Take(10).ToList(); + + this.eventLogDataCollector.WriteEventLogs( + entries, + 5, + this.dataCollectionEnvironmentContext.SessionDataCollectionContext, + TimeSpan.MaxValue, + DateTime.Now); + + this.mockFileHelper.Verify( + x => x.WriteAllTextToFile( + It.IsAny(), + It.Is( + str => str.Contains(filteredEntries[0].InstanceId.ToString()) + && str.Contains(filteredEntries[1].InstanceId.ToString()) + && str.Contains(filteredEntries[2].InstanceId.ToString()) + && str.Contains(filteredEntries[3].InstanceId.ToString()) + && str.Contains(filteredEntries[4].InstanceId.ToString())))); + } + } + + /// + /// The testable data collection events. + /// + public class TestableDataCollectionEvents : DataCollectionEvents + { + public override event EventHandler SessionStart; + + public override event EventHandler SessionEnd; + + public override event EventHandler TestCaseStart; + + public override event EventHandler TestCaseEnd; + + public Delegate[] GetTestCaseStartInvocationList() + { + return this.TestCaseStart.GetInvocationList(); + } + + public Delegate[] GetTestCaseEndInvocationList() + { + return this.TestCaseEnd.GetInvocationList(); + } + + public Delegate[] GetTestSessionStartInvocationList() + { + return this.SessionStart.GetInvocationList(); + } + + public Delegate[] GetTestSessionEndInvocationList() + { + return this.SessionEnd.GetInvocationList(); + } + } + + /// + /// The testable data collection sink. + /// + public class TestableDataCollectionSink : DataCollectionSink + { + /// + /// The send file completed. + /// + public override event AsyncCompletedEventHandler SendFileCompleted; + + /// + /// Gets or sets a value indicating whether is send file async invoked. + /// + public bool IsSendFileAsyncInvoked { get; set; } + + public override void SendFileAsync(FileTransferInformation fileTransferInformation) + { + this.IsSendFileAsyncInvoked = true; + if (this.SendFileCompleted == null) + { + return; + } + } + } +} diff --git a/test/DataCollectors/Microsoft.TestPlatform.Extensions.EventLogCollector.UnitTests/EventLogSessionContextTests.cs b/test/DataCollectors/Microsoft.TestPlatform.Extensions.EventLogCollector.UnitTests/EventLogSessionContextTests.cs new file mode 100644 index 0000000000..b327e39ab4 --- /dev/null +++ b/test/DataCollectors/Microsoft.TestPlatform.Extensions.EventLogCollector.UnitTests/EventLogSessionContextTests.cs @@ -0,0 +1,68 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace Microsoft.TestPlatform.Extensions.EventLogCollector.UnitTests +{ + using System.Collections.Generic; + using System.Diagnostics; + + using Microsoft.VisualStudio.TestTools.UnitTesting; + + using Moq; + + [TestClass] + public class EventLogSessionContextTests + { + private Dictionary eventLogContainersMap; + + private DummyEventLogContainer mockEventLogContainer; + + private EventLogSessionContext eventLogSessionContext; + + public EventLogSessionContextTests() + { + this.mockEventLogContainer = new DummyEventLogContainer(); + this.eventLogContainersMap = new Dictionary(); + this.eventLogContainersMap.Add("LogName", this.mockEventLogContainer); + } + + [TestMethod] + public void CreateEventLogContainerStartIndexMapShouldCreateStartIndexMap() + { + this.eventLogSessionContext = new EventLogSessionContext(this.eventLogContainersMap); + Assert.IsTrue(this.eventLogSessionContext.EventLogContainerStartIndexMap["LogName"] == 1); + } + + [TestMethod] + public void CreateEventLogContainerEndIndexMapShouldEndIndexCreateMap() + { + this.eventLogSessionContext = new EventLogSessionContext(this.eventLogContainersMap); + this.eventLogSessionContext.CreateEventLogContainerEndIndexMap(); + Assert.IsTrue(this.eventLogSessionContext.EventLogContainerEndIndexMap["LogName"] == 1); + } + } + + public class DummyEventLogContainer : IEventLogContainer + { + public DummyEventLogContainer() + { + this.EventLogEntries = new List(10); + EventLog eventLog = new EventLog("Application"); + int currentIndex = eventLog.Entries[eventLog.Entries.Count - 1].Index - eventLog.Entries[0].Index; + this.EventLogEntries.Add(eventLog.Entries[currentIndex]); + this.EventLogEntries.Add(eventLog.Entries[currentIndex - 1]); + } + + public void Dispose() + { + } + + public EventLog EventLog { get; } + + public List EventLogEntries { get; set; } + + public void OnEventLogEntryWritten(object source, EntryWrittenEventArgs e) + { + } + } +} diff --git a/test/DataCollectors/Microsoft.TestPlatform.Extensions.EventLogCollector.UnitTests/EventLogXmlWriterTests.cs b/test/DataCollectors/Microsoft.TestPlatform.Extensions.EventLogCollector.UnitTests/EventLogXmlWriterTests.cs new file mode 100644 index 0000000000..bcc09acc72 --- /dev/null +++ b/test/DataCollectors/Microsoft.TestPlatform.Extensions.EventLogCollector.UnitTests/EventLogXmlWriterTests.cs @@ -0,0 +1,62 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace Microsoft.TestPlatform.Extensions.EventLogCollector.UnitTests +{ + using System; + using System.Collections.Generic; + using System.Diagnostics; + using System.IO; + using System.Xml; + + using Microsoft.VisualStudio.TestPlatform.Utilities.Helpers.Interfaces; + using Microsoft.VisualStudio.TestTools.UnitTesting; + + using Moq; + + [TestClass] + public class EventLogXmlWriterTests + { + private const string FileName = "Event Log.xml"; + + private const string DefaultEventLog = @""; + + private EventLog eventLog; + + private EventLogEntry eventLogEntry; + + private List eventLogEntries; + + private Mock mockFileHelper; + + public EventLogXmlWriterTests() + { + this.eventLog = new EventLog("Application"); + var count = this.eventLog.Entries.Count; + this.eventLogEntry = this.eventLog.Entries[count - 1]; + this.eventLogEntries = new List(); + this.mockFileHelper = new Mock(); + } + + [TestMethod] + public void WriteEventLogEntriesToXmlFileShouldWriteToXMLFile() + { + EventLogXmlWriter.WriteEventLogEntriesToXmlFile( + FileName, + this.eventLogEntries, + this.mockFileHelper.Object); + + this.mockFileHelper.Verify(x => x.WriteAllTextToFile(FileName, It.IsAny()), Times.Once); + } + + [TestMethod] + public void WriteEventLogEntriesToXmlFileShouldWriteLogEntryIfPresent() + { + this.eventLogEntries.Add(this.eventLogEntry); + + EventLogXmlWriter.WriteEventLogEntriesToXmlFile(FileName, this.eventLogEntries, this.mockFileHelper.Object); + + this.mockFileHelper.Verify(x => x.WriteAllTextToFile(FileName, It.Is(str => str.Contains(this.eventLogEntry.Message)))); + } + } +} diff --git a/test/DataCollectors/Microsoft.TestPlatform.Extensions.EventLogCollector.UnitTests/Microsoft.TestPlatform.Extensions.EventLogCollector.UnitTests.csproj b/test/DataCollectors/Microsoft.TestPlatform.Extensions.EventLogCollector.UnitTests/Microsoft.TestPlatform.Extensions.EventLogCollector.UnitTests.csproj new file mode 100644 index 0000000000..d0edab5b4c --- /dev/null +++ b/test/DataCollectors/Microsoft.TestPlatform.Extensions.EventLogCollector.UnitTests/Microsoft.TestPlatform.Extensions.EventLogCollector.UnitTests.csproj @@ -0,0 +1,31 @@ + + + + ..\..\..\ + true + + + + Microsoft.TestPlatform.Extensions.EventLogCollector.UnitTests + + + net451 + + + net451 + Microsoft.TestPlatform.Extensions.EventLogCollector.UnitTests + true + + + + + true + + + + + + + + + diff --git a/test/Microsoft.TestPlatform.AcceptanceTests/EventLogCollectorTests.cs b/test/Microsoft.TestPlatform.AcceptanceTests/EventLogCollectorTests.cs new file mode 100644 index 0000000000..7d392c09a9 --- /dev/null +++ b/test/Microsoft.TestPlatform.AcceptanceTests/EventLogCollectorTests.cs @@ -0,0 +1,89 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace Microsoft.TestPlatform.AcceptanceTests +{ + using System; + using System.IO; + using System.Linq; + using Microsoft.VisualStudio.TestTools.UnitTesting; + + + [TestClass] + public class EventLogCollectorTests : AcceptanceTestBase + { + private readonly string resultsDir; + + public EventLogCollectorTests() + { + this.resultsDir = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); + } + + [CustomDataTestMethod] + [NETFullTargetFramework] + public void EventLogDataCollectorShoudCreateLogFile(string runnerFramework, string targetFramework, string targetRuntime) + { + SetTestEnvironment(this.testEnvironment, runnerFramework, targetFramework, targetRuntime); + var assemblyPaths = this.testEnvironment.GetTestAsset("EventLogUnitTestProject.dll"); + + string runSettings = this.GetRunsettingsFilePath(); + var arguments = PrepareArguments(assemblyPaths, this.GetTestAdapterPath(), runSettings, this.FrameworkArgValue); + arguments = string.Concat(arguments, $" /ResultsDirectory:{resultsDir}"); + + this.InvokeVsTest(arguments); + + this.ValidateSummaryStatus(3, 0, 0); + this.VaildateDataCollectorOutput(); + } + + private string GetRunsettingsFilePath() + { + var runsettingsPath = Path.Combine( + Path.GetTempPath(), + "test_" + Guid.NewGuid() + ".runsettings"); + + string runSettingsXml = @" + + + 0 + x64 + Framework45 + + + + + + + + + "; + + File.WriteAllText(runsettingsPath, runSettingsXml); + return runsettingsPath; + } + + private void VaildateDataCollectorOutput() + { + // Verify attachments + var di = new DirectoryInfo(this.resultsDir); + var resultFiles = di.EnumerateFiles("Event Log.xml", SearchOption.AllDirectories) + .OrderBy(d => d.CreationTime) + .Select(d => d.FullName) + .ToList(); + + Assert.AreEqual(4, resultFiles.Count); + this.StdOutputContains("Event Log.xml"); + + var fileContent1 = File.ReadAllText(resultFiles[0]); + var fileContent2 = File.ReadAllText(resultFiles[1]); + var fileContent3 = File.ReadAllText(resultFiles[2]); + var fileContent4 = File.ReadAllText(resultFiles[3]); + + Assert.IsTrue(fileContent1.Contains("10") && fileContent1.Contains("11") && fileContent1.Contains("12")); + Assert.IsTrue(fileContent2.Contains("20") && fileContent2.Contains("21") && fileContent2.Contains("22") && fileContent2.Contains("23")); + Assert.IsTrue(fileContent3.Contains("30") && fileContent3.Contains("31") && fileContent3.Contains("32")); + + Assert.IsTrue(fileContent4.Contains("10") && fileContent4.Contains("11") && fileContent4.Contains("12") && fileContent4.Contains("20") && fileContent4.Contains("21") && fileContent4.Contains("22") && fileContent4.Contains("23") && fileContent3.Contains("30") && fileContent4.Contains("31") && fileContent4.Contains("32")); + } + } +} diff --git a/test/TestAssets/EventLogUnitTestProject/EventLogUnitTestProject.csproj b/test/TestAssets/EventLogUnitTestProject/EventLogUnitTestProject.csproj new file mode 100644 index 0000000000..ce775341cb --- /dev/null +++ b/test/TestAssets/EventLogUnitTestProject/EventLogUnitTestProject.csproj @@ -0,0 +1,22 @@ + + + + + ..\..\..\ + true + + + + + + + EventLogUnitTestProject + net451 + Exe + + + + + + + diff --git a/test/TestAssets/EventLogUnitTestProject/UnitTest1.cs b/test/TestAssets/EventLogUnitTestProject/UnitTest1.cs new file mode 100644 index 0000000000..2a37e30156 --- /dev/null +++ b/test/TestAssets/EventLogUnitTestProject/UnitTest1.cs @@ -0,0 +1,40 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace EventLogUnitTestProject +{ + using System.Diagnostics; + using System.Threading; + + using Microsoft.VisualStudio.TestTools.UnitTesting; + + + [TestClass] + public class UnitTest1 + { + [TestMethod] + public void TestMethod1() + { + EventLog.WriteEntry("Application", "Application", EventLogEntryType.Error, 10); + EventLog.WriteEntry("Application", "Application", EventLogEntryType.Error, 11); + EventLog.WriteEntry("Application", "Application", EventLogEntryType.Error, 12); + } + + [TestMethod] + public void TestMethod2() + { + EventLog.WriteEntry("Application", "Application", EventLogEntryType.Error, 20); + EventLog.WriteEntry("Application", "Application", EventLogEntryType.Error, 21); + EventLog.WriteEntry("Application", "Application", EventLogEntryType.Error, 22); + EventLog.WriteEntry("Application", "Application", EventLogEntryType.Error, 23); + } + + [TestMethod] + public void TestMethod3() + { + EventLog.WriteEntry("Application", "Application", EventLogEntryType.Error, 30); + EventLog.WriteEntry("Application", "Application", EventLogEntryType.Error, 31); + EventLog.WriteEntry("Application", "Application", EventLogEntryType.Error, 32); + } + } +}