Skip to content

Commit b69ddcb

Browse files
authored
Merge pull request #3386 from rkapka/rkapka-master
Introduces a command to generate test stubs for members off the Code Explorer.
2 parents 48b02db + 75e07ba commit b69ddcb

File tree

9 files changed

+333
-6
lines changed

9 files changed

+333
-6
lines changed

RetailCoder.VBE/Navigation/CodeExplorer/CodeExplorerViewModel.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,8 @@ public CodeExplorerViewModel(FolderHelper folderHelper, RubberduckParserState st
6464
OpenDesignerCommand = commands.OfType<OpenDesignerCommand>().SingleOrDefault();
6565

6666
AddTestModuleCommand = commands.OfType<UI.CodeExplorer.Commands.AddTestModuleCommand>().SingleOrDefault();
67+
AddTestModuleWithStubsCommand = commands.OfType<AddTestModuleWithStubsCommand>().SingleOrDefault();
68+
6769
AddStdModuleCommand = commands.OfType<AddStdModuleCommand>().SingleOrDefault();
6870
AddClassModuleCommand = commands.OfType<AddClassModuleCommand>().SingleOrDefault();
6971
AddUserFormCommand = commands.OfType<AddUserFormCommand>().SingleOrDefault();
@@ -491,6 +493,7 @@ private void SwitchNodeState(CodeExplorerItemViewModel node, bool expandedState)
491493
public CommandBase OpenCommand { get; }
492494

493495
public CommandBase AddTestModuleCommand { get; }
496+
public CommandBase AddTestModuleWithStubsCommand { get; }
494497
public CommandBase AddStdModuleCommand { get; }
495498
public CommandBase AddClassModuleCommand { get; }
496499
public CommandBase AddUserFormCommand { get; }

RetailCoder.VBE/Rubberduck.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -400,6 +400,7 @@
400400
<Compile Include="UI\About\AboutDialog.Designer.cs">
401401
<DependentUpon>AboutDialog.cs</DependentUpon>
402402
</Compile>
403+
<Compile Include="UI\CodeExplorer\Commands\AddTestModuleWithStubsCommand.cs" />
403404
<Compile Include="UI\CodeExplorer\Commands\CodeExplorerCommandAttribute.cs" />
404405
<Compile Include="UI\CodeExplorer\Commands\CommitCommand.cs" />
405406
<Compile Include="UI\CodeExplorer\Commands\AddUserFormCommand.cs" />

RetailCoder.VBE/UI/CodeExplorer/CodeExplorerControl.xaml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,13 @@
161161
<Image Height="16" Source="{StaticResource AddTestModuleImage}" />
162162
</MenuItem.Icon>
163163
</MenuItem>
164+
<MenuItem Header="{Resx ResxName=Rubberduck.UI.RubberduckUI, Key=CodeExplorer_AddTestModuleWithStubsText}"
165+
Command="{Binding AddTestModuleWithStubsCommand}"
166+
CommandParameter="{Binding SelectedItem}">
167+
<MenuItem.Icon>
168+
<Image Height="16" Source="{StaticResource AddTestModuleImage}" />
169+
</MenuItem.Icon>
170+
</MenuItem>
164171
<Separator />
165172
<MenuItem Header="{Resx ResxName=Rubberduck.UI.RubberduckUI, Key=CodeExplorer_AddStdModuleText}"
166173
Command="{Binding AddStdModuleCommand}"
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
using NLog;
2+
using Rubberduck.Navigation.CodeExplorer;
3+
using Rubberduck.Parsing.Symbols;
4+
using Rubberduck.UI.Command;
5+
using Rubberduck.VBEditor.SafeComWrappers.Abstract;
6+
7+
namespace Rubberduck.UI.CodeExplorer.Commands
8+
{
9+
[CodeExplorerCommand]
10+
public class AddTestModuleWithStubsCommand : CommandBase
11+
{
12+
private readonly IVBE _vbe;
13+
private readonly Command.AddTestModuleCommand _newUnitTestModuleCommand;
14+
15+
public AddTestModuleWithStubsCommand(IVBE vbe, Command.AddTestModuleCommand newUnitTestModuleCommand) : base(LogManager.GetCurrentClassLogger())
16+
{
17+
_vbe = vbe;
18+
_newUnitTestModuleCommand = newUnitTestModuleCommand;
19+
}
20+
21+
protected override bool EvaluateCanExecute(object parameter) => parameter is CodeExplorerComponentViewModel;
22+
23+
protected override void OnExecute(object parameter)
24+
{
25+
if (parameter != null)
26+
{
27+
_newUnitTestModuleCommand.Execute(GetDeclaration(parameter));
28+
}
29+
else
30+
{
31+
_newUnitTestModuleCommand.Execute(_vbe.ActiveVBProject);
32+
}
33+
}
34+
35+
private Declaration GetDeclaration(object parameter)
36+
{
37+
var node = parameter as CodeExplorerItemViewModel;
38+
while (node != null && !(node is ICodeExplorerDeclarationViewModel))
39+
{
40+
node = node.Parent;
41+
}
42+
43+
return ((ICodeExplorerDeclarationViewModel)node)?.Declaration;
44+
}
45+
}
46+
}

RetailCoder.VBE/UI/Command/AddTestModuleCommand.cs

Lines changed: 53 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
using System.Collections.Generic;
12
using System.Linq;
23
using System.Runtime.InteropServices;
34
using NLog;
@@ -9,6 +10,7 @@
910
using Rubberduck.VBEditor.SafeComWrappers.Abstract;
1011
using Rubberduck.VBEditor.SafeComWrappers.VBA;
1112
using System.Text;
13+
using Rubberduck.Parsing.Symbols;
1214

1315
namespace Rubberduck.UI.Command
1416
{
@@ -138,7 +140,11 @@ protected override bool EvaluateCanExecute(object parameter)
138140

139141
protected override void OnExecute(object parameter)
140142
{
141-
var project = parameter as IVBProject ?? GetProject();
143+
var parameterIsModuleDeclaration = parameter is ProceduralModuleDeclaration || parameter is ClassModuleDeclaration;
144+
145+
var project = parameter as IVBProject ??
146+
(parameterIsModuleDeclaration ? ((Declaration) parameter).Project : GetProject());
147+
142148
if (project.IsWrappingNullReference)
143149
{
144150
return;
@@ -164,14 +170,47 @@ protected override void OnExecute(object parameter)
164170
var options = string.Concat(hasOptionExplicit ? string.Empty : "Option Explicit\r\n",
165171
"Option Private Module\r\n\r\n");
166172

167-
var defaultTestMethod = string.Empty;
168-
if (settings.DefaultTestStubInNewModule)
173+
if (parameterIsModuleDeclaration)
174+
{
175+
var moduleCodeBuilder = new StringBuilder();
176+
var declarationsToStub = GetDeclarationsToStub((Declaration)parameter);
177+
178+
foreach (var declaration in declarationsToStub)
179+
{
180+
var name = string.Empty;
181+
182+
switch (declaration.DeclarationType)
183+
{
184+
case DeclarationType.Procedure:
185+
case DeclarationType.Function:
186+
name = declaration.IdentifierName;
187+
break;
188+
case DeclarationType.PropertyGet:
189+
name = $"Get{declaration.IdentifierName}";
190+
break;
191+
case DeclarationType.PropertyLet:
192+
name = $"Let{declaration.IdentifierName}";
193+
break;
194+
case DeclarationType.PropertySet:
195+
name = $"Set{declaration.IdentifierName}";
196+
break;
197+
}
198+
199+
var stub = AddTestMethodCommand.TestMethodTemplate.Replace(AddTestMethodCommand.NamePlaceholder, $"{name}_Test");
200+
moduleCodeBuilder.AppendLine(stub);
201+
}
202+
203+
module.AddFromString(options + GetTestModule(settings) + moduleCodeBuilder);
204+
}
205+
else
169206
{
170-
defaultTestMethod = AddTestMethodCommand.TestMethodTemplate.Replace(
171-
AddTestMethodCommand.NamePlaceholder, "TestMethod1");
207+
var defaultTestMethod = settings.DefaultTestStubInNewModule
208+
? AddTestMethodCommand.TestMethodTemplate.Replace(AddTestMethodCommand.NamePlaceholder, "TestMethod1")
209+
: string.Empty;
210+
211+
module.AddFromString(options + GetTestModule(settings) + defaultTestMethod);
172212
}
173213

174-
module.AddFromString(options + GetTestModule(settings) + defaultTestMethod);
175214
component.Activate();
176215
_state.OnParseRequested(this, component);
177216
}
@@ -183,5 +222,13 @@ private string GetNextTestModuleName(IVBProject project)
183222

184223
return string.Concat(TestModuleBaseName, index);
185224
}
225+
226+
private IEnumerable<Declaration> GetDeclarationsToStub(Declaration parentDeclaration)
227+
{
228+
return _state.DeclarationFinder.Members(parentDeclaration)
229+
.Where(d => Equals(d.ParentDeclaration, parentDeclaration) && d.Accessibility == Accessibility.Public &&
230+
(d.DeclarationType == DeclarationType.Procedure || d.DeclarationType == DeclarationType.Function || d.DeclarationType.HasFlag(DeclarationType.Property)))
231+
.OrderBy(d => d.Context.Start.TokenIndex);
232+
}
186233
}
187234
}

RetailCoder.VBE/UI/RubberduckUI.Designer.cs

Lines changed: 9 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

RetailCoder.VBE/UI/RubberduckUI.resx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2100,4 +2100,8 @@ the projects in the VBE.</value>
21002100
<data name="EmptyUIRefreshMessage_Title" xml:space="preserve">
21012101
<value>Rubberduck doesn't see anything yet.</value>
21022102
</data>
2103+
<data name="CodeExplorer_AddTestModuleWithStubsText" xml:space="preserve">
2104+
<value>Test module with stubs</value>
2105+
<comment>TC</comment>
2106+
</data>
21032107
</root>

RubberduckTests/CodeExplorer/CodeExplorerTests.cs

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,133 @@ public void AddTestModule()
158158
components.Verify(c => c.Add(ComponentType.StandardModule), Times.Once);
159159
}
160160

161+
[TestCategory("Code Explorer")]
162+
[TestMethod]
163+
public void AddTestModuleWithStubs()
164+
{
165+
var builder = new MockVbeBuilder();
166+
var project = builder.ProjectBuilder("TestProject1", ProjectProtection.Unprotected)
167+
.AddComponent("Module1", ComponentType.StandardModule, "");
168+
169+
var components = project.MockVBComponents;
170+
171+
var vbe = builder.AddProject(project.Build()).Build();
172+
173+
var configLoader = new Mock<ConfigurationLoader>(null, null, null, null, null, null, null);
174+
configLoader.Setup(c => c.LoadConfiguration()).Returns(GetDefaultUnitTestConfig());
175+
176+
var state = new RubberduckParserState(vbe.Object, new DeclarationFinderFactory());
177+
var vbeWrapper = vbe.Object;
178+
var commands = new List<CommandBase>
179+
{
180+
new AddTestModuleWithStubsCommand(vbeWrapper, new Rubberduck.UI.Command.AddTestModuleCommand(vbeWrapper, state, configLoader.Object))
181+
};
182+
183+
var vm = new CodeExplorerViewModel(new FolderHelper(state), state, commands, _generalSettingsProvider.Object, _windowSettingsProvider.Object);
184+
185+
var parser = MockParser.Create(vbe.Object, state);
186+
parser.Parse(new CancellationTokenSource());
187+
if (parser.State.Status >= ParserState.Error) { Assert.Inconclusive("Parser Error"); }
188+
189+
vm.SelectedItem = vm.Projects.First().Items.First().Items.First();
190+
vm.AddTestModuleWithStubsCommand.Execute(vm.SelectedItem);
191+
192+
components.Verify(c => c.Add(ComponentType.StandardModule), Times.Once);
193+
}
194+
195+
[TestCategory("Code Explorer")]
196+
[TestMethod]
197+
public void AddTestModuleWithStubs_DisabledWhenParameterIsProject()
198+
{
199+
var builder = new MockVbeBuilder();
200+
var project = builder.ProjectBuilder("TestProject1", ProjectProtection.Unprotected)
201+
.AddComponent("Module1", ComponentType.StandardModule, "");
202+
203+
var vbe = builder.AddProject(project.Build()).Build();
204+
205+
var configLoader = new Mock<ConfigurationLoader>(null, null, null, null, null, null, null);
206+
configLoader.Setup(c => c.LoadConfiguration()).Returns(GetDefaultUnitTestConfig());
207+
208+
var state = new RubberduckParserState(vbe.Object, new DeclarationFinderFactory());
209+
var vbeWrapper = vbe.Object;
210+
var commands = new List<CommandBase>
211+
{
212+
new AddTestModuleWithStubsCommand(vbeWrapper, new Rubberduck.UI.Command.AddTestModuleCommand(vbeWrapper, state, configLoader.Object))
213+
};
214+
215+
var vm = new CodeExplorerViewModel(new FolderHelper(state), state, commands, _generalSettingsProvider.Object, _windowSettingsProvider.Object);
216+
217+
var parser = MockParser.Create(vbe.Object, state);
218+
parser.Parse(new CancellationTokenSource());
219+
if (parser.State.Status >= ParserState.Error) { Assert.Inconclusive("Parser Error"); }
220+
221+
vm.SelectedItem = vm.Projects.First();
222+
223+
Assert.IsFalse(vm.AddTestModuleWithStubsCommand.CanExecute(vm.SelectedItem));
224+
}
225+
226+
[TestCategory("Code Explorer")]
227+
[TestMethod]
228+
public void AddTestModuleWithStubs_DisabledWhenParameterIsFolder()
229+
{
230+
var builder = new MockVbeBuilder();
231+
var project = builder.ProjectBuilder("TestProject1", ProjectProtection.Unprotected)
232+
.AddComponent("Module1", ComponentType.StandardModule, "");
233+
234+
var vbe = builder.AddProject(project.Build()).Build();
235+
236+
var configLoader = new Mock<ConfigurationLoader>(null, null, null, null, null, null, null);
237+
configLoader.Setup(c => c.LoadConfiguration()).Returns(GetDefaultUnitTestConfig());
238+
239+
var state = new RubberduckParserState(vbe.Object, new DeclarationFinderFactory());
240+
var vbeWrapper = vbe.Object;
241+
var commands = new List<CommandBase>
242+
{
243+
new AddTestModuleWithStubsCommand(vbeWrapper, new Rubberduck.UI.Command.AddTestModuleCommand(vbeWrapper, state, configLoader.Object))
244+
};
245+
246+
var vm = new CodeExplorerViewModel(new FolderHelper(state), state, commands, _generalSettingsProvider.Object, _windowSettingsProvider.Object);
247+
248+
var parser = MockParser.Create(vbe.Object, state);
249+
parser.Parse(new CancellationTokenSource());
250+
if (parser.State.Status >= ParserState.Error) { Assert.Inconclusive("Parser Error"); }
251+
252+
vm.SelectedItem = vm.Projects.First().Items.First();
253+
254+
Assert.IsFalse(vm.AddTestModuleWithStubsCommand.CanExecute(vm.SelectedItem));
255+
}
256+
257+
[TestCategory("Code Explorer")]
258+
[TestMethod]
259+
public void AddTestModuleWithStubs_DisabledWhenParameterIsModuleMember()
260+
{
261+
var builder = new MockVbeBuilder();
262+
var project = builder.ProjectBuilder("TestProject1", ProjectProtection.Unprotected)
263+
.AddComponent("Module1", ComponentType.StandardModule, "Public Sub S()\r\nEnd Sub");
264+
265+
var vbe = builder.AddProject(project.Build()).Build();
266+
267+
var configLoader = new Mock<ConfigurationLoader>(null, null, null, null, null, null, null);
268+
configLoader.Setup(c => c.LoadConfiguration()).Returns(GetDefaultUnitTestConfig());
269+
270+
var state = new RubberduckParserState(vbe.Object, new DeclarationFinderFactory());
271+
var vbeWrapper = vbe.Object;
272+
var commands = new List<CommandBase>
273+
{
274+
new AddTestModuleWithStubsCommand(vbeWrapper, new Rubberduck.UI.Command.AddTestModuleCommand(vbeWrapper, state, configLoader.Object))
275+
};
276+
277+
var vm = new CodeExplorerViewModel(new FolderHelper(state), state, commands, _generalSettingsProvider.Object, _windowSettingsProvider.Object);
278+
279+
var parser = MockParser.Create(vbe.Object, state);
280+
parser.Parse(new CancellationTokenSource());
281+
if (parser.State.Status >= ParserState.Error) { Assert.Inconclusive("Parser Error"); }
282+
283+
vm.SelectedItem = vm.Projects.First().Items.First().Items.First().Items.First();
284+
285+
Assert.IsFalse(vm.AddTestModuleWithStubsCommand.CanExecute(vm.SelectedItem));
286+
}
287+
161288
[TestCategory("Code Explorer")]
162289
[TestMethod]
163290
public void ImportModule()

0 commit comments

Comments
 (0)