Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Introducing Busy state and SuspendParser action #3956

Merged
merged 46 commits into from Jul 31, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
f9469e9
Initial version of Busy state and SuspendParser function
bclothier Apr 23, 2018
f3b6d10
Add new unit tests to test the SuspendParser and the parser state to …
bclothier Apr 24, 2018
93e146d
Correct some gaps in the SuspendParser logic
bclothier Apr 24, 2018
f34450f
Merge branch 'next' of https://github.com/rubberduck-vba/Rubberduck i…
bclothier May 3, 2018
9f1e619
Go away, you old version that should never been merged in first place.
bclothier May 3, 2018
caf6192
Merge remote-tracking branch 'remotes/rubberduck-vba/next' into Disab…
bclothier May 4, 2018
16cc2a4
Add try catch blocks; 2 for the initialization and cleanup, and then …
bclothier May 4, 2018
4c2243b
Merge branch 'next' of https://github.com/rubberduck-vba/Rubberduck i…
bclothier May 4, 2018
2dc0ce7
Merge branch 'next' of https://github.com/rubberduck-vba/Rubberduck i…
bclothier May 6, 2018
88fada3
Merge branch 'next' of https://github.com/rubberduck-vba/Rubberduck i…
bclothier May 18, 2018
005bdca
Post merge cleanup (wrong reference to resx)
bclothier May 18, 2018
c82b58e
Address thread safety concerns; move suspend activity to ParseCoordin…
bclothier May 18, 2018
945e8b9
Merge branch 'next' of https://github.com/rubberduck-vba/Rubberduck i…
bclothier Jun 10, 2018
96bbe11
Revise the locking scheme
bclothier Jun 11, 2018
2168f55
Update unit tests, handle recursion
bclothier Jun 11, 2018
6bbf281
Improve the thread safety around the state checking and remove the ne…
bclothier Jun 11, 2018
20394da
Tighten the thread safety around the Interlocked. Address other PR co…
bclothier Jun 12, 2018
7f83b67
Merge branch 'next' of https://github.com/rubberduck-vba/Rubberduck i…
bclothier Jun 15, 2018
98dd544
Added Declined signal -- currently unused but logged to aid in uncove…
bclothier Jun 16, 2018
10f4044
Remove the unnecessary deadlock check from ParseInternal method.
bclothier Jun 17, 2018
d117066
Merge branch 'next' of https://github.com/rubberduck-vba/Rubberduck i…
bclothier Jun 17, 2018
59d3a2f
Perform cancellation before the busy action to ensure inspections abort.
bclothier Jun 17, 2018
266ae63
Add a separate lock for managing the suspension marker & stack, separ…
bclothier Jun 20, 2018
21160f9
Swap the order of lock taken
bclothier Jun 20, 2018
87819b2
Add missing using block for the mock states in all unit tests.
bclothier Jun 20, 2018
21d6126
Update the default for the timeout to be infinite by default.
bclothier Jun 20, 2018
94251a6
Behold how gooder I am with math
bclothier Jun 20, 2018
7009ab7
Merge branch 'next' of https://github.com/rubberduck-vba/Rubberduck i…
bclothier Jun 21, 2018
a75e056
Merge branch 'next' of https://github.com/rubberduck-vba/Rubberduck i…
bclothier Jun 22, 2018
efe57a5
Remove test scope flag and create a testing subtype for ParseCoordina…
bclothier Jun 23, 2018
e6d2e9f
Merge branch 'next' of https://github.com/rubberduck-vba/Rubberduck i…
bclothier Jun 25, 2018
ccd0c1b
Rearrange the cancel logic and refactor methods that are for unit tes…
bclothier Jun 25, 2018
2c9d87d
Update the reflection API to handle cancellation correctly.
bclothier Jun 25, 2018
9b9d7ee
Add old state, allowed run states, and suspension result fields to th…
bclothier Jun 26, 2018
e86d462
Fix the logic for setting the completed state of the suspension resul…
bclothier Jun 26, 2018
c7cd8b7
Merge branch 'next' of https://github.com/rubberduck-vba/Rubberduck i…
bclothier Jun 27, 2018
b45b886
Merge branch 'next' of https://github.com/rubberduck-vba/Rubberduck i…
bclothier Jun 30, 2018
5d30fc3
Remove the Cancel from API; to be handled in a future PR.
bclothier Jul 5, 2018
d50f1ae
Convert TestParseCoordinator into SynchronousParseCoordinator and ref…
bclothier Jul 6, 2018
928687f
Merge branch 'next' of https://github.com/rubberduck-vba/Rubberduck i…
bclothier Jul 14, 2018
8f5ff7c
Resolve the conflict in test project
bclothier Jul 14, 2018
7ab1229
Remove the unwanted change on Windsor dependencies.
bclothier Jul 14, 2018
6dc093c
Merge branch 'next' of https://github.com/rubberduck-vba/Rubberduck i…
bclothier Jul 20, 2018
44fe565
Remember the previous state and make inspection view model use that i…
bclothier Jul 21, 2018
823f483
Merge branch 'next' of https://github.com/rubberduck-vba/Rubberduck i…
bclothier Jul 30, 2018
29ac896
Make the parameter for state and OldState non-optional.
bclothier Jul 30, 2018
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
14 changes: 8 additions & 6 deletions Rubberduck.API/VBA/Parser.cs
Expand Up @@ -4,6 +4,7 @@
using System.Globalization;
using System.Linq;
using System.Runtime.InteropServices;
using System.Threading;
using Rubberduck.Common;
using Rubberduck.Parsing.PreProcessing;
using Rubberduck.Parsing.Symbols.DeclarationLoaders;
Expand Down Expand Up @@ -60,15 +61,17 @@ public sealed class Parser : IParser, IDisposable
{
private RubberduckParserState _state;
private AttributeParser _attributeParser;
private ParseCoordinator _parser;
private SynchronousParseCoordinator _parser;
private IVBE _vbe;
private IVBEEvents _vbeEvents;
private readonly IUiDispatcher _dispatcher;
private readonly CancellationTokenSource _tokenSource;

internal Parser()
{
UiContextProvider.Initialize();
_dispatcher = new UiDispatcher(UiContextProvider.Instance());
_tokenSource = new CancellationTokenSource();
}

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

_parser = new ParseCoordinator(
_parser = new SynchronousParseCoordinator(
_state,
parsingStageService,
parsingCacheService,
projectManager,
parserStateManager
);
}

/// <summary>
/// Blocking call, for easier unit-test code
/// </summary>
public void Parse()
{
// blocking call
_parser.Parse(new System.Threading.CancellationTokenSource());
_parser.Parse(_tokenSource);
}

/// <summary>
Expand All @@ -163,7 +165,7 @@ public void BeginParse()
// non-blocking call
_dispatcher.Invoke(() => _state.OnParseRequested(this));
}

public delegate void OnStateChangedDelegate(ParserState ParserState);
public event OnStateChangedDelegate OnStateChanged;

Expand Down
27 changes: 3 additions & 24 deletions Rubberduck.Core/Rubberduck.Core.csproj
Expand Up @@ -231,18 +231,9 @@
<Reference Include="Antlr4.Runtime, Version=4.6.0.0, Culture=neutral, PublicKeyToken=09abb75b9ed49849, processorArchitecture=MSIL">
<HintPath>..\packages\Antlr4.Runtime.4.6.4\lib\net45\Antlr4.Runtime.dll</HintPath>
</Reference>
<Reference Include="Castle.Core, Version=4.0.0.0, Culture=neutral, PublicKeyToken=407dd0808d44fbdc, processorArchitecture=MSIL">
<HintPath>..\packages\Castle.Core.4.2.1\lib\net45\Castle.Core.dll</HintPath>
</Reference>
<Reference Include="Castle.Windsor, Version=4.0.0.0, Culture=neutral, PublicKeyToken=407dd0808d44fbdc, processorArchitecture=MSIL">
<HintPath>..\packages\Castle.Windsor.4.1.0\lib\net45\Castle.Windsor.dll</HintPath>
</Reference>
<Reference Include="EasyHook, Version=2.7.6684.0, Culture=neutral, PublicKeyToken=4b580fca19d0b0c5, processorArchitecture=MSIL">
<HintPath>..\packages\EasyHook.2.7.6684\lib\net40\EasyHook.dll</HintPath>
</Reference>
<Reference Include="extensibility, Version=7.0.3300.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<EmbedInteropTypes>True</EmbedInteropTypes>
</Reference>
<Reference Include="HtmlAgilityPack, Version=1.8.4.0, Culture=neutral, PublicKeyToken=bd319b19eaf3b43a, processorArchitecture=MSIL">
<HintPath>..\packages\HtmlAgilityPack.1.8.4\lib\Net45\HtmlAgilityPack.dll</HintPath>
</Reference>
Expand All @@ -262,26 +253,14 @@
<Reference Include="PresentationCore" />
<Reference Include="PresentationFramework" />
<Reference Include="PresentationFramework.Aero" />
<Reference Include="ReachFramework" />
<Reference Include="stdole, Version=7.0.3300.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<EmbedInteropTypes>False</EmbedInteropTypes>
</Reference>
<Reference Include="System" />
<Reference Include="System.Configuration" />
<Reference Include="System.Core" />
<Reference Include="System.Data" />
<Reference Include="System.Drawing" />
<Reference Include="System.IO.Compression" />
<Reference Include="System.Net.Http" />
<Reference Include="System.Printing" />
<Reference Include="System.Runtime.Remoting" />
<Reference Include="System.Runtime.Serialization" />
<Reference Include="System.ServiceModel" />
<Reference Include="System.Transactions" />
<Reference Include="System.ValueTuple, Version=4.0.2.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
<HintPath>..\packages\System.ValueTuple.4.4.0\lib\netstandard1.0\System.ValueTuple.dll</HintPath>
</Reference>
<Reference Include="System.Web" />
<Reference Include="System.Windows.Forms" />
<Reference Include="System.Windows.Interactivity, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<HintPath>..\packages\System.Windows.Interactivity.WPF.2.0.20525\lib\net40\System.Windows.Interactivity.dll</HintPath>
Expand Down Expand Up @@ -924,6 +903,9 @@
<Folder Include="Properties\DataSources\" />
</ItemGroup>
<ItemGroup>
<Compile Include="AutoComplete\AutoCompleteFunctionBlock.cs" />
<Compile Include="AutoComplete\AutoCompletePropertyBlock.cs" />
<Compile Include="AutoComplete\AutoCompleteSubBlock.cs" />
<Content Include="EasyHook32.dll">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
Expand All @@ -942,9 +924,6 @@
<Content Include="EasyLoad64.dll">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Compile Include="AutoComplete\AutoCompleteFunctionBlock.cs" />
<Compile Include="AutoComplete\AutoCompletePropertyBlock.cs" />
<Compile Include="AutoComplete\AutoCompleteSubBlock.cs" />
<None Include="NLog.xsd">
<SubType>Designer</SubType>
</None>
Expand Down
4 changes: 1 addition & 3 deletions Rubberduck.Core/UI/Command/RunAllTestsCommand.cs
Expand Up @@ -31,11 +31,9 @@ public RunAllTestsCommand(IVBE vbe, RubberduckParserState state, ITestEngine eng
_presenter = presenter;
}

private static readonly ParserState[] AllowedRunStates = { ParserState.ResolvedDeclarations, ParserState.ResolvingReferences, ParserState.Ready };

protected override bool EvaluateCanExecute(object parameter)
{
return _vbe.IsInDesignMode && AllowedRunStates.Contains(_state.Status);
return _vbe.IsInDesignMode && _engine.AllowedRunStates.Contains(_state.Status);
}

protected override void OnExecute(object parameter)
Expand Down
5 changes: 5 additions & 0 deletions Rubberduck.Core/UI/Inspections/InspectionResultsViewModel.cs
Expand Up @@ -292,6 +292,11 @@ private void HandleStateChanged(object sender, ParserStateEventArgs e)
return;
}

if (_state.Status == ParserState.Ready && e.OldState == ParserState.Busy)
{
return;
}

if (_runInspectionsOnReparse || IsRefreshing)
{
RefreshInspections(e.Token);
Expand Down
2 changes: 2 additions & 0 deletions Rubberduck.Core/UnitTesting/ITestEngine.cs
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using Rubberduck.Parsing.VBA;
using Rubberduck.UI.UnitTesting;
using Rubberduck.VBEditor;

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

public class TestModuleEventArgs : EventArgs
Expand Down
131 changes: 93 additions & 38 deletions Rubberduck.Core/UnitTesting/TestEngine.cs
Expand Up @@ -24,6 +24,13 @@ public class TestEngine : ITestEngine
private readonly IVBETypeLibsAPI _typeLibApi;
private readonly IUiDispatcher _uiDispatcher;

public ParserState[] AllowedRunStates => new[]
{
ParserState.ResolvedDeclarations,
ParserState.ResolvingReferences,
ParserState.Ready
};

private static readonly Logger Logger = LogManager.GetCurrentClassLogger();

private bool _testRequested;
Expand All @@ -38,7 +45,7 @@ public TestEngine(TestExplorerModel model, IVBE vbe, RubberduckParserState state
_fakesFactory = fakesFactory;
_typeLibApi = typeLibApi;
_uiDispatcher = uiDispatcher;

_state.StateChanged += StateChangedHandler;
}

Expand Down Expand Up @@ -88,67 +95,115 @@ public void Run(IEnumerable<TestMethod> tests)
}

private void RunInternal(IEnumerable<TestMethod> tests)
{
if (!AllowedRunStates.Contains(_state.Status))
{
return;
}

_state.OnSuspendParser(this, AllowedRunStates, () => RunWhileSuspended(tests));
}

private void RunWhileSuspended(IEnumerable<TestMethod> tests)
{
var testMethods = tests as IList<TestMethod> ?? tests.ToList();
if (!testMethods.Any())
{
return;
}

var modules = testMethods.GroupBy(test => test.Declaration.QualifiedName.QualifiedModuleName);
foreach (var module in modules)
try
{
var testInitialize = module.Key.FindTestInitializeMethods(_state).ToList();
var testCleanup = module.Key.FindTestCleanupMethods(_state).ToList();
var modules = testMethods.GroupBy(test => test.Declaration.QualifiedName.QualifiedModuleName);
foreach (var module in modules)
{
var testInitialize = module.Key.FindTestInitializeMethods(_state).ToList();
var testCleanup = module.Key.FindTestCleanupMethods(_state).ToList();

var capturedModule = module;
var moduleTestMethods = testMethods
.Where(test => test.Declaration.QualifiedName.QualifiedModuleName.ProjectId == capturedModule.Key.ProjectId
&& test.Declaration.QualifiedName.QualifiedModuleName.ComponentName == capturedModule.Key.ComponentName);
var capturedModule = module;
var moduleTestMethods = testMethods
.Where(test =>
{
var qmn = test.Declaration.QualifiedName.QualifiedModuleName;

var fakes = _fakesFactory.Create();
Run(module.Key.FindModuleInitializeMethods(_state));
foreach (var test in moduleTestMethods)
{
// no need to run setup/teardown for ignored tests
if (test.Declaration.Annotations.Any(a => a.AnnotationType == AnnotationType.IgnoreTest))
return qmn.ProjectId == capturedModule.Key.ProjectId
&& qmn.ComponentName == capturedModule.Key.ComponentName;
});

var fakes = _fakesFactory.Create();
var initializeMethods = module.Key.FindModuleInitializeMethods(_state);
try
{
test.UpdateResult(TestOutcome.Ignored);
OnTestCompleted();
RunInternal(initializeMethods);
}
catch (COMException ex)
{
Logger.Error(ex,
"Unexpected COM exception while initializing tests for module {0}. The module will be skipped.",
module.Key.Name);
foreach (var method in moduleTestMethods)
{
method.UpdateResult(TestOutcome.Unknown, AssertMessages.Assert_ComException);
}
continue;
}
foreach (var test in moduleTestMethods)
{
// no need to run setup/teardown for ignored tests
if (test.Declaration.Annotations.Any(a => a.AnnotationType == AnnotationType.IgnoreTest))
{
test.UpdateResult(TestOutcome.Ignored);
OnTestCompleted();
continue;
}

var stopwatch = new Stopwatch();
stopwatch.Start();

try
{
fakes.StartTest();
RunInternal(testInitialize);
test.Run();
RunInternal(testCleanup);
}
catch (COMException ex)
{
Logger.Error(ex, "Unexpected COM exception while running tests.");
test.UpdateResult(TestOutcome.Inconclusive, AssertMessages.Assert_ComException);
}
finally
{
fakes.StopTest();
}

stopwatch.Stop();
test.Result.SetDuration(stopwatch.ElapsedMilliseconds);

var stopwatch = new Stopwatch();
stopwatch.Start();

OnTestCompleted();
Model.AddExecutedTest(test);
}
var cleanupMethods = module.Key.FindModuleCleanupMethods(_state);
try
{
fakes.StartTest();
Run(testInitialize);
test.Run();
Run(testCleanup);
RunInternal(cleanupMethods);
}
catch (COMException ex)
{
Logger.Error(ex, "Unexpected COM exception while running tests.", test.Declaration?.QualifiedName);
test.UpdateResult(TestOutcome.Inconclusive, AssertMessages.Assert_ComException);
}
finally
{
fakes.StopTest();
Logger.Error(ex,
"Unexpected COM exception while cleaning up tests for module {0}. Aborting any further unit tests",
module.Key.Name);
break;
}

stopwatch.Stop();
test.Result.SetDuration(stopwatch.ElapsedMilliseconds);

OnTestCompleted();
Model.AddExecutedTest(test);
}
Run(module.Key.FindModuleCleanupMethods(_state));
}
catch (Exception ex)
{
Logger.Error(ex, "Unexpected expection while running unit tests; unit tests will be aborted");
}
}

private void Run(IEnumerable<Declaration> members)
private void RunInternal(IEnumerable<Declaration> members)
{
var groupedMembers = members.GroupBy(m => m.ProjectId);
foreach (var group in groupedMembers)
Expand Down
2 changes: 0 additions & 2 deletions Rubberduck.Core/packages.config
Expand Up @@ -2,8 +2,6 @@
<packages>
<package id="Antlr4.Runtime" version="4.6.4" targetFramework="net46" />
<package id="AvalonEdit" version="5.0.4" targetFramework="net46" />
<package id="Castle.Core" version="4.2.1" targetFramework="net46" />
<package id="Castle.Windsor" version="4.1.0" targetFramework="net46" />
<package id="EasyHook" version="2.7.6684" targetFramework="net46" />
<package id="HtmlAgilityPack" version="1.8.4" targetFramework="net46" />
<package id="NLog" version="4.5.4" targetFramework="net46" />
Expand Down
13 changes: 0 additions & 13 deletions Rubberduck.Main/Rubberduck.Main.csproj
Expand Up @@ -316,30 +316,17 @@
<Reference Include="extensibility, Version=7.0.3300.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<EmbedInteropTypes>True</EmbedInteropTypes>
</Reference>
<Reference Include="Infralution.Localization.Wpf">
<HintPath>..\libs\Infralution.Localization.Wpf.dll</HintPath>
</Reference>
<Reference Include="Microsoft.CSharp" />
<Reference Include="NLog, Version=4.0.0.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c, processorArchitecture=MSIL">
<HintPath>..\packages\NLog.4.5.4\lib\net45\NLog.dll</HintPath>
</Reference>
<Reference Include="PresentationCore" />
<Reference Include="PresentationFramework" />
<Reference Include="stdole, Version=7.0.3300.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<EmbedInteropTypes>False</EmbedInteropTypes>
</Reference>
<Reference Include="System" />
<Reference Include="System.Configuration" />
<Reference Include="System.Data" />
<Reference Include="System.Drawing" />
<Reference Include="System.IO.Compression" />
<Reference Include="System.Runtime.Remoting" />
<Reference Include="System.Runtime.Serialization" />
<Reference Include="System.ServiceModel" />
<Reference Include="System.Transactions" />
<Reference Include="System.Windows.Forms" />
<Reference Include="System.Xaml" />
<Reference Include="System.Xml" />
<Reference Include="WindowsBase" />
</ItemGroup>
<ItemGroup>
Expand Down
1 change: 1 addition & 0 deletions Rubberduck.Parsing/Rubberduck.Parsing.csproj
Expand Up @@ -354,6 +354,7 @@
<Compile Include="VBA\BuiltInDeclarationLoader.cs" />
<Compile Include="PreProcessing\TokensValue.cs" />
<Compile Include="VBA\SupertypeClearer.cs" />
<Compile Include="VBA\SynchronousParseCoordinator.cs" />
<Compile Include="VBA\SynchronousSupertypeClearer.cs" />
<Compile Include="VBA\SupertypeClearerBase.cs" />
<Compile Include="VBA\ISupertypeClearer.cs" />
Expand Down