Skip to content

Commit 6494314

Browse files
authored
Merge pull request #383 from tonyhallett/improve-settings-exception-messages
improve exception messages
2 parents dc69d25 + 5f05fe8 commit 6494314

File tree

2 files changed

+113
-48
lines changed

2 files changed

+113
-48
lines changed

FineCodeCoverageTests/CoverageProject_Settings_Tests.cs

Lines changed: 75 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
using FineCodeCoverageTests.Test_helpers;
66
using Moq;
77
using NUnit.Framework;
8+
using StructureMap.AutoMocking;
89
using System;
910
using System.Collections.Generic;
1011
using System.IO;
@@ -142,13 +143,21 @@ public async Task Should_Return_Using_VsBuild_When_No_Labelled_PropertyGroup(boo
142143

143144
public class SettingsMerger_Tests
144145
{
146+
private AutoMoqer mocker;
147+
private SettingsMerger settingsMerger;
148+
149+
[SetUp]
150+
public void SetUp()
151+
{
152+
mocker = new AutoMoqer();
153+
settingsMerger = mocker.Create<SettingsMerger>();
154+
}
145155
[Test]
146156
public void Should_Use_Global_Settings_If_No_Project_Level_Or_FCC_Settings_Files()
147157
{
148158
var mockAppOptions = new Mock<IAppOptions>(MockBehavior.Strict);
149159
var appOptions = mockAppOptions.Object;
150160

151-
var settingsMerger = new SettingsMerger(null);
152161
var mergedSettings = settingsMerger.Merge(appOptions, new List<XElement>(), null);
153162

154163
Assert.AreSame(appOptions, mergedSettings);
@@ -161,7 +170,6 @@ public void Should_Overwrite_GlobalOptions_Bool_Properties_From_Settings_File()
161170
mockAppOptions.SetupSet(o => o.IncludeReferencedProjects = true);
162171
var appOptions = mockAppOptions.Object;
163172

164-
var settingsMerger = new SettingsMerger(null);
165173
var settingsFileElement = CreateIncludeReferencedProjectsElement(true);
166174
var mergedSettings = settingsMerger.Merge(appOptions, new List<XElement> { settingsFileElement}, null);
167175

@@ -176,7 +184,6 @@ public void Should_Overwrite_GlobalOptions_Bool_Properties_From_Settings_File_In
176184
mockAppOptions.SetupAllProperties();
177185
var appOptions = mockAppOptions.Object;
178186

179-
var settingsMerger = new SettingsMerger(null);
180187
var settingsFileElementTop = CreateIncludeReferencedProjectsElement(!last);
181188
var settingsFileElementLast = CreateIncludeReferencedProjectsElement(last);
182189
var mergedSettings = settingsMerger.Merge(
@@ -196,7 +203,6 @@ public void Should_Overwrite_GlobalOptions_Bool_Properties_From_Project(bool las
196203
mockAppOptions.SetupAllProperties();
197204
var appOptions = mockAppOptions.Object;
198205

199-
var settingsMerger = new SettingsMerger(null);
200206
var settingsFileElement = CreateIncludeReferencedProjectsElement(!last);
201207
var projectElement = CreateIncludeReferencedProjectsElement(last);
202208
var mergedSettings = settingsMerger.Merge(
@@ -221,7 +227,6 @@ public void Should_Overwrite_Int_Properties()
221227
</Root>
222228
");
223229

224-
var settingsMerger = new SettingsMerger(null);
225230
var mergedSettings = settingsMerger.Merge(
226231
appOptions,
227232
new List<XElement> {},
@@ -244,7 +249,6 @@ public void Should_Overwrite_Enum_Properties()
244249
</Root>
245250
");
246251

247-
var settingsMerger = new SettingsMerger(null);
248252
var mergedSettings = settingsMerger.Merge(
249253
appOptions,
250254
new List<XElement> { },
@@ -267,7 +271,6 @@ public void Should_Overwrite_String_Properties()
267271
</Root>
268272
");
269273

270-
var settingsMerger = new SettingsMerger(null);
271274
var mergedSettings = settingsMerger.Merge(
272275
appOptions,
273276
new List<XElement> { },
@@ -293,7 +296,6 @@ public void Should_Overwrite_String_Array_By_Default()
293296
</Root>
294297
");
295298

296-
var settingsMerger = new SettingsMerger(null);
297299
var mergedSettings = settingsMerger.Merge(
298300
appOptions,
299301
new List<XElement> { },
@@ -319,7 +321,6 @@ public void Should_Overwrite_String_Array_DefaultMerge_False()
319321
</Root>
320322
");
321323

322-
var settingsMerger = new SettingsMerger(null);
323324
var mergedSettings = settingsMerger.Merge(
324325
appOptions,
325326
new List<XElement> { },
@@ -345,7 +346,6 @@ public void Should_Overwrite_String_Array_DefaultMerge_True_Property_Merge_false
345346
</Root>
346347
");
347348

348-
var settingsMerger = new SettingsMerger(null);
349349
var mergedSettings = settingsMerger.Merge(
350350
appOptions,
351351
new List<XElement> { },
@@ -371,7 +371,6 @@ public void Should_Overwrite_String_Array_DefaultMerge_Not_Bool()
371371
</Root>
372372
");
373373

374-
var settingsMerger = new SettingsMerger(null);
375374
var mergedSettings = settingsMerger.Merge(
376375
appOptions,
377376
new List<XElement> { },
@@ -397,7 +396,6 @@ public void Should_Merge_String_Array_If_DefaultMerge()
397396
</Root>
398397
");
399398

400-
var settingsMerger = new SettingsMerger(null);
401399
var mergedSettings = settingsMerger.Merge(
402400
appOptions,
403401
new List<XElement> { },
@@ -423,7 +421,6 @@ public void Should_Merge_If_Property_Element_Merge()
423421
</Root>
424422
");
425423

426-
var settingsMerger = new SettingsMerger(null);
427424
var mergedSettings = settingsMerger.Merge(
428425
appOptions,
429426
new List<XElement> { },
@@ -433,6 +430,54 @@ public void Should_Merge_If_Property_Element_Merge()
433430
Assert.AreEqual(new string[] { "global", "1", "2" }, appOptions.Exclude);
434431
}
435432

433+
[Test]
434+
public void Should_Log_Failed_To_Get_Setting_From_Project_Settings_Exception_And_Not_Throw()
435+
{
436+
var mockAppOptions = new Mock<IAppOptions>();
437+
mockAppOptions.SetupAllProperties();
438+
var appOptions = mockAppOptions.Object;
439+
var element = XElement.Parse($@"
440+
<Root>
441+
<OpenCoverRegister>
442+
DefaultX
443+
</OpenCoverRegister>
444+
</Root>
445+
");
446+
447+
var mergedSettings = settingsMerger.Merge(
448+
appOptions,
449+
new List<XElement> { },
450+
element);
451+
452+
var mockLogger = mocker.GetMock<ILogger>();
453+
mockLogger.Verify(logger => logger.Log("Failed to get 'OpenCoverRegister' setting from project settings", It.IsAny<Exception>()));
454+
Assert.AreEqual(mergedSettings.OpenCoverRegister, OpenCoverRegister.Default);
455+
}
456+
457+
[Test]
458+
public void Should_Log_Failed_To_Get_Setting_From_Settings_File_Exception_And_Not_Throw()
459+
{
460+
var mockAppOptions = new Mock<IAppOptions>();
461+
mockAppOptions.SetupAllProperties();
462+
var appOptions = mockAppOptions.Object;
463+
var element = XElement.Parse($@"
464+
<Root>
465+
<OpenCoverRegister>
466+
DefaultX
467+
</OpenCoverRegister>
468+
</Root>
469+
");
470+
471+
var mergedSettings = settingsMerger.Merge(
472+
appOptions,
473+
new List<XElement> { element},
474+
null);
475+
476+
var mockLogger = mocker.GetMock<ILogger>();
477+
mockLogger.Verify(logger => logger.Log("Failed to get 'OpenCoverRegister' setting from settings file", It.IsAny<Exception>()));
478+
Assert.AreEqual(mergedSettings.OpenCoverRegister, OpenCoverRegister.Default);
479+
}
480+
436481
[Test]
437482
public void Should_Not_Throw_If_Merge_Current_Null_String_Array_Type()
438483
{
@@ -460,7 +505,7 @@ public void Should_Not_Throw_If_Merge_Current_Null_String_Array_Type()
460505
}
461506

462507
[TestCaseSource(nameof(XmlConversionCases))]
463-
public void Should_Convert_Xml_Value_Correctly(string propertyElement,string propertyName,object expectedConversion, bool expectedException)
508+
public void Should_Convert_Xml_Value_Correctly(string propertyElement,string propertyName,object expectedConversion)
464509
{
465510
var settingsMerger = new SettingsMerger(new Mock<ILogger>().Object);
466511
var settingsElement = XElement.Parse($"<Root>{propertyElement}</Root>");
@@ -474,10 +519,10 @@ public void Should_Convert_Xml_Value_Correctly(string propertyElement,string pro
474519
[Test]
475520
public void Should_Throw_For_Unsupported_Conversion()
476521
{
477-
var settingsMerger = new SettingsMerger(new Mock<ILogger>().Object);
478522
var settingsElement = XElement.Parse($"<Root><PropertyType/></Root>");
479523
var unsupported = typeof(PropertyInfo).GetProperty(nameof(PropertyInfo.PropertyType));
480-
var expectedMessage = $"Cannot handle 'PropertyType' yet";
524+
525+
var expectedMessage = $"Unexpected settings type Type for setting PropertyType in settings merger GetValueFromXml";
481526
Assert.Throws<Exception>(() => settingsMerger.GetValueFromXml(settingsElement, unsupported), expectedMessage);
482527
}
483528

@@ -503,31 +548,31 @@ string CreateElement(string elementName, string value)
503548
var cases = new object[]
504549
{
505550
// boolean
506-
new object[]{ CreateElement(hideFullyCovered, "true"),hideFullyCovered,true, false },
507-
new object[]{ CreateElement(hideFullyCovered, "false"), hideFullyCovered, false, false },
508-
new object[]{ CreateElement(hideFullyCovered, "bad"), hideFullyCovered, null, false },
509-
new object[]{ CreateElement(hideFullyCovered, ""), hideFullyCovered, null, false },
510-
new object[]{ CreateElement(hideFullyCovered, boolArray), hideFullyCovered, true, false },
551+
new object[]{ CreateElement(hideFullyCovered, "true"),hideFullyCovered,true },
552+
new object[]{ CreateElement(hideFullyCovered, "false"), hideFullyCovered, false },
553+
new object[]{ CreateElement(hideFullyCovered, "bad"), hideFullyCovered, null },
554+
new object[]{ CreateElement(hideFullyCovered, ""), hideFullyCovered, null },
555+
new object[]{ CreateElement(hideFullyCovered, boolArray), hideFullyCovered, true },
511556

512557
// int
513-
new object[]{ CreateElement(thresholdForCrapScore, "1"), thresholdForCrapScore, 1, false },
514-
new object[]{ CreateElement(thresholdForCrapScore, "bad"), thresholdForCrapScore, null, false },
515-
new object[]{ CreateElement(thresholdForCrapScore, ""), thresholdForCrapScore, null, false },
558+
new object[]{ CreateElement(thresholdForCrapScore, "1"), thresholdForCrapScore, 1 },
559+
new object[]{ CreateElement(thresholdForCrapScore, "bad"), thresholdForCrapScore, null },
560+
new object[]{ CreateElement(thresholdForCrapScore, ""), thresholdForCrapScore, null },
516561

517562
// string
518-
new object[]{ CreateElement(coverletConsoleCustomPath, "1"), coverletConsoleCustomPath, "1", false },
563+
new object[]{ CreateElement(coverletConsoleCustomPath, "1"), coverletConsoleCustomPath, "1" },
519564
// breaking change ( previous ignored )
520-
new object[]{ CreateElement(coverletConsoleCustomPath, ""), coverletConsoleCustomPath, "", false },
565+
new object[]{ CreateElement(coverletConsoleCustomPath, ""), coverletConsoleCustomPath, "" },
521566

522567
// string[]
523-
new object[]{ CreateElement(exclude, stringArray), exclude, new string[] { "1","2"}, false },
524-
new object[]{ CreateElement(exclude, ""), exclude, new string[] {}, false },
568+
new object[]{ CreateElement(exclude, stringArray), exclude, new string[] { "1","2"} },
569+
new object[]{ CreateElement(exclude, ""), exclude, new string[] {} },
525570

526571
// null for no property element
527-
new object[]{ CreateElement(exclude, "true"), hideFullyCovered, null, false},
572+
new object[]{ CreateElement(exclude, "true"), hideFullyCovered, null},
528573

529574
//exception for no type conversion
530-
new object[]{ CreateElement(enumConversion, "No"), enumConversion, RunMsCodeCoverage.No, false }
575+
new object[]{ CreateElement(enumConversion, "No"), enumConversion, RunMsCodeCoverage.No }
531576

532577
};
533578

SharedProject/Core/Model/SettingsMerger.cs

Lines changed: 38 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ private class SettingsElementDefaultMerge
7575
{
7676
public XElement SettingsElement { get; set; }
7777
public bool DefaultMerge { get; set; }
78+
public bool FromProjectSettings { get; internal set; }
7879
}
7980

8081
private readonly PropertyInfo[] settingsPropertyInfos;
@@ -90,13 +91,27 @@ ILogger logger
9091

9192
}
9293

93-
public IAppOptions Merge(IAppOptions globalOptions, List<XElement> settingsFileElements, XElement projectSettingsElement)
94+
public IAppOptions Merge(
95+
IAppOptions globalOptions,
96+
List<XElement> settingsFileElements,
97+
XElement projectSettingsElement)
9498
{
9599
var settingsElementsWithDefaultMergeStrategy =
96-
settingsFileElements.Select(e => new SettingsElementDefaultMerge { SettingsElement = e, DefaultMerge = settingsFileDefaultMerge }).ToList();
100+
settingsFileElements.Select(e => new SettingsElementDefaultMerge {
101+
SettingsElement = e,
102+
DefaultMerge = settingsFileDefaultMerge,
103+
FromProjectSettings = false
104+
}).ToList();
105+
97106
if (projectSettingsElement != null)
98107
{
99-
settingsElementsWithDefaultMergeStrategy.Add(new SettingsElementDefaultMerge { SettingsElement = projectSettingsElement, DefaultMerge = projectSettingsDefaultMerge });
108+
settingsElementsWithDefaultMergeStrategy.Add(
109+
new SettingsElementDefaultMerge {
110+
SettingsElement = projectSettingsElement,
111+
DefaultMerge = projectSettingsDefaultMerge,
112+
FromProjectSettings = true
113+
}
114+
);
100115
}
101116

102117
if (settingsElementsWithDefaultMergeStrategy.Count != 0)
@@ -107,7 +122,11 @@ public IAppOptions Merge(IAppOptions globalOptions, List<XElement> settingsFileE
107122
return globalOptions;
108123
}
109124

110-
private void Merge(IAppOptions globalOptions, PropertyInfo settingPropertyInfo, List<SettingsElementDefaultMerge> settingsElementsWithDefaultMergeStrategy)
125+
private void Merge(
126+
IAppOptions globalOptions,
127+
PropertyInfo settingPropertyInfo,
128+
List<SettingsElementDefaultMerge> settingsElementsWithDefaultMergeStrategy
129+
)
111130
{
112131
var canMerge = settingsMergeLogic.CanMerge(settingPropertyInfo.PropertyType);
113132
if (canMerge)
@@ -119,29 +138,29 @@ private void Merge(IAppOptions globalOptions, PropertyInfo settingPropertyInfo,
119138
var propertyElement = GetPropertyElement(settingsElement, settingPropertyInfo.Name);
120139
if (propertyElement != null)
121140
{
141+
var fromProjectSettings = settingsElementWithDefaultMerge.FromProjectSettings;
122142
var merge = GetMerge(defaultMerge, propertyElement);
123143
if (merge)
124144
{
125-
Merge(globalOptions, settingPropertyInfo, settingsElement);
145+
Merge(globalOptions, settingPropertyInfo, settingsElement,fromProjectSettings);
126146
}
127147
else
128148
{
129-
Overwrite(globalOptions, settingPropertyInfo, settingsElement);
149+
Overwrite(globalOptions, settingPropertyInfo, settingsElement,fromProjectSettings);
130150
}
131151
}
132152

133153
}
134154
}
135155
else
136156
{
137-
var settingsElements = settingsElementsWithDefaultMergeStrategy.Select(x => x.SettingsElement);
138-
Overwrite(globalOptions, settingPropertyInfo, settingsElements);
157+
Overwrite(globalOptions, settingPropertyInfo, settingsElementsWithDefaultMergeStrategy);
139158
}
140159
}
141160

142-
private void Merge(IAppOptions globalOptions, PropertyInfo settingPropertyInfo, XElement settingsElement)
161+
private void Merge(IAppOptions globalOptions, PropertyInfo settingPropertyInfo, XElement settingsElement,bool fromProjectSettings)
143162
{
144-
var value = TryGetValueFromXml(settingsElement, settingPropertyInfo);
163+
var value = TryGetValueFromXml(settingsElement, settingPropertyInfo,fromProjectSettings);
145164
if (value != null)
146165
{
147166
var currentValue = settingPropertyInfo.GetValue(globalOptions);
@@ -179,17 +198,17 @@ private bool GetDefaultMerge(bool defaultDefaultMerge, XElement root)
179198
return defaultMergeAttribute.Value.ToLower() == "true";
180199
}
181200

182-
private void Overwrite(IAppOptions globalOptions, PropertyInfo settingPropertyInfo, IEnumerable<XElement> settingsElements)
201+
private void Overwrite(IAppOptions globalOptions, PropertyInfo settingPropertyInfo, IEnumerable<SettingsElementDefaultMerge> settingsElementsDefaultMerge)
183202
{
184-
foreach (var settingsElement in settingsElements)
203+
foreach (var settingsElementDefaultMerge in settingsElementsDefaultMerge)
185204
{
186-
Overwrite(globalOptions, settingPropertyInfo, settingsElement);
205+
Overwrite(globalOptions, settingPropertyInfo, settingsElementDefaultMerge.SettingsElement,settingsElementDefaultMerge.FromProjectSettings);
187206
}
188207
}
189208

190-
private void Overwrite(IAppOptions globalOptions, PropertyInfo settingPropertyInfo, XElement settingsElement)
209+
private void Overwrite(IAppOptions globalOptions, PropertyInfo settingPropertyInfo, XElement settingsElement,bool fromProjectSettings)
191210
{
192-
var value = TryGetValueFromXml(settingsElement, settingPropertyInfo);
211+
var value = TryGetValueFromXml(settingsElement, settingPropertyInfo,fromProjectSettings);
193212
if (value != null)
194213
{
195214
settingPropertyInfo.SetValue(globalOptions, value);
@@ -201,15 +220,16 @@ private XElement GetPropertyElement(XElement settingsElement, string propertyNam
201220
return settingsElement.Descendants().FirstOrDefault(x => x.Name.LocalName.Equals(propertyName, StringComparison.OrdinalIgnoreCase));
202221
}
203222

204-
private object TryGetValueFromXml(XElement settingsElement, PropertyInfo property)
223+
private object TryGetValueFromXml(XElement settingsElement, PropertyInfo property,bool fromProjectSettings)
205224
{
206225
try
207226
{
208227
return GetValueFromXml(settingsElement, property);
209228
}
210229
catch (Exception exception)
211230
{
212-
logger.Log($"Failed to override '{property.Name}' setting", exception);
231+
var from = fromProjectSettings ? "project settings" : "settings file";
232+
logger.Log($"Failed to get '{property.Name}' setting from {from}", exception);
213233
}
214234
return null;
215235
}
@@ -372,7 +392,7 @@ internal object GetValueFromXml(XElement settingsElement, PropertyInfo property)
372392

373393
else
374394
{
375-
throw new Exception($"Cannot handle '{property.PropertyType.Name}' yet");
395+
throw new Exception($"Unexpected settings type '{property.PropertyType.Name}' for setting {property.Name} in settings merger GetValueFromXml");
376396
}
377397
return null;
378398

0 commit comments

Comments
 (0)