diff --git a/.github/workflows/Action-Test.yml b/.github/workflows/Action-Test.yml
index 81733bc..a471c31 100644
--- a/.github/workflows/Action-Test.yml
+++ b/.github/workflows/Action-Test.yml
@@ -7,6 +7,8 @@ on: [pull_request]
env:
GH_TOKEN: ${{ github.token }}
+permissions: {}
+
jobs:
ActionTestDefault:
name: Action-Test - [Default]
@@ -15,24 +17,13 @@ jobs:
- name: Checkout repo
uses: actions/checkout@v4
- - name: Checkout tests -> PSModuleTest
- uses: actions/checkout@v4
- with:
- repository: PSModule/PSModuleTest
- path: tests
-
- - name: Delete outputs
- shell: pwsh
- run: |
- Remove-Item -Path tests/outputs -Recurse -Force -Verbose
-
- name: Initialize environment
uses: PSModule/Initialize-PSModule@main
- name: Action-Test
uses: ./
with:
- Name: PSModule
+ Name: PSModuleTest
Path: tests/src
ModulesOutputPath: tests/outputs/modules
DocsOutputPath: tests/outputs/docs
diff --git a/.github/workflows/Linter.yml b/.github/workflows/Linter.yml
index 89ac29a..cfd32e6 100644
--- a/.github/workflows/Linter.yml
+++ b/.github/workflows/Linter.yml
@@ -4,6 +4,11 @@ run-name: "Linter - [${{ github.event.pull_request.title }} #${{ github.event.pu
on: [pull_request]
+permissions:
+ contents: read
+ packages: read
+ statuses: write # To report GitHub Actions status checks
+
jobs:
Lint:
name: Lint code base
@@ -11,8 +16,10 @@ jobs:
steps:
- name: Checkout repo
uses: actions/checkout@v4
+ with:
+ fetch-depth: 0
- name: Lint code base
- uses: github/super-linter@latest
+ uses: super-linter/super-linter@latest
env:
GITHUB_TOKEN: ${{ github.token }}
diff --git a/scripts/helpers/Build/Build-PSModuleManifest.ps1 b/scripts/helpers/Build/Build-PSModuleManifest.ps1
index 31cabc6..55a0456 100644
--- a/scripts/helpers/Build/Build-PSModuleManifest.ps1
+++ b/scripts/helpers/Build/Build-PSModuleManifest.ps1
@@ -333,8 +333,11 @@ function Build-PSModuleManifest {
Show-FileContent -Path $outputManifestPath
Stop-LogGroup
- Start-LogGroup 'Build manifest file - Result - After format'
+ Start-LogGroup 'Build manifest file - Format'
Set-ModuleManifest -Path $outputManifestPath
+ Stop-LogGroup
+
+ Start-LogGroup 'Build manifest file - Result - After format'
Show-FileContent -Path $outputManifestPath
Stop-LogGroup
diff --git a/tests/src/PSModuleTest/PSModuleTest.psd1 b/tests/src/PSModuleTest/PSModuleTest.psd1
new file mode 100644
index 0000000..466eca8
--- /dev/null
+++ b/tests/src/PSModuleTest/PSModuleTest.psd1
@@ -0,0 +1,4 @@
+@{
+ ModuleVersion = '0.0.0'
+ RootModule = 'PSModuleTest.psm1'
+}
diff --git a/tests/src/PSModuleTest/PSModuleTest.psm1 b/tests/src/PSModuleTest/PSModuleTest.psm1
new file mode 100644
index 0000000..ab67c5e
--- /dev/null
+++ b/tests/src/PSModuleTest/PSModuleTest.psm1
@@ -0,0 +1,73 @@
+[Cmdletbinding()]
+param()
+
+Write-Verbose 'Importing subcomponents'
+$Folders = 'init', 'classes', 'private', 'public'
+# Import everything in these folders
+Foreach ($Folder in $Folders) {
+ $Root = Join-Path -Path $PSScriptRoot -ChildPath $Folder
+ Write-Verbose "Processing folder: $Root"
+ if (Test-Path -Path $Root) {
+ Write-Verbose "Getting all files in $Root"
+ $Files = $null
+ $Files = Get-ChildItem -Path $Root -Include '*.ps1', '*.psm1' -Recurse
+ # dot source each file
+ foreach ($File in $Files) {
+ Write-Verbose "Importing $($File)"
+ Import-Module $File
+ Write-Verbose "Importing $($File): Done"
+ }
+ }
+}
+
+. "$PSScriptRoot\finally.ps1"
+
+# Define the types to export with type accelerators.
+$ExportableTypes = @(
+ [Book]
+ [BookList]
+)
+
+# Get the internal TypeAccelerators class to use its static methods.
+$TypeAcceleratorsClass = [psobject].Assembly.GetType(
+ 'System.Management.Automation.TypeAccelerators'
+)
+# Ensure none of the types would clobber an existing type accelerator.
+# If a type accelerator with the same name exists, throw an exception.
+$ExistingTypeAccelerators = $TypeAcceleratorsClass::Get
+foreach ($Type in $ExportableTypes) {
+ if ($Type.FullName -in $ExistingTypeAccelerators.Keys) {
+ $Message = @(
+ "Unable to register type accelerator '$($Type.FullName)'"
+ 'Accelerator already exists.'
+ ) -join ' - '
+
+ throw [System.Management.Automation.ErrorRecord]::new(
+ [System.InvalidOperationException]::new($Message),
+ 'TypeAcceleratorAlreadyExists',
+ [System.Management.Automation.ErrorCategory]::InvalidOperation,
+ $Type.FullName
+ )
+ }
+}
+# Add type accelerators for every exportable type.
+foreach ($Type in $ExportableTypes) {
+ $TypeAcceleratorsClass::Add($Type.FullName, $Type)
+}
+# Remove type accelerators when the module is removed.
+$MyInvocation.MyCommand.ScriptBlock.Module.OnRemove = {
+ foreach ($Type in $ExportableTypes) {
+ $TypeAcceleratorsClass::Remove($Type.FullName)
+ }
+}.GetNewClosure()
+
+$Param = @{
+ Function = (Get-ChildItem -Path "$PSScriptRoot\public" -Include '*.ps1' -Recurse).BaseName
+ Variable = '*'
+ Cmdlet = '*'
+ Alias = '*'
+}
+
+Write-Verbose 'Exporting module members'
+
+Export-ModuleMember @Param
diff --git a/tests/src/PSModuleTest/assemblies/LsonLib.dll b/tests/src/PSModuleTest/assemblies/LsonLib.dll
new file mode 100644
index 0000000..3661807
Binary files /dev/null and b/tests/src/PSModuleTest/assemblies/LsonLib.dll differ
diff --git a/tests/src/PSModuleTest/classes/Book.ps1 b/tests/src/PSModuleTest/classes/Book.ps1
new file mode 100644
index 0000000..3e22c27
--- /dev/null
+++ b/tests/src/PSModuleTest/classes/Book.ps1
@@ -0,0 +1,132 @@
+class Book {
+ # Class properties
+ [string] $Title
+ [string] $Author
+ [string] $Synopsis
+ [string] $Publisher
+ [datetime] $PublishDate
+ [int] $PageCount
+ [string[]] $Tags
+ # Default constructor
+ Book() { $this.Init(@{}) }
+ # Convenience constructor from hashtable
+ Book([hashtable]$Properties) { $this.Init($Properties) }
+ # Common constructor for title and author
+ Book([string]$Title, [string]$Author) {
+ $this.Init(@{Title = $Title; Author = $Author })
+ }
+ # Shared initializer method
+ [void] Init([hashtable]$Properties) {
+ foreach ($Property in $Properties.Keys) {
+ $this.$Property = $Properties.$Property
+ }
+ }
+ # Method to calculate reading time as 2 minutes per page
+ [timespan] GetReadingTime() {
+ if ($this.PageCount -le 0) {
+ throw 'Unable to determine reading time from page count.'
+ }
+ $Minutes = $this.PageCount * 2
+ return [timespan]::new(0, $Minutes, 0)
+ }
+ # Method to calculate how long ago a book was published
+ [timespan] GetPublishedAge() {
+ if (
+ $null -eq $this.PublishDate -or
+ $this.PublishDate -eq [datetime]::MinValue
+ ) { throw 'PublishDate not defined' }
+
+ return (Get-Date) - $this.PublishDate
+ }
+ # Method to return a string representation of the book
+ [string] ToString() {
+ return "$($this.Title) by $($this.Author) ($($this.PublishDate.Year))"
+ }
+}
+
+class BookList {
+ # Static property to hold the list of books
+ static [System.Collections.Generic.List[Book]] $Books
+ # Static method to initialize the list of books. Called in the other
+ # static methods to avoid needing to explicit initialize the value.
+ static [void] Initialize() { [BookList]::Initialize($false) }
+ static [bool] Initialize([bool]$force) {
+ if ([BookList]::Books.Count -gt 0 -and -not $force) {
+ return $false
+ }
+
+ [BookList]::Books = [System.Collections.Generic.List[Book]]::new()
+
+ return $true
+ }
+ # Ensure a book is valid for the list.
+ static [void] Validate([book]$Book) {
+ $Prefix = @(
+ 'Book validation failed: Book must be defined with the Title,'
+ 'Author, and PublishDate properties, but'
+ ) -join ' '
+ if ($null -eq $Book) { throw "$Prefix was null" }
+ if ([string]::IsNullOrEmpty($Book.Title)) {
+ throw "$Prefix Title wasn't defined"
+ }
+ if ([string]::IsNullOrEmpty($Book.Author)) {
+ throw "$Prefix Author wasn't defined"
+ }
+ if ([datetime]::MinValue -eq $Book.PublishDate) {
+ throw "$Prefix PublishDate wasn't defined"
+ }
+ }
+ # Static methods to manage the list of books.
+ # Add a book if it's not already in the list.
+ static [void] Add([Book]$Book) {
+ [BookList]::Initialize()
+ [BookList]::Validate($Book)
+ if ([BookList]::Books.Contains($Book)) {
+ throw "Book '$Book' already in list"
+ }
+
+ $FindPredicate = {
+ param([Book]$b)
+
+ $b.Title -eq $Book.Title -and
+ $b.Author -eq $Book.Author -and
+ $b.PublishDate -eq $Book.PublishDate
+ }.GetNewClosure()
+ if ([BookList]::Books.Find($FindPredicate)) {
+ throw "Book '$Book' already in list"
+ }
+
+ [BookList]::Books.Add($Book)
+ }
+ # Clear the list of books.
+ static [void] Clear() {
+ [BookList]::Initialize()
+ [BookList]::Books.Clear()
+ }
+ # Find a specific book using a filtering scriptblock.
+ static [Book] Find([scriptblock]$Predicate) {
+ [BookList]::Initialize()
+ return [BookList]::Books.Find($Predicate)
+ }
+ # Find every book matching the filtering scriptblock.
+ static [Book[]] FindAll([scriptblock]$Predicate) {
+ [BookList]::Initialize()
+ return [BookList]::Books.FindAll($Predicate)
+ }
+ # Remove a specific book.
+ static [void] Remove([Book]$Book) {
+ [BookList]::Initialize()
+ [BookList]::Books.Remove($Book)
+ }
+ # Remove a book by property value.
+ static [void] RemoveBy([string]$Property, [string]$Value) {
+ [BookList]::Initialize()
+ $Index = [BookList]::Books.FindIndex({
+ param($b)
+ $b.$Property -eq $Value
+ }.GetNewClosure())
+ if ($Index -ge 0) {
+ [BookList]::Books.RemoveAt($Index)
+ }
+ }
+}
diff --git a/tests/src/PSModuleTest/data/Config.psd1 b/tests/src/PSModuleTest/data/Config.psd1
new file mode 100644
index 0000000..fea4466
--- /dev/null
+++ b/tests/src/PSModuleTest/data/Config.psd1
@@ -0,0 +1,3 @@
+@{
+ RandomKey = 'RandomValue'
+}
diff --git a/tests/src/PSModuleTest/data/Settings.psd1 b/tests/src/PSModuleTest/data/Settings.psd1
new file mode 100644
index 0000000..bcfa7b4
--- /dev/null
+++ b/tests/src/PSModuleTest/data/Settings.psd1
@@ -0,0 +1,3 @@
+@{
+ RandomSetting = 'RandomSettingValue'
+}
diff --git a/tests/src/PSModuleTest/finally.ps1 b/tests/src/PSModuleTest/finally.ps1
new file mode 100644
index 0000000..e51c226
--- /dev/null
+++ b/tests/src/PSModuleTest/finally.ps1
@@ -0,0 +1,3 @@
+Write-Verbose '------------------------------' -Verbose
+Write-Verbose '--- THIS IS A LAST LOADER ---' -Verbose
+Write-Verbose '------------------------------' -Verbose
diff --git a/tests/src/PSModuleTest/formats/CultureInfo.Format.ps1xml b/tests/src/PSModuleTest/formats/CultureInfo.Format.ps1xml
new file mode 100644
index 0000000..a715e08
--- /dev/null
+++ b/tests/src/PSModuleTest/formats/CultureInfo.Format.ps1xml
@@ -0,0 +1,37 @@
+
+
+
+
+ System.Globalization.CultureInfo
+
+ System.Globalization.CultureInfo
+
+
+
+
+ 16
+
+
+ 16
+
+
+
+
+
+
+
+ LCID
+
+
+ Name
+
+
+ DisplayName
+
+
+
+
+
+
+
+
diff --git a/tests/src/PSModuleTest/formats/Mygciview.Format.ps1xml b/tests/src/PSModuleTest/formats/Mygciview.Format.ps1xml
new file mode 100644
index 0000000..4c972c2
--- /dev/null
+++ b/tests/src/PSModuleTest/formats/Mygciview.Format.ps1xml
@@ -0,0 +1,65 @@
+
+
+
+
+ mygciview
+
+ System.IO.DirectoryInfo
+ System.IO.FileInfo
+
+
+ PSParentPath
+
+
+
+
+
+ 7
+ Left
+
+
+
+ 26
+ Right
+
+
+
+ 26
+ Right
+
+
+
+ 14
+ Right
+
+
+
+ Left
+
+
+
+
+
+
+
+ ModeWithoutHardLink
+
+
+ LastWriteTime
+
+
+ CreationTime
+
+
+ Length
+
+
+ Name
+
+
+
+
+
+
+
+
diff --git a/tests/src/PSModuleTest/header.ps1 b/tests/src/PSModuleTest/header.ps1
new file mode 100644
index 0000000..cc1fde9
--- /dev/null
+++ b/tests/src/PSModuleTest/header.ps1
@@ -0,0 +1,3 @@
+[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidLongLines', '', Justification = 'Contains long links.')]
+[CmdletBinding()]
+param()
diff --git a/tests/src/PSModuleTest/init/initializer.ps1 b/tests/src/PSModuleTest/init/initializer.ps1
new file mode 100644
index 0000000..f4121d2
--- /dev/null
+++ b/tests/src/PSModuleTest/init/initializer.ps1
@@ -0,0 +1,3 @@
+Write-Verbose '-------------------------------' -Verbose
+Write-Verbose '--- THIS IS AN INITIALIZER ---' -Verbose
+Write-Verbose '-------------------------------' -Verbose
diff --git a/tests/src/PSModuleTest/modules/OtherPSModule.psm1 b/tests/src/PSModuleTest/modules/OtherPSModule.psm1
new file mode 100644
index 0000000..9e4353b
--- /dev/null
+++ b/tests/src/PSModuleTest/modules/OtherPSModule.psm1
@@ -0,0 +1,19 @@
+Function Get-OtherPSModule {
+ <#
+ .SYNOPSIS
+ Performs tests on a module.
+
+ .DESCRIPTION
+ A longer description of the function.
+
+ .EXAMPLE
+ Get-OtherPSModule -Name 'World'
+ #>
+ [CmdletBinding()]
+ param(
+ # Name of the person to greet.
+ [Parameter(Mandatory)]
+ [string] $Name
+ )
+ Write-Output "Hello, $Name!"
+}
diff --git a/tests/src/PSModuleTest/private/Get-InternalPSModule.ps1 b/tests/src/PSModuleTest/private/Get-InternalPSModule.ps1
new file mode 100644
index 0000000..3366e44
--- /dev/null
+++ b/tests/src/PSModuleTest/private/Get-InternalPSModule.ps1
@@ -0,0 +1,18 @@
+Function Get-InternalPSModule {
+ <#
+ .SYNOPSIS
+ Performs tests on a module.
+
+ .EXAMPLE
+ Test-PSModule -Name 'World'
+
+ "Hello, World!"
+ #>
+ [CmdletBinding()]
+ param (
+ # Name of the person to greet.
+ [Parameter(Mandatory)]
+ [string] $Name
+ )
+ Write-Output "Hello, $Name!"
+}
diff --git a/tests/src/PSModuleTest/private/Set-InternalPSModule.ps1 b/tests/src/PSModuleTest/private/Set-InternalPSModule.ps1
new file mode 100644
index 0000000..11c2fa1
--- /dev/null
+++ b/tests/src/PSModuleTest/private/Set-InternalPSModule.ps1
@@ -0,0 +1,22 @@
+Function Set-InternalPSModule {
+ <#
+ .SYNOPSIS
+ Performs tests on a module.
+
+ .EXAMPLE
+ Test-PSModule -Name 'World'
+
+ "Hello, World!"
+ #>
+ [Diagnostics.CodeAnalysis.SuppressMessageAttribute(
+ 'PSUseShouldProcessForStateChangingFunctions', '', Scope = 'Function',
+ Justification = 'Reason for suppressing'
+ )]
+ [CmdletBinding()]
+ param (
+ # Name of the person to greet.
+ [Parameter(Mandatory)]
+ [string] $Name
+ )
+ Write-Output "Hello, $Name!"
+}
diff --git a/tests/src/PSModuleTest/public/Get-PSModuleTest.ps1 b/tests/src/PSModuleTest/public/Get-PSModuleTest.ps1
new file mode 100644
index 0000000..0e9aacf
--- /dev/null
+++ b/tests/src/PSModuleTest/public/Get-PSModuleTest.ps1
@@ -0,0 +1,20 @@
+#Requires -Modules Utilities
+
+function Get-PSModuleTest {
+ <#
+ .SYNOPSIS
+ Performs tests on a module.
+
+ .EXAMPLE
+ Test-PSModule -Name 'World'
+
+ "Hello, World!"
+ #>
+ [CmdletBinding()]
+ param (
+ # Name of the person to greet.
+ [Parameter(Mandatory)]
+ [string] $Name
+ )
+ Write-Output "Hello, $Name!"
+}
diff --git a/tests/src/PSModuleTest/public/New-PSModuleTest.ps1 b/tests/src/PSModuleTest/public/New-PSModuleTest.ps1
new file mode 100644
index 0000000..7f26215
--- /dev/null
+++ b/tests/src/PSModuleTest/public/New-PSModuleTest.ps1
@@ -0,0 +1,24 @@
+#Requires -Modules @{ModuleName='PSSemVer'; ModuleVersion='1.0'}
+
+function New-PSModuleTest {
+ <#
+ .SYNOPSIS
+ Performs tests on a module.
+
+ .EXAMPLE
+ Test-PSModule -Name 'World'
+
+ "Hello, World!"
+ #>
+ [Diagnostics.CodeAnalysis.SuppressMessageAttribute(
+ 'PSUseShouldProcessForStateChangingFunctions', '', Scope = 'Function',
+ Justification = 'Reason for suppressing'
+ )]
+ [CmdletBinding()]
+ param (
+ # Name of the person to greet.
+ [Parameter(Mandatory)]
+ [string] $Name
+ )
+ Write-Output "Hello, $Name!"
+}
diff --git a/tests/src/PSModuleTest/public/Set-PSModuleTest.ps1 b/tests/src/PSModuleTest/public/Set-PSModuleTest.ps1
new file mode 100644
index 0000000..a87ac11
--- /dev/null
+++ b/tests/src/PSModuleTest/public/Set-PSModuleTest.ps1
@@ -0,0 +1,22 @@
+function Set-PSModuleTest {
+ <#
+ .SYNOPSIS
+ Performs tests on a module.
+
+ .EXAMPLE
+ Test-PSModule -Name 'World'
+
+ "Hello, World!"
+ #>
+ [Diagnostics.CodeAnalysis.SuppressMessageAttribute(
+ 'PSUseShouldProcessForStateChangingFunctions', '', Scope = 'Function',
+ Justification = 'Reason for suppressing'
+ )]
+ [CmdletBinding()]
+ param (
+ # Name of the person to greet.
+ [Parameter(Mandatory)]
+ [string] $Name
+ )
+ Write-Output "Hello, $Name!"
+}
diff --git a/tests/src/PSModuleTest/public/Test-PSModuleTest.ps1 b/tests/src/PSModuleTest/public/Test-PSModuleTest.ps1
new file mode 100644
index 0000000..26be2b9
--- /dev/null
+++ b/tests/src/PSModuleTest/public/Test-PSModuleTest.ps1
@@ -0,0 +1,18 @@
+function Test-PSModuleTest {
+ <#
+ .SYNOPSIS
+ Performs tests on a module.
+
+ .EXAMPLE
+ Test-PSModule -Name 'World'
+
+ "Hello, World!"
+ #>
+ [CmdletBinding()]
+ param (
+ # Name of the person to greet.
+ [Parameter(Mandatory)]
+ [string] $Name
+ )
+ Write-Output "Hello, $Name!"
+}
diff --git a/tests/src/PSModuleTest/scripts/loader.ps1 b/tests/src/PSModuleTest/scripts/loader.ps1
new file mode 100644
index 0000000..29ad42f
--- /dev/null
+++ b/tests/src/PSModuleTest/scripts/loader.ps1
@@ -0,0 +1,3 @@
+Write-Verbose '-------------------------' -Verbose
+Write-Verbose '--- THIS IS A LOADER ---' -Verbose
+Write-Verbose '-------------------------' -Verbose
diff --git a/tests/src/PSModuleTest/types/DirectoryInfo.Types.ps1xml b/tests/src/PSModuleTest/types/DirectoryInfo.Types.ps1xml
new file mode 100644
index 0000000..aef538b
--- /dev/null
+++ b/tests/src/PSModuleTest/types/DirectoryInfo.Types.ps1xml
@@ -0,0 +1,21 @@
+
+
+
+ System.IO.FileInfo
+
+
+ Status
+ Success
+
+
+
+
+ System.IO.DirectoryInfo
+
+
+ Status
+ Success
+
+
+
+
diff --git a/tests/src/PSModuleTest/types/FileInfo.Types.ps1xml b/tests/src/PSModuleTest/types/FileInfo.Types.ps1xml
new file mode 100644
index 0000000..4cfaf6b
--- /dev/null
+++ b/tests/src/PSModuleTest/types/FileInfo.Types.ps1xml
@@ -0,0 +1,14 @@
+
+
+
+ System.IO.FileInfo
+
+
+ Age
+
+ ((Get-Date) - ($this.CreationTime)).Days
+
+
+
+
+