Index: Core/Plugins/src/Core.Plugins.ProjectExplorer/ProjectExplorerGuiPlugin.cs =================================================================== diff -u -rc37787efa31f4a9aa6df2ace311109ccd21d96fd -r8598c47dd25fb62ccaab803cc9fc01e7b0be5299 --- Core/Plugins/src/Core.Plugins.ProjectExplorer/ProjectExplorerGuiPlugin.cs (.../ProjectExplorerGuiPlugin.cs) (revision c37787efa31f4a9aa6df2ace311109ccd21d96fd) +++ Core/Plugins/src/Core.Plugins.ProjectExplorer/ProjectExplorerGuiPlugin.cs (.../ProjectExplorerGuiPlugin.cs) (revision 8598c47dd25fb62ccaab803cc9fc01e7b0be5299) @@ -45,6 +45,8 @@ private IProjectOwner projectOwner; private IApplicationSelection applicationSelection; private Ribbon ribbonCommandHandler; + private IEnumerable treeNodeInfos; + private bool active; public override IRibbonCommandHandler RibbonCommandHandler { @@ -71,6 +73,7 @@ applicationSelection = value; documentViewController = value; viewCommands = value.ViewCommands; + treeNodeInfos = value.GetTreeNodeInfos(); } else { @@ -79,6 +82,7 @@ applicationSelection = null; documentViewController = null; viewCommands = null; + treeNodeInfos = null; } } } @@ -131,10 +135,16 @@ /// Thrown when is null. public override void Activate() { + if (active) + { + var message = string.Format(ProjectExplorerResources.ProjectExplorerGuiPlugin_Cannot_activate_0_twice, ProjectExplorerResources.General_ProjectExplorer); + throw new PluginActivationException(message); + } + base.Activate(); try { - projectExplorerViewController = new ProjectExplorerViewController(documentViewController, viewCommands, applicationSelection, toolViewController, Gui.GetTreeNodeInfos()); + projectExplorerViewController = new ProjectExplorerViewController(documentViewController, viewCommands, applicationSelection, toolViewController, treeNodeInfos); } catch (ArgumentNullException e) { @@ -147,14 +157,16 @@ ToggleExplorerCommand = new ToggleProjectExplorerCommand(projectExplorerViewController) }; - projectExplorerViewController.OnOpenView += (s,e) => UpdateProject(); + projectExplorerViewController.OnOpenView += (s, e) => UpdateProject(); projectExplorerViewController.OpenView(); projectOwner.ProjectOpened += ApplicationProjectOpened; + active = true; } public override void Dispose() { + Deactivate(); if (projectExplorerViewController != null) { projectExplorerViewController.Dispose(); @@ -163,8 +175,12 @@ public override void Deactivate() { - base.Deactivate(); - projectOwner.ProjectOpened -= ApplicationProjectOpened; + if (active) + { + base.Deactivate(); + projectOwner.ProjectOpened -= ApplicationProjectOpened; + active = false; + } } private void ApplicationProjectOpened(Project project) Index: Core/Plugins/src/Core.Plugins.ProjectExplorer/Properties/Resources.Designer.cs =================================================================== diff -u -rc37787efa31f4a9aa6df2ace311109ccd21d96fd -r8598c47dd25fb62ccaab803cc9fc01e7b0be5299 --- Core/Plugins/src/Core.Plugins.ProjectExplorer/Properties/Resources.Designer.cs (.../Resources.Designer.cs) (revision c37787efa31f4a9aa6df2ace311109ccd21d96fd) +++ Core/Plugins/src/Core.Plugins.ProjectExplorer/Properties/Resources.Designer.cs (.../Resources.Designer.cs) (revision 8598c47dd25fb62ccaab803cc9fc01e7b0be5299) @@ -107,6 +107,15 @@ } /// + /// Looks up a localized string similar to Kan {0} niet twee keer activeren.. + /// + public static string ProjectExplorerGuiPlugin_Cannot_activate_0_twice { + get { + return ResourceManager.GetString("ProjectExplorerGuiPlugin_Cannot_activate_0_twice", resourceCulture); + } + } + + /// /// Looks up a localized resource of type System.Drawing.Bitmap. /// public static System.Drawing.Bitmap ProjectExplorerIcon { Index: Core/Plugins/src/Core.Plugins.ProjectExplorer/Properties/Resources.resx =================================================================== diff -u -rc37787efa31f4a9aa6df2ace311109ccd21d96fd -r8598c47dd25fb62ccaab803cc9fc01e7b0be5299 --- Core/Plugins/src/Core.Plugins.ProjectExplorer/Properties/Resources.resx (.../Resources.resx) (revision c37787efa31f4a9aa6df2ace311109ccd21d96fd) +++ Core/Plugins/src/Core.Plugins.ProjectExplorer/Properties/Resources.resx (.../Resources.resx) (revision 8598c47dd25fb62ccaab803cc9fc01e7b0be5299) @@ -145,4 +145,7 @@ Het starten van de {0} is mislukt. + + Kan {0} niet twee keer activeren. + \ No newline at end of file Index: Core/Plugins/test/Core.Plugins.ProjectExplorer.Test/Core.Plugins.ProjectExplorer.Test.csproj =================================================================== diff -u -rc37787efa31f4a9aa6df2ace311109ccd21d96fd -r8598c47dd25fb62ccaab803cc9fc01e7b0be5299 --- Core/Plugins/test/Core.Plugins.ProjectExplorer.Test/Core.Plugins.ProjectExplorer.Test.csproj (.../Core.Plugins.ProjectExplorer.Test.csproj) (revision c37787efa31f4a9aa6df2ace311109ccd21d96fd) +++ Core/Plugins/test/Core.Plugins.ProjectExplorer.Test/Core.Plugins.ProjectExplorer.Test.csproj (.../Core.Plugins.ProjectExplorer.Test.csproj) (revision 8598c47dd25fb62ccaab803cc9fc01e7b0be5299) @@ -96,6 +96,7 @@ + Index: Core/Plugins/test/Core.Plugins.ProjectExplorer.Test/Exceptions/PluginActivationExceptionTest.cs =================================================================== diff -u --- Core/Plugins/test/Core.Plugins.ProjectExplorer.Test/Exceptions/PluginActivationExceptionTest.cs (revision 0) +++ Core/Plugins/test/Core.Plugins.ProjectExplorer.Test/Exceptions/PluginActivationExceptionTest.cs (revision 8598c47dd25fb62ccaab803cc9fc01e7b0be5299) @@ -0,0 +1,54 @@ +using System; +using Core.Plugins.ProjectExplorer.Exceptions; +using NUnit.Framework; + +namespace Core.Plugins.ProjectExplorer.Test.Exceptions +{ + [TestFixture] + public class PluginActivationExceptionTest + { + [Test] + public void DefaultConstructor_InnerExceptionNullAndMessageDefault() + { + // Setup + string expectedMessage = String.Format("Exception of type '{0}' was thrown.", typeof(PluginActivationException).FullName); + + // Call + var exception = new PluginActivationException(); + + // Assert + Assert.IsInstanceOf(exception); + Assert.IsNull(exception.InnerException); + Assert.AreEqual(expectedMessage, exception.Message); + } + + [Test] + public void Constructor_WithCustomMessage_InnerExceptionNullAndMessageSetToCustom() + { + // Setup + const string expectedMessage = "Some exception message"; + + // Call + var exception = new PluginActivationException(expectedMessage); + + // Assert + Assert.IsNull(exception.InnerException); + Assert.AreEqual(expectedMessage, exception.Message); + } + + [Test] + public void Constructor_WithCustomMessageAndInnerException_InnerExceptionSetAndMessageSetToCustom() + { + // Setup + const string expectedMessage = "Some exception message"; + Exception expectedInnerException = new Exception(); + + // Call + var exception = new PluginActivationException(expectedMessage, expectedInnerException); + + // Assert + Assert.AreSame(expectedInnerException, exception.InnerException); + Assert.AreEqual(expectedMessage, exception.Message); + } + } +} \ No newline at end of file Index: Core/Plugins/test/Core.Plugins.ProjectExplorer.Test/ProjectExplorerGuiPluginTest.cs =================================================================== diff -u -rc37787efa31f4a9aa6df2ace311109ccd21d96fd -r8598c47dd25fb62ccaab803cc9fc01e7b0be5299 --- Core/Plugins/test/Core.Plugins.ProjectExplorer.Test/ProjectExplorerGuiPluginTest.cs (.../ProjectExplorerGuiPluginTest.cs) (revision c37787efa31f4a9aa6df2ace311109ccd21d96fd) +++ Core/Plugins/test/Core.Plugins.ProjectExplorer.Test/ProjectExplorerGuiPluginTest.cs (.../ProjectExplorerGuiPluginTest.cs) (revision 8598c47dd25fb62ccaab803cc9fc01e7b0be5299) @@ -2,11 +2,11 @@ using System.Collections.Generic; using System.Linq; using Core.Common.Base.Data; -using Core.Common.Base.Plugin; using Core.Common.Controls.TreeView; using Core.Common.Gui; using Core.Common.Gui.Commands; using Core.Common.Gui.Plugin; +using Core.Plugins.ProjectExplorer.Exceptions; using NUnit.Framework; using Rhino.Mocks; @@ -16,6 +16,19 @@ public class ProjectExplorerGuiPluginTest { [Test] + public void DefaultConstructor_CreatesNewInstance() + { + // Call + using (var plugin = new ProjectExplorerGuiPlugin()) + { + // Assert + Assert.IsInstanceOf(plugin); + Assert.IsNull(plugin.Gui); + Assert.IsNull(plugin.RibbonCommandHandler); + } + } + + [Test] [RequiresSTA] public void RegisteringTreeNodeAddsToTreeView() { @@ -31,6 +44,7 @@ gui.Expect(g => g.CloseToolView(Arg.Matches(r => true))); gui.Expect(g => g.ProjectOpened += Arg>.Is.Anything); + gui.Expect(g => g.ProjectOpened -= Arg>.Is.Anything); gui.Replay(); @@ -55,31 +69,189 @@ } [Test] + public void Activate_WithoutGui_ThrowsPluginActivationException() + { + // Setup + using (var plugin = new ProjectExplorerGuiPlugin()) + { + // Call + TestDelegate test = () => plugin.Activate(); + + // Assert + var message = Assert.Throws(test).Message; + var expected = string.Format(Properties.Resources.ProjectExplorerGuiPlugin_Activation_of_0_failed, Properties.Resources.General_ProjectExplorer); + Assert.AreEqual(expected, message); + } + } + + [Test] [RequiresSTA] - public void GetTreeNodeInfos_ReturnsSupportedTreeNodeInfos() + public void Activate_WithGui_SubscribesToProjectEvents() { - // setup + // Setup var mocks = new MockRepository(); - var applicationCore = new ApplicationCore(); var guiStub = mocks.Stub(); guiStub.Stub(g => g.ApplicationCommands).Return(mocks.Stub()); guiStub.Stub(g => g.ProjectCommands).Return(mocks.Stub()); guiStub.Stub(g => g.ViewCommands).Return(mocks.Stub()); + guiStub.Stub(g => g.GetTreeNodeInfos()).Return(Enumerable.Empty()); - Expect.Call(guiStub.ApplicationCore).Return(applicationCore).Repeat.Any(); + using (mocks.Ordered()) + { + guiStub.Expect(g => g.IsToolWindowOpen()).Return(true); + guiStub.Expect(g => g.ProjectOpened += null).IgnoreArguments(); + guiStub.Expect(g => g.ProjectOpened -= null).IgnoreArguments(); + guiStub.Expect(g => g.IsToolWindowOpen()).Return(false); + } mocks.ReplayAll(); + using (var plugin = new ProjectExplorerGuiPlugin + { + Gui = guiStub + }) + { + // Call + plugin.Activate(); + } + + // Assert + mocks.VerifyAll(); + } + + [Test] + [RequiresSTA] + public void Activate_AlreadyActivated_ThrowsPluginActivationException() + { + // Setup + var mocks = new MockRepository(); + + var guiStub = mocks.Stub(); + guiStub.Stub(g => g.ApplicationCommands).Return(mocks.Stub()); + guiStub.Stub(g => g.ProjectCommands).Return(mocks.Stub()); + guiStub.Stub(g => g.ViewCommands).Return(mocks.Stub()); + guiStub.Stub(g => g.GetTreeNodeInfos()).Return(Enumerable.Empty()); + + using (mocks.Ordered()) + { + guiStub.Expect(g => g.IsToolWindowOpen()).Return(true); + guiStub.Expect(g => g.ProjectOpened += null).IgnoreArguments(); + guiStub.Expect(g => g.ProjectOpened -= null).IgnoreArguments(); + guiStub.Expect(g => g.IsToolWindowOpen()).Return(false); + } + + mocks.ReplayAll(); + + using (var plugin = new ProjectExplorerGuiPlugin + { + Gui = guiStub + }) + { + plugin.Activate(); + + // Call + TestDelegate test = () => plugin.Activate(); + + // Assert + var message = Assert.Throws(test).Message; + var expected = string.Format(Properties.Resources.ProjectExplorerGuiPlugin_Cannot_activate_0_twice, Properties.Resources.General_ProjectExplorer); + Assert.AreEqual(expected, message); + } + mocks.VerifyAll(); + } + + [Test] + [RequiresSTA] + public void Deactivate_WhenActive_DesubscribesToProjectEvents() + { + // Setup + var mocks = new MockRepository(); + + var guiStub = mocks.Stub(); + guiStub.Stub(g => g.ApplicationCommands).Return(mocks.Stub()); + guiStub.Stub(g => g.ProjectCommands).Return(mocks.Stub()); + guiStub.Stub(g => g.ViewCommands).Return(mocks.Stub()); + guiStub.Stub(g => g.GetTreeNodeInfos()).Return(Enumerable.Empty()); + + using (mocks.Ordered()) + { + guiStub.Expect(g => g.IsToolWindowOpen()).Return(true); + guiStub.Expect(g => g.ProjectOpened += null).IgnoreArguments(); + guiStub.Expect(g => g.ProjectOpened -= null).IgnoreArguments(); + guiStub.Expect(g => g.IsToolWindowOpen()).Return(false); + } + + mocks.ReplayAll(); + + using (var plugin = new ProjectExplorerGuiPlugin + { + Gui = guiStub + }) + { + plugin.Activate(); + + // Call + plugin.Deactivate(); + } + + // Assert + mocks.VerifyAll(); + } + + [Test] + [RequiresSTA] + public void Deactivate_AlwaysWhenNotActive_DoesNotThrow() + { + // Setup + using (var plugin = new ProjectExplorerGuiPlugin()) + { + // Call + TestDelegate test = () => plugin.Deactivate(); + + // Assert + Assert.DoesNotThrow(test); + } + } + + [Test] + [RequiresSTA] + public void Dispose_AlwaysWhenNotActive_DoesNotThrow() + { + // Setup + var plugin = new ProjectExplorerGuiPlugin(); + + // Call + TestDelegate test = () => plugin.Dispose(); + + // Assert + Assert.DoesNotThrow(test); + } + + [Test] + [RequiresSTA] + public void GetTreeNodeInfos_ReturnsSupportedTreeNodeInfos() + { + // Setup + var mocks = new MockRepository(); + + var guiStub = mocks.Stub(); + guiStub.Stub(g => g.ApplicationCommands).Return(mocks.Stub()); + guiStub.Stub(g => g.ProjectCommands).Return(mocks.Stub()); + guiStub.Stub(g => g.ViewCommands).Return(mocks.Stub()); + guiStub.Stub(g => g.GetTreeNodeInfos()).Return(Enumerable.Empty()); + + mocks.ReplayAll(); + using (var guiPlugin = new ProjectExplorerGuiPlugin { Gui = guiStub }) { - // call + // Call TreeNodeInfo[] treeNodeInfos = guiPlugin.GetTreeNodeInfos().ToArray(); - // assert + // Assert Assert.AreEqual(1, treeNodeInfos.Length); Assert.IsTrue(treeNodeInfos.Any(tni => tni.TagType == typeof(Project))); } @@ -96,27 +268,95 @@ project.Items.Add(2); project.Items.Add(3); - var plugin = new ProjectExplorerGuiPlugin(); + using (var plugin = new ProjectExplorerGuiPlugin()) + { + // Call + var childrenWithViewDefinitions = plugin.GetChildDataWithViewDefinitions(project); - // Call - var childrenWithViewDefinitions = plugin.GetChildDataWithViewDefinitions(project); - - // Assert - var expectedResult = project.Items; - CollectionAssert.AreEquivalent(expectedResult, childrenWithViewDefinitions); + // Assert + var expectedResult = project.Items; + CollectionAssert.AreEquivalent(expectedResult, childrenWithViewDefinitions); + } } [Test] public void GetChildDataWithViewDefinitions_UnsupportedDataType_ReturnEmpty() { // Setup - var plugin = new ProjectExplorerGuiPlugin(); + using (var plugin = new ProjectExplorerGuiPlugin()) + { + // Call + var childrenWithViewDefinitions = plugin.GetChildDataWithViewDefinitions(2); - // Call - var childrenWithViewDefinitions = plugin.GetChildDataWithViewDefinitions(2); + // Assert + CollectionAssert.IsEmpty(childrenWithViewDefinitions); + } + } - // Assert - CollectionAssert.IsEmpty(childrenWithViewDefinitions); + [Test] + [RequiresSTA] + public void ProjectOpened_PluginActivated_UpdateProjectExplorer() + { + // Setup + var mocks = new MockRepository(); + var guiStub = mocks.Stub(); + guiStub.Stub(g => g.ApplicationCommands).Return(mocks.Stub()); + guiStub.Stub(g => g.ProjectCommands).Return(mocks.Stub()); + guiStub.Stub(g => g.ViewCommands).Return(mocks.Stub()); + guiStub.Stub(g => g.GetTreeNodeInfos()).Return(new[] + { + new TreeNodeInfo + { + TagType = typeof(Project) + } + }); + + ProjectExplorer view = null; + + using (mocks.Ordered()) + { + // Activate + guiStub.Expect(tvc => tvc.IsToolWindowOpen()).Return(false); + guiStub.Expect(a => a.SelectionChanged += null).IgnoreArguments(); + guiStub.Expect(tvc => tvc.OpenToolView(Arg.Matches(v => true))).WhenCalled(invocation => { + view = invocation.Arguments[0] as ProjectExplorer; + }); + guiStub.Expect(tvc => tvc.IsToolWindowOpen()).Return(true); + guiStub.Expect(dvc => dvc.UpdateToolTips()); + guiStub.Expect(g => g.ProjectOpened += null).IgnoreArguments(); + + // UpdateProject + guiStub.Expect(tvc => tvc.IsToolWindowOpen()).Return(true); + guiStub.Expect(dvc => dvc.UpdateToolTips()); + + // Dispose + guiStub.Expect(g => g.ProjectOpened -= null).IgnoreArguments(); + guiStub.Expect(tvc => tvc.IsToolWindowOpen()).Return(true); + guiStub.Expect(tvc => tvc.CloseToolView(Arg.Matches(v => true))); + } + + mocks.ReplayAll(); + + using (var plugin = new ProjectExplorerGuiPlugin + { + Gui = guiStub + }) + { + var initialProject = new Project(); + guiStub.Project = initialProject; + plugin.Activate(); + + // Precondition + Assert.AreSame(view.Data, initialProject); + + // Call + var newProject = new Project(); + guiStub.Project = newProject; + guiStub.Raise(s => s.ProjectOpened += null, newProject); + + // Assert + Assert.AreSame(view.Data, newProject); + } } } } \ No newline at end of file Index: Core/Plugins/test/Core.Plugins.ProjectExplorer.Test/TreeNodeInfos/ProjectTreeNodeInfoTest.cs =================================================================== diff -u -rb3db013105d10992eeefea39f5ecbdacdd57b4d4 -r8598c47dd25fb62ccaab803cc9fc01e7b0be5299 --- Core/Plugins/test/Core.Plugins.ProjectExplorer.Test/TreeNodeInfos/ProjectTreeNodeInfoTest.cs (.../ProjectTreeNodeInfoTest.cs) (revision b3db013105d10992eeefea39f5ecbdacdd57b4d4) +++ Core/Plugins/test/Core.Plugins.ProjectExplorer.Test/TreeNodeInfos/ProjectTreeNodeInfoTest.cs (.../ProjectTreeNodeInfoTest.cs) (revision 8598c47dd25fb62ccaab803cc9fc01e7b0be5299) @@ -18,7 +18,7 @@ { private MockRepository mocks; private ProjectExplorerGuiPlugin plugin; - private Common.Controls.TreeView.TreeNodeInfo info; + private TreeNodeInfo info; [SetUp] public void SetUp() @@ -118,6 +118,7 @@ gui.Expect(g => g.Get(treeNode, info, treeViewControlMock)).Return(menuBuilderMock); gui.Expect(g => g.ViewCommands).Return(viewCommandsMock); + gui.Expect(g => g.GetTreeNodeInfos()).Return(Enumerable.Empty()); menuBuilderMock.Expect(mb => mb.AddCustomItem(null)).IgnoreArguments().Return(menuBuilderMock); menuBuilderMock.Expect(mb => mb.AddSeparator()).Return(menuBuilderMock); @@ -157,6 +158,7 @@ guiMock.Stub(g => g.Get(treeNode, info, treeViewControlMock)).Return(menuBuilder); guiMock.Stub(g => g.ProjectCommands).Return(projectCommandsMock); guiMock.Stub(g => g.ViewCommands).Return(viewCommandsMock); + guiMock.Stub(g => g.GetTreeNodeInfos()).Return(Enumerable.Empty()); projectCommandsMock.Expect(g => g.AddNewItem(project)); mocks.ReplayAll();