Skip to content

Commit 2965cd4

Browse files
authored
Upgrade to latest version of ConfigurationBinder class as of .net 8 (#1143)
* Upgrade to latest version of ConfigurationBinder class as of .net 8 added files * Added new comment for ThrowHelper * removed line * correct the test names * more name fixes * removed charcter * Code comments * Re-added the throwhelper using ArgumentNullException.ThowIfNull() method * renamed file * Fix from codereview * added link * Exclude from code coverage (MS code)
1 parent a3fe2fb commit 2965cd4

File tree

7 files changed

+1301
-403
lines changed

7 files changed

+1301
-403
lines changed

src/AppModel/NetDaemon.AppModel.Tests/Config/ConfigurationBinderTests.cs

Lines changed: 35 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -22,39 +22,42 @@ public void TestAddYamlConfigGetsSettingsCorrectly()
2222
}
2323

2424
[Fact]
25-
public void TestWhenInitIEnumerableThrowsException()
25+
public void TestWhenInitIEnumerableShouldCorrectHaveCount()
2626
{
2727
// ARRANGE
2828
// ACT
2929
// CHECK
30-
Assert.Throws<InvalidOperationException>(() => GetObjectFromSection<CollectionBindingFaultyIEnumerableTestClass>("TestCollections"));
30+
var config = GetObjectFromSection<CollectionBindingIEnumerableWithInitTestClass>("TestCollections");
31+
config!.SomeEnumerable.Should().HaveCount(2);
3132
}
3233

3334
[Fact]
34-
public void TestWhenInitIReadOnlyListThrowsException()
35+
public void TestWhenInitIReadOnlyListShouldHaveCorrectCount()
3536
{
3637
// ARRANGE
3738
// ACT
3839
// CHECK
39-
Assert.Throws<InvalidOperationException>(() => GetObjectFromSection<CollectionBindingFaultyIReadOnlyListTestClass>("TestCollections"));
40+
var config = GetObjectFromSection<CollectionBindingIReadOnlyListWithInitTestClass>("TestCollections");
41+
config!.SomeReadOnlyList.Should().HaveCount(2);
4042
}
4143

4244
[Fact]
43-
public void TestWhenInitIReadOnlyCollectionThrowsException()
45+
public void TestWhenInitIReadOnlyCollectionShouldHaveCorrectCount()
4446
{
4547
// ARRANGE
4648
// ACT
4749
// CHECK
48-
Assert.Throws<InvalidOperationException>(() => GetObjectFromSection<CollectionBindingFaultyIReadOnlyCollectionTestClass>("TestCollections"));
50+
var config = GetObjectFromSection<CollectionBindingIReadOnlyCollectionWithInitTestClass>("TestCollections");
51+
config!.SomeReadOnlyCollection.Should().HaveCount(2);
4952
}
5053

5154
[Fact]
52-
public void TestWhenInitIReadOnlyDictionaryThrowsException()
55+
public void TestWhenInitIReadOnlyDictionaryShouldHaveCount()
5356
{
5457
// ARRANGE
5558
// ACT
56-
Assert.Throws<InvalidOperationException>(() =>
57-
GetObjectFromSection<CollectionBindingFaultyIReadOnlyDictionaryTestClass>("TestCollections"));
59+
var config = GetObjectFromSection<CollectionBindingIReadOnlyDictionaryWithInitTestClass>("TestCollections");
60+
config!.SomeReadOnlyDictionary.Should().HaveCount(2);
5861
// CHECK
5962
}
6063

@@ -141,6 +144,17 @@ public void TestAddYamlConfigBadClassShouldThrowException()
141144
GetObjectFromSection<IDictionary<int, int>>("TestCollections").Should().BeEmpty();
142145
}
143146

147+
[Fact]
148+
public void TestAddYamlConfigGetsEnumWithUnderlyingTypeCorrectly()
149+
{
150+
// ARRANGE
151+
// ACT
152+
var config = GetObjectFromSection<IEnumerable<TestShortEnum>>("AnotherTestShortEnum");
153+
// CHECK
154+
config!.Should().HaveCount(2);
155+
Enum.GetUnderlyingType(config!.First().GetType()).Should().Be(typeof(ushort));
156+
}
157+
144158
private static T? GetObjectFromSection<T>(string sectionName)
145159
{
146160
var providerMock = new Mock<IServiceProvider>();
@@ -161,6 +175,12 @@ internal enum TestEnum
161175
Enum2
162176
}
163177

178+
internal enum TestShortEnum : ushort
179+
{
180+
Enum1,
181+
Enum2
182+
}
183+
164184
#region -- failing class declarations --
165185

166186
internal abstract class AbstractShouldNotSerialize
@@ -196,23 +216,25 @@ public record CollectionBindingTestClass
196216
public IDictionary<string, string>? SomeDictionary { get; init; }
197217
}
198218

199-
public record CollectionBindingFaultyIEnumerableTestClass
219+
public record CollectionBindingIEnumerableWithInitTestClass
200220
{
201221
public IEnumerable<string>? SomeEnumerable { get; init; } = new List<string>();
202222
}
203223

204-
public record CollectionBindingFaultyIReadOnlyListTestClass
224+
public record CollectionBindingIReadOnlyListWithInitTestClass
205225
{
206226
public IReadOnlyList<string>? SomeReadOnlyList { get; init; } = new List<string>();
207227
}
208228

209-
public record CollectionBindingFaultyIReadOnlyCollectionTestClass
229+
public record CollectionBindingIReadOnlyCollectionWithInitTestClass
210230
{
211231
public IReadOnlyCollection<string>? SomeReadOnlyCollection { get; init; } = new List<string>();
212232
}
213233

214-
public record CollectionBindingFaultyIReadOnlyDictionaryTestClass
234+
public record CollectionBindingIReadOnlyDictionaryWithInitTestClass
215235
{
216236
public IReadOnlyDictionary<string, string>? SomeReadOnlyDictionary { get; init; } =
217237
new Dictionary<string, string>();
218238
}
239+
240+

src/AppModel/NetDaemon.AppModel.Tests/Config/Fixtures/CollectionTestsFixture.yaml

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,4 +51,8 @@ AnotherTestArray:
5151

5252
AnotherTestEnum:
5353
- Enum1
54-
- Enum2
54+
- Enum2
55+
56+
AnotherTestShortEnum:
57+
- Enum1
58+
- Enum2
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
using System;
5+
using System.Diagnostics;
6+
using System.Diagnostics.CodeAnalysis;
7+
8+
namespace NetDaemon.AppModel.Internal.Config;
9+
10+
[ExcludeFromCodeCoverage]
11+
internal sealed class BindingPoint
12+
{
13+
private readonly Func<object?>? _initialValueProvider;
14+
private object? _initialValue;
15+
private object? _setValue;
16+
private bool _valueSet;
17+
18+
public BindingPoint(object? initialValue = null, bool isReadOnly = false)
19+
{
20+
_initialValue = initialValue;
21+
IsReadOnly = isReadOnly;
22+
}
23+
24+
public BindingPoint(Func<object?> initialValueProvider, bool isReadOnly)
25+
{
26+
_initialValueProvider = initialValueProvider;
27+
IsReadOnly = isReadOnly;
28+
}
29+
30+
public bool IsReadOnly { get; }
31+
32+
public bool HasNewValue
33+
{
34+
get
35+
{
36+
if (IsReadOnly)
37+
{
38+
return false;
39+
}
40+
41+
if (_valueSet)
42+
{
43+
return true;
44+
}
45+
46+
// When binding mutable value types, even if we didn't explicitly set a new value
47+
// We still end up editing a copy of that value and therefore should treat it as
48+
// a new value that needs to be written back to the parent object.
49+
return _initialValue?.GetType() is { } initialValueType
50+
&& initialValueType.IsValueType
51+
// Skipping primitive value types isn't strictly necessary but avoids us needing
52+
// to update the parent object in a very common case (certainly more common than
53+
// mutable structs). We'll still do a "wasted" update for non-primitive immutable structs.
54+
&& !initialValueType.IsPrimitive;
55+
}
56+
}
57+
58+
public object? Value => _valueSet ? _setValue : _initialValue ??= _initialValueProvider?.Invoke();
59+
60+
public void SetValue(object? newValue)
61+
{
62+
Debug.Assert(!IsReadOnly);
63+
Debug.Assert(!_valueSet);
64+
_setValue = newValue;
65+
_valueSet = true;
66+
}
67+
68+
public void TrySetValue(object? newValue)
69+
{
70+
if (!IsReadOnly)
71+
{
72+
SetValue(newValue);
73+
}
74+
}
75+
}

0 commit comments

Comments
 (0)