Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
85 changes: 85 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
name: CI

on:
push:
branches: [master]
pull_request:
branches: [master]
workflow_dispatch:

jobs:
test:
name: Test (${{ matrix.os }})
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]

steps:
- name: Checkout
uses: actions/checkout@v4

- name: Install Graphviz (Linux)
if: runner.os == 'Linux'
run: |
sudo apt-get update
sudo apt-get install -y graphviz

- name: Install Graphviz (macOS)
if: runner.os == 'macOS'
run: brew install graphviz

- name: Install Graphviz (Windows)
if: runner.os == 'Windows'
shell: pwsh
run: |
choco install graphviz -y --no-progress
# choco's PATH update isn't always visible to later steps in the same job.
'C:\Program Files\Graphviz\bin' | Out-File -FilePath $env:GITHUB_PATH -Append

- name: Verify dot is on PATH
shell: pwsh
run: dot -V

- name: Install PowerShell module dependencies
shell: pwsh
run: |
Get-PackageProvider -Name NuGet -ForceBootstrap | Out-Null
Install-Module -Name Pester -RequiredVersion 5.7.1 -Scope CurrentUser -Force -SkipPublisherCheck -AllowClobber
Install-Module -Name PSScriptAnalyzer, DependsOn -Scope CurrentUser -Force -SkipPublisherCheck -AllowClobber

- name: Import module from source
shell: pwsh
run: |
Import-Module Pester -RequiredVersion 5.7.1 -Force
Import-Module ./PSGraph/PSGraph.psd1 -Force

- name: Run Pester tests
shell: pwsh
run: |
Import-Module Pester -RequiredVersion 5.7.1 -Force
Import-Module ./PSGraph/PSGraph.psd1 -Force

$config = New-PesterConfiguration
$config.Run.Path = 'Tests'
$config.Run.PassThru = $true
$config.Run.Exit = $false
$config.Output.Verbosity = 'Detailed'
$config.TestResult.Enabled = $true
$config.TestResult.OutputFormat = 'NUnitXml'
$config.TestResult.OutputPath = "TestResults_$($env:RUNNER_OS).xml"

$results = Invoke-Pester -Configuration $config

if ($results.FailedCount -gt 0)
{
throw "Failed [$($results.FailedCount)] Pester tests."
}

- name: Upload test results
if: always()
uses: actions/upload-artifact@v4
with:
name: pester-results-${{ matrix.os }}
path: TestResults_*.xml
33 changes: 17 additions & 16 deletions BuildTasks/Pester.Task.ps1
Original file line number Diff line number Diff line change
@@ -1,34 +1,35 @@
task Pester {
$requiredPercent = $Script:CodeCoveragePercent

$params = @{
OutputFile = $testFile
OutputFormat = 'NUnitXml'
PassThru = $true
Path = 'Tests'
Show = 'Failed', 'Fails', 'Summary'
Tag = 'Build'
}
$config = New-PesterConfiguration
$config.Run.Path = 'Tests'
$config.Run.PassThru = $true
$config.Filter.Tag = 'Build'
$config.Output.Verbosity = 'Normal'
$config.TestResult.Enabled = $true
$config.TestResult.OutputFormat = 'NUnitXml'
$config.TestResult.OutputPath = $testFile

if($requiredPercent -gt 0.00)
if ($requiredPercent -gt 0.00)
{
$params['CodeCoverage'] = 'Output\*\*.psm1'
$params['CodeCoverageOutputFile'] = 'Output\codecoverage.xml'
$config.CodeCoverage.Enabled = $true
$config.CodeCoverage.Path = 'Output\*\*.psm1'
$config.CodeCoverage.OutputPath = 'Output\codecoverage.xml'
}

$results = Invoke-Pester @params
$results = Invoke-Pester -Configuration $config
if ($results.FailedCount -gt 0)
{
Write-Error -Message "Failed [$($results.FailedCount)] Pester tests."
}

if($results.codecoverage.NumberOfCommandsAnalyzed -gt 0)
if ($results.CodeCoverage.NumberOfCommandsAnalyzed -gt 0)
{
$codeCoverage = $results.codecoverage.NumberOfCommandsExecuted / $results.codecoverage.NumberOfCommandsAnalyzed
$codeCoverage = $results.CodeCoverage.NumberOfCommandsExecuted / $results.CodeCoverage.NumberOfCommandsAnalyzed

if($codeCoverage -lt $requiredPercent)
if ($codeCoverage -lt $requiredPercent)
{
Write-Error ("Failed Code Coverage [{0:P}] below {1:P}" -f $codeCoverage,$requiredPercent)
Write-Error ("Failed Code Coverage [{0:P}] below {1:P}" -f $codeCoverage, $requiredPercent)
}
}
}
2 changes: 1 addition & 1 deletion PSGraph/PSGraph.psd1
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@
Description = 'Builds graphs using GraphViz'

# Minimum version of the Windows PowerShell engine required by this module
# PowerShellVersion = ''
PowerShellVersion = '7.0'

# Name of the Windows PowerShell host required by this module
# PowerShellHostName = ''
Expand Down
5 changes: 3 additions & 2 deletions PSGraph/Public/Entity.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ Function Entity
.NOTES
General notes
#>
[Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSUseProcessBlockForPipelineCommand", "", Justification = "Converts one InputObject into one Record by design; not a batch/collection cmdlet.")]
[CmdletBinding()]
param (
[parameter(
Expand Down Expand Up @@ -83,8 +84,8 @@ Function Entity
{
if ($null -ne $Property)
{
$matches = $property | Where-Object {$propertyName -like $_}
if ($null -eq $matches)
$matchingProperties = $property | Where-Object {$propertyName -like $_}
if ($null -eq $matchingProperties)
{
continue
}
Expand Down
1 change: 1 addition & 0 deletions PSGraph/Public/Node.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ function Node
If you have subgraphs, it works best to define the node inside the subgraph before giving it an edge
#>
[Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidDefaultValueForMandatoryParameter", "")]
[Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidOverwritingBuiltInCmdlets", "")]
[cmdletbinding()]
param(
# The name of the node
Expand Down
46 changes: 23 additions & 23 deletions Tests/Edge.Tests.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -4,44 +4,44 @@ Describe 'Function Edge' {

It "Get-Edge should not throw an error" {

{Edge lhs rhs } | Should Not Throw
{Edge lhs rhs } | Should -Not -Throw
}

It "Edge alias should not throw an error" {

{Edge lhs rhs} | Should Not Throw
{Edge lhs rhs} | Should -Not -Throw
}

It "Edge attributes should not throw an error" {

{Edge lhs rhs @{label = 'test'}} | Should Not Throw
{Edge lhs rhs @{label = 'test'}} | Should -Not -Throw
}

It "Creates a simple Edge" {
Edge lhs rhs | Should Match '"lhs"->"rhs"'
Edge lhs rhs | Should -Match '"lhs"->"rhs"'
}

It "Creates a Edge with attributes" {
Edge lhs rhs @{label = 'test'} | Should Match '"lhs"->"rhs" \[label="test";\]'
Edge lhs rhs @{label = 'test'} | Should -Match '"lhs"->"rhs" \[label="test";\]'
}

It "Creates a Edge with multiple attributes" {
$result = Edge lhs rhs @{label = 'test'; arrowsize = '2'}

$result | Should Match 'label="test";'
$result | Should Match 'arrowsize="2";'
$result | Should -Match 'label="test";'
$result | Should -Match 'arrowsize="2";'
}

It "Creates an edge with scripted properties" {
$object = @{source = 'here'; target = 'there'}
$result = edge $object -FromScript {$_.source} -ToScript {$_.target}
$result | Should Match '"here"->"there"'
$result | Should -Match '"here"->"there"'
}

It "Creates an edge with scripted properties and attributes" {
$object = @{source = 'here'; target = 'there'; description = 'to'}
$result = edge $object -FromScript {$_.source} -ToScript {$_.target} -Attributes @{label = {$_.description}}
$result | Should Match '"here"->"there" \[label="to";\]'
$result | Should -Match '"here"->"there" \[label="to";\]'
}

It "Creates multiple edges with scripted properties and attributes" {
Expand All @@ -50,8 +50,8 @@ Describe 'Function Edge' {
@{source = 'LA'; target = 'NY'; description = 'roadtrip'}
)
$result = edge $object -FromScript {$_.source} -ToScript {$_.target} -Attributes @{label = {$_.description}}
$result[0] | Should Match '"here"->"there" \[label="to";\]'
$result[1] | Should Match '"LA"->"NY" \[label="roadtrip";\]'
$result[0] | Should -Match '"here"->"there" \[label="to";\]'
$result[1] | Should -Match '"LA"->"NY" \[label="roadtrip";\]'
}

It "should handle record labels in edges" {
Expand All @@ -74,25 +74,25 @@ Describe 'Function Edge' {
Context "Feature" {

It "Can define multiple edges at once in a chain" {
{edge one, two, three} | Should Not Throw
{edge one, two, three} | Should -Not -Throw

$result = Edge one, two, three
$result | Should Not BeNullOrEmpty
$result.count | Should be 2
$result[0] | Should match '"one"->"two"'
$result[1] | Should match '"two"->"three"'
$result | Should -Not -BeNullOrEmpty
$result.count | Should -Be 2
$result[0] | Should -Match '"one"->"two"'
$result[1] | Should -Match '"two"->"three"'
}

It "Can define multiple edges at once, with cross multiply" {
{Edge one, two three, four} | Should Not Throw
{Edge one, two three, four} | Should -Not -Throw

$result = Edge one, two three, four
$result | Should Not BeNullOrEmpty
$result.count | Should be 4
$result[0] | Should match '"one"->"three"'
$result[1] | Should match '"one"->"four"'
$result[2] | Should match '"two"->"three"'
$result[3] | Should match '"two"->"four"'
$result | Should -Not -BeNullOrEmpty
$result.count | Should -Be 4
$result[0] | Should -Match '"one"->"three"'
$result[1] | Should -Match '"one"->"four"'
$result[2] | Should -Match '"two"->"three"'
$result[3] | Should -Match '"two"->"four"'
}
}
}
38 changes: 20 additions & 18 deletions Tests/Export-PSGraph.Tests.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -7,27 +7,29 @@ $moduleName = Split-Path $moduleRoot -Leaf
# This one is not tagged with Build because it requires GraphViz
Describe "$ModuleName Export-PSGraph" -Tag graphviz {

$dot = graph g {
node 2 @{shape = 'house'}
edge 2, 4, 8, 16
BeforeAll {
$dot = graph g {
node 2 @{shape = 'house'}
edge 2, 4, 8, 16
}
}

Context "Basic features" {

It "Converts file to image" {
$path = "$testdrive\g.dot"
$path = Join-Path $testdrive "g.dot"
Set-Content -Path $path -Value $dot
Export-PSGraph -SourcePath $path -OutputFormat png

"$path.png" | Should Exist
"$path.png" | Should -Exist
}

It "Converts file to image over pipe" {
$path = "$testdrive\g2.dot"
$path = Join-Path $testdrive "g2.dot"
Set-Content -Path $path -Value $dot
$path | Export-PSGraph -OutputFormat png

"$path.png" | Should Exist
"$path.png" | Should -Exist
}
}

Expand All @@ -38,19 +40,19 @@ Describe "$ModuleName Export-PSGraph" -Tag graphviz {
New-Item -ItemType Directory -Path $dir -Force | Out-Null
$path = Join-Path $dir "spaced graph.png"

{ Export-PSGraph -Source $dot -DestinationPath $path -ErrorAction Stop } | Should Not Throw
{ Export-PSGraph -Source $dot -DestinationPath $path -ErrorAction Stop } | Should -Not -Throw

$path | Should Exist
$path | Should -Exist
}

It "-ShowGraph launches a destination path containing spaces without throwing" {
$dir = Join-Path $testdrive "spaced dir 2"
New-Item -ItemType Directory -Path $dir -Force | Out-Null
$path = Join-Path $dir "spaced graph.png"

{ Export-PSGraph -Source $dot -DestinationPath $path -ShowGraph -ErrorAction Stop } | Should Not Throw
{ Export-PSGraph -Source $dot -DestinationPath $path -ShowGraph -ErrorAction Stop } | Should -Not -Throw

$path | Should Exist
$path | Should -Exist
}
}

Expand All @@ -62,36 +64,36 @@ Describe "$ModuleName Export-PSGraph" -Tag graphviz {

It "Exports successfully even when the caller's `$OutputEncoding would inject a BOM" {
$OutputEncoding = [System.Text.UTF8Encoding]::new($true)
$path = "$testdrive\bom.dot"
$path = Join-Path $testdrive "bom.dot"

{ Export-PSGraph -Source $dot -DestinationPath $path -OutputFormat dot -ErrorAction Stop } | Should Not Throw
{ Export-PSGraph -Source $dot -DestinationPath $path -OutputFormat dot -ErrorAction Stop } | Should -Not -Throw

$bytes = [System.IO.File]::ReadAllBytes($path)
($bytes[0] -eq 0xEF -and $bytes[1] -eq 0xBB -and $bytes[2] -eq 0xBF) | Should Be $false
($bytes[0] -eq 0xEF -and $bytes[1] -eq 0xBB -and $bytes[2] -eq 0xBF) | Should -Be $false
}
}

Context "#104 Non-ASCII characters in labels" {

It "Round-trips accented/non-ASCII labels through dot without garbling" {
$accented = graph g { node cafe @{label = 'héllo wörld'} }
$path = "$testdrive\accented.dot"
$path = Join-Path $testdrive "accented.dot"

Export-PSGraph -Source $accented -DestinationPath $path -OutputFormat dot

$text = [System.IO.File]::ReadAllText($path, [System.Text.Encoding]::UTF8)
$text | Should Match 'héllo wörld'
$text | Should -Match 'héllo wörld'
}
}

Context "#75 #88 #85 Graphviz path detection" {

It "Finds dot via PATH when -GraphVizPath is not specified" {
{ Export-PSGraph -Source $dot -DestinationPath "$testdrive\pathlookup.png" -ErrorAction Stop } | Should Not Throw
{ Export-PSGraph -Source $dot -DestinationPath (Join-Path $testdrive "pathlookup.png") -ErrorAction Stop } | Should -Not -Throw
}

It "Honors an explicitly-supplied -GraphVizPath instead of silently falling back to PATH" {
{ Export-PSGraph -Source $dot -DestinationPath "$testdrive\badpath.png" -GraphVizPath 'C:\does\not\exist\dot.exe' -ErrorAction Stop } | Should Throw
{ Export-PSGraph -Source $dot -DestinationPath (Join-Path $testdrive "badpath.png") -GraphVizPath 'C:\does\not\exist\dot.exe' -ErrorAction Stop } | Should -Throw
}
}
}
Loading
Loading