Newer
Older
Jean-Paul Calderone
committed
# Copyright 2019 PrivateStorage.io, LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""
Testing helpers related to Foolscap.
"""
Jean-Paul Calderone
committed
import attr
from allmydata.interfaces import RIStorageServer
from foolscap.api import Any, Copyable, Referenceable, RemoteInterface
from foolscap.copyable import CopyableSlicer, ICopyable
from twisted.internet.defer import fail, succeed
from zope.interface import implementer
Jean-Paul Calderone
committed
Jean-Paul Calderone
committed
class RIStub(RemoteInterface):
pass
class RIEcho(RemoteInterface):
def echo(argument=Any()):
return Any()
Jean-Paul Calderone
committed
@implementer(RIStorageServer)
class StubStorageServer(object):
def set_implicit_bucket_lease_renewal(self, enabled):
pass
Jean-Paul Calderone
committed
def set_implicit_slot_lease_renewal(self, enabled):
pass
Jean-Paul Calderone
committed
Jean-Paul Calderone
committed
def get_anonymous_storage_server():
return StubStorageServer()
class BrokenCopyable(Copyable):
"""
I don't have a ``typeToCopy`` so I can't be serialized.
"""
@implementer(RIEcho)
class Echoer(Referenceable):
def remote_echo(self, argument):
return argument
Jean-Paul Calderone
committed
@attr.s
class DummyReferenceable(object):
_interface = attr.ib()
def getInterface(self):
return self._interface
def doRemoteCall(self, *a, **kw):
return None
Jean-Paul Calderone
committed
@attr.s
class LocalTracker(object):
"""
Pretend to be a tracker for a ``LocalRemote``.
"""
Jean-Paul Calderone
committed
interface = attr.ib()
interfaceName = attr.ib(default=None)
def __attrs_post_init__(self):
self.interfaceName = self.interface.__remote_name__
def getURL(self):
return b"pb://abcd@127.0.0.1:12345/efgh"
@attr.s
class LocalRemote(object):
"""
Adapt a referenceable to behave as if it were a remote reference instead.
This is only a partial implementation of ``IRemoteReference`` so it
doesn't declare the interface.
``foolscap.referenceable.LocalReferenceable`` is in many ways a better
adapter between these interfaces but it also uses ``eventually`` which
complicates matters immensely for testing.
:ivar foolscap.ipb.IReferenceable _referenceable: The object to which this
provides a simulated remote interface.
"""
Jean-Paul Calderone
committed
_referenceable = attr.ib()
check_args = attr.ib(default=True)
tracker = attr.ib(default=None)
def __attrs_post_init__(self):
self.tracker = LocalTracker(
self._referenceable.getInterface(),
)
def callRemote(self, methname, *args, **kwargs):
"""
Call the given method on the wrapped object, passing the given arguments.
Arguments and return are checked for conformance to the remote
interface but they are not actually serialized.
Jean-Paul Calderone
committed
:return Deferred: The result of the call on the wrapped object.
"""
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
try:
schema = self._referenceable.getInterface()[methname]
if self.check_args:
schema.checkAllArgs(args, kwargs, inbound=True)
_check_copyables(list(args) + kwargs.values())
result = self._referenceable.doRemoteCall(
methname,
args,
kwargs,
)
schema.checkResults(result, inbound=False)
_check_copyables([result])
return succeed(result)
except:
return fail()
def _check_copyables(copyables):
"""
Check each object to see if it is a copyable and if it is make sure it can
be sliced.
"""
for obj in copyables:
if ICopyable.providedBy(obj):
list(CopyableSlicer(obj).slice(False, None))
elif isinstance(obj, dict):
_check_copyables(obj.values())
elif isinstance(obj, list):
_check_copyables(obj)