public AffectedGraph GetAffectedGraphForChangeSet(IEnumerable<Change<MethodReference>> changes) { InvokeMinimizerMessage(MessageType.Debug, "getting tests for changes"); var start = DateTime.Now; var graph = new AffectedGraph(); var breadCrumbs = new Dictionary<string, bool>(); var affectedItems = GetAffectedItems(changes, graph); var rets = affectedItems.ForkAndJoinTo(_numthreads, x => { var tmp = new AffectedGraph(); FillAfferentGraph(x.Member, breadCrumbs, tmp, null, 0, null, false, new List<string>(), GenericContext.Empty(), false); return tmp; }); InvokeMinimizerMessage(MessageType.Debug, "there are " + rets.Count() + " graphs returned. Combining."); foreach(var g in rets) { if (g != null) { InvokeMinimizerMessage(MessageType.Debug, "there are " + g.AllNodes().Count() + " Nodes in graph. TOTAL=" + graph.AllNodes().Count()); graph = graph.Merge(g); } } InvokeMinimizerMessage(MessageType.Debug, "there are " + rets.Count() + " nodes in combined graph."); var end = DateTime.Now; InvokeMinimizerMessage(MessageType.Debug, "took " + (end - start) + " to walk graph"); return graph; }
public void adding_a_null_node_does_not_add_node() { var g = new AffectedGraph(); g.AddNode(null); Assert.AreEqual(0, g.AllNodes().Count()); }
public void adding_a_null_to_connection_does_not_add_node() { var g = new AffectedGraph(); g.AddConnection("FROM", null, false); Assert.AreEqual(0, g.AllConnections().Count()); }
public void when_no_root_node_get_root_node_returns_null() { var g = new AffectedGraph(); g.AddNode(new AffectedGraphNode("foo", false, false, false, "name", "bar::foo", "assembly", "type", new List <TestDescriptor>(), false, false, 0)); Assert.IsNull(g.GetRootNode()); }
public void node_and_direct_test_in_graph() { var graph = new AffectedGraph(); graph.AddNode(new AffectedGraphNode(displayName: "display", isInterface: false, isTest: false, isRootNode: true, name: "A name", fullName: "fullnamecode", assembly: "foo.dll", type: "Type", testDescriptors: new List <TestDescriptor>(), isChange: false, inTestAssembly: false, complexity: 0)); graph.AddNode(new AffectedGraphNode(displayName: "display", isInterface: false, isTest: true, isRootNode: true, name: "A name", fullName: "fullnametest", assembly: "foo.dll", type: "Type", testDescriptors: new List <TestDescriptor>(), isChange: false, inTestAssembly: true, complexity: 0)); graph.AddConnection("fullnamecode", "fullnametest", false); var classifier = new TestPathsGraphRiskClassifier(); Assert.AreEqual(100, classifier.CalculateRiskFor(graph)); }
public void connection_can_be_added_between_same_nodes() { var g = new AffectedGraph(); g.AddNode(new AffectedGraphNode("foo", false, false, false, "name", "bar::foo", "assembly", "type", new List <TestDescriptor>(), false, false, 0)); g.AddConnection("bar::foo", "bar::foo", false); Assert.AreEqual(1, g.AllConnections().Count()); }
public void a_connection_from_a_non_existant_node_does_not_add_connection() { var g = new AffectedGraph(); g.AddNode(new AffectedGraphNode("foo", false, false, false, "name", "bar::foo", "assembly", "type", new List <TestDescriptor>(), false, false, 0)); g.AddConnection("somethingnon-existant", "bar::foo", false); Assert.AreEqual(0, g.AllConnections().Count()); }
public void empty_graphs_merge_to_empty_graph() { var graph1 = new AffectedGraph(); var graph2 = new AffectedGraph(); var merged = graph1.Merge(graph2); Assert.AreEqual(0, merged.AllNodes().Count()); }
public void when_root_node_get_root_node_returns_root_node() { var g = new AffectedGraph(); var root = new AffectedGraphNode("foo", false, false, true, "name", "bar::foo", "assembly", "type", new List <TestDescriptor>(), false, false, 0); g.AddNode(root); g.AddNode(new AffectedGraphNode("foo", false, false, false, "name", "bar::foo2", "assembly", "type", new List <TestDescriptor>(), false, false, 0)); Assert.AreEqual(root, g.GetRootNode()); }
public void multiple_connections_can_be_added_between_two_valid_nodes_but_only_one_appears() { var g = new AffectedGraph(); g.AddNode(new AffectedGraphNode("foo", false, false, false, "name", "bar::foo", "assembly", "type", new List <TestDescriptor>(), false, false, 0)); g.AddNode(new AffectedGraphNode("foo", false, false, false, "name", "bar::foo2", "assembly", "type", new List <TestDescriptor>(), false, false, 0)); g.AddConnection("bar::foo2", "bar::foo", false); g.AddConnection("bar::foo2", "bar::foo", false); Assert.AreEqual(1, g.AllConnections().Count()); }
public void a_node_can_be_added() { var g = new AffectedGraph(); g.AddNode(new AffectedGraphNode("foo", false, false, false, "name", "bar::foo", "assembly", "type", new List <TestDescriptor>(), false, false, 0)); Assert.AreEqual(1, g.AllNodes().Count()); Assert.AreEqual("foo", g.AllNodes().First().DisplayName); Assert.AreEqual("bar::foo", g.AllNodes().First().FullName); Assert.IsNotNull(g.GetNode("bar::foo")); }
public void SetUp() { var paths = new[] { new List <string> { "GrandChild", "Child3", "Root" }, }; _graph = GraphBuilder.BuildGraphFor(paths); }
public void nodes_from_both_graphs_get_added_to_output() { var graph1 = new AffectedGraph(); graph1.AddNode(new AffectedGraphNode("test1", false, false, false, "test", "test12", "assembly", "type", new List <TestDescriptor>(), false, false, 0)); var graph2 = new AffectedGraph(); graph2.AddNode(new AffectedGraphNode("test1", false, false, false, "test", "test123", "assembly", "type", new List <TestDescriptor>(), false, false, 0)); var merged = graph1.Merge(graph2); Assert.AreEqual(2, merged.AllNodes().Count()); }
private void EnrichGraphWithProfilerInformation(string name, AffectedGraph graph) { var profilerOff = _configuration.AllSettings("mm-ProfilerSetup") == "DONTRUN"; var items = _profilerData.GetTestsFor(name); foreach (var entry in items) { var node = graph.GetNode(entry); if (node != null) { node.MarkAsProfiled(); } } }
public int CalculateRiskFor(AffectedGraph graph) { //TODO make interface take the hash not the graph (so it canbe reused between strategies instead of built n times) if (graph == null) return 0; var root = graph.GetRootNode(); if (root == null) return 0; var visited = new Dictionary<string, int>(); var hash = GraphNodeHashBuilder.GetHashFrom(graph); var testsScore = RecurseFrom(root.FullName, hash, 0, 0, visited); var complexity = root.Complexity > 1.0m ? root.Complexity : 1.0m; var overallScore = testsScore/complexity; var ret = overallScore > 1.0m ? 1.0m : overallScore; return (int) (ret * 100m); }
public AffectedGraph GetCouplingGraphFor(string cacheName) { var affected = new List<ChangeContext>(); var graph = new AffectedGraph(); var forwardCutOffPoints = new Dictionary<string, bool>(); var backCutOffPoints = new Dictionary<string, bool>(); TypeDefinition scope = null; GetEfferentGraph(null, cacheName, forwardCutOffPoints, affected, graph, true, scope); WriteWalkerDebug("got " + affected + " nodes for " + cacheName, 0); var rets = affected.ForkAndJoinTo(_numthreads, x => { FillAfferentGraph(x.Member, backCutOffPoints, graph, null, 0, null, false, new List<string>(), GenericContext.Empty(), false); //gets all tests Ca return true; }); return graph; }
public int CalculateRiskFor(AffectedGraph g) { var root = g.AllNodes().FirstOrDefault(x => x.IsRootNode); if (root == null) { return(-1); } var connections = GetHashFrom(g); var risk = RecurseRisk(root.FullName, connections, new Dictionary <string, bool>()); if (risk.nottested + risk.tested == 0) { return(0); } return((int)(risk.tested / (decimal)(risk.nottested + risk.tested) * 100.0m)); }
public void single_node_in_graph_that_is_not_a_test() { var graph = new AffectedGraph(); graph.AddNode(new AffectedGraphNode(displayName: "display", isInterface: false, isTest: false, isRootNode: true, name: "A name", fullName: "name", assembly: "foo.dll", type: "Type", testDescriptors: new List <TestDescriptor>(), isChange: false, inTestAssembly: false, complexity: 0)); var classifier = new TestPathsGraphRiskClassifier(); Assert.AreEqual(0, classifier.CalculateRiskFor(graph)); }
public void single_node_nodein_graph_that_is_a_covered_test() { var graph = new AffectedGraph(); graph.AddNode(new AffectedGraphNode(displayName: "display", isInterface: false, isTest: true, isRootNode: true, name: "A name", fullName: "name", assembly: "foo.dll", type: "Type", testDescriptors: new List <TestDescriptor>(), isChange: false, inTestAssembly: false, complexity: 0)); graph.AllNodes().First().MarkAsProfiled(); var classifier = new CoverageDistanceAndComplexityGraphRiskClassifier(); Assert.AreEqual(100, classifier.CalculateRiskFor(graph)); }
public int CalculateRiskFor(AffectedGraph graph) { if (graph == null) { return(0); } var root = graph.GetRootNode(); if (root == null) { return(0); } var connections = GraphNodeHashBuilder.GetHashFrom(graph); var risk = RecurseRisk(root.FullName, connections, new Dictionary <string, bool>()); if (risk.nottested + risk.tested == 0) { return(0); } return((int)(risk.tested / (decimal)(risk.nottested + risk.tested) * 100.0m)); }
public AffectedGraph GetProfiledGraphFor(string method) { var graph = new AffectedGraph(); var tests = GetTestsFor(method); if (tests == null || !tests.Any()) { return(graph); } var paths = new List <IEnumerable <string> >(); foreach (var test in tests) { var info = GetTestInformationFor(test); if (info != null) { var path = PathFinder.FindPathsTo(info, method); paths.AddRange(path); } } return(GraphBuilder.BuildGraphFor(paths)); }
private Dictionary <string, RiskNode> GetHashFrom(AffectedGraph graph) { var ret = new Dictionary <string, RiskNode>(); foreach (var node in graph.AllNodes()) { ret.Add(node.FullName, new RiskNode() { Node = node }); } foreach (var connection in graph.AllConnections()) { RiskNode item; if (!ret.TryGetValue(connection.From, out item)) { ret.Add(connection.From, item); } item.connections.Add(connection.To); } return(ret); }
public static AffectedGraph BuildGraphFor(IEnumerable <IEnumerable <string> > paths) { bool first = true; var ret = new AffectedGraph(); foreach (var path in paths) { string last = null; foreach (var node in path) { if (!ret.ContainsNode(node)) { ret.AddNode(new AffectedGraphNode(node, false, false, first, node, node, "", "", new List <TestDescriptor>(), false, false, 0)); first = false; } if (last != null) { ret.AddConnection(last, node, false); } last = node; } } return(ret); }
public void EnrichGraph(AffectedGraph graph) { _cache.EnrichGraph(graph); }
private IEnumerable<TestEntry> BuildEntriesFor(AffectedGraph graph) { InvokeMinimizerMessage(MessageType.Info, "there are " + graph.AllNodes().Count() + "in the graph."); foreach(var n in graph.AllNodes()) { if (n.TestDescriptors == null) continue; foreach (var desc in n.TestDescriptors) { yield return new TestEntry { TestAssembly = n.Assembly, TestClass = n.Type, TestName = desc.Target, TestRunners = new List<string> {desc.TestRunner} }; } } }
public void EnrichGraph(AffectedGraph graph) { foreach (var node in graph.AllNodes()) { var cachenode = TryGetEfferentCouplingNode(node.FullName.Replace('+', '/')); if (cachenode != null) { var r = cachenode.MemberReference; if (r != null) { node.DisplayName = r.DeclaringType.Name + "::" + r.Name; node.Assembly = r.Module.Assembly.FullName; node.IsInterface = r.DeclaringType.ThreadSafeResolve().IsInterface; } node.Profiled = true; node.IsTest = _testIdentifiers.IsTest(cachenode.MemberReference); } } }
public void adding_null_node_throws_argument_null_exception() { var graph = new AffectedGraph(); //Assert.Throws<ArgumentNullException>(() => graph.AddNode(null)); }
private void FillAfferentGraph(string fullName, Dictionary<string, bool> cutPoints, AffectedGraph graph, MemberReference parent, int depth, TypeDefinition inheritedTypeContext, bool inVirtual, List<string> breadCrumbs, GenericContext genericContext, bool inSelf) { WriteWalkerDebug(fullName, depth); if (TryBreadCrumbsPrune(fullName, depth, breadCrumbs)) { if (parent != null) { WriteWalkerDebug("truncating but adding connection to " + fullName, depth); graph.AddConnection(parent.GetCacheName(), fullName, false); } return; } breadCrumbs.Add(fullName); if (TryCutPointPrune(fullName, depth, cutPoints)) { if (parent != null) { WriteWalkerDebug("bread crumbs truncating but adding connection to " + fullName, depth); graph.AddConnection(parent.GetCacheName(), fullName, false); } return; } var efferentEntry = _cache.TryGetEfferentCouplingNode(fullName); var afferentEntry = _cache.TryGetEfferentCouplingNode(fullName); MethodDefinition currentDefinition = null; if (efferentEntry != null) { TypeReference parentType = null; if (parent != null) parentType = parent.DeclaringType; var definition = efferentEntry.MemberReference.DeclaringType.ThreadSafeResolve(); if (TryInterfacePrune(fullName, depth, breadCrumbs, efferentEntry, parentType, definition)) return; if (TryInheritancePrune(fullName, depth, breadCrumbs, inVirtual, definition, inheritedTypeContext)) {return;} inheritedTypeContext = GetNewTypeContext(inheritedTypeContext, definition); currentDefinition = efferentEntry.MemberReference as MethodDefinition; if (currentDefinition != null) { if(currentDefinition.DeclaringType == null) {} if (parent is FieldReference && currentDefinition.IsConstructor) { breadCrumbs.Remove(fullName); return; } } } var touse = afferentEntry ?? efferentEntry; WriteWalkerDebug("adding node " + fullName, depth); var newnode = GetGraphNode(fullName, touse, parent == null, false); graph.AddNode(newnode); if (parent != null) { graph.AddConnection(parent.GetCacheName(), newnode.FullName, false); } if(currentDefinition != null) RecurseSynonyms(depth, cutPoints, inheritedTypeContext, currentDefinition, graph, breadCrumbs, genericContext); RecurseAfferentCouplings(fullName, depth, cutPoints, inVirtual, inheritedTypeContext, graph, parent, breadCrumbs, genericContext); breadCrumbs.Remove(fullName); }
private IEnumerable<ChangeContext> GetAffectedItems(IEnumerable<Change<MethodReference>> changes, AffectedGraph graph) { var affected = new List<ChangeContext>(); foreach (var change in changes) { var forwardCutOffPoints = new Dictionary<string, bool>(); if (change.ChangeType == ChangeType.Remove) continue; change.ItemChanged.GetCacheName(); var scope = change.ItemChanged.DeclaringType.ThreadSafeResolve(); GetEfferentGraph(null, change.ItemChanged.GetCacheName(), forwardCutOffPoints, affected, graph, true, scope); } return affected; }
static IEnumerable<TestDescriptor> GetTestsInGraph(AffectedGraph graph) { return graph.AllNodes().Where(entry => entry.IsTest).SelectMany(entry => entry.TestDescriptors); }
private static void ConvertToGraphMessage(VisualGraphGeneratedMessage message, AffectedGraph graph) { foreach (var node in graph.AllNodes()) { message.Nodes.Add(new GraphNode { Assembly = node.Assembly, DisplayName = node.DisplayName, FullName = node.FullName, IsInterface = node.IsInterface, IsChange = node.IsChange, IsRootNode = node.IsRootNode, IsTest = node.IsTest, Name = node.Name, Type = node.Type, InTestAssembly = node.InTestAssembly, IsProfiledTest = node.Profiled, Complexity = node.Complexity, }); } foreach (var c in graph.AllConnections()) { message.Connections.Add(new Connection { From = c.From, To = c.To }); } }
private static void ConvertToGraphMessage(VisualGraphGeneratedMessage message, AffectedGraph graph) { foreach (var node in graph.AllNodes()) { message.Nodes.Add(new GraphNode { Assembly = node.Assembly, DisplayName = node.DisplayName, FullName = node.FullName, IsInterface = node.IsInterface, IsChange = node.IsChange, IsRootNode = node.IsRootNode, IsTest = node.IsTest, Name = node.Name, Type = node.Type, InTestAssembly = node.InTestAssembly, IsProfiledTest = node.Profiled, Complexity = node.Complexity, }); } foreach (var c in graph.AllConnections()) { message.Connections.Add(new Connection {From = c.From, To = c.To}); } }
private void RecurseSynonyms(int depth, Dictionary<string, bool> history, TypeDefinition inheritedTypeContext, MethodDefinition currentDefinition, AffectedGraph graph, List<string> breadCrumbs, GenericContext genericContext) { var synonyms = SynonymFinder.FindSynonymsFor(currentDefinition); foreach (var current in synonyms) { var c = current as MethodDefinition; if (c == null) continue; //shouldn't happen if (!c.DeclaringType.IsInterface) { FillAfferentGraph(current.GetCacheName(), history, graph, currentDefinition, depth + 1, inheritedTypeContext, true, breadCrumbs, genericContext, true); //always a base } else { FillAfferentGraph(current.GetCacheName(), history, graph, currentDefinition, depth + 1, inheritedTypeContext, false, breadCrumbs, genericContext, false); } } }
private void RecurseAfferentCouplings(string fullName, int depth, Dictionary<string, bool> history, bool inInheritanceChain, TypeDefinition inheritedTypeContext, AffectedGraph graph, MemberReference parent, List<string> breadCrumbs, GenericContext genericContext) { var afferentEntry = _cache.TryGetAfferentCouplingNode(fullName); if (afferentEntry == null) return; genericContext.SetIndirectConstraintsOn(afferentEntry.MemberReference, parent, inInheritanceChain); if (!inInheritanceChain && genericContext.IsClear()) { lock (history) //double lock, TryCutPointPrune already did this { if (!history.ContainsKey(fullName)) { history.Add(fullName, true); } } } foreach (var current in afferentEntry.Couplings) { if (current.IgnoreWalk) continue; var proposed = genericContext.GetGenericContextOf(current.ActualReference); if (!genericContext.CanTransitionTo(proposed)) { WriteWalkerDebug("Generics truncate", depth); continue; } var newContext = genericContext.TransitionTo(proposed); FillAfferentGraph(current.To, history, graph, afferentEntry.MemberReference, depth + 1, inheritedTypeContext, inInheritanceChain && current.IsSelfCall, breadCrumbs, newContext, current.IsSelfCall); } }
public List<TestEntry> GetTestsFor(List<string> assemblies) { var ret = new List<TestEntry>(); var param = from a in assemblies select new BuildFullMapParams(a, a); BuildFullMap(param); toDispose.Clear(); changes = GetChanges(assemblies); if (changes != null) { foreach (var change in changes) { InvokeMinimizerMessage(MessageType.Debug, change.ChangeType + " " + change.ItemChanged.GetCacheName()); } ReIndex(changes); } toDispose.ForEach(x => x.Dispose()); ModuleDefinition.KillAllReadModules(); var strategy = new GraphBuilder(_cache, _testStrategies, new IfInTestAssemblyContinueInterfaceFollowingStrategy(_cache), _debug, _numThreads); strategy.DebugMessage += (x,y) => InvokeMinimizerMessage(y.MessageType, y.Message); _lastAffectedGraph = strategy.GetAffectedGraphForChangeSet(changes); var entries = BuildEntriesFor(_lastAffectedGraph).ToList(); foreach(var file in assemblies) WriteHistoryFile(file); return entries; }
private void GetEfferentGraph(string parent, string fullName, IDictionary<string, bool> cutOffs, List<ChangeContext> found, AffectedGraph graph, bool isRootNode, TypeDefinition originalContext) { var entry = _cache.TryGetEfferentCouplingNode(fullName); var afferent = _cache.TryGetAfferentCouplingNode(fullName); if (isRootNode) { entry = entry ?? afferent; found.Add(new ChangeContext(fullName, null)); graph.AddNode(GetGraphNode(fullName, entry, true, true)); if (entry != null && entry.MemberReference != null) { var refer = entry.MemberReference as MethodReference; if (refer != null && _testStrategies.IsTest(refer)) { return; } } } //TODO Greg this hamstrings some stuff but should work better in most cases //I think we only really need statics past here but need to look it over a bit. return; WriteWalkerDebug("checking " + fullName, 0); if (entry == null) { WriteWalkerDebug("efferent entry is null", 0); if (afferent != null && afferent.MemberReference is MethodReference) return; //Leaf node to a method call } if(afferent == null) { WriteWalkerDebug("afferent entry is null", 0); } if (afferent != null) { var memref = afferent.MemberReference as MethodReference; MethodDefinition def = null; if (memref != null) { def = memref.ThreadSafeResolve(); } if (!isRootNode && def != null) { if (def.IsGetter) return; if (def.DeclaringType.IsInterface) return; //don't recurse forward on interface calls if (!def.HasThis) return; } var reference = afferent.MemberReference as FieldReference; //Leaf node to a field reference if (reference != null) { graph.AddNode(GetGraphNode(fullName, afferent, isRootNode, false)); if (parent != null) graph.AddConnection(parent, fullName, true); found.Add(new ChangeContext(fullName, originalContext)); } } if (entry == null) return; graph.AddNode(GetGraphNode(fullName, entry, isRootNode, false)); if (parent != null) graph.AddConnection(parent, fullName, true); foreach (var current in entry.Couplings) { if (cutOffs.ContainsKey(current.To)) continue; if (current.IgnoreWalk) continue; if (current.ActualReference is MethodReference && !current.IsSelfCall) continue; cutOffs.Add(current.To, true); GetEfferentGraph(fullName, current.To, cutOffs, found, graph, false, originalContext); } }