/// <summary> /// Analyzes the resulting metrics file and compares the metrics against the threshold values /// </summary> /// <param name="parent">The parent node in the build log</param> /// <param name="metrics">List of metric values</param> /// <param name="thresholds">Thresholds for errors and warnings </param> /// <param name="fullyQualifiedName">FQN for the method/type under test</param> /// <param name="currentName">Name of current method/type </param> /// <returns>Number if violations</returns> private int ProcessMetrics(IBuildInformationNode parent, IEnumerable <Metric> metrics, SpecificMetricThresholds thresholds, string fullyQualifiedName, string currentName) { var thecomplainlist = new List <string>(); foreach (var metric in metrics.Where(metric => metric != null && !string.IsNullOrEmpty(metric.Value))) { switch (metric.Name) { case MaintainabilityIndex: this.CheckLimits(metric, thresholds.MIMetricChecker, fullyQualifiedName, thecomplainlist); break; case CyclomaticComplexity: this.CheckLimits(metric, thresholds.CCMetricChecker, fullyQualifiedName, thecomplainlist); break; case ClassCoupling: this.CheckLimits(metric, thresholds.COMetricChecker, fullyQualifiedName, thecomplainlist); break; case DepthOfInheritance: this.CheckLimits(metric, thresholds.DOIMetricChecker, fullyQualifiedName, thecomplainlist); break; case LinesOfCode: this.CheckLimits(metric, thresholds.LOCMetricChecker, fullyQualifiedName, thecomplainlist); break; default: throw new UnknownMetricNameException(metric.Name); } } if (this.logCodeMetrics && parent != null && thecomplainlist.Count > 0) { var result = "Metrics for " + currentName + @"\n"; thecomplainlist.ForEach(s => result += s + @"\n"); BaseCodeActivity.AddTextNode(result, parent); } return(thecomplainlist.Count); }
/// <summary> /// Executes the logic for this workflow activity /// </summary> protected override void InternalExecute() { this.logCodeMetrics = this.LogCodeMetrics.Get(this.ActivityContext); this.BuildDetail = this.ActivityContext.GetExtension <IBuildDetail>(); string generatedFile = "Metrics.xml"; string outputLocation = this.BuildDetail.DropLocation; if (string.IsNullOrEmpty(outputLocation)) { outputLocation = this.BinariesDirectory.Get(this.ActivityContext); } if (this.GeneratedFileName != null && !string.IsNullOrEmpty(this.GeneratedFileName.Get(this.ActivityContext))) { generatedFile = Path.Combine(outputLocation, this.GeneratedFileName.Get(this.ActivityContext)); } if (!this.RunCodeMetrics(generatedFile)) { return; } IActivityTracking currentTracking = this.ActivityContext.GetExtension <IBuildLoggingExtension>().GetActivityTracking(this.ActivityContext); if (!this.AnalyzeMetricsResult.Get(this.ActivityContext)) { BaseCodeActivity.AddTextNode("Skipped code metrics analysis (not set to run)", currentTracking.Node); return; } IBuildInformationNode rootNode = AddTextNode("Analyzing code metrics results", currentTracking.Node); string fileName = Path.GetFileName(generatedFile); string pathToFileInDropFolder = Path.Combine(outputLocation, fileName); BaseCodeActivity.AddLinkNode(fileName, new Uri(pathToFileInDropFolder), rootNode); CodeMetricsReport result = CodeMetricsReport.LoadFromFile(generatedFile); if (result == null) { this.LogBuildMessage("Could not load metric result file "); return; } // Get thresholds for each level. var memberMetricThresholds = new MethodMetricsThresholds(this); var typeMetricThresholds = new TypeMetricsThresholds(this, memberMetricThresholds); // var namespaceMetricThresholds = new NameSpaceMetricsThresholds(this); //Uncomment in this if you want to perform metric checks on namespaces // var assemblyMetricThresholds = new AssemblyMetricsThresholds(this); // Uncomment in this if you want to perform metric checks on assemblies int noOfTypeViolations = 0; int noOfMethodViolations = 0; foreach (var target in result.Targets) { var targetNode = this.logCodeMetrics ? AddTextNode("Target: " + target.Name, rootNode) : null; foreach (var module in target.Modules) { var moduleNode = this.logCodeMetrics ? AddTextNode("Module: " + module.Name, targetNode) : null; foreach (var ns in module.Namespaces) { var namespaceNode = this.logCodeMetrics ? AddTextNode("Namespace: " + ns.Name, moduleNode) : null; foreach (var type in ns.Types) { var typeNode = this.logCodeMetrics ? AddTextNode("Type: " + type.Name, namespaceNode) : null; var typeInformation = new MemberInformation(null, type, ns, module); noOfTypeViolations += this.ProcessMetrics(typeNode, typeInformation.TheClass.Metrics, typeMetricThresholds, typeInformation.FullyQualifiedName, typeInformation.TheClass.Name); noOfMethodViolations += (from member in type.Members let memberNode = this.logCodeMetrics ? AddTextNode("Member: " + member.Name + " " + member.MetricsInformation, typeNode) : null let memberInformation = new MemberInformation(member, type, ns, module) select this.ProcessMetrics(memberNode, memberInformation.TheMember.Metrics, memberMetricThresholds, memberInformation.FullyQualifiedName, memberInformation.TheMember.Name)).Sum(); } } } } var numberMessageTypes = string.Format("Number of Code Metric warnings/errors on types: {0}", noOfTypeViolations); var numberMessageMethods = string.Format("Number of Code Metric warnings/errors on methods: {0}", noOfMethodViolations); BaseCodeActivity.AddTextNode(numberMessageTypes, rootNode); BaseCodeActivity.AddTextNode(numberMessageMethods, rootNode); }