Merge remote-tracking branch 'refs/remotes/PowerShell/master'

This commit is contained in:
Aleksandar Nikolic 2016-07-17 00:05:35 +02:00
commit 5aefd5616a
51 changed files with 1622 additions and 1023 deletions

View file

@ -57,16 +57,39 @@ output will *not* have a runtime identifier in the path.
Thus the output location of `powershell.exe` will be
`./src/Microsoft.PowerShell.ConsoleHost/bin/Debug/net451/powershell.exe`
While building is easy, running FullCLR version is not as simple as
CoreCLR version.
Build manually
==============
The build contains the following steps:
- generating Visual Studio project: `cmake`
- building `powershell.exe` from generated solution: `msbuild
powershell.sln`
- building managed DLLs: `dotnet publish --runtime net451`
What I can do with the produced binaries?
=========================================
Creating a deployable package out of them is **not a supported scenario**.
The reason why we are building these binaries is
we have components (i.e. workflows) that are not currently available in the CoreClr version.
We want to make sure that CoreClr PowerShell changes don't introduce regressions in FullClr PowerShelll.
It's possible to run (for test purposes) the dev version of these binaries as follows.
Running Dev version of FullClr PowerShell
-----------------------------------------
Running FullCLR version is not as simple as CoreCLR version.
If you just run `./powershell.exe`, you will get a `powershell`
process, but all the interesting DLLs (such as
`System.Management.Automation.dll`) would be loaded from the Global
Assembly Cache (GAC), not your output directory.
[@lzybkr](https://github.com/lzybkr) wrote a module to deal with it
and run side-by-side.
Use `Start-DevPowerShell` helper funciton, to workaround it with `$env:DEVPATH`
```powershell
Start-DevPowerShell
@ -88,12 +111,3 @@ Start-DevPowerShell -binDir (Split-Path -Parent (Get-PSOutput))
The default for produced `powershell.exe` is x64.
You can control it with `Start-PSBuild -FullCLR -NativeHostArch x86`
Build manually
==============
The build contains the following steps:
- generating Visual Studio project: `cmake`
- building `powershell.exe` from generated solution: `msbuild
powershell.sln`
- building managed DLLs: `dotnet publish --runtime net451`

View file

@ -1,7 +1,7 @@
Debugging in PowerShell Command-line
=====
As we know we can debug PowerShell code via GUI tools like [VS Code](./using-vscode.md#debugging-with-vs-code) or [ISE](./using-ise.md#debugging-with-ise). In addition we can directly perform debugging within the PowerShell command-line session by using the PowerShell debugger cmdlets. This document demonstrates how to use the cmdlets for the PowerShell command-line debugging. We will cover two topics: set a debug breakpoint on a line of code and on a variable.
As we know, we can debug PowerShell code via GUI tools like [VS Code](./using-vscode.md#debugging-with-vs-code) or [ISE](./using-ise.md#debugging-with-ise). In addition, we can directly perform debugging within the PowerShell command-line session by using the PowerShell debugger cmdlets. This document demonstrates how to use the cmdlets for the PowerShell command-line debugging. We will cover the following topics: setting a debug breakpoint on a line of code and on a variable.
Let's use the following code snippet as our sample script.
@ -21,17 +21,17 @@ Write-Host "$result Celsius"
```
**1. Set a Breakpoint on a Line**
**1. Setting a Breakpoint on a Line**
- Open a [PowerShell editor](learning-powershell.md#powershell-editor)
- Save the above code snippet to a file, let's say "test.ps1"
- Save the above code snippet to a file. For example, "test.ps1"
- Go to your command-line PowerShell
- Clear existing breakpoints if any
```PowerShell
PS /home/jen/debug>Get-PSBreakpoint | Remove-PSBreakpoint
```
- Use **Set-PSBreakpoint** cmdlet to set a debug breakpoint, say set it to line 5
- Use **Set-PSBreakpoint** cmdlet to set a debug breakpoint. In this case, we will set it to line 5
```PowerShell
PS /home/jen/debug>Set-PSBreakpoint -Line 5 -Script ./test.ps1
@ -40,7 +40,7 @@ ID Script Line Command Variable Action
-- ------ ---- ------- -------- ------
0 test.ps1 5
```
- Run the script, test.ps1. As we have set a breakpoint, it is expected the program will break into the debugger at the line 5.
- Run the script "test.ps1". As we have set a breakpoint, it is expected the program will break into the debugger at the line 5.
```PowerShell
@ -54,10 +54,10 @@ At /home/jen/debug/test.ps1:5 char:1
[DBG]: PS /home/jen/debug>>
```
- The PowerShell prompt has been changed to **[DBG]: PS /home/jen/debug>>** as you may noticed. This means
we have entered into the debug mode. To watch the variables like $celsius, simply type $celsius as below.
- To exit from the debugging, type **"q"**
- To get help for the debugging commands, simple type **"?"**
- The PowerShell prompt now has the prefix **[DBG]:** as you may noticed. This means
we have entered into the debug mode. To watch the variables like $celsius, simply type **$celsius** as below.
- To exit from the debugging, type **q**
- To get help for the debugging commands, simply type **?**. The following is an example of debugging output.
```PowerShell
[DBG]: PS /home/jen/debug>> $celsius
@ -103,13 +103,13 @@ PS /home/jen/debug>
```
**2. Set a Breakpoint on a Variable**
- Clear existing breakpoints if any
**2. Setting a Breakpoint on a Variable**
- Clear existing breakpoints if there are any
```PowerShell
PS /home/jen/debug>Get-PSBreakpoint | Remove-PSBreakpoint
```
- Use **Set-PSBreakpoint** cmdlet to set a debug breakpoint, say set it to line 5
- Use **Set-PSBreakpoint** cmdlet to set a debug breakpoint. In this case, we set it to line 5
```PowerShell
@ -117,10 +117,10 @@ PS /home/jen/debug>
```
- Run the script, test.ps1.
- Run the script "test.ps1"
Once hit the debug breakpoint, we can type **l** to list the source code that debugger is currently executing. As we can see line 3 has an asterisk at the front, meaning that's the line the program is currently executing and broke into the debugger as illustrated below.
- Type **q** to exit from the debugging mode
- Type **q** to exit from the debugging mode. The following is an example of debugging output.
```PowerShell
@ -165,7 +165,7 @@ PS /home/jen/debug>
```
Now you know the basics of the PowerShell debugging from PowerShell command-line. For further learning read the following articles.
Now you know the basics of the PowerShell debugging from PowerShell command-line. For further learning, read the following articles.
More Reading

View file

@ -11,11 +11,18 @@ First you need to launch a PowerShell session by following the [Installing Power
Getting Familiar with PowerShell Commands
---
In this section, you will learn how to
- create a file, delete a file and change file directory
- find syntax of PowerShell cmdlets
- get help if you needed
- discover what version of PowerShell you are currently using
- exit a PowerShell session
- and more
As mentioned above PowerShell commands is designed to have Verb-Noun structure, for instance Get-Process, Set-Location, Clear-Host, etc. Lets exercise some of the basic PowerShell commands also known as **cmdlets**.
As mentioned above, PowerShell commands is designed to have Verb-Noun structure, for instance Get-Process, Set-Location, Clear-Host, etc. Lets exercise some of the basic PowerShell commands, also known as **cmdlets**.
Please note that we will use the PowerShell prompt sign **PS />** in the following examples as it shows on Linux.
It looks like **PS C:\>** on Windows.
Please note that we will use the PowerShell prompt sign **PS />** as it appears on Linux in the following examples.
It is shown as **PS C:\\>** on Windows.
**1. Get-Process**: displays the processes running on your system.
@ -88,7 +95,7 @@ PS /> cls
PS /> Set-Location /home
PS /home>
```
**5. ls or dir - Get-ChildItem**: list all items in the specified location
**5. dir - Get-ChildItem**: list all items in the specified location
```PowerShell
Get all files under the current directory:
@ -119,10 +126,10 @@ Mode LastWriteTime Length Name
---- ------------- ------ ----
-a---- 7/7/2016 7:17 PM 0 test.ps1
```
You can use the **-value** parameter to add some data to your file. For example, the following command adds the phrase "Write-Host 'Hello There'" as a file content to the test.ps1. Because the test.txt file exists already, we use **-force** parameter to replace the existing content.
You can use the **-Value** parameter to add some data to your file. For example, the following command adds the phrase "Write-Host 'Hello There'" as a file content to the test.ps1. Because the test.ps1 file exists already, we use **-Force** parameter to replace the existing content.
```PowerShell
PS /home/jen> New-Item -Path ./test.ps1 -Value "Write-Host 'hello there'" -force
PS /home/jen> New-Item -Path ./test.ps1 -Value "Write-Host 'hello there'" -Force
Directory: /home/jen
@ -132,7 +139,7 @@ Mode LastWriteTime Length Name
-a---- 7/7/2016 7:19 PM 24 test.ps1
```
There are other ways to add some data to a file, for example, you can use Set-Content to set the file contents:
There are other ways to add some data to a file. For example, you can use Set-Content to set the file contents:
```PowerShell
PS /home/jen>Set-Content -Path ./test.ps1 -Value "Write-Host 'hello there again!'"
@ -162,19 +169,34 @@ This cmdlet will delete the file /home/jen/test.ps1:
```PowerShell
PS /home/jen> Remove-Item ./test.ps1
```
**9. Exit**: - to exit the PowerShell session, type "exit"
**9. $PSVersionTable**: displays the version of PowerShell you are currently using
Type **$PSVersionTable** in your PowerShell session, you will see something like below. "PSVersion" indicates the
PowerShell version that you are using.
```PowerShell
Name Value
---- -----
PSVersion 5.1.10032.0
PSEdition Linux
PSCompatibleVersions {1.0, 2.0, 3.0, 4.0...}
BuildVersion 3.0.0.0
CLRVersion
WSManStackVersion 1.0
PSRemotingProtocolVersion 2.3
SerializationVersion 1.1.0.1
```
**10. Exit**: to exit the PowerShell session, type "exit"
```PowerShell
PS /home/jen> exit
```
Need Help?
----
The most important command in PowerShell is possibly the Get-Help, which allows you to quickly learn PowerShell without having to surfing around the Internet. The Get-Help cmdlet also shows you how PowerShell commands work with examples.
PS />**Get-Help**
You can use this cmdlet to get help with any PowerShell commands.
The most important command in PowerShell is possibly the Get-Help, which allows you to quickly learn PowerShell without having to surf around the Internet. The Get-Help cmdlet also shows you how PowerShell commands work with examples.
PS />**Get-Help -Name Get-Process**
@ -184,14 +206,14 @@ It shows the syntax and other technical information of the Get-Process cmdlet.
PS />**Get-Help -Name Get-Process -Examples**
It displays the examples how to use the Get-Process cmdlet.
If you use **-full** parameter, i.e., "Get-Help -Name Get-Process -Full", it will display more technical information.
If you use **-Full** parameter, i.e., "Get-Help -Name Get-Process -Full", it will display more technical information.
Discover All Commands Available on Your System
----
You want to discover what PowerShell cmdlets available on your system. Simple, just run "Get-Command" as below.
You want to discover what PowerShell cmdlets available on your system? Simple, just run "Get-Command" as below.
PS /> **Get-Command**
@ -203,14 +225,14 @@ If you want to know the syntax of Get-Process cmdlet, type
PS /> **Get-Command Get-Process -Syntax**
If you want to know how to sue the get-process, type
If you want to know how to use the Get-Process, type
PS /> **Get-Help Get-Process -example**
PS /> **Get-Help Get-Process -Example**
PowerShell Pipeline '|'
----
Sometimes when you run Get-ChildItem or "dir", you want to get a list of files in a descending order. To archive that, type:
Sometimes when you run Get-ChildItem or "dir", you want to get a list of files in a descending order. To achieve that, type:
```PowerShell
PS /home/jen> dir | sort -Descending
```
@ -229,10 +251,10 @@ Mode LastWriteTime Length Name
```
How to Create and Run PowerShell scripts
----
- You can use ISE, VS Code, or any favorite editor to create a PowerShell script and save the script with a .ps1 file extension (helloworld.ps1 in the example)
- To run the script, cd to your current folder and type ./helloworld.ps1
- You can use ISE, VS Code or your favorite editor to create a PowerShell script and save the script with a .ps1 file extension (for example, helloworld.ps1)
- To run the script, cd to your current folder and type ./yourscript.ps1 (for example, ./helloworld.ps1).
See [Running PowerShell Scripts Is as Easy as 1-2-3] [run-ps] for more details.
Note: if you are using Windows, make sure you set the PowerShell's execution policy to "RemoteSigned" in this case. See [Running PowerShell Scripts Is as Easy as 1-2-3] [run-ps] for more details.
[run-ps]:http://windowsitpro.com/powershell/running-powershell-scripts-easy-1-2-3

View file

@ -1,14 +1,14 @@
Using PowerShell Integrated Scripting Environment (ISE)
====
The PowerShell ISE works on Windows. If you are not using Windows, please see [Using VS Code](./using-vscode.md).
The PowerShell ISE only works on Windows. If you are not using Windows, please see [Using VS Code](./using-vscode.md).
Editing with ISE
---
- Launch PowerShell ISE
* Press Windows Key -> type PowerShell ISE, click on PowerShell ISE to launch the ISE
* Press Windows Key -> type "PowerShell ISE", click on PowerShell ISE to launch the ISE
- Create a new PowerShell Script
* Click on File -> New
* Add a few lines of PowerShell scripts in your newly created file, for example,
* Click on **File->New**
* Add a few lines of PowerShell code in your newly created file. In this case, we will use the following script snippet.
```PowerShell
# Convert Fahrenheit to Celsius
@ -25,21 +25,21 @@ Write-Host "$result Celsius"
```
* **Note**: You can find more examples [here](http://examples.oreilly.com/9780596528492/).
- Save the script file
* Click on File-> Save As -> type "helloworld.ps1"
- Close the helloworld.ps1
* File-> Close
- Reopen the helloworld.ps1
* File-> Open, then choose helloworld.ps1.
- To save the script file
* Click on **File->Save As**. Then type "helloworld.ps1"
- To close the helloworld.ps1 file
* **File->Close**
- To reopen the helloworld.ps1 file
* **File->Open**, then choose helloworld.ps1
- For more details, go to [How to Write and Run Scripts in the Windows PowerShell ISE](https://msdn.microsoft.com/en-us/powershell/scripting/core-powershell/ise/how-to-write-and-run-scripts-in-the-windows-powershell-ise).
Debugging with ISE
----
To execute the entire script file, you can press **F5**; to execute several lines of your scripts, simply select them and press **F8**. However sometimes you would like to stop the execution on a particular line in order to exam some variables to check if the program runs as expected. In that case, you may follow the steps below. Let's take the helloworld.ps1 as an example and assume line 17 is the place where you want to stop.
To execute the entire script file, you can press **F5**. To execute several lines of your scripts, simply select them and press **F8**. If you would like to stop the execution on a particular line in order to examine some variables to check if the program runs as expected. In that case, you may follow the steps below. Let's take the helloworld.ps1 as an example and assume line 17 is the place where you want to stop.
- Set a break point: Move mouse over on the line 17, and press **F9**. You will see the line 17 is highlighted which means a breakpoint gets set.
- Set a break point: Move mouse over on the line 17, and press **F9**. You will see the line 17 is highlighted, which means a breakpoint is set.
- Press **F5** to run the script
- Enter 80 (or any number in Fahrenheit) from the command line prompt
- Notice that the ISE output pane becomes “[DBG]: PS C:\Test>>” prompt. This means the program is in the debugging mode. It stops at Line 17:
@ -52,7 +52,7 @@ Hit Line breakpoint on 'C:\test\helloword.ps1:17'
```
- From the output pane, you can type $celsius and $fahrenheit to exam the values of these variables to see if they are correct.
- From the output pane, you can type $celsius and $fahrenheit to examine the values of these variables to see if they are correct.
```PowerShell
[DBG]: PS C:\Test>> $celsius

View file

@ -1,11 +1,11 @@
Using Visual Studio Code (VS Code)
====
If you are working on Linux and OS X, you cannot use ISE because it is not supported on these platforms. In this case you can choose your favorite editor to write PowerShell scripts. Here we choose VS Code as a PowerShell editor as an example.
If you are working on Linux and OS X, you cannot use ISE because it is not supported on these platforms. In this case, you can choose your favorite editor to write PowerShell scripts. Here we choose VS Code as a PowerShell editor as an example.
You can use VS Code on Windows with PowerShell V5 by using Windows 10 or by installing [Windows Management Framework 5.0 RTM](https://www.microsoft.com/en-us/download/details.aspx?id=50395) for down-level Windows OSs.
You can use VS Code on Windows with PowerShell V5 by using Windows 10 or by installing [Windows Management Framework 5.0 RTM](https://www.microsoft.com/en-us/download/details.aspx?id=50395) for down-level Windows OSs (e.g. Windows 8.1, etc.).
Before starting it, please make sure PowerShell exists on your system. Follow the [Installing PowerShell](./learning-powershell.md#installing-powershell) instruction you can install the PowerShell and launch the PowerShell session.
Before starting it, please make sure PowerShell exists on your system. By following the [Installing PowerShell](./learning-powershell.md#installing-powershell) instructions you can install PowerShell and launch a PowerShell session.
Editing with VS Code
----
@ -28,28 +28,28 @@ Editing with VS Code
- Press **F1** (or **Ctrl+Shift+P**) which opens up the “Command Palette” inside the VS Code app.
- In the command palette, type **ext install** and hit Enter. It will show all VS Code extensions available on your system.
- Choose PowerShell and click on Install, you will see something like below
- In the command palette, type **ext install** and hit **Enter**. It will show all VS Code extensions available on your system.
- Choose PowerShell and click on **Install**, you will see something like below
![VSCode](vscode.png)
- After the install, you will see the **Install** button turns to **Enable**.
- Click on Enable and OK
- Now you are ready for editing. for example, to create a new file, click File->New; to save it, click File->Save and then provide
a file name, let's say "helloworld.ps1"; to close the file, click on "x"; to exit the VS Code, File->Exit.
- Click on **Enable** and **OK**
- Now you are ready for editing. For example, to create a new file, click **File->New**. To save it, click **File->Save** and then provide
a file name, let's say "helloworld.ps1". To close the file, click on "x" next to the file name. To exit VS Code, **File->Exit**.
Debugging with VS Code
----
- Open a file folder (**File->Open Folder**) where contains the PowerShell modules or scripts you have written already and want to debug. In this example, we saved helloworld.ps1 under home/jen/debug. Thus we select the "debug" folder and open it in VS Code.
- Open a file folder (**File->Open Folder**) that contains the PowerShell modules or scripts you have written already and want to debug. In this example, we saved the helloworld.ps1 under a directory called "demo". Thus we select the "demo" folder and open it in VS Code.
- Creating the Debug Configuration (launch.json)
Because some information regarding your scripts is needed for debugger to start executing your script, we need to set up the debug config First. This is one time only to debug PowerShell scripts under your current folder.
Because some information regarding your scripts is needed for debugger to start executing your script, we need to set up the debug config first. This is one-time process to debug PowerShell scripts under your current folder. In our case, the "demo" folder.
* Click on the **Debug** icon (or **Ctrl+Shift+D**)
* Click on the **Settings** icon that looks like a gear. The VS Code will prompt you to **Select Environment**. Choose **PowerShell**. Then the VS code will auto create a debug configuration settings file in the same folder. It looks like the following:
* Click on the **Settings** icon that looks like a gear. VS Code will prompt you to **Select Environment**. Choose **PowerShell**. Then VS code will auto create a debug configuration settings file in the same folder. It looks like the following:
```json
{
"version": "0.2.0",
@ -73,9 +73,10 @@ Debugging with VS Code
]
}
```
- Once the debug configuration is established, now go to your helloworld.ps1 and set a breakpoint by pressing **F9** on a line you wish to debug break into.
- Once the debug configuration is established, go to your helloworld.ps1 and set a breakpoint by pressing **F9** on a line you wish to debug.
- To disable the breakpoint, press **F9** again.
- Press **F5** to let the run.
- Press **F5** to run the script. The execution should stop on the line you put the breakpoint on.
- Press **F5** to continue running the script.
There are a few blogs that may be helpful to get you started using PowerShell extension for VS Code

View file

@ -61,7 +61,8 @@
"Microsoft.PowerShell.Commands.Management": "1.0.0-*",
"Microsoft.PowerShell.Commands.Utility": "1.0.0-*",
"Microsoft.PowerShell.LocalAccounts": "1.0.0-*",
"Microsoft.PowerShell.PackageManagement": "1.0.0-*"
"Microsoft.PowerShell.PackageManagement": "1.0.0-*",
"Microsoft.Management.Infrastructure.CimCmdlets": "1.0.0-*"
},
"frameworks": {

View file

@ -92,7 +92,12 @@ namespace Microsoft.PowerShell
public const ConsoleColor DefaultEmphasisForegroundColor = ConsoleColor.Cyan;
public const ConsoleColor DefaultErrorForegroundColor = ConsoleColor.Red;
public const EditMode DefaultEditMode = EditMode.Windows;
public const EditMode DefaultEditMode =
#if LINUX
EditMode.Emacs;
#else
EditMode.Windows;
#endif
public const string DefaultContinuationPrompt = ">> ";

View file

@ -114,6 +114,25 @@ namespace Microsoft.PowerShell
private Dictionary<ConsoleKeyInfo, KeyHandler> _dispatchTable;
private Dictionary<ConsoleKeyInfo, Dictionary<ConsoleKeyInfo, KeyHandler>> _chordDispatchTable;
/// <summary>
/// Helper to set bindings based on EditMode
/// </summary>
void SetDefaultBindings(EditMode editMode)
{
switch (editMode)
{
case EditMode.Emacs:
SetDefaultEmacsBindings();
break;
case EditMode.Vi:
SetDefaultViBindings();
break;
case EditMode.Windows:
SetDefaultWindowsBindings();
break;
}
}
void SetDefaultWindowsBindings()
{
_dispatchTable = new Dictionary<ConsoleKeyInfo, KeyHandler>(new ConsoleKeyInfoComparer())

View file

@ -96,18 +96,7 @@ namespace Microsoft.PowerShell
// Switching/resetting modes - clear out chord dispatch table
_chordDispatchTable.Clear();
switch (options._editMode)
{
case EditMode.Emacs:
SetDefaultEmacsBindings();
break;
case EditMode.Vi:
SetDefaultViBindings();
break;
case EditMode.Windows:
SetDefaultWindowsBindings();
break;
}
SetDefaultBindings(Options.EditMode);
}
if (options._showToolTips.HasValue)
{

View file

@ -485,8 +485,6 @@ namespace Microsoft.PowerShell
_console = new ConhostConsole();
#endif
SetDefaultWindowsBindings();
_buffer = new StringBuilder(8 * 1024);
_statusBuffer = new StringBuilder(256);
_savedCurrentLine = new HistoryItem();
@ -516,6 +514,7 @@ namespace Microsoft.PowerShell
hostName = "PSReadline";
}
_options = new PSConsoleReadlineOptions(hostName);
SetDefaultBindings(_options.EditMode);
}
private void Initialize(Runspace runspace, EngineIntrinsics engineIntrinsics)

View file

@ -893,13 +893,10 @@ namespace System.Management.Automation
return GetEnvironmentVariables();
}
if (!Platform.HasRegistrySupport())
{
// Porting note: Does not throw a PlatformUnsupported because
// GetEnvironmentVariable cannot throw, and we want the same interface.
return null;
}
#if LINUX
return null;
#else
if( target == EnvironmentVariableTarget.Machine)
{
using (RegistryKey environmentKey =
@ -916,6 +913,7 @@ namespace System.Management.Automation
return GetRegistryKeyNameValuePairs(environmentKey);
}
}
#endif
}
/// <summary>
@ -956,13 +954,9 @@ namespace System.Management.Automation
return System.Environment.GetEnvironmentVariable(variable);
}
if (!Platform.HasRegistrySupport())
{
// Porting note: This cannot throw since it would otherwise throw while
// creating a runspace. Returning null is appropriate to signal that there
// is no value for a registry key with no registry.
return null;
}
#if LINUX
return null;
#else
if (target == EnvironmentVariableTarget.Machine)
{
@ -985,6 +979,7 @@ namespace System.Management.Automation
return value;
}
}
#endif
}
#endregion EnvironmentVariable_Extensions

View file

@ -143,12 +143,12 @@ namespace System.Management.Automation
//the user has set XDG_DATA_HOME corresponding to module path
if (String.IsNullOrEmpty(xdgdatahome)){
// create the xdg folder if needed
if (!Directory.Exists(xdgDataHomeDefault))
{
Directory.CreateDirectory(xdgDataHomeDefault);
}
return xdgDataHomeDefault;
// create the xdg folder if needed
if (!Directory.Exists(xdgDataHomeDefault))
{
Directory.CreateDirectory(xdgDataHomeDefault);
}
return xdgDataHomeDefault;
}
else
{
@ -159,12 +159,12 @@ namespace System.Management.Automation
//the user has set XDG_DATA_HOME corresponding to module path
if (String.IsNullOrEmpty(xdgdatahome)){
//xdg values have not been set
if (!Directory.Exists(xdgModuleDefault)) //module folder not always guaranteed to exist
{
Directory.CreateDirectory(xdgModuleDefault);
}
return xdgModuleDefault;
//xdg values have not been set
if (!Directory.Exists(xdgModuleDefault)) //module folder not always guaranteed to exist
{
Directory.CreateDirectory(xdgModuleDefault);
}
return xdgModuleDefault;
}
else
{
@ -175,7 +175,7 @@ namespace System.Management.Automation
//the user has set XDG_CACHE_HOME
if (String.IsNullOrEmpty(xdgcachehome))
{
//xdg values have not been set
//xdg values have not been set
if (!Directory.Exists(xdgCacheDefault)) //module folder not always guaranteed to exist
{
Directory.CreateDirectory(xdgCacheDefault);
@ -217,90 +217,6 @@ namespace System.Management.Automation
}
// ComObjectType is null on CoreCLR for Linux since there is
// no COM support on Linux
internal static bool HasCom()
{
// TODO: catch exception from Type.IsComObject
return IsWindows;
}
// The Antimalware Scan Interface is not supported on Linux
internal static bool HasAmsi()
{
return IsWindows;
}
// This is mainly with respect to the auto-mounting of
// disconnected network drives on Windows
internal static bool HasDriveAutoMounting()
{
return IsWindows;
}
// Linux does not have a registry
internal static bool HasRegistrySupport()
{
return IsWindows;
}
// Linux does not have PowerShell execution policies
internal static bool HasExecutionPolicy()
{
return IsWindows;
}
// Linux has a single rooted file system
internal static bool HasSingleRootFilesystem()
{
return !IsWindows;
}
// Linux has no notion of file shares. It has mount points
// instead, which are subdirectories of its single-root
// filesystem.
internal static bool HasFileShares()
{
return !HasSingleRootFilesystem();
}
// Linux has no support for UNC, just mounts in a single rooted hierarchy
// the UNC equivalent of a "network drive" aka mount would be the mount itself
internal static bool HasUNCSupport()
{
return IsWindows;
}
// Linux uses .net to query file attributes
internal static bool UseDotNetToQueryFileAttributes()
{
return true;
}
// Linux does not have group policy support
internal static bool HasGroupPolicySupport()
{
return IsWindows;
}
// non-windows does not have network drive support
internal static bool HasNetworkDriveSupport()
{
return IsWindows;
}
// non-windows does not have reparse points
internal static bool SupportsReparsePoints()
{
return IsWindows;
}
// non-windows does not support removing drives
internal static bool SupportsRemoveDrive()
{
return IsWindows;
}
// Platform methods prefixed NonWindows are:
// - non-windows by the definition of the IsWindows method above
// - here, because porting to Linux and other operating systems
@ -313,27 +229,17 @@ namespace System.Management.Automation
internal static bool NonWindowsIsHardLink(ref IntPtr handle)
{
return LinuxPlatform.IsHardLink(ref handle);
return Unix.IsHardLink(ref handle);
}
internal static bool NonWindowsIsHardLink(FileSystemInfo fileInfo)
{
if (!IsWindows)
{
return LinuxPlatform.IsHardLink(fileInfo);
}
throw new PlatformNotSupportedException();
return Unix.IsHardLink(fileInfo);
}
internal static bool NonWindowsIsSymLink(FileSystemInfo fileInfo)
{
if (!IsWindows)
{
return LinuxPlatform.IsSymLink(fileInfo);
}
throw new PlatformNotSupportedException();
return Unix.NativeMethods.IsSymLink(fileInfo.FullName);
}
internal static string NonWindowsInternalGetTarget(SafeFileHandle handle)
@ -344,166 +250,101 @@ namespace System.Management.Automation
internal static string NonWindowsInternalGetTarget(string path)
{
if (!IsWindows)
{
return LinuxPlatform.FollowSymLink(path);
}
else
{
throw new PlatformNotSupportedException();
}
return Unix.NativeMethods.FollowSymLink(path);
}
internal static string NonWindowsGetUserFromPid(int path)
{
if (!IsWindows)
{
return LinuxPlatform.GetUserFromPid(path);
}
else
{
throw new PlatformNotSupportedException();
}
return Unix.NativeMethods.GetUserFromPid(path);
}
#if CORECLR
#if CORECLR
internal static string NonWindowsGetFolderPath(SpecialFolder folder)
{
if (!IsWindows)
{
return LinuxPlatform.GetFolderPath(folder);
}
else
{
throw new PlatformNotSupportedException();
}
return Unix.GetFolderPath(folder);
}
#endif
#endif
internal static string NonWindowsInternalGetLinkType(FileSystemInfo fileInfo)
{
if (!IsWindows)
if (NonWindowsIsSymLink(fileInfo))
{
if (NonWindowsIsSymLink(fileInfo))
{
return "SymbolicLink";
}
if (NonWindowsIsHardLink(fileInfo))
{
return "HardLink";
}
return null;
}
else
{
throw new PlatformNotSupportedException();
return "SymbolicLink";
}
if (NonWindowsIsHardLink(fileInfo))
{
return "HardLink";
}
return null;
}
internal static bool NonWindowsCreateSymbolicLink(string path, string strTargetPath, bool isDirectory)
internal static bool NonWindowsCreateSymbolicLink(string path, string target)
{
if (!IsWindows)
{
// Linux doesn't care if target is a directory or not
return LinuxPlatform.CreateSymbolicLink(path, strTargetPath);
}
else
{
throw new PlatformNotSupportedException();
}
// Linux doesn't care if target is a directory or not
return Unix.NativeMethods.CreateSymLink(path, target);
}
internal static bool NonWindowsCreateHardLink(string path, string strTargetPath)
{
if (!IsWindows)
{
return LinuxPlatform.CreateHardLink(path, strTargetPath);
}
else
{
throw new PlatformNotSupportedException();
}
return Unix.CreateHardLink(path, strTargetPath);
}
internal static void NonWindowsSetDate(DateTime dateToUse)
{
if (!IsWindows)
{
LinuxPlatform.SetDateInfoInternal date = new LinuxPlatform.SetDateInfoInternal(dateToUse);
LinuxPlatform.SetDate(date);
}
else
{
throw new PlatformNotSupportedException();
}
Unix.SetDateInfoInternal date = new Unix.SetDateInfoInternal(dateToUse);
Unix.SetDate(date);
}
internal static string NonWindowsGetDomainName()
{
if (!IsWindows)
string fullyQualifiedName = Unix.NativeMethods.GetFullyQualifiedName();
if (string.IsNullOrEmpty(fullyQualifiedName))
{
string fullyQualifiedName = LinuxPlatform.Native.GetFullyQualifiedName();
if (string.IsNullOrEmpty(fullyQualifiedName))
{
int lastError = Marshal.GetLastWin32Error();
throw new InvalidOperationException("LinuxPlatform.NonWindowsGetDomainName error: " + lastError);
}
int index = fullyQualifiedName.IndexOf('.');
if (index >= 0)
{
return fullyQualifiedName.Substring(index + 1);
}
return "";
int lastError = Marshal.GetLastWin32Error();
throw new InvalidOperationException("Unix.NonWindowsGetDomainName error: " + lastError);
}
else
int index = fullyQualifiedName.IndexOf('.');
if (index >= 0)
{
throw new PlatformNotSupportedException();
return fullyQualifiedName.Substring(index + 1);
}
return "";
}
internal static string NonWindowsGetUserName()
{
if (!IsWindows)
{
return LinuxPlatform.UserName;
}
else
{
throw new PlatformNotSupportedException();
}
return Unix.UserName;
}
// Hostname in this context seems to be the FQDN
internal static string NonWindowsGetHostName()
{
if (!IsWindows)
string hostName = Unix.NativeMethods.GetFullyQualifiedName();
if (string.IsNullOrEmpty(hostName))
{
string hostName = LinuxPlatform.Native.GetFullyQualifiedName();
if (string.IsNullOrEmpty(hostName))
{
int lastError = Marshal.GetLastWin32Error();
throw new InvalidOperationException("LinuxPlatform.NonWindowsHostName error: " + lastError);
}
return hostName;
int lastError = Marshal.GetLastWin32Error();
throw new InvalidOperationException("Unix.NonWindowsHostName error: " + lastError);
}
return hostName;
}
}
else
{
throw new PlatformNotSupportedException();
}
internal static bool NonWindowsIsFile(string path)
{
return Unix.NativeMethods.IsFile(path);
}
internal static bool NonWindowsIsDirectory(string path)
{
return Unix.NativeMethods.IsDirectory(path);
}
internal static bool NonWindowsIsExecutable(string path)
{
if (!IsWindows)
{
return LinuxPlatform.IsExecutable(path);
}
throw new PlatformNotSupportedException();
return Unix.NativeMethods.IsExecutable(path);
}
internal static uint NonWindowsGetThreadId()
@ -512,15 +353,6 @@ namespace System.Management.Automation
return 0;
}
/// <summary>
/// This exception is meant to be thrown if a code path is not supported due
/// to platform restrictions
/// </summary>
internal class PlatformNotSupportedException : System.Exception
{
public PlatformNotSupportedException() : base() {}
}
/// <summary>
/// This models the native call CommandLineToArgvW in managed code.
/// </summary>
@ -581,7 +413,7 @@ namespace System.Management.Automation
}
}
internal static class LinuxPlatform
internal static class Unix
{
private static string _userName;
public static string UserName
@ -590,11 +422,11 @@ namespace System.Management.Automation
{
if (string.IsNullOrEmpty(_userName))
{
_userName = Native.GetUserName();
_userName = NativeMethods.GetUserName();
if (string.IsNullOrEmpty(_userName))
{
int lastError = Marshal.GetLastWin32Error();
throw new InvalidOperationException("LinuxPlatform.UserName error: " + lastError);
throw new InvalidOperationException("Unix.UserName error: " + lastError);
}
}
return _userName;
@ -620,40 +452,40 @@ namespace System.Management.Automation
}
}
#if CORECLR
#if CORECLR
public static string GetFolderPath(SpecialFolder folder)
{
string s = null;
switch (folder)
{
case SpecialFolder.ProgramFiles:
s = "/bin";
break;
case SpecialFolder.ProgramFilesX86:
s = "/usr/bin";
break;
case SpecialFolder.System:
s = "/sbin";
break;
case SpecialFolder.SystemX86:
s = "/sbin";
break;
case SpecialFolder.Personal:
s = System.Environment.GetEnvironmentVariable("HOME");
break;
case SpecialFolder.LocalApplicationData:
s = System.IO.Path.Combine(System.Environment.GetEnvironmentVariable("HOME"), ".config");
if (!System.IO.Directory.Exists(s))
{
System.IO.Directory.CreateDirectory(s);
}
break;
default:
throw new NotSupportedException();
case SpecialFolder.ProgramFiles:
s = "/bin";
break;
case SpecialFolder.ProgramFilesX86:
s = "/usr/bin";
break;
case SpecialFolder.System:
s = "/sbin";
break;
case SpecialFolder.SystemX86:
s = "/sbin";
break;
case SpecialFolder.Personal:
s = System.Environment.GetEnvironmentVariable("HOME");
break;
case SpecialFolder.LocalApplicationData:
s = System.IO.Path.Combine(System.Environment.GetEnvironmentVariable("HOME"), ".config");
if (!System.IO.Directory.Exists(s))
{
System.IO.Directory.CreateDirectory(s);
}
break;
default:
throw new NotSupportedException();
}
return s;
}
#endif
#endif
public static bool IsHardLink(ref IntPtr handle)
{
@ -671,7 +503,7 @@ namespace System.Management.Automation
int count;
string filePath = fs.FullName;
int ret = Native.GetLinkCount(filePath, out count);
int ret = NativeMethods.GetLinkCount(filePath, out count);
if (ret == 1)
{
return count > 1;
@ -679,84 +511,27 @@ namespace System.Management.Automation
else
{
int lastError = Marshal.GetLastWin32Error();
throw new InvalidOperationException("LinuxPlatform.IsHardLink error: " + lastError);
throw new InvalidOperationException("Unix.IsHardLink error: " + lastError);
}
}
public static bool IsSymLink(FileSystemInfo fs)
{
if (!fs.Exists)
{
return false;
}
string filePath = fs.FullName;
int ret = Native.IsSymLink(filePath);
switch(ret)
{
case 1:
return true;
case 0:
return false;
default:
int lastError = Marshal.GetLastWin32Error();
throw new InvalidOperationException("LinuxPlatform.IsSymLink error: " + lastError);
}
}
public static bool IsExecutable(string filePath)
{
if (!File.Exists(filePath))
{
return false;
}
int ret = Native.IsExecutable(filePath);
switch(ret)
{
case 1:
return true;
case 0:
return false;
default:
int lastError = Marshal.GetLastWin32Error();
throw new InvalidOperationException("LinuxPlatform.IsExecutable error: " + lastError);
}
}
public static void SetDate(SetDateInfoInternal info)
{
int ret = Native.SetDate(info);
int ret = NativeMethods.SetDate(info);
if (ret == -1)
{
int lastError = Marshal.GetLastWin32Error();
throw new InvalidOperationException("LinuxPlatform.NonWindowsSetDate error: " + lastError);
throw new InvalidOperationException("Unix.NonWindowsSetDate error: " + lastError);
}
}
public static bool CreateSymbolicLink(string path, string strTargetPath)
{
int ret = Native.CreateSymLink(path, strTargetPath);
return ret == 1 ? true : false;
}
public static bool CreateHardLink(string path, string strTargetPath)
{
int ret = Native.CreateHardLink(path, strTargetPath);
int ret = NativeMethods.CreateHardLink(path, strTargetPath);
return ret == 1 ? true : false;
}
public static string FollowSymLink(string path)
{
return Native.FollowSymLink(path);
}
public static string GetUserFromPid(int pid)
{
return Native.GetUserFromPid(pid);
}
[StructLayout(LayoutKind.Sequential)]
internal class SetDateInfoInternal
{
@ -788,12 +563,14 @@ namespace System.Management.Automation
}
}
internal static class Native
internal static class NativeMethods
{
const string psLib = "libpsl-native";
// Ansi is a misnomer, it is hardcoded to UTF-8 on Linux and OS X
// C bools are 1 byte and so must be marshaled as I1
[DllImport(psLib, CharSet = CharSet.Ansi, SetLastError = true)]
[return: MarshalAs(UnmanagedType.LPStr)]
internal static extern string GetUserName();
@ -802,10 +579,12 @@ namespace System.Management.Automation
internal static extern int GetLinkCount([MarshalAs(UnmanagedType.LPStr)]string filePath, out int linkCount);
[DllImport(psLib, CharSet = CharSet.Ansi, SetLastError = true)]
internal static extern int IsSymLink([MarshalAs(UnmanagedType.LPStr)]string filePath);
[return: MarshalAs(UnmanagedType.I1)]
internal static extern bool IsSymLink([MarshalAs(UnmanagedType.LPStr)]string filePath);
[DllImport(psLib, CharSet = CharSet.Ansi, SetLastError = true)]
internal static extern int IsExecutable([MarshalAs(UnmanagedType.LPStr)]string filePath);
[return: MarshalAs(UnmanagedType.I1)]
internal static extern bool IsExecutable([MarshalAs(UnmanagedType.LPStr)]string filePath);
[DllImport(psLib, CharSet = CharSet.Ansi, SetLastError = true)]
[return: MarshalAs(UnmanagedType.LPStr)]
@ -815,7 +594,8 @@ namespace System.Management.Automation
internal static extern int SetDate(SetDateInfoInternal info);
[DllImport(psLib, CharSet = CharSet.Ansi, SetLastError = true)]
internal static extern int CreateSymLink([MarshalAs(UnmanagedType.LPStr)]string filePath,
[return: MarshalAs(UnmanagedType.I1)]
internal static extern bool CreateSymLink([MarshalAs(UnmanagedType.LPStr)]string filePath,
[MarshalAs(UnmanagedType.LPStr)]string target);
[DllImport(psLib, CharSet = CharSet.Ansi, SetLastError = true)]
@ -829,6 +609,15 @@ namespace System.Management.Automation
[DllImport(psLib, CharSet = CharSet.Ansi, SetLastError = true)]
[return: MarshalAs(UnmanagedType.LPStr)]
internal static extern string GetUserFromPid(int pid);
[DllImport(psLib, CharSet = CharSet.Ansi, SetLastError = true)]
[return: MarshalAs(UnmanagedType.I1)]
internal static extern bool IsFile([MarshalAs(UnmanagedType.LPStr)]string filePath);
[DllImport(psLib, CharSet = CharSet.Ansi, SetLastError = true)]
[return: MarshalAs(UnmanagedType.I1)]
internal static extern bool IsDirectory([MarshalAs(UnmanagedType.LPStr)]string filePath);
}
}

View file

@ -4464,12 +4464,9 @@ namespace System.Management.Automation
internal static List<string> GetFileShares(string machine, bool ignoreHidden)
{
// Platform notes: don't throw an exception since this is being used in auto-completion
if (!Platform.HasFileShares())
{
return new List<string>();
}
#if LINUX
return new List<string>();
#else
IntPtr shBuf;
uint numEntries;
uint totalEntries;
@ -4495,6 +4492,7 @@ namespace System.Management.Automation
}
}
return shares;
#endif
}
private static bool CheckFileExtension(string path, HashSet<string> extension)

View file

@ -1903,13 +1903,9 @@ namespace System.Management.Automation
///
internal static string GetShellPathFromRegistry(string shellID)
{
if (!Platform.HasRegistrySupport())
{
return null;
}
string result = null;
#if !LINUX
try
{
RegistryKey shellKey = Registry.LocalMachine.OpenSubKey(Utils.GetRegistryConfigurationPath(shellID));
@ -1935,6 +1931,7 @@ namespace System.Management.Automation
catch (ArgumentException)
{
}
#endif
return result;
}

View file

@ -103,13 +103,9 @@ namespace System.Management.Automation
// Gets the current WSMan stack version from the registry.
private static Version GetWSManStackVersion()
{
if (!Platform.HasRegistrySupport())
{
return new Version(1, 0);
}
Version version = null;
#if !LINUX
try
{
using (RegistryKey wsManStackVersionKey = Registry.LocalMachine.OpenSubKey(@"Software\Microsoft\Windows\CurrentVersion\WSMAN"))
@ -133,6 +129,7 @@ namespace System.Management.Automation
catch (FormatException) { }
catch (OverflowException) { }
catch (InvalidCastException) { }
#endif
return version ?? System.Management.Automation.Remoting.Client.WSManNativeApi.WSMAN_STACK_VERSION;
}

View file

@ -1469,11 +1469,12 @@ namespace System.Management.Automation
bool driveIsValid = true;
// If the drive is auto-mounted, ensure that it still exists, or remove the drive.
// note: not all platforms support drive auto-mounting
if (Platform.HasDriveAutoMounting() && (drive.IsAutoMounted || IsAStaleVhdMountedDrive(drive)))
#if !LINUX
if (drive.IsAutoMounted || IsAStaleVhdMountedDrive(drive))
{
driveIsValid = ValidateOrRemoveAutoMountedDrive(drive, lookupScope);
}
#endif
if(drive.Name.Length == 1)
{
if(!(driveNames.Contains(drive.Name)))

View file

@ -62,15 +62,6 @@ namespace System.Management.Automation
return CombineHashCodes(CombineHashCodes(h1, h2, h3, h4), CombineHashCodes(h5, h6, h7, h8));
}
/// <summary>
/// IsWinPEHost indicates if the machine on which PowerShell is hosted is WinPE or not.
/// This is a helper variable used to kep track if the IsWinPE() helper method has
/// already checked for the WinPE specific registry key or not.
/// If the WinPE specific registry key has not yet been checked even
/// once then this variable will point to null.
/// </summary>
internal static bool? isWinPEHost = null;
/// <summary>
/// The existence of the following registry confirms that the host machine is a WinPE
/// HKLM\System\CurrentControlSet\Control\MiniNT
@ -368,58 +359,31 @@ namespace System.Management.Automation
/// </summary>
internal static bool IsWinPEHost()
{
if (Platform.HasRegistrySupport())
{
return WinIsWinPEHost();
}
else
{
return false;
}
}
internal static bool WinIsWinPEHost()
{
#if !LINUX
RegistryKey winPEKey = null;
if (!isWinPEHost.HasValue)
try
{
try
{
// The existence of the following registry confirms that the host machine is a WinPE
// HKLM\System\CurrentControlSet\Control\MiniNT
winPEKey = Registry.LocalMachine.OpenSubKey(WinPEIdentificationRegKey);
// The existence of the following registry confirms that the host machine is a WinPE
// HKLM\System\CurrentControlSet\Control\MiniNT
winPEKey = Registry.LocalMachine.OpenSubKey(WinPEIdentificationRegKey);
if (null != winPEKey)
{
isWinPEHost = true;
}
else
{
isWinPEHost = false;
}
}
catch (ArgumentException) { }
catch (SecurityException) { }
catch (ObjectDisposedException) { }
finally
return winPEKey != null;
}
catch (ArgumentException) { }
catch (SecurityException) { }
catch (ObjectDisposedException) { }
finally
{
if (winPEKey != null)
{
if (winPEKey != null)
{
winPEKey.Dispose();
}
winPEKey.Dispose();
}
}
#endif
return false;
}
if (isWinPEHost.HasValue)
{
return (bool)isWinPEHost;
}
else
{
return false;
}
}
#region Versioning related methods
@ -652,13 +616,9 @@ namespace System.Management.Automation
internal static Dictionary<string, object> GetGroupPolicySetting(string groupPolicyBase, string settingName, RegistryKey[] preferenceOrder)
{
if (!Platform.HasGroupPolicySupport())
{
// Porting note: No group policy support means we have an empty set of
// policies.
return _emptyDictionary;
}
#if LINUX
return _emptyDictionary;
#else
lock (cachedGroupPolicySettings)
{
// Return cached information, if we have it
@ -733,6 +693,7 @@ namespace System.Management.Automation
return settings;
}
#endif
}
static ConcurrentDictionary<string, Dictionary<string, object>> cachedGroupPolicySettings =
new ConcurrentDictionary<string, Dictionary<string, object>>();
@ -984,38 +945,9 @@ namespace System.Management.Automation
return NativeItemExists(path, out unusedIsDirectory, out unusedException);
}
// This is done through P/Invoke since File.Exists and Directory.Exists pay 13% performance degradation
// through the CAS checks, and are terribly slow for network paths.
internal static bool NativeItemExists(string path, out bool isDirectory, out Exception exception)
{
if (Platform.UseDotNetToQueryFileAttributes())
{
// TODO:PSL replace by System.IO.File.GetAttributes
exception = null;
if (NativeDirectoryExists(path))
{
isDirectory = true;
return true;
}
else if (NativeFileExists(path))
{
isDirectory = false;
return true;
}
else
{
isDirectory = false;
return false;
}
}
else
{
// This is done through P/Invoke since File.Exists and Directory.Exists
// pay 13% performance degradation through the CAS checks, and are
// terribly slow for network paths.
return WinNativeItemExists(path,out isDirectory,out exception);
}
}
internal static bool WinNativeItemExists(string path, out bool isDirectory, out Exception exception)
{
exception = null;
@ -1024,6 +956,10 @@ namespace System.Management.Automation
isDirectory = false;
return false;
}
#if LINUX
isDirectory = Platform.NonWindowsIsDirectory(path);
return Platform.NonWindowsIsFile(path);
#else
if (IsReservedDeviceName(path))
{
@ -1057,23 +993,12 @@ namespace System.Management.Automation
((int)NativeMethods.FileAttributes.Directory);
return true;
#endif
}
// This is done through P/Invoke since we pay 13% performance degradation
// through the CAS checks required by File.Exists and Directory.Exists
internal static bool NativeFileExists(string path)
{
if (Platform.UseDotNetToQueryFileAttributes())
{
return System.IO.File.Exists(path);
}
else
{
// This is done through P/Invoke since we pay 13% performance degradation
// through the CAS checks required by File.Exists and Directory.Exists
return WinNativeFileExists(path);
}
}
internal static bool WinNativeFileExists(string path)
{
bool isDirectory;
Exception ioException;
@ -1090,18 +1015,6 @@ namespace System.Management.Automation
// This is done through P/Invoke since we pay 13% performance degradation
// through the CAS checks required by File.Exists and Directory.Exists
internal static bool NativeDirectoryExists(string path)
{
if (Platform.UseDotNetToQueryFileAttributes())
{
return System.IO.Directory.Exists(path);
}
else
{
return WinNativeDirectoryExists(path);
}
}
internal static bool WinNativeDirectoryExists(string path)
{
bool isDirectory;
Exception ioException;
@ -1116,31 +1029,6 @@ namespace System.Management.Automation
}
internal static void NativeEnumerateDirectory(string directory, out List<string> directories, out List<string> files)
{
if (Platform.UseDotNetToQueryFileAttributes())
{
IEnumerable<string> fileEnum = System.IO.Directory.EnumerateFiles(directory);
IEnumerable<string> dirEnum = System.IO.Directory.EnumerateDirectories(directory);
files = new List<string>();
directories = new List<string>();
foreach (string entry in fileEnum)
{
files.Add(directory + "/" + entry);
}
foreach (string entry in dirEnum)
{
directories.Add(directory + "/" + entry);
}
}
else
{
WinNativeEnumerateDirectory(directory, out directories, out files);
}
}
internal static void WinNativeEnumerateDirectory(string directory, out List<string> directories, out List<string> files)
{
IntPtr INVALID_HANDLE_VALUE = new IntPtr(-1);
NativeMethods.WIN32_FIND_DATA findData;
@ -1205,12 +1093,12 @@ namespace System.Management.Automation
internal static bool PathIsUnc(string path)
{
if (!Platform.HasUNCSupport())
{
return false;
}
#if LINUX
return false;
#else
Uri uri;
return !string.IsNullOrEmpty(path) && Uri.TryCreate(path, UriKind.Absolute, out uri) && uri.IsUnc;
#endif
}
internal class NativeMethods

View file

@ -96,12 +96,10 @@ namespace Microsoft.PowerShell.Commands
/// </summary>
internal static void CheckRemotingCmdletPrerequisites()
{
//If the operating system (ie Linux) does not support the registry, simply return to avoid calls to the Windows registry.
if (!Platform.HasRegistrySupport())
{
return;
}
#if LINUX
// TODO: check that PSRP requirements are installed
return;
#else
bool notSupported = true;
String WSManKeyPath = "Software\\Microsoft\\Windows\\CurrentVersion\\WSMAN\\";
@ -155,6 +153,7 @@ namespace Microsoft.PowerShell.Commands
throw new InvalidOperationException(
"Windows PowerShell remoting features are not enabled or not supported on this machine.\nThis may be because you do not have the correct version of WS-Management installed or this version of Windows does not support remoting currently.\n For more information, type 'get-help about_remote_requirements'.");
}
#endif
}
/// <summary>

View file

@ -677,14 +677,11 @@ namespace System.Management.Automation.Language
private static TypeInfo GetComObjectType()
{
if (Platform.HasCom())
{
return typeof(object).GetTypeInfo().Assembly.GetType("System.__ComObject").GetTypeInfo();
}
else
{
return null;
}
#if LINUX
return null;
#else
return typeof(object).GetTypeInfo().Assembly.GetType("System.__ComObject").GetTypeInfo();
#endif
}
internal static bool IsComObject(object obj)

View file

@ -496,7 +496,7 @@ namespace Microsoft.PowerShell.Commands
}
else
{
throw new Platform.PlatformNotSupportedException();
throw new PlatformNotSupportedException();
}
}
@ -607,15 +607,11 @@ namespace Microsoft.PowerShell.Commands
/// </returns>
protected override PSDriveInfo RemoveDrive(PSDriveInfo drive)
{
// if removing the drive is not supported, just return the drive
if (Platform.SupportsRemoveDrive())
{
return WinRemoveDrive(drive);
}
else
{
return drive;
}
#if LINUX
return drive;
#else
return WinRemoveDrive(drive);
#endif
}
private PSDriveInfo WinRemoveDrive(PSDriveInfo drive)
@ -685,15 +681,11 @@ namespace Microsoft.PowerShell.Commands
/// <returns></returns>
internal static string GetUNCForNetworkDrive(string driveName)
{
// Porting note: on systems that do not support UNC paths, the UNC equivalent is the path itself
if (Platform.HasUNCSupport())
{
return WinGetUNCForNetworkDrive(driveName);
}
else
{
return driveName;
}
#if LINUX
return driveName;
#else
return WinGetUNCForNetworkDrive(driveName);
#endif
}
private static string WinGetUNCForNetworkDrive(string driveName)
@ -747,14 +739,11 @@ namespace Microsoft.PowerShell.Commands
/// <returns></returns>
internal static string GetSubstitutedPathForNetworkDosDevice(string driveName)
{
if (Platform.HasNetworkDriveSupport())
{
return WinGetSubstitutedPathForNetworkDosDevice(driveName);
}
else
{
throw new Platform.PlatformNotSupportedException();
}
#if LINUX
throw new PlatformNotSupportedException();
#else
return WinGetSubstitutedPathForNetworkDosDevice(driveName);
#endif
}
private static string WinGetSubstitutedPathForNetworkDosDevice(string driveName)
@ -926,15 +915,15 @@ namespace Microsoft.PowerShell.Commands
if (newDrive.DriveType == DriveType.Network)
{
// Platform notes: This is important because certain mount
// points on non-Windows are enumerated as drives by .net, but
// points on non-Windows are enumerated as drives by .NET, but
// the platform itself then has no real network drive support
// as required by this context. Solution: check for network
// drive support before using it.
if (!Platform.HasNetworkDriveSupport())
{
continue;
}
#if LINUX
continue;
#else
displayRoot = GetRootPathForNetworkDriveOrDosDevice(newDrive);
#endif
}
if (newDrive.DriveType == DriveType.Fixed)
@ -947,12 +936,14 @@ namespace Microsoft.PowerShell.Commands
root = newDrive.RootDirectory.FullName;
}
#if LINUX
// Porting notes: On platforms with single root filesystems, only
// add the filesystem with the root "/" to the initial drive list,
// otherwise path handling will not work correctly because there
// is no : available to separate the filesystems from each other
if (Platform.HasSingleRootFilesystem() && root != StringLiterals.DefaultPathSeparatorString)
if (root != StringLiterals.DefaultPathSeparatorString)
continue;
#endif
// Porting notes: On non-windows platforms .net can report two
// drives with the same root, make sure to only add one of those
@ -2229,7 +2220,7 @@ namespace Microsoft.PowerShell.Commands
}
else
{
success = Platform.NonWindowsCreateSymbolicLink(path,strTargetPath,isDirectory);
success = Platform.NonWindowsCreateSymbolicLink(path,strTargetPath);
}
}
else if(itemType == ItemType.HardLink)
@ -6971,48 +6962,47 @@ namespace Microsoft.PowerShell.Commands
internal static int SafeGetFileAttributes(string path)
{
if (Platform.UseDotNetToQueryFileAttributes())
{
System.IO.FileAttributes attr = System.IO.File.GetAttributes(path);
#if LINUX
System.IO.FileAttributes attr = System.IO.File.GetAttributes(path);
int result = 0;
if ((attr & FileAttributes.Archive) == FileAttributes.Archive)
result |= 0x20;
if ((attr & FileAttributes.Compressed) == FileAttributes.Compressed)
result |= 0x800;
if ((attr & FileAttributes.Device) == FileAttributes.Device)
result |= 0x40;
if ((attr & FileAttributes.Directory) == FileAttributes.Directory)
result |= 0x10;
if ((attr & FileAttributes.Encrypted) == FileAttributes.Encrypted)
result |= 0x4000;
if ((attr & FileAttributes.Hidden) == FileAttributes.Hidden)
result |= 0x2;
if ((attr & FileAttributes.IntegrityStream) == FileAttributes.IntegrityStream)
result |= 0x8000;
if ((attr & FileAttributes.Normal) == FileAttributes.Normal)
result |= 0x80;
if ((attr & FileAttributes.NoScrubData) == FileAttributes.NoScrubData)
result |= 0x20000;
if ((attr & FileAttributes.NotContentIndexed) == FileAttributes.NotContentIndexed)
result |= 0x2000;
if ((attr & FileAttributes.Offline) == FileAttributes.Offline)
result |= 0x1000;
if ((attr & FileAttributes.ReadOnly) == FileAttributes.ReadOnly)
result |= 0x1;
if ((attr & FileAttributes.ReparsePoint) == FileAttributes.ReparsePoint)
result |= 0x400;
if ((attr & FileAttributes.SparseFile) == FileAttributes.SparseFile)
result |= 0x200;
if ((attr & FileAttributes.System) == FileAttributes.System)
result |= 0x4;
if ((attr & FileAttributes.Temporary) == FileAttributes.Temporary)
result |= 0x100;
int result = 0;
if ((attr & FileAttributes.Archive) == FileAttributes.Archive)
result |= 0x20;
if ((attr & FileAttributes.Compressed) == FileAttributes.Compressed)
result |= 0x800;
if ((attr & FileAttributes.Device) == FileAttributes.Device)
result |= 0x40;
if ((attr & FileAttributes.Directory) == FileAttributes.Directory)
result |= 0x10;
if ((attr & FileAttributes.Encrypted) == FileAttributes.Encrypted)
result |= 0x4000;
if ((attr & FileAttributes.Hidden) == FileAttributes.Hidden)
result |= 0x2;
if ((attr & FileAttributes.IntegrityStream) == FileAttributes.IntegrityStream)
result |= 0x8000;
if ((attr & FileAttributes.Normal) == FileAttributes.Normal)
result |= 0x80;
if ((attr & FileAttributes.NoScrubData) == FileAttributes.NoScrubData)
result |= 0x20000;
if ((attr & FileAttributes.NotContentIndexed) == FileAttributes.NotContentIndexed)
result |= 0x2000;
if ((attr & FileAttributes.Offline) == FileAttributes.Offline)
result |= 0x1000;
if ((attr & FileAttributes.ReadOnly) == FileAttributes.ReadOnly)
result |= 0x1;
if ((attr & FileAttributes.ReparsePoint) == FileAttributes.ReparsePoint)
result |= 0x400;
if ((attr & FileAttributes.SparseFile) == FileAttributes.SparseFile)
result |= 0x200;
if ((attr & FileAttributes.System) == FileAttributes.System)
result |= 0x4;
if ((attr & FileAttributes.Temporary) == FileAttributes.Temporary)
result |= 0x100;
return result;
}
else
return WinSafeGetFileAttributes(path);
return result;
#else
return WinSafeGetFileAttributes(path);
#endif
}
internal static int WinSafeGetFileAttributes(string path)
@ -7074,11 +7064,11 @@ namespace Microsoft.PowerShell.Commands
/// <returns></returns>
internal static bool PathIsNetworkPath(string path)
{
// no other logic possible for now on linux
if (Platform.HasNetworkDriveSupport())
return WinPathIsNetworkPath(path);
else
return false;
#if LINUX
return false;
#else
return WinPathIsNetworkPath(path);
#endif
}
internal static bool WinPathIsNetworkPath(string path)
@ -8202,7 +8192,7 @@ namespace Microsoft.PowerShell.Commands
{
if (!Platform.IsWindows)
{
throw new Platform.PlatformNotSupportedException();
throw new PlatformNotSupportedException();
}
using (SafeFileHandle handle = OpenReparsePoint(filePath, FileDesiredAccess.GenericRead))
@ -8595,14 +8585,11 @@ namespace Microsoft.PowerShell.Commands
private static SafeFileHandle OpenReparsePoint(string reparsePoint, FileDesiredAccess accessMode)
{
if (Platform.SupportsReparsePoints())
{
return WinOpenReparsePoint(reparsePoint,accessMode);
}
else
{
throw new Platform.PlatformNotSupportedException();
}
#if LINUX
throw new PlatformNotSupportedException();
#else
return WinOpenReparsePoint(reparsePoint,accessMode);
#endif
}
private static SafeFileHandle WinOpenReparsePoint(string reparsePoint, FileDesiredAccess accessMode)

View file

@ -1589,9 +1589,12 @@ namespace System.Management.Automation
/// </returns>
internal static bool IsSingleFileSystemAbsolutePath(string path)
{
return Platform.HasSingleRootFilesystem() &&
(path.StartsWith(StringLiterals.DefaultPathSeparatorString, StringComparison.Ordinal) ||
path.StartsWith(StringLiterals.AlternatePathSeparatorString, StringComparison.Ordinal));
#if LINUX
return path.StartsWith(StringLiterals.DefaultPathSeparatorString, StringComparison.Ordinal)
|| path.StartsWith(StringLiterals.AlternatePathSeparatorString, StringComparison.Ordinal);
#else
return false;
#endif
} // IsSingleFileSystemAbsolutePath
/// <summary>

View file

@ -136,11 +136,7 @@ namespace Microsoft.PowerShell.Commands
{
Collection<PSDriveInfo> drives = new Collection<PSDriveInfo>();
if (!Platform.HasRegistrySupport())
{
return drives;
}
#if !LINUX
drives.Add(
new PSDriveInfo(
"HKLM",
@ -156,6 +152,7 @@ namespace Microsoft.PowerShell.Commands
"HKEY_CURRENT_USER",
RegistryProviderStrings.HKCUDriveDescription,
null));
#endif
return drives;
} // InitializeDefaultDrives

View file

@ -58,13 +58,13 @@ namespace Microsoft.PowerShell.Commands.Internal
override protected bool ReleaseHandle()
{
if (!Platform.HasRegistrySupport())
{
return true;
}
#if LINUX
return true;
#else
// Returns a Win32 error code, 0 for success
int r = RegCloseKey(handle);
return r == 0;
#endif
}
}
}

View file

@ -138,11 +138,9 @@ namespace System.Management.Automation
internal static void SetExecutionPolicy(ExecutionPolicyScope scope, ExecutionPolicy policy, string shellId)
{
if (!Platform.HasExecutionPolicy())
{
throw new PlatformNotSupportedException();
}
#if LINUX
throw new PlatformNotSupportedException();
#else
string executionPolicy = "Restricted";
string preferenceKey = Utils.GetRegistryConfigurationPath(shellId);
const string PolicyKeyValueName = "ExecutionPolicy";
@ -227,6 +225,7 @@ namespace System.Management.Automation
break;
}
}
#endif
}
// Clean up the parents of a registry key as long as they
@ -283,11 +282,9 @@ namespace System.Management.Automation
internal static ExecutionPolicy GetExecutionPolicy(string shellId, ExecutionPolicyScope scope)
{
if (!Platform.HasExecutionPolicy())
{
throw new PlatformNotSupportedException();
}
#if LINUX
return ExecutionPolicy.Unrestricted;
#else
switch (scope)
{
case ExecutionPolicyScope.Process:
@ -368,6 +365,7 @@ namespace System.Management.Automation
}
return ExecutionPolicy.Restricted;
#endif
}
internal static ExecutionPolicy ParseExecutionPolicy(string policy)
@ -1633,14 +1631,11 @@ namespace System.Management.Automation
/// <returns>AMSI_RESULT_DETECTED if malware was detected in the sample.</returns>
internal static AmsiNativeMethods.AMSI_RESULT ScanContent(string content, string sourceMetadata)
{
if (Platform.HasAmsi())
{
return WinScanContent(content,sourceMetadata);
}
else
{
return AmsiNativeMethods.AMSI_RESULT.AMSI_RESULT_NOT_DETECTED;
}
#if LINUX
return AmsiNativeMethods.AMSI_RESULT.AMSI_RESULT_NOT_DETECTED;
#else
return WinScanContent(content,sourceMetadata);
#endif
}
internal static AmsiNativeMethods.AMSI_RESULT WinScanContent(string content, string sourceMetadata)
@ -1730,14 +1725,9 @@ namespace System.Management.Automation
internal static void CurrentDomain_ProcessExit(object sender, EventArgs e)
{
if (Platform.HasAmsi())
{
VerifyAmsiUninitializeCalled();
}
else
{
throw new PlatformNotSupportedException();
}
#if !LINUX
VerifyAmsiUninitializeCalled();
#endif
}
[SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources")]
@ -1753,16 +1743,9 @@ namespace System.Management.Automation
/// </summary>
internal static void CloseSession()
{
if (Platform.HasAmsi())
{
WinCloseSession();
}
else
{
// Porting note: cannot throw here because this is called whether or not
// AMSI was initialized in the first place
return;
}
#if !LINUX
WinCloseSession();
#endif
}
internal static void WinCloseSession()
@ -1789,16 +1772,9 @@ namespace System.Management.Automation
/// </summary>
internal static void Uninitialize()
{
if (Platform.HasAmsi())
{
WinUninitialize();
}
else
{
// Porting note: cannot throw here because this is called whether or not
// AMSI was initialized in the first place
return;
}
#if !LINUX
WinUninitialize();
#endif
}
internal static void WinUninitialize()

View file

@ -130,12 +130,9 @@ namespace System.Management.Automation
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static bool IsComObject(this Type type)
{
if (!Platform.HasCom())
{
return false;
}
#if CORECLR // Type.IsComObject(Type) is not in CoreCLR
#if LINUX
return false;
#elif CORECLR // Type.IsComObject(Type) is not in CoreCLR
return ComObjectType.IsAssignableFrom(type);
#else
return type.IsCOMObject;

View file

@ -1,5 +1,6 @@
add_library(psl-native SHARED
getstat.cpp
getlstat.cpp
getpwuid.cpp
getuserfrompid.cpp
getfileowner.cpp
@ -8,6 +9,8 @@ add_library(psl-native SHARED
getcomputername.cpp
getlinkcount.cpp
getfullyqualifiedname.cpp
isfile.cpp
isdirectory.cpp
issymlink.cpp
isexecutable.cpp
setdate.cpp

View file

@ -39,26 +39,25 @@
//! - ERROR_INVALID_FUNCTION: incorrect function
//! - ERROR_BAD_PATH_NAME: pathname is too long, or contains invalid characters
//!
//! @retval 1 if creation is successful
//! @retval 0 if createion failed
//! @retval boolean successful
//!
int32_t CreateSymLink(const char *link, const char *target)
bool CreateSymLink(const char *link, const char *target)
{
errno = 0;
errno = 0;
// Check parameters
if (!link || !target)
{
errno = ERROR_INVALID_PARAMETER;
return 0;
return false;
}
int returnCode = symlink(target, link);
int ret = symlink(target, link);
if (returnCode == 0)
if (ret == 0)
{
return 1;
return true;
}
switch(errno)
@ -102,5 +101,6 @@ int32_t CreateSymLink(const char *link, const char *target)
default:
errno = ERROR_INVALID_FUNCTION;
}
return 0;
return false;
}

View file

@ -1,9 +1,10 @@
#pragma once
#include "pal.h"
#include <stdbool.h>
PAL_BEGIN_EXTERNC
int32_t CreateSymLink(const char *link, const char *target);
bool CreateSymLink(const char *link, const char *target);
PAL_END_EXTERNC

View file

@ -0,0 +1,95 @@
//! @file getlstat.cpp
//! @author Andrew Schwartzmeyer <andschwa@microsoft.com>
//! @brief returns the lstat of a file
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <pwd.h>
#include <string.h>
#include <unistd.h>
#include "getlstat.h"
//! @brief GetLStat returns the lstat of a file. This simply delegates to the
//! lstat() system call and maps errno to the expected values for GetLastError.
//!
//! GetLstat
//!
//! @param[in] path
//! @parblock
//! A pointer to the buffer that contains the file name
//!
//! char* is marshaled as an LPStr, which on Linux is UTF-8.
//! @endparblock
//!
//! @param[in] lstat
//! @parblock
//! A pointer to the buffer in which to place the lstat information
//! @endparblock
//!
//! @exception errno Passes these errors via errno to GetLastError:
//! - ERROR_INVALID_PARAMETER: parameter is not valid
//! - ERROR_FILE_NOT_FOUND: file does not exist
//! - ERROR_ACCESS_DENIED: access is denied
//! - ERROR_INVALID_ADDRESS: attempt to access invalid address
//! - ERROR_STOPPED_ON_SYMLINK: too many symbolic links
//! - ERROR_GEN_FAILURE: I/O error occurred
//! - ERROR_INVALID_NAME: file provided is not a symbolic link
//! - ERROR_INVALID_FUNCTION: incorrect function
//! - ERROR_BAD_PATH_NAME: pathname is too long
//! - ERROR_OUTOFMEMORY insufficient kernel memory
//!
//! @retval 0 if successful
//! @retval -1 if failed
//!
int32_t GetLStat(const char* path, struct stat* buf)
{
errno = 0;
if (!path)
{
errno = ERROR_INVALID_PARAMETER;
return -1;
}
int32_t ret = lstat(path, buf);
if (ret != 0)
{
switch(errno)
{
case EACCES:
errno = ERROR_ACCESS_DENIED;
break;
case EBADF:
errno = ERROR_FILE_NOT_FOUND;
break;
case EFAULT:
errno = ERROR_INVALID_ADDRESS;
break;
case ELOOP:
errno = ERROR_STOPPED_ON_SYMLINK;
break;
case ENAMETOOLONG:
errno = ERROR_GEN_FAILURE;
break;
case ENOENT:
errno = ERROR_FILE_NOT_FOUND;
break;
case ENOMEM:
errno = ERROR_NO_SUCH_USER;
break;
case ENOTDIR:
errno = ERROR_INVALID_NAME;
break;
case EOVERFLOW:
errno = ERROR_BUFFER_OVERFLOW;
break;
default:
errno = ERROR_INVALID_FUNCTION;
}
}
return ret;
}

View file

@ -0,0 +1,10 @@
#pragma once
#include <sys/stat.h>
#include "pal.h"
PAL_BEGIN_EXTERNC
int32_t GetLStat(const char* path, struct stat* buf);
PAL_END_EXTERNC

View file

@ -89,8 +89,7 @@ int32_t GetStat(const char* path, struct stat* buf)
default:
errno = ERROR_INVALID_FUNCTION;
}
return -1;
}
return 0;
return ret;
}

View file

@ -0,0 +1,50 @@
//! @file isdirectory.cpp
//! @author Andrew Schwartzmeyer <andschwa@microsoft.com>
//! @brief returns if the path is a directory
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <pwd.h>
#include <string.h>
#include <unistd.h>
#include "getstat.h"
#include "getpwuid.h"
#include "getfileowner.h"
#include "isdirectory.h"
//! @brief returns if the path is a directory; uses stat and so follows symlinks
//!
//! IsDirectory
//!
//! @param[in] path
//! @parblock
//! A pointer to the buffer that contains the file name
//!
//! char* is marshaled as an LPStr, which on Linux is UTF-8.
//! @endparblock
//!
//! @exception errno Passes this error via errno to GetLastError:
//! - ERROR_INVALID_PARAMETER: parameter is not valid
//!
//! @retval true if directory, false otherwise
//!
bool IsDirectory(const char* path)
{
errno = 0;
if (!path)
{
errno = ERROR_INVALID_PARAMETER;
return false;
}
struct stat buf;
int32_t ret = GetStat(path, &buf);
if (ret != 0)
{
return false;
}
return S_ISDIR(buf.st_mode);
}

View file

@ -0,0 +1,10 @@
#pragma once
#include "pal.h"
#include <stdbool.h>
PAL_BEGIN_EXTERNC
bool IsDirectory(const char* path);
PAL_END_EXTERNC

View file

@ -1,5 +1,5 @@
//! @file isExecutable.cpp
//! @author George FLeming <v-geflem@microsoft.com>
//! @file isexecutable.cpp
//! @author George Fleming <v-geflem@microsoft.com>
//! @brief returns whether a file is executable
#include <errno.h>
@ -11,7 +11,7 @@
//!
//! IsExecutable
//!
//! @param[in] fileName
//! @param[in] path
//! @parblock
//! A pointer to the buffer that contains the file name
//!
@ -19,63 +19,21 @@
//! @endparblock
//!
//! @exception errno Passes these errors via errno to GetLastError:
//! - ERROR_FILE_NOT_FOUND: the system cannot find the file specified
//! - ERROR_INVALID_ADDRESS: attempt to access invalid address
//! - ERROR_GEN_FAILURE: device attached to the system is not functioning
//! - ERROR_INVALID_NAME: filename, directory name, or volume label syntax is incorrect
//! - ERROR_INVALID_FUNCTION: incorrect function
//! - ERROR_INVALID_PARAMETER: parameter to access(2) call is incorrect
//!
//! @retval 1 if path is an executable
//! @retval 0 if path is not a executable
//! @retval -1 If the function fails.. To get extended error information, call GetLastError.
//! @retval true if path is an executable, false otherwise
//!
int32_t IsExecutable(const char* fileName)
bool IsExecutable(const char* path)
{
errno = 0;
// Check parameters
if (!fileName)
if (!path)
{
errno = ERROR_INVALID_PARAMETER;
return -1;
return false;
}
int returnCode = access(fileName, X_OK);
if (returnCode == 0)
{
return 1;
}
switch(errno)
{
case EACCES:
return 0;
case EBADF:
case ENOENT:
errno = ERROR_FILE_NOT_FOUND;
break;
case EFAULT:
errno = ERROR_INVALID_ADDRESS;
break;
case ELOOP:
errno = ERROR_STOPPED_ON_SYMLINK;
break;
case EIO:
case ENOMEM:
errno = ERROR_GEN_FAILURE;
break;
case ENOTDIR:
case ENAMETOOLONG:
errno = ERROR_INVALID_NAME;
break;
case EINVAL:
errno = ERROR_INVALID_PARAMETER;
break;
default:
errno = ERROR_INVALID_FUNCTION;
}
return -1;
return access(path, X_OK) != -1;
}

View file

@ -1,9 +1,10 @@
#pragma once
#include "pal.h"
#include <stdbool.h>
PAL_BEGIN_EXTERNC
int32_t IsExecutable(const char* fileName);
bool IsExecutable(const char* path);
PAL_END_EXTERNC

View file

@ -0,0 +1,43 @@
//! @file isfile.cpp
//! @author Andrew Schwartzmeyer <andschwa@microsoft.com>
//! @brief returns if the path exists
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <pwd.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include "getlstat.h"
#include "isfile.h"
//! @brief returns if the path is a file or directory
//!
//! IsFile
//!
//! @param[in] path
//! @parblock
//! A pointer to the buffer that contains the file name
//!
//! char* is marshaled as an LPStr, which on Linux is UTF-8.
//! @endparblock
//!
//! @exception errno Passes this error via errno to GetLastError:
//! - ERROR_INVALID_PARAMETER: parameter is not valid
//!
//! @retval true if path exists, false otherwise
//!
bool IsFile(const char* path)
{
errno = 0;
if (!path)
{
errno = ERROR_INVALID_PARAMETER;
return false;
}
struct stat buf;
return GetLStat(path, &buf) == 0;
}

View file

@ -0,0 +1,10 @@
#pragma once
#include "pal.h"
#include <stdbool.h>
PAL_BEGIN_EXTERNC
bool IsFile(const char* path);
PAL_END_EXTERNC

View file

@ -7,13 +7,14 @@
#include <sys/stat.h>
#include <unistd.h>
#include <string>
#include "getlstat.h"
#include "issymlink.h"
//! @brief IsSymLink determines if path is a symbolic link
//! @brief IsSymlink determines if path is a symbolic link
//!
//! IsSymLink
//! IsSymlink
//!
//! @param[in] fileName
//! @param[in] path
//! @parblock
//! A pointer to the buffer that contains the file name
//!
@ -34,63 +35,26 @@
//! - ERROR_INVALID_FUNCTION: incorrect function
//! - ERROR_BAD_PATH_NAME: pathname is too long, or contains invalid characters
//!
//! @retval 1 if path is a symbolic link
//! @retval 0 if path is not a symbolic link
//! @retval -1 If the function fails.. To get extended error information, call GetLastError.
//! @retval true if path is a symbolic link, false otherwise
//!
int32_t IsSymLink(const char* fileName)
bool IsSymLink(const char* path)
{
errno = 0;
errno = 0;
// Check parameters
if (!fileName)
if (!path)
{
errno = ERROR_INVALID_PARAMETER;
return -1;
return false;
}
struct stat statBuf;
int returnCode = lstat(fileName, &statBuf);
if (returnCode != 0)
struct stat buf;
int32_t ret = GetLStat(path, &buf);
if (ret != 0)
{
switch(errno)
{
case EACCES:
errno = ERROR_ACCESS_DENIED;
break;
case EBADF:
errno = ERROR_FILE_NOT_FOUND;
break;
case EFAULT:
errno = ERROR_INVALID_ADDRESS;
break;
case ELOOP:
errno = ERROR_STOPPED_ON_SYMLINK;
break;
case ENAMETOOLONG:
errno = ERROR_GEN_FAILURE;
break;
case ENOENT:
errno = ERROR_FILE_NOT_FOUND;
break;
case ENOMEM:
errno = ERROR_NO_SUCH_USER;
break;
case ENOTDIR:
errno = ERROR_INVALID_NAME;
break;
case EOVERFLOW:
errno = ERROR_BUFFER_OVERFLOW;
break;
default:
errno = ERROR_INVALID_FUNCTION;
}
return -1;
return false;
}
return S_ISLNK(statBuf.st_mode) ? 1 : 0;
return S_ISLNK(buf.st_mode);
}

View file

@ -1,9 +1,10 @@
#pragma once
#include "pal.h"
#include <stdbool.h>
PAL_BEGIN_EXTERNC
int32_t IsSymLink(const char* fileName);
bool IsSymLink(const char* path);
PAL_END_EXTERNC

View file

@ -9,6 +9,8 @@ add_executable(psl-native-test
test-getcomputername.cpp
test-getlinkcount.cpp
test-getfullyqualifiedname.cpp
test-isdirectory.cpp
test-isfile.cpp
test-issymlink.cpp
test-isexecutable.cpp
test-createsymlink.cpp

View file

@ -39,36 +39,36 @@ protected:
EXPECT_TRUE(dir != NULL);
// Create symbolic link to file
int ret1 = CreateSymLink(fileSymLink.c_str(), file);
EXPECT_EQ(ret1, 1);
bool ret1 = CreateSymLink(fileSymLink.c_str(), file);
EXPECT_TRUE(ret1);
// Create symbolic link to directory
int ret2 = CreateSymLink(dirSymLink.c_str(), dir);
EXPECT_EQ(ret2, 1);
bool ret2 = CreateSymLink(dirSymLink.c_str(), dir);
EXPECT_TRUE(ret2);
}
~CreateSymLinkTest()
{
int ret;
bool ret;
ret = unlink(fileSymLink.c_str());
EXPECT_EQ(0, ret);
EXPECT_FALSE(ret);
ret = unlink(dirSymLink.c_str());
EXPECT_EQ(0, ret);
EXPECT_FALSE(ret);
ret = unlink(file);
EXPECT_EQ(0, ret);
EXPECT_FALSE(ret);
ret = rmdir(dir);
EXPECT_EQ(0, ret);
EXPECT_FALSE(ret);
}
};
TEST_F(CreateSymLinkTest, FilePathNameIsNull)
{
int retVal = CreateSymLink(NULL, NULL);
EXPECT_EQ(retVal, 0);
bool retVal = CreateSymLink(NULL, NULL);
EXPECT_FALSE(retVal);
EXPECT_EQ(ERROR_INVALID_PARAMETER, errno);
}
@ -82,8 +82,8 @@ TEST_F(CreateSymLinkTest, FilePathNameDoesNotExist)
unlink(invalidLink.c_str());
// Linux allows creation of symbolic link that points to an invalid file
int retVal = CreateSymLink(invalidLink.c_str(), invalidFile.c_str());
EXPECT_EQ(retVal, 1);
bool retVal = CreateSymLink(invalidLink.c_str(), invalidFile.c_str());
EXPECT_TRUE(retVal);
std::string target = FollowSymLink(invalidLink.c_str());
EXPECT_EQ(target, invalidFile);
@ -93,8 +93,8 @@ TEST_F(CreateSymLinkTest, FilePathNameDoesNotExist)
TEST_F(CreateSymLinkTest, SymLinkToFile)
{
int retVal = IsSymLink(fileSymLink.c_str());
EXPECT_EQ(1, retVal);
bool retVal = IsSymLink(fileSymLink.c_str());
EXPECT_TRUE(retVal);
std::string target = FollowSymLink(fileSymLink.c_str());
char buffer[PATH_MAX];
@ -104,8 +104,8 @@ TEST_F(CreateSymLinkTest, SymLinkToFile)
TEST_F(CreateSymLinkTest, SymLinkToDirectory)
{
int retVal = IsSymLink(dirSymLink.c_str());
EXPECT_EQ(1, retVal);
bool retVal = IsSymLink(dirSymLink.c_str());
EXPECT_TRUE(retVal);
std::string target = FollowSymLink(dirSymLink.c_str());
char buffer[PATH_MAX];
@ -115,7 +115,7 @@ TEST_F(CreateSymLinkTest, SymLinkToDirectory)
TEST_F(CreateSymLinkTest, SymLinkAgain)
{
int retVal = CreateSymLink(fileSymLink.c_str(), file);
EXPECT_EQ(0, retVal);
bool retVal = CreateSymLink(fileSymLink.c_str(), file);
EXPECT_FALSE(retVal);
EXPECT_EQ(ERROR_FILE_EXISTS, errno);
}

View file

@ -0,0 +1,31 @@
//! @file test-isdirectory.cpp
//! @author Andrew Schwartzmeyer <andschwa@microsoft.com>
//! @brief Tests IsDirectory
#include <gtest/gtest.h>
#include <errno.h>
#include <unistd.h>
#include "isdirectory.h"
TEST(IsDirectoryTest, RootIsDirectory)
{
EXPECT_TRUE(IsDirectory("/"));
}
TEST(IsDirectoryTest, BinLsIsNotDirectory)
{
EXPECT_FALSE(IsDirectory("/bin/ls"));
}
TEST(IsDirectoryTest, ReturnsFalseForFakeDirectory)
{
EXPECT_FALSE(IsDirectory("SomeMadeUpFileNameThatDoesNotExist"));
EXPECT_EQ(errno, ERROR_FILE_NOT_FOUND);
}
TEST(IsDirectoryTest, ReturnsFalseForNullInput)
{
EXPECT_FALSE(IsDirectory(NULL));
EXPECT_EQ(errno, ERROR_INVALID_PARAMETER);
}

View file

@ -33,62 +33,52 @@ protected:
// First create a file
int fd = mkstemp(fileTemplateBuf);
EXPECT_TRUE(fd != -1);
file = fileTemplateBuf;
file = fileTemplateBuf;
}
~IsExecutableTest()
{
int ret;
ret = unlink(file);
EXPECT_EQ(0, ret);
EXPECT_FALSE(unlink(file));
}
void ChangeFilePermission(const char* file, mode_t mode)
{
int ret = chmod(file, mode);
EXPECT_EQ(ret, 0);
EXPECT_FALSE(chmod(file, mode));
}
};
TEST_F(IsExecutableTest, FilePathNameIsNull)
{
int32_t retVal = IsExecutable(NULL);
EXPECT_EQ(retVal, -1);
EXPECT_FALSE(IsExecutable(NULL));
EXPECT_EQ(ERROR_INVALID_PARAMETER, errno);
}
TEST_F(IsExecutableTest, FilePathNameDoesNotExist)
{
std::string invalidFile = "/tmp/isexecutabletest_invalidFile";
int32_t retVal = IsExecutable(invalidFile.c_str());
EXPECT_EQ(retVal, -1);
EXPECT_FALSE(IsExecutable(invalidFile.c_str()));
EXPECT_EQ(ERROR_FILE_NOT_FOUND, errno);
}
TEST_F(IsExecutableTest, NormalFileIsNotIsexecutable)
{
int32_t retVal = IsExecutable(file);
EXPECT_EQ(0, retVal);
EXPECT_FALSE(IsExecutable(file));
ChangeFilePermission(file, mode_444);
retVal = IsExecutable(file);
EXPECT_EQ(0, retVal);
EXPECT_FALSE(IsExecutable(file));
}
TEST_F(IsExecutableTest, FilePermission_700)
{
ChangeFilePermission(file, mode_700);
int32_t retVal = IsExecutable(file);
EXPECT_EQ(1, retVal);
EXPECT_TRUE(IsExecutable(file));
}
TEST_F(IsExecutableTest, FilePermission_777)
{
ChangeFilePermission(file, mode_777);
int32_t retVal = IsExecutable(file);
EXPECT_EQ(1, retVal);
EXPECT_TRUE(IsExecutable(file));
}

View file

@ -0,0 +1,30 @@
//! @file test-isfile.cpp
//! @author Andrew Schwartzmeyer <andschwa@microsoft.com>
//! @brief Tests Isfile
#include <gtest/gtest.h>
#include <errno.h>
#include <unistd.h>
#include "isfile.h"
TEST(IsFileTest, RootIsFile)
{
EXPECT_TRUE(IsFile("/"));
}
TEST(IsFileTest, BinLsIsFile)
{
EXPECT_TRUE(IsFile("/bin/ls"));
}
TEST(IsFileTest, CannotGetOwnerOfFakeFile)
{
EXPECT_FALSE(IsFile("SomeMadeUpFileNameThatDoesNotExist"));
EXPECT_EQ(errno, ERROR_FILE_NOT_FOUND);
}
TEST(IsFileTest, ReturnsFalseForNullInput)
{
EXPECT_FALSE(IsFile(NULL));
EXPECT_EQ(errno, ERROR_INVALID_PARAMETER);
}

View file

@ -30,75 +30,60 @@ protected:
// First create a file
int fd = mkstemp(fileTemplateBuf);
EXPECT_TRUE(fd != -1);
file = fileTemplateBuf;
file = fileTemplateBuf;
// Create a temp directory
dir = mkdtemp(dirTemplateBuf);
// Create a temp directory
dir = mkdtemp(dirTemplateBuf);
EXPECT_TRUE(dir != NULL);
// Create symbolic link to file
int ret1 = symlink(file, fileSymLink.c_str());
EXPECT_EQ(ret1, 0);
EXPECT_FALSE(symlink(file, fileSymLink.c_str()));
// Create symbolic link to directory
int ret2 = symlink(dir, dirSymLink.c_str());
EXPECT_EQ(ret2, 0);
EXPECT_FALSE(symlink(dir, dirSymLink.c_str()));
}
~isSymLinkTest()
{
int ret;
EXPECT_FALSE(unlink(fileSymLink.c_str()));
ret = unlink(fileSymLink.c_str());
EXPECT_EQ(0, ret);
EXPECT_FALSE(unlink(dirSymLink.c_str()));
ret = unlink(dirSymLink.c_str());
EXPECT_EQ(0, ret);
EXPECT_FALSE(unlink(file));
ret = unlink(file);
EXPECT_EQ(0, ret);
ret = rmdir(dir);
EXPECT_EQ(0, ret);
EXPECT_FALSE(rmdir(dir));
}
};
TEST_F(isSymLinkTest, FilePathNameIsNull)
{
int retVal = IsSymLink(NULL);
EXPECT_EQ(retVal, -1);
EXPECT_FALSE(IsSymLink(NULL));
EXPECT_EQ(ERROR_INVALID_PARAMETER, errno);
}
TEST_F(isSymLinkTest, FilePathNameDoesNotExist)
{
std::string invalidFile = "/tmp/symlinktest_invalidFile";
int retVal = IsSymLink(invalidFile.c_str());
EXPECT_EQ(retVal, -1);
EXPECT_FALSE(IsSymLink(invalidFile.c_str()));
EXPECT_EQ(ERROR_FILE_NOT_FOUND, errno);
}
TEST_F(isSymLinkTest, NormalFileIsNotSymLink)
{
int retVal = IsSymLink(file);
EXPECT_EQ(0, retVal);
EXPECT_FALSE(IsSymLink(file));
}
TEST_F(isSymLinkTest, SymLinkToFile)
{
int retVal = IsSymLink(fileSymLink.c_str());
EXPECT_EQ(1, retVal);
EXPECT_TRUE(IsSymLink(fileSymLink.c_str()));
}
TEST_F(isSymLinkTest, NormalDirectoryIsNotSymbLink)
{
int retVal = IsSymLink(dir);
EXPECT_EQ(0, retVal);
EXPECT_FALSE(IsSymLink(dir));
}
TEST_F(isSymLinkTest, SymLinkToDirectory)
{
int retVal = IsSymLink(dirSymLink.c_str());
EXPECT_EQ(1, retVal);
EXPECT_TRUE(IsSymLink(dirSymLink.c_str()));
}

View file

@ -15,30 +15,6 @@ namespace PSTests
Assert.True(Platform.IsCore);
}
[Fact]
public static void TestHasCom()
{
Assert.False(Platform.HasCom());
}
[Fact]
public static void TestHasAmsi()
{
Assert.False(Platform.HasAmsi());
}
[Fact]
public static void TestHasDriveAutoMounting()
{
Assert.False(Platform.HasDriveAutoMounting());
}
[Fact]
public static void TestHasRegistrySupport()
{
Assert.False(Platform.HasRegistrySupport());
}
[Fact]
public static void TestGetUserName()
{

View file

@ -0,0 +1,6 @@
Describe 'Get-CimClass' {
# Get-CimClass works only on windows
It 'can get CIM_Error CIM class' -Skip:(-not $IsWindows) {
Get-CimClass -ClassName CIM_Error | Should Not Be $null
}
}

View file

@ -2,17 +2,17 @@ function Clean-State
{
if (Test-Path $FullyQualifiedLink)
{
Remove-Item $FullyQualifiedLink -Force
Remove-Item $FullyQualifiedLink -Force
}
if (Test-Path $FullyQualifiedFile)
{
Remove-Item $FullyQualifiedFile -Force
Remove-Item $FullyQualifiedFile -Force
}
if (Test-Path $FullyQualifiedFolder)
{
Remove-Item $FullyQualifiedFolder -Force
Remove-Item $FullyQualifiedFolder -Force
}
}
@ -26,17 +26,17 @@ Describe "New-Item" {
$FullyQualifiedLink = Join-Path -Path $tmpDirectory -ChildPath $testlink
BeforeEach {
Clean-State
Clean-State
}
It "should call the function without error" {
{ New-Item -Name $testfile -Path $tmpDirectory -ItemType file } | Should Not Throw
{ New-Item -Name $testfile -Path $tmpDirectory -ItemType file } | Should Not Throw
}
It "Should create a file without error" {
New-Item -Name $testfile -Path $tmpDirectory -ItemType file
New-Item -Name $testfile -Path $tmpDirectory -ItemType file
Test-Path $FullyQualifiedFile | Should Be $true
Test-Path $FullyQualifiedFile | Should Be $true
$fileInfo = Get-ChildItem $FullyQualifiedFile
$fileInfo.Target | Should Be $null
@ -44,72 +44,72 @@ Describe "New-Item" {
}
It "Should create a folder without an error" {
New-Item -Name newDirectory -Path $tmpDirectory -ItemType directory
New-Item -Name newDirectory -Path $tmpDirectory -ItemType directory
Test-Path $FullyQualifiedFolder | Should Be $true
Test-Path $FullyQualifiedFolder | Should Be $true
}
It "Should create a file using the ni alias" {
ni -Name $testfile -Path $tmpDirectory -ItemType file
ni -Name $testfile -Path $tmpDirectory -ItemType file
Test-Path $FullyQualifiedFile | Should Be $true
Test-Path $FullyQualifiedFile | Should Be $true
}
It "Should create a file using the Type alias instead of ItemType" {
New-Item -Name $testfile -Path $tmpDirectory -Type file
New-Item -Name $testfile -Path $tmpDirectory -Type file
Test-Path $FullyQualifiedFile | Should Be $true
Test-Path $FullyQualifiedFile | Should Be $true
}
It "Should create a file with sample text inside the file using the Value switch" {
$expected = "This is test string"
New-Item -Name $testfile -Path $tmpDirectory -ItemType file -Value $expected
$expected = "This is test string"
New-Item -Name $testfile -Path $tmpDirectory -ItemType file -Value $expected
Test-Path $FullyQualifiedFile | Should Be $true
Test-Path $FullyQualifiedFile | Should Be $true
Get-Content $FullyQualifiedFile | Should Be $expected
Get-Content $FullyQualifiedFile | Should Be $expected
}
It "Should not create a file when the Name switch is not used and only a directory specified" {
#errorAction used because permissions issue in Windows
New-Item -Path $tmpDirectory -ItemType file -ErrorAction SilentlyContinue
#errorAction used because permissions issue in Windows
New-Item -Path $tmpDirectory -ItemType file -ErrorAction SilentlyContinue
Test-Path $FullyQualifiedFile | Should Be $false
Test-Path $FullyQualifiedFile | Should Be $false
}
It "Should create a file when the Name switch is not used but a fully qualified path is specified" {
New-Item -Path $FullyQualifiedFile -ItemType file
New-Item -Path $FullyQualifiedFile -ItemType file
Test-Path $FullyQualifiedFile | Should Be $true
Test-Path $FullyQualifiedFile | Should Be $true
}
It "Should be able to create a multiple items in different directories" {
$FullyQualifiedFile2 = Join-Path -Path $tmpDirectory -ChildPath test2.txt
New-Item -ItemType file -Path $FullyQualifiedFile, $FullyQualifiedFile2
$FullyQualifiedFile2 = Join-Path -Path $tmpDirectory -ChildPath test2.txt
New-Item -ItemType file -Path $FullyQualifiedFile, $FullyQualifiedFile2
Test-Path $FullyQualifiedFile | Should Be $true
Test-Path $FullyQualifiedFile2 | Should Be $true
Test-Path $FullyQualifiedFile | Should Be $true
Test-Path $FullyQualifiedFile2 | Should Be $true
Remove-Item $FullyQualifiedFile2
Remove-Item $FullyQualifiedFile2
}
It "Should be able to call the whatif switch without error" {
{ New-Item -Name testfile.txt -Path $tmpDirectory -ItemType file -WhatIf } | Should Not Throw
{ New-Item -Name testfile.txt -Path $tmpDirectory -ItemType file -WhatIf } | Should Not Throw
}
It "Should not create a new file when the whatif switch is used" {
New-Item -Name $testfile -Path $tmpDirectory -ItemType file -WhatIf
New-Item -Name $testfile -Path $tmpDirectory -ItemType file -WhatIf
Test-Path $FullyQualifiedFile | Should Be $false
Test-Path $FullyQualifiedFile | Should Be $false
}
It "Should create a symbolic link of a file without error" {
New-Item -Name $testfile -Path $tmpDirectory -ItemType file
Test-Path $FullyQualifiedFile | Should Be $true
New-Item -Name $testfile -Path $tmpDirectory -ItemType file
Test-Path $FullyQualifiedFile | Should Be $true
New-Item -ItemType SymbolicLink -Target $FullyQualifiedFile -Name $testlink -Path $tmpDirectory
Test-Path $FullyQualifiedLink | Should Be $true
New-Item -ItemType SymbolicLink -Target $FullyQualifiedFile -Name $testlink -Path $tmpDirectory
Test-Path $FullyQualifiedLink | Should Be $true
$fileInfo = Get-ChildItem $FullyQualifiedLink
$fileInfo.Target | Should Match ([regex]::Escape($FullyQualifiedFile))
@ -128,18 +128,18 @@ Describe "New-Item" {
}
It "Should create a symbolic link from directory without error" {
New-Item -Name $testFolder -Path $tmpDirectory -ItemType directory
Test-Path $FullyQualifiedFolder | Should Be $true
New-Item -Name $testFolder -Path $tmpDirectory -ItemType directory
Test-Path $FullyQualifiedFolder | Should Be $true
New-Item -ItemType SymbolicLink -Target $FullyQualifiedFolder -Name $testlink -Path $tmpDirectory
Test-Path $FullyQualifiedLink | Should Be $true
New-Item -ItemType SymbolicLink -Target $FullyQualifiedFolder -Name $testlink -Path $tmpDirectory
Test-Path $FullyQualifiedLink | Should Be $true
$fileInfo = Get-ChildItem $FullyQualifiedLink
$fileInfo.Target | Should Match ([regex]::Escape($FullyQualifiedFolder))
$fileInfo.LinkType | Should Be "SymbolicLink"
# Remove the link explicitly to avoid broken symlink issue
Remove-Item $FullyQualifiedLink -Force
# Remove the link explicitly to avoid broken symlink issue
Remove-Item $FullyQualifiedLink -Force
}
It "Should create a hard link of a file without error" {

View file

@ -1,24 +1,24 @@
Describe "ExecutionPolicy" {
Context "Check Get-ExecutionPolicy behavior" {
It "Should throw PlatformNotSupported when not on Windows" -Skip:$IsWindows {
{ Get-ExecutionPolicy } | Should Throw "Operation is not supported on this platform."
}
It "Should unrestricted when not on Windows" -Skip:$IsWindows {
Get-ExecutionPolicy | Should Be Unrestricted
}
It "Should return Microsoft.Powershell.ExecutionPolicy PSObject on Windows" -Skip:($IsLinux -Or $IsOSX) {
(Get-ExecutionPolicy).GetType() | Should Be Microsoft.Powershell.ExecutionPolicy
}
It "Should return Microsoft.Powershell.ExecutionPolicy PSObject on Windows" -Skip:($IsLinux -Or $IsOSX) {
(Get-ExecutionPolicy).GetType() | Should Be Microsoft.Powershell.ExecutionPolicy
}
}
Context "Check Set-ExecutionPolicy behavior" {
It "Should throw PlatformNotSupported when not on Windows" -Skip:$IsWindows {
{ Set-ExecutionPolicy Unrestricted } | Should Throw "Operation is not supported on this platform."
}
It "Should throw PlatformNotSupported when not on Windows" -Skip:$IsWindows {
{ Set-ExecutionPolicy Unrestricted } | Should Throw "Operation is not supported on this platform."
}
It "Should succeed on Windows" -Skip:($IsLinux -Or $IsOSX) {
# We use the Process scope to avoid affecting the system
# Unrestricted is assumed "safe", otherwise these tests would not be running
{ Set-ExecutionPolicy -Force -Scope Process -ExecutionPolicy Unrestricted } | Should Not Throw
}
It "Should succeed on Windows" -Skip:($IsLinux -Or $IsOSX) {
# We use the Process scope to avoid affecting the system
# Unrestricted is assumed "safe", otherwise these tests would not be running
{ Set-ExecutionPolicy -Force -Scope Process -ExecutionPolicy Unrestricted } | Should Not Throw
}
}
}

View file

@ -13,6 +13,16 @@ Describe "PSReadLine" {
$module.Version | Should Be "1.2"
}
It "Should use Emacs Bindings on Linux and OS X" -skip:$IsWindows {
(Get-PSReadLineOption).EditMode | Should Be Emacs
(Get-PSReadlineKeyHandler | where { $_.Key -eq "Ctrl+A" }).Function | Should Be BeginningOfLine
}
It "Should use Windows Bindings on Windows" -skip:(-not $IsWindows) {
(Get-PSReadLineOption).EditMode | Should Be Windows
(Get-PSReadlineKeyHandler | where { $_.Key -eq "Ctrl+A" }).Function | Should Be SelectAll
}
It "Should set the edit mode" {
Set-PSReadlineOption -EditMode Windows
(Get-PSReadlineKeyHandler | where { $_.Key -eq "Ctrl+A" }).Function | Should Be SelectAll

View file

@ -0,0 +1,753 @@
Describe "ParserTests (admin\monad\tests\monad\src\engine\core\ParserTests.cs)" {
BeforeAll {
$functionDefinitionFile = Join-Path -Path $TestDrive -ChildPath "functionDefinition.ps1"
$functionDefinition = @'
function testcmd-parserbvt
{
[CmdletBinding()]
param (
[Parameter(Position = 0)]
[string] $Property1 = "unset",
[Parameter(Position = 1)]
[string] $Property2 = "unset",
[Parameter(Position = 2)]
[string] $Property3 = "unset",
[Parameter()]
[ValidateSet("default","array","object","nestedobject","struct","mshobject","nulltostring")]
[string]$ReturnType = "default"
)
BEGIN {}
PROCESS {
if ( ! $ReturnType ) {
$ReturnType = "default"
}
switch ( $ReturnType )
{
"default" {
$result = "$Property1;$Property2;$Property3"
break
}
"array" {
$result = 1,2,3
break
}
"object" {
$result = new-object psobject
break
}
"nestedobject" {
$result = [pscustomobject]@{Name="John";Person=[pscustomobject]@{Name="John";Age=30}}
break
}
"struct" {
$result = [pscustomobject]@{Name="John";Age=30}
break
}
"mshobject" {
$result = new-object psobject
break
}
"nulltostring" {
$result = $null
break
}
default {
throw ([invalidoperationexception]::new("ReturnType parameter wasn't of any Expected value!"))
break
}
}
return $result
}
END {}
}
'@
$functionDefinition>$functionDefinitionFile
$PowerShell = [powershell]::Create()
function ExecuteCommand {
param ([string]$command)
try {
$PowerShell.AddScript(". $functionDefinitionFile").Invoke()
$PowerShell.AddScript($command).Invoke()
}
finally {
$PowerShell.Commands.Clear()
}
}
}
BeforeEach {
$testfile = Join-Path -Path $TestDrive -ChildPath "testfile.ps1"
$shellfile = Join-Path -Path $TestDrive -ChildPath "testfile.cmd"
$testfolder1 = Join-Path -Path $TestDrive -ChildPath "dir1"
$testfolder2 = Join-Path -Path $testfolder1 -ChildPath "dir2"
if(-not(Test-Path $testfolder1))
{
New-Item $testfolder1 -Type Directory
}
if(-not(Test-Path $testfolder2))
{
New-Item $testfolder2 -Type Directory
}
$testdirfile1 = Join-Path -Path $testfolder2 -ChildPath "testdirfile1.txt"
$testdirfile2 = Join-Path -Path $testfolder2 -ChildPath "testdirfile2.txt"
"">$testdirfile1
"">$testdirfile2
}
AfterEach {
$PowerShell.Commands.Clear()
if(Test-Path $testfile)
{
Remove-Item $testfile
}
if(Test-Path $shellfile)
{
Remove-Item $shellfile
}
if(Test-Path $testdirfile1)
{
Remove-Item $testdirfile1
}
if(Test-Path $testdirfile2)
{
Remove-Item $testdirfile2
}
if(Test-Path $testfolder2)
{
Remove-Item $testfolder2
}
if(Test-Path $testfolder1)
{
Remove-Item $testfolder1
}
}
AfterAll {
if(Test-Path $functionDefinitionFile)
{
Remove-Item $functionDefinitionFile
}
}
It "Throws a syntax error when parsing a string without a closing quote. (line 164)" {
try {
ExecuteCommand '"This is a test'
throw "Execution OK"
}
catch {
$_.FullyQualifiedErrorId | Should be "IncompleteParseException"
}
}
It "Throws an error if an open parenthesis is not closed (line 176)" {
try {
ExecuteCommand "("
throw "Execution OK"
}
catch {
$_.FullyQualifiedErrorId | should be "IncompleteParseException"
}
}
It "Throws an exception if the the first statement starts with an empty pipe element (line 188)" {
try {
ExecuteCommand "| get-location"
throw "Execution OK"
}
catch {
$_.FullyQualifiedErrorId | should be "ParseException"
}
}
It "Throws an CommandNotFoundException exception if using a label in front of an if statement is not allowed. (line 225)"{
ExecuteCommand ":foo if ($x -eq 3) { 1 }"
$PowerShell.HadErrors | should be $true
$PowerShell.Streams.Error.FullyQualifiedErrorId | should be "CommandNotFoundException"
}
It "Pipe an expression into a value expression. (line 237)" {
try {
ExecuteCommand "testcmd-parserbvt | 3"
throw "Execution OK"
}
catch {
$_.FullyQualifiedErrorId | should be "ParseException"
}
try {
ExecuteCommand "testcmd-parserbvt | $(1 + 1)"
throw "Execution OK"
}
catch {
$_.FullyQualifiedErrorId | should be "ParseException"
}
try {
ExecuteCommand "testcmd-parserbvt | 'abc'"
throw "Execution OK"
}
catch {
$_.FullyQualifiedErrorId | should be "ParseException"
}
}
It "Throws when you pipe into a value expression (line 238)" {
foreach($command in "1;2;3|3",'1;2;3|$(1+1)',"1;2;3|'abc'") {
try {
ExecuteCommand $command
throw "Execution OK"
}
catch {
$_.FullyQualifiedErrorId | Should be "ParseException"
}
}
}
It "Throws an incomplete parse exeception when a comma follows an expression (line 247)" {
try {
ExecuteCommand "(1+ 1),"
throw "Execution OK"
}
catch {
$_.FullyQualifiedErrorId | Should be "IncompleteParseException"
}
}
It "Test that invoke has a higher precedence for a script than for an executable. (line 279)" {
"1">$testfile
$result = ExecuteCommand ". $testfile"
$result | should be 1
}
It "This test will check that a path is correctly interpreted when using '..' and '.' (line 364)" {
$result = ExecuteCommand "set-location $TestDrive; get-childitem dir1\.\.\.\..\dir1\.\dir2\..\..\dir1\.\dir2"
$result.Count | should be 2
$result[0].Name | should be "testdirfile1.txt"
$result[1].Name | should be "testdirfile2.txt"
}
It "This test will check that the parser can handle a mix of forward slashes and back slashes in the path (line 417)" {
$result = ExecuteCommand "get-childitem $TestDrive/dir1/./.\.\../dir1/.\dir2\../..\dir1\.\dir2"
$result.Count | should be 2
$result[0].Name | should be "testdirfile1.txt"
$result[1].Name | should be "testdirfile2.txt"
}
It "This test checks that the asterisk globs as expected. (line 545)" {
$result = ExecuteCommand "get-childitem $TestDrive/dir1\dir2\*.txt"
$result.Count | should be 2
$result[0].Name | should be "testdirfile1.txt"
$result[1].Name | should be "testdirfile2.txt"
}
It "This test checks that we can use a range for globbing: [1-2] (line 557)" {
$result = ExecuteCommand "get-childitem $TestDrive/dir1\dir2\testdirfile[1-2].txt"
$result.Count | should be 2
$result[0].Name | should be "testdirfile1.txt"
$result[1].Name | should be "testdirfile2.txt"
}
It "This test will check that escaping the $ sigil inside single quotes simply returns the $ character. (line 583)" {
$result = ExecuteCommand "'`$'"
$result | should be "`$"
}
It "Test that escaping a space just returns that space. (line 593)" {
$result = ExecuteCommand '"foo` bar"'
$result | should be "foo bar"
}
It "Test that escaping any character with no special meaning just returns that char. (line 602)" {
$result = ExecuteCommand '"fo`obar"'
$result | should be "foobar"
}
Context "Test that we support all of the C# escape sequences. We use the ` instead of \. (line 613)" {
# the first two sequences are tricky, because we need to provide something to
# execute without causing an incomplete parse error
$tests = @{ sequence = "write-output ""`'"""; expected = ([char]39) },
@{ sequence = 'write-output "`""'; expected = ([char]34) },
# this is a string, of 2 "\", the initial backtick should essentially be ignored
@{ sequence = '"`\\"'; expected = '\\' },
# control sequences
@{ sequence = '"`0"'; expected = ([char]0) }, # null
@{ sequence = '"`a"'; expected = ([char]7) },
@{ sequence = '"`b"'; expected = ([char]8) }, # backspace
@{ sequence = '"`f"'; expected = ([char]12) }, # form
@{ sequence = '"`n"'; expected = ([char]10) }, # newline
@{ sequence = '"`r"'; expected = ([char]13) }, # return
@{ sequence = '"`t"'; expected = ([char]9) }, # tab
@{ sequence = '"`v"'; expected = ([char]11) }
It "C# escape sequence <sequence> is supported using `` instead of \. (line 613)" -TestCases $tests {
param ( $sequence, $expected )
$result = ExecuteCommand $sequence
$result | should be $expected
}
}
It "This test checks that array substitution occurs inside double quotes. (line 646)" {
$result = ExecuteCommand '$MyArray = "a","b";"Hello $MyArray"'
$result | should be "Hello a b"
}
It "This tests declaring an array in nested variable tables. (line 761)" {
$result = ExecuteCommand "`$Variable:vtbl1:vtbl2:b=@(5,6);`$Variable:vtbl1:vtbl2:b"
$result.Count | should be 2
$result[0] | should be 5
$result[1] | should be 6
}
It "Test a simple multiple assignment. (line 773)" {
$result = ExecuteCommand '$one,$two = 1,2,3; "One = $one"; "Two = $two"'
$result.Count | should be 2
$result[0] | should be "One = 1"
$result[1] | should be "Two = 2 3"
}
It "Tests script, global and local scopes from a function inside a script. (line 824)" {
"`$var = 'script';function func { `$var; `$var = 'local'; `$local:var; `$script:var; `$global:var };func;`$var;">$testfile
ExecuteCommand "`$var = 'global'"
$result = ExecuteCommand "$testfile"
$result.Count | should be 5
$result[0] | should be "script"
$result[1] | should be "local"
$result[2] | should be "script"
$result[3] | should be "global"
$result[4] | should be "script"
}
It "Use break inside of a loop that is inside another loop. (line 945)" {
$commands = " while (1) { 1; while(1) { 2; break; 3; }; 4; break; 5; } ",
" for (;;) { 1; for(;;) { 2; break; 3; }; 4; break; 5; } ",
" foreach(`$a in 1,2,3) { 1; foreach( `$b in 1,2,3 ) { 2; break; 3; }; 4; break; 5; } "
$results = "1", "2", "4"
$i = 0
for(;$i -lt $commands.Count;$i++)
{
$result = ExecuteCommand $commands[$i]
$result | should be $results
}
}
It "Use break in two loops with same label. (line 967)" {
$commands = " :foo while (1) { 1; :foo while(1) { 2; break foo; 3; }; 4; break; 5; } ",
" :foo for (;;) { 1; :foo for(;;) { 2; break foo; 3; }; 4; break; 5; } ",
" :foo foreach(`$a in 1,2,3) { 1; :foo foreach( `$b in 1,2,3 ) { 2; break foo; 3; }; 4; break; 5; } "
$results = "1", "2", "4"
$i = 0
for(;$i -lt $commands.Count;$i++)
{
$result = ExecuteCommand $commands[$i]
$result | should be $results
}
}
It "Try continue inside of different loop statements. (line 1039)" {
$commands = " `$a = 0; while (`$a -lt 2) { `$a; `$a += 1; continue; 2; } ",
" for (`$a = 0;`$a -lt 2; `$a += 1) { 9; continue; 3; } ",
" foreach(`$a in 0,1) { `$a; continue; 2; } "
$result = ExecuteCommand $commands[0]
$result | should be "0", "1"
$result = ExecuteCommand $commands[1]
$result | should be "9", "9"
$result = ExecuteCommand $commands[2]
$result | should be "0", "1"
}
It "Use a label to continue an inner loop. (line 1059)" {
$commands = " `$x = 0; while (`$x -lt 1) { `$x += 1; `$x; `$a = 0; :foo while(`$a -lt 2) { `$a += 1; `$a; continue foo; 3; }; 4; continue; 5; } ",
" for (`$x = 0;`$x -lt 1;`$x += 1) { 1; :foo for(`$a = 0; `$a -lt 2; `$a += 1) { `$a; continue foo; 3; }; 4; continue; 5; } ",
" foreach(`$a in 1) { 1; :foo foreach( `$b in 1,2 ) { `$b; continue foo; 3; }; 4; continue; 5; } "
$result = ExecuteCommand $commands[0]
$result | should be "1", "1", "2", "4"
$result = ExecuteCommand $commands[1]
$result | should be "1", "0", "1", "4"
$result = ExecuteCommand $commands[2]
$result | should be "1", "1", "2", "4"
}
It "Use continue with a label on a nested loop. (line 1059)" {
$commands = " `$x = 0; :foo while (`$x -lt 2) { `$x; `$x += 1; :bar while(1) { 2; continue foo; 3; }; 4; continue; 5; } ",
" :foo for (`$x = 0;`$x -lt 2;`$x += 1) { 1; :bar for(;;) { 2; continue foo; 3; }; 4; continue; 5; } ",
" :foo foreach(`$a in 1,2) { 1; :bar foreach( `$b in 1,2,3 ) { 2; continue foo; 3; }; 4; continue; 5; } "
$result = ExecuteCommand $commands[0]
$result | should be "0", "2", "1", "2"
$result = ExecuteCommand $commands[1]
$result | should be "1", "2", "1", "2"
$result = ExecuteCommand $commands[2]
$result | should be "1", "2", "1", "2"
}
It "This test will check that it is a syntax error to use if without a code block. (line 1141)" {
try {
ExecuteCommand 'if ("true")'
throw "Execution OK"
}
catch {
$_.FullyQualifiedErrorId | Should be "IncompleteParseException"
}
}
It "This test will check that it is a syntax error if the if condition is not complete. (line 1150)" {
try {
ExecuteCommand 'if ('
throw "Execution OK"
}
catch {
$_.FullyQualifiedErrorId | Should be "IncompleteParseException"
}
}
It "This test will check that it is a syntax error to have an if condition without parentheses. (line 1159)" {
try {
ExecuteCommand 'if "true" { 1} else {2}'
throw "Execution OK"
}
catch {
$_.FullyQualifiedErrorId | Should be "ParseException"
}
}
It "This test will check that the parser throws a syntax error when the if condition is missing the closing parentheses. (line 1168)" {
try {
ExecuteCommand 'if ("true" { 1};'
throw "Execution OK"
}
catch {
$_.FullyQualifiedErrorId | Should be "ParseException"
}
}
It "This test will check that it is a syntax error to have an else keyword without the corresponding code block. (line 1177)" {
try {
ExecuteCommand 'if ("true") {1} else'
throw "Execution OK"
}
catch {
$_.FullyQualifiedErrorId | Should be "IncompleteParseException"
}
}
It "This test will check that the parser throws a syntax error when a foreach loop is not complete. (line 1238)" {
try {
ExecuteCommand '$count=0;$files = $(get-childitem / -filter *.txt );foreach ($i ;$count'
throw "Execution OK"
}
catch {
$_.FullyQualifiedErrorId | Should be "ParseException"
}
}
It "This test will check that the parser throws a syntax error if the foreach loop is not complete. (line 1248)" {
try {
ExecuteCommand '$count=0;$files = $(get-childitem / -filter *.txt );foreach ($i in ;$count'
throw "Execution OK"
}
catch {
$_.FullyQualifiedErrorId | Should be "ParseException"
}
}
It "This will test that the parser throws a syntax error if the foreach loop is missing a closing parentheses. (line 1258)" {
try {
ExecuteCommand '$count=0;$files = $(get-childitem / -filter *.txt );foreach ($i in $files ;$count'
throw "Execution OK"
}
catch {
$_.FullyQualifiedErrorId | Should be "ParseException"
}
}
It "Test that if an exception is thrown from the try block it will be caught in the apprpropriate catch block and that the finally block will run regardless of whether an exception is thrown. (line 1317)" {
$result = ExecuteCommand 'try { try { throw (new-object System.ArgumentException) } catch [System.DivideByZeroException] { } finally { "Finally" } } catch { $_.Exception.GetType().FullName }'
$result | should be "Finally", "System.ArgumentException"
}
It "Test that null can be passed to a method that expects a reference type. (line 1439)" {
$result = ExecuteCommand '$test = "String";$test.CompareTo($())'
$result | should be 1
}
It "Tests that command expansion operators can be used as a parameter to an object method. (line 1507)" {
$result = ExecuteCommand '$test = "String";$test.SubString($("hello" | foreach-object { $_.length - 2 } ))'
$result | should be "ing"
}
It "Test that & can be used as a parameter as long as it is quoted. (line 1606)" {
$result = ExecuteCommand 'testcmd-parserbvt `&get-childitem'
$result | should be "&get-childitem;unset;unset"
$result = ExecuteCommand 'testcmd-parserbvt `&*'
$result | should be "&*;unset;unset"
}
It "Run a command with parameters. (line 1621)" {
$result = ExecuteCommand 'testcmd-parserBVT -Property1 set'
$result | should be "set;unset;unset"
}
It "Test that typing a number at the command line will return that number. (line 1630)" {
$result = ExecuteCommand '3'
$result | should be "3"
$result.gettype() |should be ([int])
}
It "This test will check that an msh script can be run without invoking. (line 1641)" {
"1">$testfile
$result = ExecuteCommand ". $testfile"
$result | should be 1
}
It "Test that an alias is resolved before a function. (line 1657)" {
$result = ExecuteCommand 'set-alias parserInvokeTest testcmd-parserBVT;function parserInvokeTest { 3 };parserInvokeTest'
$result | should be "unset;unset;unset"
}
It "Test that functions are resolved before cmdlets. (line 1678)"{
$result = ExecuteCommand 'function testcmd-parserBVT { 3 };testcmd-parserBVT'
$result | should be "3"
}
It "Check that a command that uses shell execute can be run from the command line and that no exception is thrown. (line 1702)" {
if ( $IsLinux -or $IsOSX ) {
# because we execute on *nix based on executable bit, and the file name doesn't matter
# so we can use the same filename as for windows, just make sure it's executable with chmod
"#!/bin/sh`necho ""Hello World""" | out-file -encoding ASCII $shellfile
/bin/chmod +x $shellfile
}
else {
"@echo Hello, I'm a Cmd script!">$shellfile
}
{ ExecuteCommand "$shellfile" } | Should Not Throw
}
Context "Boolean Tests (starting at line 1723 to line 1772)" {
$testData = @(
@{ Script = '"False"'; Expected = $true }
@{ Script = 'if ("A") { $true } else { $false }'; Expected = $true }
@{ Script = 'if (" ") { $true } else { $false }'; Expected = $true }
@{ Script = 'if ("String with spaces") { $true } else { $false }'; Expected = $true }
@{ Script = 'if ("DoubleQuoted") { $true } else { $false }'; Expected = $true }
@{ Script = 'if ("StringWithNullVar$aEmbedded") { $true } else { $false }'; Expected = $true }
@{ Script = 'if (0) { $true } else { $false }'; Expected = $false }
@{ Script = '$a = $(0);if ($a) { $true } else { $false }'; Expected = $false }
@{ Script = '$obj = testcmd-parserBVT -ReturnType object;if ($obj) { $true } else { $false }'; Expected = $true }
)
It "<Script> should return <Expected>" -TestCases $testData {
param ( $Script, $Expected )
ExecuteCommand $Script | Should be $Expected
}
}
Context "Comparison operator Tests (starting at line 1785 to line 1842)" {
$testData = @(
@{ Script = 'if (1 -and 1) { $true } else { $false }'; Expected = $true }
@{ Script = 'if (1 -and 0) { $true } else { $false }'; Expected = $false }
@{ Script = 'if (0 -and 1) { $true } else { $false }'; Expected = $false }
@{ Script = 'if (0 -and 0) { $true } else { $false }'; Expected = $false }
@{ Script = 'if (1 -or 1) { $true } else { $false }'; Expected = $true }
@{ Script = 'if (1 -or 0) { $true } else { $false }'; Expected = $true }
@{ Script = 'if (0 -or 1) { $true } else { $false }'; Expected = $true }
@{ Script = 'if (0 -or 0) { $true } else { $false }'; Expected = $false }
#-eq
@{ Script = 'if ($False -eq $True -and $False) { $true } else { $false }'; Expected = $false }
@{ Script = 'if ($False -and $True -eq $False) { $true } else { $false }'; Expected = $false }
#-ieq
@{ Script = 'if ($False -ieq $True -and $False) { $true } else { $false }'; Expected = $false }
@{ Script = 'if ($False -and $True -ieq $False) { $true } else { $false }'; Expected = $false }
#-le
@{ Script = 'if ($False -le $True -and $False) { $true } else { $false }'; Expected = $false }
@{ Script = 'if ($False -and $True -le $False) { $true } else { $false }'; Expected = $false }
#-ile
@{ Script = 'if ($False -ile $True -and $False) { $true } else { $false }'; Expected = $false }
@{ Script = 'if ($False -and $True -ile $False) { $true } else { $false }'; Expected = $false }
#-ge
@{ Script = 'if ($False -ge $True -and $False) { $true } else { $false }'; Expected = $false }
@{ Script = 'if ($False -and $True -ge $False) { $true } else { $false }'; Expected = $false }
#-ige
@{ Script = 'if ($False -ige $True -and $False) { $true } else { $false }'; Expected = $false }
@{ Script = 'if ($False -and $True -ige $False) { $true } else { $false }'; Expected = $false }
#-like
@{ Script = 'if ($False -like $True -and $False) { $true } else { $false }'; Expected = $false }
@{ Script = 'if ($False -and $True -like $False) { $true } else { $false }'; Expected = $false }
#!
@{ Script = 'if (!$True -and $False) { $true } else { $false }'; Expected = $false }
)
It "<Script> should return <Expected>" -TestCases $testData {
param ( $Script, $Expected )
ExecuteCommand $Script | Should be $Expected
}
}
Context "Arithmetic and String Comparison Tests (starting at line 1848 to line 1943)" {
$testData = @(
@{ Script = '$a=10; if( !$a -eq 10) { $a=1 } else {$a=2};$a'; Expected = 2 }
@{ Script = "!3"; Expected = $false }
@{ Script = '$a=10; if($a -lt 15) { $a=1 } else {$a=2};$a'; Expected = 1 }
@{ Script = "'aaa' -ilt 'AAA'"; Expected = $false }
@{ Script = "'aaa' -lt 'AAA'"; Expected = $false }
@{ Script = "'aaa' -clt 'AAA'"; Expected = $true }
@{ Script = '$a=10; if($a -gt 15) { $a=1 } else {$a=2};$a'; Expected = 2 }
@{ Script = "'AAA' -igt 'aaa'"; Expected = $false }
@{ Script = "'AAA' -gt 'aaa'"; Expected = $false }
@{ Script = "'AAA' -cgt 'aaa'"; Expected = $true }
@{ Script = '$a=10; if($a -ge 10) { $a=1 } else {$a=2};$a'; Expected = 1 }
@{ Script = "'aaa' -ge 'aaa'"; Expected = $true }
@{ Script = '$a=10; if($a -ne 10) { $a=1 } else {$a=2};$a'; Expected = 2 }
@{ Script = "'aaa' -ine 'AAA'"; Expected = $false }
@{ Script = "'aaa' -ne 'AAA'"; Expected = $false }
@{ Script = "'aaa' -cne 'AAA'"; Expected = $true }
@{ Script = '$a="abc"; if($a -ieq "ABC") { $a=1 } else {$a=2};$a'; Expected = 1 }
@{ Script = "'aaa' -ieq 'AAA'"; Expected = $true }
@{ Script = '$a="abc"; if($a -igt "bbc") { $a=1 } else {$a=2};$a'; Expected = 2 }
@{ Script = "'aaa' -igt 'AAA'"; Expected = $false }
@{ Script = '$a="abc"; if($a -ile "bbc") { $a=1 } else {$a=2};$a'; Expected = 1 }
@{ Script = "'aaa' -ile 'AAA'"; Expected = $true }
)
It "<Script> should return <Expected>" -TestCases $testData {
param ( $Script, $Expected )
ExecuteCommand $Script | Should be $Expected
}
}
Context "Comparison Operators with Arrays Tests (starting at line 2015 to line 2178)" {
$testData = @(
@{ Script = "@(3) -eq 3"; Expected = "3" }
@{ Script = "@(4) -eq 3"; Expected = $null }
@{ Script = "@() -eq 3"; Expected = $null }
@{ Script = '$test = 1,2,@(3,4);$test -eq 3'; Expected = $null }
@{ Script = '$test = 1,2,@(3,4);$test -eq 2'; Expected = "2" }
@{ Script = '$test = 1,2,@(3,4);$test -eq 0'; Expected = $null }
)
It "<Script> should return <Expected>" -TestCases $testData {
param ( $Script, $Expected )
ExecuteCommand $Script | Should be $Expected
}
}
It "A simple test for trapping a specific exception. Expected Result: The exception is caught and ignored. (line 2265)" {
{ ExecuteCommand "trap [InvalidCastException] { continue; }; [int] 'abc'" } | Should Not Throw
}
It "Test that assign to input var and use then execute a script block with piped input. (line 2297)"{
$result = ExecuteCommand '$input = 1,2,3;4,-5,6 | & { $input }'
$result -join "" | should be (4,-5,6 -join "")
}
It "Test that pipe objects into a script and use arguments. (line 2313)"{
"`$input; `$args;">$testfile
$result = ExecuteCommand "1,2,3 | $testfile"
$result -join "" | should be (1, 2, 3 -join "")
$result = ExecuteCommand "$testfile 4 -5 6 -blah -- foo -bar"
$result | should be "4", "-5", "6", "-blah", "foo", "-bar"
}
Context "Numerical Notations Tests (starting at line 2374 to line 2452)" {
$testData = @(
#Test various numbers using the standard notation.
@{ Script = "0"; Expected = "0" }
@{ Script = "-2"; Expected = "-2" }
@{ Script = "2"; Expected = "2" }
@{ Script = $([int32]::MaxValue); Expected = $([int32]::MaxValue) }
@{ Script = $([int32]::MinValue); Expected = $([int32]::MinValue) }
#Tests for hexadecimal notation.
@{ Script = "0x0"; Expected = "0" }
@{ Script = "0xF"; Expected = "15" }
@{ Script = "0x80000000"; Expected = $([int32]::MinValue) }
@{ Script = "0xFFFFFFFF"; Expected = "-1" }
@{ Script = "0x7fffffff"; Expected = $([int32]::MaxValue) }
@{ Script = "0x100000000"; Expected = [int64]0x100000000 }
#Tests for exponential notation.
@{ Script = "0e0"; Expected = "0" }
@{ Script = "0e1"; Expected = "0" }
@{ Script = "1e2"; Expected = "100" }
@{ Script = $([int32]::MaxValue); Expected = $([int32]::MaxValue) }
@{ Script = "0e2"; Expected = "0" }
@{ Script = "-2e2"; Expected = "-200" }
@{ Script = "-0e2"; Expected = "0" }
@{ Script = "3e0"; Expected = "3" }
#Tests for floating point notation.
@{ Script = ".01"; Expected = "0.01" }
@{ Script = "0.0"; Expected = "0" }
@{ Script = "-0.1"; Expected = "-0.1" }
@{ Script = "9.12"; Expected = "9.12" }
@{ Script = $([single]::MinValue); Expected = $([float]::MinValue).ToString() }
@{ Script = $([float]::MaxValue); Expected = $([float]::MaxValue).ToString() }
#Tests for the K suffix for numbers.
@{ Script = "0kb"; Expected = "0" }
@{ Script = "1kb"; Expected = "1024" }
@{ Script = "-2KB"; Expected = "-2048" }
)
It "<Script> should return <Expected>" -TestCases $testData {
param ( $Script, $Expected )
ExecuteCommand $Script | Should be $Expected
}
}
It "This is a simple test of the concatenation of two arrays. (line 2460)"{
$result = ExecuteCommand '1,2,3 + 4,5,6'
$result -join "" | should be (1, 2, 3, 4, 5, 6 -join "")
}
It "Test that an incomplete parse exception is thrown if the array is unfinished. (line 2473)"{
try {
ExecuteCommand '1,2,'
throw "Execution OK"
}
catch {
$_.FullyQualifiedErrorId | Should be "IncompleteParseException"
}
}
It "Test that the unary comma is not valid in cmdlet parameters. (line 2482)"{
try {
ExecuteCommand 'write-output 2,,1'
throw "Execution OK"
}
catch {
$_.FullyQualifiedErrorId | Should be "ParseException"
}
}
It 'Test that "$var:" will expand to nothing inside a string. (line 2551)'{
try {
ExecuteCommand '"$var:"'
throw "Execution OK"
}
catch {
$_.FullyQualifiedErrorId | Should be "ParseException"
}
}
It "Tests the assignment to a read-only property (line 2593)"{
$result = ExecuteCommand '$A=$(testcmd-parserBVT -returntype array); $A.rank =5;$A.rank'
$result | Should be "1"
}
It "Newlines are allowed after operators (line 3033)" {
$result = ExecuteCommand "(20 %
6 *
4 +
2) /
2 -
3"
$result | should be 2
}
It "A here string must have one line (line 3266)" {
try {
ExecuteCommand "@`"`"@"
throw "Execution OK"
}
catch {
$_.FullyQualifiedErrorId | should be "ParseException"
}
}
}