Skip to content

Conversation

@dan-hughes
Copy link
Contributor

@dan-hughes dan-hughes commented Sep 1, 2025

Pull Request (PR) description

Improve performance by not using Array addition operation.

This Pull Request (PR) fixes the following issues

Task list

  • Added an entry to the change log under the Unreleased section of the file CHANGELOG.md.
    Entry should say what was changed and how that affects users (if applicable), and
    reference the issue being resolved (if applicable).
  • Documentation added/updated in README.md.
  • Comment-based help added/updated.
  • Localization strings added/updated in all localization files as appropriate.
  • Examples appropriately added/updated.
  • Unit tests added/updated. See DSC Community Testing Guidelines.
  • Integration tests added/updated (where possible). See DSC Community Testing Guidelines.
  • New/changed code adheres to DSC Community Style Guidelines.

This change is Reviewable

@coderabbitai
Copy link

coderabbitai bot commented Sep 1, 2025

Walkthrough

Refactors multiple scripts to replace array mutation via += with foreach-expression assignments for collecting items; adjusts tag/exclude-tag construction in Invoke-DscResourceTest similarly; updates HQRM build task collection; adds an Unreleased CHANGELOG entry documenting removal of array addition in listed functions.

Changes

Cohort / File(s) Summary
Private helpers: foreach-expression collection
source/Private/Get-ClassResourceNameFromFile.ps1, source/Private/Get-ModuleScriptResourceName.ps1, source/Private/Get-SuppressedPSSARuleNameList.ps1
Replace imperative array accumulation using += with declarative foreach-expression assignments that yield items directly. Minor formatting tweak in an AttributeAst FindAll call. No signature or public API changes.
Public: tag filter construction refactor
source/Public/Invoke-DscResourceTest.ps1
Rebuild TagFilter and ExcludeTagFilter using foreach-expressions that yield only passing items (preserving debug logs); assign filters only when resulting collections have items. No signature changes.
Build task: Pester containers collection
source/tasks/Invoke_HQRM_Tests.build.ps1
Collect New-PesterContainer outputs via a single foreach-expression assignment instead of iterative += accumulation.
Docs: changelog update
CHANGELOG.md
Add Unreleased/Changed entry noting removal of array addition in the listed functions.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  participant Caller
  participant Script as "Script (function)"
  participant Collector as "Collection (was accumulator)"

  Note over Script,Collector #D3E4CD: Old flow (imperative accumulation)
  Caller->>Script: call function
  Script->>Collector: init empty array []
  loop for each item
    Script->>Collector: append item via +=
  end
  Script->>Caller: return collected array

  Note over Script,Collector #FCE8D6: New flow (foreach-expression)
  Caller->>Script: call function
  Script->>Collector: evaluate foreach-expression (yields items)
  Script->>Caller: return resulting array
Loading

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes


📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • Jira integration is disabled
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 09f7a5d and 8ed3f3b.

📒 Files selected for processing (1)
  • source/Private/Get-ModuleScriptResourceName.ps1 (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • source/Private/Get-ModuleScriptResourceName.ps1
✨ Finishing Touches
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR/Issue comments)

Type @coderabbitai help to get the list of available commands.

Other keywords and placeholders

  • Add @coderabbitai ignore or @coderabbit ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Status, Documentation and Community

  • Visit our Status Page to check the current availability of CodeRabbit.
  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@codecov
Copy link

codecov bot commented Sep 1, 2025

Codecov Report

✅ All modified and coverable lines are covered by tests.
⚠️ Please upload report for BASE (main@ee4d708). Learn more about missing BASE report.
⚠️ Report is 1 commits behind head on main.

Additional details and impacted files

Impacted file tree graph

@@          Coverage Diff          @@
##             main   #173   +/-   ##
=====================================
  Coverage        ?    80%           
=====================================
  Files           ?     42           
  Lines           ?    550           
  Branches        ?      0           
=====================================
  Hits            ?    443           
  Misses          ?    107           
  Partials        ?      0           
Files with missing lines Coverage Δ
source/Private/Get-ClassResourceNameFromFile.ps1 100% <100%> (ø)
source/Private/Get-ModuleScriptResourceName.ps1 100% <100%> (ø)
source/Private/Get-SuppressedPSSARuleNameList.ps1 100% <100%> (ø)
source/Public/Invoke-DscResourceTest.ps1 56% <100%> (ø)
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
source/Public/Invoke-DscResourceTest.ps1 (2)

374-389: Don’t overwrite existing TagFilter; merge with it and fix -notin RHS to array.

Current logic replaces any existing TagFilter (e.g., from $Settings) with $newTag. Also, using a scalar RHS in -notin can enumerate string characters. Merge the sets and coerce RHS to arrays.

-        $newTag = foreach ($optInTag in $optIns)
+        $newTag = foreach ($optInTag in $optIns)
         {
-            if (
-                $optInTag -notin $PSBoundParameters['ExcludeTagFilter'] `
-                -and $optInTag -notin $PSBoundParameters['TagFilter']
-            )
+            if (
+                $optInTag -notin @($PSBoundParameters['ExcludeTagFilter']) -and
+                $optInTag -notin @($PSBoundParameters['TagFilter'])
+            )
             {
                 Write-Debug -Message "Adding tag $optInTag."
-                $optInTag
+                $optInTag
             }
         }
 
-        if ($newTag.Count -gt 0)
-        {
-            $PSBoundParameters['TagFilter'] = $newTag
-        }
+        if ($newTag)
+        {
+            $PSBoundParameters['TagFilter'] = @(
+                $PSBoundParameters['TagFilter']
+                $newTag
+            ) | Where-Object { -not [string]::IsNullOrWhiteSpace($_) } | Select-Object -Unique
+        }

392-408: Same issue for ExcludeTagFilter; preserve existing and coerce RHS to arrays.

Prevent clobbering an existing ExcludeTagFilter and avoid string RHS pitfalls with -notin.

-        $newExcludeTag = foreach ($optOutTag in $optOuts)
+        $newExcludeTag = foreach ($optOutTag in $optOuts)
         {
-            if (
-                $optOutTag -notin $PSBoundParameters['TagFilter'] `
-                -and $optOutTag -notin $PSBoundParameters['ExcludeTagFilter']
-            )
+            if (
+                $optOutTag -notin @($PSBoundParameters['TagFilter']) -and
+                $optOutTag -notin @($PSBoundParameters['ExcludeTagFilter'])
+            )
             {
                 Write-Debug -Message "Adding ExcludeTag $optOutTag."
 
-                $optOutTag
+                $optOutTag
             }
         }
 
-        if ($newExcludeTag.Count -gt 0)
-        {
-            $PSBoundParameters['ExcludeTagFilter'] = $newExcludeTag
-        }
+        if ($newExcludeTag)
+        {
+            $PSBoundParameters['ExcludeTagFilter'] = @(
+                $PSBoundParameters['ExcludeTagFilter']
+                $newExcludeTag
+            ) | Where-Object { -not [string]::IsNullOrWhiteSpace($_) } | Select-Object -Unique
+        }
🧹 Nitpick comments (3)
CHANGELOG.md (1)

56-61: Tighten wording and file name in Changed entry.

Grammar nit and a filename detail; propose small edit.

- - Remove array addition in following Public/Private functions.
+ - Remove array addition in the following functions and build task.
   - `Get-ClassResourceNameFromFile`
   - `Get-ModuleScriptResourceName`
   - `Get-SuppressedPSSARuleNameList`
   - `Invoke-DscResourceTest`
-  - `Invoke_HQRM_Tests.build`
+  - `Invoke_HQRM_Tests.build.ps1`
source/tasks/Invoke_HQRM_Tests.build.ps1 (1)

425-428: Good swap to foreach-expression; consider ensuring a stable, non-null container list.

Current code can yield $null when no files are found; also ordering may vary by filesystem. Optional guard + deterministic order below.

-    $pesterContainers = foreach ($testScript in $hqrmTestScripts)
+    $pesterContainers = foreach ($testScript in $hqrmTestScripts)
     {
         New-PesterContainer -Path $testScript.FullName -Data $pesterData.Clone()
     }
+    # Ensure array type even when no scripts are found.
+    $pesterContainers = @($pesterContainers)

Outside the changed hunk (context):

# Prefer deterministic order and files only.
$hqrmTestScripts = Get-ChildItem -Path $pathToHqrmTests -File -Filter '*.Tests.ps1' | Sort-Object -Property FullName
source/Private/Get-ClassResourceNameFromFile.ps1 (1)

32-37: Preserve consistent output type for empty matches.

Foreach-expression can yield $null when no classes match. Coerce to an empty string array to align with [OutputType([String[]])].

         $classResourceNames = foreach ($typeDefinitionAst in $typeDefinitionAsts)
         {
             if ($typeDefinitionAst.Attributes.TypeName.Name -ieq 'DscResource')
             {
                 $typeDefinitionAst.Name
             }
         }
+        # Ensure consistent array output for callers relying on enumeration or .Count.
+        $classResourceNames = @([string[]] $classResourceNames)
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • Jira integration is disabled
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between ee4d708 and 09f7a5d.

📒 Files selected for processing (6)
  • CHANGELOG.md (1 hunks)
  • source/Private/Get-ClassResourceNameFromFile.ps1 (1 hunks)
  • source/Private/Get-ModuleScriptResourceName.ps1 (1 hunks)
  • source/Private/Get-SuppressedPSSARuleNameList.ps1 (1 hunks)
  • source/Public/Invoke-DscResourceTest.ps1 (3 hunks)
  • source/tasks/Invoke_HQRM_Tests.build.ps1 (1 hunks)
🧰 Additional context used
📓 Path-based instructions (5)
**

⚙️ CodeRabbit configuration file

**: # DSC Community Guidelines

Terminology

  • Command: Public command
  • Function: Private function
  • Resource: DSC class-based resource

Build & Test Workflow

  • Run in PowerShell, from repository root
  • Build before running tests: .\build.ps1 -Tasks build
  • Always run tests in new PowerShell session: Invoke-Pester -Path @({test paths}) -Output Detailed

File Organization

  • Public commands: source/Public/{CommandName}.ps1
  • Private functions: source/Private/{FunctionName}.ps1
  • Unit tests: tests/Unit/{Classes|Public|Private}/{Name}.Tests.ps1
  • Integration tests: tests/Integration/Commands/{CommandName}.Integration.Tests.ps1

Requirements

  • Follow guidelines over existing code patterns
  • Always update CHANGELOG.md Unreleased section
  • Localize all strings using string keys; remove any orphaned string keys
  • Check DscResource.Common before creating private functions
  • Separate reusable logic into private functions
  • Add unit tests for all commands/functions/resources
  • Add integration tests for all public commands and resources

Files:

  • source/Private/Get-ModuleScriptResourceName.ps1
  • source/Private/Get-ClassResourceNameFromFile.ps1
  • CHANGELOG.md
  • source/tasks/Invoke_HQRM_Tests.build.ps1
  • source/Private/Get-SuppressedPSSARuleNameList.ps1
  • source/Public/Invoke-DscResourceTest.ps1
**/*.ps?(m|d)1

⚙️ CodeRabbit configuration file

**/*.ps?(m|d)1: # PowerShell Guidelines

Naming

  • Use descriptive names (3+ characters, no abbreviations)
  • Functions: PascalCase with Verb-Noun format using approved verbs
  • Parameters: PascalCase
  • Variables: camelCase
  • Keywords: lower-case
  • Classes: PascalCase
  • Include scope for script/global/environment variables: $script:, $global:, $env:

File naming

  • Class files: ###.ClassName.ps1 format (e.g. 001.SqlReason.ps1, 004.StartupParameters.ps1)

Formatting

Indentation & Spacing

  • Use 4 spaces (no tabs)
  • One space around operators: $a = 1 + 2
  • One space between type and variable: [String] $name
  • One space between keyword and parenthesis: if ($condition)
  • No spaces on empty lines
  • Try to limit lines to 120 characters

Braces

  • Newline before opening brace (except variable assignments)
  • One newline after opening brace
  • Two newlines after closing brace (one if followed by another brace or continuation)

Quotes

  • Use single quotes unless variable expansion is needed: 'text' vs "text $variable"

Arrays

  • Single line: @('one', 'two', 'three')
  • Multi-line: each element on separate line with proper indentation
  • Do not use the unary comma operator (,) in return statements to force
    an array

Hashtables

  • Empty: @{}
  • Each property on separate line with proper indentation
  • Properties: Use PascalCase

Comments

  • Single line: # Comment (capitalized, on own line)
  • Multi-line: <# Comment #> format (opening and closing brackets on own line), and indent text
  • No commented-out code

Comment-based help

  • Always add comment-based help to all functions and scripts
  • Comment-based help: SYNOPSIS, DESCRIPTION (40+ chars), PARAMETER, EXAMPLE sections before function/class
  • Comment-based help indentation: keywords 4 spaces, text 8 spaces
  • Include examples for all parameter sets and combinations
  • INPUTS: List each pipeline‑accepted type (one per line) with a 1‑line description.
  • OUTPUTS: Lis...

Files:

  • source/Private/Get-ModuleScriptResourceName.ps1
  • source/Private/Get-ClassResourceNameFromFile.ps1
  • source/tasks/Invoke_HQRM_Tests.build.ps1
  • source/Private/Get-SuppressedPSSARuleNameList.ps1
  • source/Public/Invoke-DscResourceTest.ps1
source/**/*.ps1

⚙️ CodeRabbit configuration file

source/**/*.ps1: # Localization Guidelines

Requirements

  • Localize all Write-Debug, Write-Verbose, Write-Error, Write-Warning and $PSCmdlet.ThrowTerminatingError() messages
  • Use localized string keys, not hardcoded strings
  • Assume $script:localizedData is available

String Files

  • Commands/functions: source/en-US/{MyModuleName}.strings.psd1
  • Class resources: source/en-US/{ResourceClassName}.strings.psd1

Key Naming Patterns

  • Format: Verb_FunctionName_Action (underscore separators), e.g. Get_Database_ConnectingToDatabase

String Format

ConvertFrom-StringData @'
    KeyName = Message with {0} placeholder. (PREFIX0001)
'@

String IDs

  • Format: (PREFIX####)
  • PREFIX: First letter of each word in class or function name (SqlSetup → SS, Get-SqlDscDatabase → GSDD)
  • Number: Sequential from 0001

Usage

Write-Verbose -Message ($script:localizedData.KeyName -f $value1)

Files:

  • source/Private/Get-ModuleScriptResourceName.ps1
  • source/Private/Get-ClassResourceNameFromFile.ps1
  • source/tasks/Invoke_HQRM_Tests.build.ps1
  • source/Private/Get-SuppressedPSSARuleNameList.ps1
  • source/Public/Invoke-DscResourceTest.ps1
**/*.md

⚙️ CodeRabbit configuration file

**/*.md: # Markdown Style Guidelines

  • Wrap lines at word boundaries when over 80 characters (except tables/code blocks)
  • Use 2 spaces for indentation
  • Use '1.' for all items in ordered lists (1/1/1 numbering style)
  • Disable MD013 rule by adding a comment for tables/code blocks exceeding 80 characters
  • Empty lines required before/after code blocks and headings (except before line 1)
  • Escape backslashes in file paths only (not in code blocks)
  • Code blocks must specify language identifiers

Text Formatting

  • Parameters: bold
  • Values/literals: inline code
  • Resource/module/product names: italic
  • Commands/files/paths: inline code

Files:

  • CHANGELOG.md
CHANGELOG.md

⚙️ CodeRabbit configuration file

CHANGELOG.md: # Changelog Guidelines

  • Always update the Unreleased section in CHANGELOG.md
  • Use Keep a Changelog format
  • Describe notable changes briefly, ≤2 items per change type
  • Reference issues using format issue #<issue_number>
  • No empty lines between list items in same section
  • Do not add item if there are already an existing item for the same change

Files:

  • CHANGELOG.md
🧠 Learnings (2)
📓 Common learnings
Learnt from: johlju
PR: dsccommunity/DscResource.Test#167
File: source/Private/Test-FileContainsClassResource.ps1:44-56
Timestamp: 2025-08-09T19:29:36.323Z
Learning: In the DscResource.Test repository, DSC resource attributes are consistently written as `[DscResource(...)]` rather than using variations like `[DscResourceAttribute()]` or fully qualified names like `[Microsoft.PowerShell.DesiredStateConfiguration.DscResource()]`. The Test-FileContainsClassResource function should focus on detecting the standard `[DscResource(...)]` pattern that is actually used in the codebase.
📚 Learning: 2025-08-09T19:29:36.323Z
Learnt from: johlju
PR: dsccommunity/DscResource.Test#167
File: source/Private/Test-FileContainsClassResource.ps1:44-56
Timestamp: 2025-08-09T19:29:36.323Z
Learning: In the DscResource.Test repository, DSC resource attributes are consistently written as `[DscResource(...)]` rather than using variations like `[DscResourceAttribute()]` or fully qualified names like `[Microsoft.PowerShell.DesiredStateConfiguration.DscResource()]`. The Test-FileContainsClassResource function should focus on detecting the standard `[DscResource(...)]` pattern that is actually used in the codebase.

Applied to files:

  • source/Private/Get-ClassResourceNameFromFile.ps1
  • CHANGELOG.md
  • source/Public/Invoke-DscResourceTest.ps1
🪛 LanguageTool
CHANGELOG.md

[grammar] ~56-~56: There might be a mistake here.
Context: ...tAnalyzer rule. - Remove array addition in following Public/Private functions. -...

(QB_NEW_EN)

Copy link
Member

@johlju johlju left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

:lgtm:

@johlju reviewed 5 of 6 files at r1, 1 of 1 files at r2, all commit messages.
Reviewable status: all files reviewed, 2 unresolved discussions (waiting on @dan-hughes)

@johlju johlju merged commit 9576d76 into dsccommunity:main Sep 1, 2025
11 of 12 checks passed
@dan-hughes dan-hughes deleted the remove-array-addition-in-functions branch September 1, 2025 16:22
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants