Skip to content

Commit f1c1f0c

Browse files
authored
Merge pull request #3956 from bclothier/DisableParserOnUnitTest
Introducing Busy state and SuspendParser action
2 parents 4176185 + 29ac896 commit f1c1f0c

File tree

18 files changed

+739
-172
lines changed

18 files changed

+739
-172
lines changed

Rubberduck.API/VBA/Parser.cs

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
using System.Globalization;
55
using System.Linq;
66
using System.Runtime.InteropServices;
7+
using System.Threading;
78
using Rubberduck.Common;
89
using Rubberduck.Parsing.PreProcessing;
910
using Rubberduck.Parsing.Symbols.DeclarationLoaders;
@@ -60,15 +61,17 @@ public sealed class Parser : IParser, IDisposable
6061
{
6162
private RubberduckParserState _state;
6263
private AttributeParser _attributeParser;
63-
private ParseCoordinator _parser;
64+
private SynchronousParseCoordinator _parser;
6465
private IVBE _vbe;
6566
private IVBEEvents _vbeEvents;
6667
private readonly IUiDispatcher _dispatcher;
68+
private readonly CancellationTokenSource _tokenSource;
6769

6870
internal Parser()
6971
{
7072
UiContextProvider.Initialize();
7173
_dispatcher = new UiDispatcher(UiContextProvider.Instance());
74+
_tokenSource = new CancellationTokenSource();
7275
}
7376

7477
// vbe is the com coclass interface from the interop assembly.
@@ -137,22 +140,21 @@ internal Parser(object vbe) : this()
137140
supertypeClearer
138141
);
139142

140-
_parser = new ParseCoordinator(
143+
_parser = new SynchronousParseCoordinator(
141144
_state,
142145
parsingStageService,
143146
parsingCacheService,
144147
projectManager,
145148
parserStateManager
146149
);
147150
}
148-
151+
149152
/// <summary>
150153
/// Blocking call, for easier unit-test code
151154
/// </summary>
152155
public void Parse()
153156
{
154-
// blocking call
155-
_parser.Parse(new System.Threading.CancellationTokenSource());
157+
_parser.Parse(_tokenSource);
156158
}
157159

158160
/// <summary>
@@ -163,7 +165,7 @@ public void BeginParse()
163165
// non-blocking call
164166
_dispatcher.Invoke(() => _state.OnParseRequested(this));
165167
}
166-
168+
167169
public delegate void OnStateChangedDelegate(ParserState ParserState);
168170
public event OnStateChangedDelegate OnStateChanged;
169171

Rubberduck.Core/Rubberduck.Core.csproj

Lines changed: 3 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -231,18 +231,9 @@
231231
<Reference Include="Antlr4.Runtime, Version=4.6.0.0, Culture=neutral, PublicKeyToken=09abb75b9ed49849, processorArchitecture=MSIL">
232232
<HintPath>..\packages\Antlr4.Runtime.4.6.4\lib\net45\Antlr4.Runtime.dll</HintPath>
233233
</Reference>
234-
<Reference Include="Castle.Core, Version=4.0.0.0, Culture=neutral, PublicKeyToken=407dd0808d44fbdc, processorArchitecture=MSIL">
235-
<HintPath>..\packages\Castle.Core.4.2.1\lib\net45\Castle.Core.dll</HintPath>
236-
</Reference>
237-
<Reference Include="Castle.Windsor, Version=4.0.0.0, Culture=neutral, PublicKeyToken=407dd0808d44fbdc, processorArchitecture=MSIL">
238-
<HintPath>..\packages\Castle.Windsor.4.1.0\lib\net45\Castle.Windsor.dll</HintPath>
239-
</Reference>
240234
<Reference Include="EasyHook, Version=2.7.6684.0, Culture=neutral, PublicKeyToken=4b580fca19d0b0c5, processorArchitecture=MSIL">
241235
<HintPath>..\packages\EasyHook.2.7.6684\lib\net40\EasyHook.dll</HintPath>
242236
</Reference>
243-
<Reference Include="extensibility, Version=7.0.3300.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
244-
<EmbedInteropTypes>True</EmbedInteropTypes>
245-
</Reference>
246237
<Reference Include="HtmlAgilityPack, Version=1.8.4.0, Culture=neutral, PublicKeyToken=bd319b19eaf3b43a, processorArchitecture=MSIL">
247238
<HintPath>..\packages\HtmlAgilityPack.1.8.4\lib\Net45\HtmlAgilityPack.dll</HintPath>
248239
</Reference>
@@ -262,26 +253,14 @@
262253
<Reference Include="PresentationCore" />
263254
<Reference Include="PresentationFramework" />
264255
<Reference Include="PresentationFramework.Aero" />
265-
<Reference Include="ReachFramework" />
266-
<Reference Include="stdole, Version=7.0.3300.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
267-
<EmbedInteropTypes>False</EmbedInteropTypes>
268-
</Reference>
269256
<Reference Include="System" />
270-
<Reference Include="System.Configuration" />
271257
<Reference Include="System.Core" />
272-
<Reference Include="System.Data" />
273258
<Reference Include="System.Drawing" />
274259
<Reference Include="System.IO.Compression" />
275260
<Reference Include="System.Net.Http" />
276-
<Reference Include="System.Printing" />
277-
<Reference Include="System.Runtime.Remoting" />
278-
<Reference Include="System.Runtime.Serialization" />
279-
<Reference Include="System.ServiceModel" />
280-
<Reference Include="System.Transactions" />
281261
<Reference Include="System.ValueTuple, Version=4.0.2.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
282262
<HintPath>..\packages\System.ValueTuple.4.4.0\lib\netstandard1.0\System.ValueTuple.dll</HintPath>
283263
</Reference>
284-
<Reference Include="System.Web" />
285264
<Reference Include="System.Windows.Forms" />
286265
<Reference Include="System.Windows.Interactivity, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
287266
<HintPath>..\packages\System.Windows.Interactivity.WPF.2.0.20525\lib\net40\System.Windows.Interactivity.dll</HintPath>
@@ -924,6 +903,9 @@
924903
<Folder Include="Properties\DataSources\" />
925904
</ItemGroup>
926905
<ItemGroup>
906+
<Compile Include="AutoComplete\AutoCompleteFunctionBlock.cs" />
907+
<Compile Include="AutoComplete\AutoCompletePropertyBlock.cs" />
908+
<Compile Include="AutoComplete\AutoCompleteSubBlock.cs" />
927909
<Content Include="EasyHook32.dll">
928910
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
929911
</Content>
@@ -942,9 +924,6 @@
942924
<Content Include="EasyLoad64.dll">
943925
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
944926
</Content>
945-
<Compile Include="AutoComplete\AutoCompleteFunctionBlock.cs" />
946-
<Compile Include="AutoComplete\AutoCompletePropertyBlock.cs" />
947-
<Compile Include="AutoComplete\AutoCompleteSubBlock.cs" />
948927
<None Include="NLog.xsd">
949928
<SubType>Designer</SubType>
950929
</None>

Rubberduck.Core/UI/Command/RunAllTestsCommand.cs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,11 +31,9 @@ public RunAllTestsCommand(IVBE vbe, RubberduckParserState state, ITestEngine eng
3131
_presenter = presenter;
3232
}
3333

34-
private static readonly ParserState[] AllowedRunStates = { ParserState.ResolvedDeclarations, ParserState.ResolvingReferences, ParserState.Ready };
35-
3634
protected override bool EvaluateCanExecute(object parameter)
3735
{
38-
return _vbe.IsInDesignMode && AllowedRunStates.Contains(_state.Status);
36+
return _vbe.IsInDesignMode && _engine.AllowedRunStates.Contains(_state.Status);
3937
}
4038

4139
protected override void OnExecute(object parameter)

Rubberduck.Core/UI/Inspections/InspectionResultsViewModel.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -292,6 +292,11 @@ private void HandleStateChanged(object sender, ParserStateEventArgs e)
292292
return;
293293
}
294294

295+
if (_state.Status == ParserState.Ready && e.OldState == ParserState.Busy)
296+
{
297+
return;
298+
}
299+
295300
if (_runInspectionsOnReparse || IsRefreshing)
296301
{
297302
RefreshInspections(e.Token);

Rubberduck.Core/UnitTesting/ITestEngine.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using System;
22
using System.Collections.Generic;
3+
using Rubberduck.Parsing.VBA;
34
using Rubberduck.UI.UnitTesting;
45
using Rubberduck.VBEditor;
56

@@ -12,6 +13,7 @@ public interface ITestEngine
1213
void Run(IEnumerable<TestMethod> tests);
1314
void Refresh();
1415
event EventHandler TestCompleted;
16+
ParserState[] AllowedRunStates { get; }
1517
}
1618

1719
public class TestModuleEventArgs : EventArgs

Rubberduck.Core/UnitTesting/TestEngine.cs

Lines changed: 93 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,13 @@ public class TestEngine : ITestEngine
2424
private readonly IVBETypeLibsAPI _typeLibApi;
2525
private readonly IUiDispatcher _uiDispatcher;
2626

27+
public ParserState[] AllowedRunStates => new[]
28+
{
29+
ParserState.ResolvedDeclarations,
30+
ParserState.ResolvingReferences,
31+
ParserState.Ready
32+
};
33+
2734
private static readonly Logger Logger = LogManager.GetCurrentClassLogger();
2835

2936
private bool _testRequested;
@@ -38,7 +45,7 @@ public TestEngine(TestExplorerModel model, IVBE vbe, RubberduckParserState state
3845
_fakesFactory = fakesFactory;
3946
_typeLibApi = typeLibApi;
4047
_uiDispatcher = uiDispatcher;
41-
48+
4249
_state.StateChanged += StateChangedHandler;
4350
}
4451

@@ -88,67 +95,115 @@ public void Run(IEnumerable<TestMethod> tests)
8895
}
8996

9097
private void RunInternal(IEnumerable<TestMethod> tests)
98+
{
99+
if (!AllowedRunStates.Contains(_state.Status))
100+
{
101+
return;
102+
}
103+
104+
_state.OnSuspendParser(this, AllowedRunStates, () => RunWhileSuspended(tests));
105+
}
106+
107+
private void RunWhileSuspended(IEnumerable<TestMethod> tests)
91108
{
92109
var testMethods = tests as IList<TestMethod> ?? tests.ToList();
93110
if (!testMethods.Any())
94111
{
95112
return;
96113
}
97114

98-
var modules = testMethods.GroupBy(test => test.Declaration.QualifiedName.QualifiedModuleName);
99-
foreach (var module in modules)
115+
try
100116
{
101-
var testInitialize = module.Key.FindTestInitializeMethods(_state).ToList();
102-
var testCleanup = module.Key.FindTestCleanupMethods(_state).ToList();
117+
var modules = testMethods.GroupBy(test => test.Declaration.QualifiedName.QualifiedModuleName);
118+
foreach (var module in modules)
119+
{
120+
var testInitialize = module.Key.FindTestInitializeMethods(_state).ToList();
121+
var testCleanup = module.Key.FindTestCleanupMethods(_state).ToList();
103122

104-
var capturedModule = module;
105-
var moduleTestMethods = testMethods
106-
.Where(test => test.Declaration.QualifiedName.QualifiedModuleName.ProjectId == capturedModule.Key.ProjectId
107-
&& test.Declaration.QualifiedName.QualifiedModuleName.ComponentName == capturedModule.Key.ComponentName);
123+
var capturedModule = module;
124+
var moduleTestMethods = testMethods
125+
.Where(test =>
126+
{
127+
var qmn = test.Declaration.QualifiedName.QualifiedModuleName;
108128

109-
var fakes = _fakesFactory.Create();
110-
Run(module.Key.FindModuleInitializeMethods(_state));
111-
foreach (var test in moduleTestMethods)
112-
{
113-
// no need to run setup/teardown for ignored tests
114-
if (test.Declaration.Annotations.Any(a => a.AnnotationType == AnnotationType.IgnoreTest))
129+
return qmn.ProjectId == capturedModule.Key.ProjectId
130+
&& qmn.ComponentName == capturedModule.Key.ComponentName;
131+
});
132+
133+
var fakes = _fakesFactory.Create();
134+
var initializeMethods = module.Key.FindModuleInitializeMethods(_state);
135+
try
115136
{
116-
test.UpdateResult(TestOutcome.Ignored);
117-
OnTestCompleted();
137+
RunInternal(initializeMethods);
138+
}
139+
catch (COMException ex)
140+
{
141+
Logger.Error(ex,
142+
"Unexpected COM exception while initializing tests for module {0}. The module will be skipped.",
143+
module.Key.Name);
144+
foreach (var method in moduleTestMethods)
145+
{
146+
method.UpdateResult(TestOutcome.Unknown, AssertMessages.Assert_ComException);
147+
}
118148
continue;
119149
}
150+
foreach (var test in moduleTestMethods)
151+
{
152+
// no need to run setup/teardown for ignored tests
153+
if (test.Declaration.Annotations.Any(a => a.AnnotationType == AnnotationType.IgnoreTest))
154+
{
155+
test.UpdateResult(TestOutcome.Ignored);
156+
OnTestCompleted();
157+
continue;
158+
}
159+
160+
var stopwatch = new Stopwatch();
161+
stopwatch.Start();
162+
163+
try
164+
{
165+
fakes.StartTest();
166+
RunInternal(testInitialize);
167+
test.Run();
168+
RunInternal(testCleanup);
169+
}
170+
catch (COMException ex)
171+
{
172+
Logger.Error(ex, "Unexpected COM exception while running tests.");
173+
test.UpdateResult(TestOutcome.Inconclusive, AssertMessages.Assert_ComException);
174+
}
175+
finally
176+
{
177+
fakes.StopTest();
178+
}
179+
180+
stopwatch.Stop();
181+
test.Result.SetDuration(stopwatch.ElapsedMilliseconds);
120182

121-
var stopwatch = new Stopwatch();
122-
stopwatch.Start();
123-
183+
OnTestCompleted();
184+
Model.AddExecutedTest(test);
185+
}
186+
var cleanupMethods = module.Key.FindModuleCleanupMethods(_state);
124187
try
125188
{
126-
fakes.StartTest();
127-
Run(testInitialize);
128-
test.Run();
129-
Run(testCleanup);
189+
RunInternal(cleanupMethods);
130190
}
131191
catch (COMException ex)
132192
{
133-
Logger.Error(ex, "Unexpected COM exception while running tests.", test.Declaration?.QualifiedName);
134-
test.UpdateResult(TestOutcome.Inconclusive, AssertMessages.Assert_ComException);
135-
}
136-
finally
137-
{
138-
fakes.StopTest();
193+
Logger.Error(ex,
194+
"Unexpected COM exception while cleaning up tests for module {0}. Aborting any further unit tests",
195+
module.Key.Name);
196+
break;
139197
}
140-
141-
stopwatch.Stop();
142-
test.Result.SetDuration(stopwatch.ElapsedMilliseconds);
143-
144-
OnTestCompleted();
145-
Model.AddExecutedTest(test);
146198
}
147-
Run(module.Key.FindModuleCleanupMethods(_state));
199+
}
200+
catch (Exception ex)
201+
{
202+
Logger.Error(ex, "Unexpected expection while running unit tests; unit tests will be aborted");
148203
}
149204
}
150205

151-
private void Run(IEnumerable<Declaration> members)
206+
private void RunInternal(IEnumerable<Declaration> members)
152207
{
153208
var groupedMembers = members.GroupBy(m => m.ProjectId);
154209
foreach (var group in groupedMembers)

Rubberduck.Core/packages.config

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,6 @@
22
<packages>
33
<package id="Antlr4.Runtime" version="4.6.4" targetFramework="net46" />
44
<package id="AvalonEdit" version="5.0.4" targetFramework="net46" />
5-
<package id="Castle.Core" version="4.2.1" targetFramework="net46" />
6-
<package id="Castle.Windsor" version="4.1.0" targetFramework="net46" />
75
<package id="EasyHook" version="2.7.6684" targetFramework="net46" />
86
<package id="HtmlAgilityPack" version="1.8.4" targetFramework="net46" />
97
<package id="NLog" version="4.5.4" targetFramework="net46" />

Rubberduck.Main/Rubberduck.Main.csproj

Lines changed: 0 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -316,30 +316,17 @@
316316
<Reference Include="extensibility, Version=7.0.3300.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
317317
<EmbedInteropTypes>True</EmbedInteropTypes>
318318
</Reference>
319-
<Reference Include="Infralution.Localization.Wpf">
320-
<HintPath>..\libs\Infralution.Localization.Wpf.dll</HintPath>
321-
</Reference>
322319
<Reference Include="Microsoft.CSharp" />
323320
<Reference Include="NLog, Version=4.0.0.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c, processorArchitecture=MSIL">
324321
<HintPath>..\packages\NLog.4.5.4\lib\net45\NLog.dll</HintPath>
325322
</Reference>
326323
<Reference Include="PresentationCore" />
327324
<Reference Include="PresentationFramework" />
328-
<Reference Include="stdole, Version=7.0.3300.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
329-
<EmbedInteropTypes>False</EmbedInteropTypes>
330-
</Reference>
331325
<Reference Include="System" />
332-
<Reference Include="System.Configuration" />
333-
<Reference Include="System.Data" />
334326
<Reference Include="System.Drawing" />
335327
<Reference Include="System.IO.Compression" />
336-
<Reference Include="System.Runtime.Remoting" />
337-
<Reference Include="System.Runtime.Serialization" />
338-
<Reference Include="System.ServiceModel" />
339-
<Reference Include="System.Transactions" />
340328
<Reference Include="System.Windows.Forms" />
341329
<Reference Include="System.Xaml" />
342-
<Reference Include="System.Xml" />
343330
<Reference Include="WindowsBase" />
344331
</ItemGroup>
345332
<ItemGroup>

Rubberduck.Parsing/Rubberduck.Parsing.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -354,6 +354,7 @@
354354
<Compile Include="VBA\BuiltInDeclarationLoader.cs" />
355355
<Compile Include="PreProcessing\TokensValue.cs" />
356356
<Compile Include="VBA\SupertypeClearer.cs" />
357+
<Compile Include="VBA\SynchronousParseCoordinator.cs" />
357358
<Compile Include="VBA\SynchronousSupertypeClearer.cs" />
358359
<Compile Include="VBA\SupertypeClearerBase.cs" />
359360
<Compile Include="VBA\ISupertypeClearer.cs" />

0 commit comments

Comments
 (0)