public void CompatibleFrameworksInPackage() { TestProject testProject = new() { Name = "TestPackage", TargetFrameworks = "netstandard2.0;net5.0", }; string sourceCode = @" namespace PackageValidationTests { public class First { public void test() { } #if NETSTANDARD2_0 public void test(string test) { } #endif } }"; testProject.SourceFiles.Add("Hello.cs", sourceCode); TestAsset asset = _testAssetsManager.CreateTestProject(testProject, testProject.Name); PackCommand packCommand = new PackCommand(Log, Path.Combine(asset.TestRoot, testProject.Name)); var result = packCommand.Execute(); Assert.Equal(string.Empty, result.StdErr); Package package = NupkgParser.CreatePackage(packCommand.GetNuGetPackage(), null); new CompatibleFrameworkInPackageValidator(string.Empty, null, _log).Validate(package); Assert.NotEmpty(_log.errors); // TODO: add asserts for assembly and header metadata. Assert.Contains("CP0002 Member 'PackageValidationTests.First.test(string)' exists on the left but not on the right", _log.errors); }
protected override void ExecuteCore() { RuntimeGraph runtimeGraph = null; if (!string.IsNullOrEmpty(RuntimeGraph)) { runtimeGraph = JsonRuntimeFormat.ReadRuntimeGraph(RuntimeGraph); } Dictionary <string, HashSet <string> > apiCompatReferences = new(); if (ReferencePaths != null) { foreach (ITaskItem taskItem in ReferencePaths) { string tfm = taskItem.GetMetadata("TargetFramework"); if (string.IsNullOrEmpty(tfm)) { continue; } string referencePath = taskItem.GetMetadata("Identity"); if (!File.Exists(referencePath)) { continue; } if (!apiCompatReferences.TryGetValue(tfm, out HashSet <string> directories)) { directories = new(); apiCompatReferences.Add(tfm, directories); } directories.Add(referencePath); } } Package package = NupkgParser.CreatePackage(PackageTargetPath, runtimeGraph); CompatibilityLogger logger = new(Log, CompatibilitySuppressionFilePath, GenerateCompatibilitySuppressionFile); new CompatibleTfmValidator(NoWarn, null, RunApiCompat, EnableStrictModeForCompatibleTfms, logger, apiCompatReferences).Validate(package); new CompatibleFrameworkInPackageValidator(NoWarn, null, EnableStrictModeForCompatibleFrameworksInPackage, logger, apiCompatReferences).Validate(package); if (!DisablePackageBaselineValidation && !string.IsNullOrEmpty(BaselinePackageTargetPath)) { Package baselinePackage = NupkgParser.CreatePackage(BaselinePackageTargetPath, runtimeGraph); new BaselinePackageValidator(baselinePackage, NoWarn, null, RunApiCompat, logger, apiCompatReferences).Validate(package); } if (GenerateCompatibilitySuppressionFile) { logger.GenerateSuppressionsFile(CompatibilitySuppressionFilePath); } }
public void MultipleCompatibleFrameworksInPackage() { string name = Path.GetFileNameWithoutExtension(Path.GetTempFileName()); TestProject testProject = new() { Name = name, TargetFrameworks = "netstandard2.0;netcoreapp3.1;net5.0", }; string sourceCode = @" namespace PackageValidationTests { public class First { public void test() { } #if NETSTANDARD2_0 public void test(string test) { } #endif #if NETCOREAPP3_1 public void test(bool test) { } #endif } }"; testProject.SourceFiles.Add("Hello.cs", sourceCode); TestAsset asset = _testAssetsManager.CreateTestProject(testProject, testProject.Name); PackCommand packCommand = new PackCommand(Log, Path.Combine(asset.TestRoot, testProject.Name)); var result = packCommand.Execute(); Assert.Equal(string.Empty, result.StdErr); Package package = NupkgParser.CreatePackage(packCommand.GetNuGetPackage(), null); new CompatibleFrameworkInPackageValidator(string.Empty, null, false, _log).Validate(package); Assert.NotEmpty(_log.errors); string assemblyName = $"{asset.TestProject.Name}.dll"; // TODO: add asserts for assembly and header metadata. Assert.Contains($"CP0002 Member 'PackageValidationTests.First.test(string)' exists on lib/netstandard2.0/{assemblyName} but not on lib/netcoreapp3.1/{assemblyName}", _log.errors); Assert.Contains($"CP0002 Member 'PackageValidationTests.First.test(bool)' exists on lib/netcoreapp3.1/{assemblyName} but not on lib/net5.0/{assemblyName}", _log.errors); }
public void ValidatePackageWithReferences() { TestLogger log = new TestLogger(); string testDependencySource = @"namespace PackageValidationTests { public class ItermediateBaseClass #if NETSTANDARD2_0 : IBaseInterface #endif { } }"; TestProject testSubDependency = CreateTestProject(@"namespace PackageValidationTests { public interface IBaseInterface { } }", "netstandard2.0"); TestProject testDependency = CreateTestProject( testDependencySource, "netstandard2.0;net5.0", new[] { testSubDependency }); TestProject testProject = CreateTestProject(@"namespace PackageValidationTests { public class First : ItermediateBaseClass { } }", "netstandard2.0;net5.0", new[] { testDependency }); TestAsset asset = _testAssetsManager.CreateTestProject(testProject, testProject.Name); PackCommand packCommand = new PackCommand(Log, Path.Combine(asset.TestRoot, testProject.Name)); var result = packCommand.Execute(); Assert.Equal(string.Empty, result.StdErr); Package package = NupkgParser.CreatePackage(packCommand.GetNuGetPackage(), null); // First we run without references. Without references, ApiCompat should not be able to see that class First // removed an interface due to it's base class removing that implementation. We validate that APICompat doesn't // log errors when not using references. new CompatibleFrameworkInPackageValidator("CP1003", null, false, log, null).Validate(package); Assert.Empty(log.errors); // Now we do pass in references. With references, ApiCompat should now detect that an interface was removed in a // dependent assembly, causing one of our types to stop implementing that assembly. We validate that a CP0008 is logged. Dictionary <string, HashSet <string> > references = new() { { "netstandard2.0", new HashSet <string> { Path.Combine(asset.TestRoot, asset.TestProject.Name, "bin", "Debug", "netstandard2.0") } }, { "net5.0", new HashSet <string> { Path.Combine(asset.TestRoot, asset.TestProject.Name, "bin", "Debug", "net5.0") } } };