Commit 3ae573c1 authored by Jon Skeet's avatar Jon Skeet

Fleshed out service interfaces, and wrote the simpler service tests. Mocking…

Fleshed out service interfaces, and wrote the simpler service tests. Mocking tests still to be done.
parent 1e42fdde
using System; using System;
using System.Collections.Generic; using Google.ProtocolBuffers.Descriptors;
using System.Text; using Google.ProtocolBuffers.TestProtos;
using NUnit.Framework; using NUnit.Framework;
namespace Google.ProtocolBuffers { namespace Google.ProtocolBuffers {
/// <summary>
/// Tests for generated service classes.
/// TODO(jonskeet): Convert the mocking tests using Rhino.Mocks.
/// </summary>
[TestFixture] [TestFixture]
public class ServiceTest { public class ServiceTest {
private static readonly MethodDescriptor FooDescriptor = TestService.Descriptor.Methods[0];
private static readonly MethodDescriptor BarDescriptor = TestService.Descriptor.Methods[1];
[Test]
public void GetRequestPrototype() {
TestService mockService = new TestServiceImpl();
Assert.AreSame(mockService.GetRequestPrototype(FooDescriptor), FooRequest.DefaultInstance);
Assert.AreSame(mockService.GetRequestPrototype(BarDescriptor), BarRequest.DefaultInstance);
}
[Test]
public void GetResponsePrototype() {
TestService mockService = new TestServiceImpl();
Assert.AreSame(mockService.GetResponsePrototype(FooDescriptor), FooResponse.DefaultInstance);
Assert.AreSame(mockService.GetResponsePrototype(BarDescriptor), BarResponse.DefaultInstance);
}
class TestServiceImpl : TestService {
public override void Foo(IRpcController controller, FooRequest request, Action<FooResponse> done) {
throw new System.NotImplementedException();
}
public override void Bar(IRpcController controller, BarRequest request, Action<BarResponse> done) {
throw new System.NotImplementedException();
}
}
} }
} }
using System; using System;
using System.Collections.Generic;
using System.Text;
using Google.ProtocolBuffers.Descriptors; using Google.ProtocolBuffers.Descriptors;
namespace Google.ProtocolBuffers { namespace Google.ProtocolBuffers {
/// <summary> /// <summary>
/// TODO(jonskeet): Do this properly. /// Interface for an RPC channel. A channel represents a communication line to
/// a service (IService implementation) which can be used to call that service's
/// methods. The service may be running on another machine. Normally, you should
/// not call an IRpcChannel directly, but instead construct a stub wrapping it.
/// Generated service classes contain a CreateStub method for precisely this purpose.
/// </summary> /// </summary>
public interface IRpcChannel { public interface IRpcChannel {
void CallMethod<T>(MethodDescriptor method, IRpcController controller, /// <summary>
IMessage request, IMessage responsePrototype, Action<T> done); /// Calls the given method of the remote service. This method is similar
/// to <see cref="IService.CallMethod" /> with one important difference: the
/// caller decides the types of the IMessage objects, not the implementation.
/// The request may be of any type as long as <c>request.Descriptor == method.InputType</c>.
/// The response passed to the callback will be of the same type as
/// <paramref name="responsePrototype"/> (which must be such that
/// <c>responsePrototype.Descriptor == method.OutputType</c>).
/// </summary>
void CallMethod(MethodDescriptor method, IRpcController controller,
IMessage request, IMessage responsePrototype, Action<IMessage> done);
} }
} }
using System; using System;
using System.Collections.Generic;
using System.Text;
namespace Google.ProtocolBuffers { namespace Google.ProtocolBuffers {
/// <summary>
/// Mediates a single method call. The primary purpose of the controller
/// is to provide a way to manipulate settings specific to the
/// RPC implementation and to find out about RPC-level errors.
///
/// The methods provided by this interface are intended to be a "least
/// common denominator" set of features which we expect all implementations to
/// support. Specific implementations may provide more advanced features,
/// (e.g. deadline propagation).
/// </summary>
public interface IRpcController { public interface IRpcController {
#region Client side calls
// These calls may be made from the client side only. Their results
// are undefined on the server side (may throw exceptions).
/// <summary>
/// Resets the controller to its initial state so that it may be reused in
/// a new call. This can be called from the client side only. It must not
/// be called while an RPC is in progress.
/// </summary>
void Reset();
/// <summary>
/// After a call has finished, returns true if the call failed. The possible
/// reasons for failure depend on the RPC implementation. Failed must
/// only be called on the client side, and must not be called before a call has
/// finished.
/// </summary>
bool Failed { get; }
/// <summary>
/// If Failed is true, ErrorText returns a human-readable description of the error.
/// </summary>
string ErrorText { get; }
/// <summary>
/// Advises the RPC system that the caller desires that the RPC call be
/// canceled. The RPC system may cancel it immediately, may wait awhile and
/// then cancel it, or may not even cancel the call at all. If the call is
/// canceled, the "done" callback will still be called and the RpcController
/// will indicate that the call failed at that time.
/// </summary>
void StartCancel();
#endregion
#region Server side calls
// These calls may be made from the server side only. Their results
// are undefined on the client side (may throw exceptions).
/// <summary>
/// Causes Failed to return true on the client side. <paramref name="reason"/>
/// will be incorporated into the message returned by ErrorText.
/// If you find you need to return machine-readable information about
/// failures, you should incorporate it into your response protocol buffer
/// and should *not* call SetFailed.
/// </summary>
void SetFailed(string reason);
/// <summary>
/// If true, indicates that the client canceled the RPC, so the server may as
/// well give up on replying to it. This method must be called on the server
/// side only. The server should still call the final "done" callback.
/// </summary>
bool isCanceled();
/// <summary>
/// Requests that the given callback be called when the RPC is canceled.
/// The parameter passed to the callback will always be null. The callback will
/// be called exactly once. If the RPC completes without being canceled, the
/// callback will be called after completion. If the RPC has already been canceled
/// when NotifyOnCancel is called, the callback will be called immediately.
///
/// NotifyOnCancel must be called no more than once per request. It must be
/// called on the server side only.
/// </summary>
/// <param name="callback"></param>
void NotifyOnCancel(Action<object> callback);
#endregion
} }
} }
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Text; using System.Text;
using Google.ProtocolBuffers.Descriptors;
namespace Google.ProtocolBuffers { namespace Google.ProtocolBuffers {
/// <summary>
/// Base interface for protocol-buffer-based RPC services. Services themselves
/// are abstract classes (implemented either by servers or as stubs) but they
/// implement this itnerface. The methods of this interface can be used to call
/// the methods of the service without knowing its exact type at compile time
/// (analagous to the IMessage interface).
/// </summary>
public interface IService { public interface IService {
// TODO(jonskeet): Fill this in /// <summary>
/// The ServiceDescriptor describing this service and its methods.
/// </summary>
ServiceDescriptor DescriptorForType { get; }
/// <summary>
/// Call a method of the service specified by MethodDescriptor. This is
/// normally implemented as a simple switch that calls the standard
/// definitions of the service's methods.
/// <para>
/// Preconditions
/// <list>
/// <item><c>method.Service == DescriptorForType</c></item>
/// <item>request is of the exact same class as the object returned by GetRequestPrototype(method)</item>
/// <item>controller is of the correct type for the RPC implementation being used by this service.
/// For stubs, the "correct type" depends on the IRpcChannel which the stub is using. Server-side
/// implementations are expected to accept whatever type of IRpcController the server-side RPC implementation
/// uses.</item>
/// </list>
/// </para>
/// <para>
/// Postconditions
/// <list>
/// <item><paramref name="done" /> will be called when the method is complete.
/// This may before CallMethod returns or it may be at some point in the future.</item>
/// <item>The parameter to <paramref name="done"/> is the response. It will be of the
/// exact same type as would be returned by <see cref="GetResponsePrototype"/>.</item>
/// <item>If the RPC failed, the parameter to <paramref name="done"/> will be null.
/// Further details about the failure can be found by querying <paramref name="controller"/>.</item>
/// </list>
/// </para>
/// </summary>
void CallMethod(MethodDescriptor method, IRpcController controller,
IMessage request, Action<IMessage> done);
/// <summary>
/// CallMethod requires that the request passed in is of a particular implementation
/// of IMessage. This method gets the default instance of this type of a given method.
/// You can then call WeakCreateBuilderForType to create a builder to build an object which
/// you can then pass to CallMethod.
/// </summary>
IMessage GetRequestPrototype(MethodDescriptor method);
/// <summary>
/// Like GetRequestPrototype, but returns a prototype of the response message.
/// This is generally not needed because the IService implementation contructs
/// the response message itself, but it may be useful in some cases to know ahead
/// of time what type of object will be returned.
/// </summary>
IMessage GetResponsePrototype(MethodDescriptor method);
} }
} }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment