Skip to content

Instantly share code, notes, and snippets.

@Luiz-Monad
Created October 25, 2022 23:59
Show Gist options
  • Save Luiz-Monad/d5aea290087a89c070da6eec84b33742 to your computer and use it in GitHub Desktop.
Save Luiz-Monad/d5aea290087a89c070da6eec84b33742 to your computer and use it in GitHub Desktop.
how-to-normalize-a-path-in-powershell
# Clear-Host
Import-Module Pester
# New-Item -ItemType Directory 'a/b' -Force -ErrorAction Ignore
# New-Item -ItemType Directory 'c' -Force -ErrorAction Ignore
# New-SmbShare -Name "Shared" -Path "./shared" -FullAccess "Everyone"
$ErrorActionPreference = 'Stop'
$PesterPreference = [PesterConfiguration]::Default
$PesterPreference.Output.StackTraceVerbosity = 'None'
$PesterPreference.Output.Verbosity = 'Detailed'
$PesterPreference.Should.ErrorAction = 'Continue'
function Load-NormalizedPath { $false }
function Get-NormalizedPath { $null }
function tests {
context "cwd" {
it 'has no external libraries' {
Load-NormalizedPath
}
it 'barely work for FileInfos on existing paths' {
Get-NormalizedPath 'a\..\c' | should -be 'c'
}
it 'process .. and . (relative paths)' {
Get-NormalizedPath 'a\b\..\..\c\.' | should -be 'c'
}
it 'must support powershell providers' {
Get-NormalizedPath "FileSystem::\\$env:COMPUTERNAME\Shared\a\..\c" | should -be "FileSystem::\\$env:COMPUTERNAME\Shared\c"
}
it 'must support powershell drives' {
Get-NormalizedPath 'HKLM:\Software\Classes\.exe\..\.dll' | should -be 'HKLM:\Software\Classes\.dll'
}
it 'works with non-existant paths' {
Get-NormalizedPath 'fred\frog\..\frag\.' | should -be 'fred\frag'
}
it 'works with non-existant drives' {
Get-NormalizedPath 'U:\fred\frog\..\frag\.' | should -be 'U:\fred\frag'
}
it 'barely work for direct UNCs' {
Get-NormalizedPath "\\$env:COMPUTERNAME\Shared\a\..\c" | should -be "\\$env:COMPUTERNAME\Shared\c"
}
}
context "reroot" {
it 'doesn`t reroot subdir' {
Get-NormalizedPath 'fred\frog\..\frag\.' | should -be 'fred\frag'
}
it 'doesn`t reroot local' {
Get-NormalizedPath '.\fred\frog\..\frag\.' | should -be 'fred\frag'
}
it 'doesn`t reroot parent' {
Get-NormalizedPath "..\$((Get-Item .).Name)\fred\frog\..\frag\." | should -be 'fred\frag'
}
}
context "drive root" {
beforeEach { Push-Location 'c:/' }
it 'works on drive root' {
Get-NormalizedPath 'fred\frog\..\..\fred\frag\' | should -be 'fred\frag\'
}
afterEach { Pop-Location }
}
context "temp drive" {
beforeEach { New-PSDrive -Name temp -PSProvider FileSystem 'b:/tools' }
it 'works on temp drive' {
Get-NormalizedPath 'fred\frog\..\..\fred\frag\' | should -be 'fred\frag\'
}
it 'works on temp drive with absolute path' {
Get-NormalizedPath 'temp:\fred\frog\..\..\fred\frag\' | should -be 'temp:\fred\frag\'
}
afterEach { Remove-PSDrive -Name temp }
}
context "unc drive" {
beforeEach { Push-Location "FileSystem::\\$env:COMPUTERNAME\Shared\" }
it 'works on unc drive' {
Get-NormalizedPath 'fred\frog\..\..\fred\frag\' | should -be 'fred\frag\'
}
afterEach { Pop-Location }
}
}
function exception($ex) {
if (($exi = $ex.InnerException)) {
exception($exi)
} elseif (($exe = $ex.Exception)) {
exception($exe)
} elseif (($exm = $ex.Message)) {
$exm
} else {
"$ex"
}
}
$esc = '``````'
$sep = '--------------------------------------------------------------------------------'
$trail = "$esc`n$sep`nTesting "
$header = "$esc"
function title ($str) { "$str`n$esc`n$sep`n$sep`n# Test $str`n" }
function code-block ($blk) { "$($esc)powershell$blk$esc" }
Import-Module .\base_test.ps1
# combine_pwd_then_getfullpath
describe @"
$(title combine_pwd_then_getfullpath)
Based on Get-AbsolutePath, but works with both absolute and relative paths.
https://stackoverflow.com/a/13847304/1964796
$(code-block {
$path = [System.IO.Path]::Combine($pwd.Path, $path);
$path = [System.IO.Path]::GetFullPath($path)
$path.Replace($pwd.Path, '').TrimStart('\').TrimStart('/')
})
> pros: produces correct escaping and normalization, simple and safe.
> cons: still requires `$pwd, uses dotnet path function directly perhaps a con.
"@ {
beforeAll {
mock Load-NormalizedPath {
$true
}
mock Get-NormalizedPath {
param($path)
try {
$path = [System.IO.Path]::Combine($pwd.Path, $path);
$path = [System.IO.Path]::GetFullPath($path)
$path.Replace($pwd.Path, '').TrimStart('\').TrimStart('/')
} catch { exception($_) }
}
}
tests
}
Import-Module .\base_test.ps1
# join_pwd_then_getfullpath
describe @"
$(title join_pwd_then_getfullpath)
This looks like a bodge, it seemed like it wouldn't work, but it does (almost).
https://stackoverflow.com/a/13847304/1964796
$(code-block {
$path = Join-Path $pwd $path
$path = [System.IO.Path]::GetFullPath($path)
$path.Substring($pwd.Path.Trim('\').Trim('/').Length + 1)
})
> pros: produces correct escaping and normalization.
> cons: uses `$pwd.
"@ {
beforeAll {
mock Load-NormalizedPath {
$true
}
mock Get-NormalizedPath {
param($path)
try {
$path = Join-Path $pwd $path
$path = [System.IO.Path]::GetFullPath($path)
$path.Substring($pwd.Path.Trim('\').Trim('/').Length + 1)
} catch { exception($_) }
}
}
tests
}
Import-Module .\base_test.ps1
# location_aware_provider_path_from_pspath
describe @"
$(title location_aware_provider_path_from_pspath)
Testing how the path stack reacts.
https://stackoverflow.com/a/4982137/1964796
$(code-block {
$norm_path = $path
# strip out provider
$path_prov_drive = [ref] $null
$path_prov_abs = $ExecutionContext.SessionState.Path.IsPSAbsolute($norm_path, $path_prov_drive)
$path_prov = $ExecutionContext.SessionState.Path.IsProviderQualified($norm_path)
if ($path_prov) { $norm_path = $norm_path.Substring($path_prov_drive.Value.Length + 4) }
# strip out drive
$path_abs_drive = [ref] $null
$path_abs = $ExecutionContext.SessionState.Path.IsPSAbsolute($norm_path, $path_abs_drive)
if ($path_abs) { $norm_path = $norm_path.Substring($path_abs_drive.Value.Length + 2) }
# strip out UNC
$path_direct = $norm_path.StartsWith('//') -or $norm_path.StartsWith('\\')
if ($path_direct) { $norm_path = $norm_path.Substring(2) }
try {
$ExecutionContext.SessionState.Path.PushCurrentLocation("normalizing")
$drive = (Get-PSDrive -PSProvider FileSystem)[0]
$drive = $ExecutionContext.SessionState.Path.Combine($drive.Root, $drive.CurrentLocation)
$null = $ExecutionContext.SessionState.Path.SetLocation($drive)
$norm_path = $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($norm_path)
$norm_path = $norm_path.Substring($drive.Length + 1)
} finally {
$null = $ExecutionContext.SessionState.Path.PopLocation("normalizing")
}
# then add back the UNC if any
if ($path_direct) {
$norm_path = $pwd.Provider.ItemSeparator + $pwd.Provider.ItemSeparator + $norm_path
}
# then add back the provider if any
elseif ($path_prov) {
$norm_path = $ExecutionContext.SessionState.Path.Combine($path_prov_drive.Value + ':://', $norm_path)
}
# or add back the drive if any
elseif ($path_abs) {
$norm_path = $ExecutionContext.SessionState.Path.Combine($path_abs_drive.Value + ':', $norm_path)
}
$norm_path
})
> pros: doesn't use the dotnet path function, uses proper powershell infrastructure.
> cons: kind of complex. Uses the path stack.
"@ {
beforeAll {
mock Load-NormalizedPath {
$true
}
mock Get-NormalizedPath {
param($path)
try {
$norm_path = $path
# strip out provider
$path_prov_drive = [ref] $null
$path_prov_abs = $ExecutionContext.SessionState.Path.IsPSAbsolute($norm_path, $path_prov_drive)
$path_prov = $ExecutionContext.SessionState.Path.IsProviderQualified($norm_path)
if ($path_prov) { $norm_path = $norm_path.Substring($path_prov_drive.Value.Length + 4) }
# strip out drive
$path_abs_drive = [ref] $null
$path_abs = $ExecutionContext.SessionState.Path.IsPSAbsolute($norm_path, $path_abs_drive)
if ($path_abs) { $norm_path = $norm_path.Substring($path_abs_drive.Value.Length + 2) }
# strip out UNC
$path_direct = $norm_path.StartsWith('//') -or $norm_path.StartsWith('\\')
if ($path_direct) { $norm_path = $norm_path.Substring(2) }
try {
$ExecutionContext.SessionState.Path.PushCurrentLocation("normalizing")
$drive = (Get-PSDrive -PSProvider FileSystem)[0]
$drive = $ExecutionContext.SessionState.Path.Combine($drive.Root, $drive.CurrentLocation)
$null = $ExecutionContext.SessionState.Path.SetLocation($drive)
$norm_path = $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($norm_path)
$norm_path = $norm_path.Substring($drive.Length + 1)
} finally {
$null = $ExecutionContext.SessionState.Path.PopLocation("normalizing")
}
# then add back the UNC if any
if ($path_direct) {
$norm_path = $pwd.Provider.ItemSeparator + $pwd.Provider.ItemSeparator + $norm_path
}
# then add back the provider if any
elseif ($path_prov) {
$norm_path = $ExecutionContext.SessionState.Path.Combine($path_prov_drive.Value + ':://', $norm_path)
}
# or add back the drive if any
elseif ($path_abs) {
$norm_path = $ExecutionContext.SessionState.Path.Combine($path_abs_drive.Value + ':', $norm_path)
}
$norm_path
} catch { exception($_) }
}
}
tests
}


PS B:\test> invoke-pester -path ./*.ps1
Pester v5.3.3

Starting discovery in 13 files.
Discovery found 195 tests in 131ms.
Running tests.



Test combine_pwd_then_getfullpath

Running tests from 'B:\test\getfullpath_combine_pwd.tests.ps1' Describing combine_pwd_then_getfullpath

Based on Get-AbsolutePath, but works with both absolute and relative paths.
https://stackoverflow.com/a/13847304/1964796

    $path = [System.IO.Path]::Combine($pwd.Path, $path);
    $path = [System.IO.Path]::GetFullPath($path)
    $path.Replace($pwd.Path, '').TrimStart('\').TrimStart('/')

pros: produces correct escaping and normalization, simple and safe.
cons: still requires $pwd, uses dotnet path function directly perhaps a con.

 Context cwd
   [+] has no external libraries 3ms (1ms|1ms)
   [+] barely work for FileInfos on existing paths 2ms (2ms|0ms)
   [+] process .. and . (relative paths) 12ms (12ms|0ms)
   [-] must support powershell providers 3ms (3ms|0ms)
    Expected strings to be the same, but they were different.
    Expected length: 32
    Actual length:   31
    Strings differ at index 13.
    Expected: 'FileSystem::\\LUIZMONAD\Shared\c'
    But was:  'FileSystem::\LUIZMONAD\Shared\c'
               -------------^
   [+] must support powershell drives 3ms (2ms|1ms)
   [+] works with non-existant paths 3ms (2ms|1ms)
   [+] works with non-existant drives 3ms (2ms|1ms)
   [-] barely work for direct UNCs 3ms (3ms|1ms)
    Expected strings to be the same, but they were different.
    Expected length: 20
    Actual length:   18
    Strings differ at index 0.
    Expected: '\\LUIZMONAD\Shared\c'
    But was:  'LUIZMONAD\Shared\c'
               ^
 Context reroot
   [+] doesn`t reroot subdir 12ms (11ms|1ms)
   [+] doesn`t reroot local 3ms (2ms|0ms)
   [+] doesn`t reroot parent 3ms (2ms|1ms)
 Context drive root
   [+] works on drive root 4ms (3ms|1ms)
 Context temp drive
   [+] works on temp drive 4ms (3ms|1ms)
   [+] works on temp drive with absolute path 3ms (2ms|0ms)
 Context unc drive
   [-] works on unc drive 19ms (18ms|1ms)
    Expected strings to be the same, but they were different.
    Expected length: 10
    Actual length:   74
    Strings differ at index 0.
    Expected: 'fred\frag\'
    But was:  'B:\test\Microsoft.PowerShell.Core\FileSystem::\LUIZMONAD\Shared\fred\frag\'
               ^


Test join_pwd_then_getfullpath

Running tests from 'B:\test\getfullpath_join_pwd.tests.ps1' Describing join_pwd_then_getfullpath

This looks like a bodge, it seemed like it wouldn't work, but it does (almost).
https://stackoverflow.com/a/13847304/1964796

    $path = Join-Path $pwd $path
    $path = [System.IO.Path]::GetFullPath($path)
    $path.Substring($pwd.Path.Trim('\').Trim('/').Length + 1)

pros: produces correct escaping and normalization.
cons: uses $pwd.

 Context cwd
   [+] has no external libraries 5ms (1ms|3ms)
   [+] barely work for FileInfos on existing paths 13ms (13ms|0ms)
   [+] process .. and . (relative paths) 3ms (2ms|1ms)
   [-] must support powershell providers 3ms (3ms|1ms)
    Expected strings to be the same, but they were different.
    Expected length: 32
    Actual length:   31
    Strings differ at index 13.
    Expected: 'FileSystem::\\LUIZMONAD\Shared\c'
    But was:  'FileSystem::\LUIZMONAD\Shared\c'
               -------------^
   [+] must support powershell drives 3ms (2ms|1ms)
   [+] works with non-existant paths 3ms (2ms|0ms)
   [+] works with non-existant drives 2ms (2ms|0ms)
   [-] barely work for direct UNCs 3ms (3ms|0ms)
    Expected strings to be the same, but they were different.
    Expected length: 20
    Actual length:   18
    Strings differ at index 0.
    Expected: '\\LUIZMONAD\Shared\c'
    But was:  'LUIZMONAD\Shared\c'
               ^
 Context reroot
   [+] doesn`t reroot subdir 3ms (2ms|1ms)
   [+] doesn`t reroot local 3ms (2ms|0ms)
   [+] doesn`t reroot parent 3ms (2ms|0ms)
 Context drive root
   [+] works on drive root 5ms (4ms|1ms)
 Context temp drive
   [+] works on temp drive 4ms (3ms|1ms)
   [+] works on temp drive with absolute path 3ms (3ms|0ms)
 Context unc drive
   [-] works on unc drive 6ms (5ms|1ms)
    Expected strings to be the same, but they were different.
    Expected length: 10
    Actual length:   17
    Strings differ at index 0.
    Expected: 'fred\frag\'
    But was:  'Shared\fred\frag\'
               ^


Test only_join_path_resolve

Running tests from 'B:\test\join_path_resolve.tests.ps1' Describing only_join_path_resolve

Using only join-path with -resolve
https://stackoverflow.com/a/72411499/1964796

    Join-Path $path '' -Resolve

pros: its simple and small and wrong.
cons: doesn`t support non-existing paths at all, only works for absolute paths.

 Context cwd
   [+] has no external libraries 15ms (1ms|13ms)
   [-] barely work for FileInfos on existing paths 4ms (3ms|0ms)
    Expected strings to be the same, but they were different.
    Expected length: 1
    Actual length:   10
    Strings differ at index 0.
    Expected: 'c'
    But was:  'B:\test\c\'
               ^
   [-] process .. and . (relative paths) 3ms (3ms|1ms)
    Expected strings to be the same, but they were different.
    Expected length: 1
    Actual length:   10
    Strings differ at index 0.
    Expected: 'c'
    But was:  'B:\test\c\'
               ^
   [-] must support powershell providers 4ms (4ms|1ms)
    Expected strings to be the same, but they were different.
    Expected length: 32
    Actual length:   72
    Strings differ at index 0.
    Expected: 'FileSystem::\\LUIZMONAD\Shared\c'
    But was:  'Cannot find path '\\LUIZMONAD\Shared\a\..\c\' because it does not exist.'
               ^
   [-] must support powershell drives 180ms (179ms|1ms)
    Expected strings to be the same, but they were different.
    Expected length: 27
    Actual length:   28
    Strings differ at index 27.
    Expected: 'HKLM:\Software\Classes\.dll'
    But was:  'HKLM:\Software\Classes\.dll\'
               ---------------------------^
   [-] works with non-existant paths 4ms (3ms|1ms)
    Expected strings to be the same, but they were different.
    Expected length: 9
    Actual length:   64
    Strings differ at index 0.
    Expected: 'fred\frag'
    But was:  'Cannot find path 'B:\test\fred\frag\' because it does not exist.'
               ^
   [-] works with non-existant drives 4ms (3ms|1ms)
    Expected strings to be the same, but they were different.
    Expected length: 12
    Actual length:   60
    Strings differ at index 0.
    Expected: 'U:\fred\frag'
    But was:  'Cannot find drive. A drive with the name 'U' does not exist.'
               ^
   [-] barely work for direct UNCs 13ms (13ms|1ms)
    Expected strings to be the same, but they were different.
    Expected length: 20
    Actual length:   72
    Strings differ at index 0.
    Expected: '\\LUIZMONAD\Shared\c'
    But was:  'Cannot find path '\\LUIZMONAD\Shared\a\..\c\' because it does not exist.'
               ^
 Context reroot
   [-] doesn`t reroot subdir 4ms (3ms|1ms)
    Expected strings to be the same, but they were different.
    Expected length: 9
    Actual length:   64
    Strings differ at index 0.
    Expected: 'fred\frag'
    But was:  'Cannot find path 'B:\test\fred\frag\' because it does not exist.'
               ^
   [-] doesn`t reroot local 4ms (3ms|1ms)
    Expected strings to be the same, but they were different.
    Expected length: 9
    Actual length:   64
    Strings differ at index 0.
    Expected: 'fred\frag'
    But was:  'Cannot find path 'B:\test\fred\frag\' because it does not exist.'
               ^
   [-] doesn`t reroot parent 4ms (4ms|1ms)
    Expected strings to be the same, but they were different.
    Expected length: 9
    Actual length:   64
    Strings differ at index 0.
    Expected: 'fred\frag'
    But was:  'Cannot find path 'B:\test\fred\frag\' because it does not exist.'
               ^
 Context drive root
   [-] works on drive root 21ms (20ms|1ms)
    Expected strings to be the same, but they were different.
    Expected length: 10
    Actual length:   59
    Strings differ at index 0.
    Expected: 'fred\frag\'
    But was:  'Cannot find path 'C:\fred\frag\' because it does not exist.'
               ^
 Context temp drive
   [-] works on temp drive 5ms (4ms|1ms)
    Expected strings to be the same, but they were different.
    Expected length: 10
    Actual length:   64
    Strings differ at index 0.
    Expected: 'fred\frag\'
    But was:  'Cannot find path 'B:\test\fred\frag\' because it does not exist.'
               ^
   [-] works on temp drive with absolute path 5ms (4ms|1ms)
    Expected strings to be the same, but they were different.
    Expected length: 16
    Actual length:   62
    Strings differ at index 0.
    Expected: 'temp:\fred\frag\'
    But was:  'Cannot find path 'temp:\fred\frag\' because it does not exist.'
               ^
 Context unc drive
   [-] works on unc drive 7ms (6ms|1ms)
    Expected strings to be the same, but they were different.
    Expected length: 10
    Actual length:   75
    Strings differ at index 0.
    Expected: 'fred\frag\'
    But was:  'Cannot find path '\\LUIZMONAD\Shared\fred\frag\' because it does not exist.'
               ^


Test normalize_relative_path

Running tests from 'B:\test\normalize_relative_path.tests.ps1' Describing normalize_relative_path

Doesn`t work, just testing what this function does.

    $path_drive = [ref] $null
    $path_abs = $ExecutionContext.SessionState.Path.IsPSAbsolute($path, $path_drive)
    $norm_path = Split-Path $path -NoQualifier
    $ExecutionContext.SessionState.Path.PushCurrentLocation("normalizing")
    try {
        $path_drive = $(if ($path_abs) { Join-Path $path_drive.Value "/" } else { $pwd.Path })
        $null = $ExecutionContext.SessionState.Path.SetLocation($path_drive)
        $norm_path = $ExecutionContext.SessionState.Path.NormalizeRelativePath($norm_path, $pwd.Path)
    } finally {
        $null = $ExecutionContext.SessionState.Path.PopLocation("normalizing")
    }
    if ($path_abs) {
        Join-Path $path_drive $norm_path
    } else {
        $norm_path
    }

pros: none.
cons: doesn`t work with invalid paths.

 Context cwd
   [+] has no external libraries 6ms (1ms|4ms)
   [+] barely work for FileInfos on existing paths 4ms (3ms|1ms)
   [+] process .. and . (relative paths) 14ms (13ms|1ms)
   [-] must support powershell providers 5ms (4ms|1ms)
    Expected strings to be the same, but they were different.
    Expected length: 32
    Actual length:   65
    Strings differ at index 0.
    Expected: 'FileSystem::\\LUIZMONAD\Shared\c'
    But was:  'Cannot find path 'B:\test\FileSystem\' because it does not exist.'
               ^
   [-] must support powershell drives 4ms (4ms|1ms)
    Expected strings to be the same, but they were different.
    Expected length: 27
    Actual length:   59
    Strings differ at index 0.
    Expected: 'HKLM:\Software\Classes\.dll'
    But was:  'Cannot find path 'B:\test\HKLM\' because it does not exist.'
               ^
   [-] works with non-existant paths 5ms (4ms|1ms)
    Expected strings to be the same, but they were different.
    Expected length: 9
    Actual length:   65
    Strings differ at index 0.
    Expected: 'fred\frag'
    But was:  'An object at the specified path B:\test\fred\frag does not exist.'
               ^
   [-] works with non-existant drives 4ms (4ms|1ms)
    Expected strings to be the same, but they were different.
    Expected length: 12
    Actual length:   56
    Strings differ at index 0.
    Expected: 'U:\fred\frag'
    But was:  'Cannot find path 'B:\test\U\' because it does not exist.'
               ^
   [-] barely work for direct UNCs 15ms (15ms|1ms)
    Expected strings to be the same, but they were different.
    Expected length: 20
    Actual length:   68
    Strings differ at index 0.
    Expected: '\\LUIZMONAD\Shared\c'
    But was:  'An object at the specified path \\LUIZMONAD\Shared\c does not exist.'
               ^
 Context reroot
   [-] doesn`t reroot subdir 5ms (4ms|1ms)
    Expected strings to be the same, but they were different.
    Expected length: 9
    Actual length:   65
    Strings differ at index 0.
    Expected: 'fred\frag'
    But was:  'An object at the specified path B:\test\fred\frag does not exist.'
               ^
   [-] doesn`t reroot local 5ms (4ms|1ms)
    Expected strings to be the same, but they were different.
    Expected length: 9
    Actual length:   65
    Strings differ at index 0.
    Expected: 'fred\frag'
    But was:  'An object at the specified path B:\test\fred\frag does not exist.'
               ^
   [-] doesn`t reroot parent 5ms (4ms|1ms)
    Expected strings to be the same, but they were different.
    Expected length: 9
    Actual length:   65
    Strings differ at index 0.
    Expected: 'fred\frag'
    But was:  'An object at the specified path B:\test\fred\frag does not exist.'
               ^
 Context drive root
   [-] works on drive root 15ms (14ms|1ms)
    Expected strings to be the same, but they were different.
    Expected length: 10
    Actual length:   60
    Strings differ at index 0.
    Expected: 'fred\frag\'
    But was:  'An object at the specified path C:\fred\frag does not exist.'
               ^
 Context temp drive
   [-] works on temp drive 6ms (5ms|1ms)
    Expected strings to be the same, but they were different.
    Expected length: 10
    Actual length:   65
    Strings differ at index 0.
    Expected: 'fred\frag\'
    But was:  'An object at the specified path B:\test\fred\frag does not exist.'
               ^
   [-] works on temp drive with absolute path 5ms (4ms|1ms)
    Expected strings to be the same, but they were different.
    Expected length: 16
    Actual length:   59
    Strings differ at index 0.
    Expected: 'temp:\fred\frag\'
    But was:  'Cannot find path 'B:\test\temp\' because it does not exist.'
               ^
 Context unc drive
   [-] works on unc drive 10ms (9ms|1ms)
    Expected strings to be the same, but they were different.
    Expected length: 10
    Actual length:   9
    Strings differ at index 0.
    Expected: 'fred\frag\'
    But was:  '..\..\..\'
               ^


Test location_aware_provider_path_from_pspath

Running tests from 'B:\test\provider_path_from_pspath_location_aware.tests.ps1' Describing location_aware_provider_path_from_pspath

Testing how the path stack reacts.
https://stackoverflow.com/a/4982137/1964796

    $norm_path = $path
    # strip out provider
    $path_prov_drive = [ref] $null
    $path_prov_abs = $ExecutionContext.SessionState.Path.IsPSAbsolute($norm_path, $path_prov_drive)
    $path_prov = $ExecutionContext.SessionState.Path.IsProviderQualified($norm_path)
    if ($path_prov) { $norm_path = $norm_path.Substring($path_prov_drive.Value.Length + 4) }
    # strip out drive
    $path_abs_drive = [ref] $null
    $path_abs = $ExecutionContext.SessionState.Path.IsPSAbsolute($norm_path, $path_abs_drive)
    if ($path_abs) { $norm_path = $norm_path.Substring($path_abs_drive.Value.Length + 2) }
    # strip out UNC
    $path_direct = $norm_path.StartsWith('//') -or $norm_path.StartsWith('\\')
    if ($path_direct) { $norm_path = $norm_path.Substring(2) }
    try {
        $ExecutionContext.SessionState.Path.PushCurrentLocation("normalizing")
        $drive = (Get-PSDrive -PSProvider FileSystem)[0]
        $drive = $ExecutionContext.SessionState.Path.Combine($drive.Root, $drive.CurrentLocation)
        $null = $ExecutionContext.SessionState.Path.SetLocation($drive)
        $norm_path = $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($norm_path)
        $norm_path = $norm_path.Substring($drive.Length + 1)
    } finally {
        $null = $ExecutionContext.SessionState.Path.PopLocation("normalizing")
    }
    # then add back the UNC if any
    if ($path_direct) {
        $norm_path = $pwd.Provider.ItemSeparator + $pwd.Provider.ItemSeparator + $norm_path
    }
    # then add back the provider if any
    elseif ($path_prov) {
        $norm_path = $ExecutionContext.SessionState.Path.Combine($path_prov_drive.Value + ':://', $norm_path)
    }
    # or add back the drive if any
    elseif ($path_abs) {
        $norm_path = $ExecutionContext.SessionState.Path.Combine($path_abs_drive.Value + ':', $norm_path)
    }
    $norm_path

pros: doesn`t use the dotnet path function, uses proper powershell infrastructure.
cons: kind of complex. Uses the path stack.

 Context cwd
   [+] has no external libraries 7ms (1ms|6ms)
   [+] barely work for FileInfos on existing paths 18ms (17ms|0ms)
   [+] process .. and . (relative paths) 7ms (7ms|1ms)
   [+] must support powershell providers 7ms (6ms|1ms)
   [+] must support powershell drives 7ms (6ms|1ms)
   [+] works with non-existant paths 7ms (6ms|0ms)
   [+] works with non-existant drives 16ms (15ms|1ms)
   [+] barely work for direct UNCs 7ms (7ms|1ms)
 Context reroot
   [+] doesn`t reroot subdir 8ms (7ms|1ms)
   [+] doesn`t reroot local 7ms (6ms|1ms)
   [+] doesn`t reroot parent 17ms (16ms|1ms)
 Context drive root
   [+] works on drive root 8ms (7ms|1ms)
 Context temp drive
   [+] works on temp drive 9ms (8ms|1ms)
   [+] works on temp drive with absolute path 8ms (7ms|1ms)
 Context unc drive
   [+] works on unc drive 12ms (11ms|1ms)


Test only_unresolved_provider_path_from_pspath

Running tests from 'B:\test\provider_path_from_pspath_only_unresolved.tests.ps1' Describing only_unresolved_provider_path_from_pspath

This seems like the correct way, but needs boilerplate.
https://stackoverflow.com/a/52157943/1964796

    $path = Join-Path '/' $path
    $path = $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($path)
    $path = $path.Replace($pwd.Path, '')
    if ($pwd.Drive.Root) { $path = $path.Replace($pwd.Drive.Root, '') }
    $path

pros: simple.
cons: needs boilerplate to make it correct, doesn`t work with other providers or non-ex drives.

 Context cwd
   [+] has no external libraries 15ms (2ms|14ms)
   [+] barely work for FileInfos on existing paths 3ms (2ms|1ms)
   [+] process .. and . (relative paths) 3ms (2ms|1ms)
   [-] must support powershell providers 4ms (3ms|1ms)
    Expected strings to be the same, but they were different.
    Expected length: 32
    Actual length:   25
    Strings differ at index 0.
    Expected: 'FileSystem::\\LUIZMONAD\Shared\c'
    But was:  '\\LUIZMONAD\Shared\a\..\c'
               ^
   [-] must support powershell drives 4ms (3ms|1ms)
    Expected strings to be the same, but they were different.
    Expected length: 27
    Actual length:   64
    Strings differ at index 0.
    Expected: 'HKLM:\Software\Classes\.dll'
    But was:  'Cannot find drive. A drive with the name '\HKLM' does not exist.'
               ^
   [+] works with non-existant paths 3ms (2ms|1ms)
   [-] works with non-existant drives 4ms (3ms|1ms)
    Expected strings to be the same, but they were different.
    Expected length: 12
    Actual length:   61
    Strings differ at index 0.
    Expected: 'U:\fred\frag'
    But was:  'Cannot find drive. A drive with the name '\U' does not exist.'
               ^
   [-] barely work for direct UNCs 4ms (3ms|1ms)
    Expected strings to be the same, but they were different.
    Expected length: 20
    Actual length:   25
    Strings differ at index 19.
    Expected: '\\LUIZMONAD\Shared\c'
    But was:  '\\LUIZMONAD\Shared\a\..\c'
               -------------------^
 Context reroot
   [+] doesn`t reroot subdir 3ms (2ms|1ms)
   [+] doesn`t reroot local 3ms (2ms|0ms)
   [-] doesn`t reroot parent 4ms (3ms|0ms)
    Expected strings to be the same, but they were different.
    Expected length: 9
    Actual length:   10
    Strings differ at index 0.
    Expected: 'fred\frag'
    But was:  '\fred\frag'
               ^
 Context drive root
   [+] works on drive root 4ms (3ms|1ms)
 Context temp drive
   [+] works on temp drive 4ms (3ms|1ms)
   [-] works on temp drive with absolute path 4ms (4ms|1ms)
    Expected strings to be the same, but they were different.
    Expected length: 16
    Actual length:   64
    Strings differ at index 0.
    Expected: 'temp:\fred\frag\'
    But was:  'Cannot find drive. A drive with the name '\temp' does not exist.'
               ^
 Context unc drive
   [+] works on unc drive 6ms (4ms|1ms)


Test proper_drive_provider_path_from_pspath

Running tests from 'B:\test\provider_path_from_pspath_proper_drive.tests.ps1' Describing proper_drive_provider_path_from_pspath

This is the final solution, with the correct handling.
Bases on:
https://stackoverflow.com/a/4982137/1964796

    $path_drive = [ref] $null
    $path_abs = $ExecutionContext.SessionState.Path.IsPSAbsolute($path, $path_drive)
    $path_prov = $ExecutionContext.SessionState.Path.IsProviderQualified($path)
    # we split the drive away, it makes UnresolvedPath fail on non-existing drives.
    $norm_path  = Split-Path $path -NoQualifier
    # strip out UNC
    $path_direct = $norm_path.StartsWith('//') -or $norm_path.StartsWith('\\')
    if ($path_direct) {
        $norm_path = $norm_path.Substring(2)
    }
    # then normalize
    $norm_path = $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($norm_path)
    # then we cut out the current location if same drive
    if (($path_drive.Value -eq $pwd.Drive.Name) -or $path_direct) {
        $norm_path = $norm_path.Substring($pwd.Path.Trim('/', '\').Length + 1)
    } elseif (-not $path_prov) {
        # or we cut out the current drive
        if ($pwd.Drive) {
            $norm_path = $norm_path.Substring($pwd.Drive.Root.Length)
        } else {
            # or we cut out the UNC special case
            $norm_path = $norm_path.Substring($pwd.ProviderPath.Length + 1)
        }
    }
    # then add back the UNC if any
    if ($path_direct) {
        $norm_path = $pwd.Provider.ItemSeparator + $pwd.Provider.ItemSeparator + $norm_path
    }
    # then add back the provider if any
    if ($path_prov) {
        $norm_path = $ExecutionContext.SessionState.Path.Combine($path_drive.Value + '::/', $norm_path)
    }
    # or add back the drive if any
    elseif ($path_abs) {
        $norm_path = $ExecutionContext.SessionState.Path.Combine($path_drive.Value + ':', $norm_path)
    }
    $norm_path

pros: doesn`t use the dotnet path function, uses proper powershell infrastructure.
cons: kind of complex.

 Context cwd
   [+] has no external libraries 8ms (1ms|6ms)
   [+] barely work for FileInfos on existing paths 7ms (6ms|1ms)
   [+] process .. and . (relative paths) 4ms (3ms|1ms)
   [+] must support powershell providers 4ms (4ms|1ms)
   [+] must support powershell drives 5ms (4ms|1ms)
   [+] works with non-existant paths 30ms (29ms|1ms)
   [+] works with non-existant drives 3ms (3ms|1ms)
   [+] barely work for direct UNCs 4ms (3ms|1ms)
 Context reroot
   [+] doesn`t reroot subdir 5ms (4ms|2ms)
   [+] doesn`t reroot local 3ms (2ms|1ms)
   [+] doesn`t reroot parent 3ms (3ms|1ms)
 Context drive root
   [+] works on drive root 6ms (5ms|1ms)
 Context temp drive
   [+] works on temp drive 4ms (3ms|1ms)
   [+] works on temp drive with absolute path 4ms (3ms|1ms)
 Context unc drive
   [+] works on unc drive 5ms (4ms|1ms)


Test reflection

Running tests from 'B:\test\reflection.tests.ps1' Describing reflection

This is a dead-end.

    $flags = [System.Reflection.BindingFlags]::NonPublic + [System.Reflection.BindingFlags]::Instance
    $pathintr = $ExecutionContext.SessionState.Path
    $prop = $pathintr.GetType().GetMember("PathResolver", $flags)
    $globber = $prop.GetGetMethod($true).Invoke($pathintr, @())
    $getglobbeds = $globber.GetType().GetMember("GetGlobbedMonadPathsFromMonadPath", $flags)
    $getglobbed = $getglobbeds | Where-Object { $_.GetParameters().Count -eq 3 }
    $getglobbed.Invoke($globber, @($path, $true, $null))

pros: it would be nice if we could get the action functions that does what we want.
cons: reflection obviously.

 Context cwd
   [+] has no external libraries 5ms (1ms|4ms)
   [-] barely work for FileInfos on existing paths 3ms (3ms|0ms)
    Expected 'c', but got B:\test\c.
   [-] process .. and . (relative paths) 3ms (3ms|0ms)
    Expected 'c', but got B:\test\c.
   [-] must support powershell providers 3ms (3ms|1ms)
    Expected 'FileSystem::\\LUIZMONAD\Shared\c', but got Microsoft.PowerShell.Core\FileSystem::\\LUIZMONAD\Shared\a\..\c.
   [+] must support powershell drives 128ms (127ms|0ms)
   [-] works with non-existant paths 3ms (3ms|1ms)
    Expected 'fred\frag', but got B:\test\fred\frag.
   [-] works with non-existant drives 4ms (3ms|1ms)
    Expected strings to be the same, but they were different.
    Expected length: 12
    Actual length:   60
    Strings differ at index 0.
    Expected: 'U:\fred\frag'
    But was:  'Cannot find drive. A drive with the name 'U' does not exist.'
               ^
   [-] barely work for direct UNCs 13ms (13ms|1ms)
    Expected '\\LUIZMONAD\Shared\c', but got Microsoft.PowerShell.Core\FileSystem::\\LUIZMONAD\Shared\a\..\c.
 Context reroot
   [-] doesn`t reroot subdir 4ms (3ms|1ms)
    Expected 'fred\frag', but got B:\test\fred\frag.
   [-] doesn`t reroot local 3ms (3ms|1ms)
    Expected 'fred\frag', but got B:\test\fred\frag.
   [-] doesn`t reroot parent 3ms (3ms|1ms)
    Expected 'fred\frag', but got B:\test\fred\frag.
 Context drive root
   [-] works on drive root 15ms (14ms|1ms)
    Expected 'fred\frag\', but got C:\fred\frag\.
 Context temp drive
   [-] works on temp drive 4ms (3ms|1ms)
    Expected 'fred\frag\', but got B:\test\fred\frag\.
   [+] works on temp drive with absolute path 4ms (3ms|1ms)
 Context unc drive
   [-] works on unc drive 6ms (5ms|1ms)
    Expected 'fred\frag\', but got Microsoft.PowerShell.Core\FileSystem::\\LUIZMONAD\Shared\fred\frag\.


Test only_resolve_path

Running tests from 'B:\test\resolve_path.tests.ps1' Describing only_resolve_path

The answer with most votes and wrong obviusly.
https://stackoverflow.com/a/499101/1964796

    $path = (Resolve-Path $path).Path
    $path.Replace($pwd.Path, '').TrimStart('\').TrimStart('/')

pros: simple, fast and wrong.
cons: doesnt work for non existant paths and produces absolute paths, needs $pwd .

 Context cwd
   [+] has no external libraries 4ms (1ms|3ms)
   [+] barely work for FileInfos on existing paths 3ms (2ms|1ms)
   [+] process .. and . (relative paths) 3ms (3ms|1ms)
   [-] must support powershell providers 15ms (14ms|1ms)
    Expected strings to be the same, but they were different.
    Expected length: 32
    Actual length:   71
    Strings differ at index 0.
    Expected: 'FileSystem::\\LUIZMONAD\Shared\c'
    But was:  'Cannot find path '\\LUIZMONAD\Shared\a\..\c' because it does not exist.'
               ^
   [+] must support powershell drives 115ms (114ms|1ms)
   [-] works with non-existant paths 4ms (3ms|1ms)
    Expected strings to be the same, but they were different.
    Expected length: 9
    Actual length:   63
    Strings differ at index 0.
    Expected: 'fred\frag'
    But was:  'Cannot find path 'B:\test\fred\frag' because it does not exist.'
               ^
   [-] works with non-existant drives 14ms (13ms|1ms)
    Expected strings to be the same, but they were different.
    Expected length: 12
    Actual length:   60
    Strings differ at index 0.
    Expected: 'U:\fred\frag'
    But was:  'Cannot find drive. A drive with the name 'U' does not exist.'
               ^
   [-] barely work for direct UNCs 4ms (4ms|1ms)
    Expected strings to be the same, but they were different.
    Expected length: 20
    Actual length:   71
    Strings differ at index 0.
    Expected: '\\LUIZMONAD\Shared\c'
    But was:  'Cannot find path '\\LUIZMONAD\Shared\a\..\c' because it does not exist.'
               ^
 Context reroot
   [-] doesn`t reroot subdir 4ms (3ms|1ms)
    Expected strings to be the same, but they were different.
    Expected length: 9
    Actual length:   63
    Strings differ at index 0.
    Expected: 'fred\frag'
    But was:  'Cannot find path 'B:\test\fred\frag' because it does not exist.'
               ^
   [-] doesn`t reroot local 4ms (3ms|1ms)
    Expected strings to be the same, but they were different.
    Expected length: 9
    Actual length:   63
    Strings differ at index 0.
    Expected: 'fred\frag'
    But was:  'Cannot find path 'B:\test\fred\frag' because it does not exist.'
               ^
   [-] doesn`t reroot parent 4ms (4ms|1ms)
    Expected strings to be the same, but they were different.
    Expected length: 9
    Actual length:   63
    Strings differ at index 0.
    Expected: 'fred\frag'
    But was:  'Cannot find path 'B:\test\fred\frag' because it does not exist.'
               ^
 Context drive root
   [-] works on drive root 5ms (4ms|1ms)
    Expected strings to be the same, but they were different.
    Expected length: 10
    Actual length:   59
    Strings differ at index 0.
    Expected: 'fred\frag\'
    But was:  'Cannot find path 'C:\fred\frag\' because it does not exist.'
               ^
 Context temp drive
   [-] works on temp drive 5ms (4ms|1ms)
    Expected strings to be the same, but they were different.
    Expected length: 10
    Actual length:   64
    Strings differ at index 0.
    Expected: 'fred\frag\'
    But was:  'Cannot find path 'B:\test\fred\frag\' because it does not exist.'
               ^
   [-] works on temp drive with absolute path 4ms (4ms|1ms)
    Expected strings to be the same, but they were different.
    Expected length: 16
    Actual length:   62
    Strings differ at index 0.
    Expected: 'temp:\fred\frag\'
    But was:  'Cannot find path 'temp:\fred\frag\' because it does not exist.'
               ^
 Context unc drive
   [-] works on unc drive 17ms (16ms|1ms)
    Expected strings to be the same, but they were different.
    Expected length: 10
    Actual length:   75
    Strings differ at index 0.
    Expected: 'fred\frag\'
    But was:  'Cannot find path '\\LUIZMONAD\Shared\fred\frag\' because it does not exist.'
               ^


Test proper_drive_provider_path_from_pspath

Running tests from 'B:\test\resolved_provider_from_pspath.tests.ps1' Describing proper_drive_provider_path_from_pspath

Lets see what this function does.
It does what we wanted, but only for valid objects that exist, such a pity.

    $prov = $ExecutionContext.SessionState.Provider.GetOne('FileSystem')
    $path = $ExecutionContext.SessionState.Path.GetResolvedProviderPathFromPSPath($path, [ref] $prov)
    $path

pros: would be almost what we wanted.
cons: doesn`t support non-existing paths at all.

 Context cwd
   [+] has no external libraries 5ms (1ms|3ms)
   [-] barely work for FileInfos on existing paths 14ms (13ms|1ms)
    Expected strings to be the same, but they were different.
    Expected length: 1
    Actual length:   9
    Strings differ at index 0.
    Expected: 'c'
    But was:  'B:\test\c'
               ^
   [-] process .. and . (relative paths) 4ms (3ms|1ms)
    Expected strings to be the same, but they were different.
    Expected length: 1
    Actual length:   9
    Strings differ at index 0.
    Expected: 'c'
    But was:  'B:\test\c'
               ^
   [-] must support powershell providers 4ms (3ms|1ms)
    Expected strings to be the same, but they were different.
    Expected length: 32
    Actual length:   71
    Strings differ at index 0.
    Expected: 'FileSystem::\\LUIZMONAD\Shared\c'
    But was:  'Cannot find path '\\LUIZMONAD\Shared\a\..\c' because it does not exist.'
               ^
   [-] must support powershell drives 179ms (178ms|1ms)
    Expected strings to be the same, but they were different.
    Expected length: 27
    Actual length:   40
    Strings differ at index 2.
    Expected: 'HKLM:\Software\Classes\.dll'
    But was:  'HKEY_LOCAL_MACHINE\Software\Classes\.dll'
               --^
   [-] works with non-existant paths 4ms (3ms|1ms)
    Expected strings to be the same, but they were different.
    Expected length: 9
    Actual length:   63
    Strings differ at index 0.
    Expected: 'fred\frag'
    But was:  'Cannot find path 'B:\test\fred\frag' because it does not exist.'
               ^
   [-] works with non-existant drives 4ms (3ms|0ms)
    Expected strings to be the same, but they were different.
    Expected length: 12
    Actual length:   60
    Strings differ at index 0.
    Expected: 'U:\fred\frag'
    But was:  'Cannot find drive. A drive with the name 'U' does not exist.'
               ^
   [-] barely work for direct UNCs 4ms (4ms|1ms)
    Expected strings to be the same, but they were different.
    Expected length: 20
    Actual length:   71
    Strings differ at index 0.
    Expected: '\\LUIZMONAD\Shared\c'
    But was:  'Cannot find path '\\LUIZMONAD\Shared\a\..\c' because it does not exist.'
               ^
 Context reroot
   [-] doesn`t reroot subdir 4ms (3ms|1ms)
    Expected strings to be the same, but they were different.
    Expected length: 9
    Actual length:   63
    Strings differ at index 0.
    Expected: 'fred\frag'
    But was:  'Cannot find path 'B:\test\fred\frag' because it does not exist.'
               ^
   [-] doesn`t reroot local 4ms (3ms|1ms)
    Expected strings to be the same, but they were different.
    Expected length: 9
    Actual length:   63
    Strings differ at index 0.
    Expected: 'fred\frag'
    But was:  'Cannot find path 'B:\test\fred\frag' because it does not exist.'
               ^
   [-] doesn`t reroot parent 4ms (3ms|1ms)
    Expected strings to be the same, but they were different.
    Expected length: 9
    Actual length:   63
    Strings differ at index 0.
    Expected: 'fred\frag'
    But was:  'Cannot find path 'B:\test\fred\frag' because it does not exist.'
               ^
 Context drive root
   [-] works on drive root 5ms (4ms|1ms)
    Expected strings to be the same, but they were different.
    Expected length: 10
    Actual length:   59
    Strings differ at index 0.
    Expected: 'fred\frag\'
    But was:  'Cannot find path 'C:\fred\frag\' because it does not exist.'
               ^
 Context temp drive
   [-] works on temp drive 5ms (4ms|1ms)
    Expected strings to be the same, but they were different.
    Expected length: 10
    Actual length:   64
    Strings differ at index 0.
    Expected: 'fred\frag\'
    But was:  'Cannot find path 'B:\test\fred\frag\' because it does not exist.'
               ^
   [-] works on temp drive with absolute path 5ms (4ms|1ms)
    Expected strings to be the same, but they were different.
    Expected length: 16
    Actual length:   62
    Strings differ at index 0.
    Expected: 'temp:\fred\frag\'
    But was:  'Cannot find path 'temp:\fred\frag\' because it does not exist.'
               ^
 Context unc drive
   [-] works on unc drive 7ms (6ms|1ms)
    Expected strings to be the same, but they were different.
    Expected length: 10
    Actual length:   75
    Strings differ at index 0.
    Expected: 'fred\frag\'
    But was:  'Cannot find path '\\LUIZMONAD\Shared\fred\frag\' because it does not exist.'
               ^


Test set_current_directory

Running tests from 'B:\test\set_current_directory.tests.ps1' Describing set_current_directory

This seems bad, kind of works.
https://stackoverflow.com/a/48954464/1964796

    [System.IO.Directory]::SetCurrentDirectory($pwd)
    $path = [System.IO.Path]::GetFullPath($path)
    $path.Replace($pwd.Path, '').TrimStart('\').TrimStart('/')

pros: its kind of simple.
cons: changes the process CWD.

 Context cwd
   [+] has no external libraries 5ms (1ms|3ms)
   [+] barely work for FileInfos on existing paths 3ms (2ms|1ms)
   [+] process .. and . (relative paths) 2ms (2ms|1ms)
   [-] must support powershell providers 3ms (3ms|0ms)
    Expected strings to be the same, but they were different.
    Expected length: 32
    Actual length:   31
    Strings differ at index 13.
    Expected: 'FileSystem::\\LUIZMONAD\Shared\c'
    But was:  'FileSystem::\LUIZMONAD\Shared\c'
               -------------^
   [+] must support powershell drives 3ms (2ms|1ms)
   [+] works with non-existant paths 3ms (2ms|0ms)
   [+] works with non-existant drives 2ms (2ms|1ms)
   [-] barely work for direct UNCs 3ms (3ms|0ms)
    Expected strings to be the same, but they were different.
    Expected length: 20
    Actual length:   18
    Strings differ at index 0.
    Expected: '\\LUIZMONAD\Shared\c'
    But was:  'LUIZMONAD\Shared\c'
               ^
 Context reroot
   [+] doesn`t reroot subdir 3ms (2ms|1ms)
   [+] doesn`t reroot local 2ms (2ms|0ms)
   [+] doesn`t reroot parent 13ms (13ms|0ms)
 Context drive root
   [+] works on drive root 4ms (3ms|1ms)
 Context temp drive
   [+] works on temp drive 4ms (3ms|1ms)
   [+] works on temp drive with absolute path 3ms (2ms|1ms)
 Context unc drive
   [-] works on unc drive 7ms (6ms|1ms)
    Expected strings to be the same, but they were different.
    Expected length: 10
    Actual length:   134
    Strings differ at index 0.
    Expected: 'fred\frag\'
    But was:  'The filename, directory name, or volume label syntax is incorrect. : 'B:\test\Microsoft.PowerShell.Core\FileSystem::\LUIZMONAD\Shared''
               ^


Test split_join_path_resolve

Running tests from 'B:\test\split_join_path_resolve.tests.ps1' Describing split_join_path_resolve

Doing a split before Join-Path -Resolve
Just seeing if it works differently based on the order of path and childpath.
https://stackoverflow.com/a/72411499/1964796

    $path = $path -split '[\\/]'
    $head = ($path | Select-Object -SkipLast 1) -join '/'
    $path = $path | Select-Object -Last 1
    Join-Path $head $path -Resolve

pros: uses only powershell functions.
cons: doesn`t support non-existing paths at all.

 Context cwd
   [+] has no external libraries 5ms (1ms|3ms)
   [-] barely work for FileInfos on existing paths 4ms (3ms|1ms)
    Expected strings to be the same, but they were different.
    Expected length: 1
    Actual length:   9
    Strings differ at index 0.
    Expected: 'c'
    But was:  'B:\test\c'
               ^
   [-] process .. and . (relative paths) 25ms (25ms|1ms)
    Expected strings to be the same, but they were different.
    Expected length: 1
    Actual length:   9
    Strings differ at index 0.
    Expected: 'c'
    But was:  'B:\test\c'
               ^
   [-] must support powershell providers 20ms (19ms|1ms)
    Expected strings to be the same, but they were different.
    Expected length: 32
    Actual length:   71
    Strings differ at index 0.
    Expected: 'FileSystem::\\LUIZMONAD\Shared\c'
    But was:  'Cannot find path '\\LUIZMONAD\Shared\a\..\c' because it does not exist.'
               ^
   [+] must support powershell drives 155ms (155ms|1ms)
   [-] works with non-existant paths 4ms (3ms|1ms)
    Expected strings to be the same, but they were different.
    Expected length: 9
    Actual length:   63
    Strings differ at index 0.
    Expected: 'fred\frag'
    But was:  'Cannot find path 'B:\test\fred\frag' because it does not exist.'
               ^
   [-] works with non-existant drives 4ms (3ms|1ms)
    Expected strings to be the same, but they were different.
    Expected length: 12
    Actual length:   60
    Strings differ at index 0.
    Expected: 'U:\fred\frag'
    But was:  'Cannot find drive. A drive with the name 'U' does not exist.'
               ^
   [-] barely work for direct UNCs 4ms (4ms|1ms)
    Expected strings to be the same, but they were different.
    Expected length: 20
    Actual length:   71
    Strings differ at index 0.
    Expected: '\\LUIZMONAD\Shared\c'
    But was:  'Cannot find path '\\LUIZMONAD\Shared\a\..\c' because it does not exist.'
               ^
 Context reroot
   [-] doesn`t reroot subdir 5ms (4ms|1ms)
    Expected strings to be the same, but they were different.
    Expected length: 9
    Actual length:   63
    Strings differ at index 0.
    Expected: 'fred\frag'
    But was:  'Cannot find path 'B:\test\fred\frag' because it does not exist.'
               ^
   [-] doesn`t reroot local 14ms (14ms|1ms)
    Expected strings to be the same, but they were different.
    Expected length: 9
    Actual length:   63
    Strings differ at index 0.
    Expected: 'fred\frag'
    But was:  'Cannot find path 'B:\test\fred\frag' because it does not exist.'
               ^
   [-] doesn`t reroot parent 4ms (4ms|1ms)
    Expected strings to be the same, but they were different.
    Expected length: 9
    Actual length:   63
    Strings differ at index 0.
    Expected: 'fred\frag'
    But was:  'Cannot find path 'B:\test\fred\frag' because it does not exist.'
               ^
 Context drive root
   [-] works on drive root 5ms (4ms|1ms)
    Expected strings to be the same, but they were different.
    Expected length: 10
    Actual length:   59
    Strings differ at index 0.
    Expected: 'fred\frag\'
    But was:  'Cannot find path 'C:\fred\frag\' because it does not exist.'
               ^
 Context temp drive
   [-] works on temp drive 6ms (5ms|1ms)
    Expected strings to be the same, but they were different.
    Expected length: 10
    Actual length:   64
    Strings differ at index 0.
    Expected: 'fred\frag\'
    But was:  'Cannot find path 'B:\test\fred\frag\' because it does not exist.'
               ^
   [-] works on temp drive with absolute path 5ms (4ms|1ms)
    Expected strings to be the same, but they were different.
    Expected length: 16
    Actual length:   62
    Strings differ at index 0.
    Expected: 'temp:\fred\frag\'
    But was:  'Cannot find path 'temp:\fred\frag\' because it does not exist.'
               ^
 Context unc drive
   [-] works on unc drive 7ms (6ms|1ms)
    Expected strings to be the same, but they were different.
    Expected length: 10
    Actual length:   75
    Strings differ at index 0.
    Expected: 'fred\frag\'
    But was:  'Cannot find path '\\LUIZMONAD\Shared\fred\frag\' because it does not exist.'
               ^


Test wrong_regex

Running tests from 'B:\test\wrong_regex.tests.ps1' Describing wrong_regex

This seems bad, lets see if it works, almost.
https://stackoverflow.com/a/11587962/1964796

    $path -creplace '(?`<grp>[^\n\\]+\\)+(?`<-grp>\.\.\\)+(?(grp)(?!))', ''

pros: none.
cons: uses regex.

 Context cwd
   [+] has no external libraries 5ms (2ms|3ms)
   [+] barely work for FileInfos on existing paths 3ms (2ms|1ms)
   [-] process .. and . (relative paths) 3ms (3ms|1ms)
    Expected strings to be the same, but they were different.
    Expected length: 1
    Actual length:   3
    Strings differ at index 1.
    Expected: 'c'
    But was:  'c\.'
               -^
   [+] must support powershell providers 3ms (2ms|1ms)
   [+] must support powershell drives 3ms (2ms|1ms)
   [-] works with non-existant paths 13ms (13ms|1ms)
    Expected strings to be the same, but they were different.
    Expected length: 9
    Actual length:   11
    Strings differ at index 9.
    Expected: 'fred\frag'
    But was:  'fred\frag\.'
               ---------^
   [-] works with non-existant drives 3ms (3ms|1ms)
    Expected strings to be the same, but they were different.
    Expected length: 12
    Actual length:   14
    Strings differ at index 12.
    Expected: 'U:\fred\frag'
    But was:  'U:\fred\frag\.'
               ------------^
   [+] barely work for direct UNCs 3ms (2ms|1ms)
 Context reroot
   [-] doesn`t reroot subdir 4ms (3ms|1ms)
    Expected strings to be the same, but they were different.
    Expected length: 9
    Actual length:   11
    Strings differ at index 9.
    Expected: 'fred\frag'
    But was:  'fred\frag\.'
               ---------^
   [-] doesn`t reroot local 3ms (3ms|1ms)
    Expected strings to be the same, but they were different.
    Expected length: 9
    Actual length:   13
    Strings differ at index 0.
    Expected: 'fred\frag'
    But was:  '.\fred\frag\.'
               ^
   [-] doesn`t reroot parent 4ms (3ms|1ms)
    Expected strings to be the same, but they were different.
    Expected length: 9
    Actual length:   19
    Strings differ at index 0.
    Expected: 'fred\frag'
    But was:  '..\test\fred\frag\.'
               ^
 Context drive root
   [+] works on drive root 4ms (3ms|1ms)
 Context temp drive
   [+] works on temp drive 4ms (3ms|1ms)
   [+] works on temp drive with absolute path 3ms (3ms|1ms)
 Context unc drive
   [+] works on unc drive 6ms (5ms|1ms)




Tests completed in 3.34s
Tests Passed: 98, Failed: 97, Skipped: 0 NotRun: 0
PS B:\test> 



Import-Module .\base_test.ps1
# normalize_relative_path
describe @"
$(title normalize_relative_path)
Doesn't work, just testing what this function does.
$(code-block {
$path_drive = [ref] $null
$path_abs = $ExecutionContext.SessionState.Path.IsPSAbsolute($path, $path_drive)
$norm_path = Split-Path $path -NoQualifier
$ExecutionContext.SessionState.Path.PushCurrentLocation("normalizing")
try {
$path_drive = $(if ($path_abs) { Join-Path $path_drive.Value "/" } else { $pwd.Path })
$null = $ExecutionContext.SessionState.Path.SetLocation($path_drive)
$norm_path = $ExecutionContext.SessionState.Path.NormalizeRelativePath($norm_path, $pwd.Path)
} finally {
$null = $ExecutionContext.SessionState.Path.PopLocation("normalizing")
}
if ($path_abs) {
Join-Path $path_drive $norm_path
} else {
$norm_path
}
})
> pros: none.
> cons: doesn't work with invalid paths.
"@ {
beforeAll {
mock Load-NormalizedPath {
$true
}
mock Get-NormalizedPath {
param($path)
try {
$path_drive = [ref] $null
$path_abs = $ExecutionContext.SessionState.Path.IsPSAbsolute($path, $path_drive)
$norm_path = Split-Path $path -NoQualifier
$ExecutionContext.SessionState.Path.PushCurrentLocation("normalizing")
try {
$path_drive = $(if ($path_abs) { Join-Path $path_drive.Value "/" } else { $pwd.Path })
$null = $ExecutionContext.SessionState.Path.SetLocation($path_drive)
$norm_path = $ExecutionContext.SessionState.Path.NormalizeRelativePath($norm_path, $pwd.Path)
} finally {
$null = $ExecutionContext.SessionState.Path.PopLocation("normalizing")
}
if ($path_abs) {
Join-Path $path_drive $norm_path
} else {
$norm_path
}
} catch { exception($_) }
}
}
tests
}
Import-Module .\base_test.ps1
# only_join_path_resolve
describe @"
$(title only_join_path_resolve)
Using only join-path with -resolve
https://stackoverflow.com/a/72411499/1964796
$(code-block {
Join-Path $path '' -Resolve
})
> pros: its simple and small and wrong.
> cons: doesn't support non-existing paths at all, only works for absolute paths.
"@ {
beforeAll {
mock Load-NormalizedPath {
$true
}
mock Get-NormalizedPath {
param($path)
try {
Join-Path $path '' -Resolve
} catch { exception($_) }
}
}
tests
}
Import-Module .\base_test.ps1
# only_resolve_path
describe @"
$(title only_resolve_path)
The answer with most votes and wrong obviusly.
https://stackoverflow.com/a/499101/1964796
$(code-block {
$path = (Resolve-Path $path).Path
$path.Replace($pwd.Path, '').TrimStart('\').TrimStart('/')
})
> pros: simple, fast and wrong.
> cons: doesnt work for non existant paths and produces absolute paths, needs `$pwd .
"@ {
beforeAll {
mock Load-NormalizedPath {
$true
}
mock Get-NormalizedPath {
param($path)
try {
$path = (Resolve-Path $path).Path
$path.Replace($pwd.Path, '').TrimStart('\').TrimStart('/')
} catch { exception($_) }
}
}
tests
}
# only_unresolved_provider_path_from_pspath
describe @"
$(title only_unresolved_provider_path_from_pspath)
This seems like the correct way, but needs boilerplate.
https://stackoverflow.com/a/52157943/1964796
$(code-block {
$path = Join-Path '/' $path
$path = $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($path)
$path = $path.Replace($pwd.Path, '')
if ($pwd.Drive.Root) { $path = $path.Replace($pwd.Drive.Root, '') }
$path
})
> pros: simple.
> cons: needs boilerplate to make it correct, doesn't work with other providers or non-ex drives.
"@ {
beforeAll {
mock Load-NormalizedPath {
$true
}
mock Get-NormalizedPath {
param($path)
try {
$path = Join-Path '/' $path
$path = $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($path)
$path = $path.Replace($pwd.Path, '')
if ($pwd.Drive.Root) { $path = $path.Replace($pwd.Drive.Root, '') }
$path
} catch { exception($_) }
}
}
tests
}
Import-Module .\base_test.ps1
# proper_drive_provider_path_from_pspath
describe @"
$(title proper_drive_provider_path_from_pspath)
This is the final solution, with the correct handling.
Bases on:
https://stackoverflow.com/a/4982137/1964796
$(code-block {
$path_drive = [ref] $null
$path_abs = $ExecutionContext.SessionState.Path.IsPSAbsolute($path, $path_drive)
$path_prov = $ExecutionContext.SessionState.Path.IsProviderQualified($path)
# we split the drive away, it makes UnresolvedPath fail on non-existing drives.
$norm_path = Split-Path $path -NoQualifier
# strip out UNC
$path_direct = $norm_path.StartsWith('//') -or $norm_path.StartsWith('\\')
if ($path_direct) {
$norm_path = $norm_path.Substring(2)
}
# then normalize
$norm_path = $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($norm_path)
# then we cut out the current location if same drive
if (($path_drive.Value -eq $pwd.Drive.Name) -or $path_direct) {
$norm_path = $norm_path.Substring($pwd.Path.Trim('/', '\').Length + 1)
} elseif (-not $path_prov) {
# or we cut out the current drive
if ($pwd.Drive) {
$norm_path = $norm_path.Substring($pwd.Drive.Root.Length)
} else {
# or we cut out the UNC special case
$norm_path = $norm_path.Substring($pwd.ProviderPath.Length + 1)
}
}
# then add back the UNC if any
if ($path_direct) {
$norm_path = $pwd.Provider.ItemSeparator + $pwd.Provider.ItemSeparator + $norm_path
}
# then add back the provider if any
if ($path_prov) {
$norm_path = $ExecutionContext.SessionState.Path.Combine($path_drive.Value + '::/', $norm_path)
}
# or add back the drive if any
elseif ($path_abs) {
$norm_path = $ExecutionContext.SessionState.Path.Combine($path_drive.Value + ':', $norm_path)
}
$norm_path
})
> pros: doesn't use the dotnet path function, uses proper powershell infrastructure.
> cons: kind of complex.
"@ {
beforeAll {
mock Load-NormalizedPath {
$true
}
mock Get-NormalizedPath {
param($path)
try {
$path_drive = [ref] $null
$path_abs = $ExecutionContext.SessionState.Path.IsPSAbsolute($path, $path_drive)
$path_prov = $ExecutionContext.SessionState.Path.IsProviderQualified($path)
# we split the drive away, it makes UnresolvedPath fail on non-existing drives.
$norm_path = Split-Path $path -NoQualifier
# strip out UNC
$path_direct = $norm_path.StartsWith('//') -or $norm_path.StartsWith('\\')
if ($path_direct) {
$norm_path = $norm_path.Substring(2)
}
# then normalize
$norm_path = $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($norm_path)
# then we cut out the current location if same drive
if (($path_drive.Value -eq $pwd.Drive.Name) -or $path_direct) {
$norm_path = $norm_path.Substring($pwd.Path.Trim('/', '\').Length + 1)
} elseif (-not $path_prov) {
# or we cut out the current drive
if ($pwd.Drive) {
$norm_path = $norm_path.Substring($pwd.Drive.Root.Length)
} else {
# or we cut out the UNC special case
$norm_path = $norm_path.Substring($pwd.ProviderPath.Length + 1)
}
}
# then add back the UNC if any
if ($path_direct) {
$norm_path = $pwd.Provider.ItemSeparator + $pwd.Provider.ItemSeparator + $norm_path
}
# then add back the provider if any
if ($path_prov) {
$norm_path = $ExecutionContext.SessionState.Path.Combine($path_drive.Value + '::/', $norm_path)
}
# or add back the drive if any
elseif ($path_abs) {
$norm_path = $ExecutionContext.SessionState.Path.Combine($path_drive.Value + ':', $norm_path)
}
$norm_path
} catch { exception($_) }
}
}
tests
}
Import-Module .\base_test.ps1
# reflection
describe @"
$(title reflection)
This is a dead-end.
$(code-block {
$flags = [System.Reflection.BindingFlags]::NonPublic + [System.Reflection.BindingFlags]::Instance
$pathintr = $ExecutionContext.SessionState.Path
$prop = $pathintr.GetType().GetMember("PathResolver", $flags)
$globber = $prop.GetGetMethod($true).Invoke($pathintr, @())
$getglobbeds = $globber.GetType().GetMember("GetGlobbedMonadPathsFromMonadPath", $flags)
$getglobbed = $getglobbeds | Where-Object { $_.GetParameters().Count -eq 3 }
$getglobbed.Invoke($globber, @($path, $true, $null))
})
> pros: it would be nice if we could get the action functions that does what we want.
> cons: reflection obviusly.
"@ {
beforeAll {
mock Load-NormalizedPath {
$true
}
mock Get-NormalizedPath {
param($path)
try {
$flags = [System.Reflection.BindingFlags]::NonPublic + [System.Reflection.BindingFlags]::Instance
$pathintr = $ExecutionContext.SessionState.Path
$prop = $pathintr.GetType().GetMember("PathResolver", $flags)
$globber = $prop.GetGetMethod($true).Invoke($pathintr, @())
$getglobbeds = $globber.GetType().GetMember("GetGlobbedMonadPathsFromMonadPath", $flags)
$getglobbed = $getglobbeds | Where-Object { $_.GetParameters().Count -eq 3 }
$getglobbed.Invoke($globber, @($path, $true, $null))
} catch { exception($_) }
}
}
tests
}
Import-Module .\base_test.ps1
# resolved_provider_from_pspath
describe @"
$(title proper_drive_provider_path_from_pspath)
Lets see what this function does.
It does what we wanted, but only for valid objects that exist, such a pity.
$(code-block {
$prov = $ExecutionContext.SessionState.Provider.GetOne('FileSystem')
$path = $ExecutionContext.SessionState.Path.GetResolvedProviderPathFromPSPath($path, [ref] $prov)
$path
})
> pros: would be almost what we wanted.
> cons: doesn't support non-existing paths at all.
"@ {
beforeAll {
mock Load-NormalizedPath {
$true
}
mock Get-NormalizedPath {
param($path)
try {
$prov = $ExecutionContext.SessionState.Provider.GetOne('FileSystem')
$path = $ExecutionContext.SessionState.Path.GetResolvedProviderPathFromPSPath($path, [ref] $prov)
$path
} catch { exception($_) }
}
}
tests
}
Import-Module .\base_test.ps1
# set_current_directory
describe @"
$(title set_current_directory)
This seems bad, kind of works.
https://stackoverflow.com/a/48954464/1964796
$(code-block {
[System.IO.Directory]::SetCurrentDirectory($pwd)
$path = [System.IO.Path]::GetFullPath($path)
$path.Replace($pwd.Path, '').TrimStart('\').TrimStart('/')
})
> pros: its kind of simple.
> cons: changes the process CWD.
"@ {
beforeAll {
mock Load-NormalizedPath {
$true
}
mock Get-NormalizedPath {
param($path)
try {
[System.IO.Directory]::SetCurrentDirectory($pwd)
$path = [System.IO.Path]::GetFullPath($path)
$path.Replace($pwd.Path, '').TrimStart('\').TrimStart('/')
} catch { exception($_) }
}
$global:__cur_dir_old = [System.IO.Directory]::GetCurrentDirectory()
}
afterAll {
[System.IO.Directory]::SetCurrentDirectory($global:__cur_dir_old)
}
tests
}
Import-Module .\base_test.ps1
# split_join_path_resolve
describe @"
$(title split_join_path_resolve)
Doing a split before Join-Path -Resolve
Just seeing if it works differently based on the order of path and childpath.
https://stackoverflow.com/a/72411499/1964796
$(code-block {
$path = $path -split '[\\/]'
$head = ($path | Select-Object -SkipLast 1) -join '/'
$path = $path | Select-Object -Last 1
Join-Path $head $path -Resolve
})
> pros: uses only powershell functions.
> cons: doesn't support non-existing paths at all.
"@ {
beforeAll {
mock Load-NormalizedPath {
$true
}
mock Get-NormalizedPath {
param($path)
try {
$path = $path -split '[\\/]'
$head = ($path | Select-Object -SkipLast 1) -join '/'
$path = $path | Select-Object -Last 1
Join-Path $head $path -Resolve
} catch { exception($_) }
}
}
tests
}
Import-Module .\base_test.ps1
# wrong_regex
describe @"
$(title wrong_regex)
This seems bad, lets see if it works, almost.
https://stackoverflow.com/a/11587962/1964796
$(code-block {
$path -creplace '(?``<grp>[^\n\\]+\\)+(?``<-grp>\.\.\\)+(?(grp)(?!))', ''
})
> pros: none.
> cons: uses regex.
"@ {
beforeAll {
mock Load-NormalizedPath {
$true
}
mock Get-NormalizedPath {
param($path)
try {
$path -creplace '(?<grp>[^\n\\]+\\)+(?<-grp>\.\.\\)+(?(grp)(?!))', ''
} catch { exception($_) }
}
}
tests
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment