diff --git a/src/System.Management.Automation/engine/ExperimentalFeature/EnableDisableExperimentalFeatureCommand.cs b/src/System.Management.Automation/engine/ExperimentalFeature/EnableDisableExperimentalFeatureCommand.cs
new file mode 100644
index 0000000000..2c9763dcdc
--- /dev/null
+++ b/src/System.Management.Automation/engine/ExperimentalFeature/EnableDisableExperimentalFeatureCommand.cs
@@ -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
+{
+ ///
+ /// Base class for Enable/Disable-ExperimentalFeature cmdlet.
+ ///
+ public class EnableDisableExperimentalFeatureCommandBase : PSCmdlet
+ {
+ ///
+ /// Gets or sets the feature names.
+ ///
+ [Parameter(ValueFromPipelineByPropertyName = true, Position = 0, Mandatory = true)]
+ [ArgumentCompleter(typeof(ExperimentalFeatureNameCompleter))]
+ public string[] Name { get; set; }
+
+ ///
+ /// Gets or sets the scope of persistence of updating the PowerShell configuration json.
+ ///
+ [Parameter]
+ public ConfigScope Scope { get; set; } = ConfigScope.CurrentUser;
+ }
+
+ ///
+ /// Implements Enable-ExperimentalFeature cmdlet.
+ ///
+ [Cmdlet(VerbsLifecycle.Enable, "ExperimentalFeature", SupportsShouldProcess = true, HelpUri = "https://go.microsoft.com/fwlink/?LinkID=2046964")]
+ public class EnableExperimentalFeatureCommand : EnableDisableExperimentalFeatureCommandBase
+ {
+ ///
+ /// ProcessRecord method of this cmdlet.
+ ///
+ protected override void ProcessRecord()
+ {
+ ExperimentalFeatureConfigHelper.UpdateConfig(this, Name, Scope, enable: true);
+ }
+ }
+
+ ///
+ /// Implements Enable-ExperimentalFeature cmdlet.
+ ///
+ [Cmdlet(VerbsLifecycle.Disable, "ExperimentalFeature", SupportsShouldProcess = true, HelpUri = "https://go.microsoft.com/fwlink/?LinkID=2046963")]
+ public class DisableExperimentalFeatureCommand : EnableDisableExperimentalFeatureCommandBase
+ {
+ ///
+ /// ProcessRecord method of this cmdlet.
+ ///
+ 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 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);
+ }
+ }
+
+ ///
+ /// Provides argument completion for ExperimentalFeature names.
+ ///
+ public class ExperimentalFeatureNameCompleter : IArgumentCompleter
+ {
+ ///
+ /// Returns completion results for experimental feature names used as arguments to experimental feature cmdlets.
+ ///
+ /// The command name.
+ /// The parameter name.
+ /// The word to complete.
+ /// The command AST.
+ /// The fake bound parameters.
+ /// List of Completion Results.
+ public IEnumerable 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 names = new HashSet();
+ var results = ps.Invoke();
+ foreach (var result in results)
+ {
+ names.Add(result.Name);
+ }
+
+ return names.OrderBy(name => name).Select(name => new CompletionResult(name, name, CompletionResultType.Text, name));
+ }
+ }
+}
diff --git a/src/System.Management.Automation/engine/ExperimentalFeature/GetExperimentalFeatureCommand.cs b/src/System.Management.Automation/engine/ExperimentalFeature/GetExperimentalFeatureCommand.cs
index 752742ffb0..07bcf02838 100644
--- a/src/System.Management.Automation/engine/ExperimentalFeature/GetExperimentalFeatureCommand.cs
+++ b/src/System.Management.Automation/engine/ExperimentalFeature/GetExperimentalFeatureCommand.cs
@@ -20,15 +20,10 @@ namespace Microsoft.PowerShell.Commands
/// Get and set the feature names.
///
[Parameter(ValueFromPipeline = true, Position = 0)]
+ [ArgumentCompleter(typeof(ExperimentalFeatureNameCompleter))]
[ValidateNotNullOrEmpty]
public string[] Name { get; set; }
- ///
- /// Get and set the switch flag to search module paths to find all available experimental features.
- ///
- [Parameter]
- public SwitchParameter ListAvailable { get; set; }
-
///
/// ProcessRecord method of this cmdlet.
///
@@ -37,19 +32,9 @@ namespace Microsoft.PowerShell.Commands
const WildcardOptions wildcardOptions = WildcardOptions.IgnoreCase | WildcardOptions.CultureInvariant;
IEnumerable 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);
}
- ///
- /// Get enabled experimental features based on the specified name patterns.
- ///
- private IEnumerable GetEnabledExperimentalFeatures(IEnumerable namePatterns)
- {
- var moduleFeatures = new List();
- var moduleNames = new HashSet(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(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);
- }
- }
- }
- }
-
///
/// Get available experimental features based on the specified name patterns.
///
- private IEnumerable GetAvailableExperimentalFeatures(IEnumerable namePatterns)
+ internal IEnumerable GetAvailableExperimentalFeatures(IEnumerable namePatterns)
{
foreach (ExperimentalFeature feature in ExperimentalFeature.EngineExperimentalFeatures)
{
diff --git a/src/System.Management.Automation/engine/InitialSessionState.cs b/src/System.Management.Automation/engine/InitialSessionState.cs
index 6c83d545b5..2b745bbe78 100644
--- a/src/System.Management.Automation/engine/InitialSessionState.cs
+++ b/src/System.Management.Automation/engine/InitialSessionState.cs
@@ -5103,76 +5103,78 @@ end
{
cmdlets = new Dictionary(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)
{
diff --git a/src/System.Management.Automation/engine/Modules/ModuleIntrinsics.cs b/src/System.Management.Automation/engine/Modules/ModuleIntrinsics.cs
index d9985110ef..76e4439a51 100644
--- a/src/System.Management.Automation/engine/Modules/ModuleIntrinsics.cs
+++ b/src/System.Management.Automation/engine/Modules/ModuleIntrinsics.cs
@@ -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 location to the end;
// if $PSHome\Modules IS found (psHomePosition >= 0) - insert 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))
{
diff --git a/src/System.Management.Automation/engine/PSConfiguration.cs b/src/System.Management.Automation/engine/PSConfiguration.cs
index 643d8d08a8..5487028d9a 100644
--- a/src/System.Management.Automation/engine/PSConfiguration.cs
+++ b/src/System.Management.Automation/engine/PSConfiguration.cs
@@ -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
+ ///
+ /// The scope of the configuration file.
+ ///
+ public enum ConfigScope
{
- // SystemWide configuration applies to all users.
- SystemWide = 0,
+ ///
+ /// AllUsers configuration applies to all users.
+ ///
+ AllUsers = 0,
- // CurrentUser configuration applies to the current user.
+ ///
+ /// CurrentUser configuration applies to the current user.
+ ///
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(scope, valueName, executionPolicy);
}
@@ -165,12 +164,12 @@ namespace System.Management.Automation.Configuration
/// Whether console prompting should happen. If the value cannot be read it defaults to false.
internal bool GetConsolePrompting()
{
- return ReadValueFromFile(ConfigScope.SystemWide, "ConsolePrompting");
+ return ReadValueFromFile(ConfigScope.AllUsers, "ConsolePrompting");
}
internal void SetConsolePrompting(bool shouldPrompt)
{
- WriteValueToFile(ConfigScope.SystemWide, "ConsolePrompting", shouldPrompt);
+ WriteValueToFile(ConfigScope.AllUsers, "ConsolePrompting", shouldPrompt);
}
///
@@ -185,12 +184,12 @@ namespace System.Management.Automation.Configuration
/// Boolean indicating whether Update-Help should prompt. If the value cannot be read, it defaults to false.
internal bool GetDisablePromptToUpdateHelp()
{
- return ReadValueFromFile(ConfigScope.SystemWide, "DisablePromptToUpdateHelp");
+ return ReadValueFromFile(ConfigScope.AllUsers, "DisablePromptToUpdateHelp");
}
internal void SetDisablePromptToUpdateHelp(bool prompt)
{
- WriteValueToFile(ConfigScope.SystemWide, "DisablePromptToUpdateHelp", prompt);
+ WriteValueToFile(ConfigScope.AllUsers, "DisablePromptToUpdateHelp", prompt);
}
///
@@ -198,7 +197,40 @@ namespace System.Management.Automation.Configuration
///
internal string[] GetExperimentalFeatures()
{
- return ReadValueFromFile(ConfigScope.SystemWide, "ExperimentalFeatures", Utils.EmptyArray());
+ string[] features = Array.Empty();
+ if (File.Exists(perUserConfigFile))
+ {
+ features = ReadValueFromFile(ConfigScope.CurrentUser, "ExperimentalFeatures", Array.Empty());
+ }
+
+ if (features.Length == 0)
+ {
+ features = ReadValueFromFile(ConfigScope.AllUsers, "ExperimentalFeatures", Array.Empty());
+ }
+
+ return features;
+ }
+
+ ///
+ /// Set the enabled list of experimental features in the config file.
+ ///
+ /// The ConfigScope of the configuration file to update.
+ /// The name of the experimental feature to change in the configuration.
+ /// If true, add to configuration; otherwise, remove from configuration.
+ internal void SetExperimentalFeatures(ConfigScope scope, string featureName, bool setEnabled)
+ {
+ var features = new List(GetExperimentalFeatures());
+ bool containsFeature = features.Contains(featureName);
+ if (setEnabled && !containsFeature)
+ {
+ features.Add(featureName);
+ WriteValueToFile(scope, "ExperimentalFeatures", features.ToArray());
+ }
+ else if (!setEnabled && containsFeature)
+ {
+ features.Remove(featureName);
+ WriteValueToFile(scope, "ExperimentalFeatures", features.ToArray());
+ }
}
///
@@ -218,7 +250,7 @@ namespace System.Management.Automation.Configuration
///
internal string GetSysLogIdentity()
{
- string identity = ReadValueFromFile(ConfigScope.SystemWide, "LogIdentity");
+ string identity = ReadValueFromFile(ConfigScope.AllUsers, "LogIdentity");
if (string.IsNullOrEmpty(identity) ||
identity.Equals(LogDefaultValue, StringComparison.OrdinalIgnoreCase))
@@ -236,7 +268,7 @@ namespace System.Management.Automation.Configuration
///
internal PSLevel GetLogLevel()
{
- string levelName = ReadValueFromFile(ConfigScope.SystemWide, "LogLevel");
+ string levelName = ReadValueFromFile(ConfigScope.AllUsers, "LogLevel");
PSLevel level;
if (string.IsNullOrEmpty(levelName) ||
@@ -266,7 +298,7 @@ namespace System.Management.Automation.Configuration
///
internal PSChannel GetLogChannels()
{
- string values = ReadValueFromFile(ConfigScope.SystemWide, "LogChannels");
+ string values = ReadValueFromFile(ConfigScope.AllUsers, "LogChannels");
PSChannel result = 0;
if (!string.IsNullOrEmpty(values))
@@ -305,7 +337,7 @@ namespace System.Management.Automation.Configuration
///
internal PSKeyword GetLogKeywords()
{
- string values = ReadValueFromFile(ConfigScope.SystemWide, "LogKeywords");
+ string values = ReadValueFromFile(ConfigScope.AllUsers, "LogKeywords");
PSKeyword result = 0;
if (!string.IsNullOrEmpty(values))
@@ -516,6 +548,16 @@ namespace System.Management.Automation.Configuration
/// The value to write.
private void WriteValueToFile(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(scope, key, value, true);
}
diff --git a/src/System.Management.Automation/engine/Utils.cs b/src/System.Management.Automation/engine/Utils.cs
index c17782be1a..848a8b7d4e 100644
--- a/src/System.Management.Automation/engine/Utils.cs
+++ b/src/System.Management.Automation/engine/Utils.cs
@@ -488,10 +488,10 @@ namespace System.Management.Automation
///
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(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
}
///
- /// 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.
///
diff --git a/src/System.Management.Automation/resources/ExperimentalFeatureStrings.resx b/src/System.Management.Automation/resources/ExperimentalFeatureStrings.resx
new file mode 100644
index 0000000000..9823f54d51
--- /dev/null
+++ b/src/System.Management.Automation/resources/ExperimentalFeatureStrings.resx
@@ -0,0 +1,67 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ No experimental feature was found that matched the name '{0}'.
+
+
+ Enabling and disabling experimental features do not take effect until next start of PowerShell.
+
+
diff --git a/src/System.Management.Automation/security/SecuritySupport.cs b/src/System.Management.Automation/security/SecuritySupport.cs
index dd89eb4ee6..7deb1bcfd1 100644
--- a/src/System.Management.Automation/security/SecuritySupport.cs
+++ b/src/System.Management.Automation/security/SecuritySupport.cs
@@ -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;
diff --git a/test/csharp/test_PSConfiguration.cs b/test/csharp/test_PSConfiguration.cs
index e474ff699a..173dea6810 100644
--- a/test/csharp/test_PSConfiguration.cs
+++ b/test/csharp/test_PSConfiguration.cs
@@ -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);
}
}
-}
\ No newline at end of file
+}
diff --git a/test/powershell/engine/Basic/DefaultCommands.Tests.ps1 b/test/powershell/engine/Basic/DefaultCommands.Tests.ps1
index a35281c149..d42a67880e 100644
--- a/test/powershell/engine/Basic/DefaultCommands.Tests.ps1
+++ b/test/powershell/engine/Basic/DefaultCommands.Tests.ps1
@@ -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 )
diff --git a/test/powershell/engine/ExperimentalFeature/EnableDisable-ExperimentalFeature.Tests.ps1 b/test/powershell/engine/ExperimentalFeature/EnableDisable-ExperimentalFeature.Tests.ps1
new file mode 100644
index 0000000000..aec3feddf7
--- /dev/null
+++ b/test/powershell/engine/ExperimentalFeature/EnableDisable-ExperimentalFeature.Tests.ps1
@@ -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: " -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: " -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 " 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"
+ }
+}
diff --git a/test/powershell/engine/ExperimentalFeature/Get-ExperimentalFeature.Tests.ps1 b/test/powershell/engine/ExperimentalFeature/Get-ExperimentalFeature.Tests.ps1
index 4681b0660e..6841079838 100644
--- a/test/powershell/engine/ExperimentalFeature/Get-ExperimentalFeature.Tests.ps1
+++ b/test/powershell/engine/ExperimentalFeature/Get-ExperimentalFeature.Tests.ps1
@@ -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
}
}
diff --git a/test/powershell/engine/Help/HelpSystem.Tests.ps1 b/test/powershell/engine/Help/HelpSystem.Tests.ps1
index 5c796105f4..10654c8a62 100644
--- a/test/powershell/engine/Help/HelpSystem.Tests.ps1
+++ b/test/powershell/engine/Help/HelpSystem.Tests.ps1
@@ -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 {
diff --git a/test/tools/TestMetadata.json b/test/tools/TestMetadata.json
index fbfcbcf45c..c49ab1758c 100644
--- a/test/tools/TestMetadata.json
+++ b/test/tools/TestMetadata.json
@@ -1,5 +1,5 @@
{
"ExperimentalFeatures": {
- "ExpTest.FeatureOne": [ "test/powershell/engine/ExperimentalFeature" ]
+ "ExpTest.FeatureOne": [ "test/powershell/engine/ExperimentalFeature/ExperimentalFeature.Basic.Tests.ps1" ]
}
}