Skip to content

Commit

Permalink
Merge pull request #2912 from Microsoft/dev
Browse files Browse the repository at this point in the history
Sync from dev
  • Loading branch information
sibille authored Feb 7, 2019
2 parents c5213ed + 2ffd67a commit c6355a7
Show file tree
Hide file tree
Showing 212 changed files with 609 additions and 288 deletions.
43 changes: 43 additions & 0 deletions code/src/Core/Extensions/StringExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
// See the LICENSE file in the project root for more information.

using System;
using System.Globalization;
using System.Security.Cryptography;
using System.Text;

Expand Down Expand Up @@ -51,5 +52,47 @@ public static bool IsMultiValue(this string value)
{
return value.GetMultiValue().Length > 1;
}

// This is the same substitution as VS makes with new projects
public static string MakeSafeProjectName(this string projectName)
{
if (projectName == null)
{
return null;
}

var stringBuilder = new StringBuilder(projectName);

for (var i = 0; i < stringBuilder.Length; i++)
{
var unicodeCategory = char.GetUnicodeCategory(stringBuilder[i]);

var flag = unicodeCategory == UnicodeCategory.UppercaseLetter ||
unicodeCategory == UnicodeCategory.LowercaseLetter ||
unicodeCategory == UnicodeCategory.TitlecaseLetter ||
unicodeCategory == UnicodeCategory.OtherLetter ||
unicodeCategory == UnicodeCategory.LetterNumber ||
stringBuilder[i] == '\u005F';

var flag1 = unicodeCategory == UnicodeCategory.NonSpacingMark ||
unicodeCategory == UnicodeCategory.SpacingCombiningMark ||
unicodeCategory == UnicodeCategory.ModifierLetter ||
unicodeCategory == UnicodeCategory.DecimalDigitNumber;

if (i == 0)
{
if (!flag)
{
stringBuilder[i] = '\u005F';
}
}
else if (!flag & !flag1)
{
stringBuilder[i] = '\u005F';
}
}

return stringBuilder.ToString();
}
}
}
2 changes: 1 addition & 1 deletion code/src/Core/Gen/GenComposer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -319,7 +319,7 @@ private static void AddDefaultParams(GenInfo genInfo, bool newItemGeneration)

if (string.IsNullOrEmpty(ns))
{
ns = GenContext.Current.ProjectName;
ns = GenContext.Current.SafeProjectName;
}

genInfo.Parameters.Add(GenParams.RootNamespace, ns);
Expand Down
2 changes: 2 additions & 0 deletions code/src/Core/Gen/IContextProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ public interface IContextProvider
{
string ProjectName { get; }

string SafeProjectName { get; }

string GenerationOutputPath { get; }

string DestinationPath { get; }
Expand Down
3 changes: 3 additions & 0 deletions code/src/UI/VisualStudio/RightClickActions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ public class RightClickActions : IContextProvider

public string ProjectName { get; private set; }

public string SafeProjectName { get; private set; }

public string GenerationOutputPath { get; private set; }

public string DestinationPath { get; private set; }
Expand Down Expand Up @@ -120,6 +122,7 @@ private void SetContext()
{
DestinationPath = GenContext.ToolBox.Shell.GetActiveProjectPath();
ProjectName = GenContext.ToolBox.Shell.GetActiveProjectName();
SafeProjectName = GenContext.ToolBox.Shell.GetActiveProjectNamespace();
}

GenerationOutputPath = GenContext.GetTempGenerationPath(ProjectName);
Expand Down
4 changes: 3 additions & 1 deletion code/src/UI/VisualStudio/SolutionWizard.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,9 @@ public abstract class SolutionWizard : IWizard, IContextProvider
private string _language;
private GenerationService _generationService = GenerationService.Instance;

public string ProjectName => _replacementsDictionary["$safeprojectname$"];
public string SafeProjectName => _replacementsDictionary["$safeprojectname$"];

public string ProjectName => _replacementsDictionary["$projectname$"];

public string DestinationPath => new DirectoryInfo(_replacementsDictionary["$destinationdirectory$"]).FullName;

Expand Down
3 changes: 3 additions & 0 deletions code/test/Fakes/FakeContextProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
// See the LICENSE file in the project root for more information.

using System.Collections.Generic;
using Microsoft.Templates.Core;
using Microsoft.Templates.Core.Diagnostics;
using Microsoft.Templates.Core.Gen;
using Microsoft.Templates.Core.PostActions.Catalog.Merge;
Expand All @@ -13,6 +14,8 @@ public class FakeContextProvider : IContextProvider
{
public string ProjectName { get; set; }

public string SafeProjectName => ProjectName.MakeSafeProjectName();

public string GenerationOutputPath { get; set; }

public string DestinationPath { get; set; }
Expand Down
8 changes: 8 additions & 0 deletions code/test/Templates.Test/BaseGenAndBuildTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,14 @@ protected static string ShortLanguageName(string language)
return language == ProgrammingLanguages.CSharp ? "CS" : "VB";
}

// Used to create names that include a number of characters that are valid in project names but have the potential to cause issues
protected static string CharactersThatMayCauseProjectNameIssues()
{
// $ is technically valid in a project name but cannot be used with WTS as it is used as an identifier in global post action file names.
// ^ is technically valid in project names but Visual Studio cannot open files with this in the path
return " -_.,'@!(£)+=";
}

protected static string ShortProjectType(string projectType)
{
switch (projectType)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,32 @@ public async Task BuildAllPagesAndFeaturesAsync(string projectType, string frame
AssertBuildProjectAsync(projectPath, projectName, platform);
}

[Theory]
[MemberData(nameof(BaseGenAndBuildTests.GetProjectTemplatesForBuild), "CaliburnMicro")]
[Trait("Type", "BuildAllPagesAndFeatures")]
public async Task BuildAllPagesAndFeaturesProjectNameValidationAsync(string projectType, string framework, string platform, string language)
{
Func<ITemplateInfo, bool> selector =
t => t.GetTemplateType() == TemplateType.Project
&& t.GetProjectTypeList().Contains(projectType)
&& t.GetFrameworkList().Contains(framework)
&& t.GetPlatform() == platform
&& !t.GetIsHidden()
&& t.GetLanguage() == language;

Func<ITemplateInfo, bool> templateSelector =
t => (t.GetTemplateType() == TemplateType.Page || t.GetTemplateType() == TemplateType.Feature)
&& t.GetFrameworkList().Contains(framework)
&& t.GetPlatform() == platform
&& !t.GetIsHidden();

var projectName = $"{ShortProjectType(projectType)}{CharactersThatMayCauseProjectNameIssues()}{ShortLanguageName(language)}";

var projectPath = await AssertGenerateProjectAsync(selector, projectName, projectType, framework, platform, language, templateSelector, BaseGenAndBuildFixture.GetDefaultName, false);

AssertBuildProjectAsync(projectPath, projectName, platform);
}

[Theory]
[MemberData(nameof(BaseGenAndBuildTests.GetProjectTemplatesForBuild), "CaliburnMicro")]
[Trait("Type", "BuildRandomNames")]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,17 +50,17 @@ public async Task BuildAllPagesAndFeaturesAsync(string projectType, string frame
{
Func<ITemplateInfo, bool> selector =
t => t.GetTemplateType() == TemplateType.Project
&& t.GetProjectTypeList().Contains(projectType)
&& t.GetFrameworkList().Contains(framework)
&& t.GetPlatform() == platform
&& !t.GetIsHidden()
&& t.GetLanguage() == language;
&& t.GetProjectTypeList().Contains(projectType)
&& t.GetFrameworkList().Contains(framework)
&& t.GetPlatform() == platform
&& !t.GetIsHidden()
&& t.GetLanguage() == language;

Func<ITemplateInfo, bool> templateSelector =
t => (t.GetTemplateType() == TemplateType.Page || t.GetTemplateType() == TemplateType.Feature)
&& t.GetFrameworkList().Contains(framework)
&& t.GetPlatform() == platform
&& !t.GetIsHidden();
&& t.GetFrameworkList().Contains(framework)
&& t.GetPlatform() == platform
&& !t.GetIsHidden();

var projectName = $"{ShortProjectType(projectType)}All{ShortLanguageName(language)}";

Expand All @@ -69,6 +69,32 @@ public async Task BuildAllPagesAndFeaturesAsync(string projectType, string frame
AssertBuildProjectAsync(projectPath, projectName, platform);
}

[Theory]
[MemberData(nameof(BaseGenAndBuildTests.GetProjectTemplatesForBuild), "CodeBehind")]
[Trait("Type", "BuildAllPagesAndFeatures")]
public async Task BuildAllPagesAndFeaturesProjectNameValidationAsync(string projectType, string framework, string platform, string language)
{
Func<ITemplateInfo, bool> selector =
t => t.GetTemplateType() == TemplateType.Project
&& t.GetProjectTypeList().Contains(projectType)
&& t.GetFrameworkList().Contains(framework)
&& t.GetPlatform() == platform
&& !t.GetIsHidden()
&& t.GetLanguage() == language;

Func<ITemplateInfo, bool> templateSelector =
t => (t.GetTemplateType() == TemplateType.Page || t.GetTemplateType() == TemplateType.Feature)
&& t.GetFrameworkList().Contains(framework)
&& t.GetPlatform() == platform
&& !t.GetIsHidden();

var projectName = $"{ShortProjectType(projectType)}{CharactersThatMayCauseProjectNameIssues()}{ShortLanguageName(language)}";

var projectPath = await AssertGenerateProjectAsync(selector, projectName, projectType, framework, platform, language, templateSelector, BaseGenAndBuildFixture.GetDefaultName, false);

AssertBuildProjectAsync(projectPath, projectName, platform);
}

[Theory]
[MemberData(nameof(BaseGenAndBuildTests.GetProjectTemplatesForBuild), "CodeBehind", ProgrammingLanguages.CSharp, Platforms.Uwp)]
[Trait("Type", "BuildRandomNames")]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,32 @@ public async Task BuildAllPagesAndFeaturesAsync(string projectType, string frame
AssertBuildProjectAsync(projectPath, projectName, platform);
}

[Theory]
[MemberData(nameof(BaseGenAndBuildTests.GetProjectTemplatesForBuild), "MVVMBasic")]
[Trait("Type", "BuildAllPagesAndFeatures")]
public async Task BuildAllPagesAndFeaturesProjectNameValidationAsync(string projectType, string framework, string platform, string language)
{
Func<ITemplateInfo, bool> selector =
t => t.GetTemplateType() == TemplateType.Project
&& t.GetProjectTypeList().Contains(projectType)
&& t.GetFrameworkList().Contains(framework)
&& t.GetPlatform() == platform
&& !t.GetIsHidden()
&& t.GetLanguage() == language;

Func<ITemplateInfo, bool> templateSelector =
t => (t.GetTemplateType() == TemplateType.Page || t.GetTemplateType() == TemplateType.Feature)
&& t.GetFrameworkList().Contains(framework)
&& t.GetPlatform() == platform
&& !t.GetIsHidden();

var projectName = $"{ShortProjectType(projectType)}{CharactersThatMayCauseProjectNameIssues()}{ShortLanguageName(language)}";

var projectPath = await AssertGenerateProjectAsync(selector, projectName, projectType, framework, platform, language, templateSelector, BaseGenAndBuildFixture.GetDefaultName, false);

AssertBuildProjectAsync(projectPath, projectName, platform);
}

[Theory]
[MemberData(nameof(BaseGenAndBuildTests.GetProjectTemplatesForBuild), "MVVMBasic", ProgrammingLanguages.CSharp, Platforms.Uwp)]
[Trait("Type", "BuildRandomNames")]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,32 @@ public async Task BuildAllPagesAndFeaturesAsync(string projectType, string frame
AssertBuildProjectAsync(projectPath, projectName, platform);
}

[Theory]
[MemberData(nameof(BaseGenAndBuildTests.GetProjectTemplatesForBuild), "MVVMLight")]
[Trait("Type", "BuildAllPagesAndFeatures")]
public async Task BuildAllPagesAndFeaturesProjectNameValidationAsync(string projectType, string framework, string platform, string language)
{
Func<ITemplateInfo, bool> selector =
t => t.GetTemplateType() == TemplateType.Project
&& t.GetProjectTypeList().Contains(projectType)
&& t.GetFrameworkList().Contains(framework)
&& t.GetPlatform() == platform
&& !t.GetIsHidden()
&& t.GetLanguage() == language;

Func<ITemplateInfo, bool> templateSelector =
t => (t.GetTemplateType() == TemplateType.Page || t.GetTemplateType() == TemplateType.Feature)
&& t.GetFrameworkList().Contains(framework)
&& t.GetPlatform() == platform
&& !t.GetIsHidden();

var projectName = $"{ShortProjectType(projectType)}{CharactersThatMayCauseProjectNameIssues()}{ShortLanguageName(language)}";

var projectPath = await AssertGenerateProjectAsync(selector, projectName, projectType, framework, platform, language, templateSelector, BaseGenAndBuildFixture.GetDefaultName, false);

AssertBuildProjectAsync(projectPath, projectName, platform);
}

[Theory]
[MemberData(nameof(BaseGenAndBuildTests.GetProjectTemplatesForBuild), "MVVMLight", ProgrammingLanguages.CSharp, Platforms.Uwp)]
[Trait("Type", "BuildRandomNames")]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,32 @@ public async Task BuildAllPagesAndFeaturesAsync(string projectType, string frame
AssertBuildProjectAsync(projectPath, projectName, platform);
}

[Theory]
[MemberData(nameof(BaseGenAndBuildTests.GetProjectTemplatesForBuild), "Prism")]
[Trait("Type", "BuildAllPagesAndFeatures")]
public async Task BuildAllPagesAndFeaturesProjectNameValidationAsync(string projectType, string framework, string platform, string language)
{
Func<ITemplateInfo, bool> selector =
t => t.GetTemplateType() == TemplateType.Project
&& t.GetProjectTypeList().Contains(projectType)
&& t.GetFrameworkList().Contains(framework)
&& t.GetPlatform() == platform
&& !t.GetIsHidden()
&& t.GetLanguage() == language;

Func<ITemplateInfo, bool> templateSelector =
t => (t.GetTemplateType() == TemplateType.Page || t.GetTemplateType() == TemplateType.Feature)
&& t.GetFrameworkList().Contains(framework)
&& t.GetPlatform() == platform
&& !t.GetIsHidden();

var projectName = $"{ShortProjectType(projectType)}{CharactersThatMayCauseProjectNameIssues()}{ShortLanguageName(language)}";

var projectPath = await AssertGenerateProjectAsync(selector, projectName, projectType, framework, platform, language, templateSelector, BaseGenAndBuildFixture.GetDefaultName, false);

AssertBuildProjectAsync(projectPath, projectName, platform);
}

[Theory]
[MemberData(nameof(BaseGenAndBuildTests.GetProjectTemplatesForBuild), "Prism")]
[Trait("Type", "BuildRandomNames")]
Expand Down
28 changes: 28 additions & 0 deletions code/test/Templates.Test/CodeStyleEnforcementTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,34 @@ void EnsureTabsNotUsed(string fileExtension)
EnsureTabsNotUsed("*.vb");
}

[Fact]
public void EnsureTemplatesDefineNamespacesCorrectly()
{
var result = new List<string>();

void EnsureDoNotUse(string shouldNotUse, string fileExtension)
{
var (success, failMessage) = CodeIsNotUsed(shouldNotUse, fileExtension);

if (!success)
{
result.Add(failMessage + " It should use 'Param_RootNamespace' instead.");
}
}

// The placeholder "Param_RootNamespace" should be used instead, to ensure that all namespaces are created equally
EnsureDoNotUse("namespace wts.DefaultProject", "*.cs");
EnsureDoNotUse("namespace wts.DefaultProject", "*.vb");
EnsureDoNotUse("namespace Param_ProjectName", "*.cs");
EnsureDoNotUse("namespace Param_ProjectName", "*.vb");
EnsureDoNotUse("using wts.DefaultProject", "*.cs");
EnsureDoNotUse("Imports wts.DefaultProject", "*.vb");
EnsureDoNotUse("using Param_ProjectName", "*.cs");
EnsureDoNotUse("Imports Param_ProjectName", "*.vb");

Assert.True(!result.Any(), string.Join(Environment.NewLine, result));
}

[Fact]
public void EnsureCodeDoesNotUseOldTodoCommentIdentifier()
{
Expand Down
2 changes: 2 additions & 0 deletions code/test/VsEmulator/Main/MainViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,8 @@ public MainViewModel(MainView host)

public string ProjectName { get; private set; }

public string SafeProjectName => ProjectName.MakeSafeProjectName();

public string GenerationOutputPath { get; set; }

public string DestinationPath { get; private set; }
Expand Down
5 changes: 5 additions & 0 deletions code/tools/TemplateValidator/TemplateFolderVerifier.cs
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,11 @@ in templateFolders
{
results.Add($"'{localizedFile.FullName}' does not have the correct identity.");
}

if (template.Name != localizedTemplate.Name)
{
results.Add($"'{localizedFile.FullName}' does not have the correct name.");
}
}
}
else
Expand Down
Loading

0 comments on commit c6355a7

Please sign in to comment.