4d0e369b414a45eebbdad975446df6e8e7c584f7
[mono.git] / mcs / class / System.Configuration / Test / System.Configuration / ConfigurationSaveTest.cs
1 //
2 // ConfigurationSaveTest.cs
3 //
4 // Author:
5 //       Martin Baulig <martin.baulig@xamarin.com>
6 //
7 // Copyright (c) 2012 Xamarin Inc. (http://www.xamarin.com)
8 //
9 // Permission is hereby granted, free of charge, to any person obtaining a copy
10 // of this software and associated documentation files (the "Software"), to deal
11 // in the Software without restriction, including without limitation the rights
12 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13 // copies of the Software, and to permit persons to whom the Software is
14 // furnished to do so, subject to the following conditions:
15 //
16 // The above copyright notice and this permission notice shall be included in
17 // all copies or substantial portions of the Software.
18 //
19 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
25 // THE SOFTWARE.
26 using System;
27 using System.IO;
28 using System.Xml;
29 using System.Xml.Schema;
30 using System.Xml.XPath;
31 using System.Text;
32 using System.Reflection;
33 using System.Globalization;
34 using System.Configuration;
35 using System.Collections.Generic;
36 using SysConfig = System.Configuration.Configuration;
37
38 using NUnit.Framework;
39 using NUnit.Framework.Constraints;
40 using NUnit.Framework.SyntaxHelpers;
41
42 namespace MonoTests.System.Configuration {
43         using Util;
44
45         [TestFixture]
46         public class ConfigurationSaveTest {
47
48                 #region Test Framework
49
50                 public abstract class ConfigProvider {
51                         public void Create (string filename)
52                         {
53                                 if (File.Exists (filename))
54                                         File.Delete (filename);
55                                 
56                                 var settings = new XmlWriterSettings ();
57                                 settings.Indent = true;
58                                 
59                                 using (var writer = XmlTextWriter.Create (filename, settings)) {
60                                         writer.WriteStartElement ("configuration");
61                                         WriteXml (writer);
62                                         writer.WriteEndElement ();
63                                 }
64                         }
65
66                         public abstract UserLevel Level {
67                                 get;
68                         }
69                         
70                         public enum UserLevel {
71                                 MachineAndExe,
72                                 RoamingAndExe
73                         }
74                         
75                         public virtual SysConfig OpenConfig (string parentFile, string configFile)
76                         {
77                                 ConfigurationUserLevel level;
78                                 var map = new ExeConfigurationFileMap ();
79                                 switch (Level) {
80                                 case UserLevel.MachineAndExe:
81                                         map.ExeConfigFilename = configFile;
82                                         map.MachineConfigFilename = parentFile;
83                                         level = ConfigurationUserLevel.None;
84                                         break;
85                                 case UserLevel.RoamingAndExe:
86                                         map.RoamingUserConfigFilename = configFile;
87                                         map.ExeConfigFilename = parentFile;
88                                         level = ConfigurationUserLevel.PerUserRoaming;
89                                         break;
90                                 default:
91                                         throw new InvalidOperationException ();
92                                 }
93                                 
94                                 return ConfigurationManager.OpenMappedExeConfiguration (map, level);
95                         }
96                         
97                         protected abstract void WriteXml (XmlWriter writer);
98                 }
99
100                 public abstract class MachineConfigProvider : ConfigProvider {
101                         protected override void WriteXml (XmlWriter writer)
102                         {
103                                 writer.WriteStartElement ("configSections");
104                                 WriteSections (writer);
105                                 writer.WriteEndElement ();
106                                 WriteValues (writer);
107                         }
108
109                         public override UserLevel Level {
110                                 get { return UserLevel.MachineAndExe; }
111                         }
112                         
113                         protected abstract void WriteSections (XmlWriter writer);
114
115                         protected abstract void WriteValues (XmlWriter writer);
116                 }
117
118                 class DefaultMachineConfig : MachineConfigProvider {
119                         protected override void WriteSections (XmlWriter writer)
120                         {
121                                 writer.WriteStartElement ("section");
122                                 writer.WriteAttributeString ("name", "my");
123                                 writer.WriteAttributeString ("type", typeof (MySection).AssemblyQualifiedName);
124                                 writer.WriteAttributeString ("allowLocation", "true");
125                                 writer.WriteAttributeString ("allowDefinition", "Everywhere");
126                                 writer.WriteAttributeString ("allowExeDefinition", "MachineToRoamingUser");
127                                 writer.WriteAttributeString ("restartOnExternalChanges", "true");
128                                 writer.WriteAttributeString ("requirePermission", "true");
129                                 writer.WriteEndElement ();
130                         }
131
132                         internal static void WriteConfigSections (XmlWriter writer)
133                         {
134                                 var provider = new DefaultMachineConfig ();
135                                 writer.WriteStartElement ("configSections");
136                                 provider.WriteSections (writer);
137                                 writer.WriteEndElement ();
138                         }
139
140                         protected override void WriteValues (XmlWriter writer)
141                         {
142                                 writer.WriteStartElement ("my");
143                                 writer.WriteEndElement ();
144                         }
145                 }
146
147                 class DefaultMachineConfig2 : MachineConfigProvider {
148                         protected override void WriteSections (XmlWriter writer)
149                         {
150                                 writer.WriteStartElement ("section");
151                                 writer.WriteAttributeString ("name", "my2");
152                                 writer.WriteAttributeString ("type", typeof (MySection2).AssemblyQualifiedName);
153                                 writer.WriteAttributeString ("allowLocation", "true");
154                                 writer.WriteAttributeString ("allowDefinition", "Everywhere");
155                                 writer.WriteAttributeString ("allowExeDefinition", "MachineToRoamingUser");
156                                 writer.WriteAttributeString ("restartOnExternalChanges", "true");
157                                 writer.WriteAttributeString ("requirePermission", "true");
158                                 writer.WriteEndElement ();
159                         }
160                         
161                         internal static void WriteConfigSections (XmlWriter writer)
162                         {
163                                 var provider = new DefaultMachineConfig2 ();
164                                 writer.WriteStartElement ("configSections");
165                                 provider.WriteSections (writer);
166                                 writer.WriteEndElement ();
167                         }
168                         
169                         protected override void WriteValues (XmlWriter writer)
170                         {
171                         }
172                 }
173
174                 abstract class ParentProvider : ConfigProvider {
175                         protected override void WriteXml (XmlWriter writer)
176                         {
177                                 DefaultMachineConfig.WriteConfigSections (writer);
178                                 writer.WriteStartElement ("my");
179                                 writer.WriteStartElement ("test");
180                                 writer.WriteAttributeString ("Hello", "29");
181                                 writer.WriteEndElement ();
182                                 writer.WriteEndElement ();
183                         }
184                 }
185
186                 class RoamingAndExe : ParentProvider {
187                         public override UserLevel Level {
188                                 get { return UserLevel.RoamingAndExe; }
189                         }
190                 }
191
192                 public delegate void TestFunction (SysConfig config, TestLabel label);
193                 public delegate void XmlCheckFunction (XPathNavigator nav, TestLabel label);
194
195                 public static void Run (string name, TestFunction func)
196                 {
197                         var label = new TestLabel (name);
198
199                         TestUtil.RunWithTempFile (filename => {
200                                 var fileMap = new ExeConfigurationFileMap ();
201                                 fileMap.ExeConfigFilename = filename;
202                                 var config = ConfigurationManager.OpenMappedExeConfiguration (
203                                         fileMap, ConfigurationUserLevel.None);
204                                 
205                                 func (config, label);
206                         });
207                 }
208
209                 public static void Run<TConfig> (string name, TestFunction func)
210                         where TConfig : ConfigProvider, new ()
211                 {
212                         Run<TConfig> (new TestLabel (name), func, null);
213                 }
214
215                 public static void Run<TConfig> (TestLabel label, TestFunction func)
216                         where TConfig : ConfigProvider, new ()
217                 {
218                         Run<TConfig> (label, func, null);
219                 }
220
221                 public static void Run<TConfig> (
222                         string name, TestFunction func, XmlCheckFunction check)
223                         where TConfig : ConfigProvider, new ()
224                 {
225                         Run<TConfig> (new TestLabel (name), func, check);
226                 }
227
228                 public static void Run<TConfig> (
229                         TestLabel label, TestFunction func, XmlCheckFunction check)
230                         where TConfig : ConfigProvider, new ()
231                 {
232                         TestUtil.RunWithTempFiles ((parent,filename) => {
233                                 var provider = new TConfig ();
234                                 provider.Create (parent);
235
236                                 Assert.That (File.Exists (filename), Is.False);
237
238                                 var config = provider.OpenConfig (parent, filename);
239
240                                 Assert.That (File.Exists (filename), Is.False);
241
242                                 try {
243                                         label.EnterScope ("config");
244                                         func (config, label);
245                                 } finally {
246                                         label.LeaveScope ();
247                                 }
248
249                                 if (check == null)
250                                         return;
251
252                                 var xml = new XmlDocument ();
253                                 xml.Load (filename);
254
255                                 var nav = xml.CreateNavigator ().SelectSingleNode ("/configuration");
256                                 try {
257                                         label.EnterScope ("xml");
258                                         check (nav, label);
259                                 } finally {
260                                         label.LeaveScope ();
261                                 }
262                         });
263                 }
264
265                 #endregion
266
267                 #region Assertion Helpers
268
269                 static void AssertNotModified (MySection my, TestLabel label)
270                 {
271                         label.EnterScope ("modified");
272                         Assert.That (my, Is.Not.Null, label.Get ());
273                         Assert.That (my.IsModified, Is.False, label.Get ());
274                         Assert.That (my.List, Is.Not.Null, label.Get ());
275                         Assert.That (my.List.Collection.Count, Is.EqualTo (0), label.Get ());
276                         Assert.That (my.List.IsModified, Is.False, label.Get ());
277                         label.LeaveScope ();
278                 }
279
280                 static void AssertListElement (XPathNavigator nav, TestLabel label)
281                 {
282                         Assert.That (nav.HasChildren, Is.True, label.Get ());
283                         var iter = nav.SelectChildren (XPathNodeType.Element);
284                         
285                         Assert.That (iter.Count, Is.EqualTo (1), label.Get ());
286                         Assert.That (iter.MoveNext (), Is.True, label.Get ());
287                         
288                         var my = iter.Current;
289                         label.EnterScope ("my");
290                         Assert.That (my.Name, Is.EqualTo ("my"), label.Get ());
291                         Assert.That (my.HasAttributes, Is.False, label.Get ());
292                         
293                         label.EnterScope ("children");
294                         Assert.That (my.HasChildren, Is.True, label.Get ());
295                         var iter2 = my.SelectChildren (XPathNodeType.Element);
296                         Assert.That (iter2.Count, Is.EqualTo (1), label.Get ());
297                         Assert.That (iter2.MoveNext (), Is.True, label.Get ());
298                         
299                         var test = iter2.Current;
300                         label.EnterScope ("test");
301                         Assert.That (test.Name, Is.EqualTo ("test"), label.Get ());
302                         Assert.That (test.HasChildren, Is.False, label.Get ());
303                         Assert.That (test.HasAttributes, Is.True, label.Get ());
304                         
305                         var attr = test.GetAttribute ("Hello", string.Empty);
306                         Assert.That (attr, Is.EqualTo ("29"), label.Get ());
307                         label.LeaveScope ();
308                         label.LeaveScope ();
309                         label.LeaveScope ();
310                 }
311                 
312                 #endregion
313
314                 #region Tests
315
316                 [Test]
317                 public void DefaultValues ()
318                 {
319                         Run<DefaultMachineConfig> ("DefaultValues", (config,label) => {
320                                 var my = config.Sections ["my"] as MySection;
321
322                                 AssertNotModified (my, label);
323
324                                 label.EnterScope ("file");
325                                 Assert.That (File.Exists (config.FilePath), Is.False, label.Get ());
326
327                                 config.Save (ConfigurationSaveMode.Minimal);
328                                 Assert.That (File.Exists (config.FilePath), Is.False, label.Get ());
329                                 label.LeaveScope ();
330                         });
331                 }
332
333                 [Test]
334                 public void AddDefaultListElement ()
335                 {
336                         Run<DefaultMachineConfig> ("AddDefaultListElement", (config,label) => {
337                                 var my = config.Sections ["my"] as MySection;
338                                 
339                                 AssertNotModified (my, label);
340                                 
341                                 label.EnterScope ("add");
342                                 var element = my.List.Collection.AddElement ();
343                                 Assert.That (my.IsModified, Is.True, label.Get ());
344                                 Assert.That (my.List.IsModified, Is.True, label.Get ());
345                                 Assert.That (my.List.Collection.IsModified, Is.True, label.Get ());
346                                 Assert.That (element.IsModified, Is.False, label.Get ());
347                                 label.LeaveScope ();
348                                 
349                                 config.Save (ConfigurationSaveMode.Minimal);
350                                 Assert.That (File.Exists (config.FilePath), Is.False, label.Get ());
351                         });
352                 }
353                 
354                 [Test]
355                 public void AddDefaultListElement2 ()
356                 {
357                         Run<DefaultMachineConfig> ("AddDefaultListElement2", (config,label) => {
358                                 var my = config.Sections ["my"] as MySection;
359                                 
360                                 AssertNotModified (my, label);
361                                 
362                                 label.EnterScope ("add");
363                                 var element = my.List.Collection.AddElement ();
364                                 Assert.That (my.IsModified, Is.True, label.Get ());
365                                 Assert.That (my.List.IsModified, Is.True, label.Get ());
366                                 Assert.That (my.List.Collection.IsModified, Is.True, label.Get ());
367                                 Assert.That (element.IsModified, Is.False, label.Get ());
368                                 label.LeaveScope ();
369                                 
370                                 config.Save (ConfigurationSaveMode.Modified);
371                                 Assert.That (File.Exists (config.FilePath), Is.True, label.Get ());
372                         }, (nav,label) => {
373                                 Assert.That (nav.HasChildren, Is.True, label.Get ());
374                                 var iter = nav.SelectChildren (XPathNodeType.Element);
375                                 
376                                 Assert.That (iter.Count, Is.EqualTo (1), label.Get ());
377                                 Assert.That (iter.MoveNext (), Is.True, label.Get ());
378                                 
379                                 var my = iter.Current;
380                                 label.EnterScope ("my");
381                                 Assert.That (my.Name, Is.EqualTo ("my"), label.Get ());
382                                 Assert.That (my.HasAttributes, Is.False, label.Get ());
383                                 Assert.That (my.HasChildren, Is.False, label.Get ());
384                                 label.LeaveScope ();
385                         });
386                 }
387
388                 [Test]
389                 public void AddDefaultListElement3 ()
390                 {
391                         Run<DefaultMachineConfig> ("AddDefaultListElement3", (config,label) => {
392                                 var my = config.Sections ["my"] as MySection;
393
394                                 AssertNotModified (my, label);
395
396                                 label.EnterScope ("add");
397                                 var element = my.List.Collection.AddElement ();
398                                 Assert.That (my.IsModified, Is.True, label.Get ());
399                                 Assert.That (my.List.IsModified, Is.True, label.Get ());
400                                 Assert.That (my.List.Collection.IsModified, Is.True, label.Get ());
401                                 Assert.That (element.IsModified, Is.False, label.Get ());
402                                 label.LeaveScope ();
403                                 
404                                 config.Save (ConfigurationSaveMode.Full);
405                                 Assert.That (File.Exists (config.FilePath), Is.True, label.Get ());
406                         }, (nav,label) => {
407                                 Assert.That (nav.HasChildren, Is.True, label.Get ());
408                                 var iter = nav.SelectChildren (XPathNodeType.Element);
409                                 
410                                 Assert.That (iter.Count, Is.EqualTo (1), label.Get ());
411                                 Assert.That (iter.MoveNext (), Is.True, label.Get ());
412                                 
413                                 var my = iter.Current;
414                                 label.EnterScope ("my");
415                                 Assert.That (my.Name, Is.EqualTo ("my"), label.Get ());
416                                 Assert.That (my.HasAttributes, Is.False, label.Get ());
417                                 
418                                 label.EnterScope ("children");
419                                 Assert.That (my.HasChildren, Is.True, label.Get ());
420                                 var iter2 = my.SelectChildren (XPathNodeType.Element);
421                                 Assert.That (iter2.Count, Is.EqualTo (2), label.Get ());
422
423                                 label.EnterScope ("list");
424                                 var iter3 = my.Select ("list/*");
425                                 Assert.That (iter3.Count, Is.EqualTo (1), label.Get ());
426                                 Assert.That (iter3.MoveNext (), Is.True, label.Get ());
427                                 var collection = iter3.Current;
428                                 Assert.That (collection.Name, Is.EqualTo ("collection"), label.Get ());
429                                 Assert.That (collection.HasChildren, Is.False, label.Get ());
430                                 Assert.That (collection.HasAttributes, Is.True, label.Get ());
431                                 var hello = collection.GetAttribute ("Hello", string.Empty);
432                                 Assert.That (hello, Is.EqualTo ("8"), label.Get ());
433                                 var world = collection.GetAttribute ("World", string.Empty);
434                                 Assert.That (world, Is.EqualTo ("0"), label.Get ());
435                                 label.LeaveScope ();
436
437                                 label.EnterScope ("test");
438                                 var iter4 = my.Select ("test");
439                                 Assert.That (iter4.Count, Is.EqualTo (1), label.Get ());
440                                 Assert.That (iter4.MoveNext (), Is.True, label.Get ());
441                                 var test = iter4.Current;
442                                 Assert.That (test.Name, Is.EqualTo ("test"), label.Get ());
443                                 Assert.That (test.HasChildren, Is.False, label.Get ());
444                                 Assert.That (test.HasAttributes, Is.True, label.Get ());
445                                 
446                                 var hello2 = test.GetAttribute ("Hello", string.Empty);
447                                 Assert.That (hello2, Is.EqualTo ("8"), label.Get ());
448                                 var world2 = test.GetAttribute ("World", string.Empty);
449                                 Assert.That (world2, Is.EqualTo ("0"), label.Get ());
450                                 label.LeaveScope ();
451                                 label.LeaveScope ();
452                                 label.LeaveScope ();
453                         });
454                 }
455
456                 [Test]
457                 public void AddListElement ()
458                 {
459                         Run<DefaultMachineConfig> ("AddListElement", (config,label) => {
460                                 var my = config.Sections ["my"] as MySection;
461                                 
462                                 AssertNotModified (my, label);
463                                 
464                                 my.Test.Hello = 29;
465                                 
466                                 label.EnterScope ("file");
467                                 Assert.That (File.Exists (config.FilePath), Is.False, label.Get ());
468                                 
469                                 config.Save (ConfigurationSaveMode.Minimal);
470                                 Assert.That (File.Exists (config.FilePath), Is.True, label.Get ());
471                                 label.LeaveScope ();
472                         }, (nav,label) => {
473                                 AssertListElement (nav, label);
474                         });
475                 }
476                 
477                 [Test]
478                 public void NotModifiedAfterSave ()
479                 {
480                         Run<DefaultMachineConfig> ("NotModifiedAfterSave", (config,label) => {
481                                 var my = config.Sections ["my"] as MySection;
482
483                                 AssertNotModified (my, label);
484
485                                 label.EnterScope ("add");
486                                 var element = my.List.Collection.AddElement ();
487                                 Assert.That (my.IsModified, Is.True, label.Get ());
488                                 Assert.That (my.List.IsModified, Is.True, label.Get ());
489                                 Assert.That (my.List.Collection.IsModified, Is.True, label.Get ());
490                                 Assert.That (element.IsModified, Is.False, label.Get ());
491                                 label.LeaveScope ();
492
493                                 label.EnterScope ("1st-save");
494                                 config.Save (ConfigurationSaveMode.Minimal);
495                                 Assert.That (File.Exists (config.FilePath), Is.False, label.Get ());
496                                 config.Save (ConfigurationSaveMode.Modified);
497                                 Assert.That (File.Exists (config.FilePath), Is.False, label.Get ());
498                                 label.LeaveScope ();
499
500                                 label.EnterScope ("modify");
501                                 element.Hello = 12;
502                                 Assert.That (my.IsModified, Is.True, label.Get ());
503                                 Assert.That (my.List.IsModified, Is.True, label.Get ());
504                                 Assert.That (my.List.Collection.IsModified, Is.True, label.Get ());
505                                 Assert.That (element.IsModified, Is.True, label.Get ());
506                                 label.LeaveScope ();
507
508                                 label.EnterScope ("2nd-save");
509                                 config.Save (ConfigurationSaveMode.Modified);
510                                 Assert.That (File.Exists (config.FilePath), Is.True, label.Get ());
511
512                                 Assert.That (my.IsModified, Is.False, label.Get ());
513                                 Assert.That (my.List.IsModified, Is.False, label.Get ());
514                                 Assert.That (my.List.Collection.IsModified, Is.False, label.Get ());
515                                 Assert.That (element.IsModified, Is.False, label.Get ());
516                                 label.LeaveScope (); // 2nd-save
517                         });
518                 }
519
520                 [Test]
521                 public void AddSection ()
522                 {
523                         Run ("AddSection", (config,label) => {
524                                 Assert.That (config.Sections ["my"], Is.Null, label.Get ());
525
526                                 var my = new MySection ();
527                                 config.Sections.Add ("my2", my);
528                                 config.Save (ConfigurationSaveMode.Full);
529
530                                 Assert.That (File.Exists (config.FilePath), Is.True, label.Get ());
531                         });
532                 }
533
534                 [Test]
535                 public void AddElement ()
536                 {
537                         Run<DefaultMachineConfig> ("AddElement", (config,label) => {
538                                 var my = config.Sections ["my"] as MySection;
539
540                                 AssertNotModified (my, label);
541
542                                 var element = my.List.DefaultCollection.AddElement ();
543                                 element.Hello = 12;
544
545                                 config.Save (ConfigurationSaveMode.Modified);
546
547                                 label.EnterScope ("file");
548                                 Assert.That (File.Exists (config.FilePath), Is.True, "#c2");
549                                 label.LeaveScope ();
550                         }, (nav,label) => {
551                                 Assert.That (nav.HasChildren, Is.True, label.Get ());
552                                 var iter = nav.SelectChildren (XPathNodeType.Element);
553                                 
554                                 Assert.That (iter.Count, Is.EqualTo (1), label.Get ());
555                                 Assert.That (iter.MoveNext (), Is.True, label.Get ());
556                                 
557                                 var my = iter.Current;
558                                 label.EnterScope ("my");
559                                 Assert.That (my.Name, Is.EqualTo ("my"), label.Get ());
560                                 Assert.That (my.HasAttributes, Is.False, label.Get ());
561                                 Assert.That (my.HasChildren, Is.True, label.Get ());
562
563                                 label.EnterScope ("children");
564                                 var iter2 = my.SelectChildren (XPathNodeType.Element);
565                                 Assert.That (iter2.Count, Is.EqualTo (1), label.Get ());
566                                 Assert.That (iter2.MoveNext (), Is.True, label.Get ());
567
568                                 var list = iter2.Current;
569                                 label.EnterScope ("list");
570                                 Assert.That (list.Name, Is.EqualTo ("list"), label.Get ());
571                                 Assert.That (list.HasChildren, Is.False, label.Get ());
572                                 Assert.That (list.HasAttributes, Is.True, label.Get ());
573
574                                 var attr = list.GetAttribute ("Hello", string.Empty);
575                                 Assert.That (attr, Is.EqualTo ("12"), label.Get ());
576                                 label.LeaveScope ();
577                                 label.LeaveScope ();
578                                 label.LeaveScope ();
579                         });
580                 }
581
582                 [Test]
583                 public void ModifyListElement ()
584                 {
585                         Run<RoamingAndExe> ("ModifyListElement", (config,label) => {
586                                 var my = config.Sections ["my"] as MySection;
587
588                                 AssertNotModified (my, label);
589                                 
590                                 my.Test.Hello = 29;
591
592                                 label.EnterScope ("file");
593                                 Assert.That (File.Exists (config.FilePath), Is.False, label.Get ());
594                                 
595                                 config.Save (ConfigurationSaveMode.Minimal);
596                                 Assert.That (File.Exists (config.FilePath), Is.False, label.Get ());
597                                 label.LeaveScope ();
598                         });
599                 }
600
601                 [Test]
602                 public void ModifyListElement2 ()
603                 {
604                         Run<RoamingAndExe> ("ModifyListElement2", (config,label) => {
605                                 var my = config.Sections ["my"] as MySection;
606
607                                 AssertNotModified (my, label);
608                                 
609                                 my.Test.Hello = 29;
610                                 
611                                 label.EnterScope ("file");
612                                 Assert.That (File.Exists (config.FilePath), Is.False, label.Get ());
613                                 
614                                 config.Save (ConfigurationSaveMode.Modified);
615                                 Assert.That (File.Exists (config.FilePath), Is.True, label.Get ());
616                                 label.LeaveScope ();
617                         }, (nav,label) => {
618                                 AssertListElement (nav, label);
619                         });
620                 }
621
622                 [Test]
623                 public void TestElementWithCollection ()
624                 {
625                         Run<DefaultMachineConfig2> ("TestElementWithCollection", (config,label) => {
626                                 label.EnterScope ("section");
627                                 var my2 = config.Sections ["my2"] as MySection2;
628                                 Assert.That (my2, Is.Not.Null, label.Get ());
629
630                                 Assert.That (my2.Test, Is.Not.Null, label.Get ());
631                                 Assert.That (my2.Test.DefaultCollection, Is.Not.Null, label.Get ());
632                                 Assert.That (my2.Test.DefaultCollection.Count, Is.EqualTo (0), label.Get ());
633                                 label.LeaveScope ();
634
635                                 my2.Test.DefaultCollection.AddElement ();
636
637                                 my2.Element.Hello = 29;
638
639                                 label.EnterScope ("file");
640                                 Assert.That (File.Exists (config.FilePath), Is.False, label.Get ());
641                                 
642                                 config.Save (ConfigurationSaveMode.Minimal);
643                                 Assert.That (File.Exists (config.FilePath), Is.True, label.Get ());
644                                 label.LeaveScope ();
645                         }, (nav,label) => {
646                                 Console.WriteLine (nav.OuterXml);
647                                 Assert.That (nav.HasChildren, Is.True, label.Get ());
648                                 var iter = nav.SelectChildren (XPathNodeType.Element);
649                                 
650                                 Assert.That (iter.Count, Is.EqualTo (1), label.Get ());
651                                 Assert.That (iter.MoveNext (), Is.True, label.Get ());
652                                 
653                                 var my = iter.Current;
654                                 label.EnterScope ("my2");
655                                 Assert.That (my.Name, Is.EqualTo ("my2"), label.Get ());
656                                 Assert.That (my.HasAttributes, Is.False, label.Get ());
657                                 Assert.That (my.HasChildren, Is.True, label.Get ());
658                                 
659                                 label.EnterScope ("children");
660                                 var iter2 = my.SelectChildren (XPathNodeType.Element);
661                                 Assert.That (iter2.Count, Is.EqualTo (1), label.Get ());
662                                 Assert.That (iter2.MoveNext (), Is.True, label.Get ());
663
664                                 var element = iter2.Current;
665                                 label.EnterScope ("element");
666                                 Assert.That (element.Name, Is.EqualTo ("element"), label.Get ());
667                                 Assert.That (element.HasChildren, Is.False, label.Get ());
668                                 Assert.That (element.HasAttributes, Is.True, label.Get ());
669                                 
670                                 var attr = element.GetAttribute ("Hello", string.Empty);
671                                 Assert.That (attr, Is.EqualTo ("29"), label.Get ());
672                                 label.LeaveScope ();
673                                 label.LeaveScope ();
674                                 label.LeaveScope ();
675                         });
676                 }
677
678                 [Test]
679                 public void TestElementWithCollection2 ()
680                 {
681                         Run<DefaultMachineConfig2> ("TestElementWithCollection2", (config,label) => {
682                                 label.EnterScope ("section");
683                                 var my2 = config.Sections ["my2"] as MySection2;
684                                 Assert.That (my2, Is.Not.Null, label.Get ());
685                                 
686                                 Assert.That (my2.Test, Is.Not.Null, label.Get ());
687                                 Assert.That (my2.Test.DefaultCollection, Is.Not.Null, label.Get ());
688                                 Assert.That (my2.Test.DefaultCollection.Count, Is.EqualTo (0), label.Get ());
689                                 label.LeaveScope ();
690                                 
691                                 var element = my2.Test.DefaultCollection.AddElement ();
692                                 var element2 = element.Test.DefaultCollection.AddElement ();
693                                 element2.Hello = 1;
694
695                                 label.EnterScope ("file");
696                                 Assert.That (File.Exists (config.FilePath), Is.False, label.Get ());
697                                 
698                                 config.Save (ConfigurationSaveMode.Minimal);
699                                 Assert.That (File.Exists (config.FilePath), Is.True, label.Get ());
700                                 label.LeaveScope ();
701                         }, (nav,label) => {
702                                 Console.WriteLine (nav.OuterXml);
703                                 Assert.That (nav.HasChildren, Is.True, label.Get ());
704                                 var iter = nav.SelectChildren (XPathNodeType.Element);
705                                 
706                                 Assert.That (iter.Count, Is.EqualTo (1), label.Get ());
707                                 Assert.That (iter.MoveNext (), Is.True, label.Get ());
708                                 
709                                 var my = iter.Current;
710                                 label.EnterScope ("my2");
711                                 Assert.That (my.Name, Is.EqualTo ("my2"), label.Get ());
712                                 Assert.That (my.HasAttributes, Is.False, label.Get ());
713                                 Assert.That (my.HasChildren, Is.True, label.Get ());
714                                 
715                                 label.EnterScope ("children");
716                                 var iter2 = my.SelectChildren (XPathNodeType.Element);
717                                 Assert.That (iter2.Count, Is.EqualTo (1), label.Get ());
718                                 Assert.That (iter2.MoveNext (), Is.True, label.Get ());
719                                 
720                                 var collection = iter2.Current;
721                                 label.EnterScope ("collection");
722                                 Assert.That (collection.Name, Is.EqualTo ("collection"), label.Get ());
723                                 Assert.That (collection.HasChildren, Is.True, label.Get ());
724                                 Assert.That (collection.HasAttributes, Is.False, label.Get ());
725
726                                 label.EnterScope ("children");
727                                 var iter3 = collection.SelectChildren (XPathNodeType.Element);
728                                 Assert.That (iter3.Count, Is.EqualTo (1), label.Get ());
729                                 Assert.That (iter3.MoveNext (), Is.True, label.Get ());
730
731                                 var element = iter3.Current;
732                                 label.EnterScope ("element");
733                                 Assert.That (element.Name, Is.EqualTo ("test"), label.Get ());
734                                 Assert.That (element.HasChildren, Is.False, label.Get ());
735                                 Assert.That (element.HasAttributes, Is.True, label.Get ());
736
737                                 var attr = element.GetAttribute ("Hello", string.Empty);
738                                 Assert.That (attr, Is.EqualTo ("1"), label.Get ());
739                                 label.LeaveScope ();
740                                 label.LeaveScope ();
741                                 label.LeaveScope ();
742                                 label.LeaveScope ();
743                                 label.LeaveScope ();
744                         });
745                 }
746                 
747                 #endregion
748
749                 #region Configuration Classes
750
751                 public class MyElement : ConfigurationElement {
752                         [ConfigurationProperty ("Hello", DefaultValue = 8)]
753                         public int Hello {
754                                 get { return (int)base ["Hello"]; }
755                                 set { base ["Hello"] = value; }
756                         }
757
758                         [ConfigurationProperty ("World", IsRequired = false)]
759                         public int World {
760                                 get { return (int)base ["World"]; }
761                                 set { base ["World"] = value; }
762                         }
763
764                         new public bool IsModified {
765                                 get { return base.IsModified (); }
766                         }
767                 }
768
769                 public class MyCollection<T> : ConfigurationElementCollection
770                         where T : ConfigurationElement, new ()
771                 {
772                         #region implemented abstract members of ConfigurationElementCollection
773                         protected override ConfigurationElement CreateNewElement ()
774                         {
775                                 return new T ();
776                         }
777                         protected override object GetElementKey (ConfigurationElement element)
778                         {
779                                 return ((T)element).GetHashCode ();
780                         }
781                         #endregion
782
783                         public override ConfigurationElementCollectionType CollectionType {
784                                 get {
785                                         return ConfigurationElementCollectionType.BasicMap;
786                                 }
787                         }
788
789                         public T AddElement ()
790                         {
791                                 var element = new T ();
792                                 BaseAdd (element);
793                                 return element;
794                         }
795
796                         public void RemoveElement (T element)
797                         {
798                                 BaseRemove (GetElementKey (element));
799                         }
800
801                         public new bool IsModified {
802                                 get { return base.IsModified (); }
803                         }
804                 }
805
806                 public class MyCollectionElement<T> : ConfigurationElement
807                         where T : ConfigurationElement, new ()
808                 {
809                         [ConfigurationProperty ("",
810                                                 Options = ConfigurationPropertyOptions.IsDefaultCollection,
811                                                 IsDefaultCollection = true)]
812                         public MyCollection<T> DefaultCollection {
813                                 get { return (MyCollection<T>)this [String.Empty]; }
814                                 set { this [String.Empty] = value; }
815                         }
816
817                         [ConfigurationProperty ("collection", Options = ConfigurationPropertyOptions.None)]
818                         public MyCollection<T> Collection {
819                                 get { return (MyCollection<T>)this ["collection"]; }
820                                 set { this ["collection"] = value; }
821                         }
822
823                         public new bool IsModified {
824                                 get { return base.IsModified (); }
825                         }
826                 }
827
828                 public class MySection : ConfigurationSection {
829                         [ConfigurationProperty ("list", Options = ConfigurationPropertyOptions.None)]
830                         public MyCollectionElement<MyElement> List {
831                                 get { return (MyCollectionElement<MyElement>) this ["list"]; }
832                         }
833
834                         [ConfigurationProperty ("test", Options = ConfigurationPropertyOptions.None)]
835                         public MyElement Test {
836                                 get { return (MyElement) this ["test"]; }
837                         }
838
839                         new public bool IsModified {
840                                 get { return base.IsModified (); }
841                         }
842                 }
843
844
845                 public class MyElementWithCollection : ConfigurationElement {
846                         [ConfigurationProperty ("test")]
847                         public MyCollectionElement<MyElement> Test {
848                                 get { return (MyCollectionElement<MyElement>) this ["test"]; }
849                         }
850                 }
851
852                 public class MySection2 : ConfigurationSection {
853                         [ConfigurationProperty ("collection", Options = ConfigurationPropertyOptions.None)]
854                         public MyCollectionElement<MyElementWithCollection> Test {
855                                 get { return (MyCollectionElement<MyElementWithCollection>) this ["collection"]; }
856                         }
857
858                         [ConfigurationProperty ("element", Options = ConfigurationPropertyOptions.None)]
859                         public MyElement Element {
860                                 get { return (MyElement)this ["element"]; }
861                         }
862                 }
863
864                 public class MySectionGroup : ConfigurationSectionGroup {
865                         public MySection2 My2 {
866                                 get { return (MySection2)Sections ["my2"]; }
867                         }
868                 }
869
870                 #endregion
871         }
872 }
873