-
Notifications
You must be signed in to change notification settings - Fork 35
Expand file tree
/
Copy pathVSharp.cs
More file actions
255 lines (228 loc) · 10.8 KB
/
VSharp.cs
File metadata and controls
255 lines (228 loc) · 10.8 KB
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
243
244
245
246
247
248
249
250
251
252
253
254
255
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using VSharp.Interpreter.IL;
using VSharp.Solver;
namespace VSharp
{
/// <summary>
/// Summary of V# test generation process.
/// </summary>
public sealed class Statistics
{
internal Statistics(TimeSpan time, DirectoryInfo outputDir, uint tests, uint errors, IEnumerable<string> iies)
{
TestGenerationTime = time;
OutputDir = outputDir;
TestsCount = tests;
ErrorsCount = errors;
IncompleteBranches = iies;
}
/// <summary>
/// Overall time of test generation.
/// </summary>
public TimeSpan TestGenerationTime { get; }
/// <summary>
/// Directory where *.vst tests are placed.
/// </summary>
public DirectoryInfo OutputDir { get; }
/// <summary>
/// The amount of generated unit tests.
/// </summary>
public uint TestsCount { get; }
/// <summary>
/// The amount of errors found.
/// </summary>
public uint ErrorsCount { get; }
/// <summary>
/// Some program branches might be failed to investigate. This enumerates the reasons of such failures.
/// </summary>
public IEnumerable<string> IncompleteBranches { get; }
/// <summary>
/// Writes textual summary of test generation process.
/// </summary>
/// <param name="writer">Output writer.</param>
public void GenerateReport(TextWriter writer)
{
writer.WriteLine("Total time: {0:00}:{1:00}:{2:00}.{3}.", TestGenerationTime.Hours,
TestGenerationTime.Minutes, TestGenerationTime.Seconds, TestGenerationTime.Milliseconds);
var count = IncompleteBranches.Count();
if (count > 0)
{
writer.WriteLine();
writer.WriteLine("{0} branch(es) with insufficient input information!", count);
foreach (var message in IncompleteBranches)
{
writer.WriteLine(message);
}
}
writer.WriteLine("Test results written to {0}", OutputDir.FullName);
}
}
public static class TestGenerator
{
private static Statistics StartExploration(List<MethodBase> methods, CoverOptions options = new(), string[] mainArguments = null)
{
var outputDirectory = options.OutputDirectory ?? new DirectoryInfo(Directory.GetCurrentDirectory());
// TODO: customize search strategies via console options
UnitTests unitTests = new UnitTests(outputDirectory.FullName);
SILI explorer = new SILI(options.ToSiliOptions(coverageZone.MethodZone));
// TODO: refresh solver ctx for each method in incremental mode
Core.API.ConfigureSolver(SolverPool.mkSolver(), options.IsSolverIncrementalityEnabled);
Core.API.SetConstraintIndependenceEnabled(options.IsConstraintIndependenceEnabled);
foreach (var method in methods)
{
if (method == method.Module.Assembly.EntryPoint)
{
explorer.InterpretEntryPoint(method, mainArguments, unitTests.GenerateTest, unitTests.GenerateError, _ => { },
e => throw e);
}
else
{
explorer.InterpretIsolated(method, unitTests.GenerateTest, unitTests.GenerateError, _ => { },
e => throw e);
}
}
var statistics = new Statistics(explorer.Statistics.CurrentExplorationTime, unitTests.TestDirectory,
unitTests.UnitTestsCount, unitTests.ErrorsCount,
explorer.Statistics.IncompleteStates.Select(e => e.iie.Value.Message).Distinct());
unitTests.WriteReport(explorer.Statistics.PrintStatistics);
return statistics;
}
public static bool Reproduce(DirectoryInfo testDir)
{
return TestRunner.TestRunner.ReproduceTests(testDir);
}
/// <summary>
/// Generates test coverage for specified method.
/// </summary>
/// <param name="method">Type to be covered with tests.</param>
/// <param name="options">Additional parameters of the run.</param>
/// <returns>Summary of tests generation process.</returns>
public static Statistics Cover(MethodBase method, CoverOptions options = new())
{
List<MethodBase> methods = new List<MethodBase> {method};
return StartExploration(methods, options);
}
/// <summary>
/// Generates test coverage for all public methods of specified type.
/// </summary>
/// <param name="type">Type to be covered with tests.</param>
/// <param name="options">Additional parameters of the run.</param>
/// <returns>Summary of tests generation process.</returns>
/// <exception cref="ArgumentException">Thrown if specified class does not contain public methods.</exception>
public static Statistics Cover(Type type, CoverOptions options = new())
{
List<MethodBase> methods = new List<MethodBase>(type.GetConstructors());
BindingFlags bindingFlags = BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public |
BindingFlags.DeclaredOnly;
methods.AddRange(type.GetMethods(bindingFlags));
if (methods.Count == 0)
{
throw new ArgumentException("I've not found any public method or constructor of class " + type.FullName);
}
return StartExploration(methods, options);
}
/// <summary>
/// Generates test coverage for all public methods of all public classes in the specified assembly.
/// </summary>
/// <param name="assembly">Assembly to be covered with tests.</param>
/// <param name="options">Additional parameters of the run.</param>
/// <returns>Summary of tests generation process.</returns>
/// <exception cref="ArgumentException">Thrown if no public methods found in assembly.
/// </exception>
public static Statistics Cover(Assembly assembly, CoverOptions options = new())
{
List<MethodBase> methods;
BindingFlags bindingFlags = BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public |
BindingFlags.DeclaredOnly;
methods = new List<MethodBase>();
foreach (var t in assembly.GetTypes())
{
if (t.IsPublic)
{
foreach (var m in t.GetMethods(bindingFlags))
{
methods.Add(m);
}
}
}
if (methods.Count == 0)
{
throw new ArgumentException("I've not found any public method in assembly");
}
return StartExploration(methods, options);
}
/// <summary>
/// Generates test coverage for the entry point of the specified assembly.
/// </summary>
/// <param name="assembly">Assembly to be covered with tests.</param>
/// <param name="args">Command line arguments of entry point.</param>
/// <param name="options">Additional parameters of the run.</param>
/// <returns>Summary of tests generation process.</returns>
/// <exception cref="ArgumentException">Thrown if assembly does not contain entry point.
/// </exception>
public static Statistics Cover(Assembly assembly, string[] args, CoverOptions options = new())
{
List<MethodBase> methods;
var entryPoint = assembly.EntryPoint;
if (entryPoint == null)
{
throw new ArgumentException("I've not found entry point in assembly");
}
methods = new List<MethodBase> { entryPoint };
return StartExploration(methods, options, args);
}
/// <summary>
/// Generates test coverage for the specified method and runs all tests.
/// </summary>
/// <param name="method">Type to be covered with tests.</param>
/// <param name="options">Additional parameters of the run.</param>
/// <returns>True if all generated tests have passed.</returns>
public static bool CoverAndRun(MethodBase method, CoverOptions options = new())
{
var stats = Cover(method, options);
return Reproduce(stats.OutputDir);
}
/// <summary>
/// Generates test coverage for the specified type and runs all tests.
/// </summary>
/// <param name="type">Type to be covered with tests.</param>
/// <param name="options">Additional parameters of the run.</param>
/// <returns>True if all generated tests have passed.</returns>
/// <exception cref="ArgumentException">Thrown if specified class does not contain public methods.</exception>
public static bool CoverAndRun(Type type, CoverOptions options = new())
{
var stats = Cover(type, options);
return Reproduce(stats.OutputDir);
}
/// <summary>
/// Generates test coverage for all public methods of all public classes of the specified assembly and runs all tests.
/// </summary>
/// <param name="assembly">Assembly to be covered with tests.</param>
/// <param name="options">Additional parameters of the run.</param>
/// <returns>True if all generated tests have passed.</returns>
/// <exception cref="ArgumentException">Thrown if no public methods found in assembly.
/// </exception>
public static bool CoverAndRun(Assembly assembly, CoverOptions options = new())
{
var stats = Cover(assembly, options);
return Reproduce(stats.OutputDir);
}
/// <summary>
/// Generates test coverage for entry point of the specified assembly and runs all tests.
/// </summary>
/// <param name="assembly">Assembly to be covered with tests.</param>
/// <param name="args">Command line arguments of entry point.</param>
/// <param name="options">Additional parameters of the run.</param>
/// <returns>True if all generated tests have passed.</returns>
/// <exception cref="ArgumentException">Thrown if assembly does not contain entry point.</exception>
public static bool CoverAndRun(Assembly assembly, string[] args, CoverOptions options = new())
{
var stats = Cover(assembly, args, options);
return Reproduce(stats.OutputDir);
}
}
}