public void When_comparing_testProject_reporting_works() { Stream htmlReport = new MemoryStream(); Stream xmlReport = new MemoryStream(); Assembly newAssembly = Assembly.ReflectionOnlyLoadFrom(@"TestProject\Version2\ApiCheckTestProject.dll"); Assembly referenceAssembly = Assembly.ReflectionOnlyLoadFrom(@"TestProject\Version1\ApiCheckTestProject.dll"); int logCount = 0; int returnValue = -1; Assert.DoesNotThrow( () => returnValue = ApiComparer.CreateInstance(referenceAssembly, newAssembly) .WithDetailLogging(s => logCount++) .WithInfoLogging(s => logCount++) .WithHtmlReport(htmlReport) .WithXmlReport(xmlReport) .Build() .CheckApi()); Assert.Greater(htmlReport.Length, 0); Assert.Greater(xmlReport.Length, 0); Assert.Greater(logCount, 0); Assert.AreEqual(62, returnValue); }
private IEnumerable <ApiTestData> GetResults() { using (AssemblyLoader assemblyLoader = new AssemblyLoader()) { object[] customAttributes = GetType().GetCustomAttributes(typeof(ApiTestAttribute), true); if (customAttributes.Length == 0) { throw new Exception(string.Format("The test class should define the {0} at least once", typeof(ApiTestAttribute))); } foreach (object customAttribute in customAttributes) { ApiTestAttribute apiTestAttribute = (ApiTestAttribute)customAttribute; Stream comparerConfigurationStream = GetReadStream(apiTestAttribute.ComparerConfigurationPath); ComparerConfiguration comparerConfiguration = ConfigurationLoader.LoadComparerConfiguration(comparerConfigurationStream); ApiComparer apiComparer = ApiComparer.CreateInstance(assemblyLoader.ReflectionOnlyLoad(apiTestAttribute.ReferenceVersionPath), assemblyLoader.ReflectionOnlyLoad(apiTestAttribute.NewVersionPath)) .WithComparerConfiguration(comparerConfiguration) .Build(); apiComparer.CheckApi(); yield return(new ApiTestData(apiComparer.ComparerResult, apiTestAttribute.Category, apiTestAttribute.Explicit, apiTestAttribute.HandleWarningsAsErrors)); } } }
private static void HasNotChanged(Type type) { var projectResolver = new ProjectResolver(); var typeLoader = new ReflectionTypeLoader(); var projectInfo = projectResolver.GetProjectInfo(type); var api = typeLoader.LoadApi(type); Directory.CreateDirectory(projectInfo.TestFolderPath); if (!projectResolver.ApiFileExists(projectInfo, type)) { projectResolver.WriteApiToFile(projectInfo, type, api); return; } var existingApi = projectResolver.ReadApiFromFile(projectInfo, type); var compareStrategy = new BestGuessEndpointMatchingStrategy(); var comparer = new ApiComparer(compareStrategy); comparer.Compare(existingApi, api); // Nice to have: // Fields / Events / Delegates // Supporting constructor arguments on attributes // Multiple public types per file // custom struct as type // Re-generate the output file if there are changes to the API that aren't breaking }
public void When_passing_null_should_not_throw() { Assembly assembly1 = ApiBuilder.CreateApi().Build(); Assembly assembly2 = ApiBuilder.CreateApi().Build(); Assert.DoesNotThrow( () => ApiComparer.CreateInstance(assembly1, assembly2).WithDetailLogging(null).WithHtmlReport(null).WithComparerConfiguration(null).WithInfoLogging(null).WithXmlReport(null).Build().CheckApi()); }
public int CheckAssemblies() { return(ApiComparer.CreateInstance(_referenceAssembly, _newAssembly) .WithComparerConfiguration(_comparerConfiguration) .WithDetailLogging(_log) .WithInfoLogging(_log) .WithHtmlReport(_htmlStream) .WithXmlReport(_xmlStream) .Build().CheckApi()); }
public void When_comparing_two_assemblies_reports_are_generated() { Assembly assembly1 = ApiBuilder.CreateApi().Class().Build().Build(); Assembly assembly2 = ApiBuilder.CreateApi().Class().Build().Build(); using (MemoryStream xmlStream = new MemoryStream()) using (MemoryStream htmlStream = new MemoryStream()) { ApiComparer.CreateInstance(assembly1, assembly2).WithHtmlReport(htmlStream).WithXmlReport(xmlStream).Build().CheckApi(); Assert.Greater(htmlStream.Length, 0); Assert.Greater(xmlStream.Length, 0); } }
private IEnumerable <ApiTestData> GetResults() { using (AssemblyLoader assemblyLoader = new AssemblyLoader()) { object[] customAttributes = GetType().GetCustomAttributes(typeof(ApiTestAttribute), true); if (customAttributes.Length == 0) { throw new Exception(string.Format("The test class should define the {0} at least once", typeof(ApiTestAttribute))); } foreach (object customAttribute in customAttributes) { ApiTestAttribute apiTestAttribute = (ApiTestAttribute)customAttribute; ApiComparer apiComparer = ApiComparer.CreateInstance(assemblyLoader.ReflectionOnlyLoad(apiTestAttribute.ReferenceVersionPath), assemblyLoader.ReflectionOnlyLoad(apiTestAttribute.NewVersionPath)) .Build(); apiComparer.CheckApi(); IList <string> ignoreList = IgnoreListLoader.LoadIgnoreList(GetReadStream(apiTestAttribute.IgnoreListPath)); yield return(new ApiTestData(apiComparer.ComparerResult, apiTestAttribute.Category, ignoreList, apiTestAttribute.Explicit)); } } }
public void When_checking_with_logging_should_write_to_stream() { Assembly assembly1 = ApiBuilder.CreateApi().Class().Build().Build(); Assembly assembly2 = ApiBuilder.CreateApi().Class().Build().Build(); using (MemoryStream detailStream = new MemoryStream()) using (MemoryStream infoStream = new MemoryStream()) { StreamWriter infoLog = new StreamWriter(infoStream) { AutoFlush = true }; StreamWriter detailLog = new StreamWriter(detailStream) { AutoFlush = true }; ApiComparer.CreateInstance(assembly1, assembly2).WithInfoLogging(infoLog.WriteLine).WithDetailLogging(detailLog.WriteLine).Build().CheckApi(); Assert.Greater(infoStream.Length, 0); Assert.Greater(detailStream.Length, 0); } }
void RunApiCheck(string packageName) { Console.WriteLine(">>>> Checking Api breaking change for: " + packageName + Environment.NewLine); var refAssemblyPath = Path.Combine(Directory.GetCurrentDirectory(), @"..\..\..\Resource\refAssemblies\" + packageName + ".dll"); var devAssemblyPath = Path.Combine(Directory.GetCurrentDirectory(), @"..\..\..\Resource\devAssemblies\" + packageName + ".dll"); var reportPath = Path.Combine(Directory.GetCurrentDirectory(), @"..\..\..\Resource\reports\" + packageName + ".report.xml"); var sb = new StringBuilder(); var succeed = true; try { using (AssemblyLoader assemblyLoader = new AssemblyLoader()) { // load assemblies Assembly refAssembly = assemblyLoader.ReflectionOnlyLoad(refAssemblyPath); Assembly devAssembly = assemblyLoader.ReflectionOnlyLoad(devAssemblyPath); // configuration ComparerConfiguration configuration = new ComparerConfiguration(); configuration.Severities.ParameterNameChanged = Severity.Warning; configuration.Severities.AssemblyNameChanged = Severity.Hint; foreach (var allowedBreakingChange in _allowedApiBreakingChanges) { configuration.Ignore.Add(allowedBreakingChange); } // compare assemblies and write xml report using (var stream = new FileStream(reportPath, FileMode.Create)) { ApiComparer.CreateInstance(refAssembly, devAssembly) .WithComparerConfiguration(configuration) .WithDetailLogging(s => Console.WriteLine(s)) .WithInfoLogging(s => Console.WriteLine(s)) .WithXmlReport(stream) .Build() .CheckApi(); } } var scenarioList = new List <string>() { "ChangedAttribute", "ChangedElement", "RemovedElement" }; // check the scenarios that we might have a breaking change foreach (var scenario in scenarioList) { XElement doc = XElement.Load(reportPath); foreach (XElement change in doc.Descendants(scenario)) { if (change.Attribute("Severity") != null && "Error".Equals(change.Attribute("Severity").Value)) { succeed = false; // append the parent, for instance, if (change.Parent != null) { sb.AppendLine($"In {change.Parent.Attribute("Context").Value} : {change.Parent.Attribute("Name").Value}"); } sb.AppendLine(change.ToString()); } } } } catch (Exception ex) { throw new ApiChangeException("Assembly comparison failed.", ex); } if (!succeed) { throw new ApiChangeException($"The following breaking changes are found: {Environment.NewLine} {sb.ToString()}"); } }
public void When_passing_invalid_values_should_throw_exception() { Assert.Throws <ArgumentNullException>(() => ApiComparer.CreateInstance(null, null)); }