From c73cc474dd013077327112ce0e50d7ef112e9387 Mon Sep 17 00:00:00 2001 From: dotnet-bot Date: Thu, 16 Jul 2026 13:59:28 +0900 Subject: [PATCH 1/2] Add DependencyAnalysisFramework tests from CoreRT repo --- .../DependencyAnalysisFrameworkTests.cs | 202 ++++++++++++++++++ .../TestGraph.cs | 177 +++++++++++++++ 2 files changed, 379 insertions(+) create mode 100644 src/coreclr/tools/aot/ILCompiler.DependencyAnalysisFramework.Tests/DependencyAnalysisFrameworkTests.cs create mode 100644 src/coreclr/tools/aot/ILCompiler.DependencyAnalysisFramework.Tests/TestGraph.cs diff --git a/src/coreclr/tools/aot/ILCompiler.DependencyAnalysisFramework.Tests/DependencyAnalysisFrameworkTests.cs b/src/coreclr/tools/aot/ILCompiler.DependencyAnalysisFramework.Tests/DependencyAnalysisFrameworkTests.cs new file mode 100644 index 00000000000000..5213f6e4b9b4d8 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.DependencyAnalysisFramework.Tests/DependencyAnalysisFrameworkTests.cs @@ -0,0 +1,202 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Generic; +using System.IO; + +using ILCompiler.DependencyAnalysisFramework; + +using Xunit; + +namespace ILCompiler.DependencyAnalysisFramework.Tests +{ + public class DependencyTests + { + public DependencyTests() + { + } + + /// + /// Test on every graph type. Used to ensure that the behavior of the various markers is consistent + /// + /// + private void TestOnGraphTypes(Action> testGraph) + { + // Test using the full logging strategy + TestGraph testGraphFull = new TestGraph(); + DependencyAnalyzerBase analyzerFull = new DependencyAnalyzer, TestGraph>(testGraphFull, null); + testGraphFull.AttachToDependencyAnalyzer(analyzerFull); + testGraph(testGraphFull, analyzerFull); + + TestGraph testGraphFirstMark = new TestGraph(); + DependencyAnalyzerBase analyzerFirstMark = new DependencyAnalyzer, TestGraph>(testGraphFirstMark, null); + testGraphFirstMark.AttachToDependencyAnalyzer(analyzerFirstMark); + testGraph(testGraphFirstMark, analyzerFirstMark); + + TestGraph testGraphNoLog = new TestGraph(); + DependencyAnalyzerBase analyzerNoLog = new DependencyAnalyzer, TestGraph>(testGraphNoLog, null); + testGraphNoLog.AttachToDependencyAnalyzer(analyzerNoLog); + testGraph(testGraphNoLog, analyzerNoLog); + } + + [Fact] + public void TestADependsOnB() + { + TestOnGraphTypes((TestGraph testGraph, DependencyAnalyzerBase analyzer) => + { + testGraph.AddStaticRule("A", "B", "A depends on B"); + testGraph.AddRoot("A", "A is root"); + List results = testGraph.AnalysisResults; + + Assert.Contains("A", results); + Assert.Contains("B", results); + }); + } + + [Fact] + public void TestADependsOnBIfC_NoC() + { + TestOnGraphTypes((TestGraph testGraph, DependencyAnalyzerBase analyzer) => + { + testGraph.AddConditionalRule("A", "C", "B", "A depends on B if C"); + testGraph.AddRoot("A", "A is root"); + List results = testGraph.AnalysisResults; + + Assert.Contains("A", results); + Assert.DoesNotContain("B", results); + Assert.DoesNotContain("C", results); + Assert.True(results.Count == 1); + }); + } + + [Fact] + public void TestADependsOnBIfC_HasC() + { + TestOnGraphTypes((TestGraph testGraph, DependencyAnalyzerBase analyzer) => + { + testGraph.AddConditionalRule("A", "C", "B", "A depends on B if C"); + testGraph.AddRoot("A", "A is root"); + testGraph.AddRoot("C", "C is root"); + List results = testGraph.AnalysisResults; + + Assert.Contains("A", results); + Assert.Contains("B", results); + Assert.Contains("C", results); + Assert.True(results.Count == 3); + }); + } + + [Fact] + public void TestSimpleDynamicRule() + { + TestOnGraphTypes((TestGraph testGraph, DependencyAnalyzerBase analyzer) => + { + testGraph.SetDynamicDependencyRule((string nodeA, string nodeB) => + { + if (nodeA.EndsWith("*") && nodeB.StartsWith("*")) + { + return new Tuple(nodeA + nodeB, "DynamicRule"); + } + return null; + }); + + testGraph.AddRoot("A*", "A* is root"); + testGraph.AddRoot("B*", "B* is root"); + testGraph.AddRoot("*C", "*C is root"); + testGraph.AddRoot("*D", "*D is root"); + testGraph.AddRoot("A*B", "A*B is root"); + List results = testGraph.AnalysisResults; + + Assert.Contains("A*", results); + Assert.Contains("B*", results); + Assert.Contains("*C", results); + Assert.Contains("*D", results); + Assert.Contains("A*B", results); + Assert.Contains("A**C", results); + Assert.Contains("A**D", results); + Assert.Contains("B**C", results); + Assert.Contains("B**D", results); + Assert.True(results.Count == 9); + }); + } + + private void BuildGraphUsingAllTypesOfRules(TestGraph testGraph, DependencyAnalyzerBase analyzer) + { + testGraph.SetDynamicDependencyRule((string nodeA, string nodeB) => + { + if (nodeA.EndsWith("*") && nodeB.StartsWith("*")) + { + return new Tuple(nodeA + nodeB, "DynamicRule"); + } + return null; + }); + + testGraph.AddConditionalRule("A**C", "B**D", "D", "A**C depends on D if B**D"); + testGraph.AddStaticRule("D", "E", "D depends on E"); + + // Rules to ensure that there are some nodes that have multiple reasons to exist + testGraph.AddStaticRule("A*", "E", "A* depends on E"); + testGraph.AddStaticRule("*C", "E", "*C depends on E"); + + testGraph.AddRoot("A*", "A* is root"); + testGraph.AddRoot("B*", "B* is root"); + testGraph.AddRoot("*C", "*C is root"); + testGraph.AddRoot("*D", "*D is root"); + testGraph.AddRoot("A*B", "A*B is root"); + + List results = testGraph.AnalysisResults; + Assert.Contains("A*", results); + Assert.Contains("B*", results); + Assert.Contains("*C", results); + Assert.Contains("*D", results); + Assert.Contains("A*B", results); + Assert.Contains("A**C", results); + Assert.Contains("A**D", results); + Assert.Contains("B**C", results); + Assert.Contains("B**D", results); + Assert.Contains("D", results); + Assert.Contains("E", results); + Assert.True(results.Count == 11); + } + + [Fact] + public void TestDGMLOutput() + { + Dictionary dgmlOutputs = new Dictionary(); + TestOnGraphTypes((TestGraph testGraph, DependencyAnalyzerBase analyzer) => + { + BuildGraphUsingAllTypesOfRules(testGraph, analyzer); + MemoryStream dgmlOutput = new MemoryStream(); + DgmlWriter.WriteDependencyGraphToStream(dgmlOutput, analyzer, testGraph); + dgmlOutput.Seek(0, SeekOrigin.Begin); + TextReader tr = new StreamReader(dgmlOutput); + dgmlOutputs[analyzer.GetType().FullName] = tr.ReadToEnd(); + }); + + foreach (var pair in dgmlOutputs) + { + int nodeCount = pair.Value.Split(new string[] { " + { + private readonly string _data; + private static readonly CombinedDependencyListEntry[] s_emptyDynamicList = new CombinedDependencyListEntry[0]; + + public TestNode(string data) + { + _data = data; + } + + public string Data + { + get + { + return _data; + } + } + + protected override string GetName(TestGraph context) + { + return _data; + } + + public override bool HasDynamicDependencies + { + get + { + return true; + } + } + + public override IEnumerable SearchDynamicDependencies(List> markedNodes, int firstNode, TestGraph context) + { + if (context._dynamicDependencyComputer == null) + return s_emptyDynamicList; + + IEnumerable returnValue = s_emptyDynamicList; + List returnValueWithData = null; + for (int i = firstNode; i < markedNodes.Count; i++) + { + Tuple nextResult = context._dynamicDependencyComputer(this.Data, ((TestNode)markedNodes[i]).Data); + + if (nextResult != null) + { + if (returnValueWithData == null) + { + returnValueWithData = new List.CombinedDependencyListEntry>(); + returnValue = returnValueWithData; + } + + returnValueWithData.Add(new CombinedDependencyListEntry(context.GetNode(nextResult.Item1), markedNodes[i], nextResult.Item2)); + } + } + + return returnValue; + } + } + + Dictionary>> _staticNonConditionalRules = new Dictionary>>(); + Dictionary>>> _staticConditionalRules = new Dictionary>>>(); + + Func> _dynamicDependencyComputer; + Dictionary _nodes = new Dictionary(); + DependencyAnalyzerBase _analyzer; + + public void AddStaticRule(string depender, string dependedOn, string reason) + { + HashSet> knownEdges = null; + if (!_staticNonConditionalRules.TryGetValue(depender, out knownEdges)) + { + knownEdges = new HashSet>(); + _staticNonConditionalRules[depender] = knownEdges; + } + knownEdges.Add(new Tuple(dependedOn, reason)); + } + + public void AddConditionalRule(string depender, string otherdepender, string dependedOn, string reason) + { + HashSet>> knownEdges = null; + if (!_staticConditionalRules.TryGetValue(depender, out knownEdges)) + { + knownEdges = new HashSet>>(); + _staticConditionalRules[depender] = knownEdges; + } + + knownEdges.Add(new Tuple>(dependedOn, new Tuple(otherdepender, reason))); + } + + public void SetDynamicDependencyRule(Func> dynamicDependencyComputer) + { + _dynamicDependencyComputer = dynamicDependencyComputer; + } + + public void AddRoot(string nodeNode, string reason) + { + _analyzer.AddRoot(this.GetNode(nodeNode), reason); + } + + public TestNode GetNode(string nodeName) + { + TestNode node; + if (!_nodes.TryGetValue(nodeName, out node)) + { + node = new TestNode(nodeName); + _nodes.Add(nodeName, node); + } + + return node; + } + + public void AttachToDependencyAnalyzer(DependencyAnalyzerBase analyzer) + { + analyzer.ComputeDependencyRoutine += Analyzer_ComputeDependencyRoutine; + _analyzer = analyzer; + } + + private void Analyzer_ComputeDependencyRoutine(List> obj) + { + foreach (TestNode node in obj) + { + List staticList = new List.DependencyListEntry>(); + List conditionalStaticList = new List.CombinedDependencyListEntry>(); + + HashSet> nonConditionalRules; + if (_staticNonConditionalRules.TryGetValue(node.Data, out nonConditionalRules)) + { + foreach (Tuple dependedOn in nonConditionalRules) + { + staticList.Add(new TestNode.DependencyListEntry(GetNode(dependedOn.Item1), dependedOn.Item2)); + } + } + + HashSet>> conditionalRules; + if (_staticConditionalRules.TryGetValue(node.Data, out conditionalRules)) + { + foreach (Tuple> dependedOn in conditionalRules) + { + conditionalStaticList.Add(new TestNode.CombinedDependencyListEntry(GetNode(dependedOn.Item1), GetNode(dependedOn.Item2.Item1), dependedOn.Item2.Item2)); + } + } + + node.SetStaticDependencies(staticList, conditionalStaticList); + } + } + + public List AnalysisResults + { + get + { + List liveNodes = new List(); + + _analyzer.ComputeMarkedNodes(); + + foreach (var node in _analyzer.MarkedNodeList) + { + liveNodes.Add(((TestNode)node).Data); + } + + return liveNodes; + } + } + } +} From c9a00a5d86a6f3fd87458fcf3b342dad6a34f4a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20Strehovsk=C3=BD?= Date: Thu, 16 Jul 2026 13:59:51 +0900 Subject: [PATCH 2/2] Hook the new tests to build --- eng/Subsets.props | 2 ++ ...r.DependencyAnalysisFramework.Tests.csproj | 20 +++++++++++++++++++ 2 files changed, 22 insertions(+) create mode 100644 src/coreclr/tools/aot/ILCompiler.DependencyAnalysisFramework.Tests/ILCompiler.DependencyAnalysisFramework.Tests.csproj diff --git a/eng/Subsets.props b/eng/Subsets.props index be4331b5e2fda6..4cac6ef4c52e6b 100644 --- a/eng/Subsets.props +++ b/eng/Subsets.props @@ -502,6 +502,8 @@ + + + ILCompiler.DependencyAnalysisFramework.Tests + $(NetCoreAppToolCurrent) + Debug;Release;Checked + -notrait category=failing + + $(NoWarn);NU1701 + x86;x64 + AnyCPU + + + + + + + + + +