Skip to content

Commit 8ba1409

Browse files
author
Quoc Truong
committed
Fix test failures and use tasks instead of backgroundworkers
1 parent bb1cf1d commit 8ba1409

14 files changed

+87
-101
lines changed

Engine/Commands/InvokeScriptAnalyzerCommand.cs

Lines changed: 72 additions & 86 deletions
Original file line numberDiff line numberDiff line change
@@ -271,14 +271,6 @@ private void ProcessPath(string path)
271271

272272
}
273273

274-
ConcurrentBag<DiagnosticRecord> diagnostics;
275-
ConcurrentBag<SuppressedRecord> suppressed;
276-
Dictionary<string, List<RuleSuppression>> ruleSuppressions;
277-
List<Regex> includeRegexList;
278-
List<Regex> excludeRegexList;
279-
CountdownEvent cde;
280-
ConcurrentDictionary<string, List<object>> ruleDictionary;
281-
282274
/// <summary>
283275
/// Analyzes a single script file.
284276
/// </summary>
@@ -287,16 +279,16 @@ private void AnalyzeFile(string filePath)
287279
{
288280
Token[] tokens = null;
289281
ParseError[] errors = null;
290-
diagnostics = new ConcurrentBag<DiagnosticRecord>();
291-
suppressed = new ConcurrentBag<SuppressedRecord>();
292-
ruleDictionary = new ConcurrentDictionary<string, List<object>>();
282+
ConcurrentBag<DiagnosticRecord> diagnostics = new ConcurrentBag<DiagnosticRecord>();
283+
ConcurrentBag<SuppressedRecord> suppressed = new ConcurrentBag<SuppressedRecord>();
284+
BlockingCollection<List<object>> verboseOrErrors = new BlockingCollection<List<object>>();
293285

294286
// Use a List of KVP rather than dictionary, since for a script containing inline functions with same signature, keys clash
295287
List<KeyValuePair<CommandInfo, IScriptExtent>> cmdInfoTable = new List<KeyValuePair<CommandInfo, IScriptExtent>>();
296288

297289
//Check wild card input for the Include/ExcludeRules and create regex match patterns
298-
includeRegexList = new List<Regex>();
299-
excludeRegexList = new List<Regex>();
290+
List<Regex> includeRegexList = new List<Regex>();
291+
List<Regex> excludeRegexList = new List<Regex>();
300292
if (includeRule != null)
301293
{
302294
foreach (string rule in includeRule)
@@ -344,7 +336,7 @@ private void AnalyzeFile(string filePath)
344336
return;
345337
}
346338

347-
ruleSuppressions = Helper.Instance.GetRuleSuppression(ast);
339+
Dictionary<string, List<RuleSuppression>> ruleSuppressions = Helper.Instance.GetRuleSuppression(ast);
348340

349341
foreach (List<RuleSuppression> ruleSuppressionsList in ruleSuppressions.Values)
350342
{
@@ -373,24 +365,77 @@ private void AnalyzeFile(string filePath)
373365

374366
if (ScriptAnalyzer.Instance.ScriptRules != null)
375367
{
376-
cde = new CountdownEvent(ScriptAnalyzer.Instance.ScriptRules.Count());
368+
var tasks = ScriptAnalyzer.Instance.ScriptRules.Select(scriptRule => Task.Factory.StartNew(() =>
369+
{
370+
bool includeRegexMatch = false;
371+
bool excludeRegexMatch = false;
377372

378-
foreach (var scriptRule in ScriptAnalyzer.Instance.ScriptRules)
379-
{
380-
BackgroundWorker bg = new BackgroundWorker();
381-
bg.DoWork += bg_DoWork;
382-
bg.RunWorkerAsync(new object[] { scriptRule });
383-
}
373+
foreach (Regex include in includeRegexList)
374+
{
375+
if (include.IsMatch(scriptRule.GetName()))
376+
{
377+
includeRegexMatch = true;
378+
break;
379+
}
380+
}
381+
382+
foreach (Regex exclude in excludeRegexList)
383+
{
384+
if (exclude.IsMatch(scriptRule.GetName()))
385+
{
386+
excludeRegexMatch = true;
387+
break;
388+
}
389+
}
390+
391+
if ((includeRule == null || includeRegexMatch) && (excludeRule == null || !excludeRegexMatch))
392+
{
393+
List<object> result = new List<object>();
394+
395+
//WriteVerbose(string.Format(CultureInfo.CurrentCulture, Strings.VerboseRunningMessage, scriptRule.GetName()));
396+
result.Add(string.Format(CultureInfo.CurrentCulture, Strings.VerboseRunningMessage, scriptRule.GetName()));
397+
398+
// Ensure that any unhandled errors from Rules are converted to non-terminating errors
399+
// We want the Engine to continue functioning even if one or more Rules throws an exception
400+
try
401+
{
402+
var records = Helper.Instance.SuppressRule(scriptRule.GetName(), ruleSuppressions, scriptRule.AnalyzeScript(ast, ast.Extent.File).ToList());
403+
foreach (var record in records.Item2)
404+
{
405+
diagnostics.Add(record);
406+
}
407+
foreach (var suppressedRec in records.Item1)
408+
{
409+
suppressed.Add(suppressedRec);
410+
}
411+
}
412+
catch (Exception scriptRuleException)
413+
{
414+
result.Add(new ErrorRecord(scriptRuleException, Strings.RuleErrorMessage, ErrorCategory.InvalidOperation, ast.Extent.File));
415+
}
384416

385-
cde.Wait();
417+
verboseOrErrors.Add(result);
418+
}
419+
}));
386420

387-
foreach (var rule in ruleDictionary.Keys)
421+
Task.Factory.ContinueWhenAll(tasks.ToArray(), t => verboseOrErrors.CompleteAdding());
422+
423+
while (!verboseOrErrors.IsCompleted)
388424
{
389-
List<object> verboseOrErrors = ruleDictionary[rule];
390-
WriteVerbose(verboseOrErrors[0] as string);
391-
if (verboseOrErrors.Count == 2)
425+
List<object> data = null;
426+
try
427+
{
428+
data = verboseOrErrors.Take();
429+
}
430+
catch (InvalidOperationException) { }
431+
432+
if (data != null)
392433
{
393-
WriteError(verboseOrErrors[1] as ErrorRecord);
434+
WriteVerbose(data[0] as string);
435+
if (data.Count == 2)
436+
{
437+
WriteError(data[1] as ErrorRecord);
438+
}
394439
}
395440
}
396441
}
@@ -620,65 +665,6 @@ private void AnalyzeFile(string filePath)
620665
}
621666
}
622667

623-
void bg_DoWork(object sender, DoWorkEventArgs e)
624-
{
625-
bool includeRegexMatch = false;
626-
bool excludeRegexMatch = false;
627-
628-
object[] parameters = e.Argument as object[];
629-
630-
IScriptRule scriptRule = parameters[0] as IScriptRule;
631-
632-
foreach (Regex include in includeRegexList)
633-
{
634-
if (include.IsMatch(scriptRule.GetName()))
635-
{
636-
includeRegexMatch = true;
637-
break;
638-
}
639-
}
640-
641-
foreach (Regex exclude in excludeRegexList)
642-
{
643-
if (exclude.IsMatch(scriptRule.GetName()))
644-
{
645-
excludeRegexMatch = true;
646-
break;
647-
}
648-
}
649-
650-
List<object> result = new List<object>();
651-
652-
if ((includeRule == null || includeRegexMatch) && (excludeRule == null || !excludeRegexMatch))
653-
{
654-
//WriteVerbose(string.Format(CultureInfo.CurrentCulture, Strings.VerboseRunningMessage, scriptRule.GetName()));
655-
result.Add(string.Format(CultureInfo.CurrentCulture, Strings.VerboseRunningMessage, scriptRule.GetName()));
656-
657-
// Ensure that any unhandled errors from Rules are converted to non-terminating errors
658-
// We want the Engine to continue functioning even if one or more Rules throws an exception
659-
try
660-
{
661-
var records = Helper.Instance.SuppressRule(scriptRule.GetName(), ruleSuppressions, scriptRule.AnalyzeScript(ast, ast.Extent.File).ToList());
662-
foreach (var record in records.Item2)
663-
{
664-
diagnostics.Add(record);
665-
}
666-
foreach (var suppressedRec in records.Item1)
667-
{
668-
suppressed.Add(suppressedRec);
669-
}
670-
}
671-
catch (Exception scriptRuleException)
672-
{
673-
result.Add(new ErrorRecord(scriptRuleException, Strings.RuleErrorMessage, ErrorCategory.InvalidOperation, ast.Extent.File));
674-
}
675-
}
676-
677-
ruleDictionary[scriptRule.GetName()] = result;
678-
679-
cde.Signal();
680-
}
681-
682668
#endregion
683669
}
684670
}

Tests/Rules/AvoidGlobalOrUnitializedVars.tests.ps1

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,14 @@
22
$globalMessage = "Found global variable 'Global:1'."
33
$globalName = "PSAvoidGlobalVars"
44
$nonInitializedName = "PSAvoidUninitializedVariable"
5-
$nonInitializedMessage = "Variable 'a' is not initialized. Non-global variables must be initialized. To fix a violation of this rule, please initialize non-global variables."
5+
$nonInitializedMessage = "Variable 'globalVars' is not initialized. Non-global variables must be initialized. To fix a violation of this rule, please initialize non-global variables."
66
$directory = Split-Path -Parent $MyInvocation.MyCommand.Path
77
$violations = Invoke-ScriptAnalyzer $directory\AvoidGlobalOrUnitializedVars.ps1
88
$dscResourceViolations = Invoke-ScriptAnalyzer $directory\DSCResources\MSFT_WaitForAny\MSFT_WaitForAny.psm1 | Where-Object {$_.RuleName -eq $nonInitializedName}
99
$globalViolations = $violations | Where-Object {$_.RuleName -eq $globalName}
1010
$nonInitializedViolations = $violations | Where-Object {$_.RuleName -eq $nonInitializedName}
1111
$noViolations = Invoke-ScriptAnalyzer $directory\AvoidGlobalOrUnitializedVarsNoViolations.ps1
12-
$noGlobalViolations = $noViolations | Where-Object {$_.RuleName -eq $violationName}
12+
$noGlobalViolations = $noViolations | Where-Object {$_.RuleName -eq $globalName}
1313
$noUninitializedViolations = $noViolations | Where-Object {$_.RuleName -eq $nonInitializedName}
1414

1515
Describe "AvoidGlobalVars" {
@@ -23,7 +23,7 @@ Describe "AvoidGlobalVars" {
2323
}
2424

2525
It "has the correct description message" {
26-
$violations[0].Message | Should Match $globalMessage
26+
$globalViolations[0].Message | Should Match $globalMessage
2727
}
2828
}
2929

Tests/Rules/AvoidPositionalParameters.tests.ps1

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
Import-Module PSScriptAnalyzer
2-
$violationMessage = "Cmdlet 'Get-Content' has positional parameter. Please use named parameters instead of positional parameters when calling a command."
2+
$violationMessage = "Cmdlet 'Write-Host' has positional parameter. Please use named parameters instead of positional parameters when calling a command."
33
$violationName = "PSAvoidUsingPositionalParameters"
44
$directory = Split-Path -Parent $MyInvocation.MyCommand.Path
55
$violations = Invoke-ScriptAnalyzer $directory\AvoidPositionalParameters.ps1 | Where-Object {$_.RuleName -eq $violationName}

Tests/Rules/AvoidShouldContinueWithoutForce.tests.ps1

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
Import-Module PSScriptAnalyzer
2-
$violationMessage = "Function 'Verb-Noun' in file 'AvoidShouldContinueWithoutForce.ps1' uses ShouldContinue but does not have a boolean force parameter. The force parameter will allow users of the script to bypass ShouldContinue prompt"
2+
$violationMessage = "Function 'Verb-Noun2' in file 'AvoidShouldContinueWithoutForce.ps1' uses ShouldContinue but does not have a boolean force parameter. The force parameter will allow users of the script to bypass ShouldContinue prompt"
33
$violationName = "PSAvoidShouldContinueWithoutForce"
44
$directory = Split-Path -Parent $MyInvocation.MyCommand.Path
55
$violations = Invoke-ScriptAnalyzer $directory\AvoidShouldContinueWithoutForce.ps1 | Where-Object {$_.RuleName -eq $violationName}

Tests/Rules/AvoidUserNameAndPasswordParams.tests.ps1

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
Import-Module PSScriptAnalyzer
22

3-
$violationMessage = "Function 'TestFunction' has both username and password parameters. A credential parameter of type PSCredential should be used."
3+
$violationMessage = "Function 'Verb-Noun' has both username and password parameters. A credential parameter of type PSCredential should be used."
44
$violationName = "PSAvoidUsingUserNameAndPasswordParams"
55
$directory = Split-Path -Parent $MyInvocation.MyCommand.Path
66
$violations = Invoke-ScriptAnalyzer $directory\AvoidUserNameAndPasswordParams.ps1 | Where-Object {$_.RuleName -eq $violationName}

Tests/Rules/AvoidUsingAlias.tests.ps1

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
Import-Module PSScriptAnalyzer
2-
$violationMessage = "'iex' is an alias of 'Invoke-Expression'. Alias can introduce possible problems and make scripts hard to maintain. Please consider changing alias to its full content."
2+
$violationMessage = "'cls' is an alias of 'Clear-Host'. Alias can introduce possible problems and make scripts hard to maintain. Please consider changing alias to its full content."
33
$violationName = "PSAvoidUsingCmdletAliases"
44
$directory = Split-Path -Parent $MyInvocation.MyCommand.Path
55
$violations = Invoke-ScriptAnalyzer $directory\AvoidUsingAlias.ps1 | Where-Object {$_.RuleName -eq $violationName}

Tests/Rules/AvoidUsingPlainTextForPassword.tests.ps1

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
Import-Module PSScriptAnalyzer
22

3-
$violationMessage = [regex]::Escape("Parameter '`$passphrases' should use SecureString, otherwise this will expose sensitive information. See ConvertTo-SecureString for more information.")
3+
$violationMessage = [regex]::Escape("Parameter '`$password' should use SecureString, otherwise this will expose sensitive information. See ConvertTo-SecureString for more information.")
44
$violationName = "PSAvoidUsingPlainTextForPassword"
55
$directory = Split-Path -Parent $MyInvocation.MyCommand.Path
66
$violations = Invoke-ScriptAnalyzer $directory\AvoidUsingPlainTextForPassword.ps1 | Where-Object {$_.RuleName -eq $violationName}

Tests/Rules/AvoidUsingUninitializedVariable.Tests.ps1

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
Import-Module PSScriptAnalyzer
22
$AvoidUninitializedVariable = "PSAvoidUninitializedVariable"
3-
$violationMessage = "Variable 'MyProgressPreference' is not initialized. Non-global variables must be initialized. To fix a violation of this rule, please initialize non-global variables."
3+
$violationMessage = "Variable 'MyVerbosePreference' is not initialized. Non-global variables must be initialized. To fix a violation of this rule, please initialize non-global variables."
44
$directory = Split-Path -Parent $MyInvocation.MyCommand.Path
55
$violations = Invoke-ScriptAnalyzer $directory\AvoidUsingUninitializedVariable.ps1 -IncludeRule $AvoidUninitializedVariable
66
$noViolations = Invoke-ScriptAnalyzer $directory\AvoidUsingUninitializedVariableNoViolations.ps1 -IncludeRule $AvoidUninitializedVariable

Tests/Rules/ProvideCommentHelp.tests.ps1

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
Import-Module PSScriptAnalyzer
2-
$violationMessage = "The cmdlet 'Verb-Files' does not have a help comment."
2+
$violationMessage = "The cmdlet 'Comment' does not have a help comment."
33
$violationName = "PSProvideCommentHelp"
44
$directory = Split-Path -Parent $MyInvocation.MyCommand.Path
55
$violations = Invoke-ScriptAnalyzer $directory\BadCmdlet.ps1 | Where-Object {$_.RuleName -eq $violationName}

Tests/Rules/ProvideDefaultParameterValue.tests.ps1

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
Import-Module PSScriptAnalyzer
22
$violationName = "PSProvideDefaultParameterValue"
3-
$violationMessage = "Parameter 'Param2' is not initialized. Parameters must have a default value. To fix a violation of this rule, please specify a default value for all parameters"
3+
$violationMessage = "Parameter 'Param1' is not initialized. Parameters must have a default value. To fix a violation of this rule, please specify a default value for all parameters"
44
$directory = Split-Path -Parent $MyInvocation.MyCommand.Path
55
$violations = Invoke-ScriptAnalyzer $directory\ProvideDefaultParameterValue.ps1 | Where-Object {$_.RuleName -match $violationName}
66
$noViolations = Invoke-ScriptAnalyzer $directory\ProvideDefaultParameterValueNoViolations.ps1

Tests/Rules/ReturnCorrectTypesForDSCFunctions.tests.ps1

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
Import-Module -Verbose PSScriptAnalyzer
22

33
$violationMessageDSCResource = "Test-TargetResource function in DSC Resource should return object of type System.Boolean instead of System.Collections.Hashtable"
4-
$violationMessageDSCClass = "Test function in DSC Class FileResource should return object of type System.Boolean instead of type System.Int32"
4+
$violationMessageDSCClass = "Get function in DSC Class FileResource should return object of type FileResource instead of type System.Collections.Hashtable"
55
$violationName = "PSDSCReturnCorrectTypesForDSCFunctions"
66
$directory = Split-Path -Parent $MyInvocation.MyCommand.Path
77
$violations = Invoke-ScriptAnalyzer $directory\DSCResources\MSFT_WaitForAll\MSFT_WaitForAll.psm1 | Where-Object {$_.RuleName -eq $violationName}

Tests/Rules/UseDeclaredVarsMoreThanAssignments.tests.ps1

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
Import-Module PSScriptAnalyzer
2-
$violationMessage = "The variable 'declaredVar' is assigned but never used."
2+
$violationMessage = "The variable 'declaredVar2' is assigned but never used."
33
$violationName = "PSUseDeclaredVarsMoreThanAssigments"
44
$directory = Split-Path -Parent $MyInvocation.MyCommand.Path
55
$violations = Invoke-ScriptAnalyzer $directory\UseDeclaredVarsMoreThanAssignments.ps1 | Where-Object {$_.RuleName -eq $violationName}

Tests/Rules/UseOutputTypeCorrectly.tests.ps1

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
Import-Module PSScriptAnalyzer
2-
$violationMessage = "The cmdlet 'Verb-Files' returns an object of type 'System.Double' but this type is not declared in the OutputType attribute."
2+
$violationMessage = "The cmdlet 'Verb-Files' returns an object of type 'System.Collections.Hashtable' but this type is not declared in the OutputType attribute."
33
$violationName = "PSUseOutputTypeCorrectly"
44
$directory = Split-Path -Parent $MyInvocation.MyCommand.Path
55
$violations = Invoke-ScriptAnalyzer $directory\BadCmdlet.ps1 | Where-Object {$_.RuleName -eq $violationName}

Tests/Rules/UseVerboseMessageInDSCResource.Tests.ps1

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
Import-Module PSScriptAnalyzer
22

3-
$violationMessage = "There is no call to Write-Verbose in DSC function ‘Set-TargetResource’. If you are using Write-Verbose in a helper function, suppress this rule application."
3+
$violationMessage = "There is no call to Write-Verbose in DSC function ‘Test-TargetResource’. If you are using Write-Verbose in a helper function, suppress this rule application."
44
$violationName = "PSDSCUseVerboseMessageInDSCResource"
55
$directory = Split-Path -Parent $MyInvocation.MyCommand.Path
66
$violations = Invoke-ScriptAnalyzer $directory\DSCResources\MSFT_WaitForAll\MSFT_WaitForAll.psm1 | Where-Object {$_.RuleName -eq $violationName}

0 commit comments

Comments
 (0)