Merge pull request #939 from PowerShell/andschwa/consolehost

Converge the console hosts
This commit is contained in:
Andy Schwartzmeyer 2016-05-17 14:44:35 -07:00
commit 85f45399c3
59 changed files with 1092 additions and 3890 deletions

View File

@ -7,7 +7,6 @@ matrix:
- os: osx
osx_image: xcode7.3
language: generic
env: PATH+=/usr/local/share/dotnet
git:
submodules: false
before_install:

View File

@ -221,10 +221,10 @@ function New-PSOptions {
[switch]$FullCLR
)
$Top = if ($FullCLR) {
"$PSScriptRoot\src\Microsoft.PowerShell.ConsoleHost"
if ($FullCLR) {
$Top = "$PSScriptRoot/src/Microsoft.PowerShell.ConsoleHost"
} else {
"$PSScriptRoot/src/Microsoft.PowerShell.CoreConsoleHost"
$Top = "$PSScriptRoot/src/powershell"
}
Write-Verbose "Top project directory is $Top"
@ -315,7 +315,7 @@ function Start-PSPester {
[string]$Directory = "$PSScriptRoot/test/powershell"
)
& (Get-PSOutput) -c "Invoke-Pester $Flags $Directory/$Tests"
& (Get-PSOutput) -noprofile -c "Invoke-Pester $Flags $Directory/$Tests"
if ($LASTEXITCODE -ne 0) {
throw "$LASTEXITCODE Pester tests failed"
}

View File

@ -33,17 +33,17 @@ test_script:
$ErrorActionPreference = 'Stop'
#
# CoreCLR
$env:CoreOutput = "$pwd\src\Microsoft.PowerShell.CoreConsoleHost\bin\Debug\netcoreapp1.0\win81-x64\publish"
$env:CoreOutput = Split-Path -Parent (Get-PSOutput -Options (New-PSOptions -Publish))
Write-Host -Foreground Green 'Run CoreCLR tests'
$testResultsFile = "$pwd\TestsResults.xml"
& ("$env:CoreOutput\powershell.exe") -c "Invoke-Pester test/powershell -OutputFormat NUnitXml -OutputFile $testResultsFile"
& ("$env:CoreOutput\powershell.exe") -noprofile -noninteractive -c "Invoke-Pester test/powershell -OutputFormat NUnitXml -OutputFile $testResultsFile"
(New-Object 'System.Net.WebClient').UploadFile("https://ci.appveyor.com/api/testresults/nunit/$($env:APPVEYOR_JOB_ID)", (Resolve-Path $testResultsFile))
#
# FullCLR
$env:FullOutput = "$pwd\src\Microsoft.PowerShell.ConsoleHost\bin\Debug\net451"
$env:FullOutput = Split-Path -Parent (Get-PSOutput -Options (New-PSOptions -FullCLR))
Write-Host -Foreground Green 'Run FullCLR tests'
$testResultsFileFullCLR = "$pwd\TestsResults.FullCLR.xml"
Start-DevPSGitHub -binDir $env:FullOutput -NoNewWindow -ArgumentList '-command', "Import-Module .\src\Modules\Pester; Invoke-Pester test/fullCLR -OutputFormat NUnitXml -OutputFile $testResultsFileFullCLR"
Start-DevPSGitHub -binDir $env:FullOutput -NoNewWindow -ArgumentList '-noprofile', '-noninteractive', '-command', "Import-Module .\src\Modules\Pester; Invoke-Pester test/fullCLR -OutputFormat NUnitXml -OutputFile $testResultsFileFullCLR"
(New-Object 'System.Net.WebClient').UploadFile("https://ci.appveyor.com/api/testresults/nunit/$($env:APPVEYOR_JOB_ID)", (Resolve-Path $testResultsFileFullCLR))
#
# Fail the build, if tests failed

View File

@ -1187,12 +1187,12 @@
"src/monad/monad/miscfiles/modules/Microsoft.PowerShell.Archive/ArchiveResources.psd1": "src/Modules/Microsoft.PowerShell.Archive/ArchiveResources.psd1",
"src/monad/monad/miscfiles/modules/Microsoft.PowerShell.Archive/Microsoft.PowerShell.Archive.psd1": "src/Modules/Microsoft.PowerShell.Archive/Microsoft.PowerShell.Archive.psd1",
"src/monad/monad/miscfiles/modules/Microsoft.PowerShell.Archive/Microsoft.PowerShell.Archive.psm1": "src/Modules/Microsoft.PowerShell.Archive/Microsoft.PowerShell.Archive.psm1",
"src/monad/monad/miscfiles/modules/Microsoft.PowerShell.Diagnostics/CoreClr/Microsoft.PowerShell.Diagnostics.psd1": "src/Microsoft.PowerShell.CoreConsoleHost/Modules/Microsoft.PowerShell.Diagnostics/Microsoft.PowerShell.Diagnostics.psd1",
"src/monad/monad/miscfiles/modules/Microsoft.PowerShell.Diagnostics/CoreClr/Microsoft.PowerShell.Diagnostics.psd1": "src/powershell/Modules/Microsoft.PowerShell.Diagnostics/Microsoft.PowerShell.Diagnostics.psd1",
"src/monad/monad/miscfiles/modules/Microsoft.PowerShell.Diagnostics/Microsoft.PowerShell.Diagnostics.psd1": "src/Microsoft.PowerShell.ConsoleHost/Modules/Microsoft.PowerShell.Diagnostics/Microsoft.PowerShell.Diagnostics.psd1",
"src/monad/monad/miscfiles/modules/Microsoft.PowerShell.Host/Microsoft.PowerShell.Host.psd1": "src/Modules/Microsoft.PowerShell.Host/Microsoft.PowerShell.Host.psd1",
"src/monad/monad/miscfiles/modules/Microsoft.PowerShell.Management/Microsoft.PowerShell.Management.psd1": "src/Modules/Microsoft.PowerShell.Management/Microsoft.PowerShell.Management.psd1",
"src/monad/monad/miscfiles/modules/Microsoft.PowerShell.Security/Microsoft.PowerShell.Security.psd1": "src/Modules/Microsoft.PowerShell.Security/Microsoft.PowerShell.Security.psd1",
"src/monad/monad/miscfiles/modules/Microsoft.PowerShell.Utility/CoreClr/Microsoft.PowerShell.Utility.psd1": "src/Microsoft.PowerShell.CoreConsoleHost/Modules/Microsoft.PowerShell.Utility/Microsoft.PowerShell.Utility.psd1",
"src/monad/monad/miscfiles/modules/Microsoft.PowerShell.Utility/CoreClr/Microsoft.PowerShell.Utility.psd1": "src/powershell/Modules/Microsoft.PowerShell.Utility/Microsoft.PowerShell.Utility.psd1",
"src/monad/monad/miscfiles/modules/Microsoft.PowerShell.Utility/Microsoft.PowerShell.Utility.psd1": "src/Microsoft.PowerShell.ConsoleHost/Modules/Microsoft.PowerShell.Utility/Microsoft.PowerShell.Utility.psd1",
"src/monad/monad/miscfiles/modules/Microsoft.PowerShell.Utility/Microsoft.PowerShell.Utility.psm1": "src/Modules/Microsoft.PowerShell.Utility/Microsoft.PowerShell.Utility.psm1",
"src/monad/monad/miscfiles/modules/Microsoft.WSMan.Management/Microsoft.WSMan.Management.psd1": "src/Modules/Microsoft.WSMan.Management/Microsoft.WSMan.Management.psd1",

View File

@ -2,12 +2,16 @@ using System.Diagnostics.CodeAnalysis;
using System.Reflection;
using System.Resources;
using System.Runtime.CompilerServices;
#if !CORECLR
using System.Runtime.ConstrainedExecution;
using System.Security.Permissions;
#endif
#if CORECLR
[assembly:AssemblyCulture("")]
[assembly:NeutralResourcesLanguage("en-US")]
[assembly:InternalsVisibleTo("powershell-tests")]
#else
[assembly:AssemblyConfiguration("")]
[assembly:AssemblyInformationalVersionAttribute (@"10.0.10011.16384")]
[assembly:ReliabilityContractAttribute(Consistency.MayCorruptAppDomain, Cer.MayFail)]
@ -15,14 +19,16 @@ using System.Security.Permissions;
[assembly:AssemblyDescription("Microsoft Windows PowerShell Console Host")]
[assembly:System.Runtime.Versioning.TargetFrameworkAttribute(".NETFramework,Version=v4.5")]
[assembly:System.Reflection.AssemblyFileVersion("10.0.10011.16384")]
[assembly:AssemblyKeyFileAttribute(@"..\signing\visualstudiopublic.snk")]
[assembly:System.Reflection.AssemblyDelaySign(true)]
#endif
[assembly:System.Runtime.InteropServices.ComVisible(false)]
[assembly:System.Reflection.AssemblyVersion("3.0.0.0")]
[assembly:System.Reflection.AssemblyProduct("Microsoft (R) Windows (R) Operating System")]
[assembly:System.Reflection.AssemblyCopyright("Copyright (c) Microsoft Corporation. All rights reserved.")]
[assembly:System.Reflection.AssemblyCompany("Microsoft Corporation")]
[assembly:System.Reflection.AssemblyFileVersion("10.0.10011.16384")]
[assembly:AssemblyKeyFileAttribute(@"..\signing\visualstudiopublic.snk")]
[assembly:System.Reflection.AssemblyDelaySign(true)]
internal static class AssemblyStrings
{

View File

@ -118,13 +118,13 @@ namespace Microsoft.PowerShell
}
}
internal bool ReadFromStdin
internal bool ExplicitReadCommandsFromStdin
{
get
{
Dbg.Assert(dirty, "Parse has not been called yet");
return readFromStdin;
return explicitReadCommandsFromStdin;
}
}
@ -365,6 +365,12 @@ namespace Microsoft.PowerShell
switchKey = switchKey.Substring(1);
// chop off the second dash so we're agnostic wrt specifying - or --
if (SpecialCharacters.IsDash(switchKey[0]))
{
switchKey = switchKey.Substring(1);
}
if (MatchSwitch(switchKey, "help", "h") || MatchSwitch(switchKey, "?", "?"))
{
showHelp = true;
@ -482,7 +488,7 @@ namespace Microsoft.PowerShell
{
// the arg to -file is -, which is secret code for "read the commands from stdin with prompts"
readFromStdin = true;
explicitReadCommandsFromStdin = true;
noPrompt = false;
}
else
@ -844,7 +850,7 @@ namespace Microsoft.PowerShell
{
// the arg to -command is -, which is secret code for "read the commands from stdin with no prompts"
readFromStdin = true;
explicitReadCommandsFromStdin = true;
noPrompt = true;
++i;
@ -859,7 +865,7 @@ namespace Microsoft.PowerShell
return false;
}
if (!parent.IsStandardInputRedirected)
if (!Console.IsInputRedirected)
{
ui.WriteErrorLine(CommandLineParameterParserStrings.StdinNotRedirected);
showHelp = true;
@ -968,7 +974,7 @@ namespace Microsoft.PowerShell
// default is sta.
private bool? staMode = null;
private bool noExit = true;
private bool readFromStdin;
private bool explicitReadCommandsFromStdin;
private bool noPrompt;
private string commandLineCommand;
private bool wasCommandEncoded;

View File

@ -1,3 +1,4 @@
#if !PORTABLE
/********************************************************************++
Copyright (c) Microsoft Corporation. All rights reserved.
--********************************************************************/
@ -3685,4 +3686,4 @@ namespace Microsoft.PowerShell
private static PSTraceSource tracer = PSTraceSource.GetTracer("ConsoleControl", "Console control methods");
}
} // namespace
#endif

View File

@ -170,8 +170,15 @@ namespace Microsoft.PowerShell
{
try
{
var profileDir = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData) +
@"\Microsoft\Windows\PowerShell";
string profileDir;
if (Platform.IsWindows)
{
profileDir = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData) +
@"\Microsoft\Windows\PowerShell";
} else
{
profileDir = System.IO.Path.Combine(Environment.GetEnvironmentVariable("HOME"), ".powershell");
}
if (!Directory.Exists(profileDir))
{
@ -190,9 +197,11 @@ namespace Microsoft.PowerShell
System.Threading.Thread.CurrentThread.Name = "ConsoleHost main thread";
theConsoleHost = ConsoleHost.CreateSingletonInstance(configuration);
#if !PORTABLE
theConsoleHost.BindBreakHandler();
#endif
PSHost.IsStdOutputRedirected = theConsoleHost.IsStandardOutputRedirected;
PSHost.IsStdOutputRedirected = Console.IsOutputRedirected;
if (args == null)
{
@ -268,6 +277,7 @@ namespace Microsoft.PowerShell
#if !PORTABLE
/// <summary>
///
/// The break handler for the program. Dispatches a break event to the current Executor.
@ -307,6 +317,7 @@ namespace Microsoft.PowerShell
return false;
}
}
#endif
private static bool BreakIntoDebugger()
{
@ -425,8 +436,10 @@ namespace Microsoft.PowerShell
// call the console APIs directly, instead of ui.rawui.FlushInputHandle, as ui may be finalized
// already if this thread is lagging behind the main thread.
#if !PORTABLE
ConsoleHandle handle = ConsoleControl.GetInputHandle();
ConsoleControl.FlushConsoleInputBuffer(handle);
#endif
ConsoleHost.SingletonInstance.breakHandlerThread = null;
}
@ -1019,11 +1032,13 @@ namespace Microsoft.PowerShell
#endif
}
#if !PORTABLE
private void BindBreakHandler()
{
breakHandlerGcHandle = GCHandle.Alloc(new ConsoleControl.BreakHandler(MyBreakHandler));
ConsoleControl.AddBreakHandler((ConsoleControl.BreakHandler)breakHandlerGcHandle.Target);
}
#endif
#if !CORECLR // Not used on NanoServer: CurrentDomain.UnhandledException not supported on CoreCLR
private void UnhandledExceptionHandler(object sender, UnhandledExceptionEventArgs args)
@ -1076,9 +1091,11 @@ namespace Microsoft.PowerShell
{
if (!isDisposed)
{
#if !PORTABLE
Dbg.Assert(breakHandlerGcHandle != null, "break handler should be set");
ConsoleControl.RemoveBreakHandler();
breakHandlerGcHandle.Free();
#endif
if (isDisposingNotFinalizing)
{
@ -1193,7 +1210,7 @@ namespace Microsoft.PowerShell
//If this shell is invoked in minishell interop mode and error is redirected,
//always write data in error stream in xml format.
if (IsInteractive == false && IsStandardErrorRedirected && wasInitialCommandEncoded)
if (IsInteractive == false && Console.IsErrorRedirected && wasInitialCommandEncoded)
{
format = Serialization.DataFormat.XML;
}
@ -1205,19 +1222,7 @@ namespace Microsoft.PowerShell
{
get
{
if (IsInteractive)
{
return false;
}
if (
(OutputFormat != Serialization.DataFormat.Text)
|| (IsStandardInputRedirected && !ui.ReadFromStdin))
{
return true;
}
return false;
return !IsInteractive && ((OutputFormat != Serialization.DataFormat.Text) || Console.IsInputRedirected);
}
}
@ -1236,7 +1241,7 @@ namespace Microsoft.PowerShell
new WrappedSerializer(
outputFormat,
"Output",
IsStandardOutputRedirected ? StandardOutputWriter : ConsoleTextWriter);
Console.IsOutputRedirected ? Console.Out : ConsoleTextWriter);
}
return outputSerializer;
}
@ -1252,7 +1257,7 @@ namespace Microsoft.PowerShell
new WrappedSerializer(
ErrorFormat,
"Error",
IsStandardErrorRedirected ? StandardErrorWriter : ConsoleTextWriter);
Console.IsErrorRedirected ? Console.Error : ConsoleTextWriter);
}
return errorSerializer;
}
@ -1328,7 +1333,7 @@ namespace Microsoft.PowerShell
inputFormat = cpp.InputFormat;
wasInitialCommandEncoded = cpp.WasInitialCommandEncoded;
ui.ReadFromStdin = cpp.ReadFromStdin;
ui.ReadFromStdin = cpp.ExplicitReadCommandsFromStdin || Console.IsInputRedirected;
ui.NoPrompt = cpp.NoPrompt;
ui.ThrowOnReadAndPrompt = cpp.ThrowOnReadAndPrompt;
noExit = cpp.NoExit;
@ -1486,8 +1491,15 @@ namespace Microsoft.PowerShell
private bool LoadPSReadline()
{
return ((cpp.InitialCommand == null && cpp.File == null) || cpp.NoExit) &&
(!IsStandardInputRedirected && !cpp.NonInteractive && !cpp.ReadFromStdin);
// Don't load PSReadline if:
// * we don't think the process will be interactive, e.g. -command or -file
// - exception: when -noexit is specified, we will be interactive after the command/file finishes
// * -noniteractive: this should be obvious, they've asked that we don't every prompt
//
// Note that PSReadline doesn't support redirected stdin/stdout, but we don't check that here because
// a future version might, and we should automatically load it at that unknown point in the future.
// PSReadline will ideally fall back to Console.ReadLine or whatever when stdin/stdout is redirected.
return ((cpp.InitialCommand == null && cpp.File == null) || cpp.NoExit) && !cpp.NonInteractive;
}
/// <summary>
@ -1528,7 +1540,7 @@ namespace Microsoft.PowerShell
{
// Create and open Runspace with PSReadline.
defaultImportModulesList = DefaultInitialSessionState.Modules;
DefaultInitialSessionState.ImportPSModule(new[] { "PSReadline" });
DefaultInitialSessionState.ImportPSModule(new[] { "PSReadLine" });
consoleRunspace = RunspaceFactory.CreateRunspace(this, DefaultInitialSessionState);
try
{
@ -1787,6 +1799,9 @@ namespace Microsoft.PowerShell
if (AstSearcher.IsUsingDollarInput(parsedInput))
{
executionOptions |= Executor.ExecutionOptions.ReadInputObjects;
// We will consume all of the input to pass to the script, so don't try to read commands from stdin.
ui.ReadFromStdin = false;
}
exec.ExecuteCommandAsyncHelper(tempPipeline, out e1, executionOptions);
@ -1846,6 +1861,9 @@ namespace Microsoft.PowerShell
if (AstSearcher.IsUsingDollarInput(parsedInput))
{
executionOptions |= Executor.ExecutionOptions.ReadInputObjects;
// We will consume all of the input to pass to the script, so don't try to read commands from stdin.
ui.ReadFromStdin = false;
}
exec.ExecuteCommandAsyncHelper(tempPipeline, out e1, executionOptions);
@ -2049,205 +2067,6 @@ namespace Microsoft.PowerShell
#endregion non-overrides
#region stdio redirection
private delegate void InitializeStandardHandleDelegate(NakedWin32Handle handle);
[ArchitectureSensitive]
private bool IsStandardHandleRedirected(
ConsoleControl.StandardHandleId handleId,
ref bool isHandleRedirectionDetermined,
ref bool isHandleRedirected,
InitializeStandardHandleDelegate handleInit)
{
// lock because we update a bunch of instance vars here...
lock (hostGlobalLock)
{
if (!isHandleRedirectionDetermined)
{
isHandleRedirected = false;
NakedWin32Handle stdHandle = ConsoleControl.GetStdHandle(handleId);
SafeFileHandle sfh = new SafeFileHandle(stdHandle, false);
if (!sfh.IsInvalid)
{
var fileType = ConsoleControl.NativeMethods.GetFileType(stdHandle);
if (fileType != ConsoleControl.NativeMethods.FileType.Char)
{
// stdHandle is not a console handle; so it has been redirected.
isHandleRedirected = true;
handleInit(stdHandle);
}
}
isHandleRedirectionDetermined = true;
}
}
return isHandleRedirected;
}
internal bool IsStandardOutputRedirected
{
get
{
if (initStandardOutDelegate == null)
{
initStandardOutDelegate = new InitializeStandardHandleDelegate(InitializeStandardOutputWriter);
}
return
IsStandardHandleRedirected(
ConsoleControl.StandardHandleId.Output,
ref isStandardOutputRedirectionDetermined,
ref isStandardOutputRedirected,
initStandardOutDelegate);
}
}
internal bool IsStandardInputRedirected
{
get
{
if (initStandardInDelegate == null)
{
initStandardInDelegate = new InitializeStandardHandleDelegate(InitializeStandardInputReader);
}
return
IsStandardHandleRedirected(
ConsoleControl.StandardHandleId.Input,
ref isStandardInputRedirectionDetermined,
ref isStandardInputRedirected,
initStandardInDelegate);
}
}
internal bool IsStandardErrorRedirected
{
get
{
if (initStandardErrorDelegate == null)
{
initStandardErrorDelegate = new InitializeStandardHandleDelegate(InitializeStandardErrorWriter);
}
return
IsStandardHandleRedirected(
ConsoleControl.StandardHandleId.Error,
ref isStandardErrorRedirectionDetermined,
ref isStandardErrorRedirected,
initStandardErrorDelegate);
}
}
[ArchitectureSensitive]
private void InitializeStandardInputReader(NakedWin32Handle stdHandle)
{
Dbg.Assert(standardInputReader == null, "standardInputReader should not exist at this point");
// stdin has been redirected. Use ReadFile instead of ReadConsole.
uint codePage = (uint) ConsoleControl.NativeMethods.GetConsoleCP();
Encoding encoding = Encoding.GetEncoding((int) codePage);
try
{
Stream s =
new FileStream(
// since stdHandle was retreived not with an open, flag its safe wrapper as not needing to be closed.
new SafeFileHandle(stdHandle, false),
FileAccess.Read);
// The "false" flag here means "don't detect the byte order mark in the input stream." I hope that's the right
// thing.
#if CORECLR // There is no TextReader.Synchronized on CoreCLR
standardInputReader = new StreamReader(s, encoding, false);
#else
standardInputReader = TextReader.Synchronized(new StreamReader(s, encoding, false));
#endif
}
catch (System.IO.IOException)
{
#if CORECLR // There is no TextReader.Synchronized on CoreCLR
standardInputReader = new StringReader("");
#else
standardInputReader = TextReader.Synchronized(new StringReader(""));
#endif
}
}
[ArchitectureSensitive]
private void InitializeStandardOutputWriter(NakedWin32Handle stdHandle)
{
Dbg.Assert(standardOutputWriter == null, "standardOutputWriter should not exist at this point");
standardOutputWriter = Console.Out;
}
[ArchitectureSensitive]
private void InitializeStandardErrorWriter(NakedWin32Handle stdHandle)
{
Dbg.Assert(standardErrorWriter == null, "standardErrorWriter should not exist at this point");
standardErrorWriter = Console.Error;
}
internal TextWriter StandardOutputWriter
{
get
{
if (IsStandardOutputRedirected)
{
Dbg.Assert(standardOutputWriter != null, "standardOutputWriter should be initialized");
return standardOutputWriter;
}
return null;
}
}
internal TextWriter StandardErrorWriter
{
get
{
if (IsStandardErrorRedirected)
{
Dbg.Assert(standardErrorWriter != null, "standardErrorWriter should be initialized");
return standardErrorWriter;
}
return null;
}
}
internal TextReader StandardInReader
{
get
{
if (IsStandardInputRedirected)
{
Dbg.Assert(standardInputReader != null, "standardInputReader should be initialized");
return standardInputReader;
}
return null;
}
}
#endregion stdio redirection
#region debugger
/// <summary>
@ -2544,12 +2363,6 @@ namespace Microsoft.PowerShell
bool inBlockMode = false;
bool previousResponseWasEmpty = false;
StringBuilder inputBlock = new StringBuilder();
bool displayPrompt = false;
// If the prompt has not been displayed in the native code or if the input loop is nested, we need to display the prompt here
if (!parent.promptDisplayedInNativeCode || inputLoopIsNested)
{
displayPrompt = true;
}
while (!parent.ShouldEndSession && !shouldExit)
{
@ -2559,53 +2372,42 @@ namespace Microsoft.PowerShell
string prompt = null;
string line = null;
if (displayPrompt)
if (!ui.NoPrompt)
{
if (!ui.NoPrompt)
if (inBlockMode)
{
if (inBlockMode)
{
// use a special prompt that denotes block mode
// use a special prompt that denotes block mode
prompt = ">> ";
}
else
{
// Make sure the cursor is at the start of the line - some external programs don't
// write a newline, so we do that for them.
if (ui.RawUI.CursorPosition.X != 0)
ui.WriteLine();
// Evaluate any suggestions
if(! previousResponseWasEmpty)
{
EvaluateSuggestions(ui);
}
// Then output the prompt
if (this.parent.InDebugMode)
{
prompt = EvaluateDebugPrompt();
}
if (prompt == null)
{
prompt = EvaluatePrompt();
}
}
ui.Write(prompt);
prompt = ">> ";
}
previousResponseWasEmpty = false;
// There could be a profile. So there could be a user defined custom readline command
line = ui.ReadLineWithTabCompletion(exec, useUserDefinedCustomReadLine: true);
else
{
// Make sure the cursor is at the start of the line - some external programs don't
// write a newline, so we do that for them.
if (ui.RawUI.CursorPosition.X != 0)
ui.WriteLine();
// Evaluate any suggestions
if(! previousResponseWasEmpty)
{
EvaluateSuggestions(ui);
}
// Then output the prompt
if (this.parent.InDebugMode)
{
prompt = EvaluateDebugPrompt();
}
if (prompt == null)
{
prompt = EvaluatePrompt();
}
}
ui.Write(prompt);
}
else
{
previousResponseWasEmpty = false;
// There are no profiles. So there is no user defined custom readline command
line = ui.ReadLineWithTabCompletion(exec, useUserDefinedCustomReadLine: false);
displayPrompt = true;
}
previousResponseWasEmpty = false;
// There could be a profile. So there could be a user defined custom readline command
line = ui.ReadLineWithTabCompletion(exec);
// line will be null in the case that Ctrl-C terminated the input
@ -2623,7 +2425,7 @@ namespace Microsoft.PowerShell
}
inBlockMode = false;
if (parent.IsStandardInputRedirected)
if (Console.IsInputRedirected)
{
// null is also the result of reading stdin to EOF.
@ -2711,7 +2513,7 @@ namespace Microsoft.PowerShell
}
else
{
if (parent.IsRunningAsync)
if (parent.IsRunningAsync && !parent.IsNested)
{
exec.ExecuteCommandAsync(line, out e, Executor.ExecutionOptions.AddOutputter | Executor.ExecutionOptions.AddToHistory);
}
@ -3047,7 +2849,9 @@ namespace Microsoft.PowerShell
/// </summary>
private RunspaceRef runspaceRef;
#if !PORTABLE
private GCHandle breakHandlerGcHandle;
#endif
private System.Threading.Thread breakHandlerThread;
private bool isDisposed;
internal ConsoleHostUserInterface ui;
@ -3076,18 +2880,6 @@ namespace Microsoft.PowerShell
private bool shouldEndSession;
private int beginApplicationNotifyCount;
private bool isStandardOutputRedirectionDetermined;
private bool isStandardOutputRedirected;
private TextWriter standardOutputWriter;
private bool isStandardErrorRedirectionDetermined;
private bool isStandardErrorRedirected;
private TextWriter standardErrorWriter;
private bool isStandardInputRedirectionDetermined;
private bool isStandardInputRedirected;
private TextReader standardInputReader;
private InitializeStandardHandleDelegate initStandardOutDelegate;
private InitializeStandardHandleDelegate initStandardInDelegate;
private InitializeStandardHandleDelegate initStandardErrorDelegate;
private ConsoleTextWriter consoleWriter;
private WrappedSerializer outputSerializer;
private WrappedSerializer errorSerializer;

View File

@ -1,3 +1,4 @@
#if !PORTABLE
/********************************************************************++
Copyright (c) Microsoft Corporation. All rights reserved.
--********************************************************************/
@ -1499,3 +1500,280 @@ namespace Microsoft.PowerShell
}
} // namespace
#else
// Managed code only implementation for portability
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Management.Automation;
using System.Management.Automation.Runspaces;
using System.Management.Automation.Host;
using System.Globalization;
using System.Reflection;
using System.Runtime.InteropServices;
namespace Microsoft.PowerShell
{
// this is all originally from https://msdn.microsoft.com/en-us/library/ee706570%28v=vs.85%29.aspx
internal sealed class ConsoleHostRawUserInterface : PSHostRawUserInterface
{
private ConsoleColor defaultForeground = ConsoleColor.Gray;
private ConsoleColor defaultBackground = ConsoleColor.Black;
private ConsoleHostUserInterface parent = null;
internal ConsoleHostRawUserInterface(ConsoleHostUserInterface mshConsole) : base()
{
defaultForeground = ForegroundColor;
defaultBackground = BackgroundColor;
parent = mshConsole;
}
/// <summary>
/// Gets or sets the background color of the displayed text.
/// This maps to the corresponding Console.Background property.
/// </summary>
public override ConsoleColor BackgroundColor
{
get { return Console.BackgroundColor; }
set { Console.BackgroundColor = value; }
}
// TODO: Make wrap width user-customizable.
private static Size WrapSize = new Size(80, 40);
/// <summary>
/// Gets or sets the size of the host buffer. In this example the
/// buffer size is adapted from the Console buffer size members.
/// </summary>
public override Size BufferSize
{
get
{
// When stdout is redirected, the buffer size is (0, 0);
// however, this is still queried for use in formatting, so we
// provide the WrapSize.
if (Console.IsOutputRedirected)
{
return WrapSize;
}
else
{
return new Size(Console.BufferWidth, Console.BufferHeight);
}
}
set { Console.SetBufferSize(value.Width, value.Height); }
}
/// <summary>
/// Gets or sets the cursor position. In this example this
/// functionality is not needed so the property throws a
/// NotImplementException exception.
/// </summary>
public override Coordinates CursorPosition
{
get { return new Coordinates(Console.CursorLeft, Console.CursorTop); }
set { Console.SetCursorPosition(value.X, value.Y); }
}
/// <summary>
/// Gets or sets the size of the displayed cursor. In this example
/// the cursor size is taken directly from the Console.CursorSize
/// property.
/// </summary>
public override int CursorSize
{
get { return Console.CursorSize; }
set { Console.CursorSize = value; }
}
/// <summary>
/// Gets or sets the foreground color of the displayed text.
/// This maps to the corresponding Console.ForgroundColor property.
/// </summary>
public override ConsoleColor ForegroundColor
{
get { return Console.ForegroundColor; }
set { Console.ForegroundColor = value; }
}
/// <summary>
/// Gets a value indicating whether the user has pressed a key. This maps
/// to the corresponding Console.KeyAvailable property.
/// </summary>
public override bool KeyAvailable
{
get { return Console.KeyAvailable; }
}
/// <summary>
/// Gets the dimensions of the largest window that could be
/// rendered in the current display, if the buffer was at the least
/// that large. This example uses the Console.LargestWindowWidth and
/// Console.LargestWindowHeight properties to determine the returned
/// value of this property.
/// </summary>
public override Size MaxPhysicalWindowSize
{
get { return new Size(Console.LargestWindowWidth, Console.LargestWindowHeight); }
}
/// <summary>
/// Gets the dimentions of the largest window size that can be
/// displayed. This example uses the Console.LargestWindowWidth and
/// console.LargestWindowHeight properties to determine the returned
/// value of this property.
/// </summary>
public override Size MaxWindowSize
{
get { return new Size(Console.LargestWindowWidth, Console.LargestWindowHeight); }
}
/// <summary>
/// Gets or sets the position of the displayed window. This example
/// uses the Console window position APIs to determine the returned
/// value of this property.
/// </summary>
public override Coordinates WindowPosition
{
get { return new Coordinates(Console.WindowLeft, Console.WindowTop); }
set { Console.SetWindowPosition(value.X, value.Y); }
}
/// <summary>
/// Gets or sets the size of the displayed window. This example
/// uses the corresponding Console window size APIs to determine the
/// returned value of this property.
/// </summary>
public override Size WindowSize
{
get
{
if (Console.IsOutputRedirected)
{
return WrapSize;
}
else
{
return new Size(Console.WindowWidth, Console.WindowHeight);
}
}
set { Console.SetWindowSize(value.Width, value.Height); }
}
/// <summary>
/// Cached Window Title, for systems that needs it
/// </summary>
private string title = String.Empty;
/// <summary>
/// Gets or sets the title of the displayed window. The example
/// maps the Console.Title property to the value of this property.
/// </summary>
public override string WindowTitle
{
get
{
// In Unix/Linux systems, Console.Title current results in a not-implemented
// exception. In that case, we return a cached copy of title that was set earlier.
// Obviously, this will not work if: 1) Title was never set in PowerShell, or 2)
// one sets the windows's title outside of PowerShell.
string result;
try
{
result = Console.Title;
}
catch (PlatformNotSupportedException)
{
return title;
}
return result;
}
set
{
Console.Title = value;
title = value;
}
}
/// <summary>
/// This API resets the input buffer. In this example this
/// functionality is not needed so the method returns nothing.
/// </summary>
public override void FlushInputBuffer()
{
}
/// <summary>
/// This API returns a rectangular region of the screen buffer. In
/// this example this functionality is not needed so the method throws
/// a NotImplementException exception.
/// </summary>
/// <param name="rectangle">Defines the size of the rectangle.</param>
/// <returns>Throws a NotImplementedException exception.</returns>
public override BufferCell[,] GetBufferContents(Rectangle rectangle)
{
throw new NotImplementedException("The method or operation is not implemented.");
}
/// <summary>
/// This API reads a pressed, released, or pressed and released keystroke
/// from the keyboard device, blocking processing until a keystroke is
/// typed that matches the specified keystroke options.
/// </summary>
/// <param name="options">Unused</param>
public override KeyInfo ReadKey(ReadKeyOptions options)
{
ConsoleKeyInfo key = Console.ReadKey();
return new KeyInfo((int)key.Key, key.KeyChar, new ControlKeyStates(), true);
}
/// <summary>
/// This API crops a region of the screen buffer. In this example
/// this functionality is not needed so the method throws a
/// NotImplementException exception.
/// </summary>
/// <param name="source">The region of the screen to be scrolled.</param>
/// <param name="destination">The region of the screen to receive the
/// source region contents.</param>
/// <param name="clip">The region of the screen to include in the operation.</param>
/// <param name="fill">The character and attributes to be used to fill all cell.</param>
public override void ScrollBufferContents(Rectangle source, Coordinates destination, Rectangle clip, BufferCell fill)
{
throw new NotImplementedException("The method or operation is not implemented.");
}
/// <summary>
/// This method copies an array of buffer cells into the screen buffer
/// at a specified location. In this example this functionality is
/// not needed so the method throws a NotImplementedException exception.
/// </summary>
/// <param name="origin">The parameter is not used.</param>
/// <param name="contents">The parameter is not used.</param>
public override void SetBufferContents(Coordinates origin,
BufferCell[,] contents)
{
throw new NotImplementedException("The method or operation is not implemented.");
}
/// <summary>
/// This method copies a given character, foreground color, and background
/// color to a region of the screen buffer. In this example this
/// functionality is not needed so the method throws a
/// NotImplementException exception./// </summary>
/// <param name="rectangle">Defines the area to be filled. </param>
/// <param name="fill">Defines the fill character.</param>
public override void SetBufferContents(Rectangle rectangle, BufferCell fill)
{
throw new NotImplementedException("The method or operation is not implemented.");
}
}
}
#endif

View File

@ -14,7 +14,9 @@ using System.Management.Automation.Internal;
using System.Management.Automation.Host;
using System.Security;
using Dbg = System.Management.Automation.Diagnostics;
#if !PORTABLE
using ConsoleHandle = Microsoft.Win32.SafeHandles.SafeFileHandle;
#endif
namespace Microsoft.PowerShell
{
@ -58,7 +60,11 @@ namespace Microsoft.PowerShell
this.parent = parent;
this.rawui = new ConsoleHostRawUserInterface(this);
isInteractiveTestToolListening = false;
#if PORTABLE
this._supportsVirtualTerminal = true;
#else
// Turn on virtual terminal if possible.
var handle = ConsoleControl.GetActiveScreenBufferHandle();
var m = ConsoleControl.GetMode(handle);
@ -71,9 +77,7 @@ namespace Microsoft.PowerShell
// systems ignore the setting.
m = ConsoleControl.GetMode(handle);
this._supportsVirtualTerminal = (m & ConsoleControl.ConsoleModes.VirtualTerminal) != 0;
isInteractiveTestToolListening = false;
isTestingShiftTab = false;
#endif
}
/// <summary>
@ -140,17 +144,7 @@ namespace Microsoft.PowerShell
///
/// </summary>
internal bool ReadFromStdin
{
get
{
return readFromStdin;
}
set
{
readFromStdin = value;
}
}
internal bool ReadFromStdin { get; set; }
/// <summary>
///
@ -284,6 +278,9 @@ namespace Microsoft.PowerShell
private object ReadLineSafe(bool isSecureString, char? printToken)
{
#if PORTABLE
throw new PlatformNotSupportedException("Cannot read secure strings!");
#else
// Don't lock (instanceLock) in here -- the caller needs to do that...
PreRead();
@ -404,8 +401,11 @@ namespace Microsoft.PowerShell
{
return result;
}
#endif
}
#if !PORTABLE
/// <summary>
///
/// Handle writing print token with proper cursor adjustment for ReadLineSafe
@ -535,7 +535,6 @@ namespace Microsoft.PowerShell
/// false otherwise
///
/// </returns>
private static bool shouldUnsetMode(
ConsoleControl.ConsoleModes flagToUnset,
ref ConsoleControl.ConsoleModes m)
@ -547,11 +546,14 @@ namespace Microsoft.PowerShell
}
return false;
}
#endif
#region WriteToConsole
internal void WriteToConsole(string value, bool transcribeResult)
{
#if !PORTABLE
ConsoleHandle handle = ConsoleControl.GetActiveScreenBufferHandle();
// Ensure that we're in the proper line-output mode. We don't lock here as it does not matter if we
@ -568,17 +570,21 @@ namespace Microsoft.PowerShell
m |= desiredMode;
ConsoleControl.SetMode(handle, m);
}
#endif
PreWrite();
// This is atomic, so we don't lock here...
#if !PORTABLE
ConsoleControl.WriteConsole(handle, value);
#else
Console.Out.Write(value);
#endif
if (isInteractiveTestToolListening && parent.IsStandardOutputRedirected)
if (isInteractiveTestToolListening && Console.IsOutputRedirected)
{
Dbg.Assert(parent.StandardOutputWriter != null, "stdout writer should be initialized");
parent.StandardOutputWriter.Write(value);
Console.Out.Write(value);
}
if (transcribeResult)
@ -659,10 +665,7 @@ namespace Microsoft.PowerShell
// If the test hook is set, write to it and continue.
if (_h != null) _h.Write(value);
TextWriter writer =
(!parent.IsStandardOutputRedirected || parent.IsInteractive)
? parent.ConsoleTextWriter
: parent.StandardOutputWriter;
TextWriter writer = Console.IsOutputRedirected ? Console.Out : parent.ConsoleTextWriter;
if (parent.IsRunningAsync)
{
@ -1362,9 +1365,9 @@ namespace Microsoft.PowerShell
}
TextWriter writer =
(!parent.IsStandardErrorRedirected || parent.IsInteractive)
(!Console.IsErrorRedirected || parent.IsInteractive)
? parent.ConsoleTextWriter
: parent.StandardErrorWriter;
: Console.Error;
if (parent.ErrorFormat == Serialization.DataFormat.XML)
{
@ -1378,7 +1381,7 @@ namespace Microsoft.PowerShell
WriteLine(errorForegroundColor, errorBackgroundColor, value);
else
parent.StandardErrorWriter.Write(value + Crlf);
Console.Error.Write(value + Crlf);
}
}
@ -1480,9 +1483,9 @@ namespace Microsoft.PowerShell
// We don't use System.Environment.NewLine because we are very platform specific with our use of the win32 console APIs
// We use System.Environment.NewLine because we are platform-agnostic
internal const string Crlf = "\x000D\x000A";
internal static string Crlf = System.Environment.NewLine;
private const string Tab = "\x0009";
internal enum ReadLineResult
@ -1555,21 +1558,75 @@ namespace Microsoft.PowerShell
// If the test hook is set, read from it.
if (_h != null) return _h.ReadLine();
string s = "";
if (parent.IsStandardInputRedirected && readFromStdin)
string restOfLine = null;
string s = ReadFromStdin
? ReadLineFromFile(initialContent)
: ReadLineFromConsole(endOnTab, initialContent, calledFromPipeline, ref restOfLine, ref result);
if (transcribeResult)
{
// When reading from a file handle instead of a console, endOnTab and initial content are ignored.
// StreamReader.ReadLine simply returns null when EOF is reached.
s = parent.StandardInReader.ReadLine();
if (endOnTab && !string.IsNullOrEmpty(s) && s.IndexOf(Tab, StringComparison.OrdinalIgnoreCase) != -1)
{
result = isTestingShiftTab ? ReadLineResult.endedOnShiftTab : ReadLineResult.endedOnTab;
return s;
}
return s;
PostRead(s);
}
else
{
PostRead();
}
if (restOfLine != null)
s += restOfLine;
return s;
}
private string ReadLineFromFile(string initialContent)
{
var sb = new StringBuilder();
if (initialContent != null)
{
sb.Append(initialContent);
}
while (true)
{
var inC = Console.In.Read();
if (inC == -1)
{
// EOF - we return null which tells our caller to exit
return null;
}
var c = unchecked((char)inC);
if (!NoPrompt) Console.Out.Write(c);
if (c == '\r')
{
// Treat as newline, but consume \n if there is one.
if (Console.In.Peek() == '\n')
{
if (!NoPrompt) Console.Out.Write('\n');
Console.In.Read();
}
sb.Append('\n');
break;
}
sb.Append(c);
if (c == '\n')
{
break;
}
}
return sb.ToString();
}
private string ReadLineFromConsole(bool endOnTab, string initialContent, bool calledFromPipeline, ref string restOfLine, ref ReadLineResult result)
{
#if PORTABLE
return ReadLineFromFile(initialContent);
#else
ConsoleHandle handle = ConsoleControl.GetInputHandle();
PreRead();
@ -1588,6 +1645,7 @@ namespace Microsoft.PowerShell
m |= desiredMode;
ConsoleControl.SetMode(handle, m);
}
// If more characters are typed than you asked, then the next call to ReadConsole will return the
// additional characters beyond those you requested.
//
@ -1604,10 +1662,10 @@ namespace Microsoft.PowerShell
// the empty string.
uint keyState = 0;
string restOfLine = null;
rawui.ClearKeyCache();
string s = "";
do
{
s += ConsoleControl.ReadConsole(handle, initialContent, maxInputLineLength, endOnTab, out keyState);
@ -1688,23 +1746,12 @@ namespace Microsoft.PowerShell
while (true);
Dbg.Assert(
(s == null && result == ReadLineResult.endedOnBreak)
|| (s != null && result != ReadLineResult.endedOnBreak),
"s should only be null if input ended with a break");
if (transcribeResult)
{
PostRead(s);
}
else
{
PostRead();
}
if (restOfLine != null)
s += restOfLine;
(s == null && result == ReadLineResult.endedOnBreak)
|| (s != null && result != ReadLineResult.endedOnBreak),
"s should only be null if input ended with a break");
return s;
#endif
}
/// <summary>
@ -1712,6 +1759,7 @@ namespace Microsoft.PowerShell
/// </summary>
/// <param name="cursorPosition">the cursor position where 'tab' is hit</param>
/// <returns></returns>
#if !PORTABLE
private char GetCharacterUnderCursor(Coordinates cursorPosition)
{
Rectangle region = new Rectangle(0, cursorPosition.Y, RawUI.BufferSize.Width - 1, cursorPosition.Y);
@ -1734,6 +1782,7 @@ namespace Microsoft.PowerShell
Dbg.Assert(false, "the character at the cursor should be retrieved, never gets to here");
return '\0';
}
#endif
/// <summary>
@ -1764,29 +1813,23 @@ namespace Microsoft.PowerShell
///
/// The Executor instance on which to run any pipelines that are needed to find matches
///
/// </param>
///
/// <param name="useUserDefinedCustomReadLine">
///
/// Flag which decides if we should look for a user defined ReadLine function
///
/// </param>
///
/// <returns>
///
/// null on a break event
/// the completed line otherwise
///
/// </returns>
internal string ReadLineWithTabCompletion(Executor exec, bool useUserDefinedCustomReadLine)
internal string ReadLineWithTabCompletion(Executor exec)
{
ConsoleHandle handle = ConsoleControl.GetActiveScreenBufferHandle();
string input = null;
string lastInput = "";
string lastCompletion = "";
ReadLineResult rlResult = ReadLineResult.endedOnEnter;
#if !PORTABLE
ConsoleHandle handle = ConsoleControl.GetActiveScreenBufferHandle();
string lastCompletion = "";
Size screenBufferSize = RawUI.BufferSize;
// Save the cursor position at the end of the prompt string so that we can restore it later to write the
@ -1796,19 +1839,17 @@ namespace Microsoft.PowerShell
CommandCompletion commandCompletion = null;
string completionInput = null;
#endif
do
{
if (TryInvokeUserDefinedReadLine(out input, useUserDefinedCustomReadLine))
if (!ReadFromStdin && TryInvokeUserDefinedReadLine(out input))
{
break;
}
input = ReadLine(true, lastInput, out rlResult, false, false);
Coordinates endOfInputCursorPos = RawUI.CursorPosition;
string completedInput = null;
if (input == null)
{
break;
@ -1819,6 +1860,13 @@ namespace Microsoft.PowerShell
break;
}
#if PORTABLE // Portable code only ends on enter (or no input), so tab is not processed
throw new PlatformNotSupportedException("This readline state is unsupported in portable code!");
#else
Coordinates endOfInputCursorPos = RawUI.CursorPosition;
string completedInput = null;
if (rlResult == ReadLineResult.endedOnTab || rlResult == ReadLineResult.endedOnShiftTab)
{
int tabIndex = input.IndexOf(Tab, StringComparison.CurrentCulture);
@ -1828,16 +1876,11 @@ namespace Microsoft.PowerShell
int leftover = input.Length - tabIndex - 1;
if (leftover > 0)
{
// Check if the std input is redirected, e.g. reading from a file
bool isStdInputRedirected = parent.IsStandardInputRedirected && readFromStdin;
if (!isStdInputRedirected)
{
// We are reading from the console
// If the cursor is at the end of a line, there is actually a space character at the cursor's position and when we type tab
// at the end of a line, that space character is replaced by the tab. But when we type tab at the middle of a line, the space
// character at the end is preserved, we should remove that space character because it's not provided by the user.
input = input.Remove(input.Length - 1);
}
// We are reading from the console (not redirected, b/c we don't end on tab when redirected)
// If the cursor is at the end of a line, there is actually a space character at the cursor's position and when we type tab
// at the end of a line, that space character is replaced by the tab. But when we type tab at the middle of a line, the space
// character at the end is preserved, we should remove that space character because it's not provided by the user.
input = input.Remove(input.Length - 1);
restOfLine = input.Substring(tabIndex + 1);
}
input = input.Remove(tabIndex);
@ -1927,6 +1970,7 @@ namespace Microsoft.PowerShell
lastInput = completedInput;
}
#endif
}
while (true);
@ -1942,6 +1986,7 @@ namespace Microsoft.PowerShell
return input;
}
#if !PORTABLE
private void SendLeftArrows(int length)
{
var inputs = new ConsoleControl.INPUT[length * 2];
@ -1971,6 +2016,7 @@ namespace Microsoft.PowerShell
ConsoleControl.MimicKeyPress(inputs);
}
#endif
private CommandCompletion GetNewCompletionResults(string input)
{
@ -2011,44 +2057,43 @@ namespace Microsoft.PowerShell
}
const string CustomReadlineCommand = "PSConsoleHostReadLine";
private bool TryInvokeUserDefinedReadLine(out string input, bool useUserDefinedCustomReadLine)
private bool TryInvokeUserDefinedReadLine(out string input)
{
// We're using GetCommands instead of GetCommand so we don't auto-load a module should the command exist, but isn't loaded.
// The idea is that if someone hasn't defined the command (say because they started -noprofile), we shouldn't auto-load
// this function.
// If we need to look for user defined custom readline command, then we need to wait for Runspace to be created.
if (useUserDefinedCustomReadLine)
var runspace = this.parent.LocalRunspace;
if (runspace != null &&
runspace.Engine.Context.EngineIntrinsics.InvokeCommand.
GetCommands(CustomReadlineCommand,
CommandTypes.Function | CommandTypes.Cmdlet, nameIsPattern: false).Any())
{
var runspace = this.parent.LocalRunspace;
if (runspace != null
&& runspace.Engine.Context.EngineIntrinsics.InvokeCommand.GetCommands(CustomReadlineCommand, CommandTypes.Function | CommandTypes.Cmdlet, nameIsPattern: false).Any())
try
{
try
PowerShell ps;
if ((runspace.ExecutionContext.EngineHostInterface.NestedPromptCount > 0) &&
(Runspace.DefaultRunspace != null))
{
PowerShell ps;
if ((runspace.ExecutionContext.EngineHostInterface.NestedPromptCount > 0) && (Runspace.DefaultRunspace != null))
{
ps = PowerShell.Create(RunspaceMode.CurrentRunspace);
}
else
{
ps = PowerShell.Create();
ps.Runspace = runspace;
}
ps = PowerShell.Create(RunspaceMode.CurrentRunspace);
}
else
{
ps = PowerShell.Create();
ps.Runspace = runspace;
}
var result = ps.AddCommand(CustomReadlineCommand).Invoke();
if (result.Count == 1)
{
input = PSObject.Base(result[0]) as string;
return true;
}
}
catch (Exception e)
var result = ps.AddCommand(CustomReadlineCommand).Invoke();
if (result.Count == 1)
{
CommandProcessorBase.CheckForSevereException(e);
input = PSObject.Base(result[0]) as string;
return true;
}
}
catch (Exception e)
{
CommandProcessorBase.CheckForSevereException(e);
}
}
input = null;
@ -2061,7 +2106,6 @@ namespace Microsoft.PowerShell
private object instanceLock = new object();
private bool readFromStdin;
private bool noPrompt;
//If this is true, class throws on read or prompt method which require
@ -2086,7 +2130,6 @@ namespace Microsoft.PowerShell
// this is a test hook for the ConsoleInteractiveTestTool, which sets this field to true.
private bool isInteractiveTestToolListening;
private bool isTestingShiftTab;
// This instance data is "read-only" and need not have access serialized.

View File

@ -215,11 +215,11 @@ namespace Microsoft.PowerShell
PipelineFinishedWaitHandle waiterThereIsAFlyInMySoup = new PipelineFinishedWaitHandle(tempPipeline);
tempPipeline.InvokeAsync();
if ((options & ExecutionOptions.ReadInputObjects) > 0 && parent.IsStandardInputRedirected)
if ((options & ExecutionOptions.ReadInputObjects) > 0 && Console.IsInputRedirected)
{
// read input objects from stdin
WrappedDeserializer des = new WrappedDeserializer(parent.InputFormat, "Input", parent.StandardInReader);
WrappedDeserializer des = new WrappedDeserializer(parent.InputFormat, "Input", Console.In);
while (!des.AtEnd)
{
object o = des.Deserialize();

View File

@ -1,7 +1,7 @@
{
"name": "Microsoft.PowerShell.ConsoleHost",
"version": "1.0.0-*",
"description": "Full Windows PowerShell Host (requires native host)",
"description": "PowerShell Host",
"authors": [ "sevoroby", "andschwa" ],
"compilationOptions": {
@ -10,6 +10,7 @@
},
"dependencies": {
"Microsoft.PowerShell.PSReadLine": "1.0.0-*",
"Microsoft.PowerShell.Commands.Management": "1.0.0-*",
"Microsoft.PowerShell.Commands.Utility": "1.0.0-*"
},
@ -21,6 +22,17 @@
],
"frameworks": {
"netstandard1.5": {
"imports": [ "dnxcore50", "portable-net45+win8" ],
"compilationOptions": {
"define": [ "CORECLR", "PORTABLE" ]
},
"dependencies": {
"Newtonsoft.Json": "8.0.2",
"System.Xml.XDocument": "4.0.11-rc2-24103",
"System.IO.MemoryMappedFiles": "4.0.0-rc2-24103"
}
},
"net451": {
}
},

View File

@ -1,3 +1,4 @@
#if !PORTABLE
/********************************************************************++
Copyright (c) Microsoft Corporation. All rights reserved.
--********************************************************************/
@ -86,3 +87,4 @@ namespace Microsoft.PowerShell
}
}
}
#endif

View File

@ -1,3 +1,4 @@
#if !PORTABLE
/********************************************************************++
Copyright (c) Microsoft Corporation. All rights reserved.
--********************************************************************/
@ -92,3 +93,4 @@ namespace Microsoft.PowerShell
}
}
}
#endif

View File

@ -1,8 +0,0 @@
using System.Runtime.CompilerServices;
using System.Reflection;
using System.Resources;
[assembly:InternalsVisibleTo("powershell-tests")]
[assembly:AssemblyCulture("")]
[assembly:NeutralResourcesLanguage("en-US")]

View File

@ -1,25 +0,0 @@
# Core PowerShell Host
This host is based off the infamous `host06` example. It relies only on managed
code, and is cross-platform. It needs a fair bit of work, but it loads
profiles, has tab-completion, and can be used to debug PowerShell scripts.
## Executable
The `bin/powershell[.exe]` executable for Core PowerShell is built by this
project, as it is the top dependency of the graph, and has `emitEntryPoint:
true`, meaning a native executable is produced automatically by CLI (no need to
own a separate native host). It is also the project that deploys the `ps1xml`
types and formatting files, as well as the default profile and the included
PowerShell modules.
Note that many of these should probably live with System.Management.Automation,
but we're waiting on a bug fix from CLI to move them.
## update-content.sh
This script is used to update our current tree's files with those that live in
`src/monad`. We only need to update them when new changes are merged from
Source Depot, and the build scripts are much simpler without this logic. With
the files living here, the `content` key in the `project.json` handles the
deployment for us in a cross-platform manner.

View File

@ -1,218 +0,0 @@
namespace Microsoft.PowerShell.CoreConsoleHost
{
using System;
using System.Globalization;
using System.Management.Automation.Host;
using System.Management.Automation.Runspaces;
/// <summary>
/// This is a sample implementation of the PSHost abstract class for
/// console applications. Not all members are implemented. Those that
/// are not implemented throw a NotImplementedException exception or
/// return nothing.
/// </summary>
internal class MyHost : PSHost, IHostSupportsInteractiveSession
{
public MyHost(Listener Listener)
{
this.Listener = Listener;
this.myHostUserInterface = new MyHostUserInterface(Listener.HasUI, Listener.Interactive);
}
/// <summary>
/// A reference to the PSHost implementation.
/// </summary>
private Listener Listener;
/// <summary>
/// The culture information of the thread that created
/// this object.
/// </summary>
private CultureInfo originalCultureInfo = CultureInfo.CurrentCulture;
/// <summary>
/// The UI culture information of the thread that created
/// this object.
/// </summary>
private CultureInfo originalUICultureInfo = CultureInfo.CurrentCulture;
/// <summary>
/// The identifier of this PSHost implementation.
/// </summary>
private static Guid instanceId = Guid.NewGuid();
/// <summary>
/// A reference to the implementation of the PSHostUserInterface
/// class for this application.
/// </summary>
private MyHostUserInterface myHostUserInterface;
/// <summary>
/// A reference to the runspace used to start an interactive session.
/// </summary>
public Runspace pushedRunspace = null;
/// <summary>
/// Gets the culture information to use. This implementation
/// returns a snapshot of the culture information of the thread
/// that created this object.
/// </summary>
public override CultureInfo CurrentCulture
{
get { return this.originalCultureInfo; }
}
/// <summary>
/// Gets the UI culture information to use. This implementation
/// returns a snapshot of the UI culture information of the thread
/// that created this object.
/// </summary>
public override CultureInfo CurrentUICulture
{
get { return this.originalUICultureInfo; }
}
/// <summary>
/// Gets an identifier for this host. This implementation always
/// returns the GUID allocated at instantiation time.
/// </summary>
public override Guid InstanceId
{
get { return instanceId; }
}
/// <summary>
/// Gets a string that contains the name of this host implementation.
/// Keep in mind that this string may be used by script writers to
/// identify when your host is being used.
/// </summary>
public override string Name
{
get { return "CoreConsoleHost"; }
}
/// <summary>
/// Gets an instance of the implementation of the PSHostUserInterface
/// class for this application. This instance is allocated once at startup time
/// and returned every time thereafter.
/// </summary>
public override PSHostUserInterface UI
{
get { return this.myHostUserInterface; }
}
/// <summary>
/// Gets the version object for this application. Typically this
/// should match the version resource in the application.
/// </summary>
public override Version Version
{
get { return new Version(1, 0, 0, 0); }
}
#region IHostSupportsInteractiveSession Properties
/// <summary>
/// Gets a value indicating whether a request
/// to open a PSSession has been made.
/// </summary>
public bool IsRunspacePushed
{
get { return this.pushedRunspace != null; }
}
/// <summary>
/// Gets or sets the runspace used by the PSSession.
/// </summary>
public Runspace Runspace
{
get { return this.Listener.myRunSpace; }
internal set { this.Listener.myRunSpace = value; }
}
#endregion IHostSupportsInteractiveSession Properties
/// <summary>
/// This API Instructs the host to interrupt the currently running
/// pipeline and start a new nested input loop. In this example this
/// functionality is not needed so the method throws a
/// NotImplementedException exception.
/// </summary>
public override void EnterNestedPrompt()
{
throw new NotImplementedException(
"The method or operation is not implemented.");
}
/// <summary>
/// This API instructs the host to exit the currently running input loop.
/// In this example this functionality is not needed so the method
/// throws a NotImplementedException exception.
/// </summary>
public override void ExitNestedPrompt()
{
throw new NotImplementedException(
"The method or operation is not implemented.");
}
/// <summary>
/// This API is called before an external application process is
/// started. Typically it is used to save state so that the parent
/// can restore state that has been modified by a child process (after
/// the child exits). In this example this functionality is not
/// needed so the method returns nothing.
/// </summary>
public override void NotifyBeginApplication()
{
return;
}
/// <summary>
/// This API is called after an external application process finishes.
/// Typically it is used to restore state that a child process has
/// altered. In this example, this functionality is not needed so
/// the method returns nothing.
/// </summary>
public override void NotifyEndApplication()
{
return;
}
/// <summary>
/// Indicate to the host application that exit has
/// been requested. Pass the exit code that the host
/// application should use when exiting the process.
/// </summary>
/// <param name="exitCode">The exit code that the
/// host application should use.</param>
public override void SetShouldExit(int exitCode)
{
this.Listener.ShouldExit = true;
this.Listener.ExitCode = exitCode;
}
#region IHostSupportsInteractiveSession Methods
/// <summary>
/// Requests to close a PSSession.
/// </summary>
public void PopRunspace()
{
Runspace = this.pushedRunspace;
this.pushedRunspace = null;
}
/// <summary>
/// Requests to open a PSSession.
/// </summary>
/// <param name="runspace">Runspace to use.</param>
public void PushRunspace(Runspace runspace)
{
this.pushedRunspace = Runspace;
Runspace = runspace;
}
#endregion IHostSupportsInteractiveSession Methods
}
}

View File

@ -1,857 +0,0 @@
namespace Microsoft.PowerShell.CoreConsoleHost
{
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Management.Automation;
using System.Management.Automation.Host;
using System.Management.Automation.Runspaces;
using System.Text;
using System.IO;
using System.Runtime.InteropServices;
using System.Linq;
using PowerShell = System.Management.Automation.PowerShell;
public static class Program
{
private enum Options {Help, File, Command, NoProfile, NonInteractive, EncodedCommand, Invalid};
private class CommandOptions
{
public Options opt;
public string optString;
public CommandOptions(Options o, string s)
{
opt = o;
optString = s;
}
}
private static CommandOptions[] commandOptions = new CommandOptions[]
{
new CommandOptions(Options.Help, "help"),
new CommandOptions(Options.Help, "?"),
new CommandOptions(Options.File, "file"),
new CommandOptions(Options.Command, "command"),
new CommandOptions(Options.NoProfile, "noprofile"),
new CommandOptions(Options.NonInteractive, "noninteractive"),
new CommandOptions(Options.EncodedCommand, "encodedcommand")
};
/// <summary>
/// Creates and initiates the listener instance.
/// </summary>
public static void Main(string[] args)
{
// Setup the Assembly Load Context, which Core PowerShell uses to
// analyze the libraries for types, functions, cmdlets, etc. and
// provide the ability to load assemblies by file path. Doing this
// here eliminates the need for a custom native host.
PowerShellAssemblyLoadContextInitializer.SetPowerShellAssemblyLoadContext(string.Empty);
// Argument parsing
string initialScript = null;
bool loadProfiles = true;
bool interactive = true;
if (args.Length > 0)
{
for (int i = 0; i < args.Length; ++i)
{
string arg = args[i];
bool hasNext = (i+1) < args.Length;
string nextArg = hasNext ? args[i+1] : string.Empty;
// options start with "-" or "--"
string option;
if (arg.StartsWith("-"))
{
if (arg.StartsWith("--"))
{
option = arg.Remove(0, 2);
}
else
{
option = arg.Remove(0, 1);
}
Options bestOption = FindOption(option);
switch (bestOption)
{
case Options.Help:
Console.WriteLine(@"
usage: powershell[.exe] [ (--help | -h | -?) ]
[ (--file | -f) <filePath> ]
[ <script>.ps1 ]
[ (--command | -c) <string> ]
[ --noprofile | -nop ]
[ --noninteractive | -non ]
[ (--encodedcommand | -e) <base64Command> ]
SYNOPSIS
Open PowerShell console can take none or one of several arguments.
OPTIONS
No arguments
Will launch PowerShell interactively.
(--file | -f) <filePath>
Given a file path, will execute as a PowerShell script.
<script>.ps1
Given a .ps1 script, will execute without needing --flag.
(--command | -c) <string>
Will execute given string as a PowerShell script.
(--noprofile | -nop)
Disables parsing of PowerShell profiles.
(--help | -h | -?)
Prints this text.
(--noninteractive | -non)
Does not present an interactive prompt to the user.
(--encodedcommand | -e) <base64Command>
Accepts a base-64-encoded string version of a command.
");
return;
case Options.NoProfile:
loadProfiles = false;
break;
case Options.File:
initialScript = Path.GetFullPath(nextArg);
++i;
break;
case Options.Command:
if (nextArg == "-")
{
initialScript = "\"TODO: read stdin using Console.OpenStandardInput\"";
}
else
{
initialScript = nextArg;
}
++i;
break;
case Options.NonInteractive:
interactive = false;
break;
case Options.EncodedCommand:
byte[] data = Convert.FromBase64String(nextArg);
initialScript = Encoding.Unicode.GetString(data);
++i;
break;
default:
Console.WriteLine("Invalid command-line option: {0}. Use '-help' option to get a list of valid options.", arg);
return;
}
}
else // not an option parameter
{
// lone argument is a script
if (!hasNext && arg.EndsWith(".ps1"))
{
initialScript = Path.GetFullPath(arg);
}
// lone argument is an inline script
else if (!hasNext)
{
initialScript = arg;
}
}
}
}
// TODO: check for input on stdin
ConsoleColor InitialForegroundColor = Console.ForegroundColor;
ConsoleColor InitialBackgroundColor = Console.BackgroundColor;
// Create the listener and run it
Listener listener = new Listener(initialScript, loadProfiles, interactive);
// only run if there was no script file passed in
if (initialScript == null)
{
listener.Run();
}
Console.ForegroundColor = InitialForegroundColor;
Console.BackgroundColor = InitialBackgroundColor;
// Exit with the desired exit code that was set by the exit command.
// The exit code is set in the host by the MyHost.SetShouldExit() method.
System.Environment.Exit(listener.ExitCode);
}
private static Options FindOption(string option)
{
int matches = 0;
int index = -1;
for (int i=0; i<commandOptions.Length; ++i)
{
if (commandOptions[i].optString.StartsWith(option, StringComparison.OrdinalIgnoreCase))
{
matches++;
index = i;
}
}
return (matches == 1) ? commandOptions[index].opt : Options.Invalid;
}
}
internal class Listener
{
/// <summary>
/// Used to read user input.
/// </summary>
internal ConsoleReadLine consoleReadLine;
/// <summary>
/// Holds a reference to the runspace for this interpeter.
/// </summary>
internal Runspace myRunSpace;
/// <summary>
/// Indicator to tell the host application that it should exit.
/// </summary>
private bool shouldExit;
/// <summary>
/// The exit code that the host application will use to exit.
/// </summary>
private int exitCode;
/// <summary>
/// Holds a reference to the PSHost implementation for this interpreter.
/// </summary>
private MyHost myHost;
/// <summary>
/// Holds a reference to the currently executing pipeline so that it can be
/// stopped by the control-C handler.
/// </summary>
private PowerShell currentPowerShell;
/// <summary>
/// Used to serialize access to instance data.
/// </summary>
private object instanceLock = new object();
/// <summary>
/// To keep track whether we've displayed the debugger help message
/// </summary>
private bool _showHelpMessage;
/// <summary>
/// To keep track whether last entered command was complete
/// </summary>
private bool incompleteLine = false;
/// <summary>
/// To store incomplete lines
/// </summary>
private string partialLine = string.Empty;
/// <summary>
/// Gets or sets a value indicating whether the host application
/// should exit.
/// </summary>
public bool ShouldExit
{
get { return this.shouldExit; }
set { this.shouldExit = value; }
}
/// <summary>
/// Gets or sets a value indicating whether the host application
/// should exit.
/// </summary>
public int ExitCode
{
get { return this.exitCode; }
set { this.exitCode = value; }
}
/// <summary>
/// Gets or sets a value indicating whether UI exists (display prompt)
/// </summary>
public bool HasUI;
/// <summary>
/// Gets or sets a value indicating whether session is interactive (allow popup)
/// </summary>
public bool Interactive;
public Listener(string initialScript, bool loadProfiles, bool interactive)
{
this.HasUI = (initialScript == null) ? true : false;
this.Interactive = interactive;
// Create the host and runspace instances for this interpreter.
// Note that this application does not support console files so
// only the default snap-ins will be available.
this.myHost = new MyHost(this);
InitialSessionState iss = InitialSessionState.CreateDefault2();
this.myRunSpace = RunspaceFactory.CreateRunspace(this.myHost, iss);
this.myRunSpace.Open();
this.consoleReadLine = new ConsoleReadLine(this.myHost.Runspace, this.myHost.UI);
if (this.myRunSpace.Debugger != null)
{
this.myRunSpace.Debugger.DebuggerStop += HandleDebuggerStopEvent;
// Workflow debugging is new for PowerShell version 4 and is an opt-in
// feature. In order to debug Workflow script functions the debugger
// DebugMode must include the DebugModes.LocalScript flag.
this.myRunSpace.Debugger.SetDebugMode(DebugModes.LocalScript);
}
if (loadProfiles)
{
LoadProfiles();
}
// run the initial script
if (initialScript != null)
{
Execute(initialScript);
}
}
internal void LoadProfiles()
{
// Create a PowerShell object to run the commands used to create
// $profile and load the profiles.
lock (this.instanceLock)
{
this.currentPowerShell = PowerShell.Create();
}
try
{
this.currentPowerShell.Runspace = this.myRunSpace;
PSCommand[] profileCommands = HostUtilities.GetProfileCommands("Microsoft.PowerShellCore");
foreach (PSCommand command in profileCommands)
{
RunCommand(command);
}
}
finally
{
// Dispose the PowerShell object and set currentPowerShell
// to null. It is locked because currentPowerShell may be
// accessed by the ctrl-C handler.
lock (this.instanceLock)
{
// The PowerShell instance will be null if it failed to
// start due to, say, a parse exception in a profile.
if (this.currentPowerShell != null) {
this.currentPowerShell.Dispose();
this.currentPowerShell = null;
}
}
}
}
/// Sets the prompt equal to the output of the prompt function
public string Prompt(Runspace rs)
{
string returnVal = string.Empty;
if (this.myHost.IsRunspacePushed)
{
returnVal = string.Format($"{System.Environment.NewLine}[{this.myRunSpace.ConnectionInfo.ComputerName}] PSL> ");
return returnVal;
}
if (incompleteLine)
{
return ">> ";
}
Collection<PSObject> output;
Command promptCommand = new Command("prompt");
using (Pipeline pipeline = rs.CreatePipeline())
{
pipeline.Commands.Add(promptCommand);
output = pipeline.Invoke();
}
foreach (PSObject item in output)
{
returnVal = item.BaseObject.ToString();
}
return returnVal;
}
/// <summary>
/// Runs individual commands
/// </summary>
/// <param name="command">command to run</param>
internal void RunCommand(PSCommand command)
{
if (command == null)
{
return;
}
command.AddCommand("out-default");
command.Commands[0].MergeMyResults(PipelineResultTypes.Error, PipelineResultTypes.Output);
this.currentPowerShell.Commands = command;
try
{
this.currentPowerShell.Invoke();
}
catch (RuntimeException e)
{
this.ReportException(e);
}
}
/// <summary>
/// A helper class that builds and executes a pipeline that writes
/// to the default output path. Any exceptions that are thrown are
/// just passed to the caller. Since all output goes to the default
/// outter, this method does not return anything.
/// </summary>
/// <param name="cmd">The script to run.</param>
/// <param name="input">Any input arguments to pass to the script.
/// If null then nothing is passed in.</param>
private void ExecuteHelper(string cmd, object input)
{
// Ignore empty command lines.
if (string.IsNullOrEmpty(cmd))
{
return;
}
// Create the pipeline object and make it available to the
// ctrl-C handle through the currentPowerShell instance
// variable.
lock (this.instanceLock)
{
this.currentPowerShell = PowerShell.Create();
}
// Add a script and command to the pipeline and then run the pipeline. Place
// the results in the currentPowerShell variable so that the pipeline can be
// stopped.
try
{
this.currentPowerShell.Runspace = this.myRunSpace;
string fullCommand = incompleteLine ? (partialLine + cmd) : cmd;
this.currentPowerShell.AddScript(fullCommand);
incompleteLine = false;
// Add the default outputter to the end of the pipe and then call the
// MergeMyResults method to merge the output and error streams from the
// pipeline. This will result in the output being written using the PSHost
// and PSHostUserInterface classes instead of returning objects to the host
// application.
this.currentPowerShell.AddCommand("out-default");
this.currentPowerShell.Commands.Commands[0].MergeMyResults(PipelineResultTypes.Error, PipelineResultTypes.Output);
// If there is any input pass it in, otherwise just invoke the
// the pipeline.
PSInvocationSettings settings = new PSInvocationSettings();
settings.AddToHistory = true;
if (input != null)
{
this.currentPowerShell.Invoke(new object[] { input }, settings);
}
else
{
this.currentPowerShell.Invoke(null, settings);
}
}
catch (IncompleteParseException)
{
incompleteLine = true;
partialLine = $"{partialLine}{cmd}{System.Environment.NewLine}";
}
finally
{
// Dispose the PowerShell object and set currentPowerShell to null.
// It is locked because currentPowerShell may be accessed by the
// ctrl-C handler.
lock (this.instanceLock)
{
this.currentPowerShell.Dispose();
this.currentPowerShell = null;
}
if (!incompleteLine)
{
partialLine = string.Empty;
}
}
}
/// <summary>
/// To display an exception using the display formatter,
/// run a second pipeline passing in the error record.
/// The runtime will bind this to the $input variable,
/// which is why $input is being piped to the Out-String
/// cmdlet. The WriteErrorLine method is called to make sure
/// the error gets displayed in the correct error color.
/// </summary>
/// <param name="e">The exception to display.</param>
private void ReportException(Exception e)
{
if (e != null)
{
// Return non-zero exit code if an exception is thrown
this.ExitCode = 1;
object error;
IContainsErrorRecord icer = e as IContainsErrorRecord;
if (icer != null)
{
error = icer.ErrorRecord;
}
else
{
error = (object)new ErrorRecord(e, "Host.ReportException", ErrorCategory.NotSpecified, null);
}
lock (this.instanceLock)
{
this.currentPowerShell = PowerShell.Create();
}
this.currentPowerShell.Runspace = this.myRunSpace;
try
{
this.currentPowerShell.AddScript("$input").AddCommand("out-string");
// Do not merge errors, this function will swallow errors.
Collection<PSObject> result;
PSDataCollection<object> inputCollection = new PSDataCollection<object>();
inputCollection.Add(error);
inputCollection.Complete();
result = this.currentPowerShell.Invoke(inputCollection);
if (result.Count > 0)
{
string str = result[0].BaseObject as string;
if (!string.IsNullOrEmpty(str))
{
// Remove \r\n, which is added by the Out-String cmdlet.
this.myHost.UI.WriteErrorLine(str.Substring(0, str.Length - 2));
}
}
}
finally
{
// Dispose of the pipeline and set it to null, locking it because
// currentPowerShell may be accessed by the ctrl-C handler.
lock (this.instanceLock)
{
this.currentPowerShell.Dispose();
this.currentPowerShell = null;
}
}
}
}
/// <summary>
/// Basic script execution routine. Any runtime exceptions are
/// caught and passed back to the Windows PowerShell engine to
/// display.
/// </summary>
/// <param name="cmd">Script to run.</param>
private void Execute(string cmd)
{
try
{
// Run the command with no input.
this.ExecuteHelper(cmd, null);
}
catch (RuntimeException rte)
{
this.ReportException(rte);
}
}
/// <summary>
/// Method used to handle control-C's from the user. It calls the
/// pipeline Stop() method to stop execution. If any exceptions occur
/// they are printed to the console but otherwise ignored.
/// </summary>
/// <param name="sender">See sender property documentation of
/// ConsoleCancelEventHandler.</param>
/// <param name="e">See e property documentation of
/// ConsoleCancelEventHandler.</param>
private void HandleControlC(object sender, ConsoleCancelEventArgs e)
{
try
{
lock (this.instanceLock)
{
if (this.currentPowerShell != null && this.currentPowerShell.InvocationStateInfo.State == PSInvocationState.Running)
{
this.currentPowerShell.Stop();
}
}
e.Cancel = true;
}
catch (Exception exception)
{
this.myHost.UI.WriteErrorLine(exception.ToString());
}
}
/// <summary>
/// Implements the basic listener loop. It sets up the ctrl-C handler, then
/// reads a command from the user, executes it and repeats until the ShouldExit
/// flag is set.
/// </summary>
internal void Run()
{
// Set up the control-C handler.
Console.CancelKeyPress += new ConsoleCancelEventHandler(this.HandleControlC);
string initialCommand = String.Empty;
// Read commands and run them until the ShouldExit flag is set by
// the user calling "exit".
while (!this.ShouldExit && this.myHost.Runspace != null)
{
// Reset exit code for each command
this.ExitCode = 0;
// If the prompt function failed for any reason, use a sane default
string prompt;
try
{
prompt = Prompt(this.myHost.Runspace);
}
catch
{
prompt = "PS> ";
}
this.myHost.UI.Write(prompt);
string input;
if (TryInvokeUserDefinedReadLine(out input, true))
{
this.Execute(input);
}
else
{
ConsoleReadLine.ReadResult result = consoleReadLine.Read(false, initialCommand);
switch(result.state)
{
case ConsoleReadLine.ReadResult.State.Abort:
incompleteLine = false;
partialLine = string.Empty;
initialCommand = String.Empty;
break;
case ConsoleReadLine.ReadResult.State.Redraw:
initialCommand = result.command;
break;
case ConsoleReadLine.ReadResult.State.Complete:
default:
this.Execute(result.command);
initialCommand = String.Empty;
break;
}
}
}
}
/// <summary>
/// Helper function to test if PSReadLine or another alternate ReadLine has been loaded
/// </summary>
const string CustomReadlineCommand = "PSConsoleHostReadLine";
private bool TryInvokeUserDefinedReadLine(out string input, bool useUserDefinedCustomReadLine)
{
if (useUserDefinedCustomReadLine)
{
var runspace = this.myHost.Runspace;
if (runspace != null
&& runspace.ExecutionContext.EngineIntrinsics.InvokeCommand.GetCommands(CustomReadlineCommand, CommandTypes.Function | CommandTypes.Cmdlet, nameIsPattern: false).Any())
{
try
{
PowerShell ps;
if ((runspace.ExecutionContext.EngineHostInterface.NestedPromptCount > 0) && (Runspace.DefaultRunspace != null))
{
ps = PowerShell.Create(RunspaceMode.CurrentRunspace);
}
else
{
ps = PowerShell.Create();
ps.Runspace = runspace;
}
var result = ps.AddCommand(CustomReadlineCommand).Invoke();
if (result.Count == 1)
{
input = PSObject.Base(result[0]) as string;
return true;
}
}
catch (Exception e)
{
CommandProcessorBase.CheckForSevereException(e);
}
}
}
input = null;
return false;
}
/// <summary>
/// Method to handle the Debugger DebuggerStop event.
/// </summary>
/// <param name="sender"> Debugger instance
/// <param name="args"> DebuggerStop event args
private void HandleDebuggerStopEvent(object sender, DebuggerStopEventArgs args)
{
Debugger debugger = sender as Debugger;
DebuggerResumeAction? resumeAction = null;
WriteDebuggerStopMessages(args);
string initialCommand = String.Empty;
// loop to process Debugger commands.
while (resumeAction == null)
{
string prompt = incompleteLine ? ">> " : "[DBG] PS >> ";
this.myHost.UI.Write(prompt);
ConsoleReadLine.ReadResult result = consoleReadLine.Read(true, initialCommand);
switch(result.state)
{
case ConsoleReadLine.ReadResult.State.Abort:
incompleteLine = false;
partialLine = string.Empty;
initialCommand = String.Empty;
continue;
case ConsoleReadLine.ReadResult.State.Redraw:
initialCommand = result.command;
continue;
case ConsoleReadLine.ReadResult.State.Complete:
default:
initialCommand = String.Empty;
break;
}
// Stream output from command processing to console.
var output = new PSDataCollection<PSObject>();
output.DataAdded += (dSender, dArgs) =>
{
foreach (var item in output.ReadAll())
{
this.myHost.UI.WriteLine(item.ToString());
}
};
// Process command.
// The Debugger.ProcesCommand method will parse and handle debugger specific
// commands such as 'h' (help), 'list', 'stepover', etc. If the command is
// not specific to the debugger then it will be evaluated as a PowerShell
// command or script. The returned DebuggerCommandResults object will indicate
// whether the command was evaluated by the debugger and if the debugger should
// be released with a specific resume action.
PSCommand psCommand = new PSCommand();
string fullCommand = incompleteLine ? (partialLine + result.command) : result.command;
psCommand.AddScript(fullCommand).AddCommand("Out-String").AddParameter("Stream", true);
incompleteLine = false;
DebuggerCommandResults results = null;
try
{
results = debugger.ProcessCommand(psCommand, output);
}
catch (IncompleteParseException)
{
incompleteLine = true;
partialLine = $"{partialLine}{result.command}{System.Environment.NewLine}";
}
if (!incompleteLine)
{
partialLine = string.Empty;
}
if (!incompleteLine && results.ResumeAction != null)
{
resumeAction = results.ResumeAction;
}
}
// Return from event handler with user resume action.
args.ResumeAction = resumeAction.Value;
}
/// <summary>
/// Helper method to write debugger stop messages.
/// </summary>
/// <param name="args">DebuggerStopEventArgs for current debugger stop</param>
private void WriteDebuggerStopMessages(DebuggerStopEventArgs args)
{
// Write debugger stop information in yellow.
ConsoleColor saveFGColor = Console.ForegroundColor;
Console.ForegroundColor = ConsoleColor.Yellow;
// Show help message only once.
if (!_showHelpMessage)
{
this.myHost.UI.WriteLine("Entering debug mode. Type 'h' to get help.");
this.myHost.UI.WriteLine();
_showHelpMessage = true;
}
// Breakpoint stop information. Writes all breakpoints that
// pertain to this debugger execution stop point.
if (args.Breakpoints.Count > 0)
{
this.myHost.UI.WriteLine("Debugger hit breakpoint on:");
foreach (var breakPoint in args.Breakpoints)
{
this.myHost.UI.WriteLine(breakPoint.ToString());
}
this.myHost.UI.WriteLine();
}
// Script position stop information.
// This writes the InvocationInfo position message if
// there is one.
if (args.InvocationInfo != null)
{
this.myHost.UI.WriteLine(args.InvocationInfo.PositionMessage);
this.myHost.UI.WriteLine();
}
Console.ForegroundColor = saveFGColor;
}
}
}

View File

@ -1,237 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Management.Automation;
using System.Management.Automation.Runspaces;
using System.Management.Automation.Host;
using System.Globalization;
using System.Reflection;
using System.Runtime.InteropServices;
namespace Microsoft.PowerShell.CoreConsoleHost
{
// this is all from https://msdn.microsoft.com/en-us/library/ee706570%28v=vs.85%29.aspx
internal class MyRawUserInterface : PSHostRawUserInterface
{
/// <summary>
/// Gets or sets the background color of the displayed text.
/// This maps to the corresponding Console.Background property.
/// </summary>
public override ConsoleColor BackgroundColor
{
get { return Console.BackgroundColor; }
set { Console.BackgroundColor = value; }
}
/// <summary>
/// Gets or sets the size of the host buffer. In this example the
/// buffer size is adapted from the Console buffer size members.
/// </summary>
public override Size BufferSize
{
get { return new Size(Console.BufferWidth, Console.BufferHeight); }
set { Console.SetBufferSize(value.Width, value.Height); }
}
/// <summary>
/// Gets or sets the cursor position. In this example this
/// functionality is not needed so the property throws a
/// NotImplementException exception.
/// </summary>
public override Coordinates CursorPosition
{
get { return new Coordinates(Console.CursorLeft, Console.CursorTop); }
set { Console.SetCursorPosition(value.X, value.Y); }
}
/// <summary>
/// Gets or sets the size of the displayed cursor. In this example
/// the cursor size is taken directly from the Console.CursorSize
/// property.
/// </summary>
public override int CursorSize
{
get { return Console.CursorSize; }
set { Console.CursorSize = value; }
}
/// <summary>
/// Gets or sets the foreground color of the displayed text.
/// This maps to the corresponding Console.ForgroundColor property.
/// </summary>
public override ConsoleColor ForegroundColor
{
get { return Console.ForegroundColor; }
set { Console.ForegroundColor = value; }
}
/// <summary>
/// Gets a value indicating whether the user has pressed a key. This maps
/// to the corresponding Console.KeyAvailable property.
/// </summary>
public override bool KeyAvailable
{
get { return Console.KeyAvailable; }
}
/// <summary>
/// Gets the dimensions of the largest window that could be
/// rendered in the current display, if the buffer was at the least
/// that large. This example uses the Console.LargestWindowWidth and
/// Console.LargestWindowHeight properties to determine the returned
/// value of this property.
/// </summary>
public override Size MaxPhysicalWindowSize
{
get { return new Size(Console.LargestWindowWidth, Console.LargestWindowHeight); }
}
/// <summary>
/// Gets the dimentions of the largest window size that can be
/// displayed. This example uses the Console.LargestWindowWidth and
/// console.LargestWindowHeight properties to determine the returned
/// value of this property.
/// </summary>
public override Size MaxWindowSize
{
get { return new Size(Console.LargestWindowWidth, Console.LargestWindowHeight); }
}
/// <summary>
/// Gets or sets the position of the displayed window. This example
/// uses the Console window position APIs to determine the returned
/// value of this property.
/// </summary>
public override Coordinates WindowPosition
{
get { return new Coordinates(Console.WindowLeft, Console.WindowTop); }
set { Console.SetWindowPosition(value.X, value.Y); }
}
/// <summary>
/// Gets or sets the size of the displayed window. This example
/// uses the corresponding Console window size APIs to determine the
/// returned value of this property.
/// </summary>
public override Size WindowSize
{
get { return new Size(Console.WindowWidth, Console.WindowHeight); }
set { Console.SetWindowSize(value.Width, value.Height); }
}
/// <summary>
/// Cached Window Title, for systems that needs it
/// </summary>
private string title = String.Empty;
/// <summary>
/// Gets or sets the title of the displayed window. The example
/// maps the Console.Title property to the value of this property.
/// </summary>
public override string WindowTitle
{
get
{
// In Unix/Linux systems, Console.Title current results in a not-implemented
// exception. In that case, we return a cached copy of title that was set earlier.
// Obviously, this will not work if: 1) Title was never set in PowerShell, or 2)
// one sets the windows's title outside of PowerShell.
string result;
try
{
result = Console.Title;
}
catch (PlatformNotSupportedException)
{
return title;
}
return result;
}
set
{
Console.Title = value;
title = value;
}
}
/// <summary>
/// This API resets the input buffer. In this example this
/// functionality is not needed so the method returns nothing.
/// </summary>
public override void FlushInputBuffer()
{
}
/// <summary>
/// This API returns a rectangular region of the screen buffer. In
/// this example this functionality is not needed so the method throws
/// a NotImplementException exception.
/// </summary>
/// <param name="rectangle">Defines the size of the rectangle.</param>
/// <returns>Throws a NotImplementedException exception.</returns>
public override BufferCell[,] GetBufferContents(Rectangle rectangle)
{
throw new NotImplementedException(
"The method or operation is not implemented.");
}
/// <summary>
/// This API reads a pressed, released, or pressed and released keystroke
/// from the keyboard device, blocking processing until a keystroke is
/// typed that matches the specified keystroke options.
/// </summary>
/// <param name="options">Unused</param>
public override KeyInfo ReadKey(ReadKeyOptions options)
{
ConsoleKeyInfo key = Console.ReadKey();
return new KeyInfo((int)key.Key, key.KeyChar, new ControlKeyStates(), true);
}
/// <summary>
/// This API crops a region of the screen buffer. In this example
/// this functionality is not needed so the method throws a
/// NotImplementException exception.
/// </summary>
/// <param name="source">The region of the screen to be scrolled.</param>
/// <param name="destination">The region of the screen to receive the
/// source region contents.</param>
/// <param name="clip">The region of the screen to include in the operation.</param>
/// <param name="fill">The character and attributes to be used to fill all cell.</param>
public override void ScrollBufferContents(Rectangle source, Coordinates destination, Rectangle clip, BufferCell fill)
{
throw new NotImplementedException(
"The method or operation is not implemented.");
}
/// <summary>
/// This method copies an array of buffer cells into the screen buffer
/// at a specified location. In this example this functionality is
/// not needed so the method throws a NotImplementedException exception.
/// </summary>
/// <param name="origin">The parameter is not used.</param>
/// <param name="contents">The parameter is not used.</param>
public override void SetBufferContents(Coordinates origin,
BufferCell[,] contents)
{
throw new NotImplementedException(
"The method or operation is not implemented.");
}
/// <summary>
/// This method copies a given character, foreground color, and background
/// color to a region of the screen buffer. In this example this
/// functionality is not needed so the method throws a
/// NotImplementException exception./// </summary>
/// <param name="rectangle">Defines the area to be filled. </param>
/// <param name="fill">Defines the fill character.</param>
public override void SetBufferContents(Rectangle rectangle, BufferCell fill)
{
throw new NotImplementedException(
"The method or operation is not implemented.");
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -1,566 +0,0 @@
namespace Microsoft.PowerShell.CoreConsoleHost
{
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Globalization;
using System.Management.Automation;
using System.Management.Automation.Host;
using System.Text;
/// <summary>
/// A sample implementation of the PSHostUserInterface abstract class for
/// console applications. Not all members are implemented. Those that are
/// not implemented throw a NotImplementedException exception or return
/// nothing. Members that are implemented include those that map easily to
/// Console APIs and a basic implementation of the prompt API provided.
/// </summary>
internal class MyHostUserInterface : PSHostUserInterface, IHostUISupportsMultipleChoiceSelection
{
/// <summary>
/// Public constructor
/// </summary>
public MyHostUserInterface(bool hasUI, bool interactive)
{
myRawUi = (hasUI) ? new MyRawUserInterface() : null;
Interactive = interactive;
}
/// <summary>
/// A reference to the PSRawUserInterface implementation.
/// </summary>
private MyRawUserInterface myRawUi;
/// <summary>
/// Gets an instance of the PSRawUserInterface class for this host
/// application.
/// </summary>
public override PSHostRawUserInterface RawUI
{
get { return this.myRawUi; }
}
/// <summary>
/// Whether user pop-ups are allowed
/// </summary>
public bool Interactive;
/// <summary>
/// Prompts the user for input.
/// <param name="caption">The caption or title of the prompt.</param>
/// <param name="message">The text of the prompt.</param>
/// <param name="descriptions">A collection of FieldDescription objects
/// that describe each field of the prompt.</param>
/// <returns>A dictionary object that contains the results of the user
/// prompts.</returns>
public override Dictionary<string, PSObject> Prompt(
string caption,
string message,
Collection<FieldDescription> descriptions)
{
if (Interactive)
{
this.Write(
ConsoleColor.White,
Console.BackgroundColor,
caption + System.Environment.NewLine + message + " ");
Dictionary<string, PSObject> results =
new Dictionary<string, PSObject>();
foreach (FieldDescription fd in descriptions)
{
string[] label = GetHotkeyAndLabel(fd.Label);
this.WriteLine(label[1]);
string userData = Console.ReadLine();
if (userData == null)
{
return null;
}
results[fd.Name] = PSObject.AsPSObject(userData);
}
return results;
}
else
{
throw new PSInvalidOperationException("Cannot prompt user when invoked with --noninteractive option.");
}
}
/// <summary>
/// Provides a set of choices that enable the user to choose a
/// single option from a set of options.
/// </summary>
/// <param name="caption">Text that proceeds (a title) the choices.</param>
/// <param name="message">A message that describes the choice.</param>
/// <param name="choices">A collection of ChoiceDescription objects that
/// describ each choice.</param>
/// <param name="defaultChoice">The index of the label in the Choices
/// parameter collection. To indicate no default choice, set to -1.</param>
/// <returns>The index of the Choices parameter collection element that
/// corresponds to the option that is selected by the user.</returns>
public override int PromptForChoice(
string caption,
string message,
Collection<ChoiceDescription> choices,
int defaultChoice)
{
if (Interactive)
{
// Write the caption and message strings in Blue.
this.WriteLine(
ConsoleColor.Blue,
Console.BackgroundColor,
caption + System.Environment.NewLine + message + System.Environment.NewLine);
// Convert the choice collection into something that is
// easier to work with. See the BuildHotkeysAndPlainLabels
// method for details.
string[,] promptData = BuildHotkeysAndPlainLabels(choices);
// Format the overall choice prompt string to display.
StringBuilder sb = new StringBuilder();
for (int element = 0; element < choices.Count; element++)
{
sb.Append(String.Format(
CultureInfo.CurrentCulture,
"|{0}> {1} ",
promptData[0, element],
promptData[1, element]));
}
sb.Append(String.Format(
CultureInfo.CurrentCulture,
"[Default is ({0}]",
promptData[0, defaultChoice]));
// Read prompts until a match is made, the default is
// chosen, or the loop is interrupted with ctrl-C.
while (true)
{
this.WriteLine(ConsoleColor.Cyan, Console.BackgroundColor, sb.ToString());
string data = Console.ReadLine().Trim().ToUpper();
// If the choice string was empty, use the default selection.
if (data.Length == 0)
{
return defaultChoice;
}
// See if the selection matched and return the
// corresponding index if it did.
for (int i = 0; i < choices.Count; i++)
{
if (promptData[0, i] == data)
{
return i;
}
}
this.WriteErrorLine("Invalid choice: " + data);
}
}
else
{
throw new PSInvalidOperationException("Cannot prompt user when invoked with --noninteractive option.");
}
}
#region IHostUISupportsMultipleChoiceSelection Members
/// <summary>
/// Provides a set of choices that enable the user to choose a one or
/// more options from a set of options.
/// </summary>
/// <param name="caption">Text that proceeds (a title) the choices.</param>
/// <param name="message">A message that describes the choice.</param>
/// <param name="choices">A collection of ChoiceDescription objects that
/// describ each choice.</param>
/// <param name="defaultChoices">The index of the label in the Choices
/// parameter collection. To indicate no default choice, set to -1.</param>
/// <returns>The index of the Choices parameter collection element that
/// corresponds to the option that is selected by the user.</returns>
public Collection<int> PromptForChoice(
string caption,
string message,
Collection<ChoiceDescription> choices,
IEnumerable<int> defaultChoices)
{
if (Interactive)
{
// Write the caption and message strings in Blue.
this.WriteLine(
ConsoleColor.Blue,
Console.BackgroundColor,
caption + System.Environment.NewLine + message + System.Environment.NewLine);
// Convert the choice collection into something that is
// easier to work with. See the BuildHotkeysAndPlainLabels
// method for details.
string[,] promptData = BuildHotkeysAndPlainLabels(choices);
// Format the overall choice prompt string to display.
StringBuilder sb = new StringBuilder();
for (int element = 0; element < choices.Count; element++)
{
sb.Append(String.Format(
CultureInfo.CurrentCulture,
"|{0}> {1} ",
promptData[0, element],
promptData[1, element]));
}
Collection<int> defaultResults = new Collection<int>();
if (defaultChoices != null)
{
int countDefaults = 0;
foreach (int defaultChoice in defaultChoices)
{
++countDefaults;
defaultResults.Add(defaultChoice);
}
if (countDefaults != 0)
{
sb.Append(countDefaults == 1 ? "[Default choice is " : "[Default choices are ");
foreach (int defaultChoice in defaultChoices)
{
sb.AppendFormat(
CultureInfo.CurrentCulture,
"\"{0}\",",
promptData[0, defaultChoice]);
}
sb.Remove(sb.Length - 1, 1);
sb.Append("]");
}
}
this.WriteLine(
ConsoleColor.Cyan,
Console.BackgroundColor,
sb.ToString());
// Read prompts until a match is made, the default is
// chosen, or the loop is interrupted with ctrl-C.
Collection<int> results = new Collection<int>();
while (true)
{
ReadNext:
string prompt = string.Format(CultureInfo.CurrentCulture, "Choice[{0}]:", results.Count);
this.Write(ConsoleColor.Cyan, Console.BackgroundColor, prompt);
string data = Console.ReadLine().Trim().ToUpper();
// If the choice string was empty, no more choices have been made.
// If there were no choices made, return the defaults
if (data.Length == 0)
{
return (results.Count == 0) ? defaultResults : results;
}
// See if the selection matched and return the
// corresponding index if it did.
for (int i = 0; i < choices.Count; i++)
{
if (promptData[0, i] == data)
{
results.Add(i);
goto ReadNext;
}
}
this.WriteErrorLine("Invalid choice: " + data);
}
}
else
{
throw new PSInvalidOperationException("Cannot prompt user when invoked with --noninteractive option.");
}
}
#endregion
/// <summary>
/// Prompts the user for credentials with a specified prompt window
/// caption, prompt message, user name, and target name. In this
/// example this functionality is not needed so the method throws a
/// NotImplementException exception.
/// </summary>
/// <param name="caption">The caption for the message window.</param>
/// <param name="message">The text of the message.</param>
/// <param name="userName">The user name whose credential is to be
/// prompted for.</param>
/// <param name="targetName">The name of the target for which the
/// credential is collected.</param>
/// <returns>Throws a NotImplementedException exception.</returns>
public override PSCredential PromptForCredential(
string caption,
string message,
string userName,
string targetName)
{
throw new NotImplementedException(
"The method or operation is not implemented.");
}
/// <summary>
/// Prompts the user for credentials by using a specified prompt window
/// caption, prompt message, user name and target name, credential
/// types allowed to be returned, and UI behavior options. In this
/// example this functionality is not needed so the method throws a
/// NotImplementException exception.
/// </summary>
/// <param name="caption">The caption for the message window.</param>
/// <param name="message">The text of the message.</param>
/// <param name="userName">The user name whose credential is to be
/// prompted for.</param>
/// <param name="targetName">The name of the target for which the
/// credential is collected.</param>
/// <param name="allowedCredentialTypes">A PSCredentialTypes constant
/// that identifies the type of credentials that can be returned.</param>
/// <param name="options">A PSCredentialUIOptions constant that
/// identifies the UI behavior when it gathers the credentials.</param>
/// <returns>Throws a NotImplementedException exception.</returns>
public override PSCredential PromptForCredential(
string caption,
string message,
string userName,
string targetName,
PSCredentialTypes allowedCredentialTypes,
PSCredentialUIOptions options)
{
throw new NotImplementedException(
"The method or operation is not implemented.");
}
/// <summary>
/// Reads characters that are entered by the user until a newline
/// (carriage return) is encountered.
/// </summary>
/// <returns>The characters that are entered by the user.</returns>
public override string ReadLine()
{
return Console.ReadLine();
}
/// <summary>
/// Reads characters entered by the user until a newline (carriage return)
/// is encountered and returns the characters as a secure string. In this
/// example this functionality is not needed so the method throws a
/// NotImplementException exception.
/// </summary>
/// <returns>Throws a NotImplemented exception.</returns>
public override System.Security.SecureString ReadLineAsSecureString()
{
throw new NotImplementedException("The method or operation is not implemented.");
}
/// <summary>
/// Writes characters to the output display of the host.
/// </summary>
/// <param name="value">The characters to be written.</param>
public override void Write(string value)
{
Console.Write(value);
}
/// <summary>
/// Writes characters to the output display of the host with possible
/// foreground and background colors.
/// </summary>
/// <param name="foregroundColor">The color of the characters.</param>
/// <param name="backgroundColor">The backgound color to use.</param>
/// <param name="value">The characters to be written.</param>
public override void Write(
ConsoleColor foregroundColor,
ConsoleColor backgroundColor,
string value)
{
ConsoleColor oldFg = Console.ForegroundColor;
ConsoleColor oldBg = Console.BackgroundColor;
Console.ForegroundColor = foregroundColor;
Console.BackgroundColor = backgroundColor;
Console.Write(value);
Console.ForegroundColor = oldFg;
Console.BackgroundColor = oldBg;
}
/// <summary>
/// Writes a line of characters to the output display of the host
/// with foreground and background colors and appends a newline (carriage return).
/// </summary>
/// <param name="foregroundColor">The foreground color of the display. </param>
/// <param name="backgroundColor">The background color of the display. </param>
/// <param name="value">The line to be written.</param>
public override void WriteLine(
ConsoleColor foregroundColor,
ConsoleColor backgroundColor,
string value)
{
ConsoleColor oldFg = Console.ForegroundColor;
ConsoleColor oldBg = Console.BackgroundColor;
Console.ForegroundColor = foregroundColor;
Console.BackgroundColor = backgroundColor;
Console.WriteLine(value);
Console.ForegroundColor = oldFg;
Console.BackgroundColor = oldBg;
}
/// <summary>
/// Writes a debug message to the output display of the host.
/// </summary>
/// <param name="message">The debug message that is displayed.</param>
public override void WriteDebugLine(string message)
{
this.WriteLine(
ConsoleColor.Yellow,
Console.BackgroundColor,
String.Format(CultureInfo.CurrentCulture, "DEBUG: {0}", message));
}
/// <summary>
/// Writes an error message to the output display of the host.
/// </summary>
/// <param name="value">The error message that is displayed.</param>
public override void WriteErrorLine(string value)
{
this.WriteLine(
ConsoleColor.Red,
Console.BackgroundColor,
value);
}
/// <summary>
/// Writes a newline character (carriage return)
/// to the output display of the host.
/// </summary>
public override void WriteLine()
{
Console.WriteLine();
}
/// <summary>
/// Writes a line of characters to the output display of the host
/// and appends a newline character(carriage return).
/// </summary>
/// <param name="value">The line to be written.</param>
public override void WriteLine(string value)
{
Console.WriteLine(value);
}
/// <summary>
/// Writes a progress report to the output display of the host.
/// </summary>
/// <param name="sourceId">Unique identifier of the source of the record. </param>
/// <param name="record">A ProgressReport object.</param>
public override void WriteProgress(long sourceId, ProgressRecord record)
{
if (record == null)
{
throw PSTraceSource.NewArgumentNullException("record");
}
string percentComplete = " [";
for (int i =0; i < record.PercentComplete; i++){
percentComplete = percentComplete + "0";
}
percentComplete = percentComplete + "]";
Console.Write ("\r{0} {1} {2} ", record.Activity, record.StatusDescription, percentComplete, "\r");
if (record.PercentComplete == 100){
Console.WriteLine(); //create a new line for the prompt
}
}
/// <summary>
/// Writes a verbose message to the output display of the host.
/// </summary>
/// <param name="message">The verbose message that is displayed.</param>
public override void WriteVerboseLine(string message)
{
this.WriteLine(
ConsoleColor.Green,
Console.BackgroundColor,
String.Format(CultureInfo.CurrentCulture, "VERBOSE: {0}", message));
}
/// <summary>
/// Writes a warning message to the output display of the host.
/// </summary>
/// <param name="message">The warning message that is displayed.</param>
public override void WriteWarningLine(string message)
{
this.WriteLine(
ConsoleColor.Yellow,
Console.BackgroundColor,
String.Format(CultureInfo.CurrentCulture, "WARNING: {0}", message));
}
/// <summary>
/// Parse a string containing a hotkey character.
/// Take a string of the form
/// Yes to &amp;all
/// and returns a two-dimensional array split out as
/// "A", "Yes to all".
/// </summary>
/// <param name="input">The string to process</param>
/// <returns>
/// A two dimensional array containing the parsed components.
/// </returns>
private static string[] GetHotkeyAndLabel(string input)
{
string[] result = new string[] { String.Empty, String.Empty };
string[] fragments = input.Split('&');
if (fragments.Length == 2)
{
if (fragments[1].Length > 0)
{
result[0] = fragments[1][0].ToString().ToUpper();
}
result[1] = (fragments[0] + fragments[1]).Trim();
}
else
{
result[1] = input;
}
return result;
}
/// <summary>
/// This is a private worker function splits out the
/// accelerator keys from the menu and builds a two
/// dimentional array with the first access containing the
/// accelerator and the second containing the label string
/// with the &amp; removed.
/// </summary>
/// <param name="choices">The choice collection to process</param>
/// <returns>
/// A two dimensional array containing the accelerator characters
/// and the cleaned-up labels</returns>
private static string[,] BuildHotkeysAndPlainLabels(
Collection<ChoiceDescription> choices)
{
// Allocate the result array
string[,] hotkeysAndPlainLabels = new string[2, choices.Count];
for (int i = 0; i < choices.Count; ++i)
{
string[] hotkeyAndLabel = GetHotkeyAndLabel(choices[i].Label);
hotkeysAndPlainLabels[0, i] = hotkeyAndLabel[0];
hotkeysAndPlainLabels[1, i] = hotkeyAndLabel[1];
}
return hotkeysAndPlainLabels;
}
}
}

View File

@ -17,6 +17,13 @@
"define": [ "CORECLR" ]
},
"imports": [ "dnxcore50", "portable-net45+win8" ]
},
"net451": {
"frameworkAssemblies": {
"System.Windows.Forms": {
"type": "build"
}
}
}
}
}

View File

@ -14,7 +14,7 @@ using System.Resources;
[assembly:InternalsVisibleTo("Microsoft.PowerShell.Commands.Utility")]
[assembly:InternalsVisibleTo("Microsoft.PowerShell.Security")]
[assembly:InternalsVisibleTo("Microsoft.PowerShell.CoreCLR.AssemblyLoadContext")]
[assembly:InternalsVisibleTo("powershell")]
[assembly:InternalsVisibleTo("Microsoft.PowerShell.ConsoleHost")]
[assembly:InternalsVisibleTo("powershell-tests")]
#else

View File

@ -60,7 +60,10 @@ namespace System.Management.Automation.Runspaces
var unused0 = RunspaceInit.OutputEncodingDescription;
// Amsi initialize can also be a little slow
AmsiUtils.Init();
if (Platform.IsWindows)
{
AmsiUtils.Init();
}
// This will init some tables and could load some assemblies.
var unused1 = TypeAccelerators.builtinTypeAccelerators;

View File

@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 2.8.4)
project(PSL-NATIVE)
add_compile_options(-std=c++11 -Wall -Werror)
set(LIBRARY_OUTPUT_PATH "${PROJECT_SOURCE_DIR}/../Microsoft.PowerShell.CoreConsoleHost")
set(LIBRARY_OUTPUT_PATH "${PROJECT_SOURCE_DIR}/../powershell")
# test in BUILD_DIR
enable_testing()

View File

@ -1,3 +1,3 @@
*.a
*.so
*.dylib
*.dylib

29
src/powershell/Program.cs Normal file
View File

@ -0,0 +1,29 @@
/********************************************************************++
Copyright (c) Microsoft Corporation. All rights reserved.
--********************************************************************/
using System.Management.Automation;
namespace Microsoft.PowerShell
{
/// <summary>
/// Defines an entry point for the .NET CLI "powershell" app
/// </summary>
public sealed class ManagedPSEntry
{
/// <summary>
/// Starts the managed MSH
/// </summary>
/// <param name="args">
/// Command line arguments to the managed MSH
/// </param>
public static int Main(string[] args)
{
#if CORECLR
// Open PowerShell has to set the ALC here, since we don't own the native host
PowerShellAssemblyLoadContextInitializer.SetPowerShellAssemblyLoadContext(string.Empty);
#endif
return UnmanagedPSEntry.Start(string.Empty, args, args.Length);
}
}
}

20
src/powershell/README.md Normal file
View File

@ -0,0 +1,20 @@
PowerShell
==========
The `powershell[.exe]` executable for Open PowerShell is built by this project,
as it is the top dependency of the graph, and has `emitEntryPoint: true`,
meaning a native executable is produced automatically by CLI (no need to own a
separate native host).
This project is a very simple shim that provides a `Main` function for .NET CLI
to produce an app. It initializes PowerShell's custom `AssemblyLoadContext` and
then delegates to the same `Start` function in
`Microsoft.PowerShell.ConsoleHost` that the original native PowerShell host
executes; thus we share the same entry point and the same PowerShell host, but
use a different native host. This lets us take full advantage of .NET CLI's
native host.
We use this shim so that the `ConsoleHost` project and the original native host
do not have to be changed. Additionally, until .NET CLI bugs surrounding content
file deployment are solved, this shim allows us to continue with our split
`Modules` folders work-around to deploy the correct versions.

View File

@ -1,21 +1,17 @@
{
"name": "powershell",
"version": "1.0.0-*",
"description": "PowerShell Managed Console for .NET Core",
"description": ".NET CLI PowerShell app",
"authors": [ "andschwa" ],
"compilationOptions": {
"warningsAsErrors": true,
"allowUnsafe": true,
"emitEntryPoint": true
},
"dependencies": {
"Newtonsoft.Json": "8.0.2",
"System.Xml.XDocument": "4.0.11-rc2-24103",
"System.IO.MemoryMappedFiles": "4.0.0-rc2-24103",
"Microsoft.PowerShell.Commands.Management": "1.0.0-*",
"Microsoft.PowerShell.Commands.Utility": "1.0.0-*",
"Microsoft.PowerShell.PSReadLine": "1.0.0-*"
"Microsoft.PowerShell.ConsoleHost": "1.0.0-*"
},
"content": [
@ -29,8 +25,11 @@
"frameworks": {
"netcoreapp1.0": {
"imports": [ "dnxcore50", "portable-net45+win8" ],
"compilationOptions": {
"define": [ "CORECLR" ]
},
"dependencies": {
"Microsoft.NETCore.App": "1.0.0-rc2-3002485"
"Microsoft.NETCore.App": "1.0.0-rc2-3002668"
}
}
},

View File

@ -1,7 +1,6 @@
using Xunit;
using System;
using System.Management.Automation;
using Microsoft.PowerShell.CoreConsoleHost;
// This collection fixture initializes Core PowerShell's AssemblyLoadContext once and only
// once. Attempting to initialize in a class level fixture will cause multiple

View File

@ -5,7 +5,7 @@
"authors": [ "andschwa" ],
"dependencies": {
"Microsoft.PowerShell.CoreConsoleHost": "1.0.0-*"
"Microsoft.PowerShell.ConsoleHost": "1.0.0-*"
},
"frameworks": {

View File

@ -9,18 +9,6 @@ namespace PSTests
[Collection("AssemblyLoadContext")]
public static class PlatformTests
{
[Fact]
public static void TestIsLinux()
{
Assert.True(Platform.IsLinux);
}
[Fact]
public static void TestIsWindows()
{
Assert.False(Platform.IsWindows);
}
[Fact]
public static void TestIsCore()
{
@ -80,7 +68,7 @@ namespace PSTests
}
}
[Fact]
[Fact(Skip="Bad arguments for OS X")]
public static void TestGetMachineName()
{
var startInfo = new ProcessStartInfo
@ -103,7 +91,7 @@ namespace PSTests
}
}
[Fact]
[Fact(Skip="Bad arguments for OS X")]
public static void TestGetFQDN()
{
var startInfo = new ProcessStartInfo
@ -126,7 +114,7 @@ namespace PSTests
}
}
[Fact]
[Fact(Skip="Bad arguments for OS X")]
public static void TestGetDomainName()
{
var startInfo = new ProcessStartInfo

View File

@ -13,7 +13,6 @@ using System.Management.Automation.Provider;
using System.Management.Automation.Runspaces;
using Microsoft.PowerShell;
using Microsoft.PowerShell.Commands;
using Microsoft.PowerShell.CoreConsoleHost;
namespace PSTests
{

View File

@ -2,7 +2,6 @@ using Xunit;
using System;
using System.Management.Automation;
using System.Management.Automation.Runspaces;
using Microsoft.PowerShell.CoreConsoleHost;
namespace PSTests
{
@ -63,55 +62,6 @@ namespace PSTests
}
}
[Fact]
public void TestRunspaceWithPowerShellAndHost()
{
Listener listener = new Listener("", false, false);
MyHost myHost = new MyHost(listener);
using (var runspace = RunspaceFactory.CreateRunspace(myHost))
{
runspace.Open();
using (PowerShell powerShell = PowerShell.Create())
{
powerShell.Runspace = runspace;
powerShell.AddScript(script);
int objCount = 0;
foreach (var result in powerShell.Invoke())
{
++objCount;
Assert.NotNull(result);
}
Assert.Equal(count, objCount);
}
runspace.Close();
}
}
[Fact]
public void TestRunspaceWithFunction()
{
Listener listener = new Listener("", false, false);
MyHost myHost = new MyHost(listener);
using (var runspace = RunspaceFactory.CreateRunspace(myHost))
{
runspace.Open();
using (PowerShell powerShell = PowerShell.Create())
{
powerShell.Runspace = runspace;
powerShell.AddScript("{1}.Invoke()");
powerShell.Invoke();
}
runspace.Close();
}
}
[Fact]
public void TestRunspaceWithPowerShellAndInitialSessionState()
{

View File

@ -9,7 +9,6 @@ using System.Management.Automation.Internal;
using System.Management.Automation.Internal.Host;
using System.Management.Automation.Runspaces;
using Microsoft.PowerShell;
using Microsoft.PowerShell.CoreConsoleHost;
namespace PSTests
{

View File

@ -0,0 +1,145 @@
using namespace System.Diagnostics
Describe "ConsoleHost unit tests" {
$powershell = Join-Path -Path $PsHome -ChildPath "powershell"
Context "CommandLine" {
It "simple -args" -Pending:($env:TRAVIS_OS_NAME -eq "osx") {
& $powershell -noprofile { $args[0] } -args "hello world" | Should Be "hello world"
}
It "array -args" -Pending:($env:TRAVIS_OS_NAME -eq "osx") {
& $powershell -noprofile { $args[0] } -args 1,(2,3) | Should Be 1
(& $powershell -noprofile { $args[1] } -args 1,(2,3))[1] | Should Be 3
}
foreach ($x in "--help", "-help", "-h", "-?", "--he", "-hel", "--HELP", "-hEl") {
It "Accepts '$x' as a parameter for help" {
& $powershell -noprofile $x | ?{ $_ -match "PowerShell[.exe] -Help | -? | /?" } | Should Not BeNullOrEmpty
}
}
It "Should accept a Base64 encoded command" {
$commandString = "Get-Location"
$encodedCommand = [System.Convert]::ToBase64String([System.Text.Encoding]::Unicode.GetBytes($commandString))
# We don't compare to `Get-Location` directly because object and formatted output comparisons are difficult
$expected = & $powershell -noprofile -command $commandString
$actual = & $powershell -noprofile -EncodedCommand $encodedCommand
$actual | Should Be $expected
}
}
Context "Pipe to/from powershell" {
$p = [PSCustomObject]@{X=10;Y=20}
It "xml input" {
$p | & $powershell -noprofile { $input | Foreach-Object {$a = 0} { $a += $_.X + $_.Y } { $a } } | Should Be 30
$p | & $powershell -noprofile -inputFormat xml { $input | Foreach-Object {$a = 0} { $a += $_.X + $_.Y } { $a } } | Should Be 30
}
It "text input" -Pending:($env:TRAVIS_OS_NAME -eq "osx") {
# Join (multiple lines) and remove whitespace (we don't care about spacing) to verify we converted to string (by generating a table)
$p | & $powershell -noprofile -inputFormat text { -join ($input -replace "\s","") } | Should Be "XY--1020"
}
It "xml output" {
& $powershell -noprofile { [PSCustomObject]@{X=10;Y=20} } | Foreach-Object {$a = 0} { $a += $_.X + $_.Y } { $a } | Should Be 30
& $powershell -noprofile -outputFormat xml { [PSCustomObject]@{X=10;Y=20} } | Foreach-Object {$a = 0} { $a += $_.X + $_.Y } { $a } | Should Be 30
}
It "text output" -Pending:($env:TRAVIS_OS_NAME -eq "osx") {
# Join (multiple lines) and remove whitespace (we don't care about spacing) to verify we converted to string (by generating a table)
-join (& $powershell -noprofile -outputFormat text { [PSCustomObject]@{X=10;Y=20} }) -replace "\s","" | Should Be "XY--1020"
}
}
Context "Redirected standard handles" {
function NewProcessStartInfo([string]$CommandLine, [switch]$RedirectStdIn)
{
return [ProcessStartInfo]@{
FileName = $powershell
Arguments = $CommandLine
RedirectStandardInput = $RedirectStdIn
RedirectStandardOutput = $true
RedirectStandardError = $true
UseShellExecute = $false
}
}
function RunPowerShell([ProcessStartInfo]$si)
{
$process = [Process]::Start($si)
return $process
}
function EnsureChildHasExited([Process]$process, [int]$WaitTimeInMS = 15000)
{
$process.WaitForExit($WaitTimeInMS)
if (!$process.HasExited)
{
$process.HasExited | Should Be $true
$process.Kill()
}
}
It "Simple redirected output" -Pending:($env:TRAVIS_OS_NAME -eq "osx") {
$si = NewProcessStartInfo "-noprofile 1+1"
$process = RunPowerShell $si
$process.StandardOutput.ReadToEnd() | Should Be 2
EnsureChildHasExited $process
}
$nl = [Environment]::Newline
# Redirected input is broken on Windows in .NET Core
It "Redirected input" -Pending:$IsWindows {
$si = NewProcessStartInfo "-noprofile ""`$function:prompt = { 'PS> ' }""" -RedirectStdIn
$process = RunPowerShell $si
$process.StandardInput.Write("1+1`n")
$process.StandardOutput.ReadLine() | Should Be "PS> 1+1"
$process.StandardOutput.ReadLine() | Should Be "2"
$process.StandardInput.Write("1+2`n")
$process.StandardOutput.ReadLine() | Should Be "PS> 1+2"
$process.StandardOutput.ReadLine() | Should Be "3"
$process.StandardInput.Close()
$process.StandardOutput.ReadToEnd() | Should Be "PS> "
EnsureChildHasExited $process
}
It "Redirected input explicit prompting" -Pending:$IsCore {
$si = NewProcessStartInfo "-noprofile -File -" -RedirectStdIn
$process = RunPowerShell $si
$process.StandardInput.Write("`$function:prompt = { 'PS> ' }`n")
$null = $process.StandardOutput.ReadLine()
$process.StandardInput.Write("1+1`n")
$process.StandardOutput.ReadLine() | Should Be "PS> 1+1"
$process.StandardOutput.ReadLine() | Should Be "2"
$process.StandardInput.Close()
$process.StandardOutput.ReadToEnd() | Should Be "PS> "
EnsureChildHasExited $process
}
It "Redirected input no prompting" -Pending:$IsCore {
$si = NewProcessStartInfo "-noprofile -" -RedirectStdIn
$process = RunPowerShell $si
$process.StandardInput.Write("1+1`n")
$process.StandardInput.Close()
$process.StandardOutput.ReadToEnd() | Should Be "2${nl}"
EnsureChildHasExited $process
}
It "Redirected input w/ nested prompt" -Pending:$IsCore {
$si = NewProcessStartInfo "-noprofile ""`$function:prompt = { 'PS' + ('>'*(`$nestedPromptLevel+1)) + ' ' }""" -RedirectStdIn
$process = RunPowerShell $si
$process.StandardInput.Write("`$host.EnterNestedPrompt()`n")
$process.StandardOutput.ReadLine() | Should Be "PS> `$host.EnterNestedPrompt()"
$process.StandardInput.Write("exit`n")
$process.StandardOutput.ReadLine() | Should Be "PS>> exit"
$process.StandardInput.Close()
$process.StandardOutput.ReadToEnd() | Should Be "PS> "
EnsureChildHasExited $process
}
}
}

View File

@ -1,21 +0,0 @@
Describe "CoreConsoleHost unit tests" {
$powershell = Join-Path -Path $PsHome -ChildPath "powershell"
Context "Command-line parsing" {
foreach ($x in "--help", "-help", "-h", "-?", "--he", "-hel", "--HELP", "-hEl") {
It "Accepts '$x' as a parameter for help" {
& $powershell -noprofile $x | ?{ $_ -match "usage: powershell" } | Should Not BeNullOrEmpty
}
}
It "Should accept a Base64 encoded command" {
$commandString = "Get-Location"
$encodedCommand = [System.Convert]::ToBase64String([System.Text.Encoding]::Unicode.GetBytes($commandString))
# We don't compare to `Get-Location` directly because object and formatted output comparisons are difficult
$expected = & $powershell -noprofile -command $commandString
$actual = & $powershell -noprofile -EncodedCommand $encodedCommand
$actual | Should Be $expected
}
}
}

View File

@ -26,19 +26,19 @@ Describe "Format-Custom" {
Describe "Format-Custom DRT basic functionality" -Tags DRT{
It "Format-Custom with subobject should work"{
$expectResult1 = "this is the name"
$expectResult2 = "this is the name of the sub object"
$testObject = @{}
$testObject.name = $expectResult1
$testObject.subObjectValue = @{}
$testObject.subObjectValue.name = $expectResult2
$testObject.subObjectValue.array = (0..63)
$testObject.subObjectValue.stringarray = @("one","two")
$result = $testObject | Format-Custom | Out-String
$result | Should Match $expectResult1
$result | Should Match $expectResult2
$result | Should Match "one"
$result | Should Match "two"
}
}
It "Format-Custom with subobject should work" -Pending:($env:TRAVIS_OS_NAME -eq "osx") {
$expectResult1 = "this is the name"
$expectResult2 = "this is the name of the sub object"
$testObject = @{}
$testObject.name = $expectResult1
$testObject.subObjectValue = @{}
$testObject.subObjectValue.name = $expectResult2
$testObject.subObjectValue.array = (0..63)
$testObject.subObjectValue.stringarray = @("one","two")
$result = $testObject | Format-Custom | Out-String
$result | Should Match $expectResult1
$result | Should Match $expectResult2
$result | Should Match "one"
$result | Should Match "two"
}
}

View File

@ -20,7 +20,7 @@ Describe "Format-List" {
$actual | Should Be $expected
}
It "Should produce the expected output" {
It "Should produce the expected output" -Pending:($env:TRAVIS_OS_NAME -eq "osx") {
$expected = "${nl}${nl}testName : testValue${nl}${nl}${nl}${nl}"
$in = New-Object PSObject
Add-Member -InputObject $in -MemberType NoteProperty -Name testName -Value testValue
@ -68,14 +68,14 @@ Describe "Format-List" {
}
Describe "Format-List DRT basic functionality" -Tags DRT{
It "Format-List with array should work"{
$al = (0..255)
$info = @{}
$info.array = $al
$result = $info | Format-List | Out-String
$result | Should Match "Name : array\s+Value : {0, 1, 2, 3...}"
}
It "Format-List with array should work"-Pending:($env:TRAVIS_OS_NAME -eq "osx") {
$al = (0..255)
$info = @{}
$info.array = $al
$result = $info | Format-List | Out-String
$result | Should Match "Name : array\s+Value : {0, 1, 2, 3...}"
}
It "Format-List with No Objects for End-To-End should work"{
$p = @{}
$result = $p | Format-List -Force -Property "foo","bar" | Out-String
@ -106,22 +106,22 @@ Describe "Format-List DRT basic functionality" -Tags DRT{
$result.Trim() | Should BeNullOrEmpty
}
It "Format-List with complex object for End-To-End should work"{
Add-Type -TypeDefinition "public enum MyDayOfWeek{Sun,Mon,Tue,Wed,Thr,Fri,Sat}"
$eto = New-Object MyDayOfWeek
$info = @{}
$info.intArray = 1,2,3,4
$info.arrayList = "string1","string2"
$info.enumerable = [MyDayOfWeek]$eto
$info.enumerableTestObject = $eto
$result = $info|Format-List|Out-String
$result | Should Match "Name : enumerableTestObject"
$result | Should Match "Value : Sun"
$result | Should Match "Name : arrayList"
$result | Should Match "Value : {string1, string2}"
$result | Should Match "Name : enumerable"
$result | Should Match "Value : Sun"
$result | Should Match "Name : intArray"
$result | Should Match "Value : {1, 2, 3, 4}"
}
It "Format-List with complex object for End-To-End should work" -Pending:($env:TRAVIS_OS_NAME -eq "osx") {
Add-Type -TypeDefinition "public enum MyDayOfWeek{Sun,Mon,Tue,Wed,Thr,Fri,Sat}"
$eto = New-Object MyDayOfWeek
$info = @{}
$info.intArray = 1,2,3,4
$info.arrayList = "string1","string2"
$info.enumerable = [MyDayOfWeek]$eto
$info.enumerableTestObject = $eto
$result = $info|Format-List|Out-String
$result | Should Match "Name : enumerableTestObject"
$result | Should Match "Value : Sun"
$result | Should Match "Name : arrayList"
$result | Should Match "Value : {string1, string2}"
$result | Should Match "Name : enumerable"
$result | Should Match "Value : Sun"
$result | Should Match "Name : intArray"
$result | Should Match "Value : {1, 2, 3, 4}"
}
}

View File

@ -1,213 +1,213 @@
Describe "Format-Table" {
It "Should call format table on piped input without error" {
{ Get-Date | Format-Table } | Should Not Throw
It "Should call format table on piped input without error" {
{ Get-Date | Format-Table } | Should Not Throw
{ Get-Date | ft } | Should Not Throw
}
{ Get-Date | ft } | Should Not Throw
}
It "Should return a format object data type" {
$val = (Get-Date | Format-Table | gm )
It "Should return a format object data type" {
$val = (Get-Date | Format-Table | gm )
$val2 = (Get-Date | Format-Table | gm )
$val2 = (Get-Date | Format-Table | gm )
$val.TypeName | Should Match "Microsoft.Powershell.Commands.Internal.Format"
$val.TypeName | Should Match "Microsoft.Powershell.Commands.Internal.Format"
$val2.TypeName | Should Match "Microsoft.Powershell.Commands.Internal.Format"
}
$val2.TypeName | Should Match "Microsoft.Powershell.Commands.Internal.Format"
}
It "Should be able to be called with optional parameters" {
$v1 = (Get-Date | Format-Table *)
$v2 = (Get-Date | Format-Table -Property Hour)
$v3 = (Get-Date | Format-Table -GroupBy Hour)
It "Should be able to be called with optional parameters" {
$v1 = (Get-Date | Format-Table *)
$v2 = (Get-Date | Format-Table -Property Hour)
$v3 = (Get-Date | Format-Table -GroupBy Hour)
$v12 = (Get-Date | ft *)
$v22 = (Get-Date | ft -Property Hour)
$v32 = (Get-Date | ft -GroupBy Hour)
$v12 = (Get-Date | ft *)
$v22 = (Get-Date | ft -Property Hour)
$v32 = (Get-Date | ft -GroupBy Hour)
}
}
}
Describe "Format-Table DRT Unit Tests" -Tags DRT{
It "Format-Table with not existing table with force should throw PipelineStoppedException"{
$obj = New-Object -typename PSObject
try
{
$obj | Format-Table -view bar -force -EA Stop
Throw "Execution OK"
It "Format-Table with not existing table with force should throw PipelineStoppedException"{
$obj = New-Object -typename PSObject
try
{
$obj | Format-Table -view bar -force -EA Stop
Throw "Execution OK"
}
catch
{
$_.CategoryInfo | Should Match "PipelineStoppedException"
$_.FullyQualifiedErrorId | Should be "FormatViewNotFound,Microsoft.PowerShell.Commands.FormatTableCommand"
}
}
catch
{
$_.CategoryInfo | Should Match "PipelineStoppedException"
$_.FullyQualifiedErrorId | Should be "FormatViewNotFound,Microsoft.PowerShell.Commands.FormatTableCommand"
It "Format-Table with array should work" -Pending:($env:TRAVIS_OS_NAME -eq "osx") {
$al = (0..255)
$info = @{}
$info.array = $al
$result = $info|Format-Table|Out-String
$result | Should Match "array\s+{0, 1, 2, 3...}"
}
}
It "Format-Table with array should work"{
$al = (0..255)
$info = @{}
$info.array = $al
$result = $info|Format-Table|Out-String
$result | Should Match "array\s+{0, 1, 2, 3...}"
}
It "Format-Table with Negative Count should work"{
$FormatEnumerationLimit = -1
$result = Format-Table -inputobject @{'test'= 1, 2}
$resultStr = $result|Out-String
$resultStr | Should Match "test\s+{1, 2}"
}
#pending on issue#888
It "Format-Table with Zero Count should work" -pending{
$FormatEnumerationLimit = 0
$result = Format-Table -inputobject @{'test'= 1, 2}
$resultStr = $result|Out-String
$resultStr | Should Match "test\s+{...}"
}
It "Format-Table with Less Count should work"{
$FormatEnumerationLimit = 1
$result = Format-Table -inputobject @{'test'= 1, 2}
$resultStr = $result|Out-String
$resultStr | Should Match "test\s+{1...}"
}
It "Format-Table with More Count should work"{
$FormatEnumerationLimit = 10
$result = Format-Table -inputobject @{'test'= 1, 2}
$resultStr = $result|Out-String
$resultStr | Should Match "test\s+{1, 2}"
}
It "Format-Table with Equal Count should work"{
$FormatEnumerationLimit = 2
$result = Format-Table -inputobject @{'test'= 1, 2}
$resultStr = $result|Out-String
$resultStr | Should Match "test\s+{1, 2}"
}
#pending on issue#888
It "Format-Table with Bogus Count should throw Exception" -pending{
$FormatEnumerationLimit = "abc"
$result = Format-Table -inputobject @{'test'= 1, 2}
$resultStr = $result|Out-String
$resultStr | Should Match "test\s+{1, 2}"
}
#pending on issue#888
It "Format-Table with Var Deleted should throw Exception" -pending{
$FormatEnumerationLimit = 2
Remove-Variable FormatEnumerationLimit
$result = Format-Table -inputobject @{'test'= 1, 2}
$resultStr = $result|Out-String
$resultStr | Should Match "test\s+{1, 2}"
}
It "Format-Table with new line should work"{
$info = @{}
$info.name = "1\n2"
$result = $info|Format-Table|Out-String
$result | Should Match "name\s+1.+2"
}
It "Format-Table with ExposeBug920454 should work"{
$IP1 = [System.Net.IPAddress]::Parse("1.1.1.1")
$IP2 = [System.Net.IPAddress]::Parse("4fde:0000:0000:0002:0022:f376:255.59.171.63")
$IPs = New-Object System.Collections.ArrayList
$IPs.Add($IP1)
$IPs.Add($IP2)
$info = @{}
$info.test = $IPs
$result = $info|Format-Table|Out-String
$result | Should Match "test\s+{1.1.1.1, 4fde::2:22:f376:ff3b:ab3f}"
}
It "Format-Table with Autosize should work"{
$IP1 = [PSCustomObject]@{'name'='Bob';'size'=1234;'booleanValue'=$true;}
$IP2 = [PSCustomObject]@{'name'='Jim';'size'=5678;'booleanValue'=$false;}
$IPs = New-Object System.Collections.ArrayList
$IPs.Add($IP1)
$IPs.Add($IP2)
$result = $IPs|Format-Table -Autosize|Out-String
$result | Should Match "name size booleanValue"
$result | Should Match "---- ---- ------------"
$result | Should Match "Bob\s+1234\s+True"
$result | Should Match "Jim\s+5678\s+False"
}
It "Format-Table with No Objects for End-To-End should work"{
$p = @{}
$result = $p|Format-Table -Property "foo","bar"|Out-String
$result | Should BeNullOrEmpty
}
It "Format-Table with Null Objects for End-To-End should work"{
$p = $null
$result = $p|Format-Table -Property "foo","bar"|Out-String
$result | Should BeNullOrEmpty
}
#pending on issue#900
It "Format-Table with single line string for End-To-End should work" -pending{
$p = "single line string"
$result = $p|Format-Table -Property "foo","bar"|Out-String
$result | Should BeNullOrEmpty
}
#pending on issue#900
It "Format-Table with multiple line string for End-To-End should work" -pending{
$p = "Line1\nLine2"
$result = $p|Format-Table -Property "foo","bar"|Out-String
$result | Should BeNullOrEmpty
}
#pending on issue#900
It "Format-Table with string sequence for End-To-End should work" -pending{
$p = "Line1","Line2"
$result = $p|Format-Table -Property "foo","bar"|Out-String
$result | Should BeNullOrEmpty
}
#pending on issue#900
It "Format-Table with string sequence for End-To-End should work" -pending{
$p = "Line1","Line2"
$result = $p|Format-Table -Property "foo","bar"|Out-String
$result | Should BeNullOrEmpty
}
It "Format-Table with complex object for End-To-End should work"{
Add-Type -TypeDefinition "public enum MyDayOfWeek{Sun,Mon,Tue,Wed,Thr,Fri,Sat}"
$eto = New-Object MyDayOfWeek
$info = @{}
$info.intArray = 1,2,3,4
$info.arrayList = "string1","string2"
$info.enumerable = [MyDayOfWeek]$eto
$info.enumerableTestObject = $eto
$result = $info|Format-Table|Out-String
$result | Should Match "intArray\s+{1, 2, 3, 4}"
$result | Should Match "arrayList\s+{string1, string2}"
$result | Should Match "enumerable\s+Sun"
$result | Should Match "enumerableTestObject\s+Sun"
}
It "Format-Table with Expand Enumerable should work"{
$obj1 = "x 0","y 0"
$obj2 = "x 1","y 1"
$objs = New-Object System.Collections.ArrayList
$objs.Add($obj1)
$objs.Add($obj2)
$mo = [PSCustomObject]@{name = "this is name";sub = $objs}
$result1 = $mo|Format-Table -Expand CoreOnly|Out-String
$result1 | Should Match "name\s+sub"
$result1 | Should Match "this is name"
$result2 = $mo|Format-Table -Expand EnumOnly|Out-String
$result2 | Should Match "name\s+sub"
$result2 | Should Match "this is name\s+{x 0 y 0, x 1 y 1}"
$result3 = $mo|Format-Table -Expand Both|Out-String
$result3 | Should Match "name\s+sub"
$result3 | Should Match "this is name\s+{x 0 y 0, x 1 y 1}"
}
}
It "Format-Table with Negative Count should work" -Pending:($env:TRAVIS_OS_NAME -eq "osx") {
$FormatEnumerationLimit = -1
$result = Format-Table -inputobject @{'test'= 1, 2}
$resultStr = $result|Out-String
$resultStr | Should Match "test\s+{1, 2}"
}
# Pending on issue#888
It "Format-Table with Zero Count should work" -Pending {
$FormatEnumerationLimit = 0
$result = Format-Table -inputobject @{'test'= 1, 2}
$resultStr = $result|Out-String
$resultStr | Should Match "test\s+{...}"
}
It "Format-Table with Less Count should work" -Pending:($env:TRAVIS_OS_NAME -eq "osx") {
$FormatEnumerationLimit = 1
$result = Format-Table -inputobject @{'test'= 1, 2}
$resultStr = $result|Out-String
$resultStr | Should Match "test\s+{1...}"
}
It "Format-Table with More Count should work" -Pending:($env:TRAVIS_OS_NAME -eq "osx") {
$FormatEnumerationLimit = 10
$result = Format-Table -inputobject @{'test'= 1, 2}
$resultStr = $result|Out-String
$resultStr | Should Match "test\s+{1, 2}"
}
It "Format-Table with Equal Count should work" -Pending:($env:TRAVIS_OS_NAME -eq "osx") {
$FormatEnumerationLimit = 2
$result = Format-Table -inputobject @{'test'= 1, 2}
$resultStr = $result|Out-String
$resultStr | Should Match "test\s+{1, 2}"
}
# Pending on issue#888
It "Format-Table with Bogus Count should throw Exception" -Pending {
$FormatEnumerationLimit = "abc"
$result = Format-Table -inputobject @{'test'= 1, 2}
$resultStr = $result|Out-String
$resultStr | Should Match "test\s+{1, 2}"
}
# Pending on issue#888
It "Format-Table with Var Deleted should throw Exception" -Pending {
$FormatEnumerationLimit = 2
Remove-Variable FormatEnumerationLimit
$result = Format-Table -inputobject @{'test'= 1, 2}
$resultStr = $result|Out-String
$resultStr | Should Match "test\s+{1, 2}"
}
It "Format-Table with new line should work" -Pending:($env:TRAVIS_OS_NAME -eq "osx") {
$info = @{}
$info.name = "1\n2"
$result = $info|Format-Table|Out-String
$result | Should Match "name\s+1.+2"
}
It "Format-Table with ExposeBug920454 should work" -Pending:($env:TRAVIS_OS_NAME -eq "osx") {
$IP1 = [System.Net.IPAddress]::Parse("1.1.1.1")
$IP2 = [System.Net.IPAddress]::Parse("4fde:0000:0000:0002:0022:f376:255.59.171.63")
$IPs = New-Object System.Collections.ArrayList
$IPs.Add($IP1)
$IPs.Add($IP2)
$info = @{}
$info.test = $IPs
$result = $info|Format-Table|Out-String
$result | Should Match "test\s+{1.1.1.1, 4fde::2:22:f376:ff3b:ab3f}"
}
It "Format-Table with Autosize should work" -Pending:($env:TRAVIS_OS_NAME -eq "osx") {
$IP1 = [PSCustomObject]@{'name'='Bob';'size'=1234;'booleanValue'=$true;}
$IP2 = [PSCustomObject]@{'name'='Jim';'size'=5678;'booleanValue'=$false;}
$IPs = New-Object System.Collections.ArrayList
$IPs.Add($IP1)
$IPs.Add($IP2)
$result = $IPs|Format-Table -Autosize|Out-String
$result | Should Match "name size booleanValue"
$result | Should Match "---- ---- ------------"
$result | Should Match "Bob\s+1234\s+True"
$result | Should Match "Jim\s+5678\s+False"
}
It "Format-Table with No Objects for End-To-End should work"{
$p = @{}
$result = $p|Format-Table -Property "foo","bar"|Out-String
$result | Should BeNullOrEmpty
}
It "Format-Table with Null Objects for End-To-End should work"{
$p = $null
$result = $p|Format-Table -Property "foo","bar"|Out-String
$result | Should BeNullOrEmpty
}
#pending on issue#900
It "Format-Table with single line string for End-To-End should work" -pending{
$p = "single line string"
$result = $p|Format-Table -Property "foo","bar"|Out-String
$result | Should BeNullOrEmpty
}
#pending on issue#900
It "Format-Table with multiple line string for End-To-End should work" -pending{
$p = "Line1\nLine2"
$result = $p|Format-Table -Property "foo","bar"|Out-String
$result | Should BeNullOrEmpty
}
#pending on issue#900
It "Format-Table with string sequence for End-To-End should work" -pending{
$p = "Line1","Line2"
$result = $p|Format-Table -Property "foo","bar"|Out-String
$result | Should BeNullOrEmpty
}
#pending on issue#900
It "Format-Table with string sequence for End-To-End should work" -pending{
$p = "Line1","Line2"
$result = $p|Format-Table -Property "foo","bar"|Out-String
$result | Should BeNullOrEmpty
}
It "Format-Table with complex object for End-To-End should work" -Pending:($env:TRAVIS_OS_NAME -eq "osx") {
Add-Type -TypeDefinition "public enum MyDayOfWeek{Sun,Mon,Tue,Wed,Thr,Fri,Sat}"
$eto = New-Object MyDayOfWeek
$info = @{}
$info.intArray = 1,2,3,4
$info.arrayList = "string1","string2"
$info.enumerable = [MyDayOfWeek]$eto
$info.enumerableTestObject = $eto
$result = $info|Format-Table|Out-String
$result | Should Match "intArray\s+{1, 2, 3, 4}"
$result | Should Match "arrayList\s+{string1, string2}"
$result | Should Match "enumerable\s+Sun"
$result | Should Match "enumerableTestObject\s+Sun"
}
It "Format-Table with Expand Enumerable should work" -Pending:($env:TRAVIS_OS_NAME -eq "osx") {
$obj1 = "x 0","y 0"
$obj2 = "x 1","y 1"
$objs = New-Object System.Collections.ArrayList
$objs.Add($obj1)
$objs.Add($obj2)
$mo = [PSCustomObject]@{name = "this is name";sub = $objs}
$result1 = $mo|Format-Table -Expand CoreOnly|Out-String
$result1 | Should Match "name\s+sub"
$result1 | Should Match "this is name"
$result2 = $mo|Format-Table -Expand EnumOnly|Out-String
$result2 | Should Match "name\s+sub"
$result2 | Should Match "this is name\s+{x 0 y 0, x 1 y 1}"
$result3 = $mo|Format-Table -Expand Both|Out-String
$result3 | Should Match "name\s+sub"
$result3 | Should Match "this is name\s+{x 0 y 0, x 1 y 1}"
}
}

View File

@ -40,7 +40,7 @@ Describe "Format-Wide" {
}
Describe "Format-Wide DRT basic functionality" -Tags DRT{
It "Format-Wide with array should work"{
It "Format-Wide with array should work" -Pending:($env:TRAVIS_OS_NAME -eq "osx") {
$al = (0..255)
$info = @{}
$info.array = $al
@ -80,7 +80,7 @@ Describe "Format-Wide DRT basic functionality" -Tags DRT{
$result | Should Match "Line2"
}
It "Format-Wide with complex object for End-To-End should work"{
It "Format-Wide with complex object for End-To-End should work" -Pending:($env:TRAVIS_OS_NAME -eq "osx") {
Add-Type -TypeDefinition "public enum MyDayOfWeek{Sun,Mon,Tue,Wed,Thr,Fri,Sat}"
$eto = New-Object MyDayOfWeek
$info = @{}
@ -94,4 +94,4 @@ Describe "Format-Wide DRT basic functionality" -Tags DRT{
$result | Should Match "enumerable"
$result | Should Match "enumerableTestObject"
}
}
}

View File

@ -12,7 +12,7 @@ Describe "Get-Date DRT Unit Tests" -Tags DRT {
$result.Millisecond | Should be 200
}
It "using -displayhint produces the correct output" {
It "using -displayhint produces the correct output" -Pending:($env:TRAVIS_OS_NAME -eq "osx") {
$d = Get-date -Date:"Jan 1, 2020" -DisplayHint Date | Out-String
$d.Trim() | Should be "Wednesday, January 1, 2020"
}

View File

@ -21,7 +21,8 @@ Describe "Import-Alias DRT Unit Tests" -Tags DRT{
Remove-Item -Path $testAliasDirectory -Recurse -Force
}
It "Import-Alias Resolve To Multiple will throw PSInvalidOperationException" {
It "Import-Alias Resolve To Multiple will throw PSInvalidOperationException" {
$ErrorActionPreference = "Stop"
try {
Import-Alias *
Throw "Execution OK"
@ -31,7 +32,8 @@ Describe "Import-Alias DRT Unit Tests" -Tags DRT{
}
}
It "Import-Alias From Exported Alias File Aliases Already Exist should throw SessionStateException skip now as bug#777" -Skip:$true{
It "Import-Alias From Exported Alias File Aliases Already Exist should throw SessionStateException" {
$ErrorActionPreference = "Stop"
{Export-Alias $fulltestpath abcd*}| Should Not Throw
try {
Import-Alias $fulltestpath

View File

@ -52,7 +52,7 @@ Describe "Out-File" {
{ Out-File -FilePath $testfile -InputObject $inObject } | Should Not Throw
}
It "Should not overwrite when the noclobber switch is used" {
It "Should not overwrite when the noclobber switch is used" -Pending:($env:TRAVIS_OS_NAME -eq "osx") {
Out-File -FilePath $testfile -InputObject $inObject
@ -67,7 +67,7 @@ Describe "Out-File" {
$actual[3] | Should Match "some test text"
}
It "Should Append a new line when the append switch is used" {
It "Should Append a new line when the append switch is used" -Pending:($env:TRAVIS_OS_NAME -eq "osx") {
{ Out-File -FilePath $testfile -InputObject $inObject } | Should Not Throw
{ Out-File -FilePath $testfile -InputObject $inObject -Append } | Should Not Throw
@ -101,7 +101,7 @@ Describe "Out-File" {
}
It "Should allow the cmdlet to overwrite an existing read-only file" {
It "Should allow the cmdlet to overwrite an existing read-only file" -Pending:($env:TRAVIS_OS_NAME -eq "osx") {
# create a read-only text file
{ Out-File -FilePath $testfile -InputObject $inObject } | Should Not Throw
Set-ItemProperty -Path $testfile -Name IsReadOnly -Value $true

View File

@ -1,6 +1,6 @@
Describe "Out-String DRT Unit Tests" -Tags DRT{
It "check display of properties with names containing wildcard characters" {
It "check display of properties with names containing wildcard characters" -Pending:($env:TRAVIS_OS_NAME -eq "osx") {
$results = new-object psobject | add-member -passthru noteproperty 'name with square brackets: [0]' 'myvalue' | out-string
$results.Length | Should BeGreaterThan 1
$results.GetType() | Should Be string

View File

@ -86,12 +86,12 @@ set-psbreakpoint -command foo
Remove-PSBreakPoint -Id $brk.Id
}
It "Should throw Exception when missing mandatory parameter -line"{
It "Should throw Exception when missing mandatory parameter -line" -Pending {
$output = & $ps -noninteractive -command "sbp -column 1 -script $scriptFileName"
[system.string]::Join(" ", $output) | Should Match "MissingMandatoryParameter,Microsoft.PowerShell.Commands.SetPSBreakpointCommand"
}
It "Should throw Exception when missing mandatory parameter" {
It "Should throw Exception when missing mandatory parameter" -Pending {
$output = & $ps -noprofile -noninteractive -command "sbp -line 1"
[system.string]::Join(" ", $output) | Should Match "MissingMandatoryParameter,Microsoft.PowerShell.Commands.SetPSBreakpointCommand"
}

View File

@ -1,31 +1,17 @@
Describe "Set-PSDebug" {
# Because it is running through pester, no functions need to be called. Pester should provide plenty
# of output.
It "Should be able to be called without error" {
{ Set-PSDebug -Trace 0 } | Should Not Throw
Context "Tracing can be used" {
AfterEach {
Set-PSDebug -Off
}
It "Should be able to go through the tracing options" -Skip:($env:APPVEYOR -eq "TRUE") {
{ Set-PSDebug -Trace 0 } | Should Not Throw
{ Set-PSDebug -Trace 1 } | Should Not Throw
{ Set-PSDebug -Trace 2 } | Should Not Throw
}
It "Should be able to set strict" -Skip:($env:APPVEYOR -eq "TRUE") {
{ Set-PSDebug -Strict } | Should Not Throw
}
}
It "Should be able to be turned off without error" {
{ Set-PSDebug -Off } | Should Not Throw
}
Context "Validate functionality" {
BeforeEach {
Set-PSDebug -Off
}
It "Should be able to go through the tracing options" {
{ Set-PSDebug -Trace 0 } | Should Not Throw
{ Set-PSDebug -Trace 1 } | Should Not Throw
{ Set-PSDebug -Trace 2 } | Should Not Throw
}
It "Should be able to set strict" {
{ Set-PSDebug -Strict } | Should Not Throw
}
}
# final cleanup
Set-PSDebug -Off
}

View File

@ -208,7 +208,7 @@ Describe "Set-Variable" {
$testVar | Should Be $testValue
}
It "Should be able to pipe object properties to output using the PassThru switch" {
It "Should be able to pipe object properties to output using the PassThru switch" -Pending:($env:TRAVIS_OS_NAME -eq "osx") {
$in = Set-Variable -Name testVar -Value "test" -Description "test description" -PassThru
$output = $in | Format-List -Property Description | Out-String

View File

@ -30,8 +30,8 @@ Describe "Write-Error DRT Unit Tests" -Tags DRT{
$Error[0].InvocationInfo.MyCommand.Name | Should BeNullOrEmpty
}
#Skip with issue #846
It "Should be works with all parameters" -Skip:$true {
# Skip with issue #846
It "Should be works with all parameters" -Pending {
$exception = New-Object -TypeName System.ArgumentNullException -ArgumentList paramname
Write-Error -Message myerrortext -Exception $exception -ErrorId myerrorid -Category syntaxerror -TargetObject TargetObject -CategoryActivity myactivity -CategoryReason myreason -CategoryTargetName mytargetname -CategoryTargetType mytargettype -RecommendedAction myrecommendedaction -ErrorAction SilentlyContinue
$Error[0] | Should Not BeNullOrEmpty
@ -104,4 +104,4 @@ Describe "Write-Error" {
$error[0]| Should Be $theError
}
}
}

View File

@ -35,11 +35,11 @@
}
}
It "all mandatory params works" {
It "all mandatory params works" -Pending {
{ write-progress -activity 'myactivity' -status 'mystatus' } | Should Not Throw
}
It "all params works" {
It "all params works" -Pending {
{ write-progress -activity 'myactivity' -status 'mystatus' -id 1 -parentId 2 -completed:$false -current 'current' -sec 1 -percent 1 } | Should Not Throw
}
}
}

View File

@ -5,67 +5,67 @@ Describe "Stream writer tests" {
# that would normally
function Write-Messages
{
[CmdletBinding()]
[CmdletBinding()]
param()
If ($PSBoundParameters['Debug']) { $DebugPreference = 'Continue' }
Write-Verbose "Verbose message"
param()
If ($PSBoundParameters['Debug']) { $DebugPreference = 'Continue' }
Write-Verbose "Verbose message"
Write-Debug "Debug message"
Write-Debug "Debug message"
}
function Get-OutputResults
{
# Get the contents of the targetfile.
# Make the array a string for less brittle testing
$output = $(Get-Content $args[0])
[String]::Join([Environment]::NewLine, $output )
# Get the contents of the targetfile.
# Make the array a string for less brittle testing
$output = $(Get-Content $args[0])
[String]::Join([Environment]::NewLine, $output )
return $output
return $output
}
Context "Redirect Stream Tests" {
# These tests validate that a stream is actually being written to by redirecting the output of that stream
# These tests validate that a stream is actually being written to by redirecting the output of that stream
AfterEach { Remove-Item $targetfile }
It "Should write warnings to the warning stream" {
Write-Warning "Test Warning" 3>&1 > $targetfile
AfterEach { Remove-Item $targetfile }
It "Should write warnings to the warning stream" -Pending:($env:TRAVIS_OS_NAME -eq "osx") {
Write-Warning "Test Warning" 3>&1 > $targetfile
Get-Content $targetfile | Should Be "Test Warning"
}
Get-Content $targetfile | Should Be "Test Warning"
}
It "Should write error messages to the error stream" {
Write-Error "Testing Error" 2>&1 > $targetfile
It "Should write error messages to the error stream" -Pending:($env:TRAVIS_OS_NAME -eq "osx") {
Write-Error "Testing Error" 2>&1 > $targetfile
$result = Get-OutputResults $targetfile
# The contents of the error stream should contain the expected text
$result -match ": Testing Error" | Should Be $true
}
$result = Get-OutputResults $targetfile
# The contents of the error stream should contain the expected text
$result -match ": Testing Error" | Should Be $true
}
It "Should write debug messages to the debug stream" {
Write-Messages -Debug -EA SilentlyContinue 5>&1 > $targetfile
It "Should write debug messages to the debug stream" -Pending:($env:TRAVIS_OS_NAME -eq "osx") {
Write-Messages -Debug -EA SilentlyContinue 5>&1 > $targetfile
$result = Get-OutputResults $targetfile
$result = Get-OutputResults $targetfile
# The contents of the debug stream should contain the expected text
$result -match "Debug Message" | Should Be $true
}
# The contents of the debug stream should contain the expected text
$result -match "Debug Message" | Should Be $true
}
It "Should write messages to the verbose stream" {
Write-Messages -Verbose 4>&1 > $targetfile
It "Should write messages to the verbose stream" -Pending:($env:TRAVIS_OS_NAME -eq "osx") {
Write-Messages -Verbose 4>&1 > $targetfile
$result = Get-OutputResults $targetfile
$result = Get-OutputResults $targetfile
# The contents of the debug stream should contain the expected text
$result -match "Verbose Message" | Should Be $true
}
# The contents of the debug stream should contain the expected text
$result -match "Verbose Message" | Should Be $true
}
}
Context "Error automatic variable" {
It "Should write error messages to the `$Error automatic variable" {
Write-Error "Test Error Message" -ErrorAction SilentlyContinue
It "Should write error messages to the `$Error automatic variable" {
Write-Error "Test Error Message" -ErrorAction SilentlyContinue
$Error[0] | Should Match "Test Error Message"
}
$Error[0] | Should Match "Test Error Message"
}
}
}