Fleshed out service interfaces, and wrote the simpler service tests. Mocking tests still to be done.

pull/288/head
Jon Skeet 17 years ago
parent 1e42fdde2e
commit 3ae573c174
  1. 38
      csharp/ProtocolBuffers.Test/ServiceTest.cs
  2. 21
      csharp/ProtocolBuffers/IRpcChannel.cs
  3. 80
      csharp/ProtocolBuffers/IRpcController.cs
  4. 59
      csharp/ProtocolBuffers/IService.cs

@ -1,10 +1,44 @@
using System;
using System.Collections.Generic;
using System.Text;
using Google.ProtocolBuffers.Descriptors;
using Google.ProtocolBuffers.TestProtos;
using NUnit.Framework;
namespace Google.ProtocolBuffers {
/// <summary>
/// Tests for generated service classes.
/// TODO(jonskeet): Convert the mocking tests using Rhino.Mocks.
/// </summary>
[TestFixture]
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();
}
}
}
}

@ -1,14 +1,25 @@
using System;
using System.Collections.Generic;
using System.Text;
using Google.ProtocolBuffers.Descriptors;
namespace Google.ProtocolBuffers {
/// <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>
public interface IRpcChannel {
void CallMethod<T>(MethodDescriptor method, IRpcController controller,
IMessage request, IMessage responsePrototype, Action<T> done);
/// <summary>
/// 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);
}
}

@ -1,8 +1,84 @@
using System;
using System.Collections.Generic;
using System.Text;
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 {
#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
}
}

@ -1,9 +1,66 @@
using System;
using System.Collections.Generic;
using System.Text;
using Google.ProtocolBuffers.Descriptors;
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 {
// 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);
}
}

Loading…
Cancel
Save