Skip to content

Commit 828e5a0

Browse files
mxprshnDomonion
authored andcommitted
Rider plugin fixes
Fixing multiple bugs Rider pluigin bug
1 parent 2793524 commit 828e5a0

15 files changed

+937
-236
lines changed

utbot-rd/src/main/rdgen/org/utbot/rd/models/CSharpModel.kt

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,27 +9,30 @@ object VSharpModel: Ext(CSharpRoot) {
99
val methodDescriptor = structdef {
1010
field("methodName", PredefinedType.string)
1111
field("typeName", PredefinedType.string)
12+
field("hasNoOverloads", PredefinedType.bool)
13+
field("parameters", immutableList(PredefinedType.string))
1214
}
1315

1416
val generateArguments = structdef {
1517
field("assemblyPath", PredefinedType.string)
1618
field("projectCsprojPath", PredefinedType.string)
1719
field("solutionFilePath", PredefinedType.string)
18-
field("method", methodDescriptor)
20+
field("methods", immutableList(methodDescriptor))
1921
field("generationTimeoutInSeconds", PredefinedType.int)
2022
field("targetFramework", PredefinedType.string.nullable)
2123
}
2224

2325
val generateResults = structdef {
24-
field("isGenerated", PredefinedType.bool)
25-
field("generatedProjectPath", PredefinedType.string)
26-
field("generatedFilesPaths", array(PredefinedType.string))
26+
field("generatedProjectPath", PredefinedType.string.nullable)
27+
field("generatedFilesPaths", immutableList(PredefinedType.string))
2728
field("exceptionMessage", PredefinedType.string.nullable)
29+
field("testsCount", PredefinedType.int)
30+
field("errorsCount", PredefinedType.int)
2831
}
2932

3033
init {
3134
call("generate", generateArguments, generateResults).async
3235
signal("ping", PredefinedType.string).async
3336
signal("log", PredefinedType.string).async
3437
}
35-
}
38+
}

utbot-rider/dotnet-sdk.cmd

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -251,9 +251,10 @@ if not exist "%DOTNET_TARGET_DIR%\dotnet.exe" (
251251

252252
REM Prevent globally installed .NET Core from leaking into this runtime's lookup
253253
SET DOTNET_MULTILEVEL_LOOKUP=0
254+
SET DOTNET_SYSTEM_GLOBALIZATION_INVARIANT=false
254255

255256
for /f "tokens=2 delims=:." %%c in ('chcp') do set /a PREV_CODE_PAGE=%%c
256-
chcp 65001 >nul && call "%DOTNET_TARGET_DIR%\dotnet.exe" %*
257+
call "%DOTNET_TARGET_DIR%\dotnet.exe" %*
257258
set /a DOTNET_EXIT_CODE=%ERRORLEVEL%
258259
chcp %PREV_CODE_PAGE% >nul
259260

utbot-rider/src/dotnet/UtBot/UtBot.Rd/Generated/VSharpModel.Generated.cs

Lines changed: 71 additions & 43 deletions
Large diffs are not rendered by default.
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
using System.Collections.Generic;
2+
3+
namespace UtBot.Rd;
4+
5+
// Type descriptors are separately serialized to json because
6+
// they are recursive (have type descriptor Parameters)
7+
public class TypeDescriptor
8+
{
9+
public int? ArrayRank { get; set; }
10+
public string Name { get; set; }
11+
public int? MethodParameterPosition { get; set; }
12+
public int? TypeParameterPosition { get; set; }
13+
public List<TypeDescriptor> Parameters { get; set; }
14+
}

utbot-rider/src/dotnet/UtBot/UtBot.VSharp/UtBot.VSharp.csproj

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@
1111
<ItemGroup>
1212
<ProjectReference Include="..\UtBot.Rd\UtBot.Rd.csproj" />
1313
</ItemGroup>
14+
1415
<ItemGroup>
15-
<PackageReference Include="VSTeam.VSharp" Version="0.0.6" />
16+
<PackageReference Include="VSTeam.VSharp" Version="0.0.7" />
1617
</ItemGroup>
1718
</Project>

utbot-rider/src/dotnet/UtBot/UtBot.VSharp/VSharpMain.cs

Lines changed: 130 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,16 @@
11
using System;
22
using System.Collections.Concurrent;
3-
using System.Diagnostics;
43
using System.IO;
4+
using System.Linq;
55
using System.Reflection;
66
using System.Runtime.Loader;
77
using System.Text;
8+
using System.Text.Json;
89
using JetBrains.Collections.Viewable;
910
using JetBrains.Lifetimes;
1011
using JetBrains.Rd;
1112
using JetBrains.Rd.Impl;
1213
using JetBrains.Rd.Tasks;
13-
using JetBrains.Util;
1414
using UtBot.Rd;
1515
using UtBot.Rd.Generated;
1616
using VSharp;
@@ -46,28 +46,136 @@ public override void WriteLine(string value)
4646
private static GenerateResults GenerateImpl(GenerateArguments arguments)
4747
{
4848
var (assemblyPath, projectCsprojPath, solutionFilePath,
49-
descriptor, generationTimeout, targetFramework) = arguments;
49+
methodDescriptors, generationTimeout, targetFramework) = arguments;
50+
5051
var assemblyLoadContext = new AssemblyLoadContext(VSharpProcessName);
51-
using var fs = File.OpenRead(assemblyPath);
52-
var ass = assemblyLoadContext.LoadFromStream(fs);
53-
var type = ass.GetType(descriptor.TypeName, throwOnError: false);
52+
assemblyLoadContext.Resolving += (context, name) =>
53+
{
54+
var found = Directory.GetFiles(new FileInfo(assemblyPath).Directory?.FullName, $"{name.Name}.dll")
55+
.FirstOrDefault();
56+
if (found is null)
57+
{
58+
return null;
59+
}
60+
61+
return context.LoadFromAssemblyPath(found);
62+
};
63+
var assembly = assemblyLoadContext.LoadFromAssemblyPath(assemblyPath);
64+
var methods = methodDescriptors.Select(d => d.ToMethodInfo(assembly)).ToList();
65+
var declaringType = methods.Select(m => m.DeclaringType).Distinct().SingleOrDefault();
66+
67+
var stat = TestGenerator.Cover(methods, generationTimeout, verbosity:Verbosity.Info);
68+
69+
var testedProject = new FileInfo(projectCsprojPath);
70+
var solution = new FileInfo(solutionFilePath);
71+
72+
var (generatedProject, renderedFiles) =
73+
Renderer.Render(stat.Results(), testedProject, declaringType, solution, targetFramework);
74+
75+
return new GenerateResults(
76+
generatedProject.FullName,
77+
renderedFiles,
78+
null,
79+
(int)stat.TestsCount,
80+
(int)stat.ErrorsCount);
81+
}
82+
83+
private static bool MatchesType(TypeDescriptor typeDescriptor, Type typ)
84+
{
85+
if (typ.IsGenericMethodParameter)
86+
{
87+
return typ.GenericParameterPosition == typeDescriptor.MethodParameterPosition;
88+
}
89+
90+
if (typ.IsGenericTypeParameter)
91+
{
92+
return typ.GenericParameterPosition == typeDescriptor.TypeParameterPosition;
93+
}
94+
95+
if (typ.IsArray)
96+
{
97+
return typ.GetArrayRank() == typeDescriptor.ArrayRank &&
98+
MatchesType(typeDescriptor.Parameters[0], typ.GetElementType());
99+
}
100+
101+
var name = typ.IsGenericType ? typ.GetGenericTypeDefinition().FullName : typ.FullName;
102+
103+
if (name != typeDescriptor.Name)
104+
{
105+
Logger.printLogString(Logger.Error, $"{typ.FullName} != {typeDescriptor.Name}");
106+
return false;
107+
}
108+
109+
var genericArguments = typ.GetGenericArguments();
110+
111+
if (genericArguments.Length != typeDescriptor.Parameters.Count)
112+
{
113+
return false;
114+
}
115+
116+
for (var i = 0; i < genericArguments.Length; ++i)
117+
{
118+
if (!MatchesType(typeDescriptor.Parameters[i], genericArguments[i]))
119+
{
120+
return false;
121+
}
122+
}
123+
124+
return true;
125+
}
126+
127+
private static bool MatchesMethod(MethodDescriptor descriptor, MethodInfo methodInfo)
128+
{
129+
var targetParameters = descriptor.Parameters.Select(p => JsonSerializer.Deserialize<TypeDescriptor>(p)).ToArray();
130+
131+
if (methodInfo.Name != descriptor.MethodName)
132+
{
133+
return false;
134+
}
135+
136+
var parameters = methodInfo.GetParameters();
137+
138+
if (parameters.Length != targetParameters.Length)
139+
{
140+
return false;
141+
}
142+
143+
for (var i = 0; i < parameters.Length; ++i)
144+
{
145+
if (!MatchesType(targetParameters[i], parameters[i].ParameterType))
146+
{
147+
return false;
148+
}
149+
}
150+
151+
return true;
152+
}
153+
154+
private static MethodInfo ToMethodInfo(this MethodDescriptor descriptor, Assembly assembly)
155+
{
156+
var type = assembly.GetType(descriptor.TypeName, throwOnError: false);
157+
54158
if (type?.FullName != descriptor.TypeName)
55-
throw new InvalidDataException($"cannot find type - {descriptor.TypeName}");
56-
var methodInfo = type.GetMethod(descriptor.MethodName,
57-
BindingFlags.Instance
58-
| BindingFlags.Static
59-
| BindingFlags.Public);
159+
throw new InvalidDataException($"Cannot find type {descriptor.TypeName}, found: {type?.Name}");
160+
161+
MethodInfo methodInfo;
162+
var bindingFlags = BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public;
163+
164+
if (descriptor.HasNoOverloads)
165+
{
166+
methodInfo = type.GetMethod(descriptor.MethodName, bindingFlags);
167+
}
168+
else
169+
{
170+
methodInfo = type.GetMethods()
171+
.FirstOrDefault(m => MatchesMethod(descriptor, m));
172+
}
173+
60174
if (methodInfo?.Name != descriptor.MethodName)
61175
throw new InvalidDataException(
62-
$"cannot find method - ${descriptor.MethodName} for type - ${descriptor.TypeName}");
63-
var stat = TestGenerator.Cover(methodInfo, generationTimeout, verbosity:Verbosity.Info);
64-
var targetProject = new FileInfo(projectCsprojPath);
65-
var solution = new FileInfo(solutionFilePath);
66-
var declaringType = methodInfo.DeclaringType;
67-
Debug.Assert(declaringType != null);
68-
var (generatedProject, renderedFiles) =
69-
Renderer.Render(stat.Results(), targetProject, declaringType, assemblyLoadContext, solution, targetFramework);
70-
return new GenerateResults(true, generatedProject.FullName, renderedFiles.ToArray(), null);
176+
$"Cannot find method ${descriptor.MethodName} for type ${descriptor.TypeName}");
177+
178+
return methodInfo;
71179
}
72180

73181
public static void Main(string[] args)
@@ -85,7 +193,7 @@ public static void Main(string[] args)
85193
{
86194
var vSharpModel = new VSharpModel(ldef.Lifetime, protocol);
87195
// Configuring V# logger: messages will be send via RD to UTBot plugin process
88-
Logger.ConfigureWriter(new SignalWriter(vSharpModel.Log));
196+
Logger.configureWriter(new SignalWriter(vSharpModel.Log));
89197
vSharpModel.Generate.Set((_, arguments) =>
90198
{
91199
try
@@ -94,7 +202,7 @@ public static void Main(string[] args)
94202
}
95203
catch (Exception e)
96204
{
97-
return new GenerateResults(false, "", EmptyArray<string>.Instance, e.ToString());
205+
return new GenerateResults(null, new(), e.ToString(), 0, 0);
98206
}
99207
finally
100208
{

utbot-rider/src/dotnet/UtBot/UtBot/GenerateUnitTestElementProvider.cs

Lines changed: 31 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,47 @@
1+
using System.Collections.Generic;
12
using JetBrains.Annotations;
23
using JetBrains.ReSharper.Feature.Services.CSharp.Generate;
34
using JetBrains.ReSharper.Feature.Services.Generate;
45
using JetBrains.ReSharper.Psi;
56
using JetBrains.ReSharper.Psi.CSharp;
67
using JetBrains.ReSharper.Psi.Resolve;
78
using JetBrains.ReSharper.Psi.Tree;
9+
using JetBrains.Util;
810

911
namespace UtBot;
1012

13+
internal class TimeoutGeneratorOption : IGeneratorOption
14+
{
15+
public static readonly string Id = "TimeoutGeneratorOption";
16+
private const string InitialValue = "10";
17+
18+
public IReadOnlyList<string> GetPossibleValues() => new string[] { };
19+
20+
public bool IsValidValue(string value) =>
21+
value.IsNullOrEmpty() || (int.TryParse(value, out var intValue) && intValue > 0);
22+
23+
public string ID => Id;
24+
25+
public string Title => "Timeout (s) for all selected methods";
26+
27+
public GeneratorOptionKind Kind => GeneratorOptionKind.Text;
28+
29+
public bool Persist => false;
30+
31+
public string Value { get; set; } = InitialValue;
32+
33+
public bool OverridesGlobalOption { get; set; } = false;
34+
35+
public bool HasDependentOptions => false;
36+
}
37+
1138
[GeneratorElementProvider(GenerateUnitTestWorkflow.Kind, typeof(CSharpLanguage))]
12-
public class GenerateUnitTestElementProvider : GeneratorProviderBase<CSharpGeneratorContext>
39+
internal class GenerateUnitTestElementProvider : GeneratorProviderBase<CSharpGeneratorContext>
1340
{
1441
public override void Populate(CSharpGeneratorContext context)
1542
{
16-
if (!VSharpFrameworkChecker.CanRunVSharp(context.PsiModule.TargetFrameworkId.Version))
17-
return;
18-
43+
context.Options.Add(new TimeoutGeneratorOption());
44+
1945
var memberSource = context.ExternalElementsSource?.GetTypeElement() ?? context.ClassDeclaration.DeclaredElement;
2046
if (memberSource == null) return;
2147

@@ -41,4 +67,4 @@ protected virtual bool MethodFilter([NotNull] IMethod method, ISubstitution subs
4167
if (method.GetAccessRights() != AccessRights.PUBLIC) return false;
4268
return true;
4369
}
44-
}
70+
}

utbot-rider/src/dotnet/UtBot/UtBot/GenerateUnitTestWorkflowProvider.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,4 +11,4 @@ public IEnumerable<IGenerateActionWorkflow> CreateWorkflow(IDataContext dataCont
1111
{
1212
return new[] { new GenerateUnitTestWorkflow() };
1313
}
14-
}
14+
}

utbot-rider/src/dotnet/UtBot/UtBot/ProcessWithRdServer.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ public ProcessWithRdServer(string name, string workingDir, int port, string exeP
4343
var wire = new SocketWire.Server(Lifetime, scheduler, socket);
4444
var serializers = new Serializers();
4545
var identities = new Identities(IdKind.Server);
46-
var startInfo = new ProcessStartInfo("dotnet", $"\"{exePath}\" {port}")
46+
var startInfo = new ProcessStartInfo("dotnet", $"--roll-forward LatestMajor \"{exePath}\" {port}")
4747
{
4848
WorkingDirectory = workingDir
4949
};

0 commit comments

Comments
 (0)