2 // ContractDescriptionTest.cs
5 // Atsushi Enomoto <atsushi@ximian.com>
7 // Copyright (C) 2005 Novell, Inc. http://www.novell.com
9 // Permission is hereby granted, free of charge, to any person obtaining
10 // a copy of this software and associated documentation files (the
11 // "Software"), to deal in the Software without restriction, including
12 // without limitation the rights to use, copy, modify, merge, publish,
13 // distribute, sublicense, and/or sell copies of the Software, and to
14 // permit persons to whom the Software is furnished to do so, subject to
15 // the following conditions:
17 // The above copyright notice and this permission notice shall be
18 // included in all copies or substantial portions of the Software.
20 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
22 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
24 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
25 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
26 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
29 using System.Collections.ObjectModel;
31 using System.Net.Security;
32 using System.Reflection;
33 using System.ServiceModel;
34 using System.ServiceModel.Channels;
35 using System.ServiceModel.Description;
36 using NUnit.Framework;
38 namespace MonoTests.System.ServiceModel.Description
41 public class ContractDescriptionTest
44 [ExpectedException (typeof (InvalidOperationException))]
45 public void GetNonContract ()
47 ContractDescription cd = ContractDescription.GetContract (
52 public void GetContract ()
54 InternalTestGetContract (
55 ContractDescription.GetContract (typeof (IFoo)));
59 public void GetContractParamRenamed ()
61 ContractDescription cd = ContractDescription.GetContract (typeof (IFooMsgParams));
63 Assert.AreEqual (1, cd.Operations.Count, "Operation count");
66 OperationDescription od = cd.Operations [0];
68 ServiceAssert.AssertOperationDescription (
70 typeof (IFooMsgParams).GetMethod ("Foo"),
74 // Operation #1 -> Message #1
75 MessageDescription md = od.Messages [0];
77 ServiceAssert.AssertMessageAndBodyDescription (
78 "http://tempuri.org/IFooMsgParams/MyFoo",
79 MessageDirection.Input,
80 null, "MyFoo", "http://tempuri.org/", false,
83 ServiceAssert.AssertMessagePartDescription (
84 "MyParam", "http://tempuri.org/", 0, false,
85 ProtectionLevel.None, typeof (string), md.Body.Parts [0], "MyFoo.msg");
89 ServiceAssert.AssertMessageAndBodyDescription (
90 "http://tempuri.org/IFooMsgParams/MyFooResponse",
91 MessageDirection.Output,
92 null, "MyFooResponse",
93 "http://tempuri.org/", true,
96 ServiceAssert.AssertMessagePartDescription (
97 "MyResult", "http://tempuri.org/", 0, false,
98 ProtectionLevel.None, typeof (string), md.Body.ReturnValue, "MyResult ReturnValue");
102 public void GetContractConfigName ()
104 ContractDescription cd = ContractDescription.GetContract (typeof (ICtorUseCase2));
105 Assert.AreEqual("CtorUseCase2", cd.ConfigurationName);
106 Assert.AreEqual("ICtorUseCase2", cd.Name);
107 cd = ContractDescription.GetContract (typeof (ICtorUseCase1));
108 Assert.AreEqual("MonoTests.System.ServiceModel.ICtorUseCase1", cd.ConfigurationName);
109 Assert.AreEqual("ICtorUseCase1", cd.Name);
113 public void GetContract2 ()
115 InternalTestGetContract (
116 ContractDescription.GetContract (typeof (Foo)));
119 public void InternalTestGetContract (ContractDescription cd)
121 ServiceAssert.AssertContractDescription (
122 "IFoo", "http://tempuri.org/", SessionMode.Allowed, typeof (IFoo), null,
125 Assert.AreEqual (2, cd.Operations.Count, "Operation count");
128 OperationDescription od = cd.Operations [0];
130 ServiceAssert.AssertOperationDescription (
131 "HeyDude", null, null,
132 typeof (IFoo).GetMethod ("HeyDude"),
136 // Operation #1 -> Message #1
137 MessageDescription md = od.Messages [0];
139 ServiceAssert.AssertMessageAndBodyDescription (
140 "http://tempuri.org/IFoo/HeyDude",
141 MessageDirection.Input,
142 null, "HeyDude", "http://tempuri.org/", false,
145 ServiceAssert.AssertMessagePartDescription (
146 "msg", "http://tempuri.org/", 0, false,
147 ProtectionLevel.None, typeof (string), md.Body.Parts [0], "HeyDude.msg");
148 ServiceAssert.AssertMessagePartDescription (
149 "msg2", "http://tempuri.org/", 1, false,
150 ProtectionLevel.None, typeof (string), md.Body.Parts [1], "HeyDude.msg");
152 // Operation #1 -> Message #2
153 md = od.Messages [1];
155 ServiceAssert.AssertMessageAndBodyDescription (
156 "http://tempuri.org/IFoo/HeyDudeResponse",
157 MessageDirection.Output,
158 null, "HeyDudeResponse",
159 "http://tempuri.org/", true,
162 ServiceAssert.AssertMessagePartDescription (
163 "HeyDudeResult", "http://tempuri.org/", 0, false,
164 ProtectionLevel.None, typeof (string), md.Body.ReturnValue, "HeyDudeResponse ReturnValue");
167 od = cd.Operations [1];
169 ServiceAssert.AssertOperationDescription (
170 "HeyHey", null, null,
171 typeof (IFoo).GetMethod ("HeyHey"),
175 // Operation #2 -> Message #1
176 md = od.Messages [0];
178 ServiceAssert.AssertMessageAndBodyDescription (
179 "http://tempuri.org/IFoo/HeyHey",
180 MessageDirection.Input,
181 null, "HeyHey", "http://tempuri.org/", false,
184 ServiceAssert.AssertMessagePartDescription (
185 "ref1", "http://tempuri.org/", 0, false,
186 ProtectionLevel.None, typeof (string), md.Body.Parts [0], "HeyHey.ref1");
188 // Operation #2 -> Message #2
189 md = od.Messages [1];
191 ServiceAssert.AssertMessageAndBodyDescription (
192 "http://tempuri.org/IFoo/HeyHeyResponse",
193 MessageDirection.Output,
194 null, "HeyHeyResponse",
195 "http://tempuri.org/", true,
198 ServiceAssert.AssertMessagePartDescription (
199 "HeyHeyResult", "http://tempuri.org/", 0, false,
200 ProtectionLevel.None, typeof (void), md.Body.ReturnValue, "HeyHeyResponse ReturnValue");
202 ServiceAssert.AssertMessagePartDescription (
203 "out1", "http://tempuri.org/", 0, false,
204 ProtectionLevel.None, typeof (string), md.Body.Parts [0], "HeyHey.out1");
205 ServiceAssert.AssertMessagePartDescription (
206 "ref1", "http://tempuri.org/", 1, false,
207 ProtectionLevel.None, typeof (string), md.Body.Parts [1], "HeyHey.ref1");
211 public void GetContractInherit ()
213 ContractDescription.GetContract (typeof (Foo));
217 [ExpectedException (typeof (InvalidOperationException))]
218 public void GetMultipleServiceContract ()
220 ContractDescription.GetContract (typeof (FooBar));
224 // [ExpectedException (typeof (InvalidOperationException))]
225 public void GetContractNoOperation ()
227 ContractDescription.GetContract (typeof (INoOperation));
231 [Category ("NotWorking")]
232 public void GetContractMessageParameter ()
234 ContractDescription cd = ContractDescription.GetContract (typeof (IMessageParameter));
236 ServiceAssert.AssertContractDescription (
237 "IMessageParameter", "http://tempuri.org/",
238 SessionMode.Allowed, typeof (IMessageParameter), null,
241 OperationDescription od = cd.Operations [0];
243 ServiceAssert.AssertOperationDescription (
244 "ReturnMessage", null, null,
245 typeof (IMessageParameter).GetMethod ("ReturnMessage"),
249 MessageDescription md = od.Messages [0];
251 ServiceAssert.AssertMessageAndBodyDescription (
252 "http://tempuri.org/IMessageParameter/ReturnMessage",
253 MessageDirection.Input,
254 // Body.WrapperName is null
255 null, null, null, false,
256 md, "ReturnMessage");
258 ServiceAssert.AssertMessagePartDescription (
259 "arg", "http://tempuri.org/", 0, false,
260 ProtectionLevel.None, typeof (Message), md.Body.Parts [0], "ReturnMessage input");
264 [ExpectedException (typeof (InvalidOperationException))]
265 public void GetContractInvalidAsync ()
267 ContractDescription.GetContract (typeof (IInvalidAsync));
271 // IMetadataExchange contains async patterns.
272 public void GetContractIMetadataExchange ()
274 ContractDescription cd = ContractDescription.GetContract (typeof (IMetadataExchange));
275 OperationDescription od = cd.Operations [0];
276 Assert.AreEqual (2, od.Messages.Count, "premise: message count");
277 foreach (MessageDescription md in od.Messages) {
278 if (md.Direction == MessageDirection.Input) {
279 Assert.AreEqual ("http://schemas.xmlsoap.org/ws/2004/09/transfer/Get", md.Action, "#1-1");
280 Assert.AreEqual (1, md.Body.Parts.Count, "#1-2");
281 Assert.IsNull (md.Body.ReturnValue, "#1-3");
282 Assert.AreEqual (typeof (Message), md.Body.Parts [0].Type, "#1-4");
284 Assert.AreEqual ("http://schemas.xmlsoap.org/ws/2004/09/transfer/GetResponse", md.Action, "#2-1");
285 Assert.AreEqual (0, md.Body.Parts.Count, "#2-2");
286 Assert.IsNotNull (md.Body.ReturnValue, "#2-3");
287 Assert.AreEqual (typeof (Message), md.Body.ReturnValue.Type, "#2-4");
293 // enable it if we want to become a compatibility kid. It has
294 // no ServiceContract, thus it should not be accepted. But
295 // there is an abuse of ChannelFactory<IRequestChannel> in
296 // MSDN documentations and probably examples.
297 [Category ("NotWorking")]
298 public void GetContractIRequestChannel ()
300 ContractDescription cd = ContractDescription.GetContract (typeof (IRequestChannel));
301 Assert.AreEqual (typeof (IRequestChannel), cd.ContractType, "#_1");
302 Assert.AreEqual ("IRequestChannel", cd.Name, "#_2");
303 Assert.AreEqual ("http://schemas.microsoft.com/2005/07/ServiceModel", cd.Namespace, "#_3");
304 Assert.AreEqual (false, cd.HasProtectionLevel, "#_4");
305 Assert.AreEqual (SessionMode.NotAllowed, cd.SessionMode, "#_5");
306 Assert.AreEqual (0, cd.Behaviors.Count, "#_6");
307 Assert.AreEqual (1, cd.Operations.Count, "#_7");
308 OperationDescription od = cd.Operations [0];
309 Assert.IsNull (od.SyncMethod, "#_8");
310 Assert.IsNull (od.BeginMethod, "#_9");
311 Assert.IsNull (od.EndMethod, "#_10");
312 Assert.AreEqual (false, od.IsOneWay, "#_11");
313 Assert.AreEqual (false, od.HasProtectionLevel, "#_12");
314 Assert.AreEqual ("Request", od.Name, "#_13");
315 Assert.AreEqual (true, od.IsInitiating, "#_14");
316 Assert.AreEqual (0, od.Behaviors.Count, "#_15");
317 Assert.AreEqual (2, od.Messages.Count, "#_16");
318 foreach (MessageDescription md in od.Messages) {
319 if (md.Direction == MessageDirection.Output) {
320 Assert.AreEqual ("*", md.Action, "#_17");
321 Assert.AreEqual (false, md.HasProtectionLevel, "#_18");
322 Assert.AreEqual (0, md.Headers.Count, "#_19");
323 Assert.AreEqual (0, md.Properties.Count, "#_20");
324 Assert.IsNull (md.MessageType, "#_21");
325 MessageBodyDescription mb = md.Body;
326 Assert.AreEqual (null, mb.WrapperName, "#_22");
327 Assert.AreEqual (null, mb.WrapperNamespace, "#_23");
328 Assert.IsNull (mb.ReturnValue, "#_24");
329 Assert.AreEqual (0, mb.Parts.Count, "#_25");
331 Assert.AreEqual ("*", md.Action, "#_17_");
332 Assert.AreEqual (false, md.HasProtectionLevel, "#_18_");
333 Assert.AreEqual (0, md.Headers.Count, "#_19_");
334 Assert.AreEqual (0, md.Properties.Count, "#_20_");
335 Assert.IsNull (md.MessageType, "#_21_");
336 MessageBodyDescription mb = md.Body;
337 Assert.AreEqual (null, mb.WrapperName, "#_22_");
338 Assert.AreEqual (null, mb.WrapperNamespace, "#_23_");
339 Assert.IsNull (mb.ReturnValue, "#_24_");
340 Assert.AreEqual (0, mb.Parts.Count, "#_25_");
346 [ExpectedException (typeof (InvalidOperationException))]
347 public void WrongAsyncEndContract ()
349 ContractDescription.GetContract (typeof (IWrongAsyncEndContract));
353 public void AsyncContract1 ()
355 ContractDescription cd =
356 ContractDescription.GetContract (typeof (IAsyncContract1));
357 Assert.AreEqual (1, cd.Operations.Count);
358 OperationDescription od = cd.Operations [0];
359 Assert.AreEqual ("Sum", od.Name, "#1");
360 Assert.IsNotNull (od.BeginMethod, "#2");
361 Assert.IsNotNull (od.EndMethod, "#3");
365 [ExpectedException (typeof (InvalidOperationException))]
366 public void DuplicateOperationNames ()
368 ContractDescription.GetContract (typeof (IDuplicateOperationNames));
372 [ExpectedException (typeof (InvalidOperationException))]
373 public void AsyncMethodNameDoesNotStartWithBegin ()
375 ContractDescription.GetContract (typeof (IAsyncMethodNameDoesNotStartWithBegin));
379 [ExpectedException (typeof (InvalidOperationException))]
380 public void AsyncNameDoesNotStartWithBeginButExplicitName ()
382 // it is still invalid ...
383 ContractDescription.GetContract (typeof (IAsyncNameDoesNotStartWithBeginButExplicitName));
387 public void MessageBodyMemberIsNotInferred ()
389 ContractDescription cd = ContractDescription.GetContract (typeof (MessageBodyMemberIsNotInferredService));
390 OperationDescription od = cd.Operations [0];
391 MessageDescription md = od.Messages [0];
392 Assert.AreEqual (0, md.Body.Parts.Count);
396 public void TestContractFromObject () {
397 ContractDescription cd = ContractDescription.GetContract (typeof (Foo));
398 ServiceAssert.AssertContractDescription (typeof (IFoo).Name, "http://tempuri.org/", SessionMode.Allowed, typeof (IFoo), null, cd, "#1");
399 Assert.AreEqual (cd.Operations.Count, 2);
400 OperationBehaviorAttribute op = cd.Operations.Find ("HeyHey").Behaviors.Find<OperationBehaviorAttribute> ();
401 Assert.IsNotNull (op);
403 op.ReleaseInstanceMode,
404 ReleaseInstanceMode.None, "#2");
406 cd = ContractDescription.GetContract (typeof (IFoo), typeof (Foo));
407 ServiceAssert.AssertContractDescription (typeof (IFoo).Name, "http://tempuri.org/", SessionMode.Allowed, typeof (IFoo), null, cd, "#3");
408 Assert.AreEqual (cd.Operations.Count, 2, "#4");
410 cd.Operations.Find ("HeyHey").Behaviors.Find<OperationBehaviorAttribute> ().ReleaseInstanceMode,
411 ReleaseInstanceMode.AfterCall, "#5");
415 public void GetDerivedContract ()
417 var cd = ContractDescription.GetContract (typeof (IFoo3));
418 Assert.AreEqual (typeof (IFoo3), cd.ContractType, "#1");
419 Assert.AreEqual (3, cd.Operations.Count, "#2");
420 cd = ContractDescription.GetContract (typeof (Foo3));
421 Assert.AreEqual (typeof (IFoo3), cd.ContractType, "#3");
422 Assert.AreEqual (3, cd.Operations.Count, "#4");
426 public void MultipleContractsInTypeHierarchy ()
428 ContractDescription.GetContract (typeof (DuplicateCheckClassWrapper.ServiceInterface));
430 var host = new ServiceHost (typeof (DuplicateCheckClassWrapper.DummyService)); // fine in MS, fails in Mono with "A contract cannot have two operations that have the identical names and different set of parameters"
434 public void GetInheritedContracts ()
436 var cd = ContractDescription.GetContract (typeof (IService));
437 var ccd = cd.GetInheritedContracts ();
438 Assert.AreEqual (1, ccd.Count, "#1");
439 Assert.AreEqual (typeof (IServiceBase), ccd [0].ContractType, "#2");
443 public void InheritedContractAndNamespaces ()
445 var cd = ContractDescription.GetContract (typeof (IService));
446 Assert.IsTrue (cd.Operations.Any (od => od.Messages.Any (md => md.Action == "http://tempuri.org/IServiceBase/Say")), "#1"); // inherited
447 Assert.IsTrue (cd.Operations.Any (od => od.SyncMethod == typeof (IService).GetMethod ("Join") && od.Messages.Any (md => md.Action == "http://tempuri.org/IService/Join")), "#2"); // self
448 Assert.IsTrue (cd.Operations.Any (od => od.SyncMethod == typeof (IService2).GetMethod ("Join") && od.Messages.Any (md => md.Action == "http://tempuri.org/IService/Join")), "#3"); // callback
451 // It is for testing attribute search in interfaces.
452 public class Foo : IFoo
454 public string HeyDude (string msg, string msg2)
459 [OperationBehavior (ReleaseInstanceMode = ReleaseInstanceMode.AfterCall)]
460 public void HeyHey (out string out1, ref string ref1)
466 // It inherits both IFoo and IBar, thus cannot be a contract.
467 public class FooBar : IFoo, IBar
469 public string HeyDude (string msg, string msg2)
474 public void HeyHey (out string out1, ref string ref1)
479 public void OpenBar () {}
483 public interface IFoo
486 string HeyDude (string msg, string msg2);
489 void HeyHey (out string out1, ref string ref1);
493 public interface IFoo2
495 // FIXME: it does not pass yet
497 OregoMessage Nanoda (OregoMessage msg);
499 // FIXME: it does not pass yet
501 OregoMessage Nanoda2 (OregoMessage msg1, OregoMessage msg2);
503 // FIXME: it does not pass yet
505 Mona NewMona (Mona source);
509 public interface IFoo3 : IFoo
512 string HeyMan (string msg, string msg2);
515 public class Foo3 : Foo, IFoo3
517 public string HeyMan (string msg, string msg2)
524 public interface IBar
531 public class OregoMessage
534 public string Neutral;
537 [MessageBodyMember] // it should be ignored ...
538 public string Setter { set { } }
539 public string NonMember;
544 public string OmaeMona;
545 public string OreMona;
549 public interface INoOperation
554 public interface IMessageParameter
557 Message ReturnMessage (Message arg);
561 public interface IInvalidAsync
564 Message ReturnMessage (Message arg);
566 [OperationContract (AsyncPattern = true)]
567 IAsyncResult BeginReturnMessage (Message arg, AsyncCallback callback, object state);
569 // and no EndReturnMessage().
573 public interface IWrongAsyncEndContract
576 int Sum (int a, int b);
578 [OperationContract (AsyncPattern = true)]
579 IAsyncResult BeginSum (int a, int b, AsyncCallback cb, object state);
581 // this OperationContractAttribute is not allowed.
582 [OperationContract (AsyncPattern = true)]
583 int EndSum (IAsyncResult result);
587 public interface IAsyncContract1
590 int Sum (int a, int b);
592 [OperationContract (AsyncPattern = true)]
593 IAsyncResult BeginSum (int a, int b, AsyncCallback cb, object state);
595 int EndSum (IAsyncResult result);
599 public interface IAsyncMethodNameDoesNotStartWithBegin
602 int Sum (int a, int b);
604 [OperationContract (AsyncPattern = true)]
605 IAsyncResult StartSum (int a, int b, AsyncCallback cb, object state);
607 int EndSum (IAsyncResult result);
611 public interface IAsyncNameDoesNotStartWithBeginButExplicitName
614 int Sum (int a, int b);
616 [OperationContract (Name = "Sum", AsyncPattern = true)]
617 IAsyncResult StartSum (int a, int b, AsyncCallback cb, object state);
619 int EndSum (IAsyncResult result);
623 public interface IDuplicateOperationNames
626 string Echo (string s);
629 string Echo (string s1, string s2);
633 public interface IFooMsgParams
635 [OperationContract (Name = "MyFoo")]
636 [return: MessageParameter (Name = "MyResult")]
637 string Foo ([MessageParameter (Name = "MyParam")] string param);
641 public class MessageBodyMemberIsNotInferredService
644 public void Echo (MessageBodyMemberIsNotInferredContract msg)
650 public class MessageBodyMemberIsNotInferredContract
659 public class DuplicateCheckClassWrapper
663 internal interface ServiceInterface : Foo
668 internal interface Foo : Bar
670 [OperationContract] void Foo();
674 internal interface Bar
676 [OperationContract] void FooBar();
679 internal class DummyService : ServiceInterface
681 public void FooBar() { }
683 public void Foo() { }
688 public interface IServiceBase
690 [OperationContract (IsOneWay = true)]
691 void Say (string word);
694 [ServiceContract (CallbackContract = typeof (IService2))]
695 public interface IService : IServiceBase
702 public interface IServiceBase2
704 [OperationContract (IsOneWay = true)]
705 void Say (string word);
709 public interface IService2 : IServiceBase2