Fully implemented the windows registry-based settings store; added more unit tests for the registry settings store

git-svn-id: file:///srv/devel/repo-conversion/nusu@319 d2e56fa2-650e-0410-a79f-9358c0239efd
This commit is contained in:
Markus Ewald 2014-07-22 16:24:08 +00:00
parent 0d1051dd84
commit 58504f4a8a
2 changed files with 107 additions and 49 deletions

View File

@ -18,15 +18,15 @@ License along with this library
*/ */
#endregion #endregion
#if UNITTEST #if UNITTEST && WINDOWS
using System; using System;
using System.Collections.Generic;
using System.Globalization;
using Microsoft.Win32;
using NUnit.Framework; using NUnit.Framework;
using System.IO;
using Microsoft.Win32;
using System.Globalization;
using System.Collections.Generic;
namespace Nuclex.Support.Settings { namespace Nuclex.Support.Settings {
@ -179,6 +179,76 @@ namespace Nuclex.Support.Settings {
var optionInfos = new List<OptionInfo>(context.Store.EnumerateOptions(null)); var optionInfos = new List<OptionInfo>(context.Store.EnumerateOptions(null));
Assert.That(optionInfos.Count, Is.EqualTo(3)); Assert.That(optionInfos.Count, Is.EqualTo(3));
string[] actualNames = new string[] {
optionInfos[0].Name, optionInfos[1].Name, optionInfos[2].Name
};
Assert.That(actualNames, Is.EquivalentTo(names));
}
}
/// <summary>
/// Verifies that accessing an option that doesn't exist throws an exception
/// </summary>
[Test]
public void AccessingNonExistingOptionThrowsException() {
using(var context = new TestContext()) {
Assert.That(
() => context.Store.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() {
using(var context = new TestContext()) {
Assert.That(
() => context.Store.Get<string>("doesn't exist", "test"),
Throws.Exception.AssignableTo<KeyNotFoundException>()
);
}
}
/// <summary>
/// Verifies that values can be removed from a registry key
/// </summary>
[Test]
public void ValuesCanBeRemovedFromRoot() {
using(var context = new TestContext()) {
context.Store.Set(null, "nothing", "short-lived");
Assert.That(context.Store.Remove(null, "nothing"), Is.True);
Assert.That(context.Store.Remove(null, "nothing"), Is.False);
Assert.That(context.Store.EnumerateOptions(), Is.Empty);
}
}
/// <summary>
/// Verifies that values can be removed from the subkey of a registry key
/// </summary>
[Test]
public void ValuesCanBeRemovedFromCategory() {
using(var context = new TestContext()) {
context.Store.Set("limbo", "nothing", "short-lived");
Assert.That(context.Store.Remove("limbo", "nothing"), Is.True);
Assert.That(context.Store.Remove("limbo", "nothing"), Is.False);
Assert.That(context.Store.EnumerateOptions("limbo"), Is.Empty);
}
}
/// <summary>
/// Verifies that values can be removed from a non-existing subkey without
/// causing an error
/// </summary>
[Test]
public void RemovingValueFromNonExistingCategoryCanBeHandled() {
using(var context = new TestContext()) {
Assert.That(context.Store.Remove("empty", "nothing"), Is.False);
} }
} }

View File

@ -131,11 +131,11 @@ namespace Nuclex.Support.Settings {
if(string.IsNullOrEmpty(category)) { if(string.IsNullOrEmpty(category)) {
return tryGetValueFromKey(this.rootKey, optionName, out value); return tryGetValueFromKey(this.rootKey, optionName, out value);
} else { } else {
RegistryKey categoryKey = this.rootKey.OpenSubKey(category, this.writable); RegistryKey categoryKey = this.rootKey.OpenSubKey(category, this.writable);
if(categoryKey == null) { if(categoryKey == null) {
value = default(TValue); value = default(TValue);
return false; return false;
} }
using(categoryKey) { using(categoryKey) {
return tryGetValueFromKey(categoryKey, optionName, out value); return tryGetValueFromKey(categoryKey, optionName, out value);
} }
@ -151,16 +151,38 @@ namespace Nuclex.Support.Settings {
if(string.IsNullOrEmpty(category)) { if(string.IsNullOrEmpty(category)) {
setValue(this.rootKey, optionName, value); setValue(this.rootKey, optionName, value);
} else { } else {
RegistryKey categoryKey = this.rootKey.OpenSubKey(category, this.writable); RegistryKey categoryKey = this.rootKey.OpenSubKey(category, this.writable);
if(categoryKey == null) { if(categoryKey == null) {
categoryKey = this.rootKey.CreateSubKey(category); categoryKey = this.rootKey.CreateSubKey(category);
} }
using(categoryKey) { using(categoryKey) {
setValue(categoryKey, optionName, value); setValue(categoryKey, optionName, value);
} }
} }
} }
/// <summary>Removes the option with the specified name</summary>
/// <param name="category">Category the option is found in. Can be null.</param>
/// <param name="optionName">Name of the option that will be removed</param>
/// <returns>True if the option was found and removed</returns>
public bool Remove(string category, string optionName) {
if(string.IsNullOrEmpty(category)) {
object value = this.rootKey.GetValue(optionName);
this.rootKey.DeleteValue(optionName, throwOnMissingValue: false);
return (value != null);
} else {
RegistryKey categoryKey = this.rootKey.OpenSubKey(category, this.writable);
if(categoryKey == null) {
return false;
}
using(categoryKey) {
object value = categoryKey.GetValue(optionName);
categoryKey.DeleteValue(optionName, throwOnMissingValue: false);
return (value != null);
}
}
}
/// <summary>Writes a setting to the registry</summary> /// <summary>Writes a setting to the registry</summary>
/// <typeparam name="TValue"></typeparam> /// <typeparam name="TValue"></typeparam>
/// <param name="registryKey"></param> /// <param name="registryKey"></param>
@ -187,14 +209,6 @@ namespace Nuclex.Support.Settings {
} }
} }
/// <summary>Removes the option with the specified name</summary>
/// <param name="category">Category the option is found in. Can be null.</param>
/// <param name="optionName">Name of the option that will be removed</param>
/// <returns>True if the option was found and removed</returns>
public bool Remove(string category, string optionName) {
throw new NotImplementedException();
}
/// <summary>Tries to retrieve the value of a registry key if it exists</summary> /// <summary>Tries to retrieve the value of a registry key if it exists</summary>
/// <typeparam name="TValue">Type of value the registry key is expected to have</typeparam> /// <typeparam name="TValue">Type of value the registry key is expected to have</typeparam>
/// <param name="categoryKey">Registry key the value is stored under</param> /// <param name="categoryKey">Registry key the value is stored under</param>
@ -228,33 +242,7 @@ namespace Nuclex.Support.Settings {
case RegistryValueKind.QWord: { return typeof(long); } case RegistryValueKind.QWord: { return typeof(long); }
case RegistryValueKind.MultiString: { return typeof(string[]); } case RegistryValueKind.MultiString: { return typeof(string[]); }
case RegistryValueKind.ExpandString: case RegistryValueKind.ExpandString:
case RegistryValueKind.String: { case RegistryValueKind.String: { return typeof(string); }
string value = (string)categoryKey.GetValue(optionName);
if(value.Length == 0) {
return typeof(string);
}
// If there are at least two characters, it may be an integer with
// a sign in front of it
if(value.Length >= 2) {
int index = 0;
if(ParserHelper.SkipInteger(value, ref index)) {
if(index >= value.Length) {
return typeof(int);
}
if(value[index] == '.') {
return typeof(float);
}
}
} else { // If it's just a single character, it may be a number
if(char.IsNumber(value, 0)) {
return typeof(int);
}
}
return typeof(string);
}
case RegistryValueKind.Unknown: case RegistryValueKind.Unknown:
case RegistryValueKind.None: case RegistryValueKind.None:
default: { return typeof(string); } default: { return typeof(string); }