Skip to content

Commit bfda853

Browse files
committed
Merge branch 'next' into FollowUpOnSCStyleImportCommands
2 parents 578206b + 599243f commit bfda853

File tree

6 files changed

+105
-17
lines changed

6 files changed

+105
-17
lines changed

Rubberduck.CodeAnalysis/Inspections/Results/IdentifierReferenceInspectionResult.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,8 @@ public IdentifierReferenceInspectionResult(IInspection inspection, string descri
3434

3535
public override bool ChangesInvalidateResult(ICollection<QualifiedModuleName> modifiedModules)
3636
{
37-
return modifiedModules.Contains(Target.QualifiedModuleName)
38-
|| base.ChangesInvalidateResult(modifiedModules);
37+
return Target != null && modifiedModules.Contains(Target.QualifiedModuleName)
38+
|| base.ChangesInvalidateResult(modifiedModules);
3939
}
4040
}
4141
}

Rubberduck.Core/UI/Command/RunSelectedTestMethodCommand.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,8 @@ private Declaration FindDeclarationFromSelection()
5050

5151
private bool IsTestMethod(Declaration member)
5252
{
53-
return member.DeclarationType == DeclarationType.Procedure
53+
return member != null
54+
&& member.DeclarationType == DeclarationType.Procedure
5455
&& member.Annotations.Any(parseTreeAnnotation =>
5556
parseTreeAnnotation.Annotation is TestMethodAnnotation);
5657
}

Rubberduck.Deployment.Build/RubberduckPostBuildTask.cs

Lines changed: 34 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -48,27 +48,51 @@ internal DllFileParameters(string dllFile, string sourceDir, string targetDir)
4848

4949
public class RubberduckPostBuildTask : AppDomainIsolatedTask
5050
{
51+
/// <summary>
52+
/// Visual Studio Build Configuration (e.g. Debug or Release)
53+
/// </summary>
5154
[Required]
5255
public string Config { get; set; }
5356

57+
/// <summary>
58+
/// Full path to NetFX SDK directory
59+
/// </summary>
5460
[Required]
5561
public string NetToolsDir { get; set; }
5662

63+
/// <summary>
64+
/// Full path to WiX SDK directory
65+
/// </summary>
5766
[Required]
5867
public string WixToolsDir { get; set; }
5968

69+
/// <summary>
70+
/// Full path to the directory containing all the source files we want to deploy.
71+
/// </summary>
6072
[Required]
6173
public string SourceDir { get; set; }
6274

75+
/// <summary>
76+
/// Full path to the directory we want to write our modified files into.
77+
/// </summary>
6378
[Required]
6479
public string TargetDir { get; set; }
6580

81+
/// <summary>
82+
/// Root path of the project executing the build task.
83+
/// </summary>
6684
[Required]
6785
public string ProjectDir { get; set; }
6886

87+
/// <summary>
88+
/// Full path to the Inno Setup's include files.
89+
/// </summary>
6990
[Required]
7091
public string IncludeDir { get; set; }
7192

93+
/// <summary>
94+
/// Pipe-delimited list of DLL to generate TLBs from. Should contain only name &amp; extension and exist in <see cref="SourceDir"/>.
95+
/// </summary>
7296
[Required]
7397
public string FilesToExtract { get; set; }
7498

@@ -77,6 +101,12 @@ public class RubberduckPostBuildTask : AppDomainIsolatedTask
77101
private string RegFilePath =>
78102
Path.Combine(Path.Combine(ProjectDir, "LocalRegistryEntries"), "DebugRegistryEntries.reg");
79103

104+
/// <remarks>
105+
/// Entry point for the build task. To use the build task in a csproj, the <c>UsingTask</c> element must be specified before defining the task. The task will
106+
/// have the same name as the class, followed by the parameters. In this case, it would be <see cref="RubberduckPreBuildTask"/> element. See <c>Rubberduck.Deployment.csproj</c>
107+
/// for usage example. The public properties are used as a parameter in the MSBuild task and are both settable and gettable. Thus, we must read from the properties when
108+
/// we run the <c>Execute</c> which influences the behvaior of the task.
109+
/// </remarks>
80110
public override bool Execute()
81111
{
82112
var result = true;
@@ -191,7 +221,7 @@ private void UpdateAddInRegistration()
191221
{
192222
this.LogMessage("Updating addin registration...");
193223
var addInRegFile = Path.Combine(Path.GetDirectoryName(RegFilePath), "RubberduckAddinRegistry.reg");
194-
var command = $"reg.exe import \"{addInRegFile}";
224+
var command = $"reg.exe import \"{addInRegFile}\"";
195225
ExecuteTask(command);
196226
}
197227

@@ -336,14 +366,14 @@ private void RemovePreviousDebugRegistration()
336366
var now = DateTime.UtcNow;
337367
if (Environment.Is64BitOperatingSystem)
338368
{
339-
var command = $"reg.exe import {lastRegFile} /reg:32";
369+
var command = $"reg.exe import \"{lastRegFile}\" /reg:32";
340370
ExecuteTask(command);
341-
command = $"reg.exe import {lastRegFile} /reg:64";
371+
command = $"reg.exe import \"{lastRegFile}\" /reg:64";
342372
ExecuteTask(command);
343373
}
344374
else
345375
{
346-
var command = $"reg.exe import {lastRegFile}";
376+
var command = $"reg.exe import \"{lastRegFile}\"";
347377
ExecuteTask(command);
348378
}
349379

Rubberduck.Deployment.Build/RubberduckPreBuildTask.cs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,24 @@ namespace Rubberduck.Deployment.Build
77
{
88
public class RubberduckPreBuildTask : AppDomainIsolatedTask
99
{
10+
/// <summary>
11+
/// Full path to the directory containing the templates we need to modify.
12+
/// </summary>
1013
[Required]
1114
public string WorkingDir { get; set; }
1215

16+
/// <summary>
17+
/// Full path to the directory where we want to place our modified files.
18+
/// </summary>
1319
[Required]
1420
public string OutputDir { get; set; }
1521

22+
/// <remarks>
23+
/// Entry point for the build task. To use the build task in a csproj, the <c>UsingTask</c> element must be specified before defining the task. The task will
24+
/// have the same name as the class, followed by the parameters. In this case, it would be <see cref="RubberduckPreBuildTask"/> element. See <c>Rubberduck.Deployment.csproj</c>
25+
/// for usage example. The public properties are used as a parameter in the MSBuild task and are both settable and gettable. Thus, we must read from the properties when
26+
/// we run the <c>Execute</c> which influences the behvaior of the task.
27+
/// </remarks>
1628
public override bool Execute()
1729
{
1830
var result = true;

Rubberduck.UnitTesting/UnitTesting/TestEngine.cs

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
using System.Diagnostics;
44
using System.Linq;
55
using System.Runtime.InteropServices;
6+
using System.Threading.Tasks;
67
using NLog;
78
using Rubberduck.JunkDrawer.Extensions;
89
using Rubberduck.Parsing.Annotations;
@@ -19,10 +20,8 @@ namespace Rubberduck.UnitTesting
1920
// FIXME litter logging around here
2021
internal class TestEngine : ITestEngine
2122
{
22-
private static readonly ParserState[] AllowedRunStates =
23+
protected static readonly ParserState[] AllowedRunStates =
2324
{
24-
ParserState.ResolvedDeclarations,
25-
ParserState.ResolvingReferences,
2625
ParserState.Ready
2726
};
2827

@@ -173,13 +172,14 @@ public void RequestCancellation()
173172
CancellationRequested = true;
174173
}
175174

176-
private void RunInternal(IEnumerable<TestMethod> tests)
175+
protected virtual void RunInternal(IEnumerable<TestMethod> tests)
177176
{
178177
if (!CanRun)
179178
{
180179
return;
181180
}
182-
_state.OnSuspendParser(this, AllowedRunStates, () => RunWhileSuspended(tests));
181+
//We push the suspension to a background thread to avoid potential deadlocks if a parse is still running.
182+
Task.Run(() => _state.OnSuspendParser(this, AllowedRunStates, () => RunWhileSuspended(tests)));
183183
}
184184

185185
private void EnsureRubberduckIsReferencedForEarlyBoundTests()
@@ -200,7 +200,15 @@ private void EnsureRubberduckIsReferencedForEarlyBoundTests()
200200
}
201201
}
202202

203-
private void RunWhileSuspended(IEnumerable<TestMethod> tests)
203+
protected void RunWhileSuspended(IEnumerable<TestMethod> tests)
204+
{
205+
//Running the tests has to be done on the UI thread, so we push the task to it from within suspension of the parser.
206+
//We have to wait for the completion to make sure that the suspension only ends after tests have been completed.
207+
var testTask = _uiDispatcher.StartTask(() => RunWhileSuspendedOnUiThread(tests));
208+
testTask.Wait();
209+
}
210+
211+
private void RunWhileSuspendedOnUiThread(IEnumerable<TestMethod> tests)
204212
{
205213
var testMethods = tests as IList<TestMethod> ?? tests.ToList();
206214
if (!testMethods.Any())

RubberduckTests/UnitTesting/MockedTestEngine.cs

Lines changed: 41 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
using System.Collections.Generic;
33
using System.Diagnostics.CodeAnalysis;
44
using System.Linq;
5+
using System.Threading.Tasks;
56
using Moq;
67
using NUnit.Framework;
78
using Rubberduck.Parsing.UIContext;
@@ -47,8 +48,17 @@ internal class MockedTestEngine : IDisposable
4748

4849
private MockedTestEngine()
4950
{
50-
Dispatcher.Setup(d => d.InvokeAsync(It.IsAny<Action>())).Callback((Action action) => action.Invoke()).Verifiable();
51-
51+
Dispatcher.Setup(d => d.InvokeAsync(It.IsAny<Action>()))
52+
.Callback((Action action) => action.Invoke())
53+
.Verifiable();
54+
Dispatcher.Setup(d => d.StartTask(It.IsAny<Action>(), It.IsAny<TaskCreationOptions>()))
55+
.Returns((Action action, TaskCreationOptions options) =>
56+
{
57+
action.Invoke();
58+
return Task.CompletedTask;
59+
})
60+
.Verifiable();
61+
5262
TypeLib.Setup(tlm => tlm.Dispose()).Verifiable();
5363
WrapperProvider.Setup(p => p.TypeLibWrapperFromProject(It.IsAny<string>())).Returns(TypeLib.Object).Verifiable();
5464

@@ -64,7 +74,7 @@ public MockedTestEngine(string testModuleCode) : this()
6474

6575
Vbe = builder.Build();
6676
ParserState = MockParser.Create(Vbe.Object).State;
67-
TestEngine = new TestEngine(ParserState, _fakesFactory.Object, VbeInteraction.Object, WrapperProvider.Object, Dispatcher.Object, Vbe.Object);
77+
TestEngine = new SynchronouslySuspendingTestEngine(ParserState, _fakesFactory.Object, VbeInteraction.Object, WrapperProvider.Object, Dispatcher.Object, Vbe.Object);
6878
}
6979

7080
public MockedTestEngine(IReadOnlyList<string> moduleNames, IReadOnlyList<int> methodCounts) : this()
@@ -87,7 +97,7 @@ public MockedTestEngine(IReadOnlyList<string> moduleNames, IReadOnlyList<int> me
8797
project.AddProjectToVbeBuilder();
8898
Vbe = builder.Build();
8999
ParserState = MockParser.Create(Vbe.Object).State;
90-
TestEngine = new TestEngine(ParserState, _fakesFactory.Object, VbeInteraction.Object, WrapperProvider.Object, Dispatcher.Object, Vbe.Object);
100+
TestEngine = new SynchronouslySuspendingTestEngine(ParserState, _fakesFactory.Object, VbeInteraction.Object, WrapperProvider.Object, Dispatcher.Object, Vbe.Object);
91101
}
92102

93103
public MockedTestEngine(int testMethodCount)
@@ -246,5 +256,32 @@ Public Sub TestCleanup()
246256
'this method runs after every test in the module.
247257
End Sub
248258
";
259+
260+
private class SynchronouslySuspendingTestEngine : TestEngine
261+
{
262+
private readonly RubberduckParserState _state;
263+
264+
public SynchronouslySuspendingTestEngine(
265+
RubberduckParserState state,
266+
IFakesFactory fakesFactory,
267+
IVBEInteraction declarationRunner,
268+
ITypeLibWrapperProvider wrapperProvider,
269+
IUiDispatcher uiDispatcher,
270+
IVBE vbe)
271+
: base(state, fakesFactory, declarationRunner, wrapperProvider, uiDispatcher, vbe)
272+
{
273+
_state = state;
274+
}
275+
276+
protected override void RunInternal(IEnumerable<TestMethod> tests)
277+
{
278+
if (!CanRun)
279+
{
280+
return;
281+
}
282+
//We have to do this on the same thread here to guarantee that the actions runs before the assert in the unit tests is called.
283+
_state.OnSuspendParser(this, AllowedRunStates, () => RunWhileSuspended(tests));
284+
}
285+
}
249286
}
250287
}

0 commit comments

Comments
 (0)