From 0ddc4c84d6f8c1edfaf592a5d2cf3c84b86d6231 Mon Sep 17 00:00:00 2001 From: Christian Soltenborn Date: Tue, 12 Mar 2019 07:57:56 +0100 Subject: [PATCH 1/9] added readme section, removed uncommented code --- GoogleTestAdapter/VsPackage.GTA/Resources/ReleaseNotes/0.15.0.md | 1 + 1 file changed, 1 insertion(+) diff --git a/GoogleTestAdapter/VsPackage.GTA/Resources/ReleaseNotes/0.15.0.md b/GoogleTestAdapter/VsPackage.GTA/Resources/ReleaseNotes/0.15.0.md index 55460be96..d5588a0c9 100644 --- a/GoogleTestAdapter/VsPackage.GTA/Resources/ReleaseNotes/0.15.0.md +++ b/GoogleTestAdapter/VsPackage.GTA/Resources/ReleaseNotes/0.15.0.md @@ -1,4 +1,5 @@ * enhancement: setting the new option [*Exit code test name*](https://github.com/csoltenborn/GoogleTestAdapter#evaluating_exit_code) results in an additional test per test executable that passes if the executable's exit code is 0, and fails otherwise. Additionally, GTA parses the test executable's output for certain [tokens](https://github.com/csoltenborn/GoogleTestAdapter#evaluating_exit_code_tokens) which allow to influence the test's outcome and message. One interesting use case for this is memory leak detection; a complete, reusable [example](https://github.com/csoltenborn/GoogleTestAdapter#evaluating_exit_code_leak_example) is provided as part of GTA's [SampleTests solution](https://github.com/csoltenborn/GoogleTestAdapter/tree/master/SampleTests) ([#266](https://github.com/csoltenborn/GoogleTestAdapter/issues/266), thanks to [alfredskpoon](https://github.com/alfredskpoon) for report, example code, and testing) +* usability: reorganized options ([#271](https://github.com/csoltenborn/GoogleTestAdapter/issues/271)) * enhancement: the GTA extension is now loaded asynchronically by Visual Studio; the drawback is that support for Visual Studio 2012 had to be dropped ([#243](https://github.com/csoltenborn/GoogleTestAdapter/issues/243)) * enhancement: the *Show release notes* option has been removed, and this release notes dialog has been slightly changed and now has very funny buttons ([#270](https://github.com/csoltenborn/GoogleTestAdapter/issues/270)) * maintenance: reduced code duplication of streaming and batch output parsers ([#263](https://github.com/csoltenborn/GoogleTestAdapter/issues/263)) From 0e3c0e40dd5fca0a6733bd97b6f97b6e5f54c5ba Mon Sep 17 00:00:00 2001 From: Christian Soltenborn Date: Sun, 31 Mar 2019 18:15:39 +0200 Subject: [PATCH 2/9] added initial support for placeholders from helper files --- .../Runners/SequentialTestRunnerTests.cs | 3 +- .../Settings/SettingsWrapperTests.cs | 3 +- GoogleTestAdapter/Core/Core.csproj | 1 + .../Core/Settings/HelperFilesCache.cs | 73 +++++++++++++ .../Core/Settings/PlaceholderReplacer.cs | 102 ++++++++++-------- .../Core/Settings/SettingsWrapper.cs | 32 ++++-- .../TestAdapter/CommonFunctions.cs | 4 +- GoogleTestAdapter/Tests.Common/TestsBase.cs | 7 +- 8 files changed, 166 insertions(+), 59 deletions(-) create mode 100644 GoogleTestAdapter/Core/Settings/HelperFilesCache.cs diff --git a/GoogleTestAdapter/Core.Tests/Runners/SequentialTestRunnerTests.cs b/GoogleTestAdapter/Core.Tests/Runners/SequentialTestRunnerTests.cs index 95a842cce..db4bd4e2f 100644 --- a/GoogleTestAdapter/Core.Tests/Runners/SequentialTestRunnerTests.cs +++ b/GoogleTestAdapter/Core.Tests/Runners/SequentialTestRunnerTests.cs @@ -123,7 +123,8 @@ private SettingsWrapper CreateSettings(string solutionWorkingDir, string project return new SettingsWrapper(mockContainer.Object, TestResources.SampleTestsSolutionDir) { - RegexTraitParser = new RegexTraitParser(MockLogger.Object) + RegexTraitParser = new RegexTraitParser(MockLogger.Object), + HelperFilesCache = new HelperFilesCache(MockLogger.Object) }; } diff --git a/GoogleTestAdapter/Core.Tests/Settings/SettingsWrapperTests.cs b/GoogleTestAdapter/Core.Tests/Settings/SettingsWrapperTests.cs index 5e30e477e..07a126086 100644 --- a/GoogleTestAdapter/Core.Tests/Settings/SettingsWrapperTests.cs +++ b/GoogleTestAdapter/Core.Tests/Settings/SettingsWrapperTests.cs @@ -32,7 +32,8 @@ public override void SetUp() containerMock.Setup(c => c.GetSettingsForExecutable(It.IsAny())).Returns(MockXmlOptions.Object); TheOptions = new SettingsWrapper(containerMock.Object) { - RegexTraitParser = new RegexTraitParser(TestEnvironment.Logger) + RegexTraitParser = new RegexTraitParser(TestEnvironment.Logger), + HelperFilesCache = new HelperFilesCache(TestEnvironment.Logger) }; } diff --git a/GoogleTestAdapter/Core/Core.csproj b/GoogleTestAdapter/Core/Core.csproj index 9da1a1442..e629b1838 100644 --- a/GoogleTestAdapter/Core/Core.csproj +++ b/GoogleTestAdapter/Core/Core.csproj @@ -72,6 +72,7 @@ + diff --git a/GoogleTestAdapter/Core/Settings/HelperFilesCache.cs b/GoogleTestAdapter/Core/Settings/HelperFilesCache.cs new file mode 100644 index 000000000..17b35b5f8 --- /dev/null +++ b/GoogleTestAdapter/Core/Settings/HelperFilesCache.cs @@ -0,0 +1,73 @@ +using System; +using System.Collections.Generic; +using System.IO; +using GoogleTestAdapter.Common; + +namespace GoogleTestAdapter.Settings +{ + public class HelperFilesCache + { + public const string HelperFileEnding = ".gta_settings_helper"; + public const string SettingsSeparator = ":::"; + + private readonly ILogger _logger; + private readonly IDictionary> _files2ReplacementMap = new Dictionary>(); + + public HelperFilesCache(ILogger logger) + { + _logger = logger; + } + + // virtual for mocking + public virtual IDictionary GetReplacementsMap(string executable) + { + lock (this) + { + if (!_files2ReplacementMap.TryGetValue(executable, out IDictionary replacementMap)) + { + replacementMap = LoadReplacementsMap(executable); + _files2ReplacementMap[executable] = replacementMap; + } + return replacementMap; + } + } + + private IDictionary LoadReplacementsMap(string executable) + { + string helperFile = GetHelperFile(executable); + try + { + if (!File.Exists(helperFile)) + return new Dictionary(); + + return ParseHelperFile(File.ReadAllText(helperFile)); + } + catch (Exception e) + { + _logger.LogWarning($"Exception while loading settings from file '{helperFile}': {e}"); + return new Dictionary(); + } + } + + private IDictionary ParseHelperFile(string content) + { + var replacementMap = new Dictionary(); + foreach (string setting in content.Split(new []{ SettingsSeparator }, StringSplitOptions.RemoveEmptyEntries)) + { + int index = setting.IndexOf('='); + if (index > 0) + { + string name = setting.Substring(0, index); + string value = setting.Substring(index + 1, setting.Length - index); + replacementMap.Add(name, value); + } + } + return replacementMap; + } + + private string GetHelperFile(string executable) + { + return $"{executable}{HelperFileEnding}"; + } + } +} \ No newline at end of file diff --git a/GoogleTestAdapter/Core/Settings/PlaceholderReplacer.cs b/GoogleTestAdapter/Core/Settings/PlaceholderReplacer.cs index ab9f43ec7..aaf12767b 100644 --- a/GoogleTestAdapter/Core/Settings/PlaceholderReplacer.cs +++ b/GoogleTestAdapter/Core/Settings/PlaceholderReplacer.cs @@ -34,14 +34,16 @@ public class PlaceholderReplacer private readonly Func _getSolutionDir; private readonly Func _getSettings; + private readonly HelperFilesCache _helperFilesCache; private string SolutionDir => _getSolutionDir(); private IGoogleTestAdapterSettings Settings => _getSettings(); - public PlaceholderReplacer(Func getSolutionDir, Func getSettings) + public PlaceholderReplacer(Func getSolutionDir, Func getSettings, HelperFilesCache helperFilesCache) { _getSolutionDir = getSolutionDir; _getSettings = getSettings; + _helperFilesCache = helperFilesCache; } @@ -55,10 +57,12 @@ public PlaceholderReplacer(Func getSolutionDir, Func SolutionDir, () => _currentSettings, HelperFilesCache); + } + } + + private PlaceholderReplacer _placeholderReplacer; + private int _nrOfRunningExecutions; private string _currentExecutable; private Thread _currentThread; @@ -33,13 +46,16 @@ public SettingsWrapper(IGoogleTestAdapterSettingsContainer settingsContainer, st _settingsContainer = settingsContainer; _solutionDir = solutionDir; _currentSettings = _settingsContainer.SolutionSettings; - _placeholderReplacer = new PlaceholderReplacer(() => SolutionDir, () => _currentSettings); _settingsPrinter = new SettingsPrinter(this); } public virtual SettingsWrapper Clone() { - return new SettingsWrapper(_settingsContainer, _solutionDir) { RegexTraitParser = RegexTraitParser }; + return new SettingsWrapper(_settingsContainer, _solutionDir) + { + RegexTraitParser = RegexTraitParser, + HelperFilesCache = HelperFilesCache + }; } public override string ToString() @@ -302,12 +318,12 @@ public IEnumerable GetAdditionalPdbs(string executable) public string GetWorkingDirForExecution(string executable, string testDirectory, int threadId) { - return _placeholderReplacer.ReplaceWorkingDirPlaceholdersForExecution(executable, WorkingDir, testDirectory, threadId); + return _placeholderReplacer.ReplaceWorkingDirPlaceholdersForExecution(WorkingDir, executable, testDirectory, threadId); } public string GetWorkingDirForDiscovery(string executable) { - return _placeholderReplacer.ReplaceWorkingDirPlaceholdersForDiscovery(executable, WorkingDir); + return _placeholderReplacer.ReplaceWorkingDirPlaceholdersForDiscovery(WorkingDir, executable); } public const string OptionPathExtension = "PATH extension"; @@ -346,7 +362,7 @@ public string GetUserParametersForDiscovery(string executable) public virtual string BatchForTestSetup => _currentSettings.BatchForTestSetup ?? OptionBatchForTestSetupDefaultValue; public string GetBatchForTestSetup(string testDirectory, int threadId) - => _placeholderReplacer.ReplaceBatchForTestSetupPlaceholders(BatchForTestSetup, testDirectory, threadId); + => _placeholderReplacer.ReplaceBatchPlaceholders(BatchForTestSetup, testDirectory, threadId); public const string OptionBatchForTestTeardown = "Test teardown batch file"; @@ -357,7 +373,7 @@ public string GetBatchForTestSetup(string testDirectory, int threadId) public virtual string BatchForTestTeardown => _currentSettings.BatchForTestTeardown ?? OptionBatchForTestTeardownDefaultValue; public string GetBatchForTestTeardown(string testDirectory, int threadId) - => _placeholderReplacer.ReplaceBatchForTestTeardownPlaceholders(BatchForTestTeardown, testDirectory, + => _placeholderReplacer.ReplaceBatchPlaceholders(BatchForTestTeardown, testDirectory, threadId); diff --git a/GoogleTestAdapter/TestAdapter/CommonFunctions.cs b/GoogleTestAdapter/TestAdapter/CommonFunctions.cs index 4cbef3c50..8e2b34326 100644 --- a/GoogleTestAdapter/TestAdapter/CommonFunctions.cs +++ b/GoogleTestAdapter/TestAdapter/CommonFunctions.cs @@ -57,8 +57,8 @@ public static void CreateEnvironment(IRunSettings runSettings, IMessageLogger me var settingsWrapper = new SettingsWrapper(ourRunSettings, solutionDir); var loggerAdapter = new VsTestFrameworkLogger(messageLogger, () => settingsWrapper.OutputMode, () => settingsWrapper.TimestampOutput); - var regexParser = new RegexTraitParser(loggerAdapter); - settingsWrapper.RegexTraitParser = regexParser; + settingsWrapper.RegexTraitParser = new RegexTraitParser(loggerAdapter); + settingsWrapper.HelperFilesCache = new HelperFilesCache(loggerAdapter); LogWarningsForDeprecatedSettings(ourRunSettings, loggerAdapter); diff --git a/GoogleTestAdapter/Tests.Common/TestsBase.cs b/GoogleTestAdapter/Tests.Common/TestsBase.cs index a6cf46d79..a2736e578 100644 --- a/GoogleTestAdapter/Tests.Common/TestsBase.cs +++ b/GoogleTestAdapter/Tests.Common/TestsBase.cs @@ -46,6 +46,7 @@ protected TestsBase() var mockRunSettings = new Mock(); mockSettingsContainer.Setup(c => c.SolutionSettings).Returns(mockRunSettings.Object); MockOptions = new Mock(mockSettingsContainer.Object, Path.GetFullPath(TestResources.SampleTestsSolutionDir)); + MockFrameworkReporter = new Mock(); TestEnvironment = new TestEnvironment(MockOptions.Object, MockLogger.Object); @@ -56,11 +57,13 @@ protected TestsBase() [TestInitialize] public virtual void SetUp() { - SetupOptions(MockOptions); + SetupOptions(MockOptions, MockLogger.Object); } - public static void SetupOptions(Mock mockOptions) + public static void SetupOptions(Mock mockOptions, ILogger logger) { + mockOptions.Object.HelperFilesCache = new HelperFilesCache(logger); + mockOptions.Setup(o => o.CheckCorrectUsage(It.IsAny())).Callback(() => { }); mockOptions.Setup(o => o.Clone()).Returns(mockOptions.Object); From 18469fe84be5fda597d41dcbe34a11dbc806226c Mon Sep 17 00:00:00 2001 From: Christian Soltenborn Date: Sun, 31 Mar 2019 19:40:09 +0200 Subject: [PATCH 3/9] SolutionDir, ConfigurationName and PlatformName can be consumed from helper files --- .../Core/Settings/HelperFilesCache.cs | 14 +-- .../Core/Settings/PlaceholderReplacer.cs | 104 +++++++++--------- 2 files changed, 57 insertions(+), 61 deletions(-) diff --git a/GoogleTestAdapter/Core/Settings/HelperFilesCache.cs b/GoogleTestAdapter/Core/Settings/HelperFilesCache.cs index 17b35b5f8..a330a663c 100644 --- a/GoogleTestAdapter/Core/Settings/HelperFilesCache.cs +++ b/GoogleTestAdapter/Core/Settings/HelperFilesCache.cs @@ -32,6 +32,11 @@ public virtual IDictionary GetReplacementsMap(string executable) } } + private string GetHelperFile(string executable) + { + return $"{executable}{HelperFileEnding}"; + } + private IDictionary LoadReplacementsMap(string executable) { string helperFile = GetHelperFile(executable); @@ -57,17 +62,12 @@ private IDictionary ParseHelperFile(string content) int index = setting.IndexOf('='); if (index > 0) { - string name = setting.Substring(0, index); + string placeholder = setting.Substring(0, index); string value = setting.Substring(index + 1, setting.Length - index); - replacementMap.Add(name, value); + replacementMap.Add(placeholder, value); } } return replacementMap; } - - private string GetHelperFile(string executable) - { - return $"{executable}{HelperFileEnding}"; - } } } \ No newline at end of file diff --git a/GoogleTestAdapter/Core/Settings/PlaceholderReplacer.cs b/GoogleTestAdapter/Core/Settings/PlaceholderReplacer.cs index aaf12767b..14e0aaf96 100644 --- a/GoogleTestAdapter/Core/Settings/PlaceholderReplacer.cs +++ b/GoogleTestAdapter/Core/Settings/PlaceholderReplacer.cs @@ -58,8 +58,8 @@ public PlaceholderReplacer(Func getSolutionDir, Func Date: Tue, 2 Apr 2019 06:06:58 +0200 Subject: [PATCH 4/9] added unit tests --- .../Core.Tests/Core.Tests.csproj | 2 + .../Settings/HelperFilesCacheTests.cs | 132 +++++++++++++++++ .../Settings/PlaceholderReplacerTests.cs | 140 ++++++++++++++++++ GoogleTestAdapter/Core.Tests/packages.config | 2 + .../Core/Settings/HelperFilesCache.cs | 6 +- .../Core/Settings/PlaceholderReplacer.cs | 2 +- .../Core/Settings/SettingsWrapper.cs | 2 +- 7 files changed, 283 insertions(+), 3 deletions(-) create mode 100644 GoogleTestAdapter/Core.Tests/Settings/HelperFilesCacheTests.cs create mode 100644 GoogleTestAdapter/Core.Tests/Settings/PlaceholderReplacerTests.cs diff --git a/GoogleTestAdapter/Core.Tests/Core.Tests.csproj b/GoogleTestAdapter/Core.Tests/Core.Tests.csproj index ab66af659..5cc726aca 100644 --- a/GoogleTestAdapter/Core.Tests/Core.Tests.csproj +++ b/GoogleTestAdapter/Core.Tests/Core.Tests.csproj @@ -97,6 +97,8 @@ + + diff --git a/GoogleTestAdapter/Core.Tests/Settings/HelperFilesCacheTests.cs b/GoogleTestAdapter/Core.Tests/Settings/HelperFilesCacheTests.cs new file mode 100644 index 000000000..45cd3d1fa --- /dev/null +++ b/GoogleTestAdapter/Core.Tests/Settings/HelperFilesCacheTests.cs @@ -0,0 +1,132 @@ +using System; +using System.Collections.Generic; +using System.IO; +using FluentAssertions; +using GoogleTestAdapter.Tests.Common; +using GoogleTestAdapter.Tests.Common.Assertions; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using static GoogleTestAdapter.Tests.Common.TestMetadata.TestCategories; + +namespace GoogleTestAdapter.Settings +{ + [TestClass] + public class HelperFilesCacheTests : TestsBase + { + [TestMethod] + [TestCategory(Unit)] + public void GetReplacementsMap_NoFile_EmptyDictionary() + { + string executable = TestResources.Tests_DebugX86; + var extraSettings = $"{executable}{HelperFilesCache.HelperFileEnding}"; + extraSettings.AsFileInfo().Should().NotExist(); + + var cache = new HelperFilesCache(MockLogger.Object); + var map = cache.GetReplacementsMap(executable); + + map.Should().BeEmpty(); + } + + [TestMethod] + [TestCategory(Unit)] + public void GetReplacementsMap_EmptyString_EmptyDictionary() + { + DoTest("", map => + { + map.Should().BeEmpty(); + }); + } + + [TestMethod] + [TestCategory(Unit)] + public void GetReplacementsMap_InvalidString_EmptyDictionary() + { + DoTest("Foo", map => + { + map.Should().BeEmpty(); + }); + } + + [TestMethod] + [TestCategory(Unit)] + public void GetReplacementsMap_SingleValue_ProperDictionary() + { + DoTest("Foo=Bar", map => + { + map.Should().HaveCount(1); + map.Should().Contain(new KeyValuePair("Foo", "Bar")); + }); + } + + [TestMethod] + [TestCategory(Unit)] + public void GetReplacementsMap_TwoValues_ProperDictionary() + { + DoTest($"Placeholder1=value1{HelperFilesCache.SettingsSeparator}Placeholder2=value2", map => + { + map.Should().HaveCount(2); + map.Should().Contain(new KeyValuePair("Placeholder1", "value1")); + map.Should().Contain(new KeyValuePair("Placeholder2", "value2")); + }); + } + + [TestMethod] + [TestCategory(Unit)] + public void GetReplacementsMap_SingleWithEmptyValue_ProperDictionary() + { + DoTest("Placeholder1=", map => + { + map.Should().HaveCount(1); + map.Should().Contain(new KeyValuePair("Placeholder1", "")); + }); + } + + [TestMethod] + [TestCategory(Unit)] + public void GetReplacementsMap_SingleWithTrailingSeparator_ProperDictionary() + { + DoTest($"Ph=V{HelperFilesCache.SettingsSeparator}", map => + { + map.Should().HaveCount(1); + map.Should().Contain(new KeyValuePair("Ph", "V")); + }); + } + + [TestMethod] + [TestCategory(Unit)] + public void GetReplacementsMap_OnlySeparator_ProperDictionary() + { + DoTest($"{HelperFilesCache.SettingsSeparator}", map => + { + map.Should().BeEmpty(); + }); + } + + [TestMethod] + [TestCategory(Unit)] + public void GetReplacementsMap_OnlyTwoSeparators_ProperDictionary() + { + DoTest($"{HelperFilesCache.SettingsSeparator}{HelperFilesCache.SettingsSeparator}", map => + { + map.Should().BeEmpty(); + }); + } + + private void DoTest(string content, Action> assertions, string executable = TestResources.Tests_DebugX86) + { + var extraSettings = $"{executable}{HelperFilesCache.HelperFileEnding}"; + try + { + extraSettings.AsFileInfo().Should().NotExist(); + File.WriteAllText(extraSettings, content); + + var cache = new HelperFilesCache(MockLogger.Object); + var map = cache.GetReplacementsMap(executable); + assertions(map); + } + finally + { + File.Delete(extraSettings); + } + } + } +} \ No newline at end of file diff --git a/GoogleTestAdapter/Core.Tests/Settings/PlaceholderReplacerTests.cs b/GoogleTestAdapter/Core.Tests/Settings/PlaceholderReplacerTests.cs new file mode 100644 index 000000000..9acf30e6b --- /dev/null +++ b/GoogleTestAdapter/Core.Tests/Settings/PlaceholderReplacerTests.cs @@ -0,0 +1,140 @@ +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Reflection; +using FluentAssertions; +using GoogleTestAdapter.Tests.Common; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Moq; +using static GoogleTestAdapter.Tests.Common.TestMetadata.TestCategories; + +namespace GoogleTestAdapter.Settings +{ + [TestClass] + public class PlaceholderReplacerTests + { + private class PlaceholderAndValue + { + public string Placeholder { get; } + public object Value { get; } + + public PlaceholderAndValue(string placeholder, object value) + { + Placeholder = placeholder; + Value = value; + } + } + + private class MethodnameAndPlaceholder + { + public string MethodName { get; } + public string Placeholder { get; } + + public MethodnameAndPlaceholder(string methodName, string placeholder) + { + MethodName = methodName; + Placeholder = placeholder; + } + } + + private static readonly string[] MethodNames = { + nameof(PlaceholderReplacer.ReplaceAdditionalPdbsPlaceholders), + nameof(PlaceholderReplacer.ReplaceAdditionalTestExecutionParamPlaceholdersForDiscovery), + nameof(PlaceholderReplacer.ReplaceAdditionalTestExecutionParamPlaceholdersForExecution), + nameof(PlaceholderReplacer.ReplaceBatchPlaceholders), + nameof(PlaceholderReplacer.ReplacePathExtensionPlaceholders), + nameof(PlaceholderReplacer.ReplaceWorkingDirPlaceholdersForDiscovery), + nameof(PlaceholderReplacer.ReplaceWorkingDirPlaceholdersForExecution) + }; + + private static readonly List PlaceholdersAndExpectedValues = new List + { + new PlaceholderAndValue(PlaceholderReplacer.SolutionDirPlaceholder, TestResources.SampleTestsSolutionDir), + new PlaceholderAndValue(PlaceholderReplacer.PlatformNamePlaceholder, "Win33"), + new PlaceholderAndValue(PlaceholderReplacer.ConfigurationNamePlaceholder, "MyDebug"), + // ReSharper disable once AssignNullToNotNullAttribute + new PlaceholderAndValue(PlaceholderReplacer.ExecutableDirPlaceholder, Path.GetFullPath(Path.GetDirectoryName(TestResources.Tests_DebugX86))), + new PlaceholderAndValue(PlaceholderReplacer.ExecutablePlaceholder, TestResources.Tests_DebugX86), + new PlaceholderAndValue(PlaceholderReplacer.TestDirPlaceholder, "testDirectory"), + new PlaceholderAndValue(PlaceholderReplacer.ThreadIdPlaceholder, 42) + }; + + private static readonly List UnsupportedCombinations = new List + { + new MethodnameAndPlaceholder(nameof(PlaceholderReplacer.ReplaceAdditionalPdbsPlaceholders), PlaceholderReplacer.TestDirPlaceholder), + new MethodnameAndPlaceholder(nameof(PlaceholderReplacer.ReplaceAdditionalTestExecutionParamPlaceholdersForDiscovery), PlaceholderReplacer.TestDirPlaceholder), + new MethodnameAndPlaceholder(nameof(PlaceholderReplacer.ReplacePathExtensionPlaceholders), PlaceholderReplacer.TestDirPlaceholder), + new MethodnameAndPlaceholder(nameof(PlaceholderReplacer.ReplaceWorkingDirPlaceholdersForDiscovery), PlaceholderReplacer.TestDirPlaceholder), + + new MethodnameAndPlaceholder(nameof(PlaceholderReplacer.ReplaceAdditionalPdbsPlaceholders), PlaceholderReplacer.ThreadIdPlaceholder), + new MethodnameAndPlaceholder(nameof(PlaceholderReplacer.ReplaceAdditionalTestExecutionParamPlaceholdersForDiscovery), PlaceholderReplacer.ThreadIdPlaceholder), + new MethodnameAndPlaceholder(nameof(PlaceholderReplacer.ReplacePathExtensionPlaceholders), PlaceholderReplacer.ThreadIdPlaceholder), + new MethodnameAndPlaceholder(nameof(PlaceholderReplacer.ReplaceWorkingDirPlaceholdersForDiscovery), PlaceholderReplacer.ThreadIdPlaceholder), + + new MethodnameAndPlaceholder(nameof(PlaceholderReplacer.ReplaceBatchPlaceholders), PlaceholderReplacer.ExecutablePlaceholder), + new MethodnameAndPlaceholder(nameof(PlaceholderReplacer.ReplaceBatchPlaceholders), PlaceholderReplacer.ExecutableDirPlaceholder), + new MethodnameAndPlaceholder(nameof(PlaceholderReplacer.ReplaceBatchPlaceholders), PlaceholderReplacer.ConfigurationNamePlaceholder), + new MethodnameAndPlaceholder(nameof(PlaceholderReplacer.ReplaceBatchPlaceholders), PlaceholderReplacer.PlatformNamePlaceholder), + }; + + + [TestMethod] + [TestCategory(Unit)] + public void AllReplacementsTest() + { + Mock mockHelperFilesCache = new Mock(); + mockHelperFilesCache.Setup(c => c.GetReplacementsMap(It.IsAny())).Returns( + new Dictionary + { + {nameof(IGoogleTestAdapterSettings.ConfigurationName), "MyDebug"}, + {nameof(IGoogleTestAdapterSettings.PlatformName), "Win33"} + }); + Mock mockOptions = new Mock(); + var placeholderReplacer = new PlaceholderReplacer( + () => TestResources.SampleTestsSolutionDir, + () => mockOptions.Object, + mockHelperFilesCache.Object); + + foreach (string methodName in MethodNames) + { + foreach (PlaceholderAndValue placeholder in PlaceholdersAndExpectedValues) + { + if (!UnsupportedCombinations.Any(combination => + combination.MethodName == methodName && combination.Placeholder == placeholder.Placeholder)) + { + GenericReplacementTest(placeholderReplacer, methodName, placeholder.Placeholder, placeholder.Value.ToString()); + } + } + } + } + + private void GenericReplacementTest(PlaceholderReplacer placeholderReplacer, + string methodName, string input, object expected) + { + var method = typeof(PlaceholderReplacer).GetMethod(methodName); + // ReSharper disable once PossibleNullReferenceException + var parameters = method.GetParameters(); + + var parameterValues = new List { input }; + for (int i = 1; i < parameters.Length; i++) + { + parameterValues.Add(GetValue(parameters[i])); + } + + string result = (string) method.Invoke(placeholderReplacer, parameterValues.ToArray()); + + result.Should().Be(expected.ToString(), $"{methodName} should replace {input} with {expected}"); + } + + private object GetValue(ParameterInfo parameter) + { + switch (parameter.Name) + { + case "executable": return TestResources.Tests_DebugX86; + case "threadId": return 42; + default: return parameter.Name; + } + } + + } +} \ No newline at end of file diff --git a/GoogleTestAdapter/Core.Tests/packages.config b/GoogleTestAdapter/Core.Tests/packages.config index 87cac3d92..31fd4c297 100644 --- a/GoogleTestAdapter/Core.Tests/packages.config +++ b/GoogleTestAdapter/Core.Tests/packages.config @@ -3,6 +3,8 @@ + + diff --git a/GoogleTestAdapter/Core/Settings/HelperFilesCache.cs b/GoogleTestAdapter/Core/Settings/HelperFilesCache.cs index a330a663c..0d206bd5a 100644 --- a/GoogleTestAdapter/Core/Settings/HelperFilesCache.cs +++ b/GoogleTestAdapter/Core/Settings/HelperFilesCache.cs @@ -13,6 +13,10 @@ public class HelperFilesCache private readonly ILogger _logger; private readonly IDictionary> _files2ReplacementMap = new Dictionary>(); + // public for mocking + // ReSharper disable once UnusedMember.Global + public HelperFilesCache() {} + public HelperFilesCache(ILogger logger) { _logger = logger; @@ -63,7 +67,7 @@ private IDictionary ParseHelperFile(string content) if (index > 0) { string placeholder = setting.Substring(0, index); - string value = setting.Substring(index + 1, setting.Length - index); + string value = setting.Substring(index + 1, setting.Length - index - 1); replacementMap.Add(placeholder, value); } } diff --git a/GoogleTestAdapter/Core/Settings/PlaceholderReplacer.cs b/GoogleTestAdapter/Core/Settings/PlaceholderReplacer.cs index 14e0aaf96..06433866e 100644 --- a/GoogleTestAdapter/Core/Settings/PlaceholderReplacer.cs +++ b/GoogleTestAdapter/Core/Settings/PlaceholderReplacer.cs @@ -55,7 +55,7 @@ public PlaceholderReplacer(Func getSolutionDir, Func _currentSettings.AdditionalPdbs ?? OptionAdditionalPdbsDefaultValue; public IEnumerable GetAdditionalPdbs(string executable) => Utils.SplitAdditionalPdbs(AdditionalPdbs) - .Select(p => _placeholderReplacer.ReplaceAdditionalPdbsPlaceholders(executable, p)); + .Select(p => _placeholderReplacer.ReplaceAdditionalPdbsPlaceholders(p, executable)); public const string OptionWorkingDir = "Working directory"; From 6de7d99b668339fe31be1d6356e436c1dcb5d576 Mon Sep 17 00:00:00 2001 From: Christian Soltenborn Date: Sun, 14 Apr 2019 11:04:16 +0200 Subject: [PATCH 5/9] added example project; warnings are printed for unknown placeholders; changed seperator and removed prefix --- .../Settings/PlaceholderReplacerTests.cs | 57 +++-- .../Settings/SettingsWrapperTests.cs | 3 +- .../Core/Settings/HelperFilesCache.cs | 6 +- .../Core/Settings/PlaceholderReplacer.cs | 71 +++++- .../Core/Settings/SettingsWrapper.cs | 8 +- .../DiaResolver.Tests/DiaResolverTests.cs | 2 +- GoogleTestAdapter/GoogleTestAdapter.sln | 13 ++ .../Settings/HelperFileTests.cs | 61 +++++ .../TestAdapter.Tests.csproj | 1 + .../TestExecutorTestsBase.cs | 1 - .../Tests.Common/TestResources.cs | 3 + README.md | 5 + .../HelperFileTests/HelperFileTests.cpp | 8 + .../HelperFileTests/HelperFileTests.vcxproj | 214 ++++++++++++++++++ .../HelperFileTests.vcxproj.filters | 28 +++ SampleTests/HelperFileTests/Main.cpp | 22 ++ .../LeakCheckTests/LeakCheckTests.vcxproj | 12 +- SampleTests/SampleTests.gta.runsettings | 47 ++-- SampleTests/SampleTests.sln | 12 + 19 files changed, 520 insertions(+), 54 deletions(-) create mode 100644 GoogleTestAdapter/TestAdapter.Tests/Settings/HelperFileTests.cs create mode 100644 SampleTests/HelperFileTests/HelperFileTests.cpp create mode 100644 SampleTests/HelperFileTests/HelperFileTests.vcxproj create mode 100644 SampleTests/HelperFileTests/HelperFileTests.vcxproj.filters create mode 100644 SampleTests/HelperFileTests/Main.cpp diff --git a/GoogleTestAdapter/Core.Tests/Settings/PlaceholderReplacerTests.cs b/GoogleTestAdapter/Core.Tests/Settings/PlaceholderReplacerTests.cs index 9acf30e6b..ebd1d2e16 100644 --- a/GoogleTestAdapter/Core.Tests/Settings/PlaceholderReplacerTests.cs +++ b/GoogleTestAdapter/Core.Tests/Settings/PlaceholderReplacerTests.cs @@ -3,6 +3,7 @@ using System.Linq; using System.Reflection; using FluentAssertions; +using GoogleTestAdapter.Common; using GoogleTestAdapter.Tests.Common; using Microsoft.VisualStudio.TestTools.UnitTesting; using Moq; @@ -41,7 +42,8 @@ public MethodnameAndPlaceholder(string methodName, string placeholder) nameof(PlaceholderReplacer.ReplaceAdditionalPdbsPlaceholders), nameof(PlaceholderReplacer.ReplaceAdditionalTestExecutionParamPlaceholdersForDiscovery), nameof(PlaceholderReplacer.ReplaceAdditionalTestExecutionParamPlaceholdersForExecution), - nameof(PlaceholderReplacer.ReplaceBatchPlaceholders), + nameof(PlaceholderReplacer.ReplaceSetupBatchPlaceholders), + nameof(PlaceholderReplacer.ReplaceTeardownBatchPlaceholders), nameof(PlaceholderReplacer.ReplacePathExtensionPlaceholders), nameof(PlaceholderReplacer.ReplaceWorkingDirPlaceholdersForDiscovery), nameof(PlaceholderReplacer.ReplaceWorkingDirPlaceholdersForExecution) @@ -71,10 +73,15 @@ public MethodnameAndPlaceholder(string methodName, string placeholder) new MethodnameAndPlaceholder(nameof(PlaceholderReplacer.ReplacePathExtensionPlaceholders), PlaceholderReplacer.ThreadIdPlaceholder), new MethodnameAndPlaceholder(nameof(PlaceholderReplacer.ReplaceWorkingDirPlaceholdersForDiscovery), PlaceholderReplacer.ThreadIdPlaceholder), - new MethodnameAndPlaceholder(nameof(PlaceholderReplacer.ReplaceBatchPlaceholders), PlaceholderReplacer.ExecutablePlaceholder), - new MethodnameAndPlaceholder(nameof(PlaceholderReplacer.ReplaceBatchPlaceholders), PlaceholderReplacer.ExecutableDirPlaceholder), - new MethodnameAndPlaceholder(nameof(PlaceholderReplacer.ReplaceBatchPlaceholders), PlaceholderReplacer.ConfigurationNamePlaceholder), - new MethodnameAndPlaceholder(nameof(PlaceholderReplacer.ReplaceBatchPlaceholders), PlaceholderReplacer.PlatformNamePlaceholder), + new MethodnameAndPlaceholder(nameof(PlaceholderReplacer.ReplaceSetupBatchPlaceholders), PlaceholderReplacer.ExecutablePlaceholder), + new MethodnameAndPlaceholder(nameof(PlaceholderReplacer.ReplaceSetupBatchPlaceholders), PlaceholderReplacer.ExecutableDirPlaceholder), + new MethodnameAndPlaceholder(nameof(PlaceholderReplacer.ReplaceSetupBatchPlaceholders), PlaceholderReplacer.ConfigurationNamePlaceholder), + new MethodnameAndPlaceholder(nameof(PlaceholderReplacer.ReplaceSetupBatchPlaceholders), PlaceholderReplacer.PlatformNamePlaceholder), + + new MethodnameAndPlaceholder(nameof(PlaceholderReplacer.ReplaceTeardownBatchPlaceholders), PlaceholderReplacer.ExecutablePlaceholder), + new MethodnameAndPlaceholder(nameof(PlaceholderReplacer.ReplaceTeardownBatchPlaceholders), PlaceholderReplacer.ExecutableDirPlaceholder), + new MethodnameAndPlaceholder(nameof(PlaceholderReplacer.ReplaceTeardownBatchPlaceholders), PlaceholderReplacer.ConfigurationNamePlaceholder), + new MethodnameAndPlaceholder(nameof(PlaceholderReplacer.ReplaceTeardownBatchPlaceholders), PlaceholderReplacer.PlatformNamePlaceholder), }; @@ -90,10 +97,12 @@ public void AllReplacementsTest() {nameof(IGoogleTestAdapterSettings.PlatformName), "Win33"} }); Mock mockOptions = new Mock(); + Mock mockLogger = new Mock(); var placeholderReplacer = new PlaceholderReplacer( () => TestResources.SampleTestsSolutionDir, () => mockOptions.Object, - mockHelperFilesCache.Object); + mockHelperFilesCache.Object, + mockLogger.Object); foreach (string methodName in MethodNames) { @@ -102,28 +111,50 @@ public void AllReplacementsTest() if (!UnsupportedCombinations.Any(combination => combination.MethodName == methodName && combination.Placeholder == placeholder.Placeholder)) { - GenericReplacementTest(placeholderReplacer, methodName, placeholder.Placeholder, placeholder.Value.ToString()); + var result = InvokeMethodWithStandardParameters(placeholderReplacer, methodName, placeholder.Placeholder); + result.Should().Be(placeholder.Value.ToString(), $"{methodName} should replace {placeholder.Placeholder} with {(object) placeholder.Value.ToString()}"); + mockLogger.Verify(l => l.LogWarning(It.IsAny()), Times.Never); } } } } - private void GenericReplacementTest(PlaceholderReplacer placeholderReplacer, - string methodName, string input, object expected) + [TestMethod] + [TestCategory(Unit)] + public void AllReplacementMethods_UnknownPlaceholderResultsInWarning() + { + Mock mockHelperFilesCache = new Mock(); + mockHelperFilesCache.Setup(c => c.GetReplacementsMap(It.IsAny())) + .Returns(new Dictionary()); + Mock mockOptions = new Mock(); + Mock mockLogger = new Mock(); + var replacer = new PlaceholderReplacer(() => "solutiondir", () => mockOptions.Object, + mockHelperFilesCache.Object, mockLogger.Object); + + string placeholder = "$(UnknownPlaceholder)"; + foreach (string methodName in MethodNames) + { + mockLogger.Reset(); + string result = InvokeMethodWithStandardParameters(replacer, methodName, placeholder); + result.Should().Be(placeholder); + mockLogger.Verify(l => l.LogWarning(It.Is(msg => msg.Contains(placeholder))), Times.Once); + } + } + + private string InvokeMethodWithStandardParameters(PlaceholderReplacer placeholderReplacer, string methodName, + string input) { var method = typeof(PlaceholderReplacer).GetMethod(methodName); // ReSharper disable once PossibleNullReferenceException var parameters = method.GetParameters(); - var parameterValues = new List { input }; + var parameterValues = new List {input}; for (int i = 1; i < parameters.Length; i++) { parameterValues.Add(GetValue(parameters[i])); } - string result = (string) method.Invoke(placeholderReplacer, parameterValues.ToArray()); - - result.Should().Be(expected.ToString(), $"{methodName} should replace {input} with {expected}"); + return (string) method.Invoke(placeholderReplacer, parameterValues.ToArray()); } private object GetValue(ParameterInfo parameter) diff --git a/GoogleTestAdapter/Core.Tests/Settings/SettingsWrapperTests.cs b/GoogleTestAdapter/Core.Tests/Settings/SettingsWrapperTests.cs index 07a126086..1da28c8de 100644 --- a/GoogleTestAdapter/Core.Tests/Settings/SettingsWrapperTests.cs +++ b/GoogleTestAdapter/Core.Tests/Settings/SettingsWrapperTests.cs @@ -644,7 +644,8 @@ private SettingsWrapper CreateSettingsWrapper(string solutionWorkdir, params str return new SettingsWrapper(containerMock.Object) { - RegexTraitParser = new RegexTraitParser(MockLogger.Object) + RegexTraitParser = new RegexTraitParser(MockLogger.Object), + HelperFilesCache = new HelperFilesCache(MockLogger.Object) }; } diff --git a/GoogleTestAdapter/Core/Settings/HelperFilesCache.cs b/GoogleTestAdapter/Core/Settings/HelperFilesCache.cs index 0d206bd5a..499eb0800 100644 --- a/GoogleTestAdapter/Core/Settings/HelperFilesCache.cs +++ b/GoogleTestAdapter/Core/Settings/HelperFilesCache.cs @@ -8,7 +8,7 @@ namespace GoogleTestAdapter.Settings public class HelperFilesCache { public const string HelperFileEnding = ".gta_settings_helper"; - public const string SettingsSeparator = ":::"; + public const string SettingsSeparator = "::GTA::"; private readonly ILogger _logger; private readonly IDictionary> _files2ReplacementMap = new Dictionary>(); @@ -22,6 +22,8 @@ public HelperFilesCache(ILogger logger) _logger = logger; } + public ILogger Logger => _logger; + // virtual for mocking public virtual IDictionary GetReplacementsMap(string executable) { @@ -36,7 +38,7 @@ public virtual IDictionary GetReplacementsMap(string executable) } } - private string GetHelperFile(string executable) + public static string GetHelperFile(string executable) { return $"{executable}{HelperFileEnding}"; } diff --git a/GoogleTestAdapter/Core/Settings/PlaceholderReplacer.cs b/GoogleTestAdapter/Core/Settings/PlaceholderReplacer.cs index 06433866e..31a74c232 100644 --- a/GoogleTestAdapter/Core/Settings/PlaceholderReplacer.cs +++ b/GoogleTestAdapter/Core/Settings/PlaceholderReplacer.cs @@ -1,5 +1,8 @@ using System; using System.IO; +using System.Linq; +using System.Text.RegularExpressions; +using GoogleTestAdapter.Common; namespace GoogleTestAdapter.Settings { @@ -32,18 +35,22 @@ public class PlaceholderReplacer private const string DescriptionOfEnvVarPlaceholders = "Environment variables are also possible, e.g. %PATH%"; + private static readonly Regex PlaceholdersRegex = new Regex(@"\$\(\w+\)", RegexOptions.Compiled); + private readonly Func _getSolutionDir; private readonly Func _getSettings; private readonly HelperFilesCache _helperFilesCache; + private readonly ILogger _logger; private string SolutionDir => _getSolutionDir(); private IGoogleTestAdapterSettings Settings => _getSettings(); - public PlaceholderReplacer(Func getSolutionDir, Func getSettings, HelperFilesCache helperFilesCache) + public PlaceholderReplacer(Func getSolutionDir, Func getSettings, HelperFilesCache helperFilesCache, ILogger logger) { _getSolutionDir = getSolutionDir; _getSettings = getSettings; _helperFilesCache = helperFilesCache; + _logger = logger; } @@ -62,6 +69,9 @@ public string ReplaceAdditionalPdbsPlaceholders(string pdb, string executable) pdb = ReplaceSolutionDirPlaceholder(pdb, executable); pdb = ReplaceEnvironmentVariables(pdb); pdb = ReplaceHelperFileSettings(pdb, executable); + + CheckForRemainingPlaceholders(pdb, SettingsWrapper.OptionAdditionalPdbs); + return pdb; } @@ -84,6 +94,9 @@ public string ReplaceWorkingDirPlaceholdersForDiscovery(string workingDir, strin workingDir = ReplaceSolutionDirPlaceholder(workingDir, executable); workingDir = ReplaceEnvironmentVariables(workingDir); workingDir = ReplaceHelperFileSettings(workingDir, executable); + + CheckForRemainingPlaceholders(workingDir, SettingsWrapper.OptionWorkingDir); + return workingDir; } @@ -91,7 +104,14 @@ public string ReplaceWorkingDirPlaceholdersForExecution(string workingDir, strin string testDirectory, int threadId) { workingDir = ReplaceTestDirAndThreadIdPlaceholders(workingDir, testDirectory, threadId); - workingDir = ReplaceWorkingDirPlaceholdersForDiscovery(workingDir, executable); + workingDir = ReplaceExecutablePlaceholders(workingDir, executable); + workingDir = ReplacePlatformAndConfigurationPlaceholders(workingDir, executable); + workingDir = ReplaceSolutionDirPlaceholder(workingDir, executable); + workingDir = ReplaceEnvironmentVariables(workingDir); + workingDir = ReplaceHelperFileSettings(workingDir, executable); + + CheckForRemainingPlaceholders(workingDir, SettingsWrapper.OptionWorkingDir); + return workingDir; } @@ -111,6 +131,9 @@ public string ReplacePathExtensionPlaceholders(string pathExtension, string exec pathExtension = ReplaceSolutionDirPlaceholder(pathExtension, executable); pathExtension = ReplaceEnvironmentVariables(pathExtension); pathExtension = ReplaceHelperFileSettings(pathExtension, executable); + + CheckForRemainingPlaceholders(pathExtension, SettingsWrapper.OptionPathExtension); + return pathExtension; } @@ -133,6 +156,9 @@ public string ReplaceAdditionalTestExecutionParamPlaceholdersForDiscovery(string additionalTestExecutionParam = ReplaceSolutionDirPlaceholder(additionalTestExecutionParam, executable); additionalTestExecutionParam = ReplaceEnvironmentVariables(additionalTestExecutionParam); additionalTestExecutionParam = ReplaceHelperFileSettings(additionalTestExecutionParam, executable); + + CheckForRemainingPlaceholders(additionalTestExecutionParam, SettingsWrapper.OptionAdditionalTestExecutionParams); + return additionalTestExecutionParam; } @@ -140,8 +166,14 @@ public string ReplaceAdditionalTestExecutionParamPlaceholdersForExecution(string { additionalTestExecutionParam = ReplaceTestDirAndThreadIdPlaceholders(additionalTestExecutionParam, testDirectory, threadId); - additionalTestExecutionParam = - ReplaceAdditionalTestExecutionParamPlaceholdersForDiscovery(additionalTestExecutionParam, executable); + additionalTestExecutionParam = ReplaceExecutablePlaceholders(additionalTestExecutionParam, executable); + additionalTestExecutionParam = ReplacePlatformAndConfigurationPlaceholders(additionalTestExecutionParam, executable); + additionalTestExecutionParam = ReplaceSolutionDirPlaceholder(additionalTestExecutionParam, executable); + additionalTestExecutionParam = ReplaceEnvironmentVariables(additionalTestExecutionParam); + additionalTestExecutionParam = ReplaceHelperFileSettings(additionalTestExecutionParam, executable); + + CheckForRemainingPlaceholders(additionalTestExecutionParam, SettingsWrapper.OptionAdditionalTestExecutionParams); + return additionalTestExecutionParam; } @@ -154,7 +186,22 @@ public string ReplaceAdditionalTestExecutionParamPlaceholdersForExecution(string DescriptionOfThreadIdPlaceholder + "\n" + DescriptionOfEnvVarPlaceholders; - public string ReplaceBatchPlaceholders(string batch, string testDirectory, int threadId) + + public string ReplaceSetupBatchPlaceholders(string batch, string testDirectory, int threadId) + { + batch = ReplaceBatchPlaceholders(batch, testDirectory, threadId); + CheckForRemainingPlaceholders(batch, SettingsWrapper.OptionBatchForTestSetup); + return batch; + } + + public string ReplaceTeardownBatchPlaceholders(string batch, string testDirectory, int threadId) + { + batch = ReplaceBatchPlaceholders(batch, testDirectory, threadId); + CheckForRemainingPlaceholders(batch, SettingsWrapper.OptionBatchForTestTeardown); + return batch; + } + + private string ReplaceBatchPlaceholders(string batch, string testDirectory, int threadId) { batch = ReplaceTestDirAndThreadIdPlaceholders(batch, testDirectory, threadId); batch = ReplacePlatformAndConfigurationPlaceholders(batch); @@ -225,7 +272,7 @@ private string ReplaceHelperFileSettings(string theString, string executable) var replacementMap = _helperFilesCache.GetReplacementsMap(executable); foreach (var nameValuePair in replacementMap) { - theString = theString.Replace($"$(GTA:{nameValuePair.Key})", nameValuePair.Value); + theString = theString.Replace($"$({nameValuePair.Key})", nameValuePair.Value); } return theString; @@ -248,6 +295,18 @@ private string ReplaceValueWithHelperFile(string theString, string placeholder, : theString.Replace(placeholder, value); } + private void CheckForRemainingPlaceholders(string theString, string optionName) + { + var matches = PlaceholdersRegex.Matches(theString); + if (matches.Count > 0) + { + + var placeholders = matches.Cast().Select(m => m.Value).Distinct().OrderBy(s => s); + string message = $"Option '{optionName}': Apparently, the following placeholders could not be replaced. {string.Join(", ", placeholders)}"; + _logger.LogWarning(message); + } + } + } } \ No newline at end of file diff --git a/GoogleTestAdapter/Core/Settings/SettingsWrapper.cs b/GoogleTestAdapter/Core/Settings/SettingsWrapper.cs index 9ae4255e0..3baf762f9 100644 --- a/GoogleTestAdapter/Core/Settings/SettingsWrapper.cs +++ b/GoogleTestAdapter/Core/Settings/SettingsWrapper.cs @@ -25,8 +25,8 @@ public HelperFilesCache HelperFilesCache private get { return _cache; } set { - _cache = value; - _placeholderReplacer = new PlaceholderReplacer(() => SolutionDir, () => _currentSettings, HelperFilesCache); + _cache = value ?? throw new ArgumentNullException(nameof(HelperFilesCache)); + _placeholderReplacer = new PlaceholderReplacer(() => SolutionDir, () => _currentSettings, HelperFilesCache, HelperFilesCache.Logger); } } @@ -362,7 +362,7 @@ public string GetUserParametersForDiscovery(string executable) public virtual string BatchForTestSetup => _currentSettings.BatchForTestSetup ?? OptionBatchForTestSetupDefaultValue; public string GetBatchForTestSetup(string testDirectory, int threadId) - => _placeholderReplacer.ReplaceBatchPlaceholders(BatchForTestSetup, testDirectory, threadId); + => _placeholderReplacer.ReplaceSetupBatchPlaceholders(BatchForTestSetup, testDirectory, threadId); public const string OptionBatchForTestTeardown = "Test teardown batch file"; @@ -373,7 +373,7 @@ public string GetBatchForTestSetup(string testDirectory, int threadId) public virtual string BatchForTestTeardown => _currentSettings.BatchForTestTeardown ?? OptionBatchForTestTeardownDefaultValue; public string GetBatchForTestTeardown(string testDirectory, int threadId) - => _placeholderReplacer.ReplaceBatchPlaceholders(BatchForTestTeardown, testDirectory, + => _placeholderReplacer.ReplaceTeardownBatchPlaceholders(BatchForTestTeardown, testDirectory, threadId); diff --git a/GoogleTestAdapter/DiaResolver.Tests/DiaResolverTests.cs b/GoogleTestAdapter/DiaResolver.Tests/DiaResolverTests.cs index 752cd4dd4..2b7889c35 100644 --- a/GoogleTestAdapter/DiaResolver.Tests/DiaResolverTests.cs +++ b/GoogleTestAdapter/DiaResolver.Tests/DiaResolverTests.cs @@ -114,7 +114,7 @@ private void DoResolveTest(string executable, string filter, int expectedLocatio } locations.Should().HaveCountGreaterOrEqualTo(expectedLocations); - fakeLogger.GetMessages(Severity.Warning, Severity.Error).Count.Should().BeGreaterOrEqualTo(expectedErrorMessages); + fakeLogger.GetMessages(Severity.Warning, Severity.Error).Should().HaveCountGreaterOrEqualTo(expectedErrorMessages); } } diff --git a/GoogleTestAdapter/GoogleTestAdapter.sln b/GoogleTestAdapter/GoogleTestAdapter.sln index dfee3be1c..1f80df367 100644 --- a/GoogleTestAdapter/GoogleTestAdapter.sln +++ b/GoogleTestAdapter/GoogleTestAdapter.sln @@ -155,6 +155,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Donations", "Donations", "{ EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "LeakCheckTests", "..\SampleTests\LeakCheckTests\LeakCheckTests.vcxproj", "{41791F46-44CF-459B-9F77-049CE1C9D203}" EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "HelperFileTests", "..\SampleTests\HelperFileTests\HelperFileTests.vcxproj", "{034E4479-F9C0-435B-AE6C-96109DE161AB}" +EndProject Global GlobalSection(SharedMSBuildProjectFiles) = preSolution VsPackage.Shared\VsPackage.Shared.projitems*{ac75f34e-190b-402a-8c46-91b0fa02450f}*SharedItemsImports = 13 @@ -539,6 +541,16 @@ Global {41791F46-44CF-459B-9F77-049CE1C9D203}.Release|x64.Build.0 = Release|x64 {41791F46-44CF-459B-9F77-049CE1C9D203}.Release|x86.ActiveCfg = Release|Win32 {41791F46-44CF-459B-9F77-049CE1C9D203}.Release|x86.Build.0 = Release|Win32 + {034E4479-F9C0-435B-AE6C-96109DE161AB}.Debug|Any CPU.ActiveCfg = Debug|Win32 + {034E4479-F9C0-435B-AE6C-96109DE161AB}.Debug|x64.ActiveCfg = Debug|x64 + {034E4479-F9C0-435B-AE6C-96109DE161AB}.Debug|x64.Build.0 = Debug|x64 + {034E4479-F9C0-435B-AE6C-96109DE161AB}.Debug|x86.ActiveCfg = Debug|Win32 + {034E4479-F9C0-435B-AE6C-96109DE161AB}.Debug|x86.Build.0 = Debug|Win32 + {034E4479-F9C0-435B-AE6C-96109DE161AB}.Release|Any CPU.ActiveCfg = Release|Win32 + {034E4479-F9C0-435B-AE6C-96109DE161AB}.Release|x64.ActiveCfg = Release|x64 + {034E4479-F9C0-435B-AE6C-96109DE161AB}.Release|x64.Build.0 = Release|x64 + {034E4479-F9C0-435B-AE6C-96109DE161AB}.Release|x86.ActiveCfg = Release|Win32 + {034E4479-F9C0-435B-AE6C-96109DE161AB}.Release|x86.Build.0 = Release|Win32 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -576,6 +588,7 @@ Global {883EFE71-6B36-4813-B1DB-80D9B972B8D5} = {1F754A4D-BD42-4368-8B90-F3C03F24A2F3} {58B6EF41-D226-40A9-A959-0543270D572F} = {883EFE71-6B36-4813-B1DB-80D9B972B8D5} {41791F46-44CF-459B-9F77-049CE1C9D203} = {1FF56AF6-0ACE-4FE8-B802-4832703EC2DC} + {034E4479-F9C0-435B-AE6C-96109DE161AB} = {1FF56AF6-0ACE-4FE8-B802-4832703EC2DC} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {C942DDD5-B04E-4D57-BA9F-A444392C9480} diff --git a/GoogleTestAdapter/TestAdapter.Tests/Settings/HelperFileTests.cs b/GoogleTestAdapter/TestAdapter.Tests/Settings/HelperFileTests.cs new file mode 100644 index 000000000..c14e86d88 --- /dev/null +++ b/GoogleTestAdapter/TestAdapter.Tests/Settings/HelperFileTests.cs @@ -0,0 +1,61 @@ +using System.Linq; +using GoogleTestAdapter.Helpers; +using GoogleTestAdapter.ProcessExecution; +using GoogleTestAdapter.TestAdapter.ProcessExecution; +using GoogleTestAdapter.Tests.Common; +using Microsoft.VisualStudio.TestPlatform.ObjectModel; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Moq; +using static GoogleTestAdapter.Tests.Common.TestMetadata.TestCategories; + + +namespace GoogleTestAdapter.TestAdapter.Settings +{ + [TestClass] + public class HelperFileTests : TestAdapterTestsBase + { + private readonly Mock _mockDebuggerAttacher = new Mock(); + + [TestInitialize] + public override void SetUp() + { + base.SetUp(); + + _mockDebuggerAttacher.Reset(); + _mockDebuggerAttacher.Setup(a => a.AttachDebugger(It.IsAny())).Returns(true); + } + + [TestMethod] + [TestCategory(Integration)] + public void HelperFileTests_AdditionalParamsAreNotProvided_TestFails() + { + RunHelperFileTestsExecutable(); + + MockFrameworkHandle.Verify(h => h.RecordResult(It.Is(tr => + tr.DisplayName.Contains("HelperFileTests.ArchDirIsSet") && + tr.Outcome == TestOutcome.Failed)), Times.Once); + } + + [TestMethod] + [TestCategory(Integration)] + public void HelperFileTests_AdditionalParamsAreProvided_TestSucceeds() + { + MockOptions.Setup(o => o.AdditionalTestExecutionParam).Returns("$(LocalDebuggerCommandArguments)"); + + RunHelperFileTestsExecutable(); + + MockFrameworkHandle.Verify(h => h.RecordResult(It.Is(tr => + tr.DisplayName.Contains("HelperFileTests.ArchDirIsSet") && + tr.Outcome == TestOutcome.Passed)), Times.Once); + } + + private void RunHelperFileTestsExecutable() + { + var testCase = new GoogleTestDiscoverer(MockLogger.Object, TestEnvironment.Options, new ProcessExecutorFactory()) + .GetTestsFromExecutable(TestResources.HelperFilesTests_ReleaseX86).Single(); + var executor = + new TestExecutor(TestEnvironment.Logger, TestEnvironment.Options, _mockDebuggerAttacher.Object); + executor.RunTests(testCase.ToVsTestCase().Yield(), MockRunContext.Object, MockFrameworkHandle.Object); + } + } +} \ No newline at end of file diff --git a/GoogleTestAdapter/TestAdapter.Tests/TestAdapter.Tests.csproj b/GoogleTestAdapter/TestAdapter.Tests/TestAdapter.Tests.csproj index 284b7640e..cd0415a01 100644 --- a/GoogleTestAdapter/TestAdapter.Tests/TestAdapter.Tests.csproj +++ b/GoogleTestAdapter/TestAdapter.Tests/TestAdapter.Tests.csproj @@ -101,6 +101,7 @@ + diff --git a/GoogleTestAdapter/TestAdapter.Tests/TestExecutorTestsBase.cs b/GoogleTestAdapter/TestAdapter.Tests/TestExecutorTestsBase.cs index 512a39daa..c00173543 100644 --- a/GoogleTestAdapter/TestAdapter.Tests/TestExecutorTestsBase.cs +++ b/GoogleTestAdapter/TestAdapter.Tests/TestExecutorTestsBase.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.Configuration; using System.IO; using System.Linq; using FluentAssertions; diff --git a/GoogleTestAdapter/Tests.Common/TestResources.cs b/GoogleTestAdapter/Tests.Common/TestResources.cs index 2b844d6c0..22448dd70 100644 --- a/GoogleTestAdapter/Tests.Common/TestResources.cs +++ b/GoogleTestAdapter/Tests.Common/TestResources.cs @@ -64,6 +64,9 @@ public static class TestResources public const string LeakCheckTests_DebugX86 = SampleTestsBuildDir + @"Debug\LeakCheckTests_gta.exe"; public const string LeakCheckTests_ReleaseX86 = SampleTestsBuildDir + @"Release\LeakCheckTests_gta.exe"; + public const string HelperFileTests_DebugX86 = SampleTestsBuildDir + @"Debug\HelperFileTests_gta.exe"; + public const string HelperFilesTests_ReleaseX86 = SampleTestsBuildDir + @"Release\HelperFileTests_gta.exe"; + public const string SucceedingBatch = @"Tests\Returns0.bat"; public const string FailingBatch = @"Tests\Returns1.bat"; diff --git a/README.md b/README.md index cfdf8e75c..e6158d690 100644 --- a/README.md +++ b/README.md @@ -93,6 +93,11 @@ Note that due to the overriding hierarchy described above, you probably want to For reference, see a settings file [AllTestSettings.gta.runsettings](https://raw.githubusercontent.com/csoltenborn/GoogleTestAdapter/master/GoogleTestAdapter/Resources/AllTestSettings.gta.runsettings) containing all available settings, a more realistic solution settings file [SampleTests.gta.runsettings](https://raw.githubusercontent.com/csoltenborn/GoogleTestAdapter/master/SampleTests/SampleTests.gta.runsettings) as delivered with the SampleTests solution, and a user settings file [NonDeterministic.runsettings](https://raw.githubusercontent.com/csoltenborn/GoogleTestAdapter/master/SampleTests/NonDeterministic.runsettings) as used by GTA's end-to-end tests. The syntax of the GTA settings files (excluding the `` node) is specified by [this schema](https://raw.githubusercontent.com/csoltenborn/GoogleTestAdapter/master/GoogleTestAdapter/TestAdapter/GoogleTestAdapterSettings.xsd). +##### GTA helper files +GTA does not provide direct access to VS project settings such as *Project* > *Properties* > *Debugging* > *Environment*. Additionally, when run as NuGet dependency, GTA does not have access to information such as solution dir or Platform/Configuration a test executable has been build with. + +To overcome these problems, GTA supports so-called *helper files* which provide that information to GTA. + #### Assigning traits to tests diff --git a/SampleTests/HelperFileTests/HelperFileTests.cpp b/SampleTests/HelperFileTests/HelperFileTests.cpp new file mode 100644 index 000000000..662009e14 --- /dev/null +++ b/SampleTests/HelperFileTests/HelperFileTests.cpp @@ -0,0 +1,8 @@ +#include "gtest/gtest.h" + +extern std::string ARCH_DIR; + +TEST(HelperFileTests, ArchDirIsSet) +{ + ASSERT_STRNE("", ARCH_DIR.c_str()); +} \ No newline at end of file diff --git a/SampleTests/HelperFileTests/HelperFileTests.vcxproj b/SampleTests/HelperFileTests/HelperFileTests.vcxproj new file mode 100644 index 000000000..c05f1a962 --- /dev/null +++ b/SampleTests/HelperFileTests/HelperFileTests.vcxproj @@ -0,0 +1,214 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + + + + + + 15.0 + {034E4479-F9C0-435B-AE6C-96109DE161AB} + Win32Proj + HelperFileTests + 8.1 + + + + + Application + true + v100 + v110 + v120 + v140 + v141 + v142 + Unicode + + + Application + false + v100 + v110 + v120 + v140 + v141 + v142 + true + Unicode + + + Application + true + v100 + v110 + v120 + v140 + v141 + v142 + Unicode + + + Application + false + v100 + v110 + v120 + v140 + v141 + v142 + true + Unicode + + + + + + + + + + + + + + + + + + + + + true + $(ProjectName)_gta + $(EnlistmentRoot)out\binaries\$(SolutionName)\$(Configuration)$(PlatformSuffix)\ + $(EnlistmentRoot)out\intermediate\$(SolutionName)\$(Configuration)\$(MSBuildProjectName)\ + + + true + $(ProjectName)_gta + $(EnlistmentRoot)out\binaries\$(SolutionName)\$(Configuration)$(PlatformSuffix)\ + $(EnlistmentRoot)out\intermediate\$(SolutionName)\$(Configuration)\$(MSBuildProjectName)\ + + + false + $(ProjectName)_gta + $(EnlistmentRoot)out\binaries\$(SolutionName)\$(Configuration)$(PlatformSuffix)\ + $(EnlistmentRoot)out\intermediate\$(SolutionName)\$(Configuration)\$(MSBuildProjectName)\ + + + false + $(ProjectName)_gta + $(EnlistmentRoot)out\binaries\$(SolutionName)\$(Configuration)$(PlatformSuffix)\ + $(EnlistmentRoot)out\intermediate\$(SolutionName)\$(Configuration)\$(MSBuildProjectName)\ + + + + NotUsing + Level3 + Disabled + true + _VARIADIC_MAX=10;WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + false + pch.h + $(ProjectDir)..\3rdparty\gtest-1.8.1\fused-src;%(AdditionalIncludeDirectories) + + + Console + DebugFull + true + + + echo SolutionPath=$(SolutionPath)::GTA::SolutionDir=$(SolutionDir)::GTA::PlatformName=$(PlatformName)::GTA::ConfigurationName=$(ConfigurationName)::GTA::LocalDebuggerCommandArguments=$(LocalDebuggerCommandArguments) > $(TargetPath).gta_settings_helper + + + + + NotUsing + Level3 + Disabled + true + _VARIADIC_MAX=10;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + false + pch.h + $(ProjectDir)..\3rdparty\gtest-1.8.1\fused-src;%(AdditionalIncludeDirectories) + + + Console + DebugFull + true + + + echo SolutionPath=$(SolutionPath)::GTA::SolutionDir=$(SolutionDir)::GTA::PlatformName=$(PlatformName)::GTA::ConfigurationName=$(ConfigurationName)::GTA::LocalDebuggerCommandArguments=$(LocalDebuggerCommandArguments) > $(TargetPath).gta_settings_helper + + + + + NotUsing + Level3 + MaxSpeed + true + true + true + _VARIADIC_MAX=10;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + false + pch.h + $(ProjectDir)..\3rdparty\gtest-1.8.1\fused-src;%(AdditionalIncludeDirectories) + + + Console + true + true + DebugFull + true + + + echo SolutionPath=$(SolutionPath)::GTA::SolutionDir=$(SolutionDir)::GTA::PlatformName=$(PlatformName)::GTA::ConfigurationName=$(ConfigurationName)::GTA::LocalDebuggerCommandArguments=$(LocalDebuggerCommandArguments) > $(TargetPath).gta_settings_helper + + + + + NotUsing + Level3 + MaxSpeed + true + true + true + _VARIADIC_MAX=10;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + false + pch.h + $(ProjectDir)..\3rdparty\gtest-1.8.1\fused-src;%(AdditionalIncludeDirectories) + + + Console + true + true + DebugFull + true + + + echo SolutionPath=$(SolutionPath)::GTA::SolutionDir=$(SolutionDir)::GTA::PlatformName=$(PlatformName)::GTA::ConfigurationName=$(ConfigurationName)::GTA::LocalDebuggerCommandArguments=$(LocalDebuggerCommandArguments) > $(TargetPath).gta_settings_helper + + + + + + \ No newline at end of file diff --git a/SampleTests/HelperFileTests/HelperFileTests.vcxproj.filters b/SampleTests/HelperFileTests/HelperFileTests.vcxproj.filters new file mode 100644 index 000000000..3874ce680 --- /dev/null +++ b/SampleTests/HelperFileTests/HelperFileTests.vcxproj.filters @@ -0,0 +1,28 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;hm;inl;inc;ipp;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Quelldateien + + + Quelldateien + + + Quelldateien + + + \ No newline at end of file diff --git a/SampleTests/HelperFileTests/Main.cpp b/SampleTests/HelperFileTests/Main.cpp new file mode 100644 index 000000000..56c205059 --- /dev/null +++ b/SampleTests/HelperFileTests/Main.cpp @@ -0,0 +1,22 @@ +#include "string.h" +#include "gtest/gtest.h" + +std::string ARCH_DIR; + +int main(int argc, char ** argv) +{ + std::string prefix_arch_dir("-arch_dir="); + + for (int i = 0; i < argc; i++) + { + if (strncmp(argv[i], prefix_arch_dir.c_str(), strlen(prefix_arch_dir.c_str())) == 0) + { + std::string arch_dir(argv[i]); + arch_dir.erase(0, strlen(prefix_arch_dir.c_str())); + ARCH_DIR = arch_dir; + } + } + + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} \ No newline at end of file diff --git a/SampleTests/LeakCheckTests/LeakCheckTests.vcxproj b/SampleTests/LeakCheckTests/LeakCheckTests.vcxproj index cf7bfb250..ba4065f17 100644 --- a/SampleTests/LeakCheckTests/LeakCheckTests.vcxproj +++ b/SampleTests/LeakCheckTests/LeakCheckTests.vcxproj @@ -30,26 +30,30 @@ Application true - v141 + v141 + v142 Unicode Application false - v141 + v141 + v142 true Unicode Application true - v141 + v141 + v142 Unicode Application false - v141 + v141 + v142 true Unicode diff --git a/SampleTests/SampleTests.gta.runsettings b/SampleTests/SampleTests.gta.runsettings index c17fd267d..c72947290 100644 --- a/SampleTests/SampleTests.gta.runsettings +++ b/SampleTests/SampleTests.gta.runsettings @@ -1,25 +1,28 @@ - - - - -testdirectory=$(TestDir) - $(SolutionDir)Tests\Returns0.bat - $(SolutionDir)Tests\Returns1.bat - $(SolutionDir) - - - - - .*Tests.*_gta.exe - - - 60 - - - MemoryLeakTest - -is_run_by_gta - - - + + + + -testdirectory=$(TestDir) + $(SolutionDir)Tests\Returns0.bat + $(SolutionDir)Tests\Returns1.bat + $(SolutionDir) + + + + + .*Tests.*_gta.exe + + + 60 + + + MemoryLeakTest + -is_run_by_gta + + + $(LocalDebuggerCommandArguments) + + + \ No newline at end of file diff --git a/SampleTests/SampleTests.sln b/SampleTests/SampleTests.sln index 18b43f429..a6fc63617 100644 --- a/SampleTests/SampleTests.sln +++ b/SampleTests/SampleTests.sln @@ -33,6 +33,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "DllProject", "DllProject\Dl EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "LeakCheckTests", "LeakCheckTests\LeakCheckTests.vcxproj", "{41791F46-44CF-459B-9F77-049CE1C9D203}" EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "HelperFileTests", "HelperFileTests\HelperFileTests.vcxproj", "{034E4479-F9C0-435B-AE6C-96109DE161AB}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -133,6 +135,16 @@ Global {41791F46-44CF-459B-9F77-049CE1C9D203}.Release|Win32.Build.0 = Release|Win32 {41791F46-44CF-459B-9F77-049CE1C9D203}.Release|x64.ActiveCfg = Release|x64 {41791F46-44CF-459B-9F77-049CE1C9D203}.Release|x64.Build.0 = Release|x64 + {034E4479-F9C0-435B-AE6C-96109DE161AB}.Debug|Any CPU.ActiveCfg = Debug|Win32 + {034E4479-F9C0-435B-AE6C-96109DE161AB}.Debug|Win32.ActiveCfg = Debug|Win32 + {034E4479-F9C0-435B-AE6C-96109DE161AB}.Debug|Win32.Build.0 = Debug|Win32 + {034E4479-F9C0-435B-AE6C-96109DE161AB}.Debug|x64.ActiveCfg = Debug|x64 + {034E4479-F9C0-435B-AE6C-96109DE161AB}.Debug|x64.Build.0 = Debug|x64 + {034E4479-F9C0-435B-AE6C-96109DE161AB}.Release|Any CPU.ActiveCfg = Release|Win32 + {034E4479-F9C0-435B-AE6C-96109DE161AB}.Release|Win32.ActiveCfg = Release|Win32 + {034E4479-F9C0-435B-AE6C-96109DE161AB}.Release|Win32.Build.0 = Release|Win32 + {034E4479-F9C0-435B-AE6C-96109DE161AB}.Release|x64.ActiveCfg = Release|x64 + {034E4479-F9C0-435B-AE6C-96109DE161AB}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE From 484e6a54363837e47fd57957a7937463ec669a5b Mon Sep 17 00:00:00 2001 From: Christian Soltenborn Date: Wed, 17 Apr 2019 07:14:01 +0200 Subject: [PATCH 6/9] debugging test fail --- appveyor.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/appveyor.yml b/appveyor.yml index 51bd6dfbe..adc1ceef9 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -12,7 +12,7 @@ install: nuget install secure-file -ExcludeVersion NuGetPackages\secure-file\tools\secure-file -decrypt GoogleTestAdapter\Keys\Key_Release.snk.enc -secret $env:RELEASE_KEY_PASSWORD } else { - # $blockRdp = $true + $blockRdp = $true iex ((new-object net.webclient).DownloadString('https://raw.githubusercontent.com/appveyor/ci/master/scripts/enable-rdp.ps1')) } before_build: From 9d0f6a6a83d05b27d1e9111cc172e42511041efe Mon Sep 17 00:00:00 2001 From: Christian Soltenborn Date: Wed, 17 Apr 2019 07:51:34 +0200 Subject: [PATCH 7/9] fixed test for CI setting --- .../TestAdapter.Tests/Settings/HelperFileTests.cs | 6 +++--- SampleTests/HelperFileTests/HelperFileTests.cpp | 6 +++--- SampleTests/HelperFileTests/HelperFileTests.vcxproj | 8 ++++---- SampleTests/HelperFileTests/Main.cpp | 7 ++++--- SampleTests/SampleTests.gta.runsettings | 2 +- appveyor.yml | 2 +- 6 files changed, 16 insertions(+), 15 deletions(-) diff --git a/GoogleTestAdapter/TestAdapter.Tests/Settings/HelperFileTests.cs b/GoogleTestAdapter/TestAdapter.Tests/Settings/HelperFileTests.cs index c14e86d88..2bc61ee8c 100644 --- a/GoogleTestAdapter/TestAdapter.Tests/Settings/HelperFileTests.cs +++ b/GoogleTestAdapter/TestAdapter.Tests/Settings/HelperFileTests.cs @@ -32,7 +32,7 @@ public void HelperFileTests_AdditionalParamsAreNotProvided_TestFails() RunHelperFileTestsExecutable(); MockFrameworkHandle.Verify(h => h.RecordResult(It.Is(tr => - tr.DisplayName.Contains("HelperFileTests.ArchDirIsSet") && + tr.DisplayName.Contains("HelperFileTests.TheTargetIsSet") && tr.Outcome == TestOutcome.Failed)), Times.Once); } @@ -40,12 +40,12 @@ public void HelperFileTests_AdditionalParamsAreNotProvided_TestFails() [TestCategory(Integration)] public void HelperFileTests_AdditionalParamsAreProvided_TestSucceeds() { - MockOptions.Setup(o => o.AdditionalTestExecutionParam).Returns("$(LocalDebuggerCommandArguments)"); + MockOptions.Setup(o => o.AdditionalTestExecutionParam).Returns("-TheTarget=$(TheTarget)"); RunHelperFileTestsExecutable(); MockFrameworkHandle.Verify(h => h.RecordResult(It.Is(tr => - tr.DisplayName.Contains("HelperFileTests.ArchDirIsSet") && + tr.DisplayName.Contains("HelperFileTests.TheTargetIsSet") && tr.Outcome == TestOutcome.Passed)), Times.Once); } diff --git a/SampleTests/HelperFileTests/HelperFileTests.cpp b/SampleTests/HelperFileTests/HelperFileTests.cpp index 662009e14..2aa21c38a 100644 --- a/SampleTests/HelperFileTests/HelperFileTests.cpp +++ b/SampleTests/HelperFileTests/HelperFileTests.cpp @@ -1,8 +1,8 @@ #include "gtest/gtest.h" -extern std::string ARCH_DIR; +extern std::string THE_TARGET; -TEST(HelperFileTests, ArchDirIsSet) +TEST(HelperFileTests, TheTargetIsSet) { - ASSERT_STRNE("", ARCH_DIR.c_str()); + ASSERT_STREQ("HelperFileTests_gta.exe", THE_TARGET.c_str()); } \ No newline at end of file diff --git a/SampleTests/HelperFileTests/HelperFileTests.vcxproj b/SampleTests/HelperFileTests/HelperFileTests.vcxproj index c05f1a962..d1c529b87 100644 --- a/SampleTests/HelperFileTests/HelperFileTests.vcxproj +++ b/SampleTests/HelperFileTests/HelperFileTests.vcxproj @@ -137,7 +137,7 @@ true - echo SolutionPath=$(SolutionPath)::GTA::SolutionDir=$(SolutionDir)::GTA::PlatformName=$(PlatformName)::GTA::ConfigurationName=$(ConfigurationName)::GTA::LocalDebuggerCommandArguments=$(LocalDebuggerCommandArguments) > $(TargetPath).gta_settings_helper + echo SolutionPath=$(SolutionPath)::GTA::SolutionDir=$(SolutionDir)::GTA::PlatformName=$(PlatformName)::GTA::ConfigurationName=$(ConfigurationName)::GTA::TheTarget=$(TargetFileName) > $(TargetPath).gta_settings_helper @@ -157,7 +157,7 @@ true - echo SolutionPath=$(SolutionPath)::GTA::SolutionDir=$(SolutionDir)::GTA::PlatformName=$(PlatformName)::GTA::ConfigurationName=$(ConfigurationName)::GTA::LocalDebuggerCommandArguments=$(LocalDebuggerCommandArguments) > $(TargetPath).gta_settings_helper + echo SolutionPath=$(SolutionPath)::GTA::SolutionDir=$(SolutionDir)::GTA::PlatformName=$(PlatformName)::GTA::ConfigurationName=$(ConfigurationName)::GTA::TheTarget=$(TargetFileName) > $(TargetPath).gta_settings_helper @@ -181,7 +181,7 @@ true - echo SolutionPath=$(SolutionPath)::GTA::SolutionDir=$(SolutionDir)::GTA::PlatformName=$(PlatformName)::GTA::ConfigurationName=$(ConfigurationName)::GTA::LocalDebuggerCommandArguments=$(LocalDebuggerCommandArguments) > $(TargetPath).gta_settings_helper + echo SolutionPath=$(SolutionPath)::GTA::SolutionDir=$(SolutionDir)::GTA::PlatformName=$(PlatformName)::GTA::ConfigurationName=$(ConfigurationName)::GTA::TheTarget=$(TargetFileName) > $(TargetPath).gta_settings_helper @@ -205,7 +205,7 @@ true - echo SolutionPath=$(SolutionPath)::GTA::SolutionDir=$(SolutionDir)::GTA::PlatformName=$(PlatformName)::GTA::ConfigurationName=$(ConfigurationName)::GTA::LocalDebuggerCommandArguments=$(LocalDebuggerCommandArguments) > $(TargetPath).gta_settings_helper + echo SolutionPath=$(SolutionPath)::GTA::SolutionDir=$(SolutionDir)::GTA::PlatformName=$(PlatformName)::GTA::ConfigurationName=$(ConfigurationName)::GTA::TheTarget=$(TargetFileName) > $(TargetPath).gta_settings_helper diff --git a/SampleTests/HelperFileTests/Main.cpp b/SampleTests/HelperFileTests/Main.cpp index 56c205059..d4de2e593 100644 --- a/SampleTests/HelperFileTests/Main.cpp +++ b/SampleTests/HelperFileTests/Main.cpp @@ -1,19 +1,20 @@ #include "string.h" #include "gtest/gtest.h" -std::string ARCH_DIR; +std::string THE_TARGET; int main(int argc, char ** argv) { - std::string prefix_arch_dir("-arch_dir="); + std::string prefix_arch_dir("-TheTarget="); for (int i = 0; i < argc; i++) { + std::string s = argv[i]; if (strncmp(argv[i], prefix_arch_dir.c_str(), strlen(prefix_arch_dir.c_str())) == 0) { std::string arch_dir(argv[i]); arch_dir.erase(0, strlen(prefix_arch_dir.c_str())); - ARCH_DIR = arch_dir; + THE_TARGET = arch_dir; } } diff --git a/SampleTests/SampleTests.gta.runsettings b/SampleTests/SampleTests.gta.runsettings index c72947290..4923c6269 100644 --- a/SampleTests/SampleTests.gta.runsettings +++ b/SampleTests/SampleTests.gta.runsettings @@ -21,7 +21,7 @@ -is_run_by_gta - $(LocalDebuggerCommandArguments) + -TheTarget=$(TheTarget) diff --git a/appveyor.yml b/appveyor.yml index adc1ceef9..51bd6dfbe 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -12,7 +12,7 @@ install: nuget install secure-file -ExcludeVersion NuGetPackages\secure-file\tools\secure-file -decrypt GoogleTestAdapter\Keys\Key_Release.snk.enc -secret $env:RELEASE_KEY_PASSWORD } else { - $blockRdp = $true + # $blockRdp = $true iex ((new-object net.webclient).DownloadString('https://raw.githubusercontent.com/appveyor/ci/master/scripts/enable-rdp.ps1')) } before_build: From 4fde9510c2287070b590711c93156ce8d025a380 Mon Sep 17 00:00:00 2001 From: Christian Soltenborn Date: Fri, 19 Apr 2019 08:35:32 +0200 Subject: [PATCH 8/9] added documentation --- .../Core.Tests/Core.Tests.csproj | 2 ++ .../Settings/HelperFilesCacheTests.cs | 4 +-- GoogleTestAdapter/Core.Tests/packages.config | 2 -- .../Resources/ReleaseNotes/0.15.0.md | 1 - .../Resources/ReleaseNotes/0.16.0.md | 3 +- README.md | 31 +++++++++++++------ 6 files changed, 28 insertions(+), 15 deletions(-) diff --git a/GoogleTestAdapter/Core.Tests/Core.Tests.csproj b/GoogleTestAdapter/Core.Tests/Core.Tests.csproj index 5cc726aca..28a9acec8 100644 --- a/GoogleTestAdapter/Core.Tests/Core.Tests.csproj +++ b/GoogleTestAdapter/Core.Tests/Core.Tests.csproj @@ -18,6 +18,8 @@ False UnitTest + + true diff --git a/GoogleTestAdapter/Core.Tests/Settings/HelperFilesCacheTests.cs b/GoogleTestAdapter/Core.Tests/Settings/HelperFilesCacheTests.cs index 45cd3d1fa..32a43b18f 100644 --- a/GoogleTestAdapter/Core.Tests/Settings/HelperFilesCacheTests.cs +++ b/GoogleTestAdapter/Core.Tests/Settings/HelperFilesCacheTests.cs @@ -17,7 +17,7 @@ public class HelperFilesCacheTests : TestsBase public void GetReplacementsMap_NoFile_EmptyDictionary() { string executable = TestResources.Tests_DebugX86; - var extraSettings = $"{executable}{HelperFilesCache.HelperFileEnding}"; + var extraSettings = HelperFilesCache.GetHelperFile(executable); extraSettings.AsFileInfo().Should().NotExist(); var cache = new HelperFilesCache(MockLogger.Object); @@ -113,7 +113,7 @@ public void GetReplacementsMap_OnlyTwoSeparators_ProperDictionary() private void DoTest(string content, Action> assertions, string executable = TestResources.Tests_DebugX86) { - var extraSettings = $"{executable}{HelperFilesCache.HelperFileEnding}"; + var extraSettings = HelperFilesCache.GetHelperFile(executable); try { extraSettings.AsFileInfo().Should().NotExist(); diff --git a/GoogleTestAdapter/Core.Tests/packages.config b/GoogleTestAdapter/Core.Tests/packages.config index 31fd4c297..87cac3d92 100644 --- a/GoogleTestAdapter/Core.Tests/packages.config +++ b/GoogleTestAdapter/Core.Tests/packages.config @@ -3,8 +3,6 @@ - - diff --git a/GoogleTestAdapter/VsPackage.GTA/Resources/ReleaseNotes/0.15.0.md b/GoogleTestAdapter/VsPackage.GTA/Resources/ReleaseNotes/0.15.0.md index d5588a0c9..55460be96 100644 --- a/GoogleTestAdapter/VsPackage.GTA/Resources/ReleaseNotes/0.15.0.md +++ b/GoogleTestAdapter/VsPackage.GTA/Resources/ReleaseNotes/0.15.0.md @@ -1,5 +1,4 @@ * enhancement: setting the new option [*Exit code test name*](https://github.com/csoltenborn/GoogleTestAdapter#evaluating_exit_code) results in an additional test per test executable that passes if the executable's exit code is 0, and fails otherwise. Additionally, GTA parses the test executable's output for certain [tokens](https://github.com/csoltenborn/GoogleTestAdapter#evaluating_exit_code_tokens) which allow to influence the test's outcome and message. One interesting use case for this is memory leak detection; a complete, reusable [example](https://github.com/csoltenborn/GoogleTestAdapter#evaluating_exit_code_leak_example) is provided as part of GTA's [SampleTests solution](https://github.com/csoltenborn/GoogleTestAdapter/tree/master/SampleTests) ([#266](https://github.com/csoltenborn/GoogleTestAdapter/issues/266), thanks to [alfredskpoon](https://github.com/alfredskpoon) for report, example code, and testing) -* usability: reorganized options ([#271](https://github.com/csoltenborn/GoogleTestAdapter/issues/271)) * enhancement: the GTA extension is now loaded asynchronically by Visual Studio; the drawback is that support for Visual Studio 2012 had to be dropped ([#243](https://github.com/csoltenborn/GoogleTestAdapter/issues/243)) * enhancement: the *Show release notes* option has been removed, and this release notes dialog has been slightly changed and now has very funny buttons ([#270](https://github.com/csoltenborn/GoogleTestAdapter/issues/270)) * maintenance: reduced code duplication of streaming and batch output parsers ([#263](https://github.com/csoltenborn/GoogleTestAdapter/issues/263)) diff --git a/GoogleTestAdapter/VsPackage.GTA/Resources/ReleaseNotes/0.16.0.md b/GoogleTestAdapter/VsPackage.GTA/Resources/ReleaseNotes/0.16.0.md index 7dc9acfb9..b8e2f3928 100644 --- a/GoogleTestAdapter/VsPackage.GTA/Resources/ReleaseNotes/0.16.0.md +++ b/GoogleTestAdapter/VsPackage.GTA/Resources/ReleaseNotes/0.16.0.md @@ -1 +1,2 @@ -* usability: reorganized options ([#271](https://github.com/csoltenborn/GoogleTestAdapter/issues/271)) +* enhancement: [*settings helper files*]([tokens](https://github.com/csoltenborn/GoogleTestAdapter#settings_helper_files)) allow to provide additional information to GTA at test discovery and execution time ([example project](https://github.com/csoltenborn/GoogleTestAdapter/tree/master/SampleTests/DllProject)). This feature can be used to remove the need for a special configuration file for CI, or to reduce configuration redundancy between project and GTA settings ([#278](https://github.com/csoltenborn/GoogleTestAdapter/issues/278)) +* usability: reorganized options ([#271](https://github.com/csoltenborn/GoogleTestAdapter/issues/271)) \ No newline at end of file diff --git a/README.md b/README.md index e6158d690..98db67bf8 100644 --- a/README.md +++ b/README.md @@ -93,10 +93,20 @@ Note that due to the overriding hierarchy described above, you probably want to For reference, see a settings file [AllTestSettings.gta.runsettings](https://raw.githubusercontent.com/csoltenborn/GoogleTestAdapter/master/GoogleTestAdapter/Resources/AllTestSettings.gta.runsettings) containing all available settings, a more realistic solution settings file [SampleTests.gta.runsettings](https://raw.githubusercontent.com/csoltenborn/GoogleTestAdapter/master/SampleTests/SampleTests.gta.runsettings) as delivered with the SampleTests solution, and a user settings file [NonDeterministic.runsettings](https://raw.githubusercontent.com/csoltenborn/GoogleTestAdapter/master/SampleTests/NonDeterministic.runsettings) as used by GTA's end-to-end tests. The syntax of the GTA settings files (excluding the `` node) is specified by [this schema](https://raw.githubusercontent.com/csoltenborn/GoogleTestAdapter/master/GoogleTestAdapter/TestAdapter/GoogleTestAdapterSettings.xsd). -##### GTA helper files +##### Settings helper files GTA does not provide direct access to VS project settings such as *Project* > *Properties* > *Debugging* > *Environment*. Additionally, when run as NuGet dependency, GTA does not have access to information such as solution dir or Platform/Configuration a test executable has been build with. -To overcome these problems, GTA supports so-called *helper files* which provide that information to GTA. +To overcome these problems, GTA supports so-called *settings helper files* which provide that information to GTA. Helper files are usually generated as part of the build, e.g. within a post-build event, which might look like this: + +``` +echo SolutionPath=$(SolutionPath)::GTA::SolutionDir=$(SolutionDir)::GTA::PlatformName=$(PlatformName)::GTA::ConfigurationName=$(ConfigurationName)::GTA::TheTarget=$(TargetFileName) > $(TargetPath).gta_settings_helper +``` + +This command generates a file `$(TargetPath).gta_settings_helper` (where `$(TargetPath)` is the path of the final test executable) containing key/value pairs separated by `::GTA::`. At file generation time, the VS macros will be replaced by the actual values, which can then in turn be used as placeholders within the GTA settings. In particular, the helper file generated by the above command will +* make sure that the `$(SolutionDir)`, `$(PlatformName)`, and `$(ConfigurationName)` placeholders will be available even if the runtime environment does not provide them (CI) and +* will provide the value of the VS `$(TargetFileName)` macro to GTA, where it can be referred to as the `$(TheTarget)` placeholder + +Note that the settings helper files need to be located within the same folder as their corresponding test executable, and must have the test executable's name concatenated with the `.gta_settings_helper` ending (make sure to ignore these files in version control if necessary). #### Assigning traits to tests @@ -188,7 +198,7 @@ GTA runs in three different environments: * Within Visual Studio and installed via NuGet (i.e., pulled via a project's NuGet dependencies) * Within `VsTestConsole.exe` (making use of the `/UseVsixExtensions:true` or the `/TestAdapterPath:` options) -For technical reasons, not all features are available in all environments; refer to the table below for details. +For technical reasons, not all features are available in all environments by default; refer to the table below for details. | Feature | VS/VSIX | VS/NuGet | VsTest.Console |--- |:---:|:---:|:---: @@ -201,18 +211,21 @@ For technical reasons, not all features are available in all environments; refer | - Solution test config file | yes | no | no | - User test config file | yes[1](#vs_settings) | yes[1](#vs_settings) | yes[2](#test_settings) | Placeholders | | | -| - `$(SolutionDir)` | yes | yes[3](#only_test_execution) | no -| - `$(PlatformName)` | yes | no | no -| - `$(ConfigurationName)` | yes | no | no +| - `$(SolutionDir)` | yes | yes[3](#helper_files), [4](#also_test_execution) | yes[3](#helper_files) +| - `$(PlatformName)` | yes | yes[3](#helper_files) | yes[3](#helper_files) +| - `$(ConfigurationName)` | yes | yes[3](#helper_files) | yes[3](#helper_files) | - `$(ExecutableDir)` | yes | yes | yes | - `$(Executable)` | yes | yes | yes -| - `$(TestDir)`[3](#only_test_execution) | yes | yes | yes -| - `$(ThreadId)`[3](#only_test_execution) | yes | yes | yes +| - `$(TestDir)`[5](#only_test_execution) | yes | yes | yes +| - `$(ThreadId)`[5](#only_test_execution) | yes | yes | yes +| - Additional placeholders | yes[3](#helper_files) | yes[3](#helper_files) | yes[3](#helper_files) | - Environment variables | yes | yes | yes 1: Via *Test/Test Settings/Select Test Settings File*
2: Via `/Settings` option
-3: Only during test execution; placeholders are removed in discovery mode +3: If [*settings helper files*](#settings_helper_files) are provided
+4: During test execution, placeholders are available even without settings helper files
+5: Only during test execution; placeholders are removed in discovery mode ### External resources From 97d5b7139181f2cda677826f0baa6a91996e6afe Mon Sep 17 00:00:00 2001 From: Christian Soltenborn Date: Sat, 27 Apr 2019 10:02:45 +0200 Subject: [PATCH 9/9] fixed merging problem --- .../TestAdapter.Tests/Settings/HelperFileTests.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/GoogleTestAdapter/TestAdapter.Tests/Settings/HelperFileTests.cs b/GoogleTestAdapter/TestAdapter.Tests/Settings/HelperFileTests.cs index 2bc61ee8c..d7a9e2808 100644 --- a/GoogleTestAdapter/TestAdapter.Tests/Settings/HelperFileTests.cs +++ b/GoogleTestAdapter/TestAdapter.Tests/Settings/HelperFileTests.cs @@ -1,4 +1,5 @@ using System.Linq; +using GoogleTestAdapter.Common; using GoogleTestAdapter.Helpers; using GoogleTestAdapter.ProcessExecution; using GoogleTestAdapter.TestAdapter.ProcessExecution; @@ -22,7 +23,7 @@ public override void SetUp() base.SetUp(); _mockDebuggerAttacher.Reset(); - _mockDebuggerAttacher.Setup(a => a.AttachDebugger(It.IsAny())).Returns(true); + _mockDebuggerAttacher.Setup(a => a.AttachDebugger(It.IsAny(), It.IsAny())).Returns(true); } [TestMethod]