I am working on setting up a configurable SSH stub shell, similar to MockSSH, and I want to do it test-driven.
I am running into issues testing when I use conch.recvline.HistoricRecvLine instead of the base twisted.internet.protocol.Protocol
# test.py
from twisted.trial import unittest
from twisted.test.proto_helpers import StringTransportenter code here
class ShellTest(unittest.TestCase):
def setUp(self):
shell_factory = StubShell.ShellFactory()
self.shell = shell_factory.buildProtocol(('127.0.0.1', 0))
self.transport = StringTransport()
self.shell.makeConnection(self.transport)
def test_echo(self):
self.shell.lineReceived('irrelevant')
self.assertEqual(self.transport.value(), 'something')
# shell.py
from twisted.internet import protocol
class ShellProtocol(HistoricRecvLine):
def connectionMade(self):
HistoricRecvLine.connectionMade(self)
def lineReceived(self, line):
self.terminal.write('line received')
class ShellFactory(protocol.Factory):
protocol = ShellProtocol
This Works just as expected. However, when I make the change:
class ShellProtocol(HistoricRecvLine):
I get the error:
exceptions.AttributeError: StringTransport instance has no attribute 'LEFT_ARROW'
NEXT STEP: Thanks to Glyph's help, i've gotten a bit further, still trying to get a bare-minimum test set up to ensure that I am setting up HistoricRecvLine protocol correctly. I stole some code from test_recvline.py, which still seems a bit magical to me, especially setting sp.factory = self(unittest.Testcase). I have been trying to hack it down to the minimum to get this test to pass.
class ShellTestConch(unittest.TestCase):
def setUp(self):
self.sp = insults.ServerProtocol()
self.transport = StringTransport()
self.my_shell = StubShell.ShellProtocol()
self.sp.protocolFactory = lambda: self.my_shell
self.sp.factory = self
self.sp.makeConnection(self.transport)
which gets me closer to expected output, however now I see that the Terminal is mangling the output a big, which should be expected.
twisted.trial.unittest.FailTest: '\x1bc>>> \x1b[4hline received' != 'line received'
For TDD purposes, I'm not sure if I should just accept that '\x1bc>>>...' version of the output (at least until I break it by setting a custom prompt) or attempt to overwrite the shell prompt to get clean output.
Excellent question; thanks for asking (and thanks for using Twisted, and doing TDD :-)).
HistoricRecvLineis aTerminalProtocol, which providesITerminalProtocol.ITerminalProtocol.makeConnectionmust be called with a provider of anITerminalTransport. On the other hand, your unit test is calling yourShellProtocol.makeConnectionwith an instance ofStringTransport, which provides onlyIConsumer,IPushProducer, andITransport.Since
ShellProtocol.makeConnectionexpects anITerminalTransport, it expects that it can call all of its methods. Unfortunately,LEFT_ARROWis not formally documented on this interface, which is an oversight, but the providers of this interface within Twisted also provide that attribute.What you want to do is to wrap a
ServerProtocolaround yourITerminalProtocol, which in the process will serve as yourITerminalTransport. You can see a simple example of this composition intwisted.conch.stdio.