Add Enable-ExperimentalFeature and Disable-ExperimentalFeature cmdlets (#8318)

[breaking change]
Major changes are as follows:
- Add `Enable-ExperimentalFeature` and `Disable-ExperimentalFeature` cmdlets.
- Remove `-ListAvailable` from `Get-ExperimentalFeature`.
- Add `ArgumentCompleter` for `Get-ExperimentalFeature` cmdlet.
- Refactor some existing Experimental Feature tests.
- Make `ConfigScope` public and renamed `SystemWide` to `AllUsers`. Also update experimental feature code to prefer the current user config over the all user config.
This commit is contained in:
Steve Lee
2018-12-05 12:20:58 -08:00
committed by Dongbo Wang
parent 74de391193
commit 52aef57ab6
14 changed files with 552 additions and 276 deletions
@@ -0,0 +1,131 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
using System;
using System.Collections;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Management.Automation;
using System.Management.Automation.Configuration;
using System.Management.Automation.Internal;
using System.Management.Automation.Language;
namespace Microsoft.PowerShell.Commands
{
/// <summary>
/// Base class for Enable/Disable-ExperimentalFeature cmdlet.
/// </summary>
public class EnableDisableExperimentalFeatureCommandBase : PSCmdlet
{
/// <summary>
/// Gets or sets the feature names.
/// </summary>
[Parameter(ValueFromPipelineByPropertyName = true, Position = 0, Mandatory = true)]
[ArgumentCompleter(typeof(ExperimentalFeatureNameCompleter))]
public string[] Name { get; set; }
/// <summary>
/// Gets or sets the scope of persistence of updating the PowerShell configuration json.
/// </summary>
[Parameter]
public ConfigScope Scope { get; set; } = ConfigScope.CurrentUser;
}
/// <summary>
/// Implements Enable-ExperimentalFeature cmdlet.
/// </summary>
[Cmdlet(VerbsLifecycle.Enable, "ExperimentalFeature", SupportsShouldProcess = true, HelpUri = "https://go.microsoft.com/fwlink/?LinkID=2046964")]
public class EnableExperimentalFeatureCommand : EnableDisableExperimentalFeatureCommandBase
{
/// <summary>
/// ProcessRecord method of this cmdlet.
/// </summary>
protected override void ProcessRecord()
{
ExperimentalFeatureConfigHelper.UpdateConfig(this, Name, Scope, enable: true);
}
}
/// <summary>
/// Implements Enable-ExperimentalFeature cmdlet.
/// </summary>
[Cmdlet(VerbsLifecycle.Disable, "ExperimentalFeature", SupportsShouldProcess = true, HelpUri = "https://go.microsoft.com/fwlink/?LinkID=2046963")]
public class DisableExperimentalFeatureCommand : EnableDisableExperimentalFeatureCommandBase
{
/// <summary>
/// ProcessRecord method of this cmdlet.
/// </summary>
protected override void ProcessRecord()
{
ExperimentalFeatureConfigHelper.UpdateConfig(this, Name, Scope, enable: false);
}
}
internal class ExperimentalFeatureConfigHelper
{
internal static void UpdateConfig(PSCmdlet cmdlet, string[] name, ConfigScope scope, bool enable)
{
IEnumerable<WildcardPattern> namePatterns = SessionStateUtilities.CreateWildcardsFromStrings(name, WildcardOptions.IgnoreCase | WildcardOptions.CultureInvariant);
GetExperimentalFeatureCommand getExperimentalFeatureCommand = new GetExperimentalFeatureCommand();
getExperimentalFeatureCommand.Context = cmdlet.Context;
bool foundFeature = false;
foreach (ExperimentalFeature feature in getExperimentalFeatureCommand.GetAvailableExperimentalFeatures(namePatterns))
{
foundFeature = true;
if (!cmdlet.ShouldProcess(feature.Name))
{
return;
}
PowerShellConfig.Instance.SetExperimentalFeatures(scope, feature.Name, enable);
}
if (!foundFeature)
{
string errMsg = string.Format(CultureInfo.InvariantCulture, ExperimentalFeatureStrings.ExperimentalFeatureNameNotFound, name);
cmdlet.WriteError(new ErrorRecord(new ItemNotFoundException(errMsg), "ItemNotFoundException", ErrorCategory.ObjectNotFound, name));
return;
}
cmdlet.WriteWarning(ExperimentalFeatureStrings.ExperimentalFeaturePending);
}
}
/// <summary>
/// Provides argument completion for ExperimentalFeature names.
/// </summary>
public class ExperimentalFeatureNameCompleter : IArgumentCompleter
{
/// <summary>
/// Returns completion results for experimental feature names used as arguments to experimental feature cmdlets.
/// </summary>
/// <param name="commandName">The command name.</param>
/// <param name="parameterName">The parameter name.</param>
/// <param name="wordToComplete">The word to complete.</param>
/// <param name="commandAst">The command AST.</param>
/// <param name="fakeBoundParameters">The fake bound parameters.</param>
/// <returns>List of Completion Results.</returns>
public IEnumerable<CompletionResult> CompleteArgument(string commandName, string parameterName, string wordToComplete, CommandAst commandAst, IDictionary fakeBoundParameters)
{
if (fakeBoundParameters == null)
{
throw PSTraceSource.NewArgumentNullException(nameof(fakeBoundParameters));
}
var commandInfo = new CmdletInfo("Get-ExperimentalFeature", typeof(GetExperimentalFeatureCommand));
var ps = System.Management.Automation.PowerShell.Create(RunspaceMode.CurrentRunspace)
.AddCommand(commandInfo)
.AddParameter("Name", wordToComplete + "*");
HashSet<string> names = new HashSet<string>();
var results = ps.Invoke<ExperimentalFeature>();
foreach (var result in results)
{
names.Add(result.Name);
}
return names.OrderBy(name => name).Select(name => new CompletionResult(name, name, CompletionResultType.Text, name));
}
}
}
@@ -20,15 +20,10 @@ namespace Microsoft.PowerShell.Commands
/// Get and set the feature names.
/// </summary>
[Parameter(ValueFromPipeline = true, Position = 0)]
[ArgumentCompleter(typeof(ExperimentalFeatureNameCompleter))]
[ValidateNotNullOrEmpty]
public string[] Name { get; set; }
/// <summary>
/// Get and set the switch flag to search module paths to find all available experimental features.
/// </summary>
[Parameter]
public SwitchParameter ListAvailable { get; set; }
/// <summary>
/// ProcessRecord method of this cmdlet.
/// </summary>
@@ -37,19 +32,9 @@ namespace Microsoft.PowerShell.Commands
const WildcardOptions wildcardOptions = WildcardOptions.IgnoreCase | WildcardOptions.CultureInvariant;
IEnumerable<WildcardPattern> namePatterns = SessionStateUtilities.CreateWildcardsFromStrings(Name, wildcardOptions);
if (ListAvailable)
foreach (ExperimentalFeature feature in GetAvailableExperimentalFeatures(namePatterns).OrderBy(GetSortingString))
{
foreach (ExperimentalFeature feature in GetAvailableExperimentalFeatures(namePatterns).OrderBy(GetSortingString))
{
WriteObject(feature);
}
}
else if (ExperimentalFeature.EnabledExperimentalFeatureNames.Count > 0)
{
foreach (ExperimentalFeature feature in GetEnabledExperimentalFeatures(namePatterns).OrderBy(GetSortingString))
{
WriteObject(feature);
}
WriteObject(feature);
}
}
@@ -67,61 +52,10 @@ namespace Microsoft.PowerShell.Commands
: (1, feature.Name);
}
/// <summary>
/// Get enabled experimental features based on the specified name patterns.
/// </summary>
private IEnumerable<ExperimentalFeature> GetEnabledExperimentalFeatures(IEnumerable<WildcardPattern> namePatterns)
{
var moduleFeatures = new List<string>();
var moduleNames = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
foreach (string featureName in ExperimentalFeature.EnabledExperimentalFeatureNames)
{
// Only process the feature names that matches any name patterns.
if (SessionStateUtilities.MatchesAnyWildcardPattern(featureName, namePatterns, defaultValue: true))
{
if (ExperimentalFeature.EngineExperimentalFeatureMap.TryGetValue(featureName, out ExperimentalFeature feature))
{
yield return feature;
}
else
{
moduleFeatures.Add(featureName);
int lastDotIndex = featureName.LastIndexOf('.');
moduleNames.Add(featureName.Substring(0, lastDotIndex));
}
}
}
if (moduleFeatures.Count > 0)
{
var featuresFromGivenModules = new Dictionary<string, ExperimentalFeature>(StringComparer.OrdinalIgnoreCase);
foreach (string moduleFile in GetValidModuleFiles(moduleNames))
{
foreach (var feature in ModuleIntrinsics.GetExperimentalFeature(moduleFile))
{
featuresFromGivenModules.TryAdd(feature.Name, feature);
}
}
foreach (string featureName in moduleFeatures)
{
if (featuresFromGivenModules.TryGetValue(featureName, out ExperimentalFeature feature))
{
yield return feature;
}
else
{
yield return new ExperimentalFeature(featureName, description: null, source: null, isEnabled: true);
}
}
}
}
/// <summary>
/// Get available experimental features based on the specified name patterns.
/// </summary>
private IEnumerable<ExperimentalFeature> GetAvailableExperimentalFeatures(IEnumerable<WildcardPattern> namePatterns)
internal IEnumerable<ExperimentalFeature> GetAvailableExperimentalFeatures(IEnumerable<WildcardPattern> namePatterns)
{
foreach (ExperimentalFeature feature in ExperimentalFeature.EngineExperimentalFeatures)
{
@@ -5103,76 +5103,78 @@ end
{
cmdlets = new Dictionary<string, SessionStateCmdletEntry>(StringComparer.OrdinalIgnoreCase)
{
{"Add-History", new SessionStateCmdletEntry("Add-History", typeof(AddHistoryCommand), helpFile) },
{"Clear-History", new SessionStateCmdletEntry("Clear-History", typeof(ClearHistoryCommand), helpFile) },
{"Debug-Job", new SessionStateCmdletEntry("Debug-Job", typeof(DebugJobCommand), helpFile) },
{ "Add-History", new SessionStateCmdletEntry("Add-History", typeof(AddHistoryCommand), helpFile) },
{ "Clear-History", new SessionStateCmdletEntry("Clear-History", typeof(ClearHistoryCommand), helpFile) },
{ "Debug-Job", new SessionStateCmdletEntry("Debug-Job", typeof(DebugJobCommand), helpFile) },
#if !UNIX
{"Disable-PSRemoting", new SessionStateCmdletEntry("Disable-PSRemoting", typeof(DisablePSRemotingCommand), helpFile) },
{"Enable-PSRemoting", new SessionStateCmdletEntry("Enable-PSRemoting", typeof(EnablePSRemotingCommand), helpFile) },
{"Disable-PSSessionConfiguration", new SessionStateCmdletEntry("Disable-PSSessionConfiguration", typeof(DisablePSSessionConfigurationCommand), helpFile) },
{"Enable-PSSessionConfiguration", new SessionStateCmdletEntry("Enable-PSSessionConfiguration", typeof(EnablePSSessionConfigurationCommand), helpFile) },
{"Get-PSSessionCapability", new SessionStateCmdletEntry("Get-PSSessionCapability", typeof(GetPSSessionCapabilityCommand), helpFile) },
{"Get-PSSessionConfiguration", new SessionStateCmdletEntry("Get-PSSessionConfiguration", typeof(GetPSSessionConfigurationCommand), helpFile) },
{"New-PSSessionConfigurationFile", new SessionStateCmdletEntry("New-PSSessionConfigurationFile", typeof(NewPSSessionConfigurationFileCommand), helpFile) },
{"Receive-PSSession", new SessionStateCmdletEntry("Receive-PSSession", typeof(ReceivePSSessionCommand), helpFile) },
{"Register-PSSessionConfiguration", new SessionStateCmdletEntry("Register-PSSessionConfiguration", typeof(RegisterPSSessionConfigurationCommand), helpFile) },
{"Unregister-PSSessionConfiguration", new SessionStateCmdletEntry("Unregister-PSSessionConfiguration", typeof(UnregisterPSSessionConfigurationCommand), helpFile) },
{"Set-PSSessionConfiguration", new SessionStateCmdletEntry("Set-PSSessionConfiguration", typeof(SetPSSessionConfigurationCommand), helpFile) },
{"Test-PSSessionConfigurationFile", new SessionStateCmdletEntry("Test-PSSessionConfigurationFile", typeof(TestPSSessionConfigurationFileCommand), helpFile) },
{"Connect-PSSession", new SessionStateCmdletEntry("Connect-PSSession", typeof(ConnectPSSessionCommand), helpFile) },
{"Disconnect-PSSession", new SessionStateCmdletEntry("Disconnect-PSSession", typeof(DisconnectPSSessionCommand), helpFile) },
{ "Disable-PSRemoting", new SessionStateCmdletEntry("Disable-PSRemoting", typeof(DisablePSRemotingCommand), helpFile) },
{ "Enable-PSRemoting", new SessionStateCmdletEntry("Enable-PSRemoting", typeof(EnablePSRemotingCommand), helpFile) },
{ "Disable-PSSessionConfiguration", new SessionStateCmdletEntry("Disable-PSSessionConfiguration", typeof(DisablePSSessionConfigurationCommand), helpFile) },
{ "Enable-PSSessionConfiguration", new SessionStateCmdletEntry("Enable-PSSessionConfiguration", typeof(EnablePSSessionConfigurationCommand), helpFile) },
{ "Get-PSSessionCapability", new SessionStateCmdletEntry("Get-PSSessionCapability", typeof(GetPSSessionCapabilityCommand), helpFile) },
{ "Get-PSSessionConfiguration", new SessionStateCmdletEntry("Get-PSSessionConfiguration", typeof(GetPSSessionConfigurationCommand), helpFile) },
{ "New-PSSessionConfigurationFile", new SessionStateCmdletEntry("New-PSSessionConfigurationFile", typeof(NewPSSessionConfigurationFileCommand), helpFile) },
{ "Receive-PSSession", new SessionStateCmdletEntry("Receive-PSSession", typeof(ReceivePSSessionCommand), helpFile) },
{ "Register-PSSessionConfiguration", new SessionStateCmdletEntry("Register-PSSessionConfiguration", typeof(RegisterPSSessionConfigurationCommand), helpFile) },
{ "Unregister-PSSessionConfiguration", new SessionStateCmdletEntry("Unregister-PSSessionConfiguration", typeof(UnregisterPSSessionConfigurationCommand), helpFile) },
{ "Set-PSSessionConfiguration", new SessionStateCmdletEntry("Set-PSSessionConfiguration", typeof(SetPSSessionConfigurationCommand), helpFile) },
{ "Test-PSSessionConfigurationFile", new SessionStateCmdletEntry("Test-PSSessionConfigurationFile", typeof(TestPSSessionConfigurationFileCommand), helpFile) },
{ "Connect-PSSession", new SessionStateCmdletEntry("Connect-PSSession", typeof(ConnectPSSessionCommand), helpFile) },
{ "Disconnect-PSSession", new SessionStateCmdletEntry("Disconnect-PSSession", typeof(DisconnectPSSessionCommand), helpFile) },
#endif
{"Enter-PSHostProcess", new SessionStateCmdletEntry("Enter-PSHostProcess", typeof(EnterPSHostProcessCommand), helpFile) },
{"Enter-PSSession", new SessionStateCmdletEntry("Enter-PSSession", typeof(EnterPSSessionCommand), helpFile) },
{"Exit-PSHostProcess", new SessionStateCmdletEntry("Exit-PSHostProcess", typeof(ExitPSHostProcessCommand), helpFile) },
{"Exit-PSSession", new SessionStateCmdletEntry("Exit-PSSession", typeof(ExitPSSessionCommand), helpFile) },
{"Export-ModuleMember", new SessionStateCmdletEntry("Export-ModuleMember", typeof(ExportModuleMemberCommand), helpFile) },
{"ForEach-Object", new SessionStateCmdletEntry("ForEach-Object", typeof(ForEachObjectCommand), helpFile) },
{"Get-Command", new SessionStateCmdletEntry("Get-Command", typeof(GetCommandCommand), helpFile) },
{"Get-ExperimentalFeature", new SessionStateCmdletEntry("Get-ExperimentalFeature", typeof(GetExperimentalFeatureCommand), helpFile) },
{"Get-Help", new SessionStateCmdletEntry("Get-Help", typeof(GetHelpCommand), helpFile) },
{"Get-History", new SessionStateCmdletEntry("Get-History", typeof(GetHistoryCommand), helpFile) },
{"Get-Job", new SessionStateCmdletEntry("Get-Job", typeof(GetJobCommand), helpFile) },
{"Get-Module", new SessionStateCmdletEntry("Get-Module", typeof(GetModuleCommand), helpFile) },
{"Get-PSHostProcessInfo", new SessionStateCmdletEntry("Get-PSHostProcessInfo", typeof(GetPSHostProcessInfoCommand), helpFile) },
{"Get-PSSession", new SessionStateCmdletEntry("Get-PSSession", typeof(GetPSSessionCommand), helpFile) },
{"Import-Module", new SessionStateCmdletEntry("Import-Module", typeof(ImportModuleCommand), helpFile) },
{"Invoke-Command", new SessionStateCmdletEntry("Invoke-Command", typeof(InvokeCommandCommand), helpFile) },
{"Invoke-History", new SessionStateCmdletEntry("Invoke-History", typeof(InvokeHistoryCommand), helpFile) },
{"New-Module", new SessionStateCmdletEntry("New-Module", typeof(NewModuleCommand), helpFile) },
{"New-ModuleManifest", new SessionStateCmdletEntry("New-ModuleManifest", typeof(NewModuleManifestCommand), helpFile) },
{"New-PSRoleCapabilityFile", new SessionStateCmdletEntry("New-PSRoleCapabilityFile", typeof(NewPSRoleCapabilityFileCommand), helpFile) },
{"New-PSSession", new SessionStateCmdletEntry("New-PSSession", typeof(NewPSSessionCommand), helpFile) },
{"New-PSSessionOption", new SessionStateCmdletEntry("New-PSSessionOption", typeof(NewPSSessionOptionCommand), helpFile) },
{"New-PSTransportOption", new SessionStateCmdletEntry("New-PSTransportOption", typeof(NewPSTransportOptionCommand), helpFile) },
{"Out-Default", new SessionStateCmdletEntry("Out-Default", typeof(OutDefaultCommand), helpFile) },
{"Out-Host", new SessionStateCmdletEntry("Out-Host", typeof(OutHostCommand), helpFile) },
{"Out-Null", new SessionStateCmdletEntry("Out-Null", typeof(OutNullCommand), helpFile) },
{"Receive-Job", new SessionStateCmdletEntry("Receive-Job", typeof(ReceiveJobCommand), helpFile) },
{"Register-ArgumentCompleter", new SessionStateCmdletEntry("Register-ArgumentCompleter", typeof(RegisterArgumentCompleterCommand), helpFile) },
{"Remove-Job", new SessionStateCmdletEntry("Remove-Job", typeof(RemoveJobCommand), helpFile) },
{"Remove-Module", new SessionStateCmdletEntry("Remove-Module", typeof(RemoveModuleCommand), helpFile) },
{"Remove-PSSession", new SessionStateCmdletEntry("Remove-PSSession", typeof(RemovePSSessionCommand), helpFile) },
{"Save-Help", new SessionStateCmdletEntry("Save-Help", typeof(SaveHelpCommand), helpFile) },
{"Set-PSDebug", new SessionStateCmdletEntry("Set-PSDebug", typeof(SetPSDebugCommand), helpFile) },
{"Set-StrictMode", new SessionStateCmdletEntry("Set-StrictMode", typeof(SetStrictModeCommand), helpFile) },
{"Start-Job", new SessionStateCmdletEntry("Start-Job", typeof(StartJobCommand), helpFile) },
{"Stop-Job", new SessionStateCmdletEntry("Stop-Job", typeof(StopJobCommand), helpFile) },
{"Test-ModuleManifest", new SessionStateCmdletEntry("Test-ModuleManifest", typeof(TestModuleManifestCommand), helpFile) },
{"Update-Help", new SessionStateCmdletEntry("Update-Help", typeof(UpdateHelpCommand), helpFile) },
{"Wait-Job", new SessionStateCmdletEntry("Wait-Job", typeof(WaitJobCommand), helpFile) },
{"Where-Object", new SessionStateCmdletEntry("Where-Object", typeof(WhereObjectCommand), helpFile) },
{ "Disable-ExperimentalFeature", new SessionStateCmdletEntry("Disable-ExperimentalFeature", typeof(DisableExperimentalFeatureCommand), helpFile) },
{ "Enable-ExperimentalFeature", new SessionStateCmdletEntry("Enable-ExperimentalFeature", typeof(EnableExperimentalFeatureCommand), helpFile) },
{ "Enter-PSHostProcess", new SessionStateCmdletEntry("Enter-PSHostProcess", typeof(EnterPSHostProcessCommand), helpFile) },
{ "Enter-PSSession", new SessionStateCmdletEntry("Enter-PSSession", typeof(EnterPSSessionCommand), helpFile) },
{ "Exit-PSHostProcess", new SessionStateCmdletEntry("Exit-PSHostProcess", typeof(ExitPSHostProcessCommand), helpFile) },
{ "Exit-PSSession", new SessionStateCmdletEntry("Exit-PSSession", typeof(ExitPSSessionCommand), helpFile) },
{ "Export-ModuleMember", new SessionStateCmdletEntry("Export-ModuleMember", typeof(ExportModuleMemberCommand), helpFile) },
{ "ForEach-Object", new SessionStateCmdletEntry("ForEach-Object", typeof(ForEachObjectCommand), helpFile) },
{ "Get-Command", new SessionStateCmdletEntry("Get-Command", typeof(GetCommandCommand), helpFile) },
{ "Get-ExperimentalFeature", new SessionStateCmdletEntry("Get-ExperimentalFeature", typeof(GetExperimentalFeatureCommand), helpFile) },
{ "Get-Help", new SessionStateCmdletEntry("Get-Help", typeof(GetHelpCommand), helpFile) },
{ "Get-History", new SessionStateCmdletEntry("Get-History", typeof(GetHistoryCommand), helpFile) },
{ "Get-Job", new SessionStateCmdletEntry("Get-Job", typeof(GetJobCommand), helpFile) },
{ "Get-Module", new SessionStateCmdletEntry("Get-Module", typeof(GetModuleCommand), helpFile) },
{ "Get-PSHostProcessInfo", new SessionStateCmdletEntry("Get-PSHostProcessInfo", typeof(GetPSHostProcessInfoCommand), helpFile) },
{ "Get-PSSession", new SessionStateCmdletEntry("Get-PSSession", typeof(GetPSSessionCommand), helpFile) },
{ "Import-Module", new SessionStateCmdletEntry("Import-Module", typeof(ImportModuleCommand), helpFile) },
{ "Invoke-Command", new SessionStateCmdletEntry("Invoke-Command", typeof(InvokeCommandCommand), helpFile) },
{ "Invoke-History", new SessionStateCmdletEntry("Invoke-History", typeof(InvokeHistoryCommand), helpFile) },
{ "New-Module", new SessionStateCmdletEntry("New-Module", typeof(NewModuleCommand), helpFile) },
{ "New-ModuleManifest", new SessionStateCmdletEntry("New-ModuleManifest", typeof(NewModuleManifestCommand), helpFile) },
{ "New-PSRoleCapabilityFile", new SessionStateCmdletEntry("New-PSRoleCapabilityFile", typeof(NewPSRoleCapabilityFileCommand), helpFile) },
{ "New-PSSession", new SessionStateCmdletEntry("New-PSSession", typeof(NewPSSessionCommand), helpFile) },
{ "New-PSSessionOption", new SessionStateCmdletEntry("New-PSSessionOption", typeof(NewPSSessionOptionCommand), helpFile) },
{ "New-PSTransportOption", new SessionStateCmdletEntry("New-PSTransportOption", typeof(NewPSTransportOptionCommand), helpFile) },
{ "Out-Default", new SessionStateCmdletEntry("Out-Default", typeof(OutDefaultCommand), helpFile) },
{ "Out-Host", new SessionStateCmdletEntry("Out-Host", typeof(OutHostCommand), helpFile) },
{ "Out-Null", new SessionStateCmdletEntry("Out-Null", typeof(OutNullCommand), helpFile) },
{ "Receive-Job", new SessionStateCmdletEntry("Receive-Job", typeof(ReceiveJobCommand), helpFile) },
{ "Register-ArgumentCompleter", new SessionStateCmdletEntry("Register-ArgumentCompleter", typeof(RegisterArgumentCompleterCommand), helpFile) },
{ "Remove-Job", new SessionStateCmdletEntry("Remove-Job", typeof(RemoveJobCommand), helpFile) },
{ "Remove-Module", new SessionStateCmdletEntry("Remove-Module", typeof(RemoveModuleCommand), helpFile) },
{ "Remove-PSSession", new SessionStateCmdletEntry("Remove-PSSession", typeof(RemovePSSessionCommand), helpFile) },
{ "Save-Help", new SessionStateCmdletEntry("Save-Help", typeof(SaveHelpCommand), helpFile) },
{ "Set-PSDebug", new SessionStateCmdletEntry("Set-PSDebug", typeof(SetPSDebugCommand), helpFile) },
{ "Set-StrictMode", new SessionStateCmdletEntry("Set-StrictMode", typeof(SetStrictModeCommand), helpFile) },
{ "Start-Job", new SessionStateCmdletEntry("Start-Job", typeof(StartJobCommand), helpFile) },
{ "Stop-Job", new SessionStateCmdletEntry("Stop-Job", typeof(StopJobCommand), helpFile) },
{ "Test-ModuleManifest", new SessionStateCmdletEntry("Test-ModuleManifest", typeof(TestModuleManifestCommand), helpFile) },
{ "Update-Help", new SessionStateCmdletEntry("Update-Help", typeof(UpdateHelpCommand), helpFile) },
{ "Wait-Job", new SessionStateCmdletEntry("Wait-Job", typeof(WaitJobCommand), helpFile) },
{ "Where-Object", new SessionStateCmdletEntry("Where-Object", typeof(WhereObjectCommand), helpFile) },
#if !CORECLR
{"Add-PSSnapin", new SessionStateCmdletEntry("Add-PSSnapin", typeof(AddPSSnapinCommand), helpFile) },
{"Export-Console", new SessionStateCmdletEntry("Export-Console", typeof(ExportConsoleCommand), helpFile) },
{"Get-PSSnapin", new SessionStateCmdletEntry("Get-PSSnapin", typeof(GetPSSnapinCommand), helpFile) },
{"Remove-PSSnapin", new SessionStateCmdletEntry("Remove-PSSnapin", typeof(RemovePSSnapinCommand), helpFile) },
{"Resume-Job", new SessionStateCmdletEntry("Resume-Job", typeof(ResumeJobCommand), helpFile) },
{"Suspend-Job", new SessionStateCmdletEntry("Suspend-Job", typeof(SuspendJobCommand), helpFile) },
{ "Add-PSSnapin", new SessionStateCmdletEntry("Add-PSSnapin", typeof(AddPSSnapinCommand), helpFile) },
{ "Export-Console", new SessionStateCmdletEntry("Export-Console", typeof(ExportConsoleCommand), helpFile) },
{ "Get-PSSnapin", new SessionStateCmdletEntry("Get-PSSnapin", typeof(GetPSSnapinCommand), helpFile) },
{ "Remove-PSSnapin", new SessionStateCmdletEntry("Remove-PSSnapin", typeof(RemovePSSnapinCommand), helpFile) },
{ "Resume-Job", new SessionStateCmdletEntry("Resume-Job", typeof(ResumeJobCommand), helpFile) },
{ "Suspend-Job", new SessionStateCmdletEntry("Suspend-Job", typeof(SuspendJobCommand), helpFile) },
#endif
// Not exported, but are added via reflection so added here as well, though maybe they shouldn't be
{"Out-LineOutput", new SessionStateCmdletEntry("Out-LineOutput", typeof(OutLineOutputCommand), helpFile) },
{"Format-Default", new SessionStateCmdletEntry("Format-Default", typeof(FormatDefaultCommand), helpFile) },
{ "Out-LineOutput", new SessionStateCmdletEntry("Out-LineOutput", typeof(OutLineOutputCommand), helpFile) },
{ "Format-Default", new SessionStateCmdletEntry("Format-Default", typeof(FormatDefaultCommand), helpFile) },
};
foreach (var val in cmdlets.Values)
{
@@ -1286,7 +1286,7 @@ namespace System.Management.Automation
// If the Windows PowerShell Module path is already present, insert the system module path
// ($PSHOME/Modules) before it.
insertIndex = PathContainsSubstring(currentProcessModulePath, windowsPowerShellModulePath);
#endif
#endif
currentProcessModulePath = AddToPath(currentProcessModulePath, systemModulePathToUse, insertIndex);
}
@@ -1295,7 +1295,7 @@ namespace System.Management.Automation
// index of $PSHome\Modules in currentProcessModulePath
int indexOfPSHomeModulePath = PathContainsSubstring(currentProcessModulePath, psHomeModulePath);
// if $PSHome\Modules not found (psHomePosition == -1) - append <Program Files> location to the end;
// if $PSHome\Modules IS found (psHomePosition >= 0) - insert <Program Files> location before $PSHome\Modules
currentProcessModulePath = AddToPath(currentProcessModulePath, sharedModulePath, indexOfPSHomeModulePath);
@@ -1322,10 +1322,10 @@ namespace System.Management.Automation
private static string SetModulePath()
{
string currentModulePath = GetExpandedEnvironmentVariable(Constants.PSModulePathEnvVar, EnvironmentVariableTarget.Process);
string systemWideModulePath = PowerShellConfig.Instance.GetModulePath(ConfigScope.SystemWide);
string allUsersModulePath = PowerShellConfig.Instance.GetModulePath(ConfigScope.AllUsers);
string personalModulePath = PowerShellConfig.Instance.GetModulePath(ConfigScope.CurrentUser);
string newModulePathString = GetModulePath(currentModulePath, systemWideModulePath, personalModulePath);
string newModulePathString = GetModulePath(currentModulePath, allUsersModulePath, personalModulePath);
if (!string.IsNullOrEmpty(newModulePathString))
{
@@ -2,6 +2,7 @@
// Licensed under the MIT License.
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Threading;
@@ -11,12 +12,19 @@ using System.Management.Automation.Internal;
namespace System.Management.Automation.Configuration
{
internal enum ConfigScope
/// <summary>
/// The scope of the configuration file.
/// </summary>
public enum ConfigScope
{
// SystemWide configuration applies to all users.
SystemWide = 0,
/// <summary>
/// AllUsers configuration applies to all users.
/// </summary>
AllUsers = 0,
// CurrentUser configuration applies to the current user.
/// <summary>
/// CurrentUser configuration applies to the current user.
/// </summary>
CurrentUser = 1
}
@@ -140,15 +148,6 @@ namespace System.Management.Automation.Configuration
internal void SetExecutionPolicy(ConfigScope scope, string shellId, string executionPolicy)
{
// Defaults to system wide.
if (ConfigScope.CurrentUser == scope)
{
// Exceptions are not caught so that they will propagate to the
// host for display to the user.
// CreateDirectory will succeed if the directory already exists
// so there is no reason to check Directory.Exists().
Directory.CreateDirectory(perUserConfigDirectory);
}
string valueName = string.Concat(shellId, ":", "ExecutionPolicy");
WriteValueToFile<string>(scope, valueName, executionPolicy);
}
@@ -165,12 +164,12 @@ namespace System.Management.Automation.Configuration
/// <returns>Whether console prompting should happen. If the value cannot be read it defaults to false.</returns>
internal bool GetConsolePrompting()
{
return ReadValueFromFile<bool>(ConfigScope.SystemWide, "ConsolePrompting");
return ReadValueFromFile<bool>(ConfigScope.AllUsers, "ConsolePrompting");
}
internal void SetConsolePrompting(bool shouldPrompt)
{
WriteValueToFile<bool>(ConfigScope.SystemWide, "ConsolePrompting", shouldPrompt);
WriteValueToFile<bool>(ConfigScope.AllUsers, "ConsolePrompting", shouldPrompt);
}
/// <summary>
@@ -185,12 +184,12 @@ namespace System.Management.Automation.Configuration
/// <returns>Boolean indicating whether Update-Help should prompt. If the value cannot be read, it defaults to false.</returns>
internal bool GetDisablePromptToUpdateHelp()
{
return ReadValueFromFile<bool>(ConfigScope.SystemWide, "DisablePromptToUpdateHelp");
return ReadValueFromFile<bool>(ConfigScope.AllUsers, "DisablePromptToUpdateHelp");
}
internal void SetDisablePromptToUpdateHelp(bool prompt)
{
WriteValueToFile<bool>(ConfigScope.SystemWide, "DisablePromptToUpdateHelp", prompt);
WriteValueToFile<bool>(ConfigScope.AllUsers, "DisablePromptToUpdateHelp", prompt);
}
/// <summary>
@@ -198,7 +197,40 @@ namespace System.Management.Automation.Configuration
/// </summary>
internal string[] GetExperimentalFeatures()
{
return ReadValueFromFile<string[]>(ConfigScope.SystemWide, "ExperimentalFeatures", Utils.EmptyArray<string>());
string[] features = Array.Empty<string>();
if (File.Exists(perUserConfigFile))
{
features = ReadValueFromFile<string[]>(ConfigScope.CurrentUser, "ExperimentalFeatures", Array.Empty<string>());
}
if (features.Length == 0)
{
features = ReadValueFromFile<string[]>(ConfigScope.AllUsers, "ExperimentalFeatures", Array.Empty<string>());
}
return features;
}
/// <summary>
/// Set the enabled list of experimental features in the config file.
/// </summary>
/// <param name="scope">The ConfigScope of the configuration file to update.</param>
/// <param name="featureName">The name of the experimental feature to change in the configuration.</param>
/// <param name="setEnabled">If true, add to configuration; otherwise, remove from configuration.</param>
internal void SetExperimentalFeatures(ConfigScope scope, string featureName, bool setEnabled)
{
var features = new List<string>(GetExperimentalFeatures());
bool containsFeature = features.Contains(featureName);
if (setEnabled && !containsFeature)
{
features.Add(featureName);
WriteValueToFile<string[]>(scope, "ExperimentalFeatures", features.ToArray());
}
else if (!setEnabled && containsFeature)
{
features.Remove(featureName);
WriteValueToFile<string[]>(scope, "ExperimentalFeatures", features.ToArray());
}
}
/// <summary>
@@ -218,7 +250,7 @@ namespace System.Management.Automation.Configuration
/// </returns>
internal string GetSysLogIdentity()
{
string identity = ReadValueFromFile<string>(ConfigScope.SystemWide, "LogIdentity");
string identity = ReadValueFromFile<string>(ConfigScope.AllUsers, "LogIdentity");
if (string.IsNullOrEmpty(identity) ||
identity.Equals(LogDefaultValue, StringComparison.OrdinalIgnoreCase))
@@ -236,7 +268,7 @@ namespace System.Management.Automation.Configuration
/// </returns>
internal PSLevel GetLogLevel()
{
string levelName = ReadValueFromFile<string>(ConfigScope.SystemWide, "LogLevel");
string levelName = ReadValueFromFile<string>(ConfigScope.AllUsers, "LogLevel");
PSLevel level;
if (string.IsNullOrEmpty(levelName) ||
@@ -266,7 +298,7 @@ namespace System.Management.Automation.Configuration
/// </returns>
internal PSChannel GetLogChannels()
{
string values = ReadValueFromFile<string>(ConfigScope.SystemWide, "LogChannels");
string values = ReadValueFromFile<string>(ConfigScope.AllUsers, "LogChannels");
PSChannel result = 0;
if (!string.IsNullOrEmpty(values))
@@ -305,7 +337,7 @@ namespace System.Management.Automation.Configuration
/// </returns>
internal PSKeyword GetLogKeywords()
{
string values = ReadValueFromFile<string>(ConfigScope.SystemWide, "LogKeywords");
string values = ReadValueFromFile<string>(ConfigScope.AllUsers, "LogKeywords");
PSKeyword result = 0;
if (!string.IsNullOrEmpty(values))
@@ -516,6 +548,16 @@ namespace System.Management.Automation.Configuration
/// <param name="value">The value to write.</param>
private void WriteValueToFile<T>(ConfigScope scope, string key, T value)
{
// Defaults to system wide.
if (ConfigScope.CurrentUser == scope)
{
// Exceptions are not caught so that they will propagate to the
// host for display to the user.
// CreateDirectory will succeed if the directory already exists
// so there is no reason to check Directory.Exists().
Directory.CreateDirectory(perUserConfigDirectory);
}
UpdateValueInFile<T>(scope, key, value, true);
}
@@ -488,10 +488,10 @@ namespace System.Management.Automation
/// </summary>
internal static string ModuleDirectory = Path.Combine(ProductNameForDirectory, "Modules");
internal readonly static ConfigScope[] SystemWideOnlyConfig = new[] { ConfigScope.SystemWide };
internal readonly static ConfigScope[] SystemWideOnlyConfig = new[] { ConfigScope.AllUsers };
internal readonly static ConfigScope[] CurrentUserOnlyConfig = new[] { ConfigScope.CurrentUser };
internal readonly static ConfigScope[] SystemWideThenCurrentUserConfig = new[] { ConfigScope.SystemWide, ConfigScope.CurrentUser };
internal readonly static ConfigScope[] CurrentUserThenSystemWideConfig = new[] { ConfigScope.CurrentUser, ConfigScope.SystemWide };
internal readonly static ConfigScope[] SystemWideThenCurrentUserConfig = new[] { ConfigScope.AllUsers, ConfigScope.CurrentUser };
internal readonly static ConfigScope[] CurrentUserThenSystemWideConfig = new[] { ConfigScope.CurrentUser, ConfigScope.AllUsers };
internal static T GetPolicySetting<T>(ConfigScope[] preferenceOrder) where T : PolicyBase, new()
{
@@ -572,7 +572,7 @@ namespace System.Management.Automation
{
Type tType = typeof(T);
// SystemWide scope means 'LocalMachine' root key when query from registry
RegistryKey rootKey = (scope == ConfigScope.SystemWide) ? Registry.LocalMachine : Registry.CurrentUser;
RegistryKey rootKey = (scope == ConfigScope.AllUsers) ? Registry.LocalMachine : Registry.CurrentUser;
GroupPolicyKeys.TryGetValue(tType.Name, out string gpoKeyPath);
Diagnostics.Assert(gpoKeyPath != null, StringUtil.Format("The GPO registry key path should be pre-defined for {0}", tType.Name));
@@ -1799,7 +1799,7 @@ namespace System.Management.Automation.Internal
}
/// <summary>
/// Test hook used to test implicit remoting batching. A local runspace must be provided that has imported a
/// Test hook used to test implicit remoting batching. A local runspace must be provided that has imported a
/// remote session, i.e., has run the Import-PSSession cmdlet. This hook will return true if the provided commandPipeline
/// is successfully batched and run in the remote session, and false if it is rejected for batching.
/// </summary>
@@ -0,0 +1,67 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<xsd:schema id="root" xmlns="" xmlns:xsd="https://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="https://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<data name="ExperimentalFeatureNameNotFound" xml:space="preserve">
<value>No experimental feature was found that matched the name '{0}'.</value>
</data>
<data name="ExperimentalFeaturePending" xml:space="preserve">
<value>Enabling and disabling experimental features do not take effect until next start of PowerShell.</value>
</data>
</root>
@@ -184,11 +184,11 @@ namespace System.Management.Automation.Internal
// They want to remove it
if (policy == ExecutionPolicy.Undefined)
{
PowerShellConfig.Instance.RemoveExecutionPolicy(ConfigScope.SystemWide, shellId);
PowerShellConfig.Instance.RemoveExecutionPolicy(ConfigScope.AllUsers, shellId);
}
else
{
PowerShellConfig.Instance.SetExecutionPolicy(ConfigScope.SystemWide, shellId, executionPolicy);
PowerShellConfig.Instance.SetExecutionPolicy(ConfigScope.AllUsers, shellId, executionPolicy);
}
break;
}
@@ -466,7 +466,7 @@ namespace System.Management.Automation.Internal
// 2: Look up the system-wide preference
case ExecutionPolicyScope.LocalMachine:
return PowerShellConfig.Instance.GetExecutionPolicy(ConfigScope.SystemWide, shellId);
return PowerShellConfig.Instance.GetExecutionPolicy(ConfigScope.AllUsers, shellId);
}
return null;
+6 -6
View File
@@ -348,7 +348,7 @@ namespace PSTests.Sequential
public void PowerShellConfig_GetPowerShellPolicies_BothConfigFilesNotEmpty()
{
fixture.SetupConfigFile1();
var sysPolicies = PowerShellConfig.Instance.GetPowerShellPolicies(ConfigScope.SystemWide);
var sysPolicies = PowerShellConfig.Instance.GetPowerShellPolicies(ConfigScope.AllUsers);
var userPolicies = PowerShellConfig.Instance.GetPowerShellPolicies(ConfigScope.CurrentUser);
Assert.NotNull(sysPolicies);
@@ -362,7 +362,7 @@ namespace PSTests.Sequential
public void PowerShellConfig_GetPowerShellPolicies_EmptyUserConfig()
{
fixture.SetupConfigFile2();
var sysPolicies = PowerShellConfig.Instance.GetPowerShellPolicies(ConfigScope.SystemWide);
var sysPolicies = PowerShellConfig.Instance.GetPowerShellPolicies(ConfigScope.AllUsers);
var userPolicies = PowerShellConfig.Instance.GetPowerShellPolicies(ConfigScope.CurrentUser);
Assert.NotNull(sysPolicies);
@@ -375,7 +375,7 @@ namespace PSTests.Sequential
public void PowerShellConfig_GetPowerShellPolicies_EmptySystemConfig()
{
fixture.SetupConfigFile3();
var sysPolicies = PowerShellConfig.Instance.GetPowerShellPolicies(ConfigScope.SystemWide);
var sysPolicies = PowerShellConfig.Instance.GetPowerShellPolicies(ConfigScope.AllUsers);
var userPolicies = PowerShellConfig.Instance.GetPowerShellPolicies(ConfigScope.CurrentUser);
Assert.Null(sysPolicies);
@@ -388,7 +388,7 @@ namespace PSTests.Sequential
public void PowerShellConfig_GetPowerShellPolicies_BothConfigFilesEmpty()
{
fixture.SetupConfigFile4();
var sysPolicies = PowerShellConfig.Instance.GetPowerShellPolicies(ConfigScope.SystemWide);
var sysPolicies = PowerShellConfig.Instance.GetPowerShellPolicies(ConfigScope.AllUsers);
var userPolicies = PowerShellConfig.Instance.GetPowerShellPolicies(ConfigScope.CurrentUser);
Assert.Null(sysPolicies);
@@ -399,7 +399,7 @@ namespace PSTests.Sequential
public void PowerShellConfig_GetPowerShellPolicies_BothConfigFilesNotExist()
{
fixture.CleanupConfigFiles();
var sysPolicies = PowerShellConfig.Instance.GetPowerShellPolicies(ConfigScope.SystemWide);
var sysPolicies = PowerShellConfig.Instance.GetPowerShellPolicies(ConfigScope.AllUsers);
var userPolicies = PowerShellConfig.Instance.GetPowerShellPolicies(ConfigScope.CurrentUser);
Assert.Null(sysPolicies);
@@ -899,4 +899,4 @@ namespace PSTests.Sequential
fixture.CompareConsoleSessionConfiguration(consoleSessionConfiguration, null);
}
}
}
}
@@ -209,6 +209,7 @@ Describe "Verify approved aliases list" -Tags "CI" {
"Cmdlet", "Debug-Process", , $($FullCLR -or $CoreWindows -or $CoreUnix)
"Cmdlet", "Debug-Runspace", , $($FullCLR -or $CoreWindows -or $CoreUnix)
"Cmdlet", "Disable-ComputerRestore", , $($FullCLR )
"Cmdlet", "Disable-ExperimentalFeature", , $( $CoreWindows -or $CoreUnix)
"Cmdlet", "Disable-PSBreakpoint", , $($FullCLR -or $CoreWindows -or $CoreUnix)
"Cmdlet", "Disable-PSRemoting", , $($FullCLR -or $CoreWindows )
"Cmdlet", "Disable-PSSessionConfiguration", , $($FullCLR -or $CoreWindows )
@@ -217,6 +218,7 @@ Describe "Verify approved aliases list" -Tags "CI" {
"Cmdlet", "Disconnect-PSSession", , $($FullCLR -or $CoreWindows )
"Cmdlet", "Disconnect-WSMan", , $($FullCLR -or $CoreWindows )
"Cmdlet", "Enable-ComputerRestore", , $($FullCLR )
"Cmdlet", "Enable-ExperimentalFeature", , $( $CoreWindows -or $CoreUnix)
"Cmdlet", "Enable-PSBreakpoint", , $($FullCLR -or $CoreWindows -or $CoreUnix)
"Cmdlet", "Enable-PSRemoting", , $($FullCLR -or $CoreWindows )
"Cmdlet", "Enable-PSSessionConfiguration", , $($FullCLR -or $CoreWindows )
@@ -0,0 +1,87 @@
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License.
Describe "Enable-ExperimentalFeature and Disable-ExperimentalFeature tests" -tags "Feature","RequireAdminOnWindows" {
BeforeAll {
$systemConfigPath = "$PSHOME/powershell.config.json"
if ($IsWindows) {
$userConfigPath = "~/Documents/powershell/powershell.config.json"
}
else {
$userConfigPath = "~/.config/powershell/powershell.config.json"
}
$systemConfigExists = $false
if (Test-Path $systemConfigPath) {
$systemConfigExists = $true
Move-Item $systemConfigPath "$systemConfigPath.backup" -Force -ErrorAction SilentlyContinue
}
$userConfigExists = $false
if (Test-Path $userConfigPath) {
$userConfigExists = $true
Move-Item $userConfigPath "$userConfigPath.backup" -Force -ErrorAction SilentlyContinue
}
$testModulePath = Join-Path -Path $PSScriptRoot -ChildPath "assets"
$originalModulePath = $env:PSModulePath
$env:PSModulePath = $testModulePath
}
AfterAll {
if ($systemConfigExists) {
Move-Item "$systemConfigPath.backup" $systemConfigPath -Force -ErrorAction SilentlyContinue
}
if ($userConfigExists) {
Move-Item "$userConfigPath.backup" $userConfigPath -Force -ErrorAction SilentlyContinue
}
$env:PSModulePath = $originalModulePath
}
AfterEach {
Remove-Item $systemConfigPath -Force -ErrorAction SilentlyContinue
Remove-Item $userConfigPath -Force -ErrorAction SilentlyContinue
}
It "Enable-ExperimentalFeature will enable Experimental Feature for scope: <scope>" -TestCases @(
@{ scope = "AllUsers" },
@{ scope = "CurrentUser" }
) {
param ($scope)
$feature = pwsh -noprofile -output xml -command Get-ExperimentalFeature ExpTest.FeatureOne
$feature.Enabled | Should -BeFalse -Because "All Experimental Features disabled when no config file"
$feature = pwsh -noprofile -output xml -command Enable-ExperimentalFeature ExpTest.FeatureOne -Scope $scope -WarningAction SilentlyContinue
$feature | Should -BeNullOrEmpty -Because "No object is output to pipeline on success"
$feature = pwsh -noprofile -output xml -command Get-ExperimentalFeature ExpTest.FeatureOne
$feature.Enabled | Should -BeTrue -Because "The experimental feature is now enabled"
}
It "Disable-ExperimentalFeature will disable Experimental Feature for scope: <scope>" -TestCases @(
@{ scope = "AllUsers" ; configPath = $systemConfigPath },
@{ scope = "CurrentUser"; configPath = $userConfigPath }
) {
param ($scope, $configPath)
'{"ExperimentalFeatures":["ExpTest.FeatureOne"]}' > $configPath
$feature = pwsh -noprofile -output xml -command Get-ExperimentalFeature ExpTest.FeatureOne
$feature.Enabled | Should -BeTrue -Because "Test config should enable ExpTest.FeatureOne"
$feature = pwsh -noprofile -output xml -command Disable-ExperimentalFeature ExpTest.FeatureOne -Scope $scope -WarningAction SilentlyContinue
$feature | Should -BeNullOrEmpty -Because "No object is output to pipeline on success"
$feature = pwsh -noprofile -output xml -command Get-ExperimentalFeature ExpTest.FeatureOne
$feature.Enabled | Should -BeFalse -Because "The experimental feature is now disabled"
}
It "<cmdlet> will output warning message" -TestCases @(
@{ cmdlet = "Enable-ExperimentalFeature" },
@{ cmdlet = "Disable-Experimentalfeature" }
) {
param ($cmdlet)
& $cmdlet ExpTest.FeatureOne -WarningVariable warning -WarningAction SilentlyContinue
$warning | Should -Not -BeNullOrEmpty -Because "A warning message is always given indicating restart is required"
}
}
@@ -1,117 +1,126 @@
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License.
Describe "Get-ExperimentalFeature basic tests - Feature-Disabled" -tags "CI" {
Describe "Get-ExperimentalFeature Tests" -tags "Feature","RequireAdminOnWindows" {
BeforeAll {
$skipTest = $EnabledExperimentalFeatures.Contains('ExpTest.FeatureOne')
if ($skipTest) {
Write-Verbose "Test Suite Skipped. The test suite requires the experimental feature 'ExpTest.FeatureOne' to be disabled." -Verbose
$originalDefaultParameterValues = $PSDefaultParameterValues.Clone()
$PSDefaultParameterValues["it:skip"] = $true
} else {
Remove-Module -Name ExpTest -Force -ErrorAction SilentlyContinue
$testModulePath = Join-Path -Path $PSScriptRoot -ChildPath "assets"
$testModuleManifestPath = Join-Path -Path $testModulePath "ExpTest" "ExpTest.psd1"
$originalModulePath = $env:PSModulePath
$env:PSModulePath = $testModulePath
$systemConfigPath = "$PSHOME/powershell.config.json"
if ($IsWindows) {
$userConfigPath = "~/Documents/powershell/powershell.config.json"
}
else {
$userConfigPath = "~/.config/powershell/powershell.config.json"
}
$systemConfigExists = $false
if (Test-Path $systemConfigPath) {
$systemConfigExists = $true
Move-Item $systemConfigPath "$systemConfigPath.backup" -Force -ErrorAction SilentlyContinue
}
$userConfigExists = $false
if (Test-Path $userConfigPath) {
$userConfigExists = $true
Move-Item $userConfigPath "$userConfigPath.backup" -Force -ErrorAction SilentlyContinue
}
$testModulePath = Join-Path -Path $PSScriptRoot -ChildPath "assets"
$testModuleManifestPath = Join-Path -Path $testModulePath "ExpTest" "ExpTest.psd1"
$originalModulePath = $env:PSModulePath
$env:PSModulePath = $testModulePath
}
AfterAll {
if ($skipTest) {
$global:PSDefaultParameterValues = $originalDefaultParameterValues
} else {
$env:PSModulePath = $originalModulePath
if ($systemConfigExists) {
Move-Item "$systemConfigPath.backup" $systemConfigPath -Force -ErrorAction SilentlyContinue
}
if ($userConfigExists) {
Move-Item "$userConfigPath.backup" $userConfigPath -Force -ErrorAction SilentlyContinue
}
$env:PSModulePath = $originalModulePath
}
AfterEach {
Remove-Item $systemConfigPath -Force -ErrorAction SilentlyContinue
Remove-Item $userConfigPath -Force -ErrorAction SilentlyContinue
}
Context "Feature disabled tests" {
It "'Get-ExperimentalFeature' should return all available features from module path" {
$features = pwsh -noprofile -output xml -command Get-ExperimentalFeature "ExpTest*"
$features | Should -Not -BeNullOrEmpty
$features[0].Name | Should -BeExactly "ExpTest.FeatureOne"
$features[0].Enabled | Should -BeFalse
$features[0].Source | Should -BeExactly $testModuleManifestPath
$features[1].Name | Should -BeExactly "ExpTest.FeatureTwo"
$features[1].Enabled | Should -BeFalse
$features[1].Source | Should -BeExactly $testModuleManifestPath
}
It "'Get-ExperimentalFeature' pipeline input" {
$features = pwsh -noprofile -output xml -command { "ExpTest.FeatureOne", "ExpTest.FeatureTwo" | Get-ExperimentalFeature }
$features | Should -Not -BeNullOrEmpty
$features[0].Name | Should -BeExactly "ExpTest.FeatureOne"
$features[0].Enabled | Should -BeFalse
$features[0].Source | Should -BeExactly $testModuleManifestPath
$features[1].Name | Should -BeExactly "ExpTest.FeatureTwo"
$features[1].Enabled | Should -BeFalse
$features[1].Source | Should -BeExactly $testModuleManifestPath
}
}
It "'Get-ExperimentalFeature' should only return enabled features" {
$EnabledExperimentalFeatures.Count | Should -Be 0
Get-ExperimentalFeature | Should -BeNullOrEmpty
Context "Feature enabled tests" {
BeforeEach {
'{"ExperimentalFeatures":["ExpTest.FeatureOne"]}' > $userConfigPath
}
It "'Get-ExperimentalFeature' should return enabled features 'ExpTest.FeatureOne'" {
pwsh -noprofile -command '$EnabledExperimentalFeatures.Count' | Should -Be 1
$feature = pwsh -noprofile -output xml -command Get-ExperimentalFeature "ExpTest.FeatureOne"
$feature | Should -Not -BeNullOrEmpty
$feature.Enabled | Should -BeTrue
$feature.Source | Should -BeExactly $testModuleManifestPath
}
It "'Get-ExperimentalFeature' should return all available features from module path" {
$features = pwsh -noprofile -output xml -command Get-ExperimentalFeature "ExpTest*"
$features | Should -Not -BeNullOrEmpty
$features[0].Name | Should -BeExactly "ExpTest.FeatureOne"
$features[0].Enabled | Should -BeTrue
$features[0].Source | Should -BeExactly $testModuleManifestPath
$features[1].Name | Should -BeExactly "ExpTest.FeatureTwo"
$features[1].Enabled | Should -BeFalse
$features[1].Source | Should -BeExactly $testModuleManifestPath
}
It "'Get-ExperimentalFeature' pipeline input" {
$features = pwsh -noprofile -output xml -command { "ExpTest.FeatureOne", "ExpTest.FeatureTwo" | Get-ExperimentalFeature }
$features | Should -Not -BeNullOrEmpty
$features[0].Name | Should -BeExactly "ExpTest.FeatureOne"
$features[0].Enabled | Should -BeTrue
$features[0].Source | Should -BeExactly $testModuleManifestPath
$features[1].Name | Should -BeExactly "ExpTest.FeatureTwo"
$features[1].Enabled | Should -BeFalse
$features[1].Source | Should -BeExactly $testModuleManifestPath
}
}
It "'Get-ExperimentalFeature -ListAvailable' should return all available features from module path" {
$features = Get-ExperimentalFeature "ExpTest*" -ListAvailable
$features | Should -Not -BeNullOrEmpty
$features[0].Name | Should -BeExactly "ExpTest.FeatureOne"
$features[0].Enabled | Should -Be $false
$features[0].Source | Should -BeExactly $testModuleManifestPath
Context "User config takes precedence over system config" {
It "Feature is enabled in user config only" {
'{"ExperimentalFeatures":["ExpTest.FeatureOne"]}' > $userConfigPath
'{"ExperimentalFeatures":["ExpTest.FeatureTwo"]}' > $systemConfigPath
$features[1].Name | Should -BeExactly "ExpTest.FeatureTwo"
$features[1].Enabled | Should -Be $false
$features[1].Source | Should -BeExactly $testModuleManifestPath
}
It "'Get-ExperimentalFeature -ListAvailable' pipeline input" {
$features = "ExpTest.FeatureOne", "ExpTest.FeatureTwo" | Get-ExperimentalFeature -ListAvailable
$features | Should -Not -BeNullOrEmpty
$features[0].Name | Should -BeExactly "ExpTest.FeatureOne"
$features[0].Enabled | Should -Be $false
$features[0].Source | Should -BeExactly $testModuleManifestPath
$features[1].Name | Should -BeExactly "ExpTest.FeatureTwo"
$features[1].Enabled | Should -Be $false
$features[1].Source | Should -BeExactly $testModuleManifestPath
}
}
Describe "Get-ExperimentalFeature basic tests - Feature-Enabled" -tags "CI" {
BeforeAll {
$skipTest = -not $EnabledExperimentalFeatures.Contains('ExpTest.FeatureOne')
if ($skipTest) {
Write-Verbose "Test Suite Skipped. The test suite requires the experimental feature 'ExpTest.FeatureOne' to be enabled." -Verbose
$originalDefaultParameterValues = $PSDefaultParameterValues.Clone()
$PSDefaultParameterValues["it:skip"] = $true
} else {
Remove-Module -Name ExpTest -Force -ErrorAction SilentlyContinue
$testModulePath = Join-Path -Path $PSScriptRoot -ChildPath "assets"
$testModuleManifestPath = Join-Path -Path $testModulePath "ExpTest" "ExpTest.psd1"
$originalModulePath = $env:PSModulePath
$env:PSModulePath = $testModulePath
$feature = pwsh -noprofile -output xml -command Get-ExperimentalFeature ExpTest.FeatureOne
$feature.Enabled | Should -BeTrue -Because "FeatureOne is enabled in user config"
$feature = pwsh -noprofile -output xml -command Get-ExperimentalFeature ExpTest.FeatureTwo
$feature.Enabled | Should -BeFalse -Because "System config is not read when user config exists"
}
}
AfterAll {
if ($skipTest) {
$global:PSDefaultParameterValues = $originalDefaultParameterValues
} else {
$env:PSModulePath = $originalModulePath
}
}
It "'Get-ExperimentalFeature' should return enabled features 'ExpTest.FeatureOne'" {
$EnabledExperimentalFeatures.Count | Should -Be 1
$feature = Get-ExperimentalFeature "ExpTest.FeatureOne"
$feature | Should -Not -BeNullOrEmpty
$feature.Enabled | Should -Be $true
$feature.Source | Should -BeExactly $testModuleManifestPath
}
It "'Get-ExperimentalFeature -ListAvailable' should return all available features from module path" {
$features = Get-ExperimentalFeature "ExpTest*" -ListAvailable
$features | Should -Not -BeNullOrEmpty
$features[0].Name | Should -BeExactly "ExpTest.FeatureOne"
$features[0].Enabled | Should -Be $true
$features[0].Source | Should -BeExactly $testModuleManifestPath
$features[1].Name | Should -BeExactly "ExpTest.FeatureTwo"
$features[1].Enabled | Should -Be $false
$features[1].Source | Should -BeExactly $testModuleManifestPath
}
It "'Get-ExperimentalFeature -ListAvailable' pipeline input" {
$features = "ExpTest.FeatureOne", "ExpTest.FeatureTwo" | Get-ExperimentalFeature -ListAvailable
$features | Should -Not -BeNullOrEmpty
$features[0].Name | Should -BeExactly "ExpTest.FeatureOne"
$features[0].Enabled | Should -Be $true
$features[0].Source | Should -BeExactly $testModuleManifestPath
$features[1].Name | Should -BeExactly "ExpTest.FeatureTwo"
$features[1].Enabled | Should -Be $false
$features[1].Source | Should -BeExactly $testModuleManifestPath
}
}
@@ -11,7 +11,9 @@ $script:cmdletsToSkip = @(
"Get-PSSessionCapability",
"Disable-PSRemoting", # Content not available: Issue # https://github.com/PowerShell/PowerShell-Docs/issues/1790
"Enable-PSRemoting",
"Get-ExperimentalFeature"
"Get-ExperimentalFeature",
"Enable-ExperimentalFeature",
"Disable-ExperimentalFeature"
)
function UpdateHelpFromLocalContentPath {
+1 -1
View File
@@ -1,5 +1,5 @@
{
"ExperimentalFeatures": {
"ExpTest.FeatureOne": [ "test/powershell/engine/ExperimentalFeature" ]
"ExpTest.FeatureOne": [ "test/powershell/engine/ExperimentalFeature/ExperimentalFeature.Basic.Tests.ps1" ]
}
}