/
githubmirror
/
roslyn
Обзор
Документация
Войти
/
githubmirror
/
roslyn
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
src/Compilers/Core/RebuildTest/BasicDeterministicKeyBuilderTests.cs
476 строк
18 KB
Jared Parsons
Remove crypto file path from key (#83594)
12 май 2026, 20:37
Не верифицирован
12 май 2026, 20:37
e445405
Код
Авторство
О чём код?
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. using System.Reflection; using System.Runtime.InteropServices; using System.Text; using Microsoft.CodeAnalysis.VisualBasic; using Microsoft.CodeAnalysis.VisualBasic.Syntax; using Roslyn.Test.Utilities; using Xunit; using Newtonsoft; using Newtonsoft.Json.Linq; using System.IO; using System.Linq; using Newtonsoft.Json; using Microsoft.CodeAnalysis.Emit; using Microsoft.CodeAnalysis.Text; using Microsoft.CodeAnalysis.PooledObjects; using System.Collections.Immutable; using Microsoft.CodeAnalysis.VisualBasic.UnitTests; using System.Collections.Generic; using System; namespace Microsoft.CodeAnalysis.Rebuild.UnitTests { public sealed class BasicDeterministicKeyBuilderTests : DeterministicKeyBuilderTests<VisualBasicCompilation, VisualBasicCompilationOptions, VisualBasicParseOptions> { public static VisualBasicCompilationOptions BasicOptions { get; } = new VisualBasicCompilationOptions(OutputKind.ConsoleApplication, deterministic: true); protected override SyntaxTree ParseSyntaxTree(string content, string fileName, SourceHashAlgorithm hashAlgorithm, VisualBasicParseOptions? parseOptions) => VisualBasicSyntaxTree.ParseText( SourceText.From(content, checksumAlgorithm: hashAlgorithm, encoding: Encoding.UTF8), path: fileName, options: parseOptions); protected override VisualBasicCompilation CreateCompilation(SyntaxTree[] syntaxTrees, MetadataReference[]? references = null, VisualBasicCompilationOptions? options = null) => VisualBasicCompilation.Create( "test", syntaxTrees, references ?? NetCoreApp.References.ToArray(), options: options ?? BasicOptions); protected override VisualBasicCompilationOptions GetCompilationOptions() => BasicOptions; protected override VisualBasicParseOptions GetParseOptions() => VisualBasicParseOptions.Default; private protected override DeterministicKeyBuilder GetDeterministicKeyBuilder() => VisualBasicDeterministicKeyBuilder.Instance; /// <summary> /// This check monitors the set of properties and fields on the various option types /// that contribute to the deterministic checksum of a <see cref="Compilation"/>. When /// any of these tests change that means the new property or field needs to be evaluated /// for inclusion into the checksum /// </summary> [Fact] public void VerifyUpToDate() { verifyCount<ParseOptions>(11); verifyCount<VisualBasicParseOptions>(10); verifyCount<CompilationOptions>(63); verifyCount<VisualBasicCompilationOptions>(22); static void verifyCount<T>(int expected) { var type = typeof(T); var flags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.DeclaredOnly | BindingFlags.Instance; var fields = type.GetFields(flags); var properties = type.GetProperties(flags); var count = fields.Length + properties.Length; Assert.Equal(expected, count); } } [Theory] [InlineData(@"hello world")] [InlineData(@"just need some text here")] [InlineData(@"yet another case")] public void ContentInAdditionalText(string content) { var syntaxTree = VisualBasicSyntaxTree.ParseText( "", path: "file.vb"); var additionalText = new TestAdditionalText(content, Encoding.UTF8, path: "file.txt", HashAlgorithm); var contentChecksum = GetChecksum(additionalText.GetText()!); var compilation = VisualBasicCompilation.Create( "test", new[] { syntaxTree }, NetCoreApp.References, options: BasicOptions); var key = compilation.GetDeterministicKey(additionalTexts: ImmutableArray.Create<AdditionalText>(additionalText)); var expected = @$" ""additionalTexts"": [ {{ ""fileName"": ""file.txt"", ""text"": {{ ""checksum"": ""{contentChecksum}"", ""checksumAlgorithm"": ""Sha256"", ""encodingName"": ""Unicode (UTF-8)"" }} }} ]"; AssertJsonSection(expected, key, "additionalTexts"); } [Fact] public void GlobalImports() { var syntaxTree = VisualBasicSyntaxTree.ParseText( "", path: "file.vb"); var options = BasicOptions .WithGlobalImports(new[] { GlobalImport.Parse(@"<xmlns:xmlNamespacePrefix = ""xmlNamespaceName"">"), GlobalImport.Parse("System.Xml") }); var compilation = VisualBasicCompilation.Create( "test", new[] { syntaxTree }, NetCoreApp.References, options: options); var key = compilation.GetDeterministicKey(); var expected = @" ""globalImports"": [ { ""name"": ""<xmlns:xmlNamespacePrefix = \""xmlNamespaceName\"">"", ""isXmlClause"": true }, { ""name"": ""System.Xml"", ""isXmlClause"": false } ]"; AssertJsonSection(expected, key, "compilation.options.globalImports"); } [Theory] [CombinatorialData] public void BasicParseOptionsLanguageVersion(LanguageVersion languageVersion) { var parseOptions = VisualBasicParseOptions.Default.WithLanguageVersion(languageVersion); var obj = GetParseOptionsValue(parseOptions); var effective = languageVersion.MapSpecifiedToEffectiveVersion(); Assert.Equal(effective.ToString(), obj.Value<string>("languageVersion")); Assert.Equal(languageVersion.ToString(), obj.Value<string>("specifiedLanguageVersion")); } [Fact] public void BasicPreprocessorSymbols() { assert(@"{}"); assert(@" { ""DEBUG"": null }", ("DEBUG", null)); assert(@" { ""DEBUG"": null, ""TRACE"": null }", ("TRACE", null), ("DEBUG", null)); assert(@" { ""DEBUG"": ""13"", ""TRACE"": ""42"" }", ("TRACE", 42), ("DEBUG", 13)); assert(@" { ""DEBUG"": ""4.2"", ""TRACE"": true }", ("TRACE", true), ("DEBUG", 4.2)); void assert(string? expected, params (string Key, object? Value)[] values) { var parseOptions = VisualBasicParseOptions.Default.WithPreprocessorSymbols(values.Select(x => new KeyValuePair<string, object>(x.Key, x.Value!))); var obj = GetParseOptionsValue(parseOptions); AssertJsonCore(expected, obj.Value<JObject>("preprocessorSymbols")?.ToString(Formatting.Indented)); } } [ConditionalTheory(typeof(WindowsOnly))] [InlineData(@"c:\src\code.vb", @"c:\src", null)] [InlineData(@"d:\src\code.vb", @"d:\src\", @"/pathmap:d:\=c:\")] [InlineData(@"e:\long\path\src\code.vb", @"e:\long\path\src\", @"/pathmap:e:\long\path\=c:\")] public void BasicPathMapWindows(string filePath, string workingDirectory, string? pathMap) { var args = new List<string>(new[] { filePath, "/nostdlib", "/vbruntime-", "/langversion:15" }); if (pathMap is not null) { args.Add(pathMap); } var compiler = new MockVisualBasicCompiler( baseDirectory: workingDirectory, args.ToArray()); compiler.FileSystem = TestableFileSystem.CreateForFiles((filePath, new TestableFile("hello"))); AssertSyntaxTreePathMap(@" [ { ""fileName"": ""c:\\src\\code.vb"", ""text"": { ""checksum"": ""2cf24dba5fb0a3e26e83b2ac5b9e29e1b161e5c1fa7425e7343362938b9824"", ""checksumAlgorithm"": ""Sha256"", ""encodingName"": ""Unicode (UTF-8)"" }, ""parseOptionsIndex"": 0 } ] ", compiler); } [Fact] public void BasicCryptoKeyOptions() { var options = new VisualBasicCompilationOptions(OutputKind.ConsoleApplication, deterministic: true) .WithCryptoKeyContainer("MyContainer") .WithCryptoKeyFile(Path.Combine("path", "to", "MyKeyFile.snk")); var obj = GetCompilationOptionsValue(options); Assert.Equal("MyContainer", obj.Value<string>("cryptoKeyContainer")); Assert.Equal("MyKeyFile.snk", obj.Value<string>("cryptoKeyFile")); } [Fact] public void BasicNetModuleCryptoKeyOptions() { var keyFile = Path.Combine("path", "to", "MyKeyFile.snk"); var options = new VisualBasicCompilationOptions(OutputKind.NetModule, deterministic: true) .WithCryptoKeyContainer("MyContainer") .WithCryptoKeyFile(keyFile); var obj = GetCompilationOptionsValue(options); Assert.Equal("MyContainer", obj.Value<string>("cryptoKeyContainer")); Assert.Equal(keyFile, obj.Value<string>("cryptoKeyFile")); } [Fact] public void MetadataReferenceCompilation() { var utilCompilation = VisualBasicCompilation.Create( assemblyName: "util", syntaxTrees: new[] { VisualBasicSyntaxTree.ParseText(@"// this is a comment", VisualBasicParseOptions.Default.WithLanguageVersion(LanguageVersion.VisualBasic15)) }, options: new VisualBasicCompilationOptions(OutputKind.DynamicallyLinkedLibrary, deterministic: true)); var compilation = CreateCompilation( Array.Empty<SyntaxTree>(), references: new[] { utilCompilation.ToMetadataReference() }); var references = GetReferenceValues(compilation); var compilationValue = references.Values<JObject>().Single()!; var expected = @" { ""compilation"": { ""publicKey"": """", ""options"": { ""outputKind"": ""DynamicallyLinkedLibrary"", ""moduleName"": null, ""scriptClassName"": ""Script"", ""mainTypeName"": null, ""cryptoPublicKey"": """", ""cryptoKeyContainer"": null, ""cryptoKeyFile"": null, ""delaySign"": null, ""publicSign"": false, ""checkOverflow"": true, ""platform"": ""AnyCpu"", ""optimizationLevel"": ""Debug"", ""generalDiagnosticOption"": ""Default"", ""warningLevel"": 1, ""deterministic"": true, ""debugPlusMode"": false, ""referencesSupersedeLowerVersions"": false, ""reportSuppressedDiagnostics"": false, ""nullableContextOptions"": ""Disable"", ""specificDiagnosticOptions"": [], ""localtime"": null, ""rootNamespace"": """", ""optionStrict"": ""Off"", ""optionInfer"": true, ""optionExplicit"": true, ""optionCompareText"": false, ""embedVbCoreRuntime"": false, ""globalImports"": [], ""parseOptions"": null }, ""parseOptions"": [ { ""kind"": ""Regular"", ""specifiedKind"": ""Regular"", ""documentationMode"": ""Parse"", ""language"": ""Visual Basic"", ""features"": {}, ""languageVersion"": ""VisualBasic15"", ""specifiedLanguageVersion"": ""VisualBasic15"", ""preprocessorSymbols"": { ""_MYTYPE"": ""Empty"" } } ], ""syntaxTrees"": [ { ""fileName"": """", ""text"": { ""checksum"": ""053e2a4aa83f63193c1069d651b63bedca1e97"", ""checksumAlgorithm"": ""Sha1"", ""encodingName"": null }, ""parseOptionsIndex"": 0 } ] } } "; AssertJson(expected, compilationValue.ToString(Formatting.Indented), "toolsVersions", "references", "extensions"); } [Fact] public void FeatureFlag() { Assert.Equal("debug-determinism", CodeAnalysis.Feature.DebugDeterminism); var compiler = TestableCompiler.CreateBasicNetCoreApp("test.vb", @"-t:library", "-nologo", "-features:debug-determinism", "-deterministic", @"-define:_MYTYPE=""Empty""", "-debug:portable"); var sourceFile = compiler.AddSourceFile("test.vb", @"' this is a test file"); compiler.AddOutputFile("test.dll"); var pdbFile = compiler.AddOutputFile("test.pdb"); var keyFile = compiler.AddOutputFile("test.dll.key"); var (result, output) = compiler.Run(); Assert.True(string.IsNullOrEmpty(output)); Assert.Equal(0, result); var json = Encoding.UTF8.GetString(keyFile.Contents.ToArray()); var expected = @$" {{ ""compilation"": {{ ""publicKey"": """", ""options"": {{ ""outputKind"": ""DynamicallyLinkedLibrary"", ""moduleName"": ""test.dll"", ""scriptClassName"": ""Script"", ""mainTypeName"": null, ""cryptoPublicKey"": """", ""cryptoKeyContainer"": null, ""cryptoKeyFile"": null, ""delaySign"": null, ""publicSign"": false, ""checkOverflow"": true, ""platform"": ""AnyCpu"", ""optimizationLevel"": ""Debug"", ""generalDiagnosticOption"": ""Default"", ""warningLevel"": 1, ""deterministic"": true, ""debugPlusMode"": false, ""referencesSupersedeLowerVersions"": false, ""reportSuppressedDiagnostics"": false, ""nullableContextOptions"": ""Disable"", ""specificDiagnosticOptions"": [], ""localtime"": null, ""rootNamespace"": """", ""optionStrict"": ""Off"", ""optionInfer"": false, ""optionExplicit"": true, ""optionCompareText"": false, ""embedVbCoreRuntime"": false, ""globalImports"": [], ""parseOptions"": {{ ""kind"": ""Regular"", ""specifiedKind"": ""Regular"", ""documentationMode"": ""None"", ""language"": ""Visual Basic"", ""features"": {{ ""debug-determinism"": ""true"" }}, ""languageVersion"": ""VisualBasic17_13"", ""specifiedLanguageVersion"": ""Default"", ""preprocessorSymbols"": {{ ""TARGET"": ""library"", ""VBC_VER"": ""17.13"", ""_MYTYPE"": ""Empty"" }} }} }}, ""parseOptions"": [ {{ ""kind"": ""Regular"", ""specifiedKind"": ""Regular"", ""documentationMode"": ""None"", ""language"": ""Visual Basic"", ""features"": {{ ""debug-determinism"": ""true"" }}, ""languageVersion"": ""VisualBasic17_13"", ""specifiedLanguageVersion"": ""Default"", ""preprocessorSymbols"": {{ ""TARGET"": ""library"", ""VBC_VER"": ""17.13"", ""_MYTYPE"": ""Empty"" }} }} ], ""syntaxTrees"": [ {{ ""fileName"": ""{Roslyn.Utilities.JsonWriter.EscapeString(sourceFile.FilePath)}"", ""text"": {{ ""checksum"": ""8f9cdc9e727da9f8f0569be3dc606bfc6c1a1b13444e18c95eefb73810bbf1"", ""checksumAlgorithm"": ""Sha256"", ""encodingName"": ""Unicode (UTF-8)"" }}, ""parseOptionsIndex"": 0 }} ] }}, ""additionalTexts"": [], ""analyzers"": [], ""generators"": [], ""emitOptions"": {{ ""emitMetadataOnly"": false, ""tolerateErrors"": false, ""includePrivateMembers"": true, ""instrumentationKinds"": [], ""subsystemVersion"": {{ ""major"": 0, ""minor"": 0 }}, ""fileAlignment"": 0, ""highEntropyVirtualAddressSpace"": false, ""baseAddress"": ""0"", ""debugInformationFormat"": ""PortablePdb"", ""outputNameOverride"": ""test.dll"", ""pdbFilePath"": ""{Roslyn.Utilities.JsonWriter.EscapeString(pdbFile.FilePath)}"", ""pdbChecksumAlgorithm"": ""SHA256"", ""runtimeMetadataVersion"": null, ""defaultSourceFileEncoding"": null, ""fallbackSourceFileEncoding"": null, ""sourceLink"": null }}, ""resources"": [] }} "; AssertJson(expected, json, "toolsVersions", "references", "extensions"); } [Fact] public void ParseOptionsDeduplication() { // Trees with different ParseOptions produce multiple entries in the parseOptions array; // trees with the same options share an index. var options1 = VisualBasicParseOptions.Default.WithPreprocessorSymbols(new KeyValuePair<string, object>("DEBUG", true)); var options2 = VisualBasicParseOptions.Default.WithPreprocessorSymbols(new KeyValuePair<string, object>("RELEASE", true)); var tree1 = VisualBasicSyntaxTree.ParseText("Class A\nEnd Class", path: "a.vb", options: options1); var tree2 = VisualBasicSyntaxTree.ParseText("Class B\nEnd Class", path: "b.vb", options: options2); var tree3 = VisualBasicSyntaxTree.ParseText("Class C\nEnd Class", path: "c.vb", options: options1); var compilation = VisualBasicCompilation.Create( "test", new[] { tree1, tree2, tree3 }, NetCoreApp.References.ToArray(), options: BasicOptions); var key = compilation.GetDeterministicKey(options: DeterministicKeyOptions.IgnoreToolVersions); // Two distinct ParseOptions → two entries in the parseOptions array. var parseOptionsArray = (Newtonsoft.Json.Linq.JArray)GetJsonProperty(key, "compilation.parseOptions").Value; Assert.Equal(2, parseOptionsArray.Count); // tree1 and tree3 share options1 (index 0); tree2 has options2 (index 1). var syntaxTreesArray = (Newtonsoft.Json.Linq.JArray)GetJsonProperty(key, "compilation.syntaxTrees").Value; Assert.Equal(0, syntaxTreesArray[0].Value<int>("parseOptionsIndex")); Assert.Equal(1, syntaxTreesArray[1].Value<int>("parseOptionsIndex")); Assert.Equal(0, syntaxTreesArray[2].Value<int>("parseOptionsIndex")); } } }