-
Notifications
You must be signed in to change notification settings - Fork 32
/
Copy pathBenchmarks.cs
242 lines (198 loc) · 8.91 KB
/
Benchmarks.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
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
#nullable enable
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using ConsoleTables;
using NUnit.Framework;
using VSharp.CoverageTool;
using VSharp.CSharpUtils;
using VSharp.Explorer;
using VSharp.TestRenderer;
namespace VSharp.Test.Benchmarks;
internal static class Benchmarks
{
private static readonly string TestRunnerPath = typeof(TestRunner.TestRunner).Assembly.Location;
private static readonly DirectoryInfo RenderedTestsDirectory = new(Path.Combine(Directory.GetCurrentDirectory(), "RenderedTests"));
private static bool TryBuildGeneratedTests()
{
var testsDir = RenderedTestsDirectory.GetDirectories("*.Tests").Single();
var info = new ProcessStartInfo
{
WorkingDirectory = testsDir.FullName,
FileName = DotnetExecutablePath.ExecutablePath,
Arguments = "build"
};
var process = new Process();
process.StartInfo = info;
process.Start();
process.WaitForExit();
return process.IsSuccess();
}
private class Reporter: IReporter
{
private readonly UnitTests _unitTests;
public Reporter(UnitTests unitTests)
{
_unitTests = unitTests;
}
public void ReportFinished(UnitTest unitTest) => _unitTests.GenerateTest(unitTest);
public void ReportException(UnitTest unitTest) => _unitTests.GenerateError(unitTest);
public void ReportIIE(InsufficientInformationException iie) => TestContext.Progress.WriteLine($"[IIE] {iie.Message}");
public void ReportInternalFail(Method? method, Exception exn) => TestContext.Progress.WriteLine($"[ERROR] {method.Name}: {exn}");
public void ReportCrash(Exception exn) => TestContext.Progress.WriteLine($"[CRASH] {exn}");
}
// TODO: Add support for fuzzing
public static BenchmarkResult Run(
BenchmarkTarget target,
searchMode searchStrategy,
int timeoutS = -1,
uint stepsLimit = 0,
bool releaseBranches = true,
int randomSeed = -1,
bool renderAndBuildTests = false,
bool calculateCoverage = false)
{
if (target.Method is null)
{
throw new NotImplementedException("Running non single method benchmarks is not implemented yet");
}
if (RenderedTestsDirectory.Exists)
{
Directory.Delete(RenderedTestsDirectory.FullName, true);
}
Directory.CreateDirectory(RenderedTestsDirectory.FullName);
var exploredMethodInfo = AssemblyManager.NormalizeMethod(target.Method);
Logger.configureWriter(TestContext.Progress);
var unitTests = new UnitTests(Directory.GetCurrentDirectory());
var svmOptions = new SVMOptions(
explorationMode: explorationMode.NewTestCoverageMode(coverageZone.MethodZone, searchStrategy),
recThreshold: 1,
solverTimeout: -1,
visualize: false,
releaseBranches: releaseBranches,
maxBufferSize: 128,
prettyChars: true,
checkAttributes: false,
stopOnCoverageAchieved: -1,
randomSeed: randomSeed,
stepsLimit: stepsLimit
);
var fuzzerOptions = new FuzzerOptions(
isolation: fuzzerIsolation.Process,
coverageZone: coverageZone.MethodZone
);
var explorationModeOptions = Explorer.explorationModeOptions.NewSVM(svmOptions);
var explorationOptions = new ExplorationOptions(
timeout: timeoutS == -1 ? TimeSpanBuilders.Infinite : TimeSpanBuilders.FromSeconds(timeoutS),
outputDirectory: unitTests.TestDirectory,
explorationModeOptions: explorationModeOptions
);
using var explorer = new Explorer.Explorer(explorationOptions, new Reporter(unitTests));
explorer.StartExploration(new[] {exploredMethodInfo}, new Tuple<MethodBase, string[]>[] { });
var result = new BenchmarkResult(false, explorer.Statistics, unitTests, target);
explorer.Statistics.PrintDebugStatistics(TestContext.Progress);
TestContext.Progress.WriteLine($"Test results written to {unitTests.TestDirectory.FullName}");
TestContext.Progress.WriteLine($"Generated tests count: {unitTests.UnitTestsCount}");
TestContext.Progress.WriteLine($"Found errors count: {unitTests.ErrorsCount}");
if (unitTests is { UnitTestsCount: 0, ErrorsCount: 0 })
{
return result with { IsSuccessful = true };
}
var testsDir = unitTests.TestDirectory;
if (renderAndBuildTests)
{
var tests = testsDir.EnumerateFiles("*.vst");
TestContext.Progress.WriteLine("Starting tests renderer...");
try
{
Renderer.Render(tests, true, false, exploredMethodInfo.DeclaringType, outputDir: RenderedTestsDirectory);
}
catch (UnexpectedExternCallException)
{
// TODO: support rendering for extern mocks
}
catch (Exception e)
{
TestContext.Progress.WriteLine($"[RENDER ERROR]: {e}");
return result;
}
if (!TryBuildGeneratedTests())
{
TestContext.Progress.WriteLine($"[BUILD]: Cannot build generated tests");
return result;
}
}
if (!TestRunner.TestRunner.ReproduceTests(unitTests.TestDirectory))
{
return result;
}
if (calculateCoverage)
{
return result with { IsSuccessful = true, Coverage = GetMethodCoverage(result) };
}
return result with { IsSuccessful = true };
}
public static void PrintStatisticsComparison(List<(string Title, BenchmarkResult Results)> titleToResults)
{
var infos = titleToResults.SelectMany(tr =>
tr.Results.Statistics.GeneratedTestInfos
.Where(ti => !ti.isError)
.Select(ti => (tr.Title, ti)))
.OrderBy(tti => tti.ti.coverage);
var header = new List<string> { "" };
header.AddRange(titleToResults.Select(s => s.Title));
var totalStatsTable = new ConsoleTable(header.ToArray());
var timeRow = new List<string> { "Elapsed time" };
timeRow.AddRange(titleToResults.Select(tr => tr.Results.Statistics.CurrentExplorationTime.ToString()));
totalStatsTable.AddRow(timeRow.ToArray());
var stepsRow = new List<string> { "Steps count" };
stepsRow.AddRange(titleToResults.Select(tr => tr.Results.Statistics.StepsCount.ToString()));
totalStatsTable.AddRow(stepsRow.ToArray());
var testsCountRow = new List<string> { "Tests generated" };
testsCountRow.AddRange(titleToResults.Select(tr => tr.Results.Tests.UnitTestsCount.ToString()));
totalStatsTable.AddRow(testsCountRow.ToArray());
var errorsCountRow = new List<string> { "Errors found" };
errorsCountRow.AddRange(titleToResults.Select(tr => tr.Results.Tests.ErrorsCount.ToString()));
totalStatsTable.AddRow(errorsCountRow.ToArray());
var coverageRow = new List<string> { "Total coverage (with tool)" };
coverageRow.AddRange(titleToResults.Select(tr => $"{tr.Results.Coverage}%"));
totalStatsTable.AddRow(coverageRow.ToArray());
totalStatsTable.Write();
var testsStatsTableHeader = new List<string> { "Steps count" };
testsStatsTableHeader.AddRange(titleToResults.Select(tr => tr.Title));
var testsStatsTable = new ConsoleTable(testsStatsTableHeader.ToArray());
foreach (var (title, info) in infos)
{
var row = new List<string> { info.stepsCount.ToString() };
foreach (var (columnHeader, _) in titleToResults)
{
row.Add(title == columnHeader ? info.coverage.ToString("0.##") : "");
}
testsStatsTable.AddRow(row.ToArray());
}
testsStatsTable.Write();
}
private static int GetMethodCoverage(BenchmarkResult result)
{
if (result.Target.Method is null)
{
throw new Exception("Cannot get coverage of BenchmarkTarget without single method");
}
var runnerWithArgs = $"{TestRunnerPath} {result.Tests.TestDirectory}";
var coverageTool = new PassiveCoverageTool(result.Tests.TestDirectory, result.Target.Method);
return coverageTool.RunWithCoverage(runnerWithArgs);
}
public static Assembly LoadBenchmarkAssembly(string suite, string dllFileName)
{
var dllPath = TestContext.Parameters["BenchmarkDllsPath"];
if (dllPath is null)
{
throw new Exception("Cannot read dll directory path from test context parameters");
}
var assemblyPath = Path.Combine(dllPath, suite, $"{dllFileName}.dll");
return AssemblyManager.LoadFromAssemblyPath(assemblyPath);
}
}