Utilizing MadMilkman.INI for ini parsing handling multi-value section entries (+ Update-IniValue parameter)
This commit is contained in:
parent
cdcff1b022
commit
39fea675b7
7 changed files with 934 additions and 7 deletions
73
LANCommander.SDK.Tests/IniHandling/ConfigurationTest.cs
Normal file
73
LANCommander.SDK.Tests/IniHandling/ConfigurationTest.cs
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
using MadMilkman.Ini;
|
||||
|
||||
namespace LANCommander.SDK.Tests.IniHandling
|
||||
{
|
||||
using SectionKey = KeyValuePair<string, IList<string>>;
|
||||
|
||||
public class ConfigurationTest
|
||||
{
|
||||
public required string Ini { get; set; }
|
||||
|
||||
public IList<string> RequiredSections { get; set; } = [];
|
||||
public IList<KeyValuePair<string, int>> RequiredSectionsCount { get; set; } = [];
|
||||
|
||||
|
||||
public IList<SectionKey> RequiredKeys { get; set; } = [];
|
||||
public IList<KeyValuePair<string, IList<KeyValuePair<string, int>>>> RequiredKeysCount { get; set; } = [];
|
||||
|
||||
public IList<SectionKeyValue> RequiredKeyValues { get; set; } = [];
|
||||
|
||||
public IList<UpdateSectionKeyValue> UpdateKeyValues { get; set; } = [];
|
||||
|
||||
|
||||
public class SectionKeyValue(string section)
|
||||
{
|
||||
public class KeyValue
|
||||
{
|
||||
public string Key { get; set; }
|
||||
public string? Value { get; set; }
|
||||
public IList<string>? Values { get; set; }
|
||||
|
||||
public KeyValue(string key, string? value)
|
||||
{
|
||||
Key = key;
|
||||
Value = value;
|
||||
}
|
||||
|
||||
public KeyValue(string key, IList<string>? values)
|
||||
{
|
||||
Key = key;
|
||||
Values = values;
|
||||
}
|
||||
}
|
||||
|
||||
public string Section { get; set; } = section;
|
||||
public IniOptions? Options { get; set; }
|
||||
|
||||
public SectionKeyValue(string section, IniOptions options) : this(section) => Options = options;
|
||||
|
||||
public IList<KeyValue> KeyValues { get; set; } = [];
|
||||
}
|
||||
|
||||
public class UpdateSectionKeyValue(string section)
|
||||
{
|
||||
public class UpdateKeyValue
|
||||
{
|
||||
public required string Key { get; set; }
|
||||
public string? OldValue { get; set; }
|
||||
public string? NewValue { get; set; }
|
||||
|
||||
public IList<string>? ExpectedValues { get; set; }
|
||||
}
|
||||
|
||||
public string Section { get; set; } = section;
|
||||
public IniOptions? Options { get; set; }
|
||||
|
||||
public UpdateSectionKeyValue(string section, IniOptions options) : this(section) => Options = options;
|
||||
|
||||
public IList<UpdateKeyValue> UpdateKeyValues { get; set; } = [];
|
||||
public IList<SectionKeyValue> CheckKeyValues { get; set; } = [];
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
240
LANCommander.SDK.Tests/IniHandling/ConfigurationTests.cs
Normal file
240
LANCommander.SDK.Tests/IniHandling/ConfigurationTests.cs
Normal file
|
|
@ -0,0 +1,240 @@
|
|||
using MadMilkman.Ini;
|
||||
|
||||
namespace LANCommander.SDK.Tests.IniHandling
|
||||
{
|
||||
using KeyValueInt = KeyValuePair<string, int>;
|
||||
using SectionKey = KeyValuePair<string, IList<string>>;
|
||||
|
||||
public static class ConfigurationTests
|
||||
{
|
||||
public static ConfigurationTest Test_SingleSection = new ConfigurationTest
|
||||
{
|
||||
Ini = @"; last modified 1 April 2001 by John Doe
|
||||
[owner]
|
||||
name = John Doe
|
||||
organization = Acme Widgets Inc.",
|
||||
|
||||
RequiredSections = { "owner" },
|
||||
RequiredKeys =
|
||||
{
|
||||
new SectionKey("owner", ["name", "organization"]),
|
||||
},
|
||||
RequiredKeyValues =
|
||||
{
|
||||
new ConfigurationTest.SectionKeyValue("owner")
|
||||
{
|
||||
KeyValues = [
|
||||
new ConfigurationTest.SectionKeyValue.KeyValue("name", "John Doe"),
|
||||
new ConfigurationTest.SectionKeyValue.KeyValue("organization", "Acme Widgets Inc."),
|
||||
],
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
public static ConfigurationTest Test_TwoSections = new ConfigurationTest
|
||||
{
|
||||
Ini = @"[person1]
|
||||
name=Mikey
|
||||
age=4
|
||||
|
||||
[person2]
|
||||
name=Becky
|
||||
age=46",
|
||||
|
||||
RequiredSections = { "person1", "person2" },
|
||||
RequiredKeys =
|
||||
{
|
||||
new SectionKey("person1", ["name", "age"]),
|
||||
new SectionKey("person2", ["name", "age"]),
|
||||
},
|
||||
RequiredKeyValues =
|
||||
{
|
||||
new ConfigurationTest.SectionKeyValue("person1")
|
||||
{
|
||||
KeyValues = [
|
||||
new ConfigurationTest.SectionKeyValue.KeyValue("name", "Mikey"),
|
||||
new ConfigurationTest.SectionKeyValue.KeyValue("age", "4"),
|
||||
],
|
||||
},
|
||||
new ConfigurationTest.SectionKeyValue("person2")
|
||||
{
|
||||
KeyValues = [
|
||||
new ConfigurationTest.SectionKeyValue.KeyValue("name", "Becky"),
|
||||
new ConfigurationTest.SectionKeyValue.KeyValue("age", "46"),
|
||||
],
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
public static ConfigurationTest Test_ArrayStatic = new ConfigurationTest
|
||||
{
|
||||
Ini = @"[Engine.Input]
|
||||
Aliases[0]=(Command=""Button bFire | Fire"",Alias=Fire)
|
||||
Aliases[1]=(Command=""Button bAltFire | AltFire"",Alias=AltFire)
|
||||
Aliases[2]=(Command=""Axis aBaseY Speed=+300.0"",Alias=MoveForward)
|
||||
0=SwitchWeapon 0
|
||||
1=SwitchWeapon 1
|
||||
2=SwitchWeapon 2
|
||||
3=SwitchWeapon 3
|
||||
"
|
||||
,
|
||||
|
||||
RequiredSections = { "Engine.Input" },
|
||||
RequiredKeys =
|
||||
{
|
||||
new SectionKey("Engine.Input", ["Aliases[0]", "Aliases[1]", "0", "1", "2", "3"]),
|
||||
},
|
||||
RequiredKeysCount =
|
||||
{
|
||||
new KeyValuePair<string, IList<KeyValueInt>>("Engine.Input", [
|
||||
new KeyValueInt("", 7)
|
||||
]),
|
||||
},
|
||||
RequiredKeyValues =
|
||||
{
|
||||
new ConfigurationTest.SectionKeyValue("Engine.Input")
|
||||
{
|
||||
KeyValues = [
|
||||
new ConfigurationTest.SectionKeyValue.KeyValue("Aliases[0]", "(Command=\"Button bFire | Fire\",Alias=Fire)"),
|
||||
new ConfigurationTest.SectionKeyValue.KeyValue("0", "SwitchWeapon 0"),
|
||||
],
|
||||
},
|
||||
},
|
||||
|
||||
UpdateKeyValues =
|
||||
{
|
||||
new ConfigurationTest.UpdateSectionKeyValue("Engine.Input")
|
||||
{
|
||||
UpdateKeyValues = [
|
||||
new ConfigurationTest.UpdateSectionKeyValue.UpdateKeyValue { Key = "Aliases[0]", NewValue = "(Command=\"Button bAltFire | AltFire\",Alias=AltFire)" },
|
||||
new ConfigurationTest.UpdateSectionKeyValue.UpdateKeyValue { Key = "Aliases[1]", NewValue = "(Command=\"Button bFire | Fire\",Alias=Fire)" },
|
||||
],
|
||||
CheckKeyValues = [
|
||||
new ConfigurationTest.SectionKeyValue("Engine.Input")
|
||||
{
|
||||
KeyValues = [
|
||||
new ConfigurationTest.SectionKeyValue.KeyValue("Aliases[0]", "(Command=\"Button bAltFire | AltFire\",Alias=AltFire)"),
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
public static ConfigurationTest Test_ArrayMultiValue = new ConfigurationTest
|
||||
{
|
||||
Ini = @"[Startup]
|
||||
Package=Core
|
||||
Package=Engine
|
||||
|
||||
[User]
|
||||
Name=Player
|
||||
Team=1
|
||||
"
|
||||
,
|
||||
|
||||
RequiredSections = { "Startup" },
|
||||
RequiredKeys =
|
||||
{
|
||||
new SectionKey("Startup", ["Package"]),
|
||||
},
|
||||
RequiredKeysCount =
|
||||
{
|
||||
new KeyValuePair<string, IList<KeyValueInt>>("Startup", [
|
||||
new KeyValueInt("Package", 2)
|
||||
]),
|
||||
},
|
||||
RequiredKeyValues =
|
||||
{
|
||||
new ConfigurationTest.SectionKeyValue("Startup")
|
||||
{
|
||||
KeyValues = [
|
||||
new ConfigurationTest.SectionKeyValue.KeyValue("Package", "Core"),
|
||||
new ConfigurationTest.SectionKeyValue.KeyValue("Package", "Engine"),
|
||||
],
|
||||
},
|
||||
},
|
||||
|
||||
UpdateKeyValues =
|
||||
{
|
||||
new ConfigurationTest.UpdateSectionKeyValue("User")
|
||||
{
|
||||
UpdateKeyValues = [new ConfigurationTest.UpdateSectionKeyValue.UpdateKeyValue { Key = "Name", NewValue = "Admin" }],
|
||||
CheckKeyValues = [
|
||||
new ConfigurationTest.SectionKeyValue("Startup")
|
||||
{
|
||||
KeyValues = [
|
||||
new ConfigurationTest.SectionKeyValue.KeyValue("Package", ["Core", "Engine"]),
|
||||
],
|
||||
},
|
||||
new ConfigurationTest.SectionKeyValue("User")
|
||||
{
|
||||
KeyValues = [
|
||||
new ConfigurationTest.SectionKeyValue.KeyValue("Name", "Admin"),
|
||||
new ConfigurationTest.SectionKeyValue.KeyValue("Team", "1"),
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
new ConfigurationTest.UpdateSectionKeyValue("Startup")
|
||||
{
|
||||
Options = new IniOptions
|
||||
{
|
||||
KeyDuplicate = IniDuplication.Ignored, // merges, takes first value of a multi-value key
|
||||
},
|
||||
UpdateKeyValues = [
|
||||
new ConfigurationTest.UpdateSectionKeyValue.UpdateKeyValue {
|
||||
Key = "Package",
|
||||
NewValue = "UI",
|
||||
ExpectedValues = ["UI"],
|
||||
},
|
||||
],
|
||||
CheckKeyValues = [
|
||||
new ConfigurationTest.SectionKeyValue("Startup")
|
||||
{
|
||||
KeyValues = [
|
||||
new ConfigurationTest.SectionKeyValue.KeyValue("Package", "UI"),
|
||||
],
|
||||
},
|
||||
new ConfigurationTest.SectionKeyValue("User")
|
||||
{
|
||||
KeyValues = [
|
||||
new ConfigurationTest.SectionKeyValue.KeyValue("Name", "Player"),
|
||||
new ConfigurationTest.SectionKeyValue.KeyValue("Team", "1"),
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
new ConfigurationTest.UpdateSectionKeyValue("Startup")
|
||||
{
|
||||
Options = new IniOptions
|
||||
{
|
||||
KeyDuplicate = IniDuplication.Allowed, // keep multi-value keys
|
||||
},
|
||||
UpdateKeyValues = [new ConfigurationTest.UpdateSectionKeyValue.UpdateKeyValue {
|
||||
Key = "Package",
|
||||
NewValue = "UI",
|
||||
ExpectedValues = ["Core", "UI"],
|
||||
}],
|
||||
CheckKeyValues = [
|
||||
new ConfigurationTest.SectionKeyValue("Startup")
|
||||
{
|
||||
KeyValues = [
|
||||
new ConfigurationTest.SectionKeyValue.KeyValue("Package", ["Core", "UI"]),
|
||||
],
|
||||
},
|
||||
new ConfigurationTest.SectionKeyValue("User")
|
||||
{
|
||||
KeyValues = [
|
||||
new ConfigurationTest.SectionKeyValue.KeyValue("Name", "Player"),
|
||||
new ConfigurationTest.SectionKeyValue.KeyValue("Team", "1"),
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,256 @@
|
|||
using System.Reflection;
|
||||
using System.Text;
|
||||
using LANCommander.SDK.Helpers;
|
||||
|
||||
namespace LANCommander.SDK.Tests.IniHandling
|
||||
{
|
||||
public class IniHandlingTests_MadMilkman
|
||||
{
|
||||
[Theory]
|
||||
[InlineData(nameof(ConfigurationTests.Test_SingleSection))]
|
||||
[InlineData(nameof(ConfigurationTests.Test_TwoSections))]
|
||||
[InlineData(nameof(ConfigurationTests.Test_ArrayStatic))]
|
||||
[InlineData(nameof(ConfigurationTests.Test_ArrayMultiValue))]
|
||||
public void Parse(string configurationName)
|
||||
{
|
||||
ConfigurationTest configuration = GetConfiguration(configurationName);
|
||||
var ini = IniHelper.FromString(configuration.Ini);
|
||||
Assert.NotNull(ini);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(nameof(ConfigurationTests.Test_SingleSection))]
|
||||
[InlineData(nameof(ConfigurationTests.Test_TwoSections))]
|
||||
[InlineData(nameof(ConfigurationTests.Test_ArrayStatic))]
|
||||
[InlineData(nameof(ConfigurationTests.Test_ArrayMultiValue))]
|
||||
public void CheckSections(string configurationName)
|
||||
{
|
||||
ConfigurationTest configuration = GetConfiguration(configurationName);
|
||||
var ini = IniHelper.FromString(configuration.Ini);
|
||||
|
||||
foreach (var requiredKey in configuration.RequiredSections)
|
||||
{
|
||||
Assert.True(ini.Sections.Contains(requiredKey));
|
||||
}
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(nameof(ConfigurationTests.Test_SingleSection))]
|
||||
[InlineData(nameof(ConfigurationTests.Test_TwoSections))]
|
||||
[InlineData(nameof(ConfigurationTests.Test_ArrayStatic))]
|
||||
[InlineData(nameof(ConfigurationTests.Test_ArrayMultiValue))]
|
||||
public void CheckSectionsCount(string configurationName)
|
||||
{
|
||||
ConfigurationTest configuration = GetConfiguration(configurationName);
|
||||
var ini = IniHelper.FromString(configuration.Ini);
|
||||
|
||||
foreach (var requiredSection in configuration.RequiredSectionsCount)
|
||||
{
|
||||
Assert.Equal(requiredSection.Value, ini.Sections.Count(x => string.Equals(x.Name, requiredSection.Key, StringComparison.InvariantCultureIgnoreCase)));
|
||||
}
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(nameof(ConfigurationTests.Test_SingleSection))]
|
||||
[InlineData(nameof(ConfigurationTests.Test_TwoSections))]
|
||||
[InlineData(nameof(ConfigurationTests.Test_ArrayStatic))]
|
||||
[InlineData(nameof(ConfigurationTests.Test_ArrayMultiValue))]
|
||||
public void CheckSectionKeys(string configurationName)
|
||||
{
|
||||
ConfigurationTest configuration = GetConfiguration(configurationName);
|
||||
var ini = IniHelper.FromString(configuration.Ini);
|
||||
|
||||
foreach (var requiredSection in configuration.RequiredKeys)
|
||||
{
|
||||
var section = ini.Sections[requiredSection.Key];
|
||||
Assert.NotNull(section);
|
||||
|
||||
foreach (var requiredKey in requiredSection.Value)
|
||||
{
|
||||
Assert.True(section.Keys.Contains(requiredKey));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
[Theory]
|
||||
[InlineData(nameof(ConfigurationTests.Test_SingleSection))]
|
||||
[InlineData(nameof(ConfigurationTests.Test_TwoSections))]
|
||||
[InlineData(nameof(ConfigurationTests.Test_ArrayStatic))]
|
||||
[InlineData(nameof(ConfigurationTests.Test_ArrayMultiValue))]
|
||||
public void CheckSectionKeysCount(string configurationName)
|
||||
{
|
||||
ConfigurationTest configuration = GetConfiguration(configurationName);
|
||||
var ini = IniHelper.FromString(configuration.Ini);
|
||||
|
||||
foreach (var requiredSection in configuration.RequiredKeysCount)
|
||||
{
|
||||
var section = ini.Sections[requiredSection.Key];
|
||||
Assert.NotNull(section);
|
||||
|
||||
foreach (var requiredKeys in requiredSection.Value)
|
||||
{
|
||||
var count = (requiredKeys.Key == "")
|
||||
? section.Keys.Count
|
||||
: section.Keys.Count(x => string.Equals(x.Name, requiredKeys.Key, StringComparison.InvariantCultureIgnoreCase));
|
||||
|
||||
Assert.Equal(requiredKeys.Value, count);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(nameof(ConfigurationTests.Test_SingleSection))]
|
||||
[InlineData(nameof(ConfigurationTests.Test_TwoSections))]
|
||||
[InlineData(nameof(ConfigurationTests.Test_ArrayStatic))]
|
||||
[InlineData(nameof(ConfigurationTests.Test_ArrayMultiValue))]
|
||||
public void CheckSectionKeyValues(string configurationName)
|
||||
{
|
||||
ConfigurationTest configuration = GetConfiguration(configurationName);
|
||||
var ini = IniHelper.FromString(configuration.Ini);
|
||||
|
||||
foreach (var requiredSection in configuration.RequiredKeyValues)
|
||||
{
|
||||
var section = ini.Sections[requiredSection.Section];
|
||||
Assert.NotNull(section);
|
||||
|
||||
foreach (var requiredKeyValue in requiredSection.KeyValues)
|
||||
{
|
||||
var match = section.Keys.FirstOrDefault(key => {
|
||||
return string.Equals(key.Name, requiredKeyValue.Key, StringComparison.InvariantCultureIgnoreCase)
|
||||
&& string.Equals(key.Value, requiredKeyValue.Value, StringComparison.InvariantCulture);
|
||||
});
|
||||
|
||||
Assert.NotNull(match);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
[Theory]
|
||||
[InlineData(nameof(ConfigurationTests.Test_SingleSection))]
|
||||
[InlineData(nameof(ConfigurationTests.Test_TwoSections))]
|
||||
[InlineData(nameof(ConfigurationTests.Test_ArrayStatic))]
|
||||
[InlineData(nameof(ConfigurationTests.Test_ArrayMultiValue))]
|
||||
public void UpdateSectionKeyValues(string configurationName)
|
||||
{
|
||||
ConfigurationTest configuration = GetConfiguration(configurationName);
|
||||
var ini = IniHelper.FromString(configuration.Ini);
|
||||
Assert.NotNull(ini);
|
||||
|
||||
foreach (var requiredSection in configuration.UpdateKeyValues)
|
||||
{
|
||||
var options = requiredSection.Options ?? IniHelper.DefaultIniOptions;
|
||||
ini = IniHelper.FromString(configuration.Ini, options);
|
||||
var section = ini.Sections[requiredSection.Section];
|
||||
Assert.NotNull(section);
|
||||
|
||||
foreach (var updateKeyValue in requiredSection.UpdateKeyValues)
|
||||
{
|
||||
bool found = false;
|
||||
|
||||
foreach (var iniKey in section.Keys.Reverse())
|
||||
{
|
||||
if (string.Equals(iniKey.Name, updateKeyValue.Key, StringComparison.InvariantCultureIgnoreCase))
|
||||
{
|
||||
if (updateKeyValue.OldValue == null || string.Equals(updateKeyValue.Key, iniKey.Value, StringComparison.InvariantCultureIgnoreCase))
|
||||
{
|
||||
iniKey.Value = updateKeyValue.NewValue;
|
||||
Assert.Equal(iniKey.Value, updateKeyValue.NewValue);
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Assert.True(found);
|
||||
}
|
||||
|
||||
// check if values are stored properly
|
||||
|
||||
foreach (var requiredKeyValue in requiredSection.UpdateKeyValues)
|
||||
{
|
||||
IList<KeyValuePair<string, string?>> searches = [];
|
||||
searches.Add(new KeyValuePair<string, string?>(requiredKeyValue.Key, requiredKeyValue.NewValue));
|
||||
|
||||
if (requiredKeyValue.ExpectedValues != null)
|
||||
{
|
||||
foreach (var expected in requiredKeyValue.ExpectedValues)
|
||||
{
|
||||
searches.Add(new KeyValuePair<string, string?>(requiredKeyValue.Key, expected));
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var search in searches)
|
||||
{
|
||||
var match = section.Keys.FirstOrDefault(key =>
|
||||
{
|
||||
return string.Equals(key.Name, requiredKeyValue.Key, StringComparison.InvariantCultureIgnoreCase)
|
||||
&& string.Equals(key.Value, requiredKeyValue.NewValue, StringComparison.InvariantCulture);
|
||||
});
|
||||
|
||||
Assert.NotNull(match);
|
||||
}
|
||||
}
|
||||
|
||||
// compare newly generated INI
|
||||
|
||||
string iniContentNew = IniHelper.ToString(ini, Encoding.Default);
|
||||
var iniNew = IniHelper.FromString(iniContentNew, options);
|
||||
Assert.NotNull(iniNew);
|
||||
|
||||
foreach (var checkSection in requiredSection.CheckKeyValues)
|
||||
{
|
||||
var sectionNew = iniNew.Sections[checkSection.Section];
|
||||
Assert.NotNull(sectionNew);
|
||||
|
||||
foreach (var checkKeyValue in checkSection.KeyValues)
|
||||
{
|
||||
IList<KeyValuePair<string, string?>> searches = [];
|
||||
if (checkKeyValue.Values != null)
|
||||
{
|
||||
foreach (var expected in checkKeyValue.Values)
|
||||
{
|
||||
searches.Add(new KeyValuePair<string, string?>(checkKeyValue.Key, expected));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
searches.Add(new KeyValuePair<string, string?>(checkKeyValue.Key, checkKeyValue.Value));
|
||||
}
|
||||
|
||||
foreach (var search in searches)
|
||||
{
|
||||
var match = sectionNew.Keys.FirstOrDefault(key =>
|
||||
{
|
||||
return string.Equals(key.Name, search.Key, StringComparison.InvariantCultureIgnoreCase)
|
||||
&& string.Equals(key.Value, search.Value, StringComparison.InvariantCulture);
|
||||
});
|
||||
|
||||
Assert.NotNull(match);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static ConfigurationTest GetConfiguration(string configurationName)
|
||||
{
|
||||
Type configTestsType = typeof(ConfigurationTests);
|
||||
FieldInfo? field = configTestsType?.GetField(configurationName, BindingFlags.Public | BindingFlags.Static);
|
||||
|
||||
if (field == null)
|
||||
{
|
||||
throw new Exception($"The configuration '{configurationName}' does not exist.");
|
||||
}
|
||||
|
||||
ConfigurationTest? configurationTest = field.GetValue(null) as ConfigurationTest;
|
||||
if (configurationTest == null)
|
||||
{
|
||||
throw new Exception($"The configuration '{configurationName}' is not of type ConfigurationTest or is null.");
|
||||
}
|
||||
|
||||
return configurationTest;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,207 @@
|
|||
using System.Reflection;
|
||||
using PeanutButter.INI;
|
||||
|
||||
namespace LANCommander.SDK.Tests.IniHandling
|
||||
{
|
||||
public class IniHandlingTests_PeanutButter
|
||||
{
|
||||
[Theory]
|
||||
[InlineData(nameof(ConfigurationTests.Test_SingleSection))]
|
||||
[InlineData(nameof(ConfigurationTests.Test_TwoSections))]
|
||||
[InlineData(nameof(ConfigurationTests.Test_ArrayStatic))]
|
||||
[InlineData(nameof(ConfigurationTests.Test_ArrayMultiValue))]
|
||||
public void Parse(string configurationName)
|
||||
{
|
||||
ConfigurationTest configuration = GetConfiguration(configurationName);
|
||||
var ini = INIFile.FromString(configuration.Ini);
|
||||
Assert.NotNull(ini);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(nameof(ConfigurationTests.Test_SingleSection))]
|
||||
[InlineData(nameof(ConfigurationTests.Test_TwoSections))]
|
||||
[InlineData(nameof(ConfigurationTests.Test_ArrayStatic))]
|
||||
[InlineData(nameof(ConfigurationTests.Test_ArrayMultiValue))]
|
||||
public void CheckSections(string configurationName)
|
||||
{
|
||||
ConfigurationTest configuration = GetConfiguration(configurationName);
|
||||
var ini = INIFile.FromString(configuration.Ini);
|
||||
|
||||
foreach (var requiredKey in configuration.RequiredSections)
|
||||
{
|
||||
Assert.True(ini.HasSection(requiredKey));
|
||||
}
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(nameof(ConfigurationTests.Test_SingleSection))]
|
||||
[InlineData(nameof(ConfigurationTests.Test_TwoSections))]
|
||||
[InlineData(nameof(ConfigurationTests.Test_ArrayStatic))]
|
||||
[InlineData(nameof(ConfigurationTests.Test_ArrayMultiValue))]
|
||||
public void CheckSectionsCount(string configurationName)
|
||||
{
|
||||
ConfigurationTest configuration = GetConfiguration(configurationName);
|
||||
var ini = INIFile.FromString(configuration.Ini);
|
||||
|
||||
foreach (var requiredSection in configuration.RequiredSectionsCount)
|
||||
{
|
||||
Assert.Equal(requiredSection.Value, ini.AllSections.Count(x => string.Equals(x, requiredSection.Key, StringComparison.InvariantCultureIgnoreCase)));
|
||||
}
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(nameof(ConfigurationTests.Test_SingleSection))]
|
||||
[InlineData(nameof(ConfigurationTests.Test_TwoSections))]
|
||||
[InlineData(nameof(ConfigurationTests.Test_ArrayStatic))]
|
||||
[InlineData(nameof(ConfigurationTests.Test_ArrayMultiValue))]
|
||||
public void CheckSectionKeys(string configurationName)
|
||||
{
|
||||
ConfigurationTest configuration = GetConfiguration(configurationName);
|
||||
var ini = INIFile.FromString(configuration.Ini);
|
||||
|
||||
foreach (var requiredSection in configuration.RequiredKeys)
|
||||
{
|
||||
var section = ini.GetSection(requiredSection.Key);
|
||||
Assert.NotNull(section);
|
||||
|
||||
foreach (var requiredKey in requiredSection.Value)
|
||||
{
|
||||
Assert.True(section.ContainsKey(requiredKey));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
[Theory]
|
||||
[InlineData(nameof(ConfigurationTests.Test_SingleSection))]
|
||||
[InlineData(nameof(ConfigurationTests.Test_TwoSections))]
|
||||
[InlineData(nameof(ConfigurationTests.Test_ArrayStatic))]
|
||||
[InlineData(nameof(ConfigurationTests.Test_ArrayMultiValue))]
|
||||
public void CheckSectionKeysCount(string configurationName)
|
||||
{
|
||||
ConfigurationTest configuration = GetConfiguration(configurationName);
|
||||
var ini = INIFile.FromString(configuration.Ini);
|
||||
|
||||
foreach (var requiredSection in configuration.RequiredKeysCount)
|
||||
{
|
||||
var section = ini.GetSection(requiredSection.Key);
|
||||
Assert.NotNull(section);
|
||||
|
||||
foreach (var requiredKeys in requiredSection.Value)
|
||||
{
|
||||
Assert.Equal(requiredKeys.Value, section.Keys.Count(x => string.Equals(x, requiredKeys.Key, StringComparison.InvariantCultureIgnoreCase)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(nameof(ConfigurationTests.Test_SingleSection))]
|
||||
[InlineData(nameof(ConfigurationTests.Test_TwoSections))]
|
||||
[InlineData(nameof(ConfigurationTests.Test_ArrayStatic))]
|
||||
[InlineData(nameof(ConfigurationTests.Test_ArrayMultiValue))]
|
||||
public void CheckSectionKeyValues(string configurationName)
|
||||
{
|
||||
ConfigurationTest configuration = GetConfiguration(configurationName);
|
||||
var ini = INIFile.FromString(configuration.Ini);
|
||||
|
||||
foreach (var requiredSection in configuration.RequiredKeyValues)
|
||||
{
|
||||
var section = ini.GetSection(requiredSection.Section);
|
||||
Assert.NotNull(section);
|
||||
|
||||
foreach (var requiredKeyValue in requiredSection.KeyValues)
|
||||
{
|
||||
var match = section.FirstOrDefault(key =>
|
||||
{
|
||||
return string.Equals(key.Key, requiredKeyValue.Key, StringComparison.InvariantCultureIgnoreCase)
|
||||
&& string.Equals(key.Value, requiredKeyValue.Value, StringComparison.InvariantCulture);
|
||||
});
|
||||
|
||||
Assert.NotEqual(match, default);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[Theory]
|
||||
//[InlineData(nameof(ConfigurationTests.Test_SingleSection))]
|
||||
//[InlineData(nameof(ConfigurationTests.Test_TwoSections))]
|
||||
//[InlineData(nameof(ConfigurationTests.Test_ArrayStatic))]
|
||||
[InlineData(nameof(ConfigurationTests.Test_ArrayMultiValue))]
|
||||
public void UpdateSectionKeyValues(string configurationName)
|
||||
{
|
||||
ConfigurationTest configuration = GetConfiguration(configurationName);
|
||||
var ini = INIFile.FromString(configuration.Ini);
|
||||
Assert.NotNull(ini);
|
||||
|
||||
foreach (var requiredSection in configuration.UpdateKeyValues)
|
||||
{
|
||||
var section = ini.GetSection(requiredSection.Section);
|
||||
Assert.NotNull(section);
|
||||
|
||||
foreach (var updateKeyValue in requiredSection.UpdateKeyValues)
|
||||
{
|
||||
ini.SetValue(requiredSection.Section, updateKeyValue.Key, updateKeyValue.NewValue);
|
||||
var value = ini.GetValue(requiredSection.Section, updateKeyValue.Key);
|
||||
Assert.True(updateKeyValue.NewValue == null || string.Equals(updateKeyValue.NewValue, value, StringComparison.InvariantCulture));
|
||||
}
|
||||
|
||||
// compare newly generated INI
|
||||
string iniContentNew = ini.ToString();
|
||||
var iniNew = INIFile.FromString(iniContentNew);
|
||||
Assert.NotNull(iniNew);
|
||||
|
||||
foreach (var checkSection in requiredSection.CheckKeyValues)
|
||||
{
|
||||
var sectionNew = iniNew.GetSection(checkSection.Section);
|
||||
Assert.NotNull(sectionNew);
|
||||
|
||||
foreach (var checkKeyValue in checkSection.KeyValues)
|
||||
{
|
||||
IList<KeyValuePair<string, string?>> searches = [];
|
||||
if (checkKeyValue.Values != null)
|
||||
{
|
||||
foreach (var expected in checkKeyValue.Values)
|
||||
{
|
||||
searches.Add(new KeyValuePair<string, string?>(checkKeyValue.Key, expected));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
searches.Add(new KeyValuePair<string, string?>(checkKeyValue.Key, checkKeyValue.Value));
|
||||
}
|
||||
|
||||
foreach (var search in searches)
|
||||
{
|
||||
var match = section.FirstOrDefault(key =>
|
||||
{
|
||||
return string.Equals(key.Key, search.Key, StringComparison.InvariantCultureIgnoreCase)
|
||||
&& string.Equals(key.Value, search.Value, StringComparison.InvariantCulture);
|
||||
});
|
||||
|
||||
Assert.NotEqual(match, default);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static ConfigurationTest GetConfiguration(string configurationName)
|
||||
{
|
||||
Type configTestsType = typeof(ConfigurationTests);
|
||||
FieldInfo? field = configTestsType?.GetField(configurationName, BindingFlags.Public | BindingFlags.Static);
|
||||
|
||||
if (field == null)
|
||||
{
|
||||
throw new Exception($"The configuration '{configurationName}' does not exist.");
|
||||
}
|
||||
|
||||
ConfigurationTest? configurationTest = field.GetValue(null) as ConfigurationTest;
|
||||
if (configurationTest == null)
|
||||
{
|
||||
throw new Exception($"The configuration '{configurationName}' is not of type ConfigurationTest or is null.");
|
||||
}
|
||||
|
||||
return configurationTest;
|
||||
}
|
||||
}
|
||||
}
|
||||
49
LANCommander.SDK/Helpers/IniHelper.cs
Normal file
49
LANCommander.SDK/Helpers/IniHelper.cs
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
using System.IO;
|
||||
using System.Text;
|
||||
using MadMilkman.Ini;
|
||||
|
||||
namespace LANCommander.SDK.Helpers
|
||||
{
|
||||
public static class IniHelper
|
||||
{
|
||||
public static readonly IniOptions DefaultIniOptions = new()
|
||||
{
|
||||
};
|
||||
|
||||
public static IniFile FromString(string iniFileContent)
|
||||
{
|
||||
return FromString(iniFileContent, DefaultIniOptions);
|
||||
}
|
||||
|
||||
public static IniFile FromString(string iniFileContent, IniOptions options)
|
||||
{
|
||||
IniFile file = new(options);
|
||||
|
||||
using (var stream = new MemoryStream(options.Encoding.GetBytes(iniFileContent)))
|
||||
file.Load(stream);
|
||||
|
||||
return file;
|
||||
}
|
||||
|
||||
public static string ToString(IniFile file, IniOptions options)
|
||||
{
|
||||
return ToString(file, options.Encoding);
|
||||
}
|
||||
|
||||
public static string ToString(IniFile file, Encoding encoding)
|
||||
{
|
||||
string iniFileContent;
|
||||
using (var stream = new MemoryStream())
|
||||
using (var reader = new StreamReader(stream, encoding))
|
||||
{
|
||||
file.Save(stream);
|
||||
stream.Flush();
|
||||
stream.Seek(0, SeekOrigin.Begin);
|
||||
|
||||
iniFileContent = reader.ReadToEnd();
|
||||
}
|
||||
|
||||
return iniFileContent;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -19,6 +19,7 @@
|
|||
<PackageReference Include="CommandLineParser" Version="2.9.1" />
|
||||
<PackageReference Include="Crc32.NET" Version="1.2.0" />
|
||||
<PackageReference Include="Facepunch.Steamworks" Version="2.3.3" />
|
||||
<PackageReference Include="MadMilkman.Ini" Version="1.0.6" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="9.0.1" />
|
||||
<PackageReference Include="Microsoft.PowerShell.Commands.Diagnostics" Version="7.4.7" />
|
||||
<PackageReference Include="Microsoft.PowerShell.SDK" Version="7.4.7" />
|
||||
|
|
|
|||
|
|
@ -1,7 +1,11 @@
|
|||
using PeanutButter.INI;
|
||||
using MadMilkman.Ini;
|
||||
using PeanutButter.INI;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Management.Automation;
|
||||
using System.Text;
|
||||
using YamlDotNet.Core.Tokens;
|
||||
|
||||
namespace LANCommander.SDK.PowerShell.Cmdlets
|
||||
{
|
||||
|
|
@ -10,30 +14,127 @@ namespace LANCommander.SDK.PowerShell.Cmdlets
|
|||
public class UpdateIniValueCmdlet : Cmdlet
|
||||
{
|
||||
[Parameter(Mandatory = true, Position = 0)]
|
||||
[Alias("s")]
|
||||
public string Section { get; set; }
|
||||
|
||||
[Parameter(Mandatory = true, Position = 1)]
|
||||
[Alias("k")]
|
||||
public string Key { get; set; }
|
||||
|
||||
[Parameter(Mandatory = true, Position = 2)]
|
||||
[Alias("v")]
|
||||
public string Value { get; set; }
|
||||
|
||||
[Parameter(Mandatory = true, Position = 3)]
|
||||
[Alias("f")]
|
||||
public string FilePath { get; set; }
|
||||
|
||||
[Parameter(Mandatory = false)]
|
||||
[Alias("wrap", "quotes")]
|
||||
public bool WrapValueInQuotes { get; set; } = false;
|
||||
|
||||
[Parameter(Mandatory = false)]
|
||||
[Alias("add")]
|
||||
public bool UpdateOrAdd { get; set; } = true;
|
||||
|
||||
[Alias("remove-only")]
|
||||
[Parameter(Mandatory = false)]
|
||||
public SwitchParameter OnlyRemove { get; set; } = false;
|
||||
|
||||
[Parameter(Mandatory = false)]
|
||||
public SwitchParameter Clear { get; set; } = false;
|
||||
|
||||
[Alias("append")]
|
||||
[Parameter(Mandatory = false)]
|
||||
public SwitchParameter AlwaysAppend { get; set; } = false;
|
||||
|
||||
[Parameter(Mandatory = false)]
|
||||
[Alias("insert")]
|
||||
public int? InsertIndex { get; set; } = null;
|
||||
|
||||
[Alias("keepkey", "keydup")]
|
||||
[Parameter(Mandatory = false)]
|
||||
public bool KeepKeyDuplicates { get; set; } = true;
|
||||
|
||||
[Alias("keepsec", "secdup")]
|
||||
[Parameter(Mandatory = false)]
|
||||
public bool KeepSectionDuplicates { get; set; } = true;
|
||||
|
||||
[Alias("encoding", "enc")]
|
||||
[Parameter(Mandatory = false)]
|
||||
public string Codepage { get; set; } = "Latin";
|
||||
|
||||
protected Encoding GetEncoding()
|
||||
{
|
||||
string page = Codepage?.ToLower();
|
||||
switch (page)
|
||||
{
|
||||
case "uft8": return Encoding.UTF8;
|
||||
case "latin":
|
||||
case "latin1":
|
||||
case "ISO-8859-1":
|
||||
return Encoding.Latin1;
|
||||
case "asci":
|
||||
case "ascii":
|
||||
return Encoding.ASCII;
|
||||
case "unicode":
|
||||
return Encoding.Unicode;
|
||||
}
|
||||
|
||||
return Encoding.Default;
|
||||
}
|
||||
|
||||
protected override void ProcessRecord()
|
||||
{
|
||||
if (File.Exists(FilePath))
|
||||
{
|
||||
var ini = new INIFile(FilePath);
|
||||
if (!File.Exists(FilePath))
|
||||
return;
|
||||
|
||||
ini.SetValue(Section, Key, Value);
|
||||
ini.WrapValueInQuotes = WrapValueInQuotes;
|
||||
ini.Persist();
|
||||
var iniOptions = new IniOptions()
|
||||
{
|
||||
Encoding = GetEncoding(),
|
||||
SectionDuplicate = KeepSectionDuplicates ? IniDuplication.Allowed : IniDuplication.Ignored,
|
||||
KeyDuplicate = KeepKeyDuplicates ? IniDuplication.Allowed : IniDuplication.Ignored,
|
||||
};
|
||||
var ini = new IniFile(iniOptions);
|
||||
ini.Load(FilePath);
|
||||
|
||||
var iniSection = ini.Sections[Section];
|
||||
if (iniSection == null)
|
||||
return;
|
||||
|
||||
bool keyMatcher(IniKey x) => string.Equals(x.Name, Key, StringComparison.OrdinalIgnoreCase);
|
||||
|
||||
if (OnlyRemove || Clear)
|
||||
{
|
||||
var list = iniSection.Keys.Where(keyMatcher).ToList();
|
||||
list.ForEach(x => iniSection.Keys.Remove(x));
|
||||
}
|
||||
|
||||
if (!OnlyRemove)
|
||||
{
|
||||
// assuming most of the engines interpret INI files from top to bottom using the last value of a multiple existing key, we update the last found key
|
||||
var firstMatch = iniSection.Keys.LastOrDefault(keyMatcher);
|
||||
if (AlwaysAppend.ToBool() || (UpdateOrAdd && firstMatch == null))
|
||||
{
|
||||
if (InsertIndex.HasValue && InsertIndex.Value >= 0)
|
||||
iniSection.Keys.Insert(Math.Clamp(InsertIndex.Value, 0, iniSection.Keys.Count - 1), Key, Value);
|
||||
else
|
||||
iniSection.Keys.Add(Key, Value);
|
||||
}
|
||||
else if (firstMatch != null)
|
||||
{
|
||||
var insertIndex = (InsertIndex.HasValue && InsertIndex.Value >= 0) ? Math.Clamp(InsertIndex.Value, 0, iniSection.Keys.Count - 1) : -1;
|
||||
if (insertIndex >= 0)
|
||||
{
|
||||
iniSection.Keys.Remove(firstMatch);
|
||||
iniSection.Keys.Insert(insertIndex, firstMatch);
|
||||
}
|
||||
firstMatch.Value = Value;
|
||||
}
|
||||
// no else, as it should be possible to only update an existing value without adding a new one
|
||||
}
|
||||
|
||||
ini.Save(FilePath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue