Options can now be removed from a configuration file; added more unit tests to cover all the code paths
git-svn-id: file:///srv/devel/repo-conversion/nusu@306 d2e56fa2-650e-0410-a79f-9358c0239efd
This commit is contained in:
parent
7bde463b2a
commit
55b3ffd923
|
@ -244,6 +244,144 @@ namespace Nuclex.Support.Settings {
|
||||||
Assert.That(save(configurationFile), Contains.Substring("[general]"));
|
Assert.That(save(configurationFile), Contains.Substring("[general]"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Verifies that accessing an option that doesn't exist throws an exception
|
||||||
|
/// </summary>
|
||||||
|
[Test]
|
||||||
|
public void AccessingNonExistingOptionThrowsException() {
|
||||||
|
var configurationFile = new ConfigurationFileStore();
|
||||||
|
|
||||||
|
Assert.That(
|
||||||
|
() => configurationFile.Get<string>(null, "doesn't exist"),
|
||||||
|
Throws.Exception.AssignableTo<KeyNotFoundException>()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Verifies that accessing a category that doesn't exist throws an exception
|
||||||
|
/// </summary>
|
||||||
|
[Test]
|
||||||
|
public void AccessingNonExistingCategoryThrowsException() {
|
||||||
|
var configurationFile = new ConfigurationFileStore();
|
||||||
|
configurationFile.Set<string>(null, "test", "123");
|
||||||
|
|
||||||
|
Assert.That(
|
||||||
|
() => configurationFile.Get<string>("doesn't exist", "test"),
|
||||||
|
Throws.Exception.AssignableTo<KeyNotFoundException>()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Verifies that it's possible to enumerate a category that doesn't exist
|
||||||
|
/// </summary>
|
||||||
|
[Test]
|
||||||
|
public void NonExistingCategoryCanBeEnumerated() {
|
||||||
|
var configurationFile = new ConfigurationFileStore();
|
||||||
|
|
||||||
|
Assert.That(configurationFile.EnumerateOptions("doesn't exist"), Is.Empty);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Verifies that it's possible to create an option without a value
|
||||||
|
/// </summary>
|
||||||
|
[Test]
|
||||||
|
public void ValuelessOptionsCanBeCreated() {
|
||||||
|
var configurationFile = new ConfigurationFileStore();
|
||||||
|
|
||||||
|
configurationFile.Set<string>(null, "test", null);
|
||||||
|
Assert.That(configurationFile.Get<string>(null, "test"), Is.Null.Or.Empty);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Verifies that it's possible to assign an empty value to an option
|
||||||
|
/// </summary>
|
||||||
|
[Test]
|
||||||
|
public void OptionValueCanBeCleared() {
|
||||||
|
string fileContents = "test = 123 ; comment";
|
||||||
|
ConfigurationFileStore configurationFile = load(fileContents);
|
||||||
|
|
||||||
|
configurationFile.Set<string>(null, "test", null);
|
||||||
|
Assert.That(configurationFile.Get<string>(null, "test"), Is.Null.Or.Empty);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Verifies that it's possible to assign an empty value to an option
|
||||||
|
/// </summary>
|
||||||
|
[Test]
|
||||||
|
public void OptionsCanBeRemoved() {
|
||||||
|
var configurationFile = new ConfigurationFileStore();
|
||||||
|
configurationFile.Set<string>(null, "test", null);
|
||||||
|
|
||||||
|
Assert.That(configurationFile.Remove(null, "test"), Is.True);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Verifies that it's possible to assign an empty value to an option
|
||||||
|
/// </summary>
|
||||||
|
[Test]
|
||||||
|
public void RemovingOptionShiftsFollowingOptionsUp() {
|
||||||
|
string fileContents =
|
||||||
|
"first = 1\r\n" +
|
||||||
|
"second = 2";
|
||||||
|
ConfigurationFileStore configurationFile = load(fileContents);
|
||||||
|
|
||||||
|
Assert.That(configurationFile.Remove(null, "first"), Is.True);
|
||||||
|
configurationFile.Set<string>(null, "second", "yay! first!");
|
||||||
|
|
||||||
|
Assert.That(save(configurationFile), Has.No.ContainsSubstring("1"));
|
||||||
|
Assert.That(save(configurationFile), Contains.Substring("second"));
|
||||||
|
Assert.That(save(configurationFile), Contains.Substring("yay! first!"));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Verifies that it's not an error to remove an option from a non-existing category
|
||||||
|
/// </summary>
|
||||||
|
[Test]
|
||||||
|
public void CanRemoveOptionFromNonExistingCategory() {
|
||||||
|
var configurationFile = new ConfigurationFileStore();
|
||||||
|
Assert.That(configurationFile.Remove("nothing", "first"), Is.False);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Verifies that it's not an error to remove a non-existing option
|
||||||
|
/// </summary>
|
||||||
|
[Test]
|
||||||
|
public void CanRemoveNonExistingOption() {
|
||||||
|
var configurationFile = new ConfigurationFileStore();
|
||||||
|
Assert.That(configurationFile.Remove(null, "first"), Is.False);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Verifies that it's not an error to remove a non-existing option
|
||||||
|
/// </summary>
|
||||||
|
[
|
||||||
|
Test,
|
||||||
|
TestCase("text = world", typeof(string)),
|
||||||
|
TestCase("short=9", typeof(int)),
|
||||||
|
TestCase("integer = 123", typeof(int)),
|
||||||
|
TestCase("integer = 123 ", typeof(int)),
|
||||||
|
TestCase("float = 123.45", typeof(float)),
|
||||||
|
TestCase("float = 123.45 ", typeof(float)),
|
||||||
|
TestCase("boolean = true", typeof(bool)),
|
||||||
|
TestCase("boolean = false", typeof(bool)),
|
||||||
|
TestCase("boolean = yes", typeof(bool)),
|
||||||
|
TestCase("boolean = no", typeof(bool))
|
||||||
|
]
|
||||||
|
public void OptionTypeCanBeIdentified(string assignment, Type expectedType) {
|
||||||
|
ConfigurationFileStore configurationFile = load(assignment);
|
||||||
|
|
||||||
|
OptionInfo info;
|
||||||
|
using(
|
||||||
|
IEnumerator<OptionInfo> enumerator = configurationFile.EnumerateOptions().GetEnumerator()
|
||||||
|
) {
|
||||||
|
Assert.That(enumerator.MoveNext(), Is.True);
|
||||||
|
info = enumerator.Current;
|
||||||
|
Assert.That(enumerator.MoveNext(), Is.False);
|
||||||
|
}
|
||||||
|
|
||||||
|
Assert.That(info.OptionType, Is.EqualTo(expectedType));
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>Loads a configuration file from a string</summary>
|
/// <summary>Loads a configuration file from a string</summary>
|
||||||
/// <param name="fileContents">Contents of the configuration file</param>
|
/// <param name="fileContents">Contents of the configuration file</param>
|
||||||
/// <returns>The configuration file loaded from the string</returns>
|
/// <returns>The configuration file loaded from the string</returns>
|
||||||
|
|
|
@ -103,6 +103,9 @@ namespace Nuclex.Support.Settings {
|
||||||
/// <returns>An enumerable list of all options in the category</returns>
|
/// <returns>An enumerable list of all options in the category</returns>
|
||||||
public IEnumerable<OptionInfo> EnumerateOptions(string category = null) {
|
public IEnumerable<OptionInfo> EnumerateOptions(string category = null) {
|
||||||
Category enumeratedCategory = getCategoryByName(category);
|
Category enumeratedCategory = getCategoryByName(category);
|
||||||
|
if(enumeratedCategory == null) {
|
||||||
|
yield break;
|
||||||
|
}
|
||||||
|
|
||||||
foreach(Option option in this.RootCategory.OptionLookup.Values) {
|
foreach(Option option in this.RootCategory.OptionLookup.Values) {
|
||||||
OptionInfo optionInfo = new OptionInfo() {
|
OptionInfo optionInfo = new OptionInfo() {
|
||||||
|
@ -147,15 +150,16 @@ namespace Nuclex.Support.Settings {
|
||||||
/// </returns>
|
/// </returns>
|
||||||
public bool TryGet<TValue>(string category, string optionName, out TValue value) {
|
public bool TryGet<TValue>(string category, string optionName, out TValue value) {
|
||||||
Category containingCategory = getCategoryByName(category);
|
Category containingCategory = getCategoryByName(category);
|
||||||
|
if(containingCategory != null) {
|
||||||
Option option;
|
Option option;
|
||||||
if(containingCategory.OptionLookup.TryGetValue(optionName, out option)) {
|
if(containingCategory.OptionLookup.TryGetValue(optionName, out option)) {
|
||||||
value = (TValue)Convert.ChangeType(option.OptionValue.ToString(), typeof(TValue));
|
value = (TValue)Convert.ChangeType(option.OptionValue.ToString(), typeof(TValue));
|
||||||
return true;
|
return true;
|
||||||
} else {
|
}
|
||||||
value = default(TValue);
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
value = default(TValue);
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>Saves an option in the settings store</summary>
|
/// <summary>Saves an option in the settings store</summary>
|
||||||
|
@ -164,30 +168,22 @@ namespace Nuclex.Support.Settings {
|
||||||
/// <param name="optionName">Name of the option that will be saved</param>
|
/// <param name="optionName">Name of the option that will be saved</param>
|
||||||
/// <param name="value">The value under which the option will be saved</param>
|
/// <param name="value">The value under which the option will be saved</param>
|
||||||
public void Set<TValue>(string category, string optionName, TValue value) {
|
public void Set<TValue>(string category, string optionName, TValue value) {
|
||||||
Category targetCategory;
|
|
||||||
bool optionMightExist;
|
|
||||||
|
|
||||||
if(string.IsNullOrEmpty(category)) {
|
|
||||||
targetCategory = this.RootCategory;
|
|
||||||
optionMightExist = true;
|
|
||||||
} else if(this.categoryLookup.TryGetValue(category, out targetCategory)) {
|
|
||||||
optionMightExist = true;
|
|
||||||
} else {
|
|
||||||
targetCategory = createCategory(category);
|
|
||||||
optionMightExist = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
string valueAsString = (string)Convert.ChangeType(
|
string valueAsString = (string)Convert.ChangeType(
|
||||||
value, typeof(string), CultureInfo.InvariantCulture
|
value, typeof(string), CultureInfo.InvariantCulture
|
||||||
);
|
);
|
||||||
|
|
||||||
|
Category targetCategory;
|
||||||
|
if(string.IsNullOrEmpty(category)) {
|
||||||
|
targetCategory = this.RootCategory;
|
||||||
|
} else if(!this.categoryLookup.TryGetValue(category, out targetCategory)) {
|
||||||
|
targetCategory = createCategory(category);
|
||||||
|
createOption(targetCategory, optionName, valueAsString);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
Option targetOption;
|
Option targetOption;
|
||||||
if(optionMightExist) {
|
if(targetCategory.OptionLookup.TryGetValue(optionName, out targetOption)) {
|
||||||
if(targetCategory.OptionLookup.TryGetValue(optionName, out targetOption)) {
|
changeOption(targetCategory, targetOption, valueAsString);
|
||||||
changeOption(targetCategory, targetOption, valueAsString);
|
|
||||||
} else {
|
|
||||||
createOption(targetCategory, optionName, valueAsString);
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
createOption(targetCategory, optionName, valueAsString);
|
createOption(targetCategory, optionName, valueAsString);
|
||||||
}
|
}
|
||||||
|
@ -198,7 +194,26 @@ namespace Nuclex.Support.Settings {
|
||||||
/// <param name="optionName">Name of the option that will be removed</param>
|
/// <param name="optionName">Name of the option that will be removed</param>
|
||||||
/// <returns>True if the option was found and removed</returns>
|
/// <returns>True if the option was found and removed</returns>
|
||||||
public bool Remove(string category, string optionName) {
|
public bool Remove(string category, string optionName) {
|
||||||
throw new NotImplementedException();
|
Category sourceCategory = getCategoryByName(category);
|
||||||
|
if(sourceCategory == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Option option;
|
||||||
|
if(!sourceCategory.OptionLookup.TryGetValue(optionName, out option)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
sourceCategory.Lines.RemoveAt(option.LineIndex);
|
||||||
|
sourceCategory.OptionLookup.Remove(optionName);
|
||||||
|
|
||||||
|
foreach(Option shiftedOption in sourceCategory.OptionLookup.Values) {
|
||||||
|
if(shiftedOption.LineIndex > option.LineIndex) {
|
||||||
|
--shiftedOption.LineIndex;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>Looks a category up by its name</summary>
|
/// <summary>Looks a category up by its name</summary>
|
||||||
|
@ -212,9 +227,7 @@ namespace Nuclex.Support.Settings {
|
||||||
if(string.IsNullOrEmpty(categoryName)) {
|
if(string.IsNullOrEmpty(categoryName)) {
|
||||||
category = this.RootCategory;
|
category = this.RootCategory;
|
||||||
} else if(!this.categoryLookup.TryGetValue(categoryName, out category)) {
|
} else if(!this.categoryLookup.TryGetValue(categoryName, out category)) {
|
||||||
throw new KeyNotFoundException(
|
return null;
|
||||||
"There is no category named '" + categoryName + "' in the configuration file"
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return category;
|
return category;
|
||||||
|
@ -281,7 +294,7 @@ namespace Nuclex.Support.Settings {
|
||||||
string line = option.OptionValue.Text;
|
string line = option.OptionValue.Text;
|
||||||
{
|
{
|
||||||
StringBuilder builder = new StringBuilder(
|
StringBuilder builder = new StringBuilder(
|
||||||
line.Length - option.OptionValue.Count + newValue.Length
|
line.Length - option.OptionValue.Count + newValueLength
|
||||||
);
|
);
|
||||||
|
|
||||||
// Stuff before the value
|
// Stuff before the value
|
||||||
|
|
Loading…
Reference in New Issue
Block a user