Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/mono/monodevelop.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/main/src
diff options
context:
space:
mode:
authorMatt Ward <ward.matt@gmail.com>2017-07-24 12:21:09 +0300
committerGitHub <noreply@github.com>2017-07-24 12:21:09 +0300
commita05fa40edec864b2c6c064cdf7cb21538199a4ab (patch)
tree5de66a42e8a31b0ec5499c2e922d8030adac6db6 /main/src
parent927ea1c108062763a242c7b492034fb98237c778 (diff)
parentd5b73a82d59474835b7808e1b082ea7c84d6b3a2 (diff)
Merge pull request #2786 from mono/nuget-transitive-assembly-references-not-available
[NuGet] Transitive assembly references not available until solution reloaded
Diffstat (limited to 'main/src')
-rw-r--r--main/src/addins/MonoDevelop.DotNetCore/MonoDevelop.DotNetCore/DotNetCoreProjectReloadMonitor.cs2
-rw-r--r--main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.Commands/RestorePackagesInProjectHandler.cs17
-rw-r--r--main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.Tests/MonoDevelop.PackageManagement.Tests.Helpers/FakeMonoDevelopBuildIntegratedRestorer.cs58
-rw-r--r--main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.Tests/MonoDevelop.PackageManagement.Tests.Helpers/FakeNuGetProject.cs2
-rw-r--r--main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.Tests/MonoDevelop.PackageManagement.Tests.Helpers/FakeSolutionManager.cs11
-rw-r--r--main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.Tests/MonoDevelop.PackageManagement.Tests.Helpers/TestableDotNetCoreNuGetProject.cs10
-rw-r--r--main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.Tests/MonoDevelop.PackageManagement.Tests.csproj2
-rw-r--r--main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.Tests/MonoDevelop.PackageManagement.Tests/DotNetCoreNuGetProjectTests.cs251
-rw-r--r--main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.Tests/MonoDevelop.PackageManagement.Tests/DotNetProjectExtensionsTests.cs224
-rw-r--r--main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.Tests/MonoDevelop.PackageManagement.Tests/RestoreNuGetPackagesInNuGetIntegratedProjectTests.cs151
-rw-r--r--main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.csproj1
-rw-r--r--main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement/DotNetCoreNuGetProject.cs21
-rw-r--r--main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement/DotNetProjectExtensions.cs92
-rw-r--r--main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement/IBuildIntegratedNuGetProject.cs8
-rw-r--r--main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement/IMonoDevelopBuildIntegratedRestorer.cs46
-rw-r--r--main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement/MonoDevelopBuildIntegratedRestorer.cs30
-rw-r--r--main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement/PackageReferenceNuGetProject.cs2
-rw-r--r--main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement/ProjectJsonBuildIntegratedProjectSystem.cs2
-rw-r--r--main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement/RestoreNuGetPackagesInNuGetIntegratedProject.cs67
19 files changed, 961 insertions, 36 deletions
diff --git a/main/src/addins/MonoDevelop.DotNetCore/MonoDevelop.DotNetCore/DotNetCoreProjectReloadMonitor.cs b/main/src/addins/MonoDevelop.DotNetCore/MonoDevelop.DotNetCore/DotNetCoreProjectReloadMonitor.cs
index 1e875529c0..637d0a6f84 100644
--- a/main/src/addins/MonoDevelop.DotNetCore/MonoDevelop.DotNetCore/DotNetCoreProjectReloadMonitor.cs
+++ b/main/src/addins/MonoDevelop.DotNetCore/MonoDevelop.DotNetCore/DotNetCoreProjectReloadMonitor.cs
@@ -76,7 +76,7 @@ namespace MonoDevelop.DotNetCore
void OnDotNetCoreProjectReloaded (ProjectReloadedEventArgs e)
{
DotNetCoreProjectBuilderMaintainer.OnProjectReload (e);
- RestorePackagesInProjectHandler.Run (e.NewProject.DotNetProject);
+ RestorePackagesInProjectHandler.Run (e.NewProject.DotNetProject, restoreTransitiveProjectReferences: true);
}
async void FileChanged (object sender, FileEventArgs e)
diff --git a/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.Commands/RestorePackagesInProjectHandler.cs b/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.Commands/RestorePackagesInProjectHandler.cs
index aca0efb247..fbeb69dffa 100644
--- a/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.Commands/RestorePackagesInProjectHandler.cs
+++ b/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.Commands/RestorePackagesInProjectHandler.cs
@@ -43,25 +43,34 @@ namespace MonoDevelop.PackageManagement.Commands
info.Enabled = SelectedDotNetProjectHasPackages ();
}
- public static void Run (DotNetProject project)
+ public static void Run (DotNetProject project)
+ {
+ Run (project, false);
+ }
+
+ public static void Run (DotNetProject project, bool restoreTransitiveProjectReferences)
{
try {
ProgressMonitorStatusMessage message = ProgressMonitorStatusMessageFactory.CreateRestoringPackagesInProjectMessage ();
- IPackageAction action = CreateRestorePackagesAction (project);
+ IPackageAction action = CreateRestorePackagesAction (project, restoreTransitiveProjectReferences);
PackageManagementServices.BackgroundPackageActionRunner.Run (message, action);
} catch (Exception ex) {
ShowStatusBarError (ex);
}
}
- static IPackageAction CreateRestorePackagesAction (DotNetProject project)
+ static IPackageAction CreateRestorePackagesAction (DotNetProject project, bool restoreTransitiveProjectReferences)
{
var solutionManager = PackageManagementServices.Workspace.GetSolutionManager (project.ParentSolution);
var nugetProject = solutionManager.GetNuGetProject (new DotNetProjectProxy (project));
var buildIntegratedProject = nugetProject as BuildIntegratedNuGetProject;
if (buildIntegratedProject != null) {
- return new RestoreNuGetPackagesInNuGetIntegratedProject (project, buildIntegratedProject, solutionManager);
+ return new RestoreNuGetPackagesInNuGetIntegratedProject (
+ project,
+ buildIntegratedProject,
+ solutionManager,
+ restoreTransitiveProjectReferences);
}
var nugetAwareProject = project as INuGetAwareProject;
diff --git a/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.Tests/MonoDevelop.PackageManagement.Tests.Helpers/FakeMonoDevelopBuildIntegratedRestorer.cs b/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.Tests/MonoDevelop.PackageManagement.Tests.Helpers/FakeMonoDevelopBuildIntegratedRestorer.cs
new file mode 100644
index 0000000000..c60ab67a26
--- /dev/null
+++ b/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.Tests/MonoDevelop.PackageManagement.Tests.Helpers/FakeMonoDevelopBuildIntegratedRestorer.cs
@@ -0,0 +1,58 @@
+//
+// FakeMonoDevelopBuildIntegratedRestorer.cs
+//
+// Author:
+// Matt Ward <matt.ward@xamarin.com>
+//
+// Copyright (c) 2017 Xamarin Inc. (http://xamarin.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+using System.Collections.Generic;
+using System.Threading;
+using System.Threading.Tasks;
+using NuGet.ProjectManagement.Projects;
+
+namespace MonoDevelop.PackageManagement.Tests.Helpers
+{
+ class FakeMonoDevelopBuildIntegratedRestorer : IMonoDevelopBuildIntegratedRestorer
+ {
+ public bool LockFileChanged { get; set; }
+
+ public BuildIntegratedNuGetProject ProjectRestored;
+
+ public Task RestorePackages (
+ BuildIntegratedNuGetProject project,
+ CancellationToken cancellationToken)
+ {
+ ProjectRestored = project;
+ return Task.FromResult (0);
+ }
+
+ public List<BuildIntegratedNuGetProject> ProjectsRestored = new List<BuildIntegratedNuGetProject> ();
+
+ public Task RestorePackages (
+ IEnumerable<BuildIntegratedNuGetProject> projects,
+ CancellationToken cancellationToken)
+ {
+ ProjectsRestored.AddRange (projects);
+ return Task.FromResult (0);
+ }
+ }
+}
diff --git a/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.Tests/MonoDevelop.PackageManagement.Tests.Helpers/FakeNuGetProject.cs b/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.Tests/MonoDevelop.PackageManagement.Tests.Helpers/FakeNuGetProject.cs
index b2a0de2557..4b9eaae8f3 100644
--- a/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.Tests/MonoDevelop.PackageManagement.Tests.Helpers/FakeNuGetProject.cs
+++ b/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.Tests/MonoDevelop.PackageManagement.Tests.Helpers/FakeNuGetProject.cs
@@ -107,7 +107,7 @@ namespace MonoDevelop.PackageManagement.Tests.Helpers
return Task.FromResult (0);
}
- public void NotifyProjectReferencesChanged ()
+ public void NotifyProjectReferencesChanged (bool includeTransitiveProjectReferences)
{
}
}
diff --git a/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.Tests/MonoDevelop.PackageManagement.Tests.Helpers/FakeSolutionManager.cs b/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.Tests/MonoDevelop.PackageManagement.Tests.Helpers/FakeSolutionManager.cs
index 0cf7e37214..b88ace6581 100644
--- a/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.Tests/MonoDevelop.PackageManagement.Tests.Helpers/FakeSolutionManager.cs
+++ b/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.Tests/MonoDevelop.PackageManagement.Tests.Helpers/FakeSolutionManager.cs
@@ -27,6 +27,7 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
+using MonoDevelop.Projects;
using NuGet.Configuration;
using NuGet.PackageManagement;
using NuGet.ProjectManagement;
@@ -113,14 +114,20 @@ namespace MonoDevelop.PackageManagement.Tests.Helpers
throw new NotImplementedException ();
}
- public Dictionary<IDotNetProject, FakeNuGetProject> NuGetProjects = new Dictionary<IDotNetProject, FakeNuGetProject> ();
+ public Dictionary<IDotNetProject, NuGetProject> NuGetProjects = new Dictionary<IDotNetProject, NuGetProject> ();
+ public Dictionary<DotNetProject, NuGetProject> NuGetProjectsUsingDotNetProjects = new Dictionary<DotNetProject, NuGetProject> ();
public NuGetProject GetNuGetProject (IDotNetProject project)
{
- FakeNuGetProject nugetProject = null;
+ NuGetProject nugetProject = null;
if (NuGetProjects.TryGetValue (project, out nugetProject))
return nugetProject;
+ if (project.DotNetProject != null) {
+ if (NuGetProjectsUsingDotNetProjects.TryGetValue (project.DotNetProject, out nugetProject))
+ return nugetProject;
+ }
+
return new FakeNuGetProject (project);
}
diff --git a/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.Tests/MonoDevelop.PackageManagement.Tests.Helpers/TestableDotNetCoreNuGetProject.cs b/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.Tests/MonoDevelop.PackageManagement.Tests.Helpers/TestableDotNetCoreNuGetProject.cs
index 6380f38036..3ccbecf181 100644
--- a/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.Tests/MonoDevelop.PackageManagement.Tests.Helpers/TestableDotNetCoreNuGetProject.cs
+++ b/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.Tests/MonoDevelop.PackageManagement.Tests.Helpers/TestableDotNetCoreNuGetProject.cs
@@ -34,6 +34,7 @@ namespace MonoDevelop.PackageManagement.Tests.Helpers
public TestableDotNetCoreNuGetProject (DotNetProject project)
: base (project, new [] { "netcoreapp1.0" })
{
+ BuildIntegratedRestorer = new FakeMonoDevelopBuildIntegratedRestorer ();
}
public bool IsSaved { get; set; }
@@ -43,5 +44,14 @@ namespace MonoDevelop.PackageManagement.Tests.Helpers
IsSaved = true;
return Task.FromResult (0);
}
+
+ public FakeMonoDevelopBuildIntegratedRestorer BuildIntegratedRestorer;
+ public Solution SolutionUsedToCreateBuildIntegratedRestorer;
+
+ protected override IMonoDevelopBuildIntegratedRestorer CreateBuildIntegratedRestorer (Solution solution)
+ {
+ SolutionUsedToCreateBuildIntegratedRestorer = solution;
+ return BuildIntegratedRestorer;
+ }
}
}
diff --git a/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.Tests/MonoDevelop.PackageManagement.Tests.csproj b/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.Tests/MonoDevelop.PackageManagement.Tests.csproj
index 6d12c04bbb..f26b667dc4 100644
--- a/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.Tests/MonoDevelop.PackageManagement.Tests.csproj
+++ b/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.Tests/MonoDevelop.PackageManagement.Tests.csproj
@@ -210,6 +210,8 @@
<Compile Include="MonoDevelop.PackageManagement.Tests.Helpers\TestableUninstallNuGetPackagesAction.cs" />
<Compile Include="MonoDevelop.PackageManagement.Tests\PackageReferenceNuGetProjectTests.cs" />
<Compile Include="MonoDevelop.PackageManagement.Tests.Helpers\TestablePackageReferenceNuGetProject.cs" />
+ <Compile Include="MonoDevelop.PackageManagement.Tests.Helpers\FakeMonoDevelopBuildIntegratedRestorer.cs" />
+ <Compile Include="MonoDevelop.PackageManagement.Tests\RestoreNuGetPackagesInNuGetIntegratedProjectTests.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\core\MonoDevelop.Core\MonoDevelop.Core.csproj">
diff --git a/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.Tests/MonoDevelop.PackageManagement.Tests/DotNetCoreNuGetProjectTests.cs b/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.Tests/MonoDevelop.PackageManagement.Tests/DotNetCoreNuGetProjectTests.cs
index e9148330f5..69135e9943 100644
--- a/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.Tests/MonoDevelop.PackageManagement.Tests/DotNetCoreNuGetProjectTests.cs
+++ b/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.Tests/MonoDevelop.PackageManagement.Tests/DotNetCoreNuGetProjectTests.cs
@@ -30,6 +30,7 @@ using System.Threading.Tasks;
using MonoDevelop.PackageManagement.Tests.Helpers;
using MonoDevelop.Projects;
using NUnit.Framework;
+using NuGet.PackageManagement;
using NuGet.Packaging.Core;
using NuGet.ProjectManagement;
using NuGet.ProjectModel;
@@ -44,14 +45,24 @@ namespace MonoDevelop.PackageManagement.Tests
TestableDotNetCoreNuGetProject project;
FakeNuGetProjectContext context;
DependencyGraphCacheContext dependencyGraphCacheContext;
+ FakeMonoDevelopBuildIntegratedRestorer buildIntegratedRestorer;
void CreateNuGetProject (string projectName = "MyProject", string fileName = @"d:\projects\MyProject\MyProject.csproj")
{
context = new FakeNuGetProjectContext ();
- dotNetProject = new DummyDotNetProject ();
- dotNetProject.Name = projectName;
- dotNetProject.FileName = fileName.ToNativePath ();
+ dotNetProject = CreateDotNetCoreProject (projectName, fileName);
+ var solution = new Solution ();
+ solution.RootFolder.AddItem (dotNetProject);
project = new TestableDotNetCoreNuGetProject (dotNetProject);
+ buildIntegratedRestorer = project.BuildIntegratedRestorer;
+ }
+
+ static DummyDotNetProject CreateDotNetCoreProject (string projectName = "MyProject", string fileName = @"d:\projects\MyProject\MyProject.csproj")
+ {
+ var project = new DummyDotNetProject ();
+ project.Name = projectName;
+ project.FileName = fileName.ToNativePath ();
+ return project;
}
void AddDotNetProjectPackageReference (string packageId, string version)
@@ -86,6 +97,12 @@ namespace MonoDevelop.PackageManagement.Tests
return specs.Single ();
}
+ void OnAfterExecuteActions (string packageId, string version, NuGetProjectActionType actionType)
+ {
+ var action = new FakeNuGetProjectAction (packageId, version, actionType);
+ project.OnAfterExecuteActions (new [] { action });
+ }
+
[Test]
public async Task GetInstalledPackagesAsync_OnePackageReference_ReturnsOnePackageReference ()
{
@@ -255,5 +272,233 @@ namespace MonoDevelop.PackageManagement.Tests
Assert.IsTrue (packageReference.IsFloating ());
Assert.AreEqual ("2.6.0-*", packageReference.AllowedVersions.Float.ToString ());
}
+
+ [Test]
+ public async Task PostProcessAsync_ProjectAssetsFile_NotifyChangeInAssetsFile ()
+ {
+ CreateNuGetProject ();
+ AddDotNetProjectPackageReference ("NUnit", "2.6.0");
+ dotNetProject.BaseIntermediateOutputPath = @"d:\projects\MyProject\obj".ToNativePath ();
+ string fileNameChanged = null;
+ PackageManagementServices.PackageManagementEvents.FileChanged += (sender, e) => {
+ fileNameChanged = e.Single ().FileName;
+ };
+
+ await project.PostProcessAsync (context, CancellationToken.None);
+
+ string expectedFileNameChanged = @"d:\projects\MyProject\obj\project.assets.json".ToNativePath ();
+ Assert.AreEqual (expectedFileNameChanged, fileNameChanged);
+ }
+
+ [Test]
+ public async Task PostProcessAsync_References_NotifyReferencesChangedEventFired ()
+ {
+ CreateNuGetProject ();
+ AddDotNetProjectPackageReference ("NUnit", "2.6.0");
+ string modifiedHint = null;
+ dotNetProject.Modified += (sender, e) => {
+ modifiedHint = e.Single ().Hint;
+ };
+
+ await project.PostProcessAsync (context, CancellationToken.None);
+
+ Assert.AreEqual ("References", modifiedHint);
+ }
+
+ [Test]
+ public async Task PostProcessAsync_RestoreRunLockFileNotChanged_NotifyReferencesChangedEventFired ()
+ {
+ CreateNuGetProject ();
+ AddDotNetProjectPackageReference ("NUnit", "2.6.0");
+ string modifiedHint = null;
+ dotNetProject.Modified += (sender, e) => {
+ modifiedHint = e.Single ().Hint;
+ };
+ OnAfterExecuteActions ("NUnit", "2.6.3", NuGetProjectActionType.Install);
+
+ await project.PostProcessAsync (context, CancellationToken.None);
+
+ Assert.IsNotNull (dotNetProject.ParentSolution);
+ Assert.AreEqual (dotNetProject.ParentSolution, project.SolutionUsedToCreateBuildIntegratedRestorer);
+ Assert.AreEqual (project, buildIntegratedRestorer.ProjectRestored);
+ Assert.AreEqual ("References", modifiedHint);
+ }
+
+ [Test]
+ public async Task PostProcessAsync_RestoreRunLockFileNotChanged_NotifyChangeInAssetsFile ()
+ {
+ CreateNuGetProject ();
+ AddDotNetProjectPackageReference ("NUnit", "2.6.0");
+ dotNetProject.BaseIntermediateOutputPath = @"d:\projects\MyProject\obj".ToNativePath ();
+ string fileNameChanged = null;
+ PackageManagementServices.PackageManagementEvents.FileChanged += (sender, e) => {
+ fileNameChanged = e.Single ().FileName;
+ };
+ OnAfterExecuteActions ("NUnit", "2.6.3", NuGetProjectActionType.Install);
+
+ await project.PostProcessAsync (context, CancellationToken.None);
+
+ string expectedFileNameChanged = @"d:\projects\MyProject\obj\project.assets.json".ToNativePath ();
+ Assert.AreEqual (expectedFileNameChanged, fileNameChanged);
+ Assert.AreEqual (project, buildIntegratedRestorer.ProjectRestored);
+ }
+
+ /// <summary>
+ /// Build restorer would trigger the notification itself.
+ /// </summary>
+ [Test]
+ public async Task PostProcessAsync_RestoreRunLockFileChanged_NotifyReferencesChangedEventNotFired ()
+ {
+ CreateNuGetProject ();
+ AddDotNetProjectPackageReference ("NUnit", "2.6.0");
+ string modifiedHint = null;
+ dotNetProject.Modified += (sender, e) => {
+ modifiedHint = e.Single ().Hint;
+ };
+ buildIntegratedRestorer.LockFileChanged = true;
+ OnAfterExecuteActions ("NUnit", "2.6.3", NuGetProjectActionType.Install);
+
+ await project.PostProcessAsync (context, CancellationToken.None);
+
+ Assert.AreEqual (project, buildIntegratedRestorer.ProjectRestored);
+ Assert.IsNull (modifiedHint);
+ }
+
+ /// <summary>
+ /// Build restorer would trigger the notification itself.
+ /// </summary>
+ [Test]
+ public async Task PostProcessAsync_RestoreRunLockFileChanged_NotifyChangeInAssetsFileIsNotMade ()
+ {
+ CreateNuGetProject ();
+ AddDotNetProjectPackageReference ("NUnit", "2.6.0");
+ dotNetProject.BaseIntermediateOutputPath = @"d:\projects\MyProject\obj".ToNativePath ();
+ string fileNameChanged = null;
+ PackageManagementServices.PackageManagementEvents.FileChanged += (sender, e) => {
+ fileNameChanged = e.Single ().FileName;
+ };
+ buildIntegratedRestorer.LockFileChanged = true;
+ OnAfterExecuteActions ("NUnit", "2.6.3", NuGetProjectActionType.Install);
+
+ await project.PostProcessAsync (context, CancellationToken.None);
+
+ Assert.IsNull (fileNameChanged);
+ Assert.AreEqual (project, buildIntegratedRestorer.ProjectRestored);
+ }
+
+ [Test]
+ public void NotifyProjectReferencesChanged_References_NotifyReferencesChangedEventFired ()
+ {
+ CreateNuGetProject ();
+ string modifiedHint = null;
+ dotNetProject.Modified += (sender, e) => {
+ modifiedHint = e.Single ().Hint;
+ };
+
+ project.NotifyProjectReferencesChanged (false);
+
+ Assert.AreEqual ("References", modifiedHint);
+ }
+
+ /// <summary>
+ /// Assembly references are transitive for .NET Core projects so any .NET Core project
+ /// that references the project having a NuGet package being installed needs to refresh
+ /// references for the other projects not just itself.
+ /// </summary>
+ [Test]
+ public async Task PostProcessAsync_DotNetCoreProjectReferencesThisProject_NotifyReferencesChangedEventFiredForBothProjects ()
+ {
+ CreateNuGetProject ();
+ AddDotNetProjectPackageReference ("NUnit", "2.6.0");
+ var dotNetProjectWithProjectReference = CreateDotNetCoreProject ("MyProject2", @"d:\projects\MyProject2\MyProject2.csproj");
+ dotNetProject.ParentSolution.RootFolder.AddItem (dotNetProjectWithProjectReference);
+ var projectReference = ProjectReference.CreateProjectReference (dotNetProject);
+ dotNetProjectWithProjectReference.References.Add (projectReference);
+ string modifiedHintMainProject = null;
+ dotNetProject.Modified += (sender, e) => {
+ modifiedHintMainProject = e.Single ().Hint;
+ };
+ string modifiedHintProjectWithReference = null;
+ dotNetProjectWithProjectReference.Modified += (sender, e) => {
+ modifiedHintProjectWithReference = e.Single ().Hint;
+ };
+
+ await project.PostProcessAsync (context, CancellationToken.None);
+
+ Assert.AreEqual ("References", modifiedHintMainProject);
+ Assert.AreEqual ("References", modifiedHintProjectWithReference);
+ }
+
+ [Test]
+ public async Task PostProcessAsync_DotNetCoreProjectReferencesThisProjectLockFileNotChanged_NotifyReferencesChangedEventFiredForBothProjects ()
+ {
+ CreateNuGetProject ();
+ AddDotNetProjectPackageReference ("NUnit", "2.6.0");
+ var dotNetProjectWithProjectReference = CreateDotNetCoreProject ("MyProject2", @"d:\projects\MyProject2\MyProject2.csproj");
+ dotNetProject.ParentSolution.RootFolder.AddItem (dotNetProjectWithProjectReference);
+ var projectReference = ProjectReference.CreateProjectReference (dotNetProject);
+ dotNetProjectWithProjectReference.References.Add (projectReference);
+ string modifiedHintMainProject = null;
+ dotNetProject.Modified += (sender, e) => {
+ modifiedHintMainProject = e.Single ().Hint;
+ };
+ string modifiedHintProjectWithReference = null;
+ dotNetProjectWithProjectReference.Modified += (sender, e) => {
+ modifiedHintProjectWithReference = e.Single ().Hint;
+ };
+ OnAfterExecuteActions ("NUnit", "2.6.3", NuGetProjectActionType.Install);
+
+ await project.PostProcessAsync (context, CancellationToken.None);
+
+ Assert.AreEqual (project, buildIntegratedRestorer.ProjectRestored);
+ Assert.AreEqual ("References", modifiedHintMainProject);
+ Assert.AreEqual ("References", modifiedHintProjectWithReference);
+ }
+
+ [Test]
+ public void NotifyProjectReferencesChanged_IncludeTransitiveReferences_NotifyReferencesChangedEventFiredForAllProjects ()
+ {
+ CreateNuGetProject ();
+ var dotNetProjectWithProjectReference = CreateDotNetCoreProject ("MyProject2", @"d:\projects\MyProject2\MyProject2.csproj");
+ dotNetProject.ParentSolution.RootFolder.AddItem (dotNetProjectWithProjectReference);
+ var projectReference = ProjectReference.CreateProjectReference (dotNetProject);
+ dotNetProjectWithProjectReference.References.Add (projectReference);
+ string modifiedHintMainProject = null;
+ dotNetProject.Modified += (sender, e) => {
+ modifiedHintMainProject = e.Single ().Hint;
+ };
+ string modifiedHintProjectWithReference = null;
+ dotNetProjectWithProjectReference.Modified += (sender, e) => {
+ modifiedHintProjectWithReference = e.Single ().Hint;
+ };
+
+ project.NotifyProjectReferencesChanged (true);
+
+ Assert.AreEqual ("References", modifiedHintMainProject);
+ Assert.AreEqual ("References", modifiedHintProjectWithReference);
+ }
+
+ [Test]
+ public void NotifyProjectReferencesChanged_DoNotIncludeTransitiveReferences_NotifyReferencesChangedEventFiredForMainProjectOnly ()
+ {
+ CreateNuGetProject ();
+ var dotNetProjectWithProjectReference = CreateDotNetCoreProject ("MyProject2", @"d:\projects\MyProject2\MyProject2.csproj");
+ dotNetProject.ParentSolution.RootFolder.AddItem (dotNetProjectWithProjectReference);
+ var projectReference = ProjectReference.CreateProjectReference (dotNetProject);
+ dotNetProjectWithProjectReference.References.Add (projectReference);
+ string modifiedHintMainProject = null;
+ dotNetProject.Modified += (sender, e) => {
+ modifiedHintMainProject = e.Single ().Hint;
+ };
+ string modifiedHintProjectWithReference = null;
+ dotNetProjectWithProjectReference.Modified += (sender, e) => {
+ modifiedHintProjectWithReference = e.Single ().Hint;
+ };
+
+ project.NotifyProjectReferencesChanged (false);
+
+ Assert.AreEqual ("References", modifiedHintMainProject);
+ Assert.IsNull (modifiedHintProjectWithReference);
+ }
}
}
diff --git a/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.Tests/MonoDevelop.PackageManagement.Tests/DotNetProjectExtensionsTests.cs b/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.Tests/MonoDevelop.PackageManagement.Tests/DotNetProjectExtensionsTests.cs
index c92c7b099b..303c4f39d5 100644
--- a/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.Tests/MonoDevelop.PackageManagement.Tests/DotNetProjectExtensionsTests.cs
+++ b/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.Tests/MonoDevelop.PackageManagement.Tests/DotNetProjectExtensionsTests.cs
@@ -25,8 +25,9 @@
// THE SOFTWARE.
using System.Collections.Generic;
-using MonoDevelop.PackageManagement;
+using System.Linq;
using MonoDevelop.PackageManagement.Tests.Helpers;
+using MonoDevelop.Projects;
using NUnit.Framework;
namespace MonoDevelop.PackageManagement.Tests
@@ -56,6 +57,20 @@ namespace MonoDevelop.PackageManagement.Tests
existingFiles.Add (fileName.ToNativePath ());
}
+ static DummyDotNetProject CreateDotNetCoreProject (string projectName = "MyProject", string fileName = @"d:\projects\MyProject\MyProject.csproj")
+ {
+ var project = new DummyDotNetProject ();
+ project.Name = projectName;
+ project.FileName = fileName.ToNativePath ();
+ return project;
+ }
+
+ void AddParentSolution (DotNetProject dotNetProject)
+ {
+ var solution = new Solution ();
+ solution.RootFolder.AddItem (dotNetProject);
+ }
+
[Test]
public void GetPackagesConfigFilePath_ProjectPackagesConfigFileDoesNotExist_ReturnsDefaultPackagesConfigFile ()
{
@@ -162,6 +177,213 @@ namespace MonoDevelop.PackageManagement.Tests
Assert.IsTrue (result);
}
+
+ [Test]
+ public void DotNetCoreNotifyReferencesChanged_NoProjectReferencesAllProjects_NotifyReferencesChangedForProject ()
+ {
+ var dotNetProject = CreateDotNetCoreProject ();
+ AddParentSolution (dotNetProject);
+ string modifiedHint = null;
+ dotNetProject.Modified += (sender, e) => {
+ modifiedHint = e.First ().Hint;
+ };
+
+ dotNetProject.DotNetCoreNotifyReferencesChanged (transitiveOnly: false);
+
+ Assert.AreEqual ("References", modifiedHint);
+ }
+
+ [Test]
+ public void DotNetCoreNotifyReferencesChanged_NoProjectReferencesTransitiveProjectReferencesOnly_NotifyReferencesChangedNotFiredForProject ()
+ {
+ var dotNetProject = CreateDotNetCoreProject ();
+ AddParentSolution (dotNetProject);
+ string modifiedHint = null;
+ dotNetProject.Modified += (sender, e) => {
+ modifiedHint = e.First ().Hint;
+ };
+
+ dotNetProject.DotNetCoreNotifyReferencesChanged (transitiveOnly: true);
+
+ Assert.IsNull (modifiedHint);
+ }
+
+ [Test]
+ public void DotNetCoreNotifyReferencesChanged_OneProjectReferencesProject_NotifyReferencesChangedForAllProjects ()
+ {
+ var dotNetProject = CreateDotNetCoreProject ();
+ AddParentSolution (dotNetProject);
+ var referencingProject = CreateDotNetCoreProject ();
+ dotNetProject.ParentSolution.RootFolder.AddItem (referencingProject);
+ referencingProject.References.Add (ProjectReference.CreateProjectReference (dotNetProject));
+ string modifiedHint = null;
+ dotNetProject.Modified += (sender, e) => {
+ modifiedHint = e.First ().Hint;
+ };
+ string modifiedHintForReferencingProject = null;
+ referencingProject.Modified += (sender, e) => {
+ modifiedHintForReferencingProject = e.First ().Hint;
+ };
+
+ dotNetProject.DotNetCoreNotifyReferencesChanged ();
+
+ Assert.AreEqual ("References", modifiedHint);
+ Assert.AreEqual ("References", modifiedHintForReferencingProject);
+ }
+
+ [Test]
+ public void DotNetCoreNotifyReferencesChanged_OneProjectReferencesProjectWithReferencedOutputAssemblyFalse_NotifyReferencesChangedNotFiredForReferencingProject ()
+ {
+ var dotNetProject = CreateDotNetCoreProject ();
+ AddParentSolution (dotNetProject);
+ var referencingProject = CreateDotNetCoreProject ();
+ dotNetProject.ParentSolution.RootFolder.AddItem (referencingProject);
+ var projectReference = ProjectReference.CreateProjectReference (dotNetProject);
+ projectReference.ReferenceOutputAssembly = false;
+ referencingProject.References.Add (projectReference);
+ string modifiedHintForReferencingProject = null;
+ referencingProject.Modified += (sender, e) => {
+ modifiedHintForReferencingProject = e.First ().Hint;
+ };
+
+ dotNetProject.DotNetCoreNotifyReferencesChanged (true);
+
+ Assert.IsNull (modifiedHintForReferencingProject);
+ }
+
+ [Test]
+ public void DotNetCoreNotifyReferencesChanged_TwoOneProjectReferencesModifiedProject_NotifyReferencesChangedForAllProjects ()
+ {
+ var dotNetProject = CreateDotNetCoreProject ();
+ AddParentSolution (dotNetProject);
+ var referencingProject1 = CreateDotNetCoreProject ();
+ dotNetProject.ParentSolution.RootFolder.AddItem (referencingProject1);
+ referencingProject1.References.Add (ProjectReference.CreateProjectReference (dotNetProject));
+ var referencingProject2 = CreateDotNetCoreProject ();
+ dotNetProject.ParentSolution.RootFolder.AddItem (referencingProject2);
+ referencingProject2.References.Add (ProjectReference.CreateProjectReference (dotNetProject));
+ string modifiedHint = null;
+ dotNetProject.Modified += (sender, e) => {
+ modifiedHint = e.First ().Hint;
+ };
+ string modifiedHintForReferencingProject1 = null;
+ referencingProject1.Modified += (sender, e) => {
+ modifiedHintForReferencingProject1 = e.First ().Hint;
+ };
+ string modifiedHintForReferencingProject2 = null;
+ referencingProject2.Modified += (sender, e) => {
+ modifiedHintForReferencingProject2 = e.First ().Hint;
+ };
+
+ dotNetProject.DotNetCoreNotifyReferencesChanged ();
+
+ Assert.AreEqual ("References", modifiedHint);
+ Assert.AreEqual ("References", modifiedHintForReferencingProject1);
+ Assert.AreEqual ("References", modifiedHintForReferencingProject2);
+ }
+
+ [Test]
+ public void DotNetCoreNotifyReferencesChanged_TwoProjectReferencesChainToModifiedProject_NotifyReferencesChangedForAllProjects ()
+ {
+ var dotNetProject = CreateDotNetCoreProject ();
+ AddParentSolution (dotNetProject);
+ var referencingProject1 = CreateDotNetCoreProject ();
+ dotNetProject.ParentSolution.RootFolder.AddItem (referencingProject1);
+ referencingProject1.References.Add (ProjectReference.CreateProjectReference (dotNetProject));
+ var referencingProject2 = CreateDotNetCoreProject ();
+ dotNetProject.ParentSolution.RootFolder.AddItem (referencingProject2);
+ referencingProject2.References.Add (ProjectReference.CreateProjectReference (referencingProject1));
+ string modifiedHint = null;
+ dotNetProject.Modified += (sender, e) => {
+ modifiedHint = e.First ().Hint;
+ };
+ string modifiedHintForReferencingProject1 = null;
+ referencingProject1.Modified += (sender, e) => {
+ modifiedHintForReferencingProject1 = e.First ().Hint;
+ };
+ string modifiedHintForReferencingProject2 = null;
+ referencingProject2.Modified += (sender, e) => {
+ modifiedHintForReferencingProject2 = e.First ().Hint;
+ };
+
+ dotNetProject.DotNetCoreNotifyReferencesChanged ();
+
+ Assert.AreEqual ("References", modifiedHint);
+ Assert.AreEqual ("References", modifiedHintForReferencingProject1);
+ Assert.AreEqual ("References", modifiedHintForReferencingProject2);
+ }
+
+ /// <summary>
+ /// Same as above but the projects are added to the solution in a different order.
+ /// </summary>
+ [Test]
+ public void DotNetCoreNotifyReferencesChanged_TwoProjectReferencesChainToModifiedProject_NotifyReferencesChangedForAllProjects2 ()
+ {
+ var dotNetProject = CreateDotNetCoreProject ();
+ AddParentSolution (dotNetProject);
+ var referencingProject1 = CreateDotNetCoreProject ();
+ var referencingProject2 = CreateDotNetCoreProject ();
+ dotNetProject.ParentSolution.RootFolder.AddItem (referencingProject2);
+ dotNetProject.ParentSolution.RootFolder.AddItem (referencingProject1);
+ referencingProject1.References.Add (ProjectReference.CreateProjectReference (dotNetProject));
+ referencingProject2.References.Add (ProjectReference.CreateProjectReference (referencingProject1));
+ string modifiedHint = null;
+ dotNetProject.Modified += (sender, e) => {
+ modifiedHint = e.First ().Hint;
+ };
+ string modifiedHintForReferencingProject1 = null;
+ referencingProject1.Modified += (sender, e) => {
+ modifiedHintForReferencingProject1 = e.First ().Hint;
+ };
+ string modifiedHintForReferencingProject2 = null;
+ referencingProject2.Modified += (sender, e) => {
+ modifiedHintForReferencingProject2 = e.First ().Hint;
+ };
+
+ dotNetProject.DotNetCoreNotifyReferencesChanged ();
+
+ Assert.AreEqual ("References", modifiedHint);
+ Assert.AreEqual ("References", modifiedHintForReferencingProject1);
+ Assert.AreEqual ("References", modifiedHintForReferencingProject2);
+ }
+
+ [Test]
+ public void GetReferencingProjects_ThreeProjectsOneProjectReferencesModifiedProject_OneProjectReturned ()
+ {
+ var dotNetProject = CreateDotNetCoreProject ();
+ AddParentSolution (dotNetProject);
+ var referencedProject = CreateDotNetCoreProject ();
+ dotNetProject.ParentSolution.RootFolder.AddItem (referencedProject);
+ referencedProject.References.Add (ProjectReference.CreateProjectReference (dotNetProject));
+ var otherProject = CreateDotNetCoreProject ();
+ dotNetProject.ParentSolution.RootFolder.AddItem (otherProject);
+
+ var projects = dotNetProject.GetReferencingProjects ().ToList ();
+
+ Assert.AreEqual (1, projects.Count);
+ Assert.AreEqual (projects[0], referencedProject);
+ }
+
+ [Test]
+ public void GetReferencingProjects_TwoProjectReferencesChainToModifiedProject_NotifyReferencesChangedForAllProjects ()
+ {
+ var dotNetProject = CreateDotNetCoreProject ();
+ AddParentSolution (dotNetProject);
+ var referencingProject1 = CreateDotNetCoreProject ();
+ dotNetProject.ParentSolution.RootFolder.AddItem (referencingProject1);
+ referencingProject1.References.Add (ProjectReference.CreateProjectReference (dotNetProject));
+ var referencingProject2 = CreateDotNetCoreProject ();
+ dotNetProject.ParentSolution.RootFolder.AddItem (referencingProject2);
+ referencingProject2.References.Add (ProjectReference.CreateProjectReference (referencingProject1));
+ var otherProject = CreateDotNetCoreProject ();
+ dotNetProject.ParentSolution.RootFolder.AddItem (otherProject);
+
+ var projects = dotNetProject.GetReferencingProjects ().ToList ();
+
+ Assert.AreEqual (2, projects.Count);
+ Assert.That (projects, Contains.Item (referencingProject1));
+ Assert.That (projects, Contains.Item (referencingProject2));
+ }
}
}
diff --git a/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.Tests/MonoDevelop.PackageManagement.Tests/RestoreNuGetPackagesInNuGetIntegratedProjectTests.cs b/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.Tests/MonoDevelop.PackageManagement.Tests/RestoreNuGetPackagesInNuGetIntegratedProjectTests.cs
new file mode 100644
index 0000000000..bf48dc9bdd
--- /dev/null
+++ b/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.Tests/MonoDevelop.PackageManagement.Tests/RestoreNuGetPackagesInNuGetIntegratedProjectTests.cs
@@ -0,0 +1,151 @@
+//
+// RestoreNuGetPackagesInNuGetIntegratedProjectTests.cs
+//
+// Author:
+// Matt Ward <matt.ward@xamarin.com>
+//
+// Copyright (c) 2017 Xamarin Inc. (http://xamarin.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+using MonoDevelop.PackageManagement.Tests.Helpers;
+using MonoDevelop.Projects;
+using NUnit.Framework;
+namespace MonoDevelop.PackageManagement.Tests
+{
+ [TestFixture]
+ class RestoreNuGetPackagesInNuGetIntegratedProjectTests
+ {
+ RestoreNuGetPackagesInNuGetIntegratedProject action;
+ FakeSolutionManager solutionManager;
+ FakeSolution fakeSolution;
+ DotNetProject dotNetProject;
+ FakeDotNetProject fakeDotNetProject;
+ TestableDotNetCoreNuGetProject nugetProject;
+ FakeMonoDevelopBuildIntegratedRestorer buildIntegratedRestorer;
+
+ void CreateProject ()
+ {
+ solutionManager = new FakeSolutionManager ();
+ fakeSolution = new FakeSolution ();
+ CreateNuGetProject ();
+ fakeDotNetProject = new FakeDotNetProject (dotNetProject.FileName);
+ fakeDotNetProject.ParentSolution = fakeSolution;
+ fakeDotNetProject.DotNetProject = dotNetProject;
+ fakeSolution.Projects.Add (fakeDotNetProject);
+ }
+
+ void CreateAction (bool restoreTransitiveProjectReferences = false)
+ {
+ action = new RestoreNuGetPackagesInNuGetIntegratedProject (
+ fakeDotNetProject,
+ nugetProject,
+ solutionManager,
+ buildIntegratedRestorer,
+ restoreTransitiveProjectReferences);
+ }
+
+ void CreateNuGetProject (string projectName = "MyProject", string fileName = @"d:\projects\MyProject\MyProject.csproj")
+ {
+ dotNetProject = CreateDotNetCoreProject (projectName, fileName);
+ var solution = new Solution ();
+ solution.RootFolder.AddItem (dotNetProject);
+ nugetProject = new TestableDotNetCoreNuGetProject (dotNetProject);
+ buildIntegratedRestorer = nugetProject.BuildIntegratedRestorer;
+ }
+
+ TestableDotNetCoreNuGetProject CreateNuGetProject (DotNetProject project)
+ {
+ var dotNetProjectProxy = new FakeDotNetProject ();
+ dotNetProjectProxy.DotNetProject = project;
+ fakeSolution.Projects.Add (dotNetProjectProxy);
+
+ var dotNetCoreNuGetProject = new TestableDotNetCoreNuGetProject (project);
+ dotNetCoreNuGetProject.BuildIntegratedRestorer = null;
+
+ solutionManager.NuGetProjectsUsingDotNetProjects.Add (project, dotNetCoreNuGetProject);
+
+ return dotNetCoreNuGetProject;
+ }
+
+ static DummyDotNetProject CreateDotNetCoreProject (string projectName = "MyProject", string fileName = @"d:\projects\MyProject\MyProject.csproj")
+ {
+ var project = new DummyDotNetProject ();
+ project.Name = projectName;
+ project.FileName = fileName.ToNativePath ();
+ return project;
+ }
+
+ [Test]
+ public void Execute_BuildIntegratedRestorer_PackagesRestoredForProject ()
+ {
+ CreateProject ();
+ CreateAction ();
+
+ action.Execute ();
+
+ Assert.AreEqual (nugetProject, buildIntegratedRestorer.ProjectRestored);
+ }
+
+ [Test]
+ public void Execute_Events_PackagesRestoredEventFired ()
+ {
+ CreateProject ();
+ CreateAction ();
+ bool packagesRestoredEventFired = false;
+ PackageManagementServices.PackageManagementEvents.PackagesRestored += (sender, e) => {
+ packagesRestoredEventFired = true;
+ };
+
+ action.Execute ();
+
+ Assert.IsTrue (packagesRestoredEventFired);
+ }
+
+ [Test]
+ public void Execute_ReferenceStatus_IsRefreshed ()
+ {
+ CreateProject ();
+ CreateAction ();
+
+ action.Execute ();
+
+ Assert.IsTrue (fakeDotNetProject.IsReferenceStatusRefreshed);
+ }
+
+ [Test]
+ public void IncludeTransitiveProjectReferences_ThreeProjectsOneReferencedAnother_TwoProjectsRestored ()
+ {
+ CreateProject ();
+ var referencingProject = CreateDotNetCoreProject ();
+ dotNetProject.ParentSolution.RootFolder.AddItem (referencingProject);
+ referencingProject.References.Add (ProjectReference.CreateProjectReference (dotNetProject));
+ var otherProject = CreateDotNetCoreProject ();
+ dotNetProject.ParentSolution.RootFolder.AddItem (otherProject);
+ var referencingNuGetProject = CreateNuGetProject (referencingProject);
+ CreateAction (true);
+
+ action.Execute ();
+
+ Assert.AreEqual (2, buildIntegratedRestorer.ProjectsRestored.Count);
+ Assert.AreEqual (buildIntegratedRestorer.ProjectsRestored[0], nugetProject);
+ Assert.AreEqual (buildIntegratedRestorer.ProjectsRestored[1], referencingNuGetProject);
+ }
+ }
+}
diff --git a/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.csproj b/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.csproj
index c14285774d..20f387686f 100644
--- a/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.csproj
+++ b/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.csproj
@@ -425,6 +425,7 @@
<Compile Include="MonoDevelop.PackageManagement\PendingPackageActionsInformation.cs" />
<Compile Include="MonoDevelop.PackageManagement\PackageActionType.cs" />
<Compile Include="MonoDevelop.PackageManagement\PendingPackageActionsHandler.cs" />
+ <Compile Include="MonoDevelop.PackageManagement\IMonoDevelopBuildIntegratedRestorer.cs" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="MonoDevelop.PackageManagement.addin.xml" />
diff --git a/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement/DotNetCoreNuGetProject.cs b/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement/DotNetCoreNuGetProject.cs
index 701ea37597..c861d731d8 100644
--- a/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement/DotNetCoreNuGetProject.cs
+++ b/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement/DotNetCoreNuGetProject.cs
@@ -281,7 +281,7 @@ namespace MonoDevelop.PackageManagement
}
Runtime.RunInMainThread (() => {
- DotNetProject.NotifyModified ("References");
+ DotNetProject.DotNetCoreNotifyReferencesChanged ();
});
packageManagementEvents.OnFileChanged (project.GetNuGetAssetsFilePath ());
@@ -292,8 +292,7 @@ namespace MonoDevelop.PackageManagement
async Task RestorePackages (INuGetProjectContext nuGetProjectContext, CancellationToken token)
{
var packageRestorer = await Runtime.RunInMainThread (() => {
- var solutionManager = PackageManagementServices.Workspace.GetSolutionManager (project.ParentSolution);
- return new MonoDevelopBuildIntegratedRestorer (solutionManager);
+ return CreateBuildIntegratedRestorer (project.ParentSolution);
});
var restoreTask = packageRestorer.RestorePackages (this, token);
@@ -304,7 +303,7 @@ namespace MonoDevelop.PackageManagement
if (!packageRestorer.LockFileChanged) {
// Need to refresh the references since the restore did not.
await Runtime.RunInMainThread (() => {
- DotNetProject.NotifyModified ("References");
+ DotNetProject.DotNetCoreNotifyReferencesChanged ();
packageManagementEvents.OnFileChanged (project.GetNuGetAssetsFilePath ());
});
}
@@ -312,6 +311,12 @@ namespace MonoDevelop.PackageManagement
await base.PostProcessAsync (nuGetProjectContext, token);
}
+ protected virtual IMonoDevelopBuildIntegratedRestorer CreateBuildIntegratedRestorer (Solution solution)
+ {
+ var solutionManager = PackageManagementServices.Workspace.GetSolutionManager (project.ParentSolution);
+ return new MonoDevelopBuildIntegratedRestorer (solutionManager);
+ }
+
public void OnBeforeUninstall (IEnumerable<NuGetProjectAction> actions)
{
}
@@ -321,12 +326,16 @@ namespace MonoDevelop.PackageManagement
restoreRequired = actions.Any (action => action.NuGetProjectActionType == NuGetProjectActionType.Install);
}
- public void NotifyProjectReferencesChanged ()
+ public void NotifyProjectReferencesChanged (bool includeTransitiveProjectReferences)
{
Runtime.AssertMainThread ();
DotNetProject.RefreshProjectBuilder ();
- DotNetProject.NotifyModified ("References");
+
+ if (includeTransitiveProjectReferences)
+ DotNetProject.DotNetCoreNotifyReferencesChanged ();
+ else
+ DotNetProject.NotifyModified ("References");
}
/// <summary>
diff --git a/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement/DotNetProjectExtensions.cs b/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement/DotNetProjectExtensions.cs
index fb79aee86b..4d230bb4e3 100644
--- a/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement/DotNetProjectExtensions.cs
+++ b/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement/DotNetProjectExtensions.cs
@@ -216,5 +216,97 @@ namespace MonoDevelop.PackageManagement
return File.Exists (propsFileName) &&
File.Exists (targetsFileName);
}
+
+ /// <summary>
+ /// If a NuGet package is installed into a .NET Core project then all .NET Core projects that
+ /// reference this project need to have their reference information updated. This allows the
+ /// assemblies from the NuGet package to be made available to the other projects since .NET
+ /// Core projects support transitive references. This method calls NotifyModified for each
+ /// project that references it, as well as for the project itself, passing the hint 'References'
+ /// which will cause the type system to refresh its reference information, which will be taken
+ /// from MSBuild.
+ ///
+ /// All projects that reference .NET Core projects will have their references refreshed. If a
+ /// .NET Framework project (non SDK), has PackageReferences and references a .NET Standard project
+ /// (SDK project) then NuGet dependencies from the .NET Standard project are available to the
+ /// .NET Framework project transitively without needing the NuGet package to be installed into
+ /// the .NET Framework project. So refreshing the references of any project that references a
+ /// .NET Core project will ensure assemblies from NuGet packages are available after installing
+ /// a new NuGet package into the referenced project.
+ /// </summary>
+ /// <param name="project">.NET Core project</param>
+ /// <param name="transitiveOnly">If false then the project passed will also have its
+ /// references refreshed. Otherwise only the projects that reference the project will
+ /// have their references refreshed.</param>
+ public static void DotNetCoreNotifyReferencesChanged (this DotNetProject project, bool transitiveOnly = false)
+ {
+ if (!transitiveOnly)
+ project.NotifyModified ("References");
+
+ foreach (var referencingProject in project.GetReferencingProjects ()) {
+ referencingProject.NotifyModified ("References");
+ }
+ }
+
+ /// <summary>
+ /// Returns all projects that directly or indirectly referencing the specified project.
+ /// </summary>
+ public static IEnumerable<DotNetProject> GetReferencingProjects (this DotNetProject project)
+ {
+ var projects = new List<DotNetProject> ();
+ var traversedProjects = new Dictionary<string, bool> (StringComparer.OrdinalIgnoreCase);
+ traversedProjects.Add (project.ItemId, true);
+
+ foreach (var currentProject in project.ParentSolution.GetAllDotNetProjects ()) {
+ if (!traversedProjects.ContainsKey (currentProject.ItemId))
+ GetReferencingProjects (project, currentProject, traversedProjects, projects);
+ }
+
+ return projects;
+ }
+
+ static bool GetReferencingProjects (
+ DotNetProject mainProject,
+ DotNetProject project,
+ Dictionary<string, bool> traversedProjects,
+ List<DotNetProject> referencingProjects)
+ {
+ foreach (var projectReference in project.References.Where (IncludeProjectReference)) {
+ var resolvedProject = projectReference.ResolveProject (mainProject.ParentSolution) as DotNetProject;
+ if (resolvedProject == null)
+ continue;
+
+ if (resolvedProject == mainProject) {
+ traversedProjects [project.ItemId] = true;
+ referencingProjects.Add (project);
+ return true;
+ }
+
+ if (traversedProjects.TryGetValue (resolvedProject.ItemId, out bool referencesProject)) {
+ if (referencesProject) {
+ traversedProjects [project.ItemId] = referencesProject;
+ referencingProjects.Add (project);
+ return true;
+ }
+ continue;
+ }
+
+ if (GetReferencingProjects (mainProject, resolvedProject, traversedProjects, referencingProjects)) {
+ traversedProjects [project.ItemId] = true;
+ referencingProjects.Add (project);
+ return true;
+ }
+ }
+
+ traversedProjects [project.ItemId] = false;
+
+ return false;
+ }
+
+ static bool IncludeProjectReference (ProjectReference projectReference)
+ {
+ return projectReference.ReferenceType == ReferenceType.Project &&
+ projectReference.ReferenceOutputAssembly;
+ }
}
}
diff --git a/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement/IBuildIntegratedNuGetProject.cs b/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement/IBuildIntegratedNuGetProject.cs
index b14d19a7ba..666e49130f 100644
--- a/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement/IBuildIntegratedNuGetProject.cs
+++ b/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement/IBuildIntegratedNuGetProject.cs
@@ -37,7 +37,13 @@ namespace MonoDevelop.PackageManagement
void OnBeforeUninstall (IEnumerable<NuGetProjectAction> actions);
void OnAfterExecuteActions (IEnumerable<NuGetProjectAction> actions);
Task PostProcessAsync (INuGetProjectContext nuGetProjectContext, CancellationToken token);
- void NotifyProjectReferencesChanged ();
+
+ /// <summary>
+ /// Notifies the project references changed.
+ /// </summary>
+ /// <param name="includeTransitiveProjectReferences">If set to <c>true</c> also notify references
+ /// have changed in all projects that transitively reference this project.</param>
+ void NotifyProjectReferencesChanged (bool includeTransitiveProjectReferences);
}
}
diff --git a/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement/IMonoDevelopBuildIntegratedRestorer.cs b/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement/IMonoDevelopBuildIntegratedRestorer.cs
new file mode 100644
index 0000000000..8c693b321e
--- /dev/null
+++ b/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement/IMonoDevelopBuildIntegratedRestorer.cs
@@ -0,0 +1,46 @@
+//
+// IMonoDevelopBuildIntegratedRestorer.cs
+//
+// Author:
+// Matt Ward <matt.ward@xamarin.com>
+//
+// Copyright (c) 2017 Xamarin Inc. (http://xamarin.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+using System.Collections.Generic;
+using System.Threading;
+using System.Threading.Tasks;
+using NuGet.ProjectManagement.Projects;
+
+namespace MonoDevelop.PackageManagement
+{
+ interface IMonoDevelopBuildIntegratedRestorer
+ {
+ Task RestorePackages (
+ BuildIntegratedNuGetProject project,
+ CancellationToken cancellationToken);
+
+ Task RestorePackages (
+ IEnumerable<BuildIntegratedNuGetProject> projects,
+ CancellationToken cancellationToken);
+
+ bool LockFileChanged { get; }
+ }
+}
diff --git a/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement/MonoDevelopBuildIntegratedRestorer.cs b/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement/MonoDevelopBuildIntegratedRestorer.cs
index f11a4b1082..21cf4da934 100644
--- a/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement/MonoDevelopBuildIntegratedRestorer.cs
+++ b/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement/MonoDevelopBuildIntegratedRestorer.cs
@@ -44,7 +44,7 @@ using NuGet.Protocol.Core.Types;
namespace MonoDevelop.PackageManagement
{
- internal class MonoDevelopBuildIntegratedRestorer
+ internal class MonoDevelopBuildIntegratedRestorer : IMonoDevelopBuildIntegratedRestorer
{
IPackageManagementEvents packageManagementEvents;
List<SourceRepository> sourceRepositories;
@@ -98,8 +98,10 @@ namespace MonoDevelop.PackageManagement
LockFileChanged = true;
await Runtime.RunInMainThread (() => {
FileService.NotifyFilesChanged (changedLocks);
- foreach (var project in affectedProjects) {
- NotifyProjectReferencesChanged (project);
+ foreach (var project in affectedProjects) {
+ // Restoring the entire solution so do not refresh references for
+ // transitive project references since they should be refreshed anyway.
+ NotifyProjectReferencesChanged (project, includeTransitiveProjectReferences: false);
}
});
}
@@ -114,12 +116,17 @@ namespace MonoDevelop.PackageManagement
var changedLock = await RestorePackagesInternal (project, cancellationToken);
if (projectToReload != null) {
- await ReloadProject (projectToReload, changedLock);
+ // Need to ensure transitive project references are refreshed if only the single
+ // project is reloaded since they will still be out of date.
+ await ReloadProject (projectToReload, changedLock, refreshTransitiveReferences: true);
} else if (changedLock != null) {
LockFileChanged = true;
await Runtime.RunInMainThread (() => {
- FileService.NotifyFileChanged (changedLock);
- NotifyProjectReferencesChanged (project);
+ FileService.NotifyFileChanged (changedLock);
+
+ // Restoring a single project so ensure references are refreshed for
+ // transitive project references.
+ NotifyProjectReferencesChanged (project, includeTransitiveProjectReferences: true);
});
}
}
@@ -153,11 +160,13 @@ namespace MonoDevelop.PackageManagement
return null;
}
- static void NotifyProjectReferencesChanged (BuildIntegratedNuGetProject project)
+ static void NotifyProjectReferencesChanged (
+ BuildIntegratedNuGetProject project,
+ bool includeTransitiveProjectReferences)
{
var buildIntegratedProject = project as IBuildIntegratedNuGetProject;
if (buildIntegratedProject != null) {
- buildIntegratedProject.NotifyProjectReferencesChanged ();
+ buildIntegratedProject.NotifyProjectReferencesChanged (includeTransitiveProjectReferences);
}
}
@@ -219,7 +228,7 @@ namespace MonoDevelop.PackageManagement
return null;
}
- Task ReloadProject (DotNetProject projectToReload, string changedLock)
+ Task ReloadProject (DotNetProject projectToReload, string changedLock, bool refreshTransitiveReferences = false)
{
return Runtime.RunInMainThread (async () => {
if (changedLock != null) {
@@ -227,6 +236,9 @@ namespace MonoDevelop.PackageManagement
FileService.NotifyFileChanged (changedLock);
}
await projectToReload.ReevaluateProject (new ProgressMonitor ());
+
+ if (refreshTransitiveReferences)
+ projectToReload.DotNetCoreNotifyReferencesChanged (transitiveOnly: true);
});
}
}
diff --git a/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement/PackageReferenceNuGetProject.cs b/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement/PackageReferenceNuGetProject.cs
index 5caaec2673..a85f52f17f 100644
--- a/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement/PackageReferenceNuGetProject.cs
+++ b/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement/PackageReferenceNuGetProject.cs
@@ -284,7 +284,7 @@ namespace MonoDevelop.PackageManagement
{
}
- public void NotifyProjectReferencesChanged ()
+ public void NotifyProjectReferencesChanged (bool includeTransitiveProjectReferences)
{
Runtime.AssertMainThread ();
diff --git a/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement/ProjectJsonBuildIntegratedProjectSystem.cs b/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement/ProjectJsonBuildIntegratedProjectSystem.cs
index 36f92f4490..1ea10e4766 100644
--- a/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement/ProjectJsonBuildIntegratedProjectSystem.cs
+++ b/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement/ProjectJsonBuildIntegratedProjectSystem.cs
@@ -247,7 +247,7 @@ namespace MonoDevelop.PackageManagement
return excludedReferences;
}
- public void NotifyProjectReferencesChanged ()
+ public void NotifyProjectReferencesChanged (bool includeTransitiveProjectReferences)
{
Runtime.AssertMainThread ();
diff --git a/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement/RestoreNuGetPackagesInNuGetIntegratedProject.cs b/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement/RestoreNuGetPackagesInNuGetIntegratedProject.cs
index 7b796c7b1c..e5cef02523 100644
--- a/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement/RestoreNuGetPackagesInNuGetIntegratedProject.cs
+++ b/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement/RestoreNuGetPackagesInNuGetIntegratedProject.cs
@@ -24,6 +24,8 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
+using System.Collections.Generic;
+using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using MonoDevelop.Core;
@@ -34,21 +36,42 @@ namespace MonoDevelop.PackageManagement
{
internal class RestoreNuGetPackagesInNuGetIntegratedProject : IPackageAction
{
- DotNetProject project;
+ IDotNetProject project;
BuildIntegratedNuGetProject nugetProject;
- MonoDevelopBuildIntegratedRestorer packageRestorer;
+ IMonoDevelopSolutionManager solutionManager;
+ IMonoDevelopBuildIntegratedRestorer packageRestorer;
IPackageManagementEvents packageManagementEvents;
+ List<BuildIntegratedNuGetProject> referencingProjects;
public RestoreNuGetPackagesInNuGetIntegratedProject (
DotNetProject project,
BuildIntegratedNuGetProject nugetProject,
- IMonoDevelopSolutionManager solutionManager)
+ IMonoDevelopSolutionManager solutionManager,
+ bool restoreTransitiveProjectReferences = false)
+ : this (
+ new DotNetProjectProxy (project),
+ nugetProject,
+ solutionManager,
+ new MonoDevelopBuildIntegratedRestorer (solutionManager),
+ restoreTransitiveProjectReferences)
+ {
+ }
+
+ public RestoreNuGetPackagesInNuGetIntegratedProject (
+ IDotNetProject project,
+ BuildIntegratedNuGetProject nugetProject,
+ IMonoDevelopSolutionManager solutionManager,
+ IMonoDevelopBuildIntegratedRestorer packageRestorer,
+ bool restoreTransitiveProjectReferences)
{
this.project = project;
this.nugetProject = nugetProject;
+ this.solutionManager = solutionManager;
+ this.packageRestorer = packageRestorer;
packageManagementEvents = PackageManagementServices.PackageManagementEvents;
-
- packageRestorer = new MonoDevelopBuildIntegratedRestorer (solutionManager);
+
+ if (restoreTransitiveProjectReferences)
+ IncludeTransitiveProjectReferences ();
}
public PackageActionType ActionType {
@@ -75,12 +98,44 @@ namespace MonoDevelop.PackageManagement
async Task ExecuteAsync (CancellationToken cancellationToken)
{
- await packageRestorer.RestorePackages (nugetProject, cancellationToken);
+ if (referencingProjects == null)
+ await packageRestorer.RestorePackages (nugetProject, cancellationToken);
+ else
+ await RestoreMultiplePackages (cancellationToken);
await Runtime.RunInMainThread (() => project.RefreshReferenceStatus ());
packageManagementEvents.OnPackagesRestored ();
}
+
+ /// <summary>
+ /// Execute will restore packages for all projects that transitively reference the project
+ /// passed to the constructor of this restore action if this method is passed.
+ /// </summary>
+ void IncludeTransitiveProjectReferences ()
+ {
+ var projects = project.DotNetProject.GetReferencingProjects ();
+ if (!projects.Any ())
+ return;
+
+ referencingProjects = new List<BuildIntegratedNuGetProject> ();
+ foreach (var referencingProject in projects) {
+ var projectProxy = new DotNetProjectProxy (referencingProject);
+ var currentNuGetProject = solutionManager.GetNuGetProject (projectProxy) as BuildIntegratedNuGetProject;
+ if (currentNuGetProject != null) {
+ referencingProjects.Add (currentNuGetProject);
+ }
+ }
+ }
+
+ Task RestoreMultiplePackages (CancellationToken cancellationToken)
+ {
+ var projects = new List<BuildIntegratedNuGetProject> ();
+ projects.Add (nugetProject);
+ projects.AddRange (referencingProjects);
+
+ return packageRestorer.RestorePackages (projects, cancellationToken);
+ }
}
}