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