Merge pull request #11 from Microsoft/develop

Create release for VS2017
This commit is contained in:
Heath Stewart 2017-03-07 21:35:10 -08:00 коммит произвёл GitHub
Родитель e114d3d128 8cb8fbc214
Коммит 846bcd07c7
29 изменённых файлов: 900 добавлений и 129 удалений

Просмотреть файл

@ -53,13 +53,28 @@ It's also recommended that, if your machine supports it, you install [Docker for
```batch
REM You only need to build once unless updating the Dockerfile or files it copies.
test\docker\build
docker\build
REM This will automatically map build output. Defaults to Debug configuration. Pass -? for options.
test\docker\test
docker\test
```
You can also run `test\docker\run.cmd` to start an interactive shell for exploratory testing.
For a faster development process, you can run `docker\run -detach`, copy the container ID printed to the window, then subsequently run `docker\test -on <container id>` replacing `<container id>` with the container ID you copied previously. You can make changes to the test data and even rebuild the module and run this command again as frequently as you need. This is especially handy for quick turn around when debugging and fixing a problem.
To stop the container, run `docker stop <container id>`. If you did not pass `-keep` when you started the container it will be removed automatically.
### Debugging
You can also run `docker\run.cmd` to start an interactive shell for exploratory testing. If no other commands are passed when starting the container, the Visual Studio Remote Debugger will launch by default. Remote debugging services are discoverable on your private network.
1. Click *Debug -> Attach to Process*
2. Change *Transport* to "Remote (no authentication)"
3. Click *Find*
4. Click *Select* on the container (host name will be the container name)
5. Select "powershell" under *Available Processes*
6. Click *Attach*
If you know the host name or IP address (depending on your network configuration for the container), you can type it into the *Qualifier* directory along with port 4020, e.g. "172.22.0.1:4020".
## Pull Requests

Просмотреть файл

@ -16,7 +16,7 @@ To install for all users, pass `AllUsers` instead of `CurrentUser`, or just leav
You can also download the ZIP package from the [Releases][releases] page on this project site and extract to a directory named _VSSetup_ under a directory in your `$env:PSMODULEPATH`.
```powershell
Expand-Archive VSSetup.zip "${env:USERPROFILE}\Documents\WindowsPowerShell\Modules\VSSetup"
Expand-Archive VSSetup.zip "$([Environment]::GetFolderPath("MyDocuments"))\WindowsPowerShell\Modules\VSSetup"
```
## Using

Просмотреть файл

@ -4,11 +4,24 @@
# Require .NET Framework
FROM microsoft/windowsservercore
# Download and register current query APIs
# Download and install Remote Debugger
SHELL ["powershell.exe", "-ExecutionPolicy", "Bypass", "-Command"]
ENV API_VERSION="1.5.125-rc"
RUN $ErrorActionPreference = 'Stop' ; \
$VerbosePreference = 'Continue' ; \
New-Item -Path C:\Downloads -Type Directory | Out-Null ; \
Invoke-WebRequest -Uri 'https://go.microsoft.com/fwlink/?LinkId=615470&clcid=0x409' -OutFile C:\Downloads\rtools_setup_x64.exe ; \
Start-Process -Wait -FilePath C:\Downloads\rtools_setup_x64.exe -ArgumentList '-q'
# Configure Remote Debugger
EXPOSE 3702 4020 4021
RUN $ErrorActionPreference = 'Stop' ; \
$VerbosePreference = 'Continue' ; \
Start-Process -Wait -FilePath 'C:\Program Files\Microsoft Visual Studio 14.0\Common7\IDE\Remote Debugger\x64\msvsmon.exe' -ArgumentList '/prepcomputer', '/private', '/quiet'
# Download and register current query APIs
ENV API_VERSION="1.8.24"
RUN $ErrorActionPreference = 'Stop' ; \
$VerbosePreference = 'Continue' ; \
Invoke-WebRequest -Uri "https://www.nuget.org/api/v2/package/Microsoft.VisualStudio.Setup.Configuration.Native/${env:API_VERSION}" -OutFile C:\Downloads\Microsoft.VisualStudio.Setup.Configuration.Native.zip ; \
Expand-Archive -Path C:\Downloads\Microsoft.VisualStudio.Setup.Configuration.Native.zip -DestinationPath C:\Downloads\Microsoft.VisualStudio.Setup.Configuration.Native ; \
C:\Windows\System32\regsvr32.exe /s C:\Downloads\Microsoft.VisualStudio.Setup.Configuration.Native\tools\x64\Microsoft.VisualStudio.Setup.Configuration.Native.dll ; \
@ -16,7 +29,10 @@ RUN $ErrorActionPreference = 'Stop' ; \
# Install latest version of Pester for integration testing
RUN $ErrorActionPreference = 'Stop' ; \
$VerbosePreference = 'Continue' ; \
Install-PackageProvider -Name nuget -MinimumVersion 2.8.5.201 -Force ; \
Install-Module -Name Pester -Scope CurrentUser -SkipPublisherCheck -Force
# Start Remote Debugger if no other command is passed to PowerShell
ENTRYPOINT ["powershell.exe", "-ExecutionPolicy", "Unrestricted"]
CMD ["-NoExit", "-Command", "& 'C:\\Program Files\\Microsoft Visual Studio 14.0\\Common7\\IDE\\Remote Debugger\\x64\\msvsmon.exe' /silent /noauth /anyuser"]

Просмотреть файл

Просмотреть файл

@ -73,6 +73,13 @@
"logFilePath": "C:\\TEMP\\dd_setup_201601180315_003_Microsoft.VisualStudio.Workload.Office_errors.log",
"description": "Failed to install Microsoft.VisualStudio.Workload.Office"
}
],
"skippedPackages": [
{
"id": "Microsoft.VisualStudio.Component.Sharepoint.Tools",
"version": "15.0.260009.0",
"type": "Component"
}
]
}
}
}

Просмотреть файл

Просмотреть файл

Просмотреть файл

Просмотреть файл

@ -62,6 +62,13 @@ Describe 'Get-VSSetupInstance' {
$instance.InstanceId | Should Be 1
$instance.InstallationVersion | Should Be '15.0.26116.0'
}
It 'Does not contain errors' {
$instance = Get-VSSetupInstance 'C:\VS\Community'
$instance.State -band 'NoErrors' | Should Be 'NoErrors'
$instance.Errors | Should Be $null
}
}
Context 'Contains custom properties' {
@ -86,4 +93,23 @@ Describe 'Get-VSSetupInstance' {
$instance.Properties.Count | Should Be 0
}
}
Context 'Contains errors' {
$instance = Get-VSSetupInstance C:\VS\Enterprise
It 'Contains errors' {
$instance.State -band 'NoErrors' | Should Be 0
$instance.Errors | Should Not Be $null
}
It 'Contains failed packages' {
$instance.Errors.FailedPackages.Count | Should Be 1
$instance.Errors.FailedPackages[0].Id | Should Be 'Microsoft.VisualStudio.Workload.Office'
}
It 'Contains skipped packages' {
$instance.Errors.SkippedPackages.Count | Should Be 1
$instance.Errors.SkippedPackages[0].Id | Should Be 'Microsoft.VisualStudio.Component.Sharepoint.Tools'
}
}
}

Просмотреть файл

Просмотреть файл

@ -8,10 +8,11 @@ setlocal
if "%script%"=="" set script=%~nx0
set projectDir=%~dp0
set solutionDir=%projectDir:~0,-12%
set solutionDir=%projectDir:~0,-7%
set configuration=Debug
set name=vssetup/test
set mode=-it
:parse
if "%1"=="" goto :parse_end
@ -20,6 +21,10 @@ if /i "%1"=="-name" set name=%2& shift& shift& goto :parse
if /i "%1"=="/name" set name=%2& shift& shift& goto :parse
if /i "%1"=="-configuration" set configuration=%2& shift& shift& goto :parse
if /i "%1"=="/configuration" set configuration=%2& shift& shift& goto :parse
if /i "%1"=="-detach" set mode=-d& shift& goto :parse
if /i "%1"=="/detach" set mode=-d& shift& goto :parse
if /i "%1"=="-on" set id=%2& shift& shift& goto :parse
if /i "%1"=="/on" set id=%2& shift& shift& goto :parse
if /i "%1"=="-network" set params=%params% --network "%2"& shift& shift& goto :parse
if /i "%1"=="/network" set params=%params% --network "%2"& shift& shift& goto :parse
if /i "%1"=="-keep" set keep=1& shift& goto :parse
@ -38,19 +43,26 @@ goto :help
if "%keep%"=="" set params=%params% --rm
set outputPath=%solutionDir%src\VSSetup.PowerShell\bin\%configuration%
set volumes=-v "%projectDir%Instances:C:\ProgramData\Microsoft\VisualStudio\Packages\_Instances"
set volumes=-v "%outputPath%:C:\Users\ContainerAdministrator\Documents\WindowsPowerShell\Modules\VSSetup:ro"
set volumes=%volumes% -v "%projectDir%Instances:C:\ProgramData\Microsoft\VisualStudio\Packages\_Instances:ro"
set volumes=%volumes% -v C:\VS\Community
set volumes=%volumes% -v C:\VS\Professional
set volumes=%volumes% -v C:\VS\Enterprise
set volumes=%volumes% -v C:\BuildTools
set volumes=%volumes% -v "%projectDir%Tests:C:\Tests"
set volumes=%volumes% -v "%outputPath%:C:\Users\ContainerAdministrator\Documents\WindowsPowerShell\Modules\VSSetup"
@echo on
docker run -it %volumes%%params% %name% %args%
if "%id%"=="" (
REM Uses the ENTRYPOINT declaration in the Dockerfile
set cmd=docker run %mode% %volumes%%params% %name% %args%
) else (
REM Keep in sync with the ENTRYPOINT in the Dockerfile
set cmd=docker exec %mode% %id% powershell.exe -ExecutionPolicy Unrestricted %args%
)
echo %cmd%
call %cmd%
@if errorlevel 1 exit /b %ERRORLEVEL%
@echo off
echo.
goto :EOF
@ -63,11 +75,13 @@ echo.
echo %usage%
echo.
echo Options:
echo -name Image name. Defaults to vssetup/test.
echo -configuration The build configuration to map. Defaults to Debug.
echo -network External network name. Defaults to discovered transparent network.
echo -keep Do not delete the container after exiting.
echo -? Displays this help message.
echo -name value Image name. Defaults to vssetup/test.
echo -configuration value The build configuration to map. Defaults to Debug.
echo -detach Detach from the container and show the ID.
echo -on value Run command on specified container ID.
echo -network value External network name. Defaults to discovered transparent network.
echo -keep Do not delete the container after exiting.
echo -? Displays this help message.
echo.
if "%noargs%"=="" (
echo Arguments:

Просмотреть файл

Просмотреть файл

@ -0,0 +1,38 @@
// <copyright file="Errors.cs" company="Microsoft Corporation">
// Copyright (C) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE.txt in the project root for license information.
// </copyright>
namespace Microsoft.VisualStudio.Setup
{
using System.Collections.Generic;
using System.Collections.ObjectModel;
using Configuration;
/// <summary>
/// Represents errors that occurred during the last install operation.
/// </summary>
public class Errors
{
private readonly IList<FailedPackageReference> failedPackages;
private readonly IList<PackageReference> skippedPackages;
internal Errors(ISetupErrorState errors)
{
Validate.NotNull(errors, nameof(errors));
FailedPackages = Utilities.TrySetCollection(ref failedPackages, nameof(FailedPackages), errors.GetFailedPackages, PackageReferenceFactory.Create);
SkippedPackages = Utilities.TrySetCollection(ref skippedPackages, nameof(SkippedPackages), errors.GetSkippedPackages, PackageReferenceFactory.Create);
}
/// <summary>
/// Gets a collection of references to packages that failed to install.
/// </summary>
public ReadOnlyCollection<FailedPackageReference> FailedPackages { get; }
/// <summary>
/// Gets a collection of references to packages skipped because other packages in their parent workload or components failed.
/// </summary>
public ReadOnlyCollection<PackageReference> SkippedPackages { get; }
}
}

Просмотреть файл

@ -0,0 +1,26 @@
// <copyright file="FailedPackageReference.cs" company="Microsoft Corporation">
// Copyright (C) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE.txt in the project root for license information.
// </copyright>
namespace Microsoft.VisualStudio.Setup
{
using Configuration;
/// <summary>
/// Represents a failed package reference.
/// </summary>
public class FailedPackageReference : PackageReference
{
/// <summary>
/// Initializes a new instance of the <see cref="FailedPackageReference"/> class.
/// </summary>
/// <param name="reference">The <see cref="ISetupFailedPackageReference"/> to adapt.</param>
/// <exception cref="ArgumentNullException"><paramref name="reference"/> is null.</exception>
internal FailedPackageReference(ISetupFailedPackageReference reference)
: base(reference)
{
Validate.NotNull(reference, nameof(reference));
}
}
}

Просмотреть файл

@ -12,7 +12,6 @@ namespace Microsoft.VisualStudio.Setup
using System.Globalization;
using System.Linq;
using System.Reflection;
using System.Runtime.InteropServices;
using Configuration;
/// <summary>
@ -58,75 +57,101 @@ namespace Microsoft.VisualStudio.Setup
// The instance ID is required, but then try to set other properties to release the COM object and its resources ASAP.
InstanceId = instance.GetInstanceId();
TrySet(ref installationName, nameof(InstallationName), instance.GetInstallationName);
TrySet(ref installationPath, nameof(InstallationPath), instance.GetInstallationPath);
TrySet(ref installationVersion, nameof(InstallationVersion), () =>
{
Version version;
var versionString = instance.GetInstallationVersion();
if (Version.TryParse(versionString, out version))
Utilities.TrySet(ref installationName, nameof(InstallationName), instance.GetInstallationName, OnError);
Utilities.TrySet(ref installationPath, nameof(InstallationPath), instance.GetInstallationPath, OnError);
Utilities.TrySet(
ref installationVersion,
nameof(InstallationVersion),
() =>
{
return version.Normalize();
}
Version version;
return null;
});
var versionString = instance.GetInstallationVersion();
if (Version.TryParse(versionString, out version))
{
return version.Normalize();
}
TrySet(ref installDate, nameof(InstallDate), () =>
{
var ft = instance.GetInstallDate();
var l = ((long)ft.dwHighDateTime << 32) + ft.dwLowDateTime;
return null;
},
OnError);
return DateTime.FromFileTime(l);
});
Utilities.TrySet(
ref installDate,
nameof(InstallDate),
() =>
{
var ft = instance.GetInstallDate();
var l = ((long)ft.dwHighDateTime << 32) + ft.dwLowDateTime;
TrySet(ref state, nameof(State), instance.GetState);
return DateTime.FromFileTime(l);
},
OnError);
Utilities.TrySet(ref state, nameof(State), instance.GetState, OnError);
var lcid = CultureInfo.CurrentUICulture.LCID;
TrySet(ref displayName, nameof(DisplayName), () =>
{
return instance.GetDisplayName(lcid);
});
TrySet(ref description, nameof(Description), () =>
{
return instance.GetDescription(lcid);
});
TrySet(ref productPath, nameof(ProductPath), () =>
{
var path = instance.GetProductPath();
return instance.ResolvePath(path);
});
TrySet(ref product, nameof(Product), () =>
{
var reference = instance.GetProduct();
if (reference != null)
Utilities.TrySet(
ref displayName,
nameof(DisplayName),
() =>
{
return new PackageReference(reference);
}
return instance.GetDisplayName(lcid);
},
OnError);
return null;
});
Utilities.TrySet(
ref description,
nameof(Description),
() =>
{
return instance.GetDescription(lcid);
},
OnError);
TrySet(ref packages, nameof(Packages), () =>
Utilities.TrySet(
ref productPath,
nameof(ProductPath),
() =>
{
var path = instance.GetProductPath();
return instance.ResolvePath(path);
},
OnError);
Utilities.TrySet(
ref product,
nameof(Product),
() =>
{
var reference = instance.GetProduct();
if (reference != null)
{
return new PackageReference(reference);
}
return null;
},
OnError);
Packages = Utilities.TrySetCollection(ref packages, nameof(Packages), instance.GetPackages, PackageReferenceFactory.Create, OnError);
var errors = instance.GetErrors();
if (errors != null)
{
return new List<PackageReference>(GetPackages(instance));
});
if (packages != null && packages.Any())
{
Packages = new ReadOnlyCollection<PackageReference>(packages);
Errors = new Errors(errors);
}
TrySet(ref properties, nameof(Properties), () =>
{
var properties = instance.GetProperties();
return properties?.GetNames()
.ToDictionary(name => name.ToPascalCase(), name => properties.GetValue(name), StringComparer.OrdinalIgnoreCase);
});
Utilities.TrySet(
ref properties,
nameof(Properties),
() =>
{
var properties = instance.GetProperties();
return properties?.GetNames()
.ToDictionary(name => name.ToPascalCase(), name => properties.GetValue(name), StringComparer.OrdinalIgnoreCase);
},
OnError);
if (properties != null)
{
@ -138,9 +163,9 @@ namespace Microsoft.VisualStudio.Setup
Properties = ReadOnlyDictionary<string, object>.Empty;
}
TrySet(ref enginePath, nameof(EnginePath), instance.GetEnginePath);
TrySet(ref isComplete, nameof(IsComplete), instance.IsComplete);
TrySet(ref isLaunchable, nameof(IsLaunchable), instance.IsLaunchable);
Utilities.TrySet(ref enginePath, nameof(EnginePath), instance.GetEnginePath, OnError);
Utilities.TrySet(ref isComplete, nameof(IsComplete), instance.IsComplete, OnError);
Utilities.TrySet(ref isLaunchable, nameof(IsLaunchable), instance.IsLaunchable, OnError);
// Get all properties of the instance not explicitly declared.
var store = (ISetupPropertyStore)instance;
@ -209,6 +234,11 @@ namespace Microsoft.VisualStudio.Setup
/// </summary>
public IDictionary<string, object> Properties { get; }
/// <summary>
/// Gets errors that occurred during the last install (if any).
/// </summary>
public Errors Errors { get; }
/// <summary>
/// Gets the path to the engine that installed the instance.
/// </summary>
@ -229,31 +259,9 @@ namespace Microsoft.VisualStudio.Setup
/// </summary>
internal IDictionary<string, object> AdditionalProperties { get; }
private static IEnumerable<PackageReference> GetPackages(ISetupInstance2 instance)
private void OnError(string propertyName)
{
var references = instance.GetPackages();
if (references != null)
{
foreach (var reference in instance.GetPackages())
{
if (reference != null)
{
yield return new PackageReference(reference);
}
}
}
}
private void TrySet<T>(ref T property, string propertyName, Func<T> action)
{
try
{
property = action.Invoke();
}
catch (COMException ex) when (ex.ErrorCode == NativeMethods.E_NOTFOUND)
{
Trace.WriteLine($@"Instance: property ""{propertyName}"" not found on instance ""{InstanceId}"".");
}
Trace.WriteLine($@"Instance: property ""{propertyName}"" not found on instance ""{InstanceId}"".");
}
}
}

Просмотреть файл

@ -6,8 +6,6 @@
namespace Microsoft.VisualStudio.Setup
{
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using Configuration;
/// <summary>
@ -34,7 +32,7 @@ namespace Microsoft.VisualStudio.Setup
// The package reference ID is required, but then try to set other properties to release the COM object and its resources ASAP.
Id = reference.GetId();
TrySet(ref version, nameof(Version), () =>
Utilities.TrySet(ref version, nameof(Version), () =>
{
Version version;
@ -47,11 +45,11 @@ namespace Microsoft.VisualStudio.Setup
return null;
});
TrySet(ref chip, nameof(Chip), reference.GetChip);
TrySet(ref branch, nameof(Branch), reference.GetBranch);
TrySet(ref type, nameof(Type), reference.GetType);
TrySet(ref isExtension, nameof(IsExtension), reference.GetIsExtension);
TrySet(ref uniqueId, nameof(UniqueId), reference.GetUniqueId);
Utilities.TrySet(ref chip, nameof(Chip), reference.GetChip);
Utilities.TrySet(ref branch, nameof(Branch), reference.GetBranch);
Utilities.TrySet(ref type, nameof(Type), reference.GetType);
Utilities.TrySet(ref isExtension, nameof(IsExtension), reference.GetIsExtension);
Utilities.TrySet(ref uniqueId, nameof(UniqueId), reference.GetUniqueId);
}
/// <summary>
@ -97,17 +95,5 @@ namespace Microsoft.VisualStudio.Setup
{
return uniqueId ?? base.ToString();
}
private void TrySet<T>(ref T property, string propertyName, Func<T> action)
{
try
{
property = action.Invoke();
}
catch (COMException ex) when (ex.ErrorCode == NativeMethods.E_NOTFOUND)
{
Trace.WriteLine($@"Instance: property ""{propertyName}"" not found on package reference ""{Id}"".");
}
}
}
}

Просмотреть файл

@ -0,0 +1,41 @@
// <copyright file="PackageReferenceFactory.cs" company="Microsoft Corporation">
// Copyright (C) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE.txt in the project root for license information.
// </copyright>
namespace Microsoft.VisualStudio.Setup
{
using Configuration;
/// <summary>
/// Creates <see cref="PackageReference"/> or derivative classes.
/// </summary>
internal static class PackageReferenceFactory
{
/// <summary>
/// Creates a new <see cref="PackageReference"/> from an <see cref="ISetupPackageReference"/>.
/// </summary>
/// <param name="package">The <see cref="ISetupPackageReference"/> to wrap.</param>
/// <returns>A <see cref="PackageReference"/> that wraps the <see cref="ISetupPackageReference"/>.</returns>
/// <exception cref="ArgumentNullException"><paramref name="package"/> is null.</exception>
public static PackageReference Create(ISetupPackageReference package)
{
Validate.NotNull(package, nameof(package));
return new PackageReference(package);
}
/// <summary>
/// Creates a new <see cref="FailedPackageReference"/> from an <see cref="ISetupFailedPackageReference"/>.
/// </summary>
/// <param name="package">The <see cref="ISetupFailedPackageReference"/> to wrap.</param>
/// <returns>A <see cref="FailedPackageReference"/> that wraps the <see cref="ISetupFailedPackageReference"/>.</returns>
/// <exception cref="ArgumentNullException"><paramref name="package"/> is null.</exception>
public static FailedPackageReference Create(ISetupFailedPackageReference package)
{
Validate.NotNull(package, nameof(package));
return new FailedPackageReference(package);
}
}
}

Просмотреть файл

@ -0,0 +1,131 @@
// <copyright file="Utilities.cs" company="Microsoft Corporation">
// Copyright (C) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE.txt in the project root for license information.
// </copyright>
namespace Microsoft.VisualStudio.Setup
{
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Runtime.InteropServices;
using Configuration;
/// <summary>
/// Utility methods.
/// </summary>
internal static class Utilities
{
/// <summary>
/// Gets an empty <see cref="ReadOnlyCollection{T}"/>.
/// </summary>
/// <typeparam name="T">The type of element.</typeparam>
/// <returns>An empty <see cref="ReadOnlyCollection{T}"/>.</returns>
public static ReadOnlyCollection<T> EmptyReadOnlyCollection<T>()
{
return EmptyReadOnlyCollectionContainer<T>.Instance;
}
/// <summary>
/// Gets an <see cref="IEnumerable{T}"/> of adapted packages from type <typeparamref name="T"/> to type <typeparamref name="R"/>.
/// </summary>
/// <typeparam name="T">The type of the package reference to adapt.</typeparam>
/// <typeparam name="R">The adapted type of the package reference.</typeparam>
/// <param name="action">A method that gets an <see cref="IEnumerable{T}"/> of package reference of type <typeparamref name="T"/>.</param>
/// <param name="creator">A method that creates an adapted reference of type <typeparamref name="R"/>.</param>
/// <returns>An <see cref="IEnumerable{T}"/> of adapted packages. This enumeration may yield zero results.</returns>
/// <exception cref="ArgumentNullException"><paramref name="action"/> or <paramref name="creator"/> is null.</exception>
public static IEnumerable<R> GetAdaptedPackages<T, R>(Func<IEnumerable<T>> action, Func<T, R> creator)
where T : ISetupPackageReference
where R : PackageReference
{
Validate.NotNull(action, nameof(action));
Validate.NotNull(creator, nameof(creator));
return YieldAdaptedPackages(action, creator);
}
/// <summary>
/// Sets the given <paramref name="property"/> if the <paramref name="action"/> does not throw a <see cref="COMException"/> for 0x80070490.
/// </summary>
/// <typeparam name="T">The type of the property to set.</typeparam>
/// <param name="property">A reference to the property to set.</param>
/// <param name="propertyName">The name of the property for diagnostic purposes.</param>
/// <param name="action">A method that returns the value of the property to set.</param>
/// <param name="error">Optional error handler that accepts the name of the property.</param>
/// <exception cref="ArgumentException"><paramref name="propertyName"/> is an empty string.</exception>
/// <exception cref="ArgumentNullException"><paramref name="propertyName"/> or <paramref name="action"/> is null.</exception>
public static void TrySet<T>(ref T property, string propertyName, Func<T> action, Action<string> error = null)
{
Validate.NotNullOrEmpty(propertyName, nameof(propertyName));
Validate.NotNull(action, nameof(action));
try
{
property = action.Invoke();
}
catch (COMException ex) when (ex.ErrorCode == NativeMethods.E_NOTFOUND)
{
error?.Invoke(propertyName);
}
}
/// <summary>
/// Sets the given package reference collection <paramref name="property"/> if the <paramref name="action"/> does not throw a <see cref="COMException"/> for 0x80070490.
/// </summary>
/// <typeparam name="T">The type of the package reference to adapt.</typeparam>
/// <typeparam name="R">The adapted type of the package reference.</typeparam>
/// <param name="property">A reference to the property to set.</param>
/// <param name="propertyName">The name of the property for diagnostic purposes.</param>
/// <param name="action">A method that returns the value of the property to set.</param>
/// <param name="creator">A method that creates the adapted reference type.</param>
/// <param name="error">Optional error handler that accepts the name of the property.</param>
/// <returns>A <see cref="ReadOnlyCollection{T}"/> containing the adapted package references. This collection may be empty.</returns>
/// <exception cref="ArgumentException"><paramref name="propertyName"/> is an empty string.</exception>
/// <exception cref="ArgumentNullException">One or more parameters is null.</exception>
public static ReadOnlyCollection<R> TrySetCollection<T, R>(
ref IList<R> property,
string propertyName,
Func<IEnumerable<T>> action,
Func<T, R> creator,
Action<string> error = null)
where T : ISetupPackageReference
where R : PackageReference
{
Validate.NotNullOrEmpty(propertyName, nameof(propertyName));
Validate.NotNull(action, nameof(action));
Validate.NotNull(creator, nameof(creator));
var packages = GetAdaptedPackages(action, creator);
TrySet(ref property, propertyName, packages.ToList, error);
if (property != null && property.Any())
{
return new ReadOnlyCollection<R>(property);
}
return EmptyReadOnlyCollection<R>();
}
private static IEnumerable<R> YieldAdaptedPackages<T, R>(Func<IEnumerable<T>> action, Func<T, R> creator)
{
var references = action?.Invoke();
if (references != null)
{
foreach (var reference in references)
{
if (reference != null)
{
yield return creator(reference);
}
}
}
}
private class EmptyReadOnlyCollectionContainer<T>
{
public static readonly ReadOnlyCollection<T> Instance = new ReadOnlyCollection<T>(new T[0]);
}
}
}

Просмотреть файл

@ -35,7 +35,7 @@
</PropertyGroup>
<ItemGroup>
<Reference Include="Microsoft.VisualStudio.Setup.Configuration.Interop, Version=1.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\..\packages\Microsoft.VisualStudio.Setup.Configuration.Interop.1.5.125-rc\lib\net35\Microsoft.VisualStudio.Setup.Configuration.Interop.dll</HintPath>
<HintPath>..\..\packages\Microsoft.VisualStudio.Setup.Configuration.Interop.1.8.24\lib\net35\Microsoft.VisualStudio.Setup.Configuration.Interop.dll</HintPath>
<EmbedInteropTypes>True</EmbedInteropTypes>
</Reference>
<Reference Include="System" />
@ -47,10 +47,13 @@
<Reference Include="Microsoft.CSharp" />
</ItemGroup>
<ItemGroup>
<Compile Include="Errors.cs" />
<Compile Include="Extensions.cs" />
<Compile Include="FailedPackageReference.cs" />
<Compile Include="Instance.cs" />
<Compile Include="NativeMethods.cs" />
<Compile Include="PackageReference.cs" />
<Compile Include="PackageReferenceFactory.cs" />
<Compile Include="QueryFactory.cs" />
<Compile Include="PowerShell\Extensions.cs" />
<Compile Include="PowerShell\GetInstanceCommand.cs" />
@ -64,6 +67,7 @@
<DependentUpon>Resources.resx</DependentUpon>
</Compile>
<Compile Include="ReadOnlyDictionary.cs" />
<Compile Include="Utilities.cs" />
<Compile Include="Validate.cs" />
</ItemGroup>
<ItemGroup>

Просмотреть файл

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Microsoft.PowerShell.3.ReferenceAssemblies" version="1.0.0" targetFramework="net40-client" />
<package id="Microsoft.VisualStudio.Setup.Configuration.Interop" version="1.5.125-rc" targetFramework="net40-client" developmentDependency="true" />
<package id="Microsoft.VisualStudio.Setup.Configuration.Interop" version="1.8.24" targetFramework="net40-client" developmentDependency="true" />
<package id="Nerdbank.GitVersioning" version="1.5.62" targetFramework="net40-client" developmentDependency="true" />
<package id="NuProj.Common" version="0.11.14-beta" targetFramework="net40-client" developmentDependency="true" />
<package id="StyleCop.Analyzers" version="1.0.0" targetFramework="net40-client" developmentDependency="true" />

Просмотреть файл

@ -0,0 +1,86 @@
// <copyright file="ErrorsTests.cs" company="Microsoft Corporation">
// Copyright (C) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE.txt in the project root for license information.
// </copyright>
namespace Microsoft.VisualStudio.Setup
{
using System;
using System.Runtime.InteropServices;
using Configuration;
using Moq;
using Xunit;
public class ErrorsTests
{
[Fact]
public void New_Errors_Null_Throws()
{
Assert.Throws<ArgumentNullException>("errors", () => new Errors(null));
}
[Fact]
public void New_Missing_FailedPackages()
{
var errors = new Mock<ISetupErrorState>();
errors.Setup(x => x.GetFailedPackages()).Throws(new COMException("Not found", NativeMethods.E_NOTFOUND));
var sut = new Errors(errors.Object);
Assert.Empty(sut.FailedPackages);
}
[Fact]
public void New_FailedPackages()
{
var a = new Mock<ISetupFailedPackageReference>();
a.As<ISetupPackageReference>().Setup(x => x.GetId()).Returns("a");
var b = new Mock<ISetupFailedPackageReference>();
b.As<ISetupPackageReference>().Setup(x => x.GetId()).Returns("b");
var errors = new Mock<ISetupErrorState>();
errors.Setup(x => x.GetFailedPackages()).Returns(new[] { a.Object, b.Object });
var sut = new Errors(errors.Object);
Assert.NotNull(sut.FailedPackages);
Assert.Collection(
sut.FailedPackages,
x => Assert.Equal("a", x.Id),
x => Assert.Equal("b", x.Id));
}
[Fact]
public void New_Missing_SkippedPackages()
{
var errors = new Mock<ISetupErrorState>();
errors.Setup(x => x.GetSkippedPackages()).Throws(new COMException("Not found", NativeMethods.E_NOTFOUND));
var sut = new Errors(errors.Object);
Assert.Empty(sut.SkippedPackages);
}
[Fact]
public void New_SkippedPackages()
{
var skippedPackages = new[]
{
Mock.Of<ISetupPackageReference>(x => x.GetId() == "a"),
Mock.Of<ISetupPackageReference>(x => x.GetId() == "b"),
};
var errors = new Mock<ISetupErrorState>();
errors.Setup(x => x.GetSkippedPackages()).Returns(skippedPackages);
var sut = new Errors(errors.Object);
Assert.NotNull(sut.SkippedPackages);
Assert.Collection(
sut.SkippedPackages,
x => Assert.Equal("a", x.Id),
x => Assert.Equal("b", x.Id));
}
}
}

Просмотреть файл

@ -0,0 +1,31 @@
// <copyright file="FailedPackageReferenceTests.cs" company="Microsoft Corporation">
// Copyright (C) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE.txt in the project root for license information.
// </copyright>
namespace Microsoft.VisualStudio.Setup
{
using System;
using Configuration;
using Moq;
using Xunit;
public class FailedPackageReferenceTests
{
[Fact]
public void New_Reference_Null_Throws()
{
Assert.Throws<ArgumentNullException>("reference", () => new FailedPackageReference(null));
}
[Fact]
public void New_Valid()
{
var reference = new Mock<ISetupFailedPackageReference>();
reference.As<ISetupPackageReference>().Setup(x => x.GetId()).Returns("a");
var sut = new FailedPackageReference(reference.Object);
Assert.Equal("a", sut.Id);
}
}
}

Просмотреть файл

@ -106,7 +106,7 @@ namespace Microsoft.VisualStudio.Setup
instance.Setup(x => x.GetInstanceId()).Returns("test");
var sut = new Instance(instance.Object);
Assert.Null(sut.Packages);
Assert.Empty(sut.Packages);
}
[Fact]
@ -116,7 +116,7 @@ namespace Microsoft.VisualStudio.Setup
instance.Setup(x => x.GetPackages()).Returns(Enumerable.Empty<ISetupPackageReference>().ToArray());
var sut = new Instance(instance.Object);
Assert.Null(sut.Packages);
Assert.Empty(sut.Packages);
}
[Fact]
@ -152,5 +152,63 @@ namespace Microsoft.VisualStudio.Setup
Assert.Equal(2, sut.AdditionalProperties["B"]);
Assert.Equal(2, sut.AdditionalProperties["b"]);
}
[Fact]
public void New_No_Errors()
{
instance.Setup(x => x.GetInstanceId()).Returns("test");
var sut = new Instance(instance.Object);
Assert.Null(sut.Errors);
}
[Fact]
public void New_FailedPackages()
{
var a = new Mock<ISetupFailedPackageReference>();
a.As<ISetupPackageReference>().Setup(x => x.GetId()).Returns("a");
var b = new Mock<ISetupFailedPackageReference>();
b.As<ISetupPackageReference>().Setup(x => x.GetId()).Returns("b");
var errors = new Mock<ISetupErrorState>();
errors.Setup(x => x.GetFailedPackages()).Returns(new[] { a.Object, b.Object });
instance.Setup(x => x.GetInstanceId()).Returns("test");
instance.Setup(x => x.GetErrors()).Returns(errors.Object);
var sut = new Instance(instance.Object);
Assert.NotNull(sut.Errors);
Assert.Collection(
sut.Errors.FailedPackages,
x => Assert.Equal("a", x.Id),
x => Assert.Equal("b", x.Id));
}
[Fact]
public void New_SkippedPackages()
{
var skippedPackages = new[]
{
Mock.Of<ISetupPackageReference>(x => x.GetId() == "a"),
Mock.Of<ISetupPackageReference>(x => x.GetId() == "b"),
};
var errors = new Mock<ISetupErrorState>();
errors.Setup(x => x.GetSkippedPackages()).Returns(skippedPackages);
instance.Setup(x => x.GetInstanceId()).Returns("test");
instance.Setup(x => x.GetErrors()).Returns(errors.Object);
var sut = new Instance(instance.Object);
Assert.NotNull(sut.Errors);
Assert.Collection(
sut.Errors.SkippedPackages,
x => Assert.Equal("a", x.Id),
x => Assert.Equal("b", x.Id));
}
}
}

Просмотреть файл

@ -0,0 +1,47 @@
// <copyright file="PackageReferenceFactoryTests.cs" company="Microsoft Corporation">
// Copyright (C) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE.txt in the project root for license information.
// </copyright>
namespace Microsoft.VisualStudio.Setup
{
using System;
using Configuration;
using Moq;
using Xunit;
public class PackageReferenceFactoryTests
{
[Fact]
public void Create_PackageReference_Null_Throws()
{
Assert.Throws<ArgumentNullException>("package", () => PackageReferenceFactory.Create((ISetupPackageReference)null));
}
[Fact]
public void Create_PackageReference()
{
var reference = Mock.Of<ISetupPackageReference>(x => x.GetId() == "a");
var actual = PackageReferenceFactory.Create(reference);
Assert.IsType<PackageReference>(actual);
}
[Fact]
public void Create_FailedPackageReference_Null_Throws()
{
Assert.Throws<ArgumentNullException>("package", () => PackageReferenceFactory.Create((ISetupFailedPackageReference)null));
}
[Fact]
public void Create_FailedPackageReference()
{
var reference = new Mock<ISetupFailedPackageReference>();
reference.As<ISetupPackageReference>().Setup(x => x.GetId()).Returns("a");
var actual = PackageReferenceFactory.Create(reference.Object);
Assert.IsType<FailedPackageReference>(actual);
}
}
}

Просмотреть файл

@ -0,0 +1,233 @@
// <copyright file="UtilitiesTests.cs" company="Microsoft Corporation">
// Copyright (C) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE.txt in the project root for license information.
// </copyright>
namespace Microsoft.VisualStudio.Setup
{
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using Configuration;
using Moq;
using Xunit;
public class UtilitiesTests
{
[Fact]
public void EmptyReadOnlyCollection_Empty()
{
Assert.Empty(Utilities.EmptyReadOnlyCollection<PackageReference>());
}
[Fact]
public void EmptyReadOnlyCollection_Singleton()
{
Assert.Same(Utilities.EmptyReadOnlyCollection<PackageReference>(), Utilities.EmptyReadOnlyCollection<PackageReference>());
}
[Fact]
public void GetAdaptedProperties_Action_Null_Throws()
{
Assert.Throws<ArgumentNullException>("action", () => Utilities.GetAdaptedPackages<ISetupPackageReference, PackageReference>(null, null));
}
[Fact]
public void GetAdaptedProperties_Creator_Null_Throws()
{
var references = new[]
{
Mock.Of<ISetupPackageReference>(x => x.GetId() == "a"),
Mock.Of<ISetupPackageReference>(x => x.GetId() == "b"),
};
Assert.Throws<ArgumentNullException>("creator", () => Utilities.GetAdaptedPackages<ISetupPackageReference, PackageReference>(() => references, null));
}
[Fact]
public void GetAdaptedProperties_Empty()
{
var references = new ISetupPackageReference[0];
Func<ISetupPackageReference, PackageReference> creator = reference => PackageReferenceFactory.Create(reference);
var packages = Utilities.GetAdaptedPackages(() => references, creator).ToList();
Assert.Empty(packages);
}
[Fact]
public void GetAdaptedProperties()
{
var references = new[]
{
Mock.Of<ISetupPackageReference>(x => x.GetId() == "a"),
Mock.Of<ISetupPackageReference>(x => x.GetId() == "b"),
};
Func<ISetupPackageReference, PackageReference> creator = reference => PackageReferenceFactory.Create(reference);
var packages = Utilities.GetAdaptedPackages(() => references, creator).ToList();
Assert.Collection(
packages,
x => Assert.Equal("a", x.Id),
x => Assert.Equal("b", x.Id));
}
[Fact]
public void TrySet_PropertyName_Null_Throws()
{
string property = null;
Assert.Throws<ArgumentNullException>("propertyName", () => Utilities.TrySet(ref property, null, null));
}
[Fact]
public void TrySet_PropertyName_Empty_Throws()
{
string property = null;
Assert.Throws<ArgumentException>("propertyName", () => Utilities.TrySet(ref property, string.Empty, null));
}
[Fact]
public void TrySet_Action_Null_Throws()
{
string property = null;
Assert.Throws<ArgumentNullException>("action", () => Utilities.TrySet(ref property, nameof(property), null));
}
[Fact]
public void TrySet_COMException_E_NOTFOUND()
{
string property = null;
Utilities.TrySet(ref property, nameof(property), () => { throw new COMException("Not found", NativeMethods.E_NOTFOUND); });
Assert.Null(property);
}
[Fact]
public void TrySet_COMException_REGDB_E_CLASSNOTREG()
{
string property = null;
var ex = Assert.Throws<COMException>(() => Utilities.TrySet(ref property, nameof(property), () => { throw new COMException("Not registered", NativeMethods.REGDB_E_CLASSNOTREG); }));
Assert.Equal(NativeMethods.REGDB_E_CLASSNOTREG, ex.ErrorCode);
}
[Fact]
public void TrySet()
{
string property = null;
Utilities.TrySet(ref property, nameof(property), () => { return "test"; });
Assert.Equal("test", property);
}
[Fact]
public void TrySet_Error_Callback()
{
string property = null;
var called = false;
Utilities.TrySet(
ref property,
nameof(property),
() => { throw new COMException("Not found", NativeMethods.E_NOTFOUND); },
propertyName => called = true);
Assert.Null(property);
Assert.True(called);
}
[Fact]
public void TrySetCollection_PropertyName_Null_Throws()
{
IList<PackageReference> property = null;
Assert.Throws<ArgumentNullException>("propertyName", () => Utilities.TrySetCollection<ISetupPackageReference, PackageReference>(ref property, null, null, null));
}
[Fact]
public void TrySetCollection_PropertyName_Empty_Throws()
{
IList<PackageReference> property = null;
Assert.Throws<ArgumentException>("propertyName", () => Utilities.TrySetCollection<ISetupPackageReference, PackageReference>(ref property, string.Empty, null, null));
}
[Fact]
public void TrySetCollection_Action_Null_Throws()
{
IList<PackageReference> property = null;
Assert.Throws<ArgumentNullException>("action", () => Utilities.TrySetCollection<ISetupPackageReference, PackageReference>(ref property, nameof(property), null, null));
}
[Fact]
public void TrySetCollection_Creator_Null_Throws()
{
IList<PackageReference> property = null;
var references = new[]
{
Mock.Of<ISetupPackageReference>(x => x.GetId() == "a"),
Mock.Of<ISetupPackageReference>(x => x.GetId() == "b"),
};
Assert.Throws<ArgumentNullException>("creator", () => Utilities.TrySetCollection(ref property, nameof(property), () => references, null));
}
[Fact]
public void TrySetCollection_Empty()
{
IList<PackageReference> property = null;
var references = new ISetupPackageReference[0];
Func<ISetupPackageReference, PackageReference> creator = reference => PackageReferenceFactory.Create(reference);
var packages = Utilities.TrySetCollection(ref property, nameof(property), () => references, creator);
Assert.Empty(packages);
}
[Fact]
public void TrySetCollection()
{
IList<PackageReference> property = null;
var references = new[]
{
Mock.Of<ISetupPackageReference>(x => x.GetId() == "a"),
Mock.Of<ISetupPackageReference>(x => x.GetId() == "b"),
};
Func<ISetupPackageReference, PackageReference> creator = reference => PackageReferenceFactory.Create(reference);
var packages = Utilities.TrySetCollection(ref property, nameof(property), () => references, creator);
Assert.Collection(
packages,
x => Assert.Equal("a", x.Id),
x => Assert.Equal("b", x.Id));
}
[Fact]
public void TrySetCollection_Error_Callback()
{
IList<PackageReference> property = null;
var called = false;
var references = new[]
{
Mock.Of<ISetupPackageReference>(x => x.GetId() == "a"),
Mock.Of<ISetupPackageReference>(x => x.GetId() == "b"),
};
Func<ISetupPackageReference, PackageReference> creator = reference => PackageReferenceFactory.Create(reference);
var packages = Utilities.TrySetCollection(
ref property,
nameof(property),
() => { throw new COMException("Not found", NativeMethods.E_NOTFOUND); },
creator,
propertyName => called = true);
Assert.Null(property);
Assert.Empty(packages);
Assert.True(called);
}
}
}

Просмотреть файл

@ -46,7 +46,7 @@
<Private>True</Private>
</Reference>
<Reference Include="Microsoft.VisualStudio.Setup.Configuration.Interop, Version=1.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\..\packages\Microsoft.VisualStudio.Setup.Configuration.Interop.1.5.125-rc\lib\net35\Microsoft.VisualStudio.Setup.Configuration.Interop.dll</HintPath>
<HintPath>..\..\packages\Microsoft.VisualStudio.Setup.Configuration.Interop.1.8.24\lib\net35\Microsoft.VisualStudio.Setup.Configuration.Interop.dll</HintPath>
<EmbedInteropTypes>False</EmbedInteropTypes>
</Reference>
<Reference Include="Moq, Version=4.5.30.0, Culture=neutral, PublicKeyToken=69f491c39445e920, processorArchitecture=MSIL">
@ -78,13 +78,17 @@
</Reference>
</ItemGroup>
<ItemGroup>
<Compile Include="ErrorsTests.cs" />
<Compile Include="ExtensionsTests.cs" />
<Compile Include="FailedPackageReferenceTests.cs" />
<Compile Include="InstanceTests.cs" />
<Compile Include="PackageReferenceFactoryTests.cs" />
<Compile Include="PackageReferenceTests.cs" />
<Compile Include="PowerShell\ExtensionsTests.cs" />
<Compile Include="PowerShell\SelectInstanceCommandTests.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="ReadOnlyDictionaryTests.cs" />
<Compile Include="UtilitiesTests.cs" />
<Compile Include="ValidateTests.cs" />
</ItemGroup>
<ItemGroup>

Просмотреть файл

@ -2,7 +2,7 @@
<packages>
<package id="Castle.Core" version="3.3.3" targetFramework="net45" />
<package id="Microsoft.PowerShell.3.ReferenceAssemblies" version="1.0.0" targetFramework="net45" />
<package id="Microsoft.VisualStudio.Setup.Configuration.Interop" version="1.5.125-rc" targetFramework="net45" />
<package id="Microsoft.VisualStudio.Setup.Configuration.Interop" version="1.8.24" targetFramework="net45" />
<package id="Moq" version="4.5.30" targetFramework="net45" />
<package id="Nerdbank.GitVersioning" version="1.5.62" targetFramework="net40-client" developmentDependency="true" requireReinstallation="true" />
<package id="StyleCop.Analyzers" version="1.0.0" targetFramework="net40-client" developmentDependency="true" />

Просмотреть файл

@ -1,6 +1,6 @@
{
"$schema": "https://raw.githubusercontent.com/AArnott/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json",
"version": "1.0.0-rc",
"version": "1.0.0",
"publicReleaseRefSpec": [
"^refs/heads/master$",
"^refs/tags/v\\d\\.\\d"