Merge pull request #799 from kebby/master
[mono.git] / mcs / class / System.XML / Test / System.Xml.Schema / XmlSchemaDatatypeTests.cs
1 //
2 // System.Xml.XmlSchemaDatatypeTests.cs
3 //
4 // Author:
5 //   Atsushi Enomoto <ginga@kit.hi-ho.ne.jp>
6 //   Wojciech Kotlarski <wojciech.kotlarski@7digital.com>
7 //   Andres G. Aragoneses <andres.aragoneses@7digital.com>
8 //
9 // (C) 2002 Atsushi Enomoto
10 // (C) 2012 7digital Media Ltd.
11 //
12
13 using System;
14 using System.IO;
15 using System.Xml;
16 using System.Xml.Schema;
17 using System.Collections.Generic;
18 using NUnit.Framework;
19
20 using QName = System.Xml.XmlQualifiedName;
21 using SimpleType = System.Xml.Schema.XmlSchemaSimpleType;
22 using SimpleRest = System.Xml.Schema.XmlSchemaSimpleTypeRestriction;
23 using AssertType = NUnit.Framework.Assert;
24
25 namespace MonoTests.System.Xml
26 {
27         [TestFixture]
28         public class XmlSchemaDatatypeTests
29         {
30                 private XmlSchema GetSchema (string path)
31                 {
32                         return XmlSchema.Read (new XmlTextReader (path), null);
33                 }
34
35                 private XmlQualifiedName QName (string name, string ns)
36                 {
37                         return new XmlQualifiedName (name, ns);
38                 }
39
40                 private void AssertDatatype (XmlSchema schema, int index,
41                         XmlTokenizedType tokenizedType, Type type, string rawValue, object parsedValue)
42                 {
43                         XmlSchemaElement element = schema.Items [index] as XmlSchemaElement;
44                         XmlSchemaDatatype dataType = element.ElementType as XmlSchemaDatatype;
45                         Assert.AreEqual (tokenizedType, dataType.TokenizedType, "#1");
46                         Assert.AreEqual (type, dataType.ValueType, "#2");
47                         Assert.AreEqual (parsedValue, dataType.ParseValue (rawValue, null, null), "#3");
48                 }
49
50                 [Test]
51                 [Ignore ("The behavior has been inconsistent between versions, so it is not worthy of testing.")]
52                 // Note that it could also apply to BaseTypeName (since if 
53                 // it is xs:anyType and BaseType is empty, BaseTypeName
54                 // should be xs:anyType).
55                 public void TestAnyType ()
56                 {
57                         XmlSchema schema = GetSchema ("Test/XmlFiles/xsd/datatypesTest.xsd");
58                         schema.Compile (null);
59                         XmlSchemaElement any = schema.Elements [QName ("e00", "urn:bar")] as XmlSchemaElement;
60                         XmlSchemaComplexType cType = any.ElementType as XmlSchemaComplexType;
61                         Assert.AreEqual (typeof (XmlSchemaComplexType), cType.GetType (), "#1");
62                         Assert.IsNotNull (cType, "#2");
63                         Assert.AreEqual (XmlQualifiedName.Empty, cType.QualifiedName, "#3");
64                         Assert.IsNull (cType.BaseSchemaType, "#4");  // In MS.NET 2.0 its null. In 1.1 it is not null.
65                         Assert.IsNotNull (cType.ContentTypeParticle, "#5");
66                 }
67
68                 [Test]
69                 public void TestAll ()
70                 {
71                         XmlSchema schema = GetSchema ("Test/XmlFiles/xsd/datatypesTest.xsd");
72                         schema.Compile (null);
73
74                         AssertDatatype (schema, 1, XmlTokenizedType.CDATA, typeof (string), " f o o ", " f o o ");
75                         AssertDatatype (schema, 2, XmlTokenizedType.CDATA, typeof (string), " f o o ", " f o o ");
76                         // token shouldn't allow " f o o "
77                         AssertDatatype (schema, 3, XmlTokenizedType.CDATA, typeof (string), "f o o", "f o o");
78                         // language seems to be checked strictly
79                         AssertDatatype (schema, 4, XmlTokenizedType.CDATA, typeof (string), "x-foo", "x-foo");
80
81                         // NMTOKEN shouldn't allow " f o o "
82 //                      AssertDatatype (schema, 5, XmlTokenizedType.NMTOKEN, typeof (string), "foo", "foo");
83 //                      AssertDatatype (schema, 6, XmlTokenizedType.NMTOKEN, typeof (string []), "f o o", new string [] {"f",  "o",  "o"});
84                 }
85
86                 [Test]
87                 public void AnyUriRelativePath ()
88                 {
89                         XmlValidatingReader vr = new XmlValidatingReader (
90                                 new XmlTextReader (
91                                         // relative path value that contains ':' should be still valid.
92                                         "<root>../copy/myserver/</root>", 
93                                         XmlNodeType.Document, null));
94                         vr.Schemas.Add (XmlSchema.Read (
95                                 new XmlTextReader ("<xs:schema xmlns:xs='"
96                                         + XmlSchema.Namespace +
97                                         "'><xs:element name='root' type='xs:anyURI' /></xs:schema>",
98                                         XmlNodeType.Document, null), null));
99                         vr.Read ();
100                         vr.Read ();
101                         vr.Read ();
102                 }
103
104                 [Test]
105 #if !NET_2_0
106                 [Category ("NotDotNet")]
107 #endif
108                 public void AnyUriRelativePathContainsColon ()
109                 {
110                         XmlValidatingReader vr = new XmlValidatingReader (
111                                 new XmlTextReader (
112                                         // relative path value that contains ':' should be still valid.
113                                         "<root>../copy/myserver/c:/foo</root>", 
114                                         XmlNodeType.Document, null));
115                         vr.Schemas.Add (XmlSchema.Read (
116                                 new XmlTextReader ("<xs:schema xmlns:xs='"
117                                         + XmlSchema.Namespace +
118                                         "'><xs:element name='root' type='xs:anyURI' /></xs:schema>",
119                                         XmlNodeType.Document, null), null));
120                         vr.Read ();
121                         vr.Read ();
122                         vr.Read ();
123                 }
124
125                 string [] allTypes = new string [] {
126                         "string", "boolean", "float", "double", "decimal", 
127                         "duration", "dateTime", "time", "date", "gYearMonth", 
128                         "gYear", "gMonthDay", "gDay", "gMonth", "hexBinary", 
129                         "base64Binary", "anyURI", "QName", "NOTATION", 
130                         "normalizedString", "token", "language", "IDREFS",
131                         "ENTITIES", "NMTOKEN", "NMTOKENS", "Name", "NCName",
132                         "ID", "IDREF", "ENTITY", "integer",
133                         "nonPositiveInteger", "negativeInteger", "long",
134                         "int", "short", "byte", "nonNegativeInteger",
135                         "unsignedLong", "unsignedInt", "unsignedShort",
136                         "unsignedByte", "positiveInteger"
137                         };
138
139                 XmlSchemaSet allWrappers;
140
141                 void SetupSimpleTypeWrappers ()
142                 {
143                         XmlSchema schema = new XmlSchema ();
144                         List<QName> qnames = new List<QName> ();
145                         foreach (string name in allTypes) {
146                                 SimpleType st = new SimpleType ();
147                                 st.Name = "x-" + name;
148                                 SimpleRest r = new SimpleRest ();
149                                 st.Content = r;
150                                 QName qname = new QName (name, XmlSchema.Namespace);
151                                 r.BaseTypeName = qname;
152                                 qnames.Add (qname);
153                                 schema.Items.Add (st);
154                         }
155                         XmlSchemaSet sset = new XmlSchemaSet ();
156                         sset.Add (schema);
157                         sset.Compile ();
158                         allWrappers = sset;
159                 }
160
161                 XmlSchemaDatatype GetDatatype (string name)
162                 {
163                         return (allWrappers.GlobalTypes [new QName ("x-" + name,
164                                 String.Empty)] as SimpleType).Datatype;
165                 }
166
167                 string [] GetDerived (string target)
168                 {
169                         XmlSchemaDatatype strType = GetDatatype (target);
170                         List<string> results = new List<string> ();
171                         foreach (string name in allTypes) {
172                                 if (name == target)
173                                         continue;
174                                 XmlSchemaDatatype deriv = GetDatatype (name);
175                                 if (deriv.IsDerivedFrom (strType))
176                                         results.Add (name);
177                                 else Console.Error.WriteLine (deriv.GetType () + " is not derived from " + strType.GetType ());
178                         }
179                         return results.ToArray ();
180                 }
181
182                 [Test]
183                 public void IsDerivedFrom ()
184                 {
185                         SetupSimpleTypeWrappers ();
186
187                         // Funky, but XmlSchemaDatatype.IsDerivedFrom() is
188                         // documented to always return false, but actually
189                         // matches the same type - which could be guessed that
190                         // this method is used only to detect user-defined
191                         // simpleType derivation.
192                         foreach (string b in allTypes)
193                                 foreach (string d in allTypes)
194                                         AssertType.AreEqual (b == d, GetDatatype (d).IsDerivedFrom (GetDatatype (b)), b);
195
196                         AssertType.IsFalse (GetDatatype ("string").IsDerivedFrom (null), "null arg");
197                 }
198
199                 [Test]
200                 public void ChangeType_StringTest()
201                 {
202                         XmlSchemaDatatype datatype = XmlSchemaType.GetBuiltInSimpleType(XmlTypeCode.String).Datatype;
203                         Assert.IsTrue (datatype != null);
204                         Assert.AreEqual (XmlTypeCode.String, datatype.TypeCode);
205                         Assert.AreEqual (typeof(string), datatype.ValueType);
206
207                         Assert.AreEqual ("test", datatype.ChangeType("test", typeof(string)));
208                 }
209
210                 [Test]
211                 public void ChangeType_StringToObjectTest()
212                 {
213                         XmlSchemaDatatype datatype = XmlSchemaType.GetBuiltInSimpleType(XmlTypeCode.String).Datatype;
214                         Assert.IsTrue (datatype != null);
215                         Assert.AreEqual (XmlTypeCode.String, datatype.TypeCode);
216                         Assert.AreEqual (typeof(string), datatype.ValueType);
217
218                         Assert.AreEqual ("test", datatype.ChangeType("test", typeof(object)));
219                 }
220
221                 [Test]
222                 public void ChangeType_IntegerTest()
223                 {
224                         XmlSchemaDatatype datatype = XmlSchemaType.GetBuiltInSimpleType(XmlTypeCode.Integer).Datatype;
225                         Assert.IsTrue (datatype != null);
226                         Assert.AreEqual (XmlTypeCode.Integer, datatype.TypeCode);
227                         Assert.AreEqual (typeof(decimal), datatype.ValueType);
228
229                         Assert.AreEqual (300, datatype.ChangeType("300", typeof(int)));
230                 }
231
232                 [Test]
233                 public void ChangeType_FromDateTimeTest()
234                 {
235                         XmlSchemaDatatype datatype = XmlSchemaType.GetBuiltInSimpleType(XmlTypeCode.DateTime).Datatype;
236                         Assert.IsTrue (datatype != null);
237                         Assert.AreEqual (XmlTypeCode.DateTime, datatype.TypeCode);
238                         Assert.AreEqual (typeof(DateTime), datatype.ValueType);
239
240                         DateTime date = new DateTime (2012, 06, 27, 0, 0, 0, DateTimeKind.Utc);
241                         Assert.AreEqual ("2012-06-27T00:00:00Z", datatype.ChangeType(date, typeof(string)));
242                 }
243
244                 [Test]
245                 public void ChangeType_FromTimeSpanTest()
246                 {
247                         XmlSchemaDatatype datatype = XmlSchemaType.GetBuiltInSimpleType(XmlTypeCode.DayTimeDuration).Datatype;
248                         Assert.IsTrue (datatype != null);
249                         Assert.AreEqual (XmlTypeCode.DayTimeDuration, datatype.TypeCode);
250                         Assert.AreEqual (typeof(TimeSpan), datatype.ValueType);
251
252                         TimeSpan span = new TimeSpan(1, 2, 3);
253                         Assert.AreEqual ("PT1H2M3S", datatype.ChangeType(span, typeof(string)));
254                 }
255
256                 [Test]
257                 public void ChangeType_ToDateTimeTest()
258                 {
259                         XmlSchemaDatatype datatype = XmlSchemaType.GetBuiltInSimpleType(XmlTypeCode.DateTime).Datatype;
260                         Assert.IsTrue (datatype != null);
261                         Assert.AreEqual (XmlTypeCode.DateTime, datatype.TypeCode);
262                         Assert.AreEqual (typeof(DateTime), datatype.ValueType);
263
264                         DateTime date = new DateTime (2012, 06, 27, 0, 0, 0, DateTimeKind.Utc);
265                         Assert.AreEqual (date, datatype.ChangeType("2012-06-27T00:00:00Z", typeof(DateTime)));
266                 }
267
268                 [Test]
269                 public void ChangeType_ToTimeSpanTest()
270                 {
271                         XmlSchemaDatatype datatype = XmlSchemaType.GetBuiltInSimpleType(XmlTypeCode.DayTimeDuration).Datatype;
272                         Assert.IsTrue (datatype != null);
273                         Assert.AreEqual (XmlTypeCode.DayTimeDuration, datatype.TypeCode);
274                         Assert.AreEqual (typeof(TimeSpan), datatype.ValueType);
275
276                         TimeSpan span = new TimeSpan(1, 2, 3);
277                         Assert.AreEqual (span, datatype.ChangeType("PT1H2M3S", typeof(TimeSpan)));
278                 }
279
280                 [Test]
281                 [ExpectedException(typeof(ArgumentNullException))]
282                 public void ChangeType_NullValueArgumentInFromStringTest()
283                 {
284                         XmlSchemaDatatype datatype = XmlSchemaType.GetBuiltInSimpleType(XmlTypeCode.Integer).Datatype;
285                         datatype.ChangeType(null, typeof(string));
286                 }
287
288                 [Test]
289                 [ExpectedException(typeof(ArgumentNullException))]
290                 public void ChangeType_NullValueArgumentInToStringTest()
291                 {
292                         XmlSchemaDatatype datatype = XmlSchemaType.GetBuiltInSimpleType(XmlTypeCode.Integer).Datatype;
293                         datatype.ChangeType(null, typeof(int));
294                 }
295
296                 [Test]
297                 [ExpectedException(typeof(ArgumentNullException))]
298                 public void ChangeType_NullTargetArgumentInFromStringTest()
299                 {
300                         XmlSchemaDatatype datatype = XmlSchemaType.GetBuiltInSimpleType(XmlTypeCode.Integer).Datatype;
301                         datatype.ChangeType("100", null);
302                 }
303
304                 [Test]
305                 [ExpectedException(typeof(ArgumentNullException))]
306                 public void ChangeType_NullNamespaceResolverArgumentInFromStringTest()
307                 {
308                         XmlSchemaDatatype datatype = XmlSchemaType.GetBuiltInSimpleType(XmlTypeCode.Integer).Datatype;
309                         datatype.ChangeType("100", typeof(string), null);
310                 }
311
312                 [Test]
313                 [Category("NotWorking")]
314                 [ExpectedException (typeof(InvalidCastException))]
315                 public void InvalidCastExceptionTest()
316                 {
317                         XmlSchemaDatatype datatype = XmlSchemaType.GetBuiltInSimpleType(XmlTypeCode.DateTime).Datatype;
318                         Assert.IsTrue (datatype != null);
319                         Assert.AreEqual (XmlTypeCode.DateTime, datatype.TypeCode);
320                         Assert.AreEqual (typeof(DateTime), datatype.ValueType);
321
322                         datatype.ChangeType(300, typeof (int));
323                 }
324                 
325                 [Test]
326                 public void Bug12469 ()
327                 {
328                         Dictionary<string, string> validValues = new Dictionary<string, string> {
329                                 {"string", "abc"},
330
331                                 {"normalizedString", "abc"},
332                                 {"token", "abc"},
333                                 {"language", "en"},
334                                 {"Name", "abc"},
335                                 {"NCName", "abc"},
336                                 {"ID", "abc"},
337                                 {"ENTITY", "abc"},
338                                 {"NMTOKEN", "abc"},
339
340                                 {"boolean", "true"},
341                                 {"decimal", "1"},
342                                 {"integer", "1"},
343                                 {"nonPositiveInteger", "0"},
344                                 {"negativeInteger", "-1"},
345                                 {"long", "9223372036854775807"},
346                                 {"int", "2147483647"},
347                                 {"short", "32767"},
348                                 {"byte", "127"},
349                                 {"nonNegativeInteger", "0"},
350                                 {"unsignedLong", "18446744073709551615"},
351                                 {"unsignedInt", "4294967295"},
352                                 {"unsignedShort", "65535"},
353                                 {"unsignedByte", "255"},
354                                 {"positiveInteger", "1"},
355                                 {"float", "1.1"},
356                                 {"double", "1.1"},
357                                 {"time", "00:00:00"},
358                                 {"date", "1999-12-31"},
359                                 {"dateTime", "1999-12-31T00:00:00.000"},
360                                 {"duration", "P1Y2M3DT10H30M"},
361                                 {"gYearMonth", "1999-01"},
362                                 {"gYear", "1999"},
363                                 {"gMonthDay", "--12-31"},
364                                 {"gMonth", "--12"},
365                                 {"gDay", "---31"},
366
367                                 {"base64Binary", "AbCd eFgH IjKl 019+"},
368                                 {"hexBinary", "0123456789ABCDEF"},
369
370                                 {"anyURI", "https://www.server.com"},
371                                 {"QName", "xml:abc"},
372                                 };
373
374                         // FIXME: implement validation
375                         Dictionary<string, string> invalidValues = new Dictionary<string, string> {
376                                 {"Name", "***"},
377                                 {"NCName", "a::"},
378                                 {"ID", "123"},
379                                 {"ENTITY", "***"},
380                                 {"NMTOKEN", "***"},
381
382                                 {"boolean", "ABC"},
383                                 {"decimal", "1A"},
384                                 {"integer", "1.5"},
385                                 {"nonPositiveInteger", "5"},
386                                 {"negativeInteger", "10"},
387                                 {"long", "999999999999999999999999999999999999999"},
388                                 {"int", "999999999999999999999999999999999999999"},
389                                 {"short", "32768"},
390                                 {"byte", "128"},
391                                 {"nonNegativeInteger", "-1"},
392                                 {"unsignedLong", "-1"},
393                                 {"unsignedInt", "-1"},
394                                 {"unsignedShort", "-1"},
395                                 {"unsignedByte", "-1"},
396                                 {"positiveInteger", "0"},
397                                 {"float", "1.1x"},
398                                 {"double", "1.1x"},
399                                 {"time", "0"},
400                                 {"date", "1"},
401                                 {"dateTime", "2"},
402                                 {"duration", "P1"},
403                                 {"gYearMonth", "1999"},
404                                 {"gYear", "-1"},
405                                 {"gMonthDay", "-12-31"},
406                                 {"gMonth", "-12"},
407                                 {"gDay", "--31"},
408
409                                 {"base64Binary", "####"},
410                                 {"hexBinary", "G"},
411
412                                 // anyURI passes everything (as long as I observed)
413                                 {"QName", "::"},
414                                 };
415
416                         const string schemaTemplate = @"
417                                 <xs:schema xmlns:xs='http://www.w3.org/2001/XMLSchema' elementFormDefault='qualified'> 
418                                         <xs:element name='EL'>
419                                                 <xs:complexType>
420                                                         <xs:attribute name='attr' type='xs:{0}' use='required' />
421                                                 </xs:complexType>
422                                         </xs:element>
423                                 </xs:schema>";
424
425                         const string documentTemplate = @"<EL attr='{0}' />";
426
427                         foreach (var type in validValues.Keys) {
428                                 try {
429                                         var schema = string.Format (schemaTemplate, type);
430                                         var document = string.Format (documentTemplate, validValues[type]);
431
432                                         var schemaSet = new XmlSchemaSet ();
433                                         using (var reader = new StringReader (schema))
434                                                 schemaSet.Add (XmlSchema.Read (reader, null));
435                                         schemaSet.Compile ();
436                                         var doc = new XmlDocument ();
437                                         using (var reader = new StringReader (document))
438                                                 doc.Load (reader);
439                                         doc.Schemas = schemaSet;
440                                         doc.Validate (null);
441
442                                         // FIXME: implement validation
443                                         /*
444                                         if (!invalidValues.ContainsKey (type))
445                                                 continue;
446                                         try {
447                                                 doc = new XmlDocument ();
448                                                 document = string.Format (documentTemplate, invalidValues [type]);
449                                                 using (var reader = new StringReader (document))
450                                                         doc.Load (reader);
451                                                 doc.Schemas = schemaSet;
452                                                 doc.Validate (null);
453                                                 Assert.Fail (string.Format ("Failed to invalidate {0} for {1}", document, type));
454                                         } catch (XmlSchemaException) {
455                                         }
456                                         */
457                                 } catch (Exception) {
458                                         Console.Error.WriteLine (type);
459                                         throw;
460                                 }
461                         }
462                 }
463         }
464 }