Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/mono/api-doc-tools.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/mdoc
diff options
context:
space:
mode:
authorMikhail Melnikov <mikhail_melnikov@epam.com>2017-10-25 15:02:52 +0300
committerJoel Martinez <joelmartinez@gmail.com>2017-10-25 17:30:41 +0300
commitbc635e48317843fb126c4be0875d50ab1eeb61db (patch)
tree3926845a8877ef48d0bab5aa3fd45ad6ef6b4275 /mdoc
parentc173a8b596b5a9dc6d2746dabc7fe46f82478ef7 (diff)
mdoc: Analytics mode
Added -statistics CLI argument with required parameter <path>. Statistics module counts how many namespaces/types/members are added/removed/total. If <path> parameter just points to a folder the default filename "statistics.txt" is appended. If the path can't be resolved a warning message is printed ("mdoc: Unable to save statistics file: Could not find a part of the path 'Z:\'.") Unit tests for new classes are added; "check-monodocer-fx-statistics-remove" integration test is added, 2 other tests check statistics result file as well. Closes #100
Diffstat (limited to 'mdoc')
-rw-r--r--mdoc/Makefile26
-rw-r--r--mdoc/Mono.Documentation/MDocUpdater.cs104
-rw-r--r--mdoc/Mono.Documentation/Updater/Statistics/StatisticsCollector.cs33
-rw-r--r--mdoc/Mono.Documentation/Updater/Statistics/StatisticsFormatter.cs31
-rw-r--r--mdoc/Mono.Documentation/Updater/Statistics/StatisticsItem.cs9
-rw-r--r--mdoc/Mono.Documentation/Updater/Statistics/StatisticsMetrics.cs9
-rw-r--r--mdoc/Mono.Documentation/Updater/Statistics/StatisticsSaver.cs23
-rw-r--r--mdoc/Mono.Documentation/Updater/Statistics/StatisticsStorage.cs30
-rw-r--r--mdoc/Test/expected_fx_remove_statistics.txt14
-rw-r--r--mdoc/Test/expected_remove_statistics.txt14
-rw-r--r--mdoc/Test/expected_statistics.txt28
-rw-r--r--mdoc/Test/fx-statistics-remove-configuration.xml6
-rw-r--r--mdoc/mdoc.Test/StatisticsTests.cs88
-rw-r--r--mdoc/mdoc.Test/mdoc.Test.csproj147
-rw-r--r--mdoc/mdoc.csproj6
15 files changed, 485 insertions, 83 deletions
diff --git a/mdoc/Makefile b/mdoc/Makefile
index 5bb56f29..e1d23a7e 100644
--- a/mdoc/Makefile
+++ b/mdoc/Makefile
@@ -227,6 +227,7 @@ check-monodocer-dropns-multi-withexisting:
check-monodocer-dropns-delete:
-rm -Rf Test/en.actual
+ -rm -Rf Test/actual_statistics.txt
rm -Rf Test/DocTest-DropNS-classic-deletetest.dll
rm -Rf Test/DocTest-DropNS-unified-deletetest.dll
$(MAKE) Test/DocTest-DropNS-classic-deletetest.dll
@@ -236,8 +237,9 @@ check-monodocer-dropns-delete:
$(MAKE) Test/DocTest-DropNS-classic-deletetest-V2.dll
$(MONO) $(PROGRAM) update --delete --exceptions=all -o Test/en.actual Test/DocTest-DropNS-classic-deletetest.dll --api-style=classic
$(MAKE) Test/DocTest-DropNS-unified-deletetest-V2.dll
- $(MONO) $(PROGRAM) update --delete --exceptions=all -o Test/en.actual Test/DocTest-DropNS-unified-deletetest.dll --api-style=unified --dropns Test/DocTest-DropNS-unified-deletetest.dll=MyFramework
+ $(MONO) $(PROGRAM) update --delete --exceptions=all -o Test/en.actual Test/DocTest-DropNS-unified-deletetest.dll --api-style=unified --dropns Test/DocTest-DropNS-unified-deletetest.dll=MyFramework -statistics Test/actual_statistics.txt
$(DIFF) Test/en.expected-dropns-delete Test/en.actual
+ $(DIFF) Test/expected_remove_statistics.txt Test/actual_statistics.txt
check-monodocer-dropns-classic-withsecondary:
# tests case where a secondary assembly is included with a --dropns parameter
@@ -364,6 +366,7 @@ check-monodocer-importecmadoc:
check-monodocer-import-fx-work: Test/DocTest.dll-v1 Test/DocTest-DropNS-classic-secondary.dll Test/DocTest-DropNS-classic.dll
rm -Rf Test/en.actual
rm -Rf Test/fx-import
+ rm -Rf Test/actual_statistics.txt
mkdir Test/fx-import
mkdir Test/fx-import/one
mkdir Test/fx-import/two
@@ -375,7 +378,25 @@ check-monodocer-import-fx-work: Test/DocTest.dll-v1 Test/DocTest-DropNS-classic-
cp Test/DocTest.xml Test/fx-import/TestEcmaDocs.xml
cp Test/CLILibraryTypes.dtd Test/fx-import/
cp Test/fx-import-configuration.xml Test/fx-import/frameworks.xml
+ $(MONO) $(PROGRAM) update -o Test/en.actual -frameworks Test/fx-import -statistics Test/actual_statistics.txt
+ $(DIFF) Test/expected_statistics.txt Test/actual_statistics.txt
+
+
+check-monodocer-fx-statistics-remove:
+ rm -Rf Test/en.actual
+ rm -Rf Test/fx-import
+ rm -Rf Test/actual_statistics.txt
+ mkdir Test/fx-import
+ mkdir Test/fx-import/one
+ cp Test/fx-statistics-remove-configuration.xml Test/fx-import/frameworks.xml
+ $(MAKE) Test/DocTest-DropNS-unified-deletetest.dll
+ cp Test/DocTest-DropNS-unified-deletetest.dll Test/fx-import/one/DocTest.dll
$(MONO) $(PROGRAM) update -o Test/en.actual -frameworks Test/fx-import
+ $(MAKE) Test/DocTest-DropNS-unified.dll
+ rm -rf Test/fx-import/one/DocTest.dll
+ cp Test/DocTest-DropNS-unified.dll Test/fx-import/one/DocTest.dll
+ $(MONO) $(PROGRAM) update -o Test/en.actual -frameworks Test/fx-import -statistics Test/actual_statistics.txt
+ $(DIFF) Test/expected_fx_remove_statistics.txt Test/actual_statistics.txt
.PHONY: check-monodocer-import-fx
check-monodocer-import-fx: check-monodocer-import-fx-work
@@ -498,7 +519,8 @@ check-doc-tools: check-monodocer-since \
check-monodocer-frameworks \
check-monodocer-frameworks-inheritance \
check-monodocer-docid \
- check-monodocer-operators
+ check-monodocer-operators \
+ check-monodocer-fx-statistics-remove
check-doc-tools-update: check-monodocer-since-update \
check-monodocer-importecmadoc-update \
diff --git a/mdoc/Mono.Documentation/MDocUpdater.cs b/mdoc/Mono.Documentation/MDocUpdater.cs
index d789e9af..8a347c72 100644
--- a/mdoc/Mono.Documentation/MDocUpdater.cs
+++ b/mdoc/Mono.Documentation/MDocUpdater.cs
@@ -12,14 +12,15 @@ using System.Xml.XPath;
using Mono.Cecil;
using Mono.Documentation.Updater;
-using Mono.Documentation.Updater.Frameworks;
+using Mono.Documentation.Updater.Frameworks;
+using Mono.Documentation.Updater.Statistics;
using Mono.Documentation.Util;
using Mono.Options;
using MyXmlNodeList = System.Collections.Generic.List<System.Xml.XmlNode>;
using StringList = System.Collections.Generic.List<string>;
using StringToXmlNodeMap = System.Collections.Generic.Dictionary<string, System.Xml.XmlNode>;
-
+
namespace Mono.Documentation
{
class MDocUpdater : MDocCommand
@@ -63,6 +64,8 @@ namespace Mono.Documentation
public static string droppedNamespace = string.Empty;
+ private HashSet<string> memberSet;
+
public static bool HasDroppedNamespace (TypeDefinition forType)
{
return HasDroppedNamespace (forType.Module);
@@ -102,11 +105,16 @@ namespace Mono.Documentation
string FrameworksPath = string.Empty;
FrameworkIndex frameworks;
FrameworkIndex frameworksCache;
+ IEnumerable<XDocument> oldFrameworkXmls;
+
+ private StatisticsCollector statisticsCollector = new StatisticsCollector();
static List<string> droppedAssemblies = new List<string> ();
public string PreserveTag { get; set; }
public bool DisableSearchDirectoryRecurse = false;
+ private bool statisticsEnabled = false;
+ private string statisticsFilePath;
public static MDocUpdater Instance { get; private set; }
public static bool SwitchingToMagicTypes { get; private set; }
@@ -129,7 +137,10 @@ namespace Mono.Documentation
" for NEW types/members\n" +
"If nothing is specified, then only exceptions from the member will " +
"be listed.",
- v => exceptions = ParseExceptionLocations (v) },
+ v =>
+ {
+ exceptions = ParseExceptionLocations(v);
+ } },
{ "f=",
"Specify a {FLAG} to alter behavior. See later -f* options for available flags.",
v => {
@@ -205,6 +216,15 @@ namespace Mono.Documentation
{ "disable-searchdir-recurse",
"Default behavior for adding search directories ('-L') is to recurse them and search in all subdirectories. This disables that",
v => DisableSearchDirectoryRecurse = true },
+ {
+ "statistics=",
+ "Save statistics to the specified file",
+ v =>
+ {
+ statisticsEnabled = true;
+ if (!string.IsNullOrEmpty(v))
+ statisticsFilePath = v;
+ } },
};
var assemblyPaths = Parse (p, args, "update",
"[OPTIONS]+ ASSEMBLIES",
@@ -235,6 +255,17 @@ namespace Mono.Documentation
})
.Where (f => Directory.Exists (f.Path));
+ oldFrameworkXmls = fxconfig.Root
+ .Elements("Framework")
+ .Select(f => new
+ {
+ Name = f.Attribute("Name").Value,
+ Source = f.Attribute("Source").Value,
+ XmlPath = Path.Combine(srcPath, "FrameworksIndex", f.Attribute("Source").Value + ".xml"),
+ })
+ .Where(f => File.Exists(f.XmlPath))
+ .Select(f => XDocument.Load(f.XmlPath));
+
Func<string, string, IEnumerable<string>> getFiles = (string path, string filters) =>
{
return filters
@@ -327,6 +358,18 @@ namespace Mono.Documentation
if (!string.IsNullOrWhiteSpace (FrameworksPath))
frameworks.WriteToDisk (srcPath);
+ if (statisticsEnabled)
+ {
+ try
+ {
+ StatisticsSaver.Save(statisticsCollector, statisticsFilePath);
+ }
+ catch (Exception exception)
+ {
+ Warning($"Unable to save statistics file: {exception.Message}");
+ }
+ }
+
Console.WriteLine ("Members Added: {0}, Members Deleted: {1}", additions, deletions);
}
@@ -554,6 +597,10 @@ namespace Mono.Documentation
{
foreach (AssemblyDefinition assembly in assemblySet.Assemblies)
{
+ var typeSet = new HashSet<string> ();
+ var namespacesSet = new HashSet<string> ();
+ memberSet = new HashSet<string> ();
+
var frameworkEntry = frameworks.StartProcessingAssembly (assembly, assemblySet.Importers);
foreach (TypeDefinition type in docEnum.GetDocumentationTypes (assembly, typenames))
@@ -571,7 +618,14 @@ namespace Mono.Documentation
index.Add (assembly);
index.Add (type);
+
+ namespacesSet.Add (type.Namespace);
+ typeSet.Add (type.FullName);
}
+
+ statisticsCollector.AddMetric (frameworkEntry.Name, StatisticsItem.Types, StatisticsMetrics.Total, typeSet.Count);
+ statisticsCollector.AddMetric (frameworkEntry.Name, StatisticsItem.Namespaces, StatisticsMetrics.Total, namespacesSet.Count);
+ statisticsCollector.AddMetric (frameworkEntry.Name, StatisticsItem.Members, StatisticsMetrics.Total, memberSet.Count);
}
}
}
@@ -579,6 +633,7 @@ namespace Mono.Documentation
if (index != null)
index.Write ();
+
if (ignore_missing_types)
return;
@@ -664,8 +719,8 @@ namespace Mono.Documentation
// Find the file, if it exists
string[] searchLocations = new string[] {
- nsname
- };
+ nsname
+ };
if (MDocUpdater.HasDroppedNamespace (type))
{
@@ -914,8 +969,11 @@ namespace Mono.Documentation
private void DoUpdateAssembly (AssemblySet assemblySet, AssemblyDefinition assembly, XmlElement index_types, string source, string dest, HashSet<string> goodfiles)
{
- var frameworkEntry = frameworks.StartProcessingAssembly (assembly, assemblySet.Importers);
+ var namespacesSet = new HashSet<string> ();
+ var typeSet = new HashSet<string> ();
+ memberSet = new HashSet<string> ();
+ var frameworkEntry = frameworks.StartProcessingAssembly (assembly, assemblySet.Importers);
foreach (TypeDefinition type in docEnum.GetDocumentationTypes (assembly, null))
{
string typename = GetTypeFileName (type);
@@ -939,6 +997,7 @@ namespace Mono.Documentation
}
string onsdoc = DocUtils.PathCombine (dest, namespaceToUse + ".xml");
string nsdoc = DocUtils.PathCombine (dest, "ns-" + namespaceToUse + ".xml");
+ namespacesSet.Add (namespaceToUse);
if (File.Exists (onsdoc))
{
File.Move (onsdoc, nsdoc);
@@ -946,12 +1005,17 @@ namespace Mono.Documentation
if (!File.Exists (nsdoc))
{
+ statisticsCollector.AddMetric (frameworkEntry.Name, StatisticsItem.Namespaces, StatisticsMetrics.Added);
Console.WriteLine ("New Namespace File: " + type.Namespace);
WriteNamespaceStub (namespaceToUse, dest);
}
goodfiles.Add (reltypepath);
+ typeSet.Add (type.FullName);
}
+ statisticsCollector.AddMetric (frameworkEntry.Name, StatisticsItem.Types, StatisticsMetrics.Total, typeSet.Count);
+ statisticsCollector.AddMetric (frameworkEntry.Name, StatisticsItem.Namespaces, StatisticsMetrics.Total, namespacesSet.Count);
+ statisticsCollector.AddMetric (frameworkEntry.Name, StatisticsItem.Members, StatisticsMetrics.Total, memberSet.Count);
}
private static void SortIndexEntries (XmlElement indexTypes)
@@ -1068,6 +1132,7 @@ namespace Mono.Documentation
XmlDocument doc = new XmlDocument ();
doc.Load (typefile.FullName);
XmlElement e = doc.SelectSingleNode ("/Type") as XmlElement;
+ var typeFullName = e.GetAttribute("FullName");
var assemblyNameNode = doc.SelectSingleNode ("/Type/AssemblyInfo/AssemblyName");
if (assemblyNameNode == null)
{
@@ -1102,6 +1167,23 @@ namespace Mono.Documentation
try { System.IO.File.Delete (newname); } catch (Exception) { Warning ("Unable to delete existing file: {0}", newname); }
try { typefile.MoveTo (newname); } catch (Exception) { Warning ("Unable to rename to: {0}", newname); }
Console.WriteLine ("Class no longer present; file renamed: " + Path.Combine (nsdir.Name, typefile.Name));
+
+ // Here we don't know the framwork which contained the removed type. So, we determine it by the old frameworks XML-file
+ // If there is only one framework, use it as a default value
+ var defaultFramework = frameworks.Frameworks.SingleOrDefault();
+ // If there is no frameworks (no frameworks mode) or there is more than one framework
+ if (defaultFramework == null)
+ // Use FrameworkEntry.Empty as the default value (as well as in FrameworkIndex/StartProcessingAssembly)
+ defaultFramework = FrameworkEntry.Empty;
+ var frameworkName = defaultFramework.Name;
+ // Try to find the removed type in the old frameworks XML-file
+ var frameworkXml = oldFrameworkXmls?.FirstOrDefault
+ (i => i.XPathSelectElements($"Framework/Namespace/Type[@Name='{typeFullName}']").Any());
+ var frameworkNameAttribute = frameworkXml?.Root?.Attribute ("Name");
+ // If the removed type is found in the old frameworks XML-file, use this framework name
+ if (frameworkNameAttribute != null)
+ frameworkName = frameworkNameAttribute.Value;
+ statisticsCollector.AddMetric (frameworkName, StatisticsItem.Types, StatisticsMetrics.Removed);
};
if (string.IsNullOrWhiteSpace (PreserveTag))
@@ -1242,7 +1324,6 @@ namespace Mono.Documentation
public void DoUpdateType2 (string message, XmlDocument basefile, TypeDefinition type, FrameworkTypeEntry typeEntry, string output, bool insertSince)
{
Console.WriteLine (message + ": " + type.FullName);
-
StringToXmlNodeMap seenmembers = new StringToXmlNodeMap ();
// Update type metadata
@@ -1308,6 +1389,7 @@ namespace Mono.Documentation
continue;
DeleteMember ("Member Removed", output, oldmember, todelete, type);
+ statisticsCollector.AddMetric(typeEntry.Framework.Name, StatisticsItem.Members, StatisticsMetrics.Removed);
continue;
}
@@ -1319,13 +1401,16 @@ namespace Mono.Documentation
// ignore, already seen
}
else
+ {
DeleteMember ("Duplicate Member Found", output, oldmember, todelete, type);
-
+ statisticsCollector.AddMetric(typeEntry.Framework.Name, StatisticsItem.Members, StatisticsMetrics.Removed);
+ }
continue;
}
// Update signature information
UpdateMember (info, typeEntry);
+ memberSet.Add (info.Member.FullName);
// get all apistyles of sig from info.Node
var styles = oldmember.GetElementsByTagName ("MemberSignature").Cast<XmlElement> ()
@@ -1394,6 +1479,8 @@ namespace Mono.Documentation
mm.AddApiStyle (ApiStyle.Unified);
}
+ statisticsCollector.AddMetric (typeEntry.Framework.Name, StatisticsItem.Members, StatisticsMetrics.Added);
+ memberSet.Add (m.FullName);
Console.WriteLine ("Member Added: " + mm.SelectSingleNode ("MemberSignature/@Value").InnerText);
additions++;
}
@@ -1686,6 +1773,7 @@ namespace Mono.Documentation
var frameworkEntry = frameworks.StartProcessingAssembly (type.Module.Assembly, importers);
var typeEntry = frameworkEntry.ProcessType (type);
DoUpdateType2 ("New Type", doc, type, typeEntry, output, true);
+ statisticsCollector.AddMetric (typeEntry.Framework.Name, StatisticsItem.Types, StatisticsMetrics.Added);
return root;
}
diff --git a/mdoc/Mono.Documentation/Updater/Statistics/StatisticsCollector.cs b/mdoc/Mono.Documentation/Updater/Statistics/StatisticsCollector.cs
new file mode 100644
index 00000000..c5200f10
--- /dev/null
+++ b/mdoc/Mono.Documentation/Updater/Statistics/StatisticsCollector.cs
@@ -0,0 +1,33 @@
+using System.Collections.Generic;
+
+namespace Mono.Documentation.Updater.Statistics
+{
+ /// <summary>
+ /// The class stores statistics for different frameworks
+ /// </summary>
+ public class StatisticsCollector
+ {
+ /// <summary>
+ /// Collected statistics for each framework
+ /// </summary>
+ public Dictionary<string, StatisticsStorage> Storages { get; } =
+ new Dictionary<string, StatisticsStorage>();
+
+ /// <summary>
+ /// Change metric value for the item
+ /// </summary>
+ /// <param name="framework">The framework name which statistics is being collected</param>
+ /// <param name="item">The item which metrics value is changing</param>
+ /// <param name="metrics">The metrics which value is changing</param>
+ /// <param name="delta">The value by which the metrics value should be changed</param>
+ public void AddMetric(string framework, StatisticsItem item, StatisticsMetrics metrics, int delta = 1)
+ {
+ if (!Storages.ContainsKey(framework))
+ {
+ Storages[framework] = new StatisticsStorage();
+ }
+
+ Storages[framework].AddMetric(item, metrics, delta);
+ }
+ }
+} \ No newline at end of file
diff --git a/mdoc/Mono.Documentation/Updater/Statistics/StatisticsFormatter.cs b/mdoc/Mono.Documentation/Updater/Statistics/StatisticsFormatter.cs
new file mode 100644
index 00000000..430807c4
--- /dev/null
+++ b/mdoc/Mono.Documentation/Updater/Statistics/StatisticsFormatter.cs
@@ -0,0 +1,31 @@
+using System.Collections.Generic;
+using System.Text;
+
+namespace Mono.Documentation.Updater.Statistics
+{
+ public static class StatisticsFormatter
+ {
+ public static string Format(Dictionary<string, StatisticsStorage> input)
+ {
+ var result = new StringBuilder();
+ foreach (var statisticsStoragePair in input)
+ {
+ var framework = statisticsStoragePair.Key;
+ var statisticsStorage = statisticsStoragePair.Value;
+ result.AppendLine($"Framework: {framework}");
+ result.AppendLine("--------");
+ foreach (var statistics in statisticsStorage.Values)
+ {
+ var staticsItem = statistics.Key;
+ foreach (var statisticsValuePair in statistics.Value)
+ {
+ result.AppendLine($"{staticsItem} {statisticsValuePair.Key}: {statisticsValuePair.Value}");
+ }
+ result.AppendLine();
+ }
+ }
+
+ return result.ToString();
+ }
+ }
+} \ No newline at end of file
diff --git a/mdoc/Mono.Documentation/Updater/Statistics/StatisticsItem.cs b/mdoc/Mono.Documentation/Updater/Statistics/StatisticsItem.cs
new file mode 100644
index 00000000..68ff2e22
--- /dev/null
+++ b/mdoc/Mono.Documentation/Updater/Statistics/StatisticsItem.cs
@@ -0,0 +1,9 @@
+namespace Mono.Documentation.Updater.Statistics
+{
+ public enum StatisticsItem
+ {
+ Types,
+ Namespaces,
+ Members
+ }
+} \ No newline at end of file
diff --git a/mdoc/Mono.Documentation/Updater/Statistics/StatisticsMetrics.cs b/mdoc/Mono.Documentation/Updater/Statistics/StatisticsMetrics.cs
new file mode 100644
index 00000000..cef043d7
--- /dev/null
+++ b/mdoc/Mono.Documentation/Updater/Statistics/StatisticsMetrics.cs
@@ -0,0 +1,9 @@
+namespace Mono.Documentation.Updater.Statistics
+{
+ public enum StatisticsMetrics
+ {
+ Added,
+ Removed,
+ Total
+ }
+} \ No newline at end of file
diff --git a/mdoc/Mono.Documentation/Updater/Statistics/StatisticsSaver.cs b/mdoc/Mono.Documentation/Updater/Statistics/StatisticsSaver.cs
new file mode 100644
index 00000000..d8c9cc23
--- /dev/null
+++ b/mdoc/Mono.Documentation/Updater/Statistics/StatisticsSaver.cs
@@ -0,0 +1,23 @@
+using System;
+using System.IO;
+
+namespace Mono.Documentation.Updater.Statistics
+{
+ public static class StatisticsSaver
+ {
+ private const string DefaltStatisticsFileName = "statistics.txt";
+
+ /// <summary>
+ /// Save statistics to the file
+ /// </summary>
+ /// <param name="statisticsCollector">Statistics values which should be saved</param>
+ /// <param name="statisticsFilePath">Path to the file where statistics should be saved</param>
+ public static void Save(StatisticsCollector statisticsCollector, string statisticsFilePath)
+ {
+ if (Directory.Exists(statisticsFilePath))
+ statisticsFilePath = Path.Combine(statisticsFilePath, DefaltStatisticsFileName);
+ File.WriteAllText(statisticsFilePath, StatisticsFormatter.Format(statisticsCollector.Storages));
+ Console.WriteLine($"Statistics saved to {statisticsFilePath}");
+ }
+ }
+} \ No newline at end of file
diff --git a/mdoc/Mono.Documentation/Updater/Statistics/StatisticsStorage.cs b/mdoc/Mono.Documentation/Updater/Statistics/StatisticsStorage.cs
new file mode 100644
index 00000000..d7bac0da
--- /dev/null
+++ b/mdoc/Mono.Documentation/Updater/Statistics/StatisticsStorage.cs
@@ -0,0 +1,30 @@
+using System;
+using System.Collections.Generic;
+
+namespace Mono.Documentation.Updater.Statistics
+{
+ /// <summary>
+ /// The class stores statistics: int value for each StatisticsItem-StatisticsMetrics pair
+ /// </summary>
+ public class StatisticsStorage
+ {
+ public Dictionary<StatisticsItem, Dictionary<StatisticsMetrics, int>> Values { get; } = new Dictionary<StatisticsItem, Dictionary<StatisticsMetrics, int>>();
+
+ public StatisticsStorage()
+ {
+ foreach (var statisticsItem in Enum.GetValues(typeof(StatisticsItem)))
+ {
+ var metrics = Values[(StatisticsItem)statisticsItem] = new Dictionary<StatisticsMetrics, int>();
+ foreach (var statisticsMetrics in Enum.GetValues(typeof(StatisticsMetrics)))
+ {
+ metrics[(StatisticsMetrics) statisticsMetrics] = 0;
+ }
+ }
+ }
+
+ public void AddMetric(StatisticsItem item, StatisticsMetrics metrics, int delta = 1)
+ {
+ Values[item][metrics] += delta;
+ }
+ }
+} \ No newline at end of file
diff --git a/mdoc/Test/expected_fx_remove_statistics.txt b/mdoc/Test/expected_fx_remove_statistics.txt
new file mode 100644
index 00000000..82b80e4d
--- /dev/null
+++ b/mdoc/Test/expected_fx_remove_statistics.txt
@@ -0,0 +1,14 @@
+Framework: One
+--------
+Types Added: 0
+Types Removed: 1
+Types Total: 2
+
+Namespaces Added: 0
+Namespaces Removed: 0
+Namespaces Total: 1
+
+Members Added: 0
+Members Removed: 5
+Members Total: 5
+
diff --git a/mdoc/Test/expected_remove_statistics.txt b/mdoc/Test/expected_remove_statistics.txt
new file mode 100644
index 00000000..6ba550d0
--- /dev/null
+++ b/mdoc/Test/expected_remove_statistics.txt
@@ -0,0 +1,14 @@
+Framework: Empty
+--------
+Types Added: 0
+Types Removed: 1
+Types Total: 3
+
+Namespaces Added: 0
+Namespaces Removed: 0
+Namespaces Total: 1
+
+Members Added: 1
+Members Removed: 6
+Members Total: 10
+
diff --git a/mdoc/Test/expected_statistics.txt b/mdoc/Test/expected_statistics.txt
new file mode 100644
index 00000000..aea1339d
--- /dev/null
+++ b/mdoc/Test/expected_statistics.txt
@@ -0,0 +1,28 @@
+Framework: one
+--------
+Types Added: 31
+Types Removed: 0
+Types Total: 31
+
+Namespaces Added: 4
+Namespaces Removed: 0
+Namespaces Total: 4
+
+Members Added: 127
+Members Removed: 0
+Members Total: 127
+
+Framework: two
+--------
+Types Added: 3
+Types Removed: 0
+Types Total: 3
+
+Namespaces Added: 2
+Namespaces Removed: 0
+Namespaces Total: 2
+
+Members Added: 9
+Members Removed: 0
+Members Total: 9
+
diff --git a/mdoc/Test/fx-statistics-remove-configuration.xml b/mdoc/Test/fx-statistics-remove-configuration.xml
new file mode 100644
index 00000000..47c3a1f5
--- /dev/null
+++ b/mdoc/Test/fx-statistics-remove-configuration.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Frameworks>
+ <Framework Name="One" Source="One">
+ <assemblySearchPath>One</assemblySearchPath>
+ </Framework>
+</Frameworks> \ No newline at end of file
diff --git a/mdoc/mdoc.Test/StatisticsTests.cs b/mdoc/mdoc.Test/StatisticsTests.cs
new file mode 100644
index 00000000..222e2ea0
--- /dev/null
+++ b/mdoc/mdoc.Test/StatisticsTests.cs
@@ -0,0 +1,88 @@
+using System.Collections.Generic;
+using NUnit.Framework;
+
+using Mono.Documentation.Updater.Statistics;
+
+namespace mdoc.Test
+{
+ [TestFixture()]
+ public class StatisticsTests
+ {
+ const string FRAMEWORK_NAME = "frameworkName";
+
+ [Test()]
+ public void StatisticsStorage_Increment()
+ {
+ var statisticsStorage = new StatisticsStorage();
+
+ statisticsStorage.AddMetric(StatisticsItem.Members, StatisticsMetrics.Added);
+
+ Assert.AreEqual(statisticsStorage.Values[StatisticsItem.Members][StatisticsMetrics.Added], 1);
+ Assert.AreEqual(statisticsStorage.Values[StatisticsItem.Members][StatisticsMetrics.Removed], 0);
+ Assert.AreEqual(statisticsStorage.Values[StatisticsItem.Namespaces][StatisticsMetrics.Added], 0);
+ Assert.AreEqual(statisticsStorage.Values[StatisticsItem.Namespaces][StatisticsMetrics.Removed], 0);
+ }
+
+ [Test()]
+ public void StatisticsStorage_ManyIncrements()
+ {
+ var statisticsStorage = new StatisticsStorage();
+
+ statisticsStorage.AddMetric(StatisticsItem.Members, StatisticsMetrics.Added);
+ statisticsStorage.AddMetric(StatisticsItem.Members, StatisticsMetrics.Added);
+ statisticsStorage.AddMetric(StatisticsItem.Members, StatisticsMetrics.Added);
+
+ Assert.AreEqual(statisticsStorage.Values[StatisticsItem.Members][StatisticsMetrics.Added], 3);
+ Assert.AreEqual(statisticsStorage.Values[StatisticsItem.Members][StatisticsMetrics.Removed], 0);
+ Assert.AreEqual(statisticsStorage.Values[StatisticsItem.Namespaces][StatisticsMetrics.Added], 0);
+ Assert.AreEqual(statisticsStorage.Values[StatisticsItem.Namespaces][StatisticsMetrics.Removed], 0);
+ }
+
+ [Test()]
+ public void StatisticsStorage_IncrementDelta()
+ {
+ var statisticsStorage = new StatisticsStorage();
+
+ statisticsStorage.AddMetric(StatisticsItem.Members, StatisticsMetrics.Added, 5);
+
+ Assert.AreEqual(statisticsStorage.Values[StatisticsItem.Members][StatisticsMetrics.Added], 5);
+ Assert.AreEqual(statisticsStorage.Values[StatisticsItem.Members][StatisticsMetrics.Removed], 0);
+ Assert.AreEqual(statisticsStorage.Values[StatisticsItem.Namespaces][StatisticsMetrics.Added], 0);
+ Assert.AreEqual(statisticsStorage.Values[StatisticsItem.Namespaces][StatisticsMetrics.Removed], 0);
+ }
+
+ [Test()]
+ public void StatisticsCollector_Increment()
+ {
+ var statisticsCollector = new StatisticsCollector();
+
+ statisticsCollector.AddMetric(FRAMEWORK_NAME, StatisticsItem.Members, StatisticsMetrics.Added);
+
+ Assert.AreEqual(
+ statisticsCollector.Storages[FRAMEWORK_NAME].Values[StatisticsItem.Members][StatisticsMetrics.Added], 1);
+ Assert.AreEqual(
+ statisticsCollector.Storages[FRAMEWORK_NAME].Values[StatisticsItem.Members][StatisticsMetrics.Removed], 0);
+ Assert.AreEqual(
+ statisticsCollector.Storages[FRAMEWORK_NAME].Values[StatisticsItem.Namespaces][StatisticsMetrics.Added], 0);
+ Assert.AreEqual(
+ statisticsCollector.Storages[FRAMEWORK_NAME].Values[StatisticsItem.Namespaces][StatisticsMetrics.Removed], 0);
+ }
+
+ [Test()]
+ public void StatisticsCollector_IncrementDelta()
+ {
+ var statisticsCollector = new StatisticsCollector();
+
+ statisticsCollector.AddMetric(FRAMEWORK_NAME, StatisticsItem.Members, StatisticsMetrics.Added, 7);
+
+ Assert.AreEqual(
+ statisticsCollector.Storages[FRAMEWORK_NAME].Values[StatisticsItem.Members][StatisticsMetrics.Added], 7);
+ Assert.AreEqual(
+ statisticsCollector.Storages[FRAMEWORK_NAME].Values[StatisticsItem.Members][StatisticsMetrics.Removed], 0);
+ Assert.AreEqual(
+ statisticsCollector.Storages[FRAMEWORK_NAME].Values[StatisticsItem.Namespaces][StatisticsMetrics.Added], 0);
+ Assert.AreEqual(
+ statisticsCollector.Storages[FRAMEWORK_NAME].Values[StatisticsItem.Namespaces][StatisticsMetrics.Removed], 0);
+ }
+ }
+} \ No newline at end of file
diff --git a/mdoc/mdoc.Test/mdoc.Test.csproj b/mdoc/mdoc.Test/mdoc.Test.csproj
index 1278af31..a610b28f 100644
--- a/mdoc/mdoc.Test/mdoc.Test.csproj
+++ b/mdoc/mdoc.Test/mdoc.Test.csproj
@@ -1,77 +1,78 @@
-<?xml version="1.0" encoding="utf-8"?>
-<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
- <PropertyGroup>
- <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
- <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
- <ProjectGuid>{5ADDEFB6-930C-46BC-8B2B-FDE5C7E3B5AD}</ProjectGuid>
- <OutputType>Library</OutputType>
- <RootNamespace>mdoc.Test</RootNamespace>
- <AssemblyName>mdoc.Test</AssemblyName>
- <TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>
- </PropertyGroup>
- <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
- <DebugSymbols>true</DebugSymbols>
- <DebugType>full</DebugType>
- <Optimize>false</Optimize>
- <OutputPath>bin\Debug</OutputPath>
- <DefineConstants>DEBUG;</DefineConstants>
- <ErrorReport>prompt</ErrorReport>
- <WarningLevel>4</WarningLevel>
- </PropertyGroup>
- <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
- <Optimize>true</Optimize>
- <OutputPath>bin\Release</OutputPath>
- <ErrorReport>prompt</ErrorReport>
- <WarningLevel>4</WarningLevel>
- </PropertyGroup>
- <ItemGroup>
- <Reference Include="System" />
- <Reference Include="nunit.framework">
- <HintPath>..\..\packages\NUnit.2.6.4\lib\nunit.framework.dll</HintPath>
- </Reference>
- <Reference Include="Mono.Cecil, Version=0.10.0.0, Culture=neutral, PublicKeyToken=50cebf1cceb9d05e, processorArchitecture=MSIL">
- <HintPath>..\..\packages\Mono.Cecil.0.10.0-beta5\lib\net40\Mono.Cecil.dll</HintPath>
- </Reference>
- <Reference Include="Mono.Cecil.Mdb, Version=0.10.0.0, Culture=neutral, PublicKeyToken=50cebf1cceb9d05e, processorArchitecture=MSIL">
- <HintPath>..\..\packages\Mono.Cecil.0.10.0-beta5\lib\net40\Mono.Cecil.Mdb.dll</HintPath>
- </Reference>
- <Reference Include="Mono.Cecil.Pdb, Version=0.10.0.0, Culture=neutral, PublicKeyToken=50cebf1cceb9d05e, processorArchitecture=MSIL">
- <HintPath>..\..\packages\Mono.Cecil.0.10.0-beta5\lib\net40\Mono.Cecil.Pdb.dll</HintPath>
- </Reference>
- <Reference Include="Mono.Cecil.Rocks, Version=0.10.0.0, Culture=neutral, PublicKeyToken=50cebf1cceb9d05e, processorArchitecture=MSIL">
- <HintPath>..\..\packages\Mono.Cecil.0.10.0-beta5\lib\net40\Mono.Cecil.Rocks.dll</HintPath>
- </Reference>
- </ItemGroup>
- <ItemGroup>
- <Compile Include="FormatterTests.cs" />
- <Compile Include="SampleClasses\TestClass.cs" />
- <Compile Include="SampleClasses\TestPrivateClass.cs" />
- <Compile Include="SampleClasses\TestClassTwo.cs" />
- </ItemGroup>
- <ItemGroup>
- <None Include="packages.config" />
- <None Include="cppcli\cppcli\cppcli.h">
- <Link>SampleClasses\cppcli.h</Link>
- </None>
- </ItemGroup>
- <ItemGroup>
- <ProjectReference Include="..\mdoc.csproj">
- <Project>{7DA7CD97-614F-4BCD-A2FA-B379590CEA48}</Project>
- <Name>mdoc</Name>
- </ProjectReference>
- <ProjectReference Include="..\..\monodoc\monodoc.csproj">
- <Project>{6E644802-B579-4037-9809-9CF4C7172C9D}</Project>
- <Name>monodoc</Name>
- </ProjectReference>
- </ItemGroup>
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <PropertyGroup>
+ <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+ <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+ <ProjectGuid>{5ADDEFB6-930C-46BC-8B2B-FDE5C7E3B5AD}</ProjectGuid>
+ <OutputType>Library</OutputType>
+ <RootNamespace>mdoc.Test</RootNamespace>
+ <AssemblyName>mdoc.Test</AssemblyName>
+ <TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
+ <DebugSymbols>true</DebugSymbols>
+ <DebugType>full</DebugType>
+ <Optimize>false</Optimize>
+ <OutputPath>bin\Debug</OutputPath>
+ <DefineConstants>DEBUG;</DefineConstants>
+ <ErrorReport>prompt</ErrorReport>
+ <WarningLevel>4</WarningLevel>
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
+ <Optimize>true</Optimize>
+ <OutputPath>bin\Release</OutputPath>
+ <ErrorReport>prompt</ErrorReport>
+ <WarningLevel>4</WarningLevel>
+ </PropertyGroup>
+ <ItemGroup>
+ <Reference Include="System" />
+ <Reference Include="nunit.framework">
+ <HintPath>..\..\packages\NUnit.2.6.4\lib\nunit.framework.dll</HintPath>
+ </Reference>
+ <Reference Include="Mono.Cecil, Version=0.10.0.0, Culture=neutral, PublicKeyToken=50cebf1cceb9d05e, processorArchitecture=MSIL">
+ <HintPath>..\..\packages\Mono.Cecil.0.10.0-beta5\lib\net40\Mono.Cecil.dll</HintPath>
+ </Reference>
+ <Reference Include="Mono.Cecil.Mdb, Version=0.10.0.0, Culture=neutral, PublicKeyToken=50cebf1cceb9d05e, processorArchitecture=MSIL">
+ <HintPath>..\..\packages\Mono.Cecil.0.10.0-beta5\lib\net40\Mono.Cecil.Mdb.dll</HintPath>
+ </Reference>
+ <Reference Include="Mono.Cecil.Pdb, Version=0.10.0.0, Culture=neutral, PublicKeyToken=50cebf1cceb9d05e, processorArchitecture=MSIL">
+ <HintPath>..\..\packages\Mono.Cecil.0.10.0-beta5\lib\net40\Mono.Cecil.Pdb.dll</HintPath>
+ </Reference>
+ <Reference Include="Mono.Cecil.Rocks, Version=0.10.0.0, Culture=neutral, PublicKeyToken=50cebf1cceb9d05e, processorArchitecture=MSIL">
+ <HintPath>..\..\packages\Mono.Cecil.0.10.0-beta5\lib\net40\Mono.Cecil.Rocks.dll</HintPath>
+ </Reference>
+ </ItemGroup>
+ <ItemGroup>
+ <Compile Include="FormatterTests.cs" />
+ <Compile Include="SampleClasses\TestClass.cs" />
+ <Compile Include="SampleClasses\TestPrivateClass.cs" />
+ <Compile Include="SampleClasses\TestClassTwo.cs" />
+ <Compile Include="StatisticsTests.cs" />
+ </ItemGroup>
+ <ItemGroup>
+ <None Include="packages.config" />
+ <None Include="cppcli\cppcli\cppcli.h">
+ <Link>SampleClasses\cppcli.h</Link>
+ </None>
+ </ItemGroup>
+ <ItemGroup>
+ <ProjectReference Include="..\mdoc.csproj">
+ <Project>{7DA7CD97-614F-4BCD-A2FA-B379590CEA48}</Project>
+ <Name>mdoc</Name>
+ </ProjectReference>
+ <ProjectReference Include="..\..\monodoc\monodoc.csproj">
+ <Project>{6E644802-B579-4037-9809-9CF4C7172C9D}</Project>
+ <Name>monodoc</Name>
+ </ProjectReference>
+ </ItemGroup>
<ItemGroup>
<Folder Include="SampleClasses\" />
</ItemGroup>
- <ItemGroup>
- <Content Include="cppcli\Debug\cppcli.dll">
- <Link>SampleClasses\cppcli.dll</Link>
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
- </ItemGroup>
- <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
+ <ItemGroup>
+ <Content Include="cppcli\Debug\cppcli.dll">
+ <Link>SampleClasses\cppcli.dll</Link>
+ <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+ </Content>
+ </ItemGroup>
+ <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
</Project> \ No newline at end of file
diff --git a/mdoc/mdoc.csproj b/mdoc/mdoc.csproj
index a1b2ea59..bcf3a3a5 100644
--- a/mdoc/mdoc.csproj
+++ b/mdoc/mdoc.csproj
@@ -77,6 +77,12 @@
<Compile Include="Mono.Documentation\Updater\EcmaDocumentationEnumerator.cs" />
<Compile Include="Mono.Documentation\Updater\DocumentationEnumerator.cs" />
<Compile Include="Mono.Documentation\Updater\DocsNodeInfo.cs" />
+ <Compile Include="Mono.Documentation\Updater\Statistics\StatisticsCollector.cs" />
+ <Compile Include="Mono.Documentation\Updater\Statistics\StatisticsFormatter.cs" />
+ <Compile Include="Mono.Documentation\Updater\Statistics\StatisticsItem.cs" />
+ <Compile Include="Mono.Documentation\Updater\Statistics\StatisticsMetrics.cs" />
+ <Compile Include="Mono.Documentation\Updater\Statistics\StatisticsSaver.cs" />
+ <Compile Include="Mono.Documentation\Updater\Statistics\StatisticsStorage.cs" />
<Compile Include="Mono.Documentation\Util\ApiStyle.cs" />
<Compile Include="Mono.Documentation\Updater\DocUtils.cs" />
<Compile Include="Mono.Documentation\Util\CecilExtensions.cs" />