diff --git a/src/Microsoft.DotNet.ProjectJsonMigration/Rules/MigrateRuntimeOptionsRule.cs b/src/Microsoft.DotNet.ProjectJsonMigration/Rules/MigrateRuntimeOptionsRule.cs index 3b808dd6a..5f7d63e3b 100644 --- a/src/Microsoft.DotNet.ProjectJsonMigration/Rules/MigrateRuntimeOptionsRule.cs +++ b/src/Microsoft.DotNet.ProjectJsonMigration/Rules/MigrateRuntimeOptionsRule.cs @@ -1,6 +1,8 @@ // Copyright (c) .NET Foundation and contributors. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. +using Microsoft.DotNet.ProjectJsonMigration.Transforms; +using Newtonsoft.Json.Linq; using System; using System.IO; @@ -8,8 +10,16 @@ namespace Microsoft.DotNet.ProjectJsonMigration.Rules { internal class MigrateRuntimeOptionsRule : IMigrationRule { + private const string ConfigPropertiesTokenName = "configProperties"; + private const string SystemGCServerTokenName = "System.GC.Server"; + private readonly ITransformApplicator _transformApplicator; private static readonly string s_runtimeOptionsFileName = "runtimeconfig.template.json"; + public MigrateRuntimeOptionsRule(ITransformApplicator transformApplicator = null) + { + _transformApplicator = transformApplicator ?? new TransformApplicator(); + } + public void Apply(MigrationSettings migrationSettings, MigrationRuleInputs migrationRuleInputs) { var projectContext = migrationRuleInputs.DefaultProjectContext; @@ -24,8 +34,69 @@ namespace Microsoft.DotNet.ProjectJsonMigration.Rules String.Format(LocalizableStrings.ProjAlreadyExistsError, outputRuntimeOptionsFile)).Throw(); } - File.WriteAllText(outputRuntimeOptionsFile, raw); + var runtimeOptions = JObject.Parse(raw); + if (HasServerGCProperty(runtimeOptions)) + { + bool serverGCValue = GetServerGCValue(runtimeOptions); + + if (!IsServerGCValueInjectedBySdk(serverGCValue, projectContext.ProjectFile.GetProjectType())) + { + var propertyTransform = new AddPropertyTransform( + "ServerGarbageCollection", + gcValue => gcValue.ToString().ToLower(), + gcValue => true); + + _transformApplicator.Execute( + propertyTransform.Transform(serverGCValue), + migrationRuleInputs.CommonPropertyGroup, + true); + } + + RemoveServerGCProperty(runtimeOptions); + } + + if (runtimeOptions.HasValues) + { + File.WriteAllText(outputRuntimeOptionsFile, runtimeOptions.ToString()); + } } - } + } + + private bool IsServerGCValueInjectedBySdk(bool serverGCValue, ProjectType projectType) + { + return (projectType == ProjectType.Web && serverGCValue); + } + + private bool HasServerGCProperty(JObject runtimeOptions) + { + bool hasServerGCProperty = false; + + var configProperties = runtimeOptions.Value(ConfigPropertiesTokenName); + if (configProperties != null) + { + hasServerGCProperty = configProperties[SystemGCServerTokenName] != null; + } + + return hasServerGCProperty; + } + + private bool GetServerGCValue(JObject runtimeOptions) + { + var configProperties = runtimeOptions[ConfigPropertiesTokenName]; + return configProperties.Value(SystemGCServerTokenName); + } + + private void RemoveServerGCProperty(JObject runtimeOptions) + { + var configProperties = runtimeOptions.Value(ConfigPropertiesTokenName); + if (configProperties != null) + { + configProperties.Remove(SystemGCServerTokenName); + if (!configProperties.HasValues) + { + runtimeOptions.Remove(ConfigPropertiesTokenName); + } + } + } } } diff --git a/test/Microsoft.DotNet.ProjectJsonMigration.Tests/Rules/GivenThatIWantToMigrateRuntimeOptions.cs b/test/Microsoft.DotNet.ProjectJsonMigration.Tests/Rules/GivenThatIWantToMigrateRuntimeOptions.cs index 7dfad829a..6619b436c 100644 --- a/test/Microsoft.DotNet.ProjectJsonMigration.Tests/Rules/GivenThatIWantToMigrateRuntimeOptions.cs +++ b/test/Microsoft.DotNet.ProjectJsonMigration.Tests/Rules/GivenThatIWantToMigrateRuntimeOptions.cs @@ -23,7 +23,7 @@ namespace Microsoft.DotNet.ProjectJsonMigration.Tests private static readonly string s_runtimeConfigFileName = "runtimeconfig.template.json"; [Fact] - public void RuntimeOptions_are_copied_from_projectJson_to_runtimeconfig_template_json_file() + public void RuntimeOptionsAreCopiedFromProjectJsonToRuntimeConfigTemplateJsonFile() { var testInstance = TestAssetsManager.CreateTestInstance("TestAppWithRuntimeOptions"); var projectDir = testInstance.Path; @@ -47,7 +47,7 @@ namespace Microsoft.DotNet.ProjectJsonMigration.Tests } [Fact] - public void Migrating_ProjectJson_with_no_RuntimeOptions_produces_no_runtimeconfig_template_json_file() + public void MigratingProjectJsonWithNoRuntimeOptionsProducesNoRuntimeConfigTemplateJsonFile() { var testInstance = TestAssetsManager.CreateTestInstance("PJTestAppSimple"); var projectDir = testInstance.Path; @@ -62,5 +62,173 @@ namespace Microsoft.DotNet.ProjectJsonMigration.Tests File.Exists(migratedRuntimeOptionsPath).Should().BeFalse(); } + + [Fact] + public void MigratingProjectJsonWithOnlyServerGCRuntimeOptionsProducesNoRuntimeConfigTemplateJsonFile() + { + var testDirectory = Temp.CreateDirectory().Path; + + var pj = @" + { + ""runtimeOptions"": { + ""configProperties"": { + ""System.GC.Server"": true + } + } + }"; + + RunMigrateRuntimeOptionsRulePj(pj, testDirectory); + var migratedRuntimeOptionsPath = Path.Combine(testDirectory, s_runtimeConfigFileName); + File.Exists(migratedRuntimeOptionsPath).Should().BeFalse(); + } + + [Fact] + public void MigratingProjectJsonWithServerGCAndOtherConfigPropertiesProducesRuntimeConfigTemplateJsonFile() + { + var testDirectory = Temp.CreateDirectory().Path; + + var pj = @" + { + ""runtimeOptions"": { + ""configProperties"": { + ""System.GC.Server"": false, + ""Other"": false + } + } + }"; + + RunMigrateRuntimeOptionsRulePj(pj, testDirectory); + var migratedRuntimeOptionsPath = Path.Combine(testDirectory, s_runtimeConfigFileName); + File.Exists(migratedRuntimeOptionsPath).Should().BeTrue(); + + var root = JObject.Parse(File.ReadAllText(migratedRuntimeOptionsPath)); + var configProperties = root.Value("configProperties"); + configProperties.Should().NotBeNull(); + configProperties["System.GC.Server"].Should().BeNull(); + configProperties["Other"].Should().NotBeNull(); + } + + [Fact] + public void MigratingProjectJsonWithServerGCAndOtherRuntimeOptionsProducesRuntimeConfigTemplateJsonFile() + { + var testDirectory = Temp.CreateDirectory().Path; + + var pj = @" + { + ""runtimeOptions"": { + ""configProperties"": { + ""System.GC.Server"": false + }, + ""Other"": false + } + }"; + + RunMigrateRuntimeOptionsRulePj(pj, testDirectory); + var migratedRuntimeOptionsPath = Path.Combine(testDirectory, s_runtimeConfigFileName); + File.Exists(migratedRuntimeOptionsPath).Should().BeTrue(); + + var root = JObject.Parse(File.ReadAllText(migratedRuntimeOptionsPath)); + root.Value("configProperties").Should().BeNull(); + } + + [Fact] + public void MigratingProjectJsonWithServerGCTrueProducesServerGarbageCollectionProperty() + { + var testDirectory = Temp.CreateDirectory().Path; + + var pj = @" + { + ""runtimeOptions"": { + ""configProperties"": { + ""System.GC.Server"": true + } + } + }"; + + var mockProj = RunMigrateRuntimeOptionsRulePj(pj, testDirectory); + var props = mockProj.Properties.Where(p => p.Name.Equals("ServerGarbageCollection", StringComparison.Ordinal)); + props.Count().Should().Be(1); + props.First().Value.Should().Be("true"); + } + + [Fact] + public void MigratingProjectJsonWithServerGCFalseProducesServerGarbageCollectionProperty() + { + var testDirectory = Temp.CreateDirectory().Path; + + var pj = @" + { + ""runtimeOptions"": { + ""configProperties"": { + ""System.GC.Server"": false + } + } + }"; + + var mockProj = RunMigrateRuntimeOptionsRulePj(pj, testDirectory); + var props = mockProj.Properties.Where(p => p.Name.Equals("ServerGarbageCollection", StringComparison.Ordinal)); + props.Count().Should().Be(1); + props.First().Value.Should().Be("false"); + } + + [Fact] + public void MigratingWebProjectJsonWithServerGCTrueDoesNotProduceServerGarbageCollectionProperty() + { + var testDirectory = Temp.CreateDirectory().Path; + + var pj = @" + { + ""buildOptions"": { + ""emitEntryPoint"": true + }, + ""dependencies"": { + ""Microsoft.AspNetCore.Mvc"": ""1.0.0"" + }, + ""runtimeOptions"": { + ""configProperties"": { + ""System.GC.Server"": true + } + } + }"; + + var mockProj = RunMigrateRuntimeOptionsRulePj(pj, testDirectory); + var props = mockProj.Properties.Where(p => p.Name.Equals("ServerGarbageCollection", StringComparison.Ordinal)); + props.Count().Should().Be(0); + } + + [Fact] + public void MigratingWebProjectJsonWithServerGCFalseProducesServerGarbageCollectionProperty() + { + var testDirectory = Temp.CreateDirectory().Path; + + var pj = @" + { + ""buildOptions"": { + ""emitEntryPoint"": true + }, + ""dependencies"": { + ""Microsoft.AspNetCore.Mvc"": ""1.0.0"" + }, + ""runtimeOptions"": { + ""configProperties"": { + ""System.GC.Server"": false + } + } + }"; + + var mockProj = RunMigrateRuntimeOptionsRulePj(pj, testDirectory); + var props = mockProj.Properties.Where(p => p.Name.Equals("ServerGarbageCollection", StringComparison.Ordinal)); + props.Count().Should().Be(1); + props.First().Value.Should().Be("false"); + } + + private ProjectRootElement RunMigrateRuntimeOptionsRulePj(string s, string testDirectory = null) + { + testDirectory = testDirectory ?? Temp.CreateDirectory().Path; + return TemporaryProjectFileRuleRunner.RunRules(new IMigrationRule[] + { + new MigrateRuntimeOptionsRule() + }, s, testDirectory); + } } }