Skip to content

Commit

Permalink
Add rule summaries #12 (#21)
Browse files Browse the repository at this point in the history
- Added option to report rule summary using -As parameter of Invoke-PSRule #12
- Added performance benchmarks
  • Loading branch information
BernieWhite authored Dec 10, 2018
1 parent dfd53c7 commit b18afbc
Show file tree
Hide file tree
Showing 30 changed files with 709 additions and 384 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,4 @@
/**/*.user
/src/**/*-help.xml
/src/**/*.help.txt
/BenchmarkDotNet.Artifacts/
10 changes: 10 additions & 0 deletions .vscode/tasks.json
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,16 @@
"type": "shell",
"command": "Invoke-Build Analyze",
"problemMatcher": []
},
{
"label": "benchmark",
"type": "shell",
"command": "Invoke-Build Benchmark",
"problemMatcher": [],
"presentation": {
"clear": true,
"panel": "dedicated"
}
}
]
}
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

- Added rule tags to results to enable grouping and sorting [#14](https://github.com/BernieWhite/PSRule/issues/14)
- Added support to check for rule tag existence. Use `*` for tag value on `-Tag` parameter with `Invoke-PSRule` and `Get-PSRule`
- Added option to report rule summary using `-As` parameter of `Invoke-PSRule` [#12](https://github.com/BernieWhite/PSRule/issues/12)

## v0.1.0-B181212

Expand Down
4 changes: 4 additions & 0 deletions PSRule.build.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,10 @@ task TestModule Pester, PSScriptAnalyzer, {
}
}

task Benchmark {
dotnet run -p src/PSRule.Benchmark -f net472 -c Release -- benchmark --output $PWD;
}

# Synopsis: Run script analyzer
task Analyze Build, PSScriptAnalyzer, {

Expand Down
19 changes: 18 additions & 1 deletion PSRule.sln
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,19 @@ Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15
VisualStudioVersion = 15.0.28307.106
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PSRule", "src\PSRule\PSRule.csproj", "{ACAFD790-980B-4B64-912F-9BAD91DFF749}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PSRule", "src\PSRule\PSRule.csproj", "{ACAFD790-980B-4B64-912F-9BAD91DFF749}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PSRule.Benchmark", "src\PSRule.Benchmark\PSRule.Benchmark.csproj", "{0693DC93-1F72-410A-B77F-A81A92391995}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{8FFA09C2-4E4A-4F9D-89E4-744E3ACD5280}"
ProjectSection(SolutionItems) = preProject
dotnet.psess = dotnet.psess
EndProjectSection
EndProject
Global
GlobalSection(Performance) = preSolution
HasPerformanceSessions = true
EndGlobalSection
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
Expand All @@ -15,11 +25,18 @@ Global
{ACAFD790-980B-4B64-912F-9BAD91DFF749}.Debug|Any CPU.Build.0 = Debug|Any CPU
{ACAFD790-980B-4B64-912F-9BAD91DFF749}.Release|Any CPU.ActiveCfg = Release|Any CPU
{ACAFD790-980B-4B64-912F-9BAD91DFF749}.Release|Any CPU.Build.0 = Release|Any CPU
{0693DC93-1F72-410A-B77F-A81A92391995}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{0693DC93-1F72-410A-B77F-A81A92391995}.Debug|Any CPU.Build.0 = Debug|Any CPU
{0693DC93-1F72-410A-B77F-A81A92391995}.Release|Any CPU.ActiveCfg = Release|Any CPU
{0693DC93-1F72-410A-B77F-A81A92391995}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {533491EB-BAE9-472E-B57F-A675ECD335B5}
EndGlobalSection
GlobalSection(Performance) = preSolution
HasPerformanceSessions = true
EndGlobalSection
EndGlobal
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ You can download and install the PSRule module from the PowerShell Gallery.

Module | Description | Downloads / instructions
------ | ----------- | ------------------------
PSRule | A PowerShell rules engine | [latest][psg-psrule] / [instructions][install]
PSRule | Validate objects using PowerShell rules | [latest][psg-psrule] / [instructions][install]

## Getting started

Expand Down
4 changes: 2 additions & 2 deletions azure-pipelines.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,10 @@ steps:
displayName: 'Publish test results'
inputs:
testRunTitle: 'Pester unit tests'
testResultsFormat: NUnit
testRunner: NUnit
testResultsFiles: 'reports/*.xml'
mergeTestResults: true
buildConfiguration: $(buildConfiguration)
configuration: $(buildConfiguration)
condition: succeededOrFailed()

# Generate artifacts
Expand Down
30 changes: 27 additions & 3 deletions docs/commands/PSRule/en-US/Invoke-PSRule.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ Evaluate pipeline objects against matching rules.

```text
Invoke-PSRule [[-Path] <String[]>] [-Name <String[]>] [-Tag <Hashtable>] -InputObject <PSObject>
[-Status <RuleResultOutcome>] [-Option <PSRuleOption>] [<CommonParameters>]
[-Status <RuleOutcome>] [-Option <PSRuleOption>] [-As <ResultFormat>] [<CommonParameters>]
```

## DESCRIPTION
Expand Down Expand Up @@ -87,7 +87,7 @@ Accept wildcard characters: False
Filter output to only show rules with a specific status.
```yaml
Type: RuleResultOutcome
Type: RuleOutcome
Parameter Sets: (All)
Aliases:
Accepted values: Success, Failed
Expand Down Expand Up @@ -133,6 +133,28 @@ Accept pipeline input: False
Accept wildcard characters: False
```

### -As

The format to return results. Results are returned using detailed by default.

The following result formats are available:

- `Detail` - Returns pass/ fail results for each individual object
- `Summary` - Returns summarized results for the rule and an overall outcome
- `Default` - Same as `Detail`.

```yaml
Type: ResultFormat
Parameter Sets: (All)
Aliases:
Required: False
Position: Named
Default value: None
Accept pipeline input: False
Accept wildcard characters: False
```

### CommonParameters

This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. For more information, see about_CommonParameters (http://go.microsoft.com/fwlink/?LinkID=113216).
Expand All @@ -143,7 +165,9 @@ This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable

## OUTPUTS

### PSRule.Rules.RuleResult
### PSRule.Rules.RuleRecord

### PSRule.Rules.RuleSummaryRecord

## NOTES

Expand Down
11 changes: 11 additions & 0 deletions src/PSRule.Benchmark/Benchmark.Rule.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
#
# A set of benchmark rules for testing PSRule performance
#

Rule 'BenchmarkOdd' -If { ($TargetObject.Name % 2) -gt 0 } {
Hint 'Odd message'
}

Rule 'BenchmarkEven' -If { ($TargetObject.Name % 2) -ge 0 } {
Hint 'Even message'
}
45 changes: 45 additions & 0 deletions src/PSRule.Benchmark/PSRule.Benchmark.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFrameworks>netcoreapp2.1;net472</TargetFrameworks>
</PropertyGroup>

<!-- <PropertyGroup Condition="'$(Configuration)|$(TargetFramework)|$(Platform)'=='Release|netcoreapp2.1|AnyCPU'">
<Optimize>false</Optimize>
<DefineConstants>TRACE</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(TargetFramework)|$(Platform)'=='Debug|netcoreapp2.1|AnyCPU'">
<DefineConstants />
</PropertyGroup> -->

<PropertyGroup Condition="'$(Configuration)'=='Release'">
<DefineConstants>TRACE;BENCHMARK</DefineConstants>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="BenchmarkDotNet" Version="0.11.3" />
<PackageReference Include="Microsoft.Extensions.CommandLineUtils" Version="1.1.1" />
</ItemGroup>

<ItemGroup Condition="'$(TargetFramework)' == 'net472'">
<PackageReference Include="BenchmarkDotNet.Diagnostics.Windows" Version="0.11.3" />
<PackageReference Include="PowerShellStandard.Library" Version="5.1.0" PrivateAssets="All" />
</ItemGroup>

<ItemGroup Condition="'$(TargetFramework)' == 'netcoreapp2.1'">
<PackageReference Include="Microsoft.PowerShell.SDK" Version="6.1.1" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\PSRule\PSRule.csproj" />
</ItemGroup>

<ItemGroup>
<None Update="Benchmark.Rule.ps1">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>

</Project>
61 changes: 61 additions & 0 deletions src/PSRule.Benchmark/PSRule.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
using BenchmarkDotNet.Attributes;
using PSRule.Pipeline;
using PSRule.Rules;
using System;
using System.Collections.Generic;
using System.Management.Automation;
using System.Linq;
using BenchmarkDotNet.Engines;
using System.IO;
using System.Reflection;

namespace PSRule.Benchmark
{
/// <summary>
/// Define a set of benchmarks for performance testing PSRule internals.
/// </summary>
[MemoryDiagnoser]
[MarkdownExporterAttribute.GitHub]
public class PSRule
{
private PSObject[] _TargetObject;
private InvokeRulePipeline _Invoke;

public sealed class TargetObject
{
public TargetObject(string name, string message)
{
Name = name;
Message = message;
}

public string Name { get; private set; }

public string Message { get; private set; }
}

[GlobalSetup]
public void Prepare()
{
var builder = PipelineBuilder.Invoke();

builder.Source(new string[] { Path.Combine(Path.GetDirectoryName(Assembly.GetEntryAssembly().Location), "Benchmark.Rule.ps1") });
_Invoke = builder.Build();

var r = new Random();
var randomBuffer = new byte[40];
var targetObjects = new List<PSObject>();
while (targetObjects.Count < 1000)
{
r.NextBytes(randomBuffer);
var o = new TargetObject(name: targetObjects.Count.ToString(), message: Convert.ToBase64String(randomBuffer));
targetObjects.Add(PSObject.AsPSObject(o));
}

_TargetObject = targetObjects.ToArray();
}

[Benchmark]
public void Invoke() => _Invoke.Process(_TargetObject).Consume(new Consumer());
}
}
87 changes: 87 additions & 0 deletions src/PSRule.Benchmark/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
using BenchmarkDotNet.Analysers;
using BenchmarkDotNet.Columns;
using BenchmarkDotNet.Configs;
using BenchmarkDotNet.Exporters;
using BenchmarkDotNet.Loggers;
using BenchmarkDotNet.Running;
using Microsoft.Extensions.CommandLineUtils;
using PSRule.Pipeline;
using System;

namespace PSRule.Benchmark
{
internal sealed class Program
{
private class BenchmarkConfig : ManualConfig
{
public BenchmarkConfig(string artifactsPath)
{
ArtifactsPath = artifactsPath;
}
}

static void Main(string[] args)
{

var app = new CommandLineApplication();
app.Name = "PSRule Benchmark";
app.Description = "";

#if !BENCHMARK
// Do profiling
DebugProfile();
#endif

#if BENCHMARK
RunProfile(app);
app.Execute(args);
#endif
}

private static void RunProfile(CommandLineApplication app)
{
var config = ManualConfig.CreateEmpty()
.With(ConsoleLogger.Default)
.With(DefaultColumnProviders.Instance)
.With(EnvironmentAnalyser.Default)
.With(OutliersAnalyser.Default)
.With(MinIterationTimeAnalyser.Default)
.With(MultimodalDistributionAnalyzer.Default)
.With(RuntimeErrorAnalyser.Default)
.With(ZeroMeasurementAnalyser.Default);

app.Command("benchmark", cmd =>
{
var output = cmd.Option("-o | --output", "The path to store report output.", CommandOptionType.SingleValue);

cmd.OnExecute(() =>
{
if (output.HasValue())
{
config.WithArtifactsPath(output.Value());
}

// Do benchmarks
var summary = BenchmarkRunner.Run<PSRule>(config);

return 0;
});

cmd.HelpOption("-? | -h | --help");
});

app.HelpOption("-? | -h | --help");
}

private static void DebugProfile()
{
var profile = new PSRule();
profile.Prepare();

for (var i = 0; i < 1000; i++)
{
profile.Invoke();
}
}
}
}
8 changes: 8 additions & 0 deletions src/PSRule.Benchmark/Properties/launchSettings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"profiles": {
"PSRule.Benchmark": {
"commandName": "Project",
"commandLineArgs": "benchmark"
}
}
}
4 changes: 2 additions & 2 deletions src/PSRule/Commands/RuleKeyword.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@ namespace PSRule.Commands
/// </summary>
internal abstract class RuleKeyword : PSCmdlet
{
protected RuleResult GetResult()
protected RuleRecord GetResult()
{
return GetVariableValue("Rule") as RuleResult;
return GetVariableValue("Rule") as RuleRecord;
}

protected bool GetField(object obj, string name, out object value)
Expand Down
Loading

0 comments on commit b18afbc

Please sign in to comment.