- android - 多次调用 OnPrimaryClipChangedListener
- android - 无法更新 RecyclerView 中的 TextView 字段
- android.database.CursorIndexOutOfBoundsException : Index 0 requested, 光标大小为 0
- android - 使用 AppCompat 时,我们是否需要明确指定其 UI 组件(Spinner、EditText)颜色
最近,我不得不重构大量测试,以便根据报告的结果将它们全部标记为特定的 TestCategory 属性。如果测试被列在报告中,它应该被标记为“好类别”,否则它应该被标记为“坏类别”。这些类别将用于过滤哪些测试将作为我们的门控构建的一部分运行。
这里有一些例子说明了它是如何完成的。
最佳答案
流程的第一步是加载解决方案
var wkps = MSBuildWorkspace.Create();
var sln = wkps.OpenSolutionAsync(slnPath).Result;
现在我们有了解决方案引用,我们可以遍历每个项目并获取其语法树。然后我们可以在每个 SyntaxTree 上调用 GetRoot 并将其转换为 CompilationUnitSyntax。从这一点开始,我们搜索所有符合我们的标准的 DecsendantNodes,即定义了 TestMethod 属性的方法。
这是整体的样子
foreach (var proj in sln.Projects)
{
var comp = proj.GetCompilationAsync().Result;
foreach (var method in root.DescendantNodes().OfType<MethodDeclarationSyntax>().Where(m => HasAttribute(m, TEST_METHOD)))
{
//do something with this test method
}
}
上面有一个名为 HasAttribute 的辅助方法,它只在名为“TestMethod”的方法上查找任何属性。这是它的样子
bool HasAttribute(MethodDeclarationSyntax method, string attributeName)
{
return method.AttributeLists
.Any(al => al.Attributes
.Any(a => a.Name is IdentifierNameSyntax && (((IdentifierNameSyntax)a.Name).Identifier.Text == attributeName)));
}
现在我们已经有了一种循环访问所有 TestMethod 方法的方法,我们需要将我们的 TestCategory 属性分配给它们。这是上面循环的“做某事”部分。
这里的过程有两个步骤。首先是编辑我们的 SyntaxTree,以便我们添加和/或删除我们想要的类别。其次是将该 SyntaxTree 写回源文件。
我们需要做的第一件事是根据我们的输入列表检查方法的名称。假设我们有一个方法名称字典,它可能看起来像这样
var methodName = method.Identifier.ValueText;
var testIsOnList = testDictionary.ContainsKey(methodFullName);
但是,此测试假设在您的整个解决方案中,测试名称是全局唯一的。不幸的是,在我的例子中,这不是真的。为了解决这个问题,我们将输入列表设为“完全合格的测试名称”,因为它会出现在 MSTest 测试运行程序中。这将是:
例如。 My.Long.NameSpace.ParentClass.ChildClass.Method
这是一个小的辅助方法,它将在给定 MethodDeclarationSyntax 的情况下创建 FQTN
string BuildFullTestName(MethodDeclarationSyntax method)
{
StringBuilder sb = new StringBuilder();
sb.Append(method.Identifier.ValueText);
SyntaxNode node = method;
while(node.Parent is ClassDeclarationSyntax)
{
node = node.Parent;
sb.Insert(0, ".");
sb.Insert(0, ((ClassDeclarationSyntax)node).Identifier.ValueText);
}
if(node.Parent is NamespaceDeclarationSyntax)
{
node = node.Parent;
sb.Insert(0, ".");
sb.Insert(0, ((NamespaceDeclarationSyntax)node).Name.ToString());
}
else
{
throw new Exception("method \{method.Identifier.ValueText} has wierd parents.");
}
return sb.ToString();
}
所以我们已经完成了比较,我们想用我们的好或坏 TestCategory 属性来标记测试。这是另一个辅助方法,它将接受 MethodDeclarationSyntax、属性名称(在我们的例子中是 TestCateogry)和属性的参数(在我们的例子中是类别的名称)。它将返回包含我们更改的新 MethodDeclarationSyntax。
MethodDeclarationSyntax AddMethodProperty(MethodDeclarationSyntax method, string propertyName, string argumentName)
{
return method.AddAttributeLists(
SyntaxFactory.AttributeList(
SyntaxFactory.SingletonSeparatedList(
SyntaxFactory.Attribute(
SyntaxFactory.IdentifierName(propertyName),
SyntaxFactory.AttributeArgumentList(
SyntaxFactory.SingletonSeparatedList(
SyntaxFactory.AttributeArgument(
SyntaxFactory.LiteralExpression(
SyntaxKind.StringLiteralExpression,
SyntaxFactory.Token(
default(SyntaxTriviaList),
SyntaxKind.StringLiteralToken,
argumentName,
argumentName,
default(SyntaxTriviaList))
))))))));
}
因为所有 SyntaxNodes 都是不可变的,我们不能就地更新方法。因此,现在我们已经有了新的 MethodDeclarationSyntax,我们需要创建一个新的 SyntaxTree,在其中我们用新方法替换了旧方法。
SyntaxTree newTree = SyntaxFactory.SyntaxTree(
Formatter.Format(syntaxRoot.ReplaceNode(method, newMethod), wkps))
.WithFilePath(method.SyntaxTree.FilePath);
注意:需要 .WithFilePath 以便新的 SyntaxTree 保留其映射到哪个源文件的上下文。
现在我们可以将新的 SyntaxTree 写入磁盘。标准的东西在这里。
using (StreamWriter file = File.CreateText(method.SyntaxTree.FilePath))
{
file.Write(newTree.ToString());
file.Flush();
}
在遍历您的方法时,需要记住一个主要问题。每次创建新的 SyntaxTree 时,其根 CompilationUnitSyntax 必须传递给循环的 future 迭代。此外,我们上面对 syntaxRoot.ReplaceNode 的调用只有在被替换的方法实际上来自该 SyntaxTree 时才会起作用。换句话说,我们的大嵌套 foreach 的下一次迭代中的 MethodDeclarationSyntax 引用将不会在您新创建的 SyntaxTree 中找到。为了处理这个问题,我创建了另一个辅助方法,它将在给定旧语法树的新语法树中找到 MethodDeclarationSyntax。
MethodDeclarationSyntax GetMethodFromSyntaxRoot(CompilationUnitSyntax root, string nameSpaceName, string className, MethodDeclarationSyntax method)
{
var result = root.Members.OfType<NamespaceDeclarationSyntax>().Single(ns => ns.Name.ToString() == nameSpaceName)
.DescendantNodes(d => true).OfType<ClassDeclarationSyntax>().Single(c => c.Identifier.ValueText == className)
.Members.OfType<MethodDeclarationSyntax>().SingleOrDefault(m => m.Identifier.ValueText == method.Identifier.ValueText && m.ParameterList.ToString() == method.ParameterList.ToString());
}
关于c# - 使用 Roslyn 重构 TestCategory 属性,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28160440/
最初的问题是我有一个巨大的解决方案,其中项目有不同的选项(例如,x64 或 x86 配置、是否允许不安全代码等等)。我正在尝试使用 Roslyn (2.9.0) 通过 DEBUG x64 配置来编译\
当前的 .Net 编译器是完全独立的。 Roslyn 应该将它们组合成一个编译器。有谁知道这是否会引入在单个项目中使用多种语言的能力?或者甚至可能在单个文件/类中? 目前您能做的最好的事情就是在一个解
我已经开始使用 Roslyn 的语法和语义 API。还没有真正深入挖掘,但是语义 API 是否提供了任何代码优化,例如: 消除死代码,吊装或某种指针分析?或者其他分析? 我知道 roslyn 提供了
Roslyn 似乎提供了新的 API 来公开许多编译器内部数据结构以进行代码分析等。为此目的重写了 C# 和 VB 编译器。那么除了新的 API 之外,我还可以访问编译器源代码吗? 最佳答案 Rosl
我的解决方案在 roslyn 中构建正常,因此应该解析所有类型 我能够像这样获取在元数据程序集中定义的类型: string typeName = "MyCompany.MyLibrary.MyType
Roslyn 项目中的 CaaS(编译器即服务)是什么? 与当前 C# 4.0 编译器相比,使用 Roslyn 功能如何提高 C# 应用程序的性能? Roslyn-CTP 有哪些已知的限制/问题? 最
我们最近将构建系统从 VS 2013 升级到 2015 Update 2,构建时间显着增加。我们的构建环境是独立的,因此我们从包(使用 devpath)而不是从安装位置运行 MSBuild。查看日志,
我正在为 Roslyn 制作一个分析器。我正在做的是一种诊断,可以找到太长的方法。我想对任何被认为“太长”的内容进行可配置,最好是整个解决方案或项目的一种配置。解决这个问题的最佳方法是什么? 我想到的
如果我想在我的应用程序中支持脚本,是否 scriptcs提供比仅使用普通 Vanilla 的任何特殊优势 Roslyn脚本引擎? 最佳答案 不幸的是,目前还没有太多关于托管 scriptcs 的文档,
我创建了这个测试控制台应用程序,以使用 Roslyn 脚本引擎(在 Microsoft.CodeAnalysis.CSharp.Scripting nuget 包中)运行一些 C# 代码。
我对 stackoverflow 做了一些研究,但找不到我需要的结果。 我的问题是“如何使用 Roslyn 确定源文件的行代码位置”。 例如:我有一个源文件(名为:sample.cs)和它看起来像的内
来自 Visual Studio 2015 CTP5 包,如何获取当前的 Roslyn 工作区? 我在看 How to get reference to 'Roslyn' Workspace obje
我想在另一个非脚本 Roslyn 编译中将脚本作为动态程序集重用,但我终究无法弄清楚如何实现它。 例如,假设我以正常方式创建脚本,然后使用类似以下内容将脚本作为程序集发送到字节流: var compi
我使用 VS 2015 模板创建了一个 Roslyn 分析器。假设默认情况下启用了诊断,我的一切正常,包括单元测试。 如果我将DiagnosticDescriptor 上的isEnabledByDef
如何从 ITypeSymbol 获取基础类型对于 IEnumerable ?我明白了ITypeSymbol.OriginalDefinition包含指向 IEnumerable<> 的链接,但是我在哪
我有一个 VS 包项目,我需要从加载的 IVsSolution 访问 Roslyn 或 Microsoft.CodeAnalysis 的工作区或解决方案对象. 我需要知道如何实现这一目标? 我找到了t
我在做什么用一句话 查看分行 Update-1来自 Roslyn github repository ,构建 csc.exe,并使用我自己构建的 csc.exe 版本编译随机解决方案。 预期结果 我希
在我投入大量时间学习 roslyn 编译器服务之前,我想问一下 roslyn 是否可以实现以下场景。是否可以编译程序集而无需将任何内容写入磁盘并执行它?我基于元模型生成完整的解决方案,我想采用它并编译
我有一个带有两个输出 dll 的解决方案(实际上更多,但让我们保持简单)。项目“Special”引用项目“Common”。 我尝试编写一个代码生成器来解析“Special”中的一些文件,并将生成的 s
我创建了几个诊断分析器和代码修复。它们都按照预期在实验 hive 中工作。 我将它们构建为 Nuget 包,并添加到 VS2015 正常实例中的项目中。奇怪的是,分析器/代码修复组合之一可以正常工作,
我是一名优秀的程序员,十分优秀!