Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Replace IsOriginalValue() with IsModified and fix merging and casting of default option values #2201

Merged
merged 9 commits into from
Jul 25, 2022
13 changes: 12 additions & 1 deletion build.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -215,8 +215,19 @@ if ($Clean) {
$ViewDef = $formatViewCtor.Invoke(($section.FullName, $listControl, [guid]::NewGuid())) -as [System.Collections.Generic.List[System.Management.Automation.FormatViewDefinition]]
New-Object -TypeName 'System.Management.Automation.ExtendedTypeDefinition' $section.FullName, $ViewDef
}
Export-FormatData -InputObject $typeDefs -Path "$PSScriptRoot/bin/PesterConfiguration.Format.ps1xml"

# Create view for Option to ensure Table and hide IsModified
$builder = [System.Management.Automation.TableControl]::Create().StartRowDefinition()
[Pester.Option[bool]].GetProperties() | Where-Object Name -notin 'IsModified' | ForEach-Object {
$builder.AddPropertyColumn($_.Name, [System.Management.Automation.Alignment]::Undefined, $null) > $null
}
$tableControl = $builder.EndRowDefinition().EndTable()

$ViewDef = $formatViewCtor.Invoke(('Pester.Option', $tableControl, [guid]::NewGuid())) -as [System.Collections.Generic.List[System.Management.Automation.FormatViewDefinition]]
$typeDefs += New-Object -TypeName 'System.Management.Automation.ExtendedTypeDefinition' 'Pester.Option', $ViewDef

# Export all formatdata
Export-FormatData -InputObject $typeDefs -Path "$PSScriptRoot/bin/PesterConfiguration.Format.ps1xml"
}

if (-not $PSBoundParameters.ContainsKey("Inline")) {
Expand Down
21 changes: 10 additions & 11 deletions src/csharp/Pester/CodeCoverageConfiguration.cs
Original file line number Diff line number Diff line change
Expand Up @@ -57,17 +57,16 @@ public CodeCoverageConfiguration(IDictionary configuration) : this()
{
if (configuration != null)
{
Enabled = configuration.GetValueOrNull<bool>("Enabled") ?? Enabled;
OutputFormat = configuration.GetObjectOrNull<string>("OutputFormat") ?? OutputFormat;
OutputPath = configuration.GetObjectOrNull<string>("OutputPath") ?? OutputPath;
OutputEncoding = configuration.GetObjectOrNull<string>("OutputEncoding") ?? OutputEncoding;
Path = configuration.GetArrayOrNull<string>("Path") ?? Path;
ExcludeTests = configuration.GetValueOrNull<bool>("ExcludeTests") ?? ExcludeTests;
RecursePaths = configuration.GetValueOrNull<bool>("RecursePaths") ?? RecursePaths;
CoveragePercentTarget = configuration.GetValueOrNull<decimal>("CoveragePercentTarget") ?? CoveragePercentTarget;

SingleHitBreakpoints = configuration.GetValueOrNull<bool>("SingleHitBreakpoints") ?? SingleHitBreakpoints;
UseBreakpoints = configuration.GetValueOrNull<bool>("UseBreakpoints") ?? UseBreakpoints;
configuration.AssignValueIfNotNull<bool>(nameof(Enabled), v => Enabled = v);
configuration.AssignObjectIfNotNull<string>(nameof(OutputFormat), v => OutputFormat = v);
configuration.AssignObjectIfNotNull<string>(nameof(OutputPath), v => OutputPath = v);
configuration.AssignObjectIfNotNull<string>(nameof(OutputEncoding), v => OutputEncoding = v);
configuration.AssignArrayIfNotNull<string>(nameof(Path), v => Path = v);
configuration.AssignValueIfNotNull<bool>(nameof(ExcludeTests), v => ExcludeTests = v);
configuration.AssignValueIfNotNull<bool>(nameof(RecursePaths), v => RecursePaths = v);
configuration.AssignValueIfNotNull<bool>(nameof(UseBreakpoints), v => UseBreakpoints = v);
configuration.AssignValueIfNotNull<decimal>(nameof(CoveragePercentTarget), v => CoveragePercentTarget = v);
configuration.AssignValueIfNotNull<bool>(nameof(SingleHitBreakpoints), v => SingleHitBreakpoints = v);
}
}

Expand Down
10 changes: 5 additions & 5 deletions src/csharp/Pester/DebugConfiguration.cs
Original file line number Diff line number Diff line change
Expand Up @@ -41,11 +41,11 @@ public DebugConfiguration(IDictionary configuration) : this()
{
if (configuration != null)
{
ShowFullErrors = configuration.GetValueOrNull<bool>("ShowFullErrors") ?? ShowFullErrors;
WriteDebugMessages = configuration.GetValueOrNull<bool>("WriteDebugMessages") ?? WriteDebugMessages;
WriteDebugMessagesFrom = configuration.GetArrayOrNull<string>("WriteDebugMessagesFrom") ?? WriteDebugMessagesFrom;
ShowNavigationMarkers = configuration.GetValueOrNull<bool>("ShowNavigationMarkers") ?? ShowNavigationMarkers;
ReturnRawResultObject = configuration.GetValueOrNull<bool>("ReturnRawResultObject") ?? ReturnRawResultObject;
configuration.AssignValueIfNotNull<bool>(nameof(ShowFullErrors), v => ShowFullErrors = v);
configuration.AssignValueIfNotNull<bool>(nameof(WriteDebugMessages), v => WriteDebugMessages = v);
configuration.AssignArrayIfNotNull<string>(nameof(WriteDebugMessagesFrom), v => WriteDebugMessagesFrom = v);
configuration.AssignValueIfNotNull<bool>(nameof(ShowNavigationMarkers), v => ShowNavigationMarkers = v);
configuration.AssignValueIfNotNull<bool>(nameof(ReturnRawResultObject), v => ReturnRawResultObject = v);
}
}

Expand Down
30 changes: 30 additions & 0 deletions src/csharp/Pester/DictionaryExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -111,5 +111,35 @@ public static T[] GetArrayOrNull<T>(this IDictionary dictionary, string key) whe

return null;
}

public static void AssignValueIfNotNull<T>(this IDictionary dictionary, string key, Action<T> assign)
where T : struct
{
var value = GetValueOrNull<T>(dictionary, key);
if (value != null)
{
assign(value.Value);
}
}

public static void AssignObjectIfNotNull<T>(this IDictionary dictionary, string key, Action<T> assign)
where T : class
{
var value = GetObjectOrNull<T>(dictionary, key);
if (value != null)
{
assign(value);
}
}

public static void AssignArrayIfNotNull<T>(this IDictionary dictionary, string key, Action<T[]> assign)
where T : class
{
var value = GetArrayOrNull<T>(dictionary, key);
if (value != null)
{
assign(value);
}
}
}
}
10 changes: 5 additions & 5 deletions src/csharp/Pester/FilterConfiguration.cs
Original file line number Diff line number Diff line change
Expand Up @@ -37,11 +37,11 @@ public FilterConfiguration(IDictionary configuration) : this()
{
if (configuration != null)
{
Tag = configuration.GetArrayOrNull<string>("Tag") ?? Tag;
ExcludeTag = configuration.GetArrayOrNull<string>("ExcludeTag") ?? ExcludeTag;
Line = configuration.GetArrayOrNull<string>("Line") ?? Line;
ExcludeLine = configuration.GetArrayOrNull<string>("ExcludeLine") ?? ExcludeLine;
FullName = configuration.GetArrayOrNull<string>("FullName") ?? FullName;
configuration.AssignArrayIfNotNull<string>(nameof(Tag), v => Tag = v);
configuration.AssignArrayIfNotNull<string>(nameof(ExcludeTag), v => ExcludeTag = v);
configuration.AssignArrayIfNotNull<string>(nameof(Line), v => Line = v);
configuration.AssignArrayIfNotNull<string>(nameof(ExcludeLine), v => ExcludeLine = v);
configuration.AssignArrayIfNotNull<string>(nameof(FullName), v => FullName = v);
}
}
public FilterConfiguration() : base("Filter configuration")
Expand Down
4 changes: 2 additions & 2 deletions src/csharp/Pester/GenericOption.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,12 @@ public abstract class Option<T> : Option
{
public Option(Option<T> option, T value) : this(option.Description, option.Default, value)
{
_isOriginalValue = false;
_isModified = true;
}

public Option(string description, T defaultValue, T value)
{
_isOriginalValue = true;
_isModified = false;
Default = defaultValue;
Value = value;
Description = description;
Expand Down
10 changes: 8 additions & 2 deletions src/csharp/Pester/Merger.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,14 +29,20 @@ internal static class Merger
foreach (var p in properties.Where(p => p.CanRead && p.CanWrite))
{
object value;
var configurationValue = p.GetValue(configuration);
var overrideValue = p.GetValue(@override);
if (!((Option)overrideValue).IsOriginalValue())

if (((Option)overrideValue).IsModified)
{
value = overrideValue;
}
else
{
value = p.GetValue(configuration);
if(!((Option)configurationValue).IsModified)
{
continue;
}
value = configurationValue;
}

p.SetValue(cfg, value);
Expand Down
10 changes: 8 additions & 2 deletions src/csharp/Pester/Option.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,21 @@
// to have in "type accelerator" form, but without the hassle of actually adding it as a type accelerator
// that way you can easily do `[PesterConfiguration]::Default` and then inspect it, or cast a hashtable to it

using System;

namespace Pester
{
public abstract class Option
{
protected bool _isOriginalValue;
protected bool _isModified;

[Obsolete("IsOriginalValue() is obsolete and should no longer be used. Use the property IsModified instead.")]
public bool IsOriginalValue()
{
return _isOriginalValue;
return !_isModified;
}
public bool IsModified {
get { return _isModified; }
}
}
}
6 changes: 3 additions & 3 deletions src/csharp/Pester/OutputConfiguration.cs
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,9 @@ public OutputConfiguration(IDictionary configuration) : this()
{
if (configuration != null)
{
Verbosity = configuration.GetObjectOrNull<string>("Verbosity") ?? Verbosity;
StackTraceVerbosity = configuration.GetObjectOrNull<string>("StackTraceVerbosity") ?? StackTraceVerbosity;
CIFormat = configuration.GetObjectOrNull<string>("CIFormat") ?? CIFormat;
configuration.AssignObjectIfNotNull<string>(nameof(Verbosity), v => Verbosity = v);
configuration.AssignObjectIfNotNull<string>(nameof(StackTraceVerbosity), v => StackTraceVerbosity = v);
configuration.AssignObjectIfNotNull<string>(nameof(CIFormat), v => CIFormat = v);
}
}

Expand Down
8 changes: 8 additions & 0 deletions src/csharp/Pester/PesterConfigurationDeserializer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,14 @@ private Hashtable ConvertToSectionHashtable(PSObject sourceSection, string secti

foreach (var property in sourceSection.Properties)
{
var IsModified = ((PSObject)property.Value).Properties["IsModified"];

// Doing this instead of IsModified -> Add to be compatible with saved PesterConfigurations from previous versions
// Consider rewriting in next major release
if (IsModified != null && !((bool)IsModified.Value)) {
continue;
}

configurationSection.Add(
property.Name,
GetPropertyValue(
Expand Down
20 changes: 10 additions & 10 deletions src/csharp/Pester/RunConfiguration.cs
Original file line number Diff line number Diff line change
Expand Up @@ -43,16 +43,16 @@ public RunConfiguration(IDictionary configuration) : this()
{
if (configuration != null)
{
Path = configuration.GetArrayOrNull<string>(nameof(Path)) ?? Path;
ExcludePath = configuration.GetArrayOrNull<string>(nameof(ExcludePath)) ?? ExcludePath;
ScriptBlock = configuration.GetArrayOrNull<ScriptBlock>(nameof(ScriptBlock)) ?? ScriptBlock;
Container = configuration.GetArrayOrNull<ContainerInfo>(nameof(Container)) ?? Container;
TestExtension = configuration.GetObjectOrNull<string>(nameof(TestExtension)) ?? TestExtension;
Exit = configuration.GetValueOrNull<bool>(nameof(Exit)) ?? Exit;
Throw = configuration.GetValueOrNull<bool>(nameof(Throw)) ?? Throw;
PassThru = configuration.GetValueOrNull<bool>(nameof(PassThru)) ?? PassThru;
SkipRun = configuration.GetValueOrNull<bool>(nameof(SkipRun)) ?? SkipRun;
SkipRemainingOnFailure = configuration.GetObjectOrNull<string>(nameof(SkipRemainingOnFailure)) ?? SkipRemainingOnFailure;
configuration.AssignArrayIfNotNull<string>(nameof(Path), v => Path = v);
configuration.AssignArrayIfNotNull<string>(nameof(ExcludePath), v => ExcludePath = v);
configuration.AssignArrayIfNotNull<ScriptBlock>(nameof(ScriptBlock), v => ScriptBlock = v);
configuration.AssignArrayIfNotNull<ContainerInfo>(nameof(Container), v => Container = v);
configuration.AssignObjectIfNotNull<string>(nameof(TestExtension), v => TestExtension = v);
configuration.AssignValueIfNotNull<bool>(nameof(Exit), v => Exit = v);
configuration.AssignValueIfNotNull<bool>(nameof(Throw), v => Throw = v);
configuration.AssignValueIfNotNull<bool>(nameof(PassThru), v => PassThru = v);
configuration.AssignValueIfNotNull<bool>(nameof(SkipRun), v => SkipRun = v);
configuration.AssignObjectIfNotNull<string>(nameof(SkipRemainingOnFailure), v => SkipRemainingOnFailure = v);
}
}

Expand Down
2 changes: 1 addition & 1 deletion src/csharp/Pester/ShouldConfiguration.cs
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ public ShouldConfiguration(IDictionary configuration) : this()
{
if (configuration != null)
{
ErrorAction = configuration.GetObjectOrNull<string>("ErrorAction") ?? ErrorAction;
configuration.AssignObjectIfNotNull<string>(nameof(ErrorAction), v => ErrorAction = v);
}
}

Expand Down
2 changes: 1 addition & 1 deletion src/csharp/Pester/TestDriveConfiguration.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ public TestDriveConfiguration(IDictionary configuration) : this()
{
if (configuration != null)
{
Enabled = configuration.GetValueOrNull<bool>(nameof(Enabled)) ?? Enabled;
configuration.AssignValueIfNotNull<bool>(nameof(Enabled), v => Enabled = v);
}
}

Expand Down
2 changes: 1 addition & 1 deletion src/csharp/Pester/TestRegistryConfiguration.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ public TestRegistryConfiguration(IDictionary configuration) : this()
{
if (configuration != null)
{
Enabled = configuration.GetValueOrNull<bool>(nameof(Enabled)) ?? Enabled;
configuration.AssignValueIfNotNull<bool>(nameof(Enabled), v => Enabled = v);
}
}

Expand Down
10 changes: 5 additions & 5 deletions src/csharp/Pester/TestResultConfiguration.cs
Original file line number Diff line number Diff line change
Expand Up @@ -47,11 +47,11 @@ public TestResultConfiguration(IDictionary configuration) : this()
{
if (configuration != null)
{
Enabled = configuration.GetValueOrNull<bool>("Enabled") ?? Enabled;
OutputFormat = configuration.GetObjectOrNull<string>("OutputFormat") ?? OutputFormat;
OutputPath = configuration.GetObjectOrNull<string>("OutputPath") ?? OutputPath;
OutputEncoding = configuration.GetObjectOrNull<string>("OutputEncoding") ?? OutputPath;
TestSuiteName = configuration.GetObjectOrNull<string>("TestSuiteName") ?? TestSuiteName;
configuration.AssignValueIfNotNull<bool>(nameof(Enabled), v => Enabled = v);
configuration.AssignObjectIfNotNull<string>(nameof(OutputFormat), v => OutputFormat = v);
configuration.AssignObjectIfNotNull<string>(nameof(OutputPath), v => OutputPath = v);
configuration.AssignObjectIfNotNull<string>(nameof(OutputEncoding), v => OutputEncoding = v);
configuration.AssignObjectIfNotNull<string>(nameof(TestSuiteName), v => TestSuiteName = v);
}
}

Expand Down
39 changes: 36 additions & 3 deletions tst/Pester.RSpec.Configuration.ts.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -262,11 +262,11 @@ i -PassThru:$PassThru {
{ $config.Run.Path.Value = 'invalid' } | Verify-Throw
}

t "IsOriginalValue returns false after change even if same as default" {
t "IsModified returns true after change even if same as default" {
$config = [PesterConfiguration]::Default
$config.Run.Path.IsOriginalValue() | Verify-True
$config.Run.Path.IsModified | Verify-False
$config.Run.Path = $config.Run.Path.Default
$config.Run.Path.IsOriginalValue() | Verify-False
$config.Run.Path.IsModified | Verify-True
}
}

Expand Down Expand Up @@ -342,6 +342,16 @@ i -PassThru:$PassThru {
# override does not touch it
$result.Filter.Tag.Value | Verify-Equal "abc"
}

t 'IsModified returns False after merging two original values' {
$one = [PesterConfiguration]::Default
$two = [PesterConfiguration]::Default
$result = [PesterConfiguration]::Merge($one, $two)

# has the same value as default but was written so it will override
$result.Output.Verbosity.Value | Verify-Equal $one.Output.Verbosity.Value
$result.Output.Verbosity.IsModified | Verify-False
}
}

b "Advanced interface - Run paths" {
Expand Down Expand Up @@ -574,6 +584,23 @@ i -PassThru:$PassThru {
$config.Filter.Tag.Value -contains 'Core' | Verify-True
}

t 'IsModified is only True on modified properties after merging Hashtable' {
$MyOptions = @{
Run = @{
PassThru = $true
}
Filter = @{
Tag = 'Core'
}
}
$config = New-PesterConfiguration -Hashtable $MyOptions

$config.Run.PassThru.Value | Verify-Equal $true
$config.Filter.Tag.Value -contains 'Core' | Verify-True
$config.Run.PassThru.IsModified | Verify-True
$config.Run.SkipRun.IsModified | Verify-False
}

t "Merges configuration when a hashtable has been serialized" {
$BeforeSerialization = @{
Run = @{
Expand All @@ -590,6 +617,9 @@ i -PassThru:$PassThru {

$config.Run.PassThru.Value | Verify-Equal $true
$config.Filter.Tag.Value -contains 'Core' | Verify-True
$config.Run.PassThru.IsModified | Verify-True
$config.Run.SkipRun.IsModified | Verify-False
$config.Output.Verbosity.IsModified | Verify-False
}

t "Merges configuration when a PesterConfiguration object has been serialized" {
Expand All @@ -609,6 +639,9 @@ i -PassThru:$PassThru {

$config.Run.PassThru.Value | Verify-Equal $true
$config.Filter.Tag.Value -contains 'Core' | Verify-True
$config.Run.PassThru.IsModified | Verify-True
$config.Run.SkipRun.IsModified | Verify-False
$config.Output.Verbosity.IsModified | Verify-False
}

t "Merges configuration when a PesterConfiguration object includes an array of values" {
Expand Down
25 changes: 25 additions & 0 deletions tst/PesterConfiguration.Tests.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,31 @@ Describe "PesterConfiguration.Format.ps1xml" {
$missingOptions | Should -Be @()
}
}

Context "Testing format data for 'Pester.Option[T]'" {
BeforeAll {
$formatData = Get-FormatData -TypeName 'Pester.Option'
$options = [Pester.Option[bool]].GetProperties() | Where-Object Name -notin 'IsModified'
}
It 'Has a single view defined of type TableControl' {
$formatData | Should -Not -BeNullOrEmpty
$formatData.FormatViewDefinition.Count | Should -Be 1
$formatData.FormatViewDefinition[0].Name | Should -BeExactly 'Pester.Option'
$formatData.FormatViewDefinition[0].Control | Should -BeOfType [System.Management.Automation.TableControl]
}

It 'View includes all options' {
$propertiesInView = @($formatData.FormatViewDefinition[0].Control.Rows.Columns.DisplayEntry | Where-Object ValueType -EQ 'Property')
$propertiesInView.Count | Should -Be $options.Count
$missingOptions = $options.Name | Where-Object { $propertiesInView.Value -notcontains $_ }
$missingOptions | Should -Be @()
}

It 'View does not include IsModified' {
$propertiesInView = @($formatData.FormatViewDefinition[0].Control.Rows.Columns.DisplayEntry | Where-Object ValueType -EQ 'Property')
$propertiesInView.Value | Should -Not -Contain 'IsModified'
}
}
}

Describe 'PesterConfiguration' {
Expand Down