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 	{
GetSchema(string path)30 		private XmlSchema GetSchema (string path)
31 		{
32 			return XmlSchema.Read (new XmlTextReader (path), null);
33 		}
34 
QName(string name, string ns)35 		private XmlQualifiedName QName (string name, string ns)
36 		{
37 			return new XmlQualifiedName (name, ns);
38 		}
39 
AssertDatatype(XmlSchema schema, int index, XmlTokenizedType tokenizedType, Type type, string rawValue, object parsedValue)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).
TestAnyType()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]
TestAll()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]
AnyUriRelativePath()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]
AnyUriRelativePathContainsColon()105 		public void AnyUriRelativePathContainsColon ()
106 		{
107 			XmlValidatingReader vr = new XmlValidatingReader (
108 				new XmlTextReader (
109 					// relative path value that contains ':' should be still valid.
110 					"<root>../copy/myserver/c:/foo</root>",
111 					XmlNodeType.Document, null));
112 			vr.Schemas.Add (XmlSchema.Read (
113 				new XmlTextReader ("<xs:schema xmlns:xs='"
114 					+ XmlSchema.Namespace +
115 					"'><xs:element name='root' type='xs:anyURI' /></xs:schema>",
116 					XmlNodeType.Document, null), null));
117 			vr.Read ();
118 			vr.Read ();
119 			vr.Read ();
120 		}
121 
122 		string [] allTypes = new string [] {
123 			"string", "boolean", "float", "double", "decimal",
124 			"duration", "dateTime", "time", "date", "gYearMonth",
125 			"gYear", "gMonthDay", "gDay", "gMonth", "hexBinary",
126 			"base64Binary", "anyURI", "QName", "NOTATION",
127 			"normalizedString", "token", "language", "IDREFS",
128 			"ENTITIES", "NMTOKEN", "NMTOKENS", "Name", "NCName",
129 			"ID", "IDREF", "ENTITY", "integer",
130 			"nonPositiveInteger", "negativeInteger", "long",
131 			"int", "short", "byte", "nonNegativeInteger",
132 			"unsignedLong", "unsignedInt", "unsignedShort",
133 			"unsignedByte", "positiveInteger"
134 			};
135 
136 		XmlSchemaSet allWrappers;
137 
SetupSimpleTypeWrappers()138 		void SetupSimpleTypeWrappers ()
139 		{
140 			XmlSchema schema = new XmlSchema ();
141 			List<QName> qnames = new List<QName> ();
142 			foreach (string name in allTypes) {
143 				SimpleType st = new SimpleType ();
144 				st.Name = "x-" + name;
145 				SimpleRest r = new SimpleRest ();
146 				st.Content = r;
147 				QName qname = new QName (name, XmlSchema.Namespace);
148 				r.BaseTypeName = qname;
149 				qnames.Add (qname);
150 				schema.Items.Add (st);
151 			}
152 			XmlSchemaSet sset = new XmlSchemaSet ();
153 			sset.Add (schema);
154 			sset.Compile ();
155 			allWrappers = sset;
156 		}
157 
GetDatatype(string name)158 		XmlSchemaDatatype GetDatatype (string name)
159 		{
160 			return (allWrappers.GlobalTypes [new QName ("x-" + name,
161 				String.Empty)] as SimpleType).Datatype;
162 		}
163 
GetDerived(string target)164 		string [] GetDerived (string target)
165 		{
166 			XmlSchemaDatatype strType = GetDatatype (target);
167 			List<string> results = new List<string> ();
168 			foreach (string name in allTypes) {
169 				if (name == target)
170 					continue;
171 				XmlSchemaDatatype deriv = GetDatatype (name);
172 				if (deriv.IsDerivedFrom (strType))
173 					results.Add (name);
174 				else Console.Error.WriteLine (deriv.GetType () + " is not derived from " + strType.GetType ());
175 			}
176 			return results.ToArray ();
177 		}
178 
179 		[Test]
IsDerivedFrom()180 		public void IsDerivedFrom ()
181 		{
182 			SetupSimpleTypeWrappers ();
183 
184 			// Funky, but XmlSchemaDatatype.IsDerivedFrom() is
185 			// documented to always return false, but actually
186 			// matches the same type - which could be guessed that
187 			// this method is used only to detect user-defined
188 			// simpleType derivation.
189 			foreach (string b in allTypes)
190 				foreach (string d in allTypes)
191 					AssertType.AreEqual (b == d, GetDatatype (d).IsDerivedFrom (GetDatatype (b)), b);
192 
193 			AssertType.IsFalse (GetDatatype ("string").IsDerivedFrom (null), "null arg");
194 		}
195 
196 		[Test]
ChangeType_StringTest()197 		public void ChangeType_StringTest()
198 		{
199 			XmlSchemaDatatype datatype = XmlSchemaType.GetBuiltInSimpleType(XmlTypeCode.String).Datatype;
200 			Assert.IsTrue (datatype != null);
201 			Assert.AreEqual (XmlTypeCode.String, datatype.TypeCode);
202 			Assert.AreEqual (typeof(string), datatype.ValueType);
203 
204 			Assert.AreEqual ("test", datatype.ChangeType("test", typeof(string)));
205 		}
206 
207 		[Test]
ChangeType_StringToObjectTest()208 		public void ChangeType_StringToObjectTest()
209 		{
210 			XmlSchemaDatatype datatype = XmlSchemaType.GetBuiltInSimpleType(XmlTypeCode.String).Datatype;
211 			Assert.IsTrue (datatype != null);
212 			Assert.AreEqual (XmlTypeCode.String, datatype.TypeCode);
213 			Assert.AreEqual (typeof(string), datatype.ValueType);
214 
215 			Assert.AreEqual ("test", datatype.ChangeType("test", typeof(object)));
216 		}
217 
218 		[Test]
ChangeType_IntegerTest()219 		public void ChangeType_IntegerTest()
220 		{
221 			XmlSchemaDatatype datatype = XmlSchemaType.GetBuiltInSimpleType(XmlTypeCode.Integer).Datatype;
222 			Assert.IsTrue (datatype != null);
223 			Assert.AreEqual (XmlTypeCode.Integer, datatype.TypeCode);
224 			Assert.AreEqual (typeof(decimal), datatype.ValueType);
225 
226 			Assert.AreEqual (300, datatype.ChangeType("300", typeof(int)));
227 		}
228 
229 		[Test]
ChangeType_FromDateTimeTest()230 		public void ChangeType_FromDateTimeTest()
231 		{
232 			XmlSchemaDatatype datatype = XmlSchemaType.GetBuiltInSimpleType(XmlTypeCode.DateTime).Datatype;
233 			Assert.IsTrue (datatype != null);
234 			Assert.AreEqual (XmlTypeCode.DateTime, datatype.TypeCode);
235 			Assert.AreEqual (typeof(DateTime), datatype.ValueType);
236 
237 			DateTime date = new DateTime (2012, 06, 27, 0, 0, 0, DateTimeKind.Utc);
238 			Assert.AreEqual ("2012-06-27T00:00:00Z", datatype.ChangeType(date, typeof(string)));
239 		}
240 
241 		[Test]
ChangeType_FromTimeSpanTest()242 		public void ChangeType_FromTimeSpanTest()
243 		{
244 			XmlSchemaDatatype datatype = XmlSchemaType.GetBuiltInSimpleType(XmlTypeCode.DayTimeDuration).Datatype;
245 			Assert.IsTrue (datatype != null);
246 			Assert.AreEqual (XmlTypeCode.DayTimeDuration, datatype.TypeCode);
247 			Assert.AreEqual (typeof(TimeSpan), datatype.ValueType);
248 
249 			TimeSpan span = new TimeSpan(1, 2, 3);
250 			Assert.AreEqual ("PT1H2M3S", datatype.ChangeType(span, typeof(string)));
251 		}
252 
253 		[Test]
ChangeType_ToDateTimeTest()254 		public void ChangeType_ToDateTimeTest()
255 		{
256 			XmlSchemaDatatype datatype = XmlSchemaType.GetBuiltInSimpleType(XmlTypeCode.DateTime).Datatype;
257 			Assert.IsTrue (datatype != null);
258 			Assert.AreEqual (XmlTypeCode.DateTime, datatype.TypeCode);
259 			Assert.AreEqual (typeof(DateTime), datatype.ValueType);
260 
261 			DateTime date = new DateTime (2012, 06, 27, 0, 0, 0, DateTimeKind.Utc);
262 			Assert.AreEqual (date, datatype.ChangeType("2012-06-27T00:00:00Z", typeof(DateTime)));
263 		}
264 
265 		[Test]
ChangeType_ToTimeSpanTest()266 		public void ChangeType_ToTimeSpanTest()
267 		{
268 			XmlSchemaDatatype datatype = XmlSchemaType.GetBuiltInSimpleType(XmlTypeCode.DayTimeDuration).Datatype;
269 			Assert.IsTrue (datatype != null);
270 			Assert.AreEqual (XmlTypeCode.DayTimeDuration, datatype.TypeCode);
271 			Assert.AreEqual (typeof(TimeSpan), datatype.ValueType);
272 
273 			TimeSpan span = new TimeSpan(1, 2, 3);
274 			Assert.AreEqual (span, datatype.ChangeType("PT1H2M3S", typeof(TimeSpan)));
275 		}
276 
277 		[Test]
278 		[ExpectedException(typeof(ArgumentNullException))]
ChangeType_NullValueArgumentInFromStringTest()279 		public void ChangeType_NullValueArgumentInFromStringTest()
280 		{
281 			XmlSchemaDatatype datatype = XmlSchemaType.GetBuiltInSimpleType(XmlTypeCode.Integer).Datatype;
282 			datatype.ChangeType(null, typeof(string));
283 		}
284 
285 		[Test]
286 		[ExpectedException(typeof(ArgumentNullException))]
ChangeType_NullValueArgumentInToStringTest()287 		public void ChangeType_NullValueArgumentInToStringTest()
288 		{
289 			XmlSchemaDatatype datatype = XmlSchemaType.GetBuiltInSimpleType(XmlTypeCode.Integer).Datatype;
290 			datatype.ChangeType(null, typeof(int));
291 		}
292 
293 		[Test]
294 		[ExpectedException(typeof(ArgumentNullException))]
ChangeType_NullTargetArgumentInFromStringTest()295 		public void ChangeType_NullTargetArgumentInFromStringTest()
296 		{
297 			XmlSchemaDatatype datatype = XmlSchemaType.GetBuiltInSimpleType(XmlTypeCode.Integer).Datatype;
298 			datatype.ChangeType("100", null);
299 		}
300 
301 		[Test]
302 		[ExpectedException(typeof(ArgumentNullException))]
ChangeType_NullNamespaceResolverArgumentInFromStringTest()303 		public void ChangeType_NullNamespaceResolverArgumentInFromStringTest()
304 		{
305 			XmlSchemaDatatype datatype = XmlSchemaType.GetBuiltInSimpleType(XmlTypeCode.Integer).Datatype;
306 			datatype.ChangeType("100", typeof(string), null);
307 		}
308 
309 		[Test]
310 		[Category("NotWorking")]
311 		[ExpectedException (typeof(InvalidCastException))]
InvalidCastExceptionTest()312 		public void InvalidCastExceptionTest()
313 		{
314 			XmlSchemaDatatype datatype = XmlSchemaType.GetBuiltInSimpleType(XmlTypeCode.DateTime).Datatype;
315 			Assert.IsTrue (datatype != null);
316 			Assert.AreEqual (XmlTypeCode.DateTime, datatype.TypeCode);
317 			Assert.AreEqual (typeof(DateTime), datatype.ValueType);
318 
319 			datatype.ChangeType(300, typeof (int));
320 		}
321 
322 		[Test]
Bug12469()323 		public void Bug12469 ()
324 		{
325 			Dictionary<string, string> validValues = new Dictionary<string, string> {
326 				{"string", "abc"},
327 
328 				{"normalizedString", "abc"},
329 				{"token", "abc"},
330 				{"language", "en"},
331 				{"Name", "abc"},
332 				{"NCName", "abc"},
333 				{"ID", "abc"},
334 				{"ENTITY", "abc"},
335 				{"NMTOKEN", "abc"},
336 
337 				{"boolean", "true"},
338 				{"decimal", "1"},
339 				{"integer", "1"},
340 				{"nonPositiveInteger", "0"},
341 				{"negativeInteger", "-1"},
342 				{"long", "9223372036854775807"},
343 				{"int", "2147483647"},
344 				{"short", "32767"},
345 				{"byte", "127"},
346 				{"nonNegativeInteger", "0"},
347 				{"unsignedLong", "18446744073709551615"},
348 				{"unsignedInt", "4294967295"},
349 				{"unsignedShort", "65535"},
350 				{"unsignedByte", "255"},
351 				{"positiveInteger", "1"},
352 				{"float", "1.1"},
353 				{"double", "1.1"},
354 				{"time", "00:00:00"},
355 				{"date", "1999-12-31"},
356 				{"dateTime", "1999-12-31T00:00:00.000"},
357 				{"duration", "P1Y2M3DT10H30M"},
358 				{"gYearMonth", "1999-01"},
359 				{"gYear", "1999"},
360 				{"gMonthDay", "--12-31"},
361 				{"gMonth", "--12"},
362 				{"gDay", "---31"},
363 
364 				{"base64Binary", "AbCd eFgH IjKl 019+"},
365 				{"hexBinary", "0123456789ABCDEF"},
366 
367 				{"anyURI", "https://www.server.com"},
368 				{"QName", "xml:abc"},
369 				};
370 
371 			// FIXME: implement validation
372 			Dictionary<string, string> invalidValues = new Dictionary<string, string> {
373 				{"Name", "***"},
374 				{"NCName", "a::"},
375 				{"ID", "123"},
376 				{"ENTITY", "***"},
377 				{"NMTOKEN", "***"},
378 
379 				{"boolean", "ABC"},
380 				{"decimal", "1A"},
381 				{"integer", "1.5"},
382 				{"nonPositiveInteger", "5"},
383 				{"negativeInteger", "10"},
384 				{"long", "999999999999999999999999999999999999999"},
385 				{"int", "999999999999999999999999999999999999999"},
386 				{"short", "32768"},
387 				{"byte", "128"},
388 				{"nonNegativeInteger", "-1"},
389 				{"unsignedLong", "-1"},
390 				{"unsignedInt", "-1"},
391 				{"unsignedShort", "-1"},
392 				{"unsignedByte", "-1"},
393 				{"positiveInteger", "0"},
394 				{"float", "1.1x"},
395 				{"double", "1.1x"},
396 				{"time", "0"},
397 				{"date", "1"},
398 				{"dateTime", "2"},
399 				{"duration", "P1"},
400 				{"gYearMonth", "1999"},
401 				{"gYear", "-1"},
402 				{"gMonthDay", "-12-31"},
403 				{"gMonth", "-12"},
404 				{"gDay", "--31"},
405 
406 				{"base64Binary", "####"},
407 				{"hexBinary", "G"},
408 
409 				// anyURI passes everything (as long as I observed)
410 				{"QName", "::"},
411 				};
412 
413 			const string schemaTemplate = @"
414 				<xs:schema xmlns:xs='http://www.w3.org/2001/XMLSchema' elementFormDefault='qualified'>
415 					<xs:element name='EL'>
416 						<xs:complexType>
417 							<xs:attribute name='attr' type='xs:{0}' use='required' />
418 						</xs:complexType>
419 					</xs:element>
420 				</xs:schema>";
421 
422 			const string documentTemplate = @"<EL attr='{0}' />";
423 
424 			foreach (var type in validValues.Keys) {
425 				try {
426 					var schema = string.Format (schemaTemplate, type);
427 					var document = string.Format (documentTemplate, validValues[type]);
428 
429 					var schemaSet = new XmlSchemaSet ();
430 					using (var reader = new StringReader (schema))
431 						schemaSet.Add (XmlSchema.Read (reader, null));
432 					schemaSet.Compile ();
433 					var doc = new XmlDocument ();
434 					using (var reader = new StringReader (document))
435 						doc.Load (reader);
436 					doc.Schemas = schemaSet;
437 					doc.Validate (null);
438 
439 					// FIXME: implement validation
440 					/*
441 					if (!invalidValues.ContainsKey (type))
442 						continue;
443 					try {
444 						doc = new XmlDocument ();
445 						document = string.Format (documentTemplate, invalidValues [type]);
446 						using (var reader = new StringReader (document))
447 							doc.Load (reader);
448 						doc.Schemas = schemaSet;
449 						doc.Validate (null);
450 						Assert.Fail (string.Format ("Failed to invalidate {0} for {1}", document, type));
451 					} catch (XmlSchemaException) {
452 					}
453 					*/
454 				} catch (Exception) {
455 					Console.Error.WriteLine (type);
456 					throw;
457 				}
458 			}
459 		}
460 	}
461 }
462