public async Task TestFileChangeNotification() { // Prevent multiple tests accessing the app config. using (await _configFileLock.LockAsync()) { // Get the test config file path. string filePath = TestConfigurationSection.Active.FilePath; Trace.WriteLine($"Configuration file: '{filePath}'"); Assert.IsFalse(string.IsNullOrWhiteSpace(filePath)); Assert.IsTrue(File.Exists(filePath)); // Load config file directly (we load into string so we can recreate at end of the test. string original = File.ReadAllText(filePath); // Parse it and modify XDocument document = XDocument.Parse(original); XElement testElement = document.Element("test"); Assert.IsNotNull(testElement); XElement customElement = testElement.Element("custom"); Tuple <XObject, XObject> difference = customElement.DeepEquals( TestConfigurationSection.Active.Custom, XObjectComparisonOptions.Semantic); Assert.IsNull(difference, $"{difference?.Item1} : {difference?.Item2}"); // Modify string str1Random = Tester.RandomString() ?? string.Empty; string str2Random = Tester.RandomString() ?? string.Empty; // Update the custom node. customElement.RemoveAll(); XNamespace ns = customElement.Name.Namespace; customElement.Add( new XElement(ns + "string1", str1Random), new XElement(ns + "string2", str2Random)); // Save file document.Save(filePath); // Check for changes ConfigurationSection <TestConfigurationSection> .ConfigurationChangedEventArgs change = await WaitForChanges(new CancellationTokenSource(TimeSpan.FromSeconds(1)).Token); Assert.IsNotNull(change); Assert.IsTrue(change.Contains("<test>")); Assert.IsTrue(change.WasChanged("<test><custom>")); XElement activeCustom = TestConfigurationSection.Active.Custom; Assert.IsNotNull(activeCustom); AssertString("custom", activeCustom.Name.LocalName, options: TextOptions.NormalizeLineEndings); AssertString( str1Random, activeCustom.Element("string1")?.Value, options: TextOptions.NormalizeLineEndings); AssertString( str2Random, activeCustom.Element("string2")?.Value, options: TextOptions.NormalizeLineEndings); // Revert File.WriteAllText(filePath, original); change = await WaitForChanges(new CancellationTokenSource(TimeSpan.FromSeconds(1)).Token); Assert.IsNotNull(change); Assert.IsTrue(change.Contains("<test>")); Assert.IsTrue(change.WasChanged("<test><custom>")); } }
public async Task TestChangeNotificationAndSave() { // Prevent multiple tests accessing the app config. using (await _configFileLock.LockAsync()) { Assert.IsTrue(TestConfigurationSection.Active.HasFile); XElement custom = TestConfigurationSection.Active.Custom; Assert.IsNotNull(custom); XElement custom2 = TestConfigurationSection.Active.Custom; Assert.IsNotNull(custom2); Assert.AreNotSame( custom, custom2, "Get element should retrieve fresh XElement objects to prevent corruption."); // Get original values. string str1 = TestConfigurationSection.Active.String; string str2 = TestConfigurationSection.Active.String2; string cStr = custom.ToString(SaveOptions.DisableFormatting); Assert.AreEqual(cStr, custom2.ToString(SaveOptions.DisableFormatting)); Assert.AreEqual("custom", custom.Name.LocalName); // Generate random values string str1Random = Tester.RandomString() ?? string.Empty; string str2Random = Tester.RandomString() ?? string.Empty; // Update the custom node. XNamespace ns = custom.Name.Namespace; TestConfigurationSection.Active.Custom = new XElement( ns + "custom", new XElement(ns + "string1", str1Random), new XElement(ns + "string2", str2Random)); TestConfigurationSection.Active.String = str1Random; TestConfigurationSection.Active.String2 = str2Random; TestConfigurationSection.Active.Save(); // Check for changes ConfigurationSection <TestConfigurationSection> .ConfigurationChangedEventArgs change = await WaitForChanges(new CancellationTokenSource(TimeSpan.FromSeconds(1)).Token); Assert.IsNotNull(change); Assert.IsTrue(change.WasChanged("<test>")); Assert.IsTrue(change.Contains("<test><custom>")); Assert.IsTrue(change.Contains("<test>.string")); Assert.IsTrue(change.Contains("<test>.string2")); custom = TestConfigurationSection.Active.Custom; Assert.IsNotNull(custom); Assert.AreEqual("custom", custom.Name.LocalName); AssertString(str1Random, custom.Element("string1")?.Value, options: TextOptions.NormalizeLineEndings); AssertString(str2Random, custom.Element("string2")?.Value, options: TextOptions.NormalizeLineEndings); AssertString( str1Random, TestConfigurationSection.Active.String, options: TextOptions.NormalizeLineEndings); AssertString( str2Random, TestConfigurationSection.Active.String2, options: TextOptions.NormalizeLineEndings); // Revert // Update the custom node. TestConfigurationSection.Active.Custom = XElement.Parse(cStr, LoadOptions.PreserveWhitespace); TestConfigurationSection.Active.String = str1; TestConfigurationSection.Active.String2 = str2; TestConfigurationSection.Active.Save(); // Check for changeschange = change = await WaitForChanges(new CancellationTokenSource(TimeSpan.FromSeconds(1)).Token); Assert.IsNotNull(change); Assert.IsTrue(change.WasChanged("<test>")); Assert.IsTrue(change.Contains("<test><custom>")); Assert.IsTrue(change.Contains("<test>.string")); Assert.IsTrue(change.Contains("<test>.string2")); custom = TestConfigurationSection.Active.Custom; Assert.IsNotNull(change); AssertString("custom", custom.Name.LocalName, options: TextOptions.NormalizeLineEndings); AssertString( cStr, custom.ToString(SaveOptions.DisableFormatting), options: TextOptions.NormalizeLineEndings); AssertString(str1, TestConfigurationSection.Active.String, options: TextOptions.NormalizeLineEndings); AssertString(str2, TestConfigurationSection.Active.String2, options: TextOptions.NormalizeLineEndings); } }