Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -39,15 +39,10 @@ Copyright (c) .NET Foundation. All rights reserved.
<FileAlignment Condition=" '$(FileAlignment)' == '' ">512</FileAlignment>
<ErrorReport Condition=" '$(ErrorReport)' == '' ">prompt</ErrorReport>
<AssemblyName Condition=" '$(AssemblyName)' == '' ">$(MSBuildProjectName)</AssemblyName>
<RootNamespace Condition=" '$(RootNamespace)' == '' ">$(MSBuildProjectName.Replace(" ", "_"))</RootNamespace>
<Deterministic Condition=" '$(Deterministic)' == '' ">true</Deterministic>
</PropertyGroup>

<PropertyGroup Condition=" '$(RootNamespace)' == '' ">
<!-- Transform project name for RootNamespace: replace spaces and dashes with underscores, prefix with underscore if starts with digit -->
<RootNamespace>$(MSBuildProjectName.Replace(" ", "_").Replace("-", "_"))</RootNamespace>
<RootNamespace Condition=" $([System.Char]::IsDigit($(RootNamespace), 0)) ">_$(RootNamespace)</RootNamespace>
</PropertyGroup>

<!-- User-facing configuration-specific defaults -->
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
<DebugSymbols Condition=" '$(DebugSymbols)' == '' ">true</DebugSymbols>
Expand Down
147 changes: 15 additions & 132 deletions test/Microsoft.NET.Build.Tests/GivenThatWeWantToBuildALibrary.cs
Original file line number Diff line number Diff line change
Expand Up @@ -105,24 +105,6 @@ internal static List<string> GetValuesFromTestLibrary(
return itemValues;
}

private string GetPropertyValue(string propertyName, string projectFolder, string targetFramework)
{
var getValuesCommand = new GetValuesCommand(Log, projectFolder,
targetFramework, propertyName, GetValuesCommand.ValueType.Property)
{
Configuration = "Debug"
};

getValuesCommand
.Execute()
.Should()
.Pass();

var values = getValuesCommand.GetValues();
values.Count.Should().Be(1);
return values[0];
}

private TestAsset CreateDocumentationFileLibraryAsset(bool? generateDocumentationFile, string documentationFile, string language, [CallerMemberName] string callingMethod = "")
{
string genDocFileIdentifier = generateDocumentationFile == null ? "null" : generateDocumentationFile.Value.ToString();
Expand Down Expand Up @@ -1068,124 +1050,25 @@ public class ProjectNameWithSpacesClass
.Should()
.Pass();

GetPropertyValue("RootNamespace", projectFolder, testProject.TargetFrameworks).Should().Be("Project_Name_With_Spaces");
}

[Theory]
[InlineData("netcoreapp3.1")]
[InlineData("netcoreapp5.0")]
public void It_makes_RootNamespace_safe_when_project_name_has_dashes(string targetFramework)
{
var testProject = new TestProject()
{
Name = "my-project-with-dashes",
TargetFrameworks = targetFramework,
};

var testAsset = _testAssetsManager.CreateTestProject(testProject, identifier: targetFramework);

// Overwrite the default file. CreateTestProject uses the defined project name for the namespace.
// We need a buildable project to extract the property to verify it
// since this issue only surfaces in VS when adding a new class through an item template.
File.WriteAllText(Path.Combine(testAsset.Path, testProject.Name, $"{testProject.Name}.cs"), @"
using System;
using System.Collections.Generic;

namespace MyProjectWithDashes
{
public class MyProjectWithDashesClass
{
public static string Name { get { return ""my-project-with-dashes""; } }
public static List<string> List { get { return null; } }
}
}");
string projectFolder = Path.Combine(testAsset.Path, testProject.Name);

var buildCommand = new BuildCommand(testAsset, $"{testProject.Name}");
buildCommand
.Execute()
.Should()
.Pass();

GetPropertyValue("RootNamespace", projectFolder, testProject.TargetFrameworks).Should().Be("my_project_with_dashes");
}

[Theory]
[InlineData("netcoreapp3.1")]
[InlineData("netcoreapp5.0")]
public void It_makes_RootNamespace_safe_when_project_name_starts_with_digit(string targetFramework)
{
var testProject = new TestProject()
string GetPropertyValue(string propertyName)
{
Name = "13monkeys",
TargetFrameworks = targetFramework,
};

var testAsset = _testAssetsManager.CreateTestProject(testProject, identifier: targetFramework);

// Overwrite the default file. CreateTestProject uses the defined project name for the namespace.
// We need a buildable project to extract the property to verify it
// since this issue only surfaces in VS when adding a new class through an item template.
File.WriteAllText(Path.Combine(testAsset.Path, testProject.Name, $"{testProject.Name}.cs"), @"
using System;
using System.Collections.Generic;

namespace _13monkeys
{
public class _13monkeysClass
{
public static string Name { get { return ""13monkeys""; } }
public static List<string> List { get { return null; } }
}
}");
string projectFolder = Path.Combine(testAsset.Path, testProject.Name);

var buildCommand = new BuildCommand(testAsset, $"{testProject.Name}");
buildCommand
.Execute()
.Should()
.Pass();

GetPropertyValue("RootNamespace", projectFolder, testProject.TargetFrameworks).Should().Be("_13monkeys");
}

[Theory]
[InlineData("netcoreapp3.1")]
[InlineData("netcoreapp5.0")]
public void It_makes_RootNamespace_safe_when_project_name_has_dashes_and_starts_with_digit(string targetFramework)
{
var testProject = new TestProject()
{
Name = "13-monkeys-project",
TargetFrameworks = targetFramework,
};

var testAsset = _testAssetsManager.CreateTestProject(testProject, identifier: targetFramework);

// Overwrite the default file. CreateTestProject uses the defined project name for the namespace.
// We need a buildable project to extract the property to verify it
// since this issue only surfaces in VS when adding a new class through an item template.
File.WriteAllText(Path.Combine(testAsset.Path, testProject.Name, $"{testProject.Name}.cs"), @"
using System;
using System.Collections.Generic;
var getValuesCommand = new GetValuesCommand(Log, projectFolder,
testProject.TargetFrameworks, propertyName, GetValuesCommand.ValueType.Property)
{
Configuration = "Debug"
};

namespace _13_monkeys_project
{
public class _13_monkeys_projectClass
{
public static string Name { get { return ""13-monkeys-project""; } }
public static List<string> List { get { return null; } }
}
}");
string projectFolder = Path.Combine(testAsset.Path, testProject.Name);
getValuesCommand
.Execute()
.Should()
.Pass();

var buildCommand = new BuildCommand(testAsset, $"{testProject.Name}");
buildCommand
.Execute()
.Should()
.Pass();
var values = getValuesCommand.GetValues();
values.Count.Should().Be(1);
return values[0];
}

GetPropertyValue("RootNamespace", projectFolder, testProject.TargetFrameworks).Should().Be("_13_monkeys_project");
GetPropertyValue("RootNamespace").Should().Be("Project_Name_With_Spaces");
Comment on lines +1053 to +1071
Copy link
Preview

Copilot AI Aug 26, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nitpick] The GetPropertyValue method is now a local function but duplicates logic that was previously in a private method. Consider whether this local function approach provides sufficient reusability if similar property extraction is needed elsewhere in the test class.

Copilot uses AI. Check for mistakes.

}

[WindowsOnlyFact(Skip = "We need new SDK packages with different assembly versions to build this (.38 and .39 have the same assembly version)")]
Expand Down