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
452 public void MessageContractAttributes ()
454 var cd = ContractDescription.GetContract (typeof (IFoo2));
455 var od = cd.Operations.First (o => o.Name == "Nanoda");
456 var md = od.Messages.First (m => m.Direction == MessageDirection.Input);
457 Assert.AreEqual (typeof (OregoMessage), md.MessageType, "message type");
458 Assert.AreEqual ("http://tempuri.org/IFoo2/Nanoda", md.Action, "action");
459 Assert.AreEqual (1, md.Headers.Count, "headers");
460 Assert.AreEqual (3, md.Body.Parts.Count, "body parts");
461 Assert.AreEqual (0, md.Properties.Count, "properties");
464 // .NET complains: The operation Nanoda2 either has a parameter or a return type that is attributed with MessageContractAttribute. In order to represent the request message using a Message Contract, the operation must have a single parameter attributed with MessageContractAttribute. In order to represent the response message using a Message Contract, the operation's return value must be a type that is attributed with MessageContractAttribute and the operation may not have any out or ref parameters.
466 [ExpectedException (typeof (InvalidOperationException))]
467 public void MessageContractAttributes2 ()
469 ContractDescription.GetContract (typeof (IFoo2_2));
473 public void MessageContractAttributes3 ()
475 ContractDescription.GetContract (typeof (IFoo2_3));
479 public void MessageContractAttributes4 ()
481 ContractDescription.GetContract (typeof (IFoo2_4));
485 public void MessageContractAttributes5 ()
487 ContractDescription.GetContract (typeof (IFoo2_5));
491 public void MessageContractAttributes6 ()
493 ContractDescription.GetContract (typeof (IFoo2_6));
496 // It is for testing attribute search in interfaces.
497 public class Foo : IFoo
499 public string HeyDude (string msg, string msg2)
504 [OperationBehavior (ReleaseInstanceMode = ReleaseInstanceMode.AfterCall)]
505 public void HeyHey (out string out1, ref string ref1)
511 // It inherits both IFoo and IBar, thus cannot be a contract.
512 public class FooBar : IFoo, IBar
514 public string HeyDude (string msg, string msg2)
519 public void HeyHey (out string out1, ref string ref1)
524 public void OpenBar () {}
528 public interface IFoo
531 string HeyDude (string msg, string msg2);
534 void HeyHey (out string out1, ref string ref1);
538 public interface IFoo2
540 // FIXME: it does not pass yet
542 OregoMessage Nanoda (OregoMessage msg);
544 // FIXME: it does not pass yet
546 Mona NewMona (Mona source);
550 public interface IFoo2_2
552 [OperationContract] // wrong operation contract, must have only one parameter with MessageContractAttribute
553 OregoMessage Nanoda2 (OregoMessage msg1, OregoMessage msg2);
557 public interface IFoo2_3
560 string Nanoda2 (OregoMessage msg1);
564 public interface IFoo2_4
567 OregoMessage Nanoda2 (string s, string s2);
571 public interface IFoo2_5
574 Message Nanoda2 (OregoMessage msg1);
578 public interface IFoo2_6
581 OregoMessage Nanoda2 (Message msg1);
585 public interface IFoo3 : IFoo
588 string HeyMan (string msg, string msg2);
591 public class Foo3 : Foo, IFoo3
593 public string HeyMan (string msg, string msg2)
600 public interface IBar
607 public class OregoMessage
612 public string Neutral;
615 [MessageBodyMember] // it should be ignored ...
616 public string Setter { set { } }
617 public string NonMember;
622 public string OmaeMona;
623 public string OreMona;
627 public interface INoOperation
632 public interface IMessageParameter
635 Message ReturnMessage (Message arg);
639 public interface IInvalidAsync
642 Message ReturnMessage (Message arg);
644 [OperationContract (AsyncPattern = true)]
645 IAsyncResult BeginReturnMessage (Message arg, AsyncCallback callback, object state);
647 // and no EndReturnMessage().
651 public interface IWrongAsyncEndContract
654 int Sum (int a, int b);
656 [OperationContract (AsyncPattern = true)]
657 IAsyncResult BeginSum (int a, int b, AsyncCallback cb, object state);
659 // this OperationContractAttribute is not allowed.
660 [OperationContract (AsyncPattern = true)]
661 int EndSum (IAsyncResult result);
665 public interface IAsyncContract1
668 int Sum (int a, int b);
670 [OperationContract (AsyncPattern = true)]
671 IAsyncResult BeginSum (int a, int b, AsyncCallback cb, object state);
673 int EndSum (IAsyncResult result);
677 public interface IAsyncMethodNameDoesNotStartWithBegin
680 int Sum (int a, int b);
682 [OperationContract (AsyncPattern = true)]
683 IAsyncResult StartSum (int a, int b, AsyncCallback cb, object state);
685 int EndSum (IAsyncResult result);
689 public interface IAsyncNameDoesNotStartWithBeginButExplicitName
692 int Sum (int a, int b);
694 [OperationContract (Name = "Sum", AsyncPattern = true)]
695 IAsyncResult StartSum (int a, int b, AsyncCallback cb, object state);
697 int EndSum (IAsyncResult result);
701 public interface IDuplicateOperationNames
704 string Echo (string s);
707 string Echo (string s1, string s2);
711 public interface IFooMsgParams
713 [OperationContract (Name = "MyFoo")]
714 [return: MessageParameter (Name = "MyResult")]
715 string Foo ([MessageParameter (Name = "MyParam")] string param);
719 public class MessageBodyMemberIsNotInferredService
722 public void Echo (MessageBodyMemberIsNotInferredContract msg)
728 public class MessageBodyMemberIsNotInferredContract
737 public class DuplicateCheckClassWrapper
741 internal interface ServiceInterface : Foo
746 internal interface Foo : Bar
748 [OperationContract] void Foo();
752 internal interface Bar
754 [OperationContract] void FooBar();
757 internal class DummyService : ServiceInterface
759 public void FooBar() { }
761 public void Foo() { }
766 public interface IServiceBase
768 [OperationContract (IsOneWay = true)]
769 void Say (string word);
772 [ServiceContract (CallbackContract = typeof (IService2))]
773 public interface IService : IServiceBase
780 public interface IServiceBase2
782 [OperationContract (IsOneWay = true)]
783 void Say (string word);
787 public interface IService2 : IServiceBase2