-
Notifications
You must be signed in to change notification settings - Fork 5
/
Copy pathAssert.Group.cs
192 lines (172 loc) · 8.85 KB
/
Assert.Group.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
using System;
using System.Collections.Generic;
using System.Linq;
using System.IO;
using System.Reflection;
using System.Threading.Tasks;
namespace DevTools
{
public static partial class Assert
{
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]
internal class GroupAttribute : Attribute
{
internal const string __FILE__BOOLEAN_CONDITION_CHECKS = "BOOLEAN_CONDITION_CHECKS";
internal const string __FILE__NULL_CHECKS = "NULL_CHECKS";
internal const string __FILE__FILE_PATH_CHECKS = "FILE_PATH_CHECKS";
internal const string __FILE__ARRAY_BOUNDS_CHECKS = "ARRAY_BOUNDS_CHECKS";
internal const string __FILE__COMPARISON_CHECKS = "COMPARISON_CHECKS";
internal const string __FILE__ARITHMETIC_LOGIC_CHECKS = "ARITHMETIC_LOGIC_CHECKS";
internal const string __FILE__MEMORY_CHECKS = "MEMORY_CHECKS";
/// <summary> Boolean Condition Checks </summary>
internal const string __NAME__BOOLEAN_CONDITION_CHECKS = "Boolean Condition Checks";
/// <summary> Null Checks </summary>
internal const string __NAME__NULL_CHECKS = "Null Checks";
/// <summary> File Path Checks </summary>
internal const string __NAME__FILE_PATH_CHECKS = "File Path Checks";
/// <summary> Array Bounds Checks </summary>
internal const string __NAME__ARRAY_BOUNDS_CHECKS = "Array Bounds Checks";
/// <summary> Comparison Checks </summary>
internal const string __NAME__COMPARISON_CHECKS = "Comparison Checks";
/// <summary> Arithmetic-Logic Checks </summary>
internal const string __NAME__ARITHMETIC_LOGIC_CHECKS = "Arithmetic-Logic Checks";
/// <summary> Memory Checks </summary>
internal const string __NAME__MEMORY_CHECKS = "Memory Checks";
private GroupAttribute() { }
internal GroupAttribute(string publicName)
{
PublicName = publicName;
FileContent = Defines.Where(grp => grp.PublicName == publicName).First().FileContent;
}
internal string FileContent { get; private set; }
internal string PublicName { get; private set; }
internal static Assert.GroupAttribute[] Defines => new Assert.GroupAttribute[]
{
new Assert.GroupAttribute{ FileContent = __FILE__BOOLEAN_CONDITION_CHECKS, PublicName = __NAME__BOOLEAN_CONDITION_CHECKS },
new Assert.GroupAttribute{ FileContent = __FILE__NULL_CHECKS, PublicName = __NAME__NULL_CHECKS },
new Assert.GroupAttribute{ FileContent = __FILE__FILE_PATH_CHECKS, PublicName = __NAME__FILE_PATH_CHECKS },
new Assert.GroupAttribute{ FileContent = __FILE__ARRAY_BOUNDS_CHECKS, PublicName = __NAME__ARRAY_BOUNDS_CHECKS },
new Assert.GroupAttribute{ FileContent = __FILE__COMPARISON_CHECKS, PublicName = __NAME__COMPARISON_CHECKS },
new Assert.GroupAttribute{ FileContent = __FILE__ARITHMETIC_LOGIC_CHECKS, PublicName = __NAME__ARITHMETIC_LOGIC_CHECKS },
new Assert.GroupAttribute{ FileContent = __FILE__MEMORY_CHECKS, PublicName = __NAME__MEMORY_CHECKS }
};
private static string[] KnownUsingsWithAssertClasses => new string[]
{
"using NUnit.Framework;",
"using UnityEngine.Assertions",
"using static System.Diagnostics.Debug;"
};
internal static async Task<Dictionary<Assert.GroupAttribute, uint>> CountMethodCallsAsync(string projectPath)
{
try
{
Dictionary<MethodInfo, Assert.GroupAttribute> methodToGroupMap = GetAssertionsMappedToGroups();
List<Task<Dictionary<Assert.GroupAttribute, uint>>> jobs = CreateCountingTasks(methodToGroupMap, projectPath);
return await CombineResults(jobs);
}
catch (Exception ex)
{
ex.Log();
return null;
}
}
private static bool ContainsOverload(Dictionary<MethodInfo, Assert.GroupAttribute> result, MethodInfo method)
{
foreach (KeyValuePair<MethodInfo, Assert.GroupAttribute> item in result)
{
if (item.Key.Name == method.Name)
{
return true;
}
}
return false;
}
private static Dictionary<MethodInfo, Assert.GroupAttribute> GetAssertionsMappedToGroups()
{
Dictionary<MethodInfo, Assert.GroupAttribute> result = new Dictionary<MethodInfo, Assert.GroupAttribute>();
foreach (MethodInfo method in typeof(Assert).GetMethods())
{
Assert.GroupAttribute attribute = method.GetCustomAttribute<Assert.GroupAttribute>(false);
if (attribute != null && !ContainsOverload(result, method))
{
result.Add(method, attribute);
}
}
return result;
}
private static string GetMethodPrefixFromUsingStatements(string script)
{
if (script.Contains("using static DevTools.Assert;"))
{
return string.Empty;
}
else if (script.Contains("using DevTools;") && KnownUsingsWithAssertClasses.All(__using => !script.Contains(__using)))
{
return "Assert.";
}
else
{
return "DevTools.Assert.";
}
}
private static uint CountSubstrings(string instance, string value)
{
Assert.IsFalse(string.IsNullOrEmpty(value));
uint count = 0;
int index = instance.IndexOf(value);
while (index != -1 & index < instance.Length)
{
count++;
index = instance.IndexOf(value, index + value.Length);
}
return count;
}
private static List<Task<Dictionary<Assert.GroupAttribute, uint>>> CreateCountingTasks(Dictionary<MethodInfo, Assert.GroupAttribute> methodToGroupMap, string path)
{
List<Task<Dictionary<Assert.GroupAttribute, uint>>> tasks = new List<Task<Dictionary<Assert.GroupAttribute, uint>>>(256);
DirectoryExtensions.ForEachFile(path,
(file) =>
{
if (Path.GetExtension(file) != ".cs")
{
return;
}
tasks.Add(Task<Dictionary<Assert.GroupAttribute, uint>>.Factory.StartNew(
() =>
{
Dictionary<Assert.GroupAttribute, uint> callCounts = new Dictionary<Assert.GroupAttribute, uint>();
string script = File.ReadAllText(file);
string prefix = GetMethodPrefixFromUsingStatements(script);
foreach (KeyValuePair<MethodInfo, Assert.GroupAttribute> methodMapping in methodToGroupMap)
{
Assert.GroupAttribute group = methodMapping.Value;
uint numCalls = CountSubstrings(script, prefix + methodMapping.Key.Name);
if (callCounts.ContainsKey(group))
{
callCounts[group] += numCalls;
}
else
{
callCounts.Add(group, numCalls);
}
}
return callCounts;
}));
});
return tasks;
}
private static async Task<Dictionary<Assert.GroupAttribute, uint>> CombineResults(List<Task<Dictionary<Assert.GroupAttribute, uint>>> jobs)
{
Dictionary<Assert.GroupAttribute, uint> result = await jobs[0]; // at the very least this very script is assigned a job
for (int i = 1; i < jobs.Count; i++)
{
foreach (KeyValuePair<Assert.GroupAttribute, uint> callCount in await jobs[i])
{
result[callCount.Key] += callCount.Value;
}
}
return result;
}
}
}
}