Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(8)

Side by Side Diff: third_party/twisted_8_1/twisted/conch/test/test_cftp.py

Issue 12261012: Remove third_party/twisted_8_1 (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/tools/build
Patch Set: Created 7 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
1 # -*- test-case-name: twisted.conch.test.test_cftp -*-
2 # Copyright (c) 2001-2008 Twisted Matrix Laboratories.
3 # See LICENSE file for details.
4
5 import sys, os
6
7 try:
8 import Crypto
9 except ImportError:
10 Crypto = None
11
12 try:
13 from twisted.conch import unix
14 from twisted.conch.scripts import cftp
15 from twisted.conch.client import connect, default, options
16 from twisted.conch.test.test_filetransfer import FileTransferForTestAvatar
17 except ImportError:
18 unix = None
19 try:
20 del sys.modules['twisted.conch.unix'] # remove the bad import
21 except KeyError:
22 # In Python 2.4, the bad import has already been cleaned up for us.
23 pass
24
25 from twisted.cred import portal
26 from twisted.internet import reactor, protocol, interfaces, defer, error
27 from twisted.internet.utils import getProcessOutputAndValue
28 from twisted.python import log
29
30 from twisted.conch.test import test_ssh, test_conch
31 from twisted.conch.test.test_filetransfer import SFTPTestBase
32 from twisted.conch.test.test_filetransfer import FileTransferTestAvatar
33
34
35 class FileTransferTestRealm:
36 def __init__(self, testDir):
37 self.testDir = testDir
38
39 def requestAvatar(self, avatarID, mind, *interfaces):
40 a = FileTransferTestAvatar(self.testDir)
41 return interfaces[0], a, lambda: None
42
43
44 class SFTPTestProcess(protocol.ProcessProtocol):
45 """
46 Protocol for testing cftp. Provides an interface between Python (where all
47 the tests are) and the cftp client process (which does the work that is
48 being tested).
49 """
50
51 def __init__(self, onOutReceived):
52 """
53 @param onOutReceived: A L{Deferred} to be fired as soon as data is
54 received from stdout.
55 """
56 self.clearBuffer()
57 self.onOutReceived = onOutReceived
58 self.onProcessEnd = None
59 self._expectingCommand = None
60 self._processEnded = False
61
62 def clearBuffer(self):
63 """
64 Clear any buffered data received from stdout. Should be private.
65 """
66 self.buffer = ''
67 self._linesReceived = []
68 self._lineBuffer = ''
69
70 def outReceived(self, data):
71 """
72 Called by Twisted when the cftp client prints data to stdout.
73 """
74 log.msg('got %s' % data)
75 lines = (self._lineBuffer + data).split('\n')
76 self._lineBuffer = lines.pop(-1)
77 self._linesReceived.extend(lines)
78 # XXX - not strictly correct.
79 # We really want onOutReceived to fire after the first 'cftp>' prompt
80 # has been received. (See use in TestOurServerCmdLineClient.setUp)
81 if self.onOutReceived is not None:
82 d, self.onOutReceived = self.onOutReceived, None
83 d.callback(data)
84 self.buffer += data
85 self._checkForCommand()
86
87 def _checkForCommand(self):
88 prompt = 'cftp> '
89 if self._expectingCommand and self._lineBuffer == prompt:
90 buf = '\n'.join(self._linesReceived)
91 if buf.startswith(prompt):
92 buf = buf[len(prompt):]
93 self.clearBuffer()
94 d, self._expectingCommand = self._expectingCommand, None
95 d.callback(buf)
96
97 def errReceived(self, data):
98 """
99 Called by Twisted when the cftp client prints data to stderr.
100 """
101 log.msg('err: %s' % data)
102
103 def getBuffer(self):
104 """
105 Return the contents of the buffer of data received from stdout.
106 """
107 return self.buffer
108
109 def runCommand(self, command):
110 """
111 Issue the given command via the cftp client. Return a C{Deferred} that
112 fires when the server returns a result. Note that the C{Deferred} will
113 callback even if the server returns some kind of error.
114
115 @param command: A string containing an sftp command.
116
117 @return: A C{Deferred} that fires when the sftp server returns a
118 result. The payload is the server's response string.
119 """
120 self._expectingCommand = defer.Deferred()
121 self.clearBuffer()
122 self.transport.write(command + '\n')
123 return self._expectingCommand
124
125 def runScript(self, commands):
126 """
127 Run each command in sequence and return a Deferred that fires when all
128 commands are completed.
129
130 @param commands: A list of strings containing sftp commands.
131
132 @return: A C{Deferred} that fires when all commands are completed. The
133 payload is a list of response strings from the server, in the same
134 order as the commands.
135 """
136 sem = defer.DeferredSemaphore(1)
137 dl = [sem.run(self.runCommand, command) for command in commands]
138 return defer.gatherResults(dl)
139
140 def killProcess(self):
141 """
142 Kill the process if it is still running.
143
144 If the process is still running, sends a KILL signal to the transport
145 and returns a C{Deferred} which fires when L{processEnded} is called.
146
147 @return: a C{Deferred}.
148 """
149 if self._processEnded:
150 return defer.succeed(None)
151 self.onProcessEnd = defer.Deferred()
152 self.transport.signalProcess('KILL')
153 return self.onProcessEnd
154
155 def processEnded(self, reason):
156 """
157 Called by Twisted when the cftp client process ends.
158 """
159 self._processEnded = True
160 if self.onProcessEnd:
161 d, self.onProcessEnd = self.onProcessEnd, None
162 d.callback(None)
163
164
165 class CFTPClientTestBase(SFTPTestBase):
166 def setUp(self):
167 f = open('dsa_test.pub','w')
168 f.write(test_ssh.publicDSA_openssh)
169 f.close()
170 f = open('dsa_test','w')
171 f.write(test_ssh.privateDSA_openssh)
172 f.close()
173 os.chmod('dsa_test', 33152)
174 f = open('kh_test','w')
175 f.write('127.0.0.1 ' + test_ssh.publicRSA_openssh)
176 f.close()
177 return SFTPTestBase.setUp(self)
178
179 def startServer(self):
180 realm = FileTransferTestRealm(self.testDir)
181 p = portal.Portal(realm)
182 p.registerChecker(test_ssh.ConchTestPublicKeyChecker())
183 fac = test_ssh.ConchTestServerFactory()
184 fac.portal = p
185 self.server = reactor.listenTCP(0, fac, interface="127.0.0.1")
186
187 def stopServer(self):
188 if not hasattr(self.server.factory, 'proto'):
189 return self._cbStopServer(None)
190 self.server.factory.proto.expectedLoseConnection = 1
191 d = defer.maybeDeferred(
192 self.server.factory.proto.transport.loseConnection)
193 d.addCallback(self._cbStopServer)
194 return d
195
196 def _cbStopServer(self, ignored):
197 return defer.maybeDeferred(self.server.stopListening)
198
199 def tearDown(self):
200 for f in ['dsa_test.pub', 'dsa_test', 'kh_test']:
201 try:
202 os.remove(f)
203 except:
204 pass
205 return SFTPTestBase.tearDown(self)
206
207
208
209 class TestOurServerCmdLineClient(CFTPClientTestBase):
210
211 def setUp(self):
212 CFTPClientTestBase.setUp(self)
213
214 self.startServer()
215 cmds = ('-p %i -l testuser '
216 '--known-hosts kh_test '
217 '--user-authentications publickey '
218 '--host-key-algorithms ssh-rsa '
219 '-K direct '
220 '-i dsa_test '
221 '-a --nocache '
222 '-v '
223 '127.0.0.1')
224 port = self.server.getHost().port
225 cmds = test_conch._makeArgs((cmds % port).split(), mod='cftp')
226 log.msg('running %s %s' % (sys.executable, cmds))
227 d = defer.Deferred()
228 self.processProtocol = SFTPTestProcess(d)
229 d.addCallback(lambda _: self.processProtocol.clearBuffer())
230 env = os.environ.copy()
231 env['PYTHONPATH'] = os.pathsep.join(sys.path)
232 reactor.spawnProcess(self.processProtocol, sys.executable, cmds,
233 env=env)
234 return d
235
236 def tearDown(self):
237 d = self.stopServer()
238 d.addCallback(lambda _: self.processProtocol.killProcess())
239 return d
240
241 def _killProcess(self, ignored):
242 try:
243 self.processProtocol.transport.signalProcess('KILL')
244 except error.ProcessExitedAlready:
245 pass
246
247 def runCommand(self, command):
248 """
249 Run the given command with the cftp client. Return a C{Deferred} that
250 fires when the command is complete. Payload is the server's output for
251 that command.
252 """
253 return self.processProtocol.runCommand(command)
254
255 def runScript(self, *commands):
256 """
257 Run the given commands with the cftp client. Returns a C{Deferred}
258 that fires when the commands are all complete. The C{Deferred}'s
259 payload is a list of output for each command.
260 """
261 return self.processProtocol.runScript(commands)
262
263 def testCdPwd(self):
264 """
265 Test that 'pwd' reports the current remote directory, that 'lpwd'
266 reports the current local directory, and that changing to a
267 subdirectory then changing to its parent leaves you in the original
268 remote directory.
269 """
270 # XXX - not actually a unit test, see docstring.
271 homeDir = os.path.join(os.getcwd(), self.testDir)
272 d = self.runScript('pwd', 'lpwd', 'cd testDirectory', 'cd ..', 'pwd')
273 d.addCallback(lambda xs: xs[:3] + xs[4:])
274 d.addCallback(self.assertEqual,
275 [homeDir, os.getcwd(), '', homeDir])
276 return d
277
278 def testChAttrs(self):
279 """
280 Check that 'ls -l' output includes the access permissions and that
281 this output changes appropriately with 'chmod'.
282 """
283 def _check(results):
284 self.flushLoggedErrors()
285 self.assertTrue(results[0].startswith('-rw-r--r--'))
286 self.assertEqual(results[1], '')
287 self.assertTrue(results[2].startswith('----------'), results[2])
288 self.assertEqual(results[3], '')
289
290 d = self.runScript('ls -l testfile1', 'chmod 0 testfile1',
291 'ls -l testfile1', 'chmod 644 testfile1')
292 return d.addCallback(_check)
293 # XXX test chgrp/own
294
295
296 def testList(self):
297 """
298 Check 'ls' works as expected. Checks for wildcards, hidden files,
299 listing directories and listing empty directories.
300 """
301 def _check(results):
302 self.assertEqual(results[0], ['testDirectory', 'testRemoveFile',
303 'testRenameFile', 'testfile1'])
304 self.assertEqual(results[1], ['testDirectory', 'testRemoveFile',
305 'testRenameFile', 'testfile1'])
306 self.assertEqual(results[2], ['testRemoveFile', 'testRenameFile'])
307 self.assertEqual(results[3], ['.testHiddenFile', 'testRemoveFile',
308 'testRenameFile'])
309 self.assertEqual(results[4], [''])
310 d = self.runScript('ls', 'ls ../' + os.path.basename(self.testDir),
311 'ls *File', 'ls -a *File', 'ls -l testDirectory')
312 d.addCallback(lambda xs: [x.split('\n') for x in xs])
313 return d.addCallback(_check)
314
315 def testHelp(self):
316 """
317 Check that running the '?' command returns help.
318 """
319 d = self.runCommand('?')
320 d.addCallback(self.assertEqual,
321 cftp.StdioClient(None).cmd_HELP('').strip())
322 return d
323
324 def assertFilesEqual(self, name1, name2, msg=None):
325 """
326 Assert that the files at C{name1} and C{name2} contain exactly the
327 same data.
328 """
329 f1 = file(name1).read()
330 f2 = file(name2).read()
331 self.failUnlessEqual(f1, f2, msg)
332
333
334 def testGet(self):
335 """
336 Test that 'get' saves the remote file to the correct local location,
337 that the output of 'get' is correct and that 'rm' actually removes
338 the file.
339 """
340 # XXX - not actually a unit test
341 expectedOutput = ("Transferred %s/%s/testfile1 to %s/test file2"
342 % (os.getcwd(), self.testDir, self.testDir))
343 def _checkGet(result):
344 self.assertTrue(result.endswith(expectedOutput))
345 self.assertFilesEqual(self.testDir + '/testfile1',
346 self.testDir + '/test file2',
347 "get failed")
348 return self.runCommand('rm "test file2"')
349
350 d = self.runCommand('get testfile1 "%s/test file2"' % (self.testDir,))
351 d.addCallback(_checkGet)
352 d.addCallback(lambda _: self.failIf(
353 os.path.exists(self.testDir + '/test file2')))
354 return d
355
356
357 def testWildcardGet(self):
358 """
359 Test that 'get' works correctly when given wildcard parameters.
360 """
361 def _check(ignored):
362 self.assertFilesEqual(self.testDir + '/testRemoveFile',
363 'testRemoveFile',
364 'testRemoveFile get failed')
365 self.assertFilesEqual(self.testDir + '/testRenameFile',
366 'testRenameFile',
367 'testRenameFile get failed')
368
369 d = self.runCommand('get testR*')
370 return d.addCallback(_check)
371
372
373 def testPut(self):
374 """
375 Check that 'put' uploads files correctly and that they can be
376 successfully removed. Also check the output of the put command.
377 """
378 # XXX - not actually a unit test
379 expectedOutput = ('Transferred %s/testfile1 to %s/%s/test"file2'
380 % (self.testDir, os.getcwd(), self.testDir))
381 def _checkPut(result):
382 self.assertFilesEqual(self.testDir + '/testfile1',
383 self.testDir + '/test"file2')
384 self.failUnless(result.endswith(expectedOutput))
385 return self.runCommand('rm "test\\"file2"')
386
387 d = self.runCommand('put %s/testfile1 "test\\"file2"'
388 % (self.testDir,))
389 d.addCallback(_checkPut)
390 d.addCallback(lambda _: self.failIf(
391 os.path.exists(self.testDir + '/test"file2')))
392 return d
393
394
395 def testWildcardPut(self):
396 """
397 What happens if you issue a 'put' command and include a wildcard (i.e.
398 '*') in parameter? Check that all files matching the wildcard are
399 uploaded to the correct directory.
400 """
401 def check(results):
402 self.assertEqual(results[0], '')
403 self.assertEqual(results[2], '')
404 self.assertFilesEqual(self.testDir + '/testRemoveFile',
405 self.testDir + '/../testRemoveFile',
406 'testRemoveFile get failed')
407 self.assertFilesEqual(self.testDir + '/testRenameFile',
408 self.testDir + '/../testRenameFile',
409 'testRenameFile get failed')
410
411 d = self.runScript('cd ..',
412 'put %s/testR*' % (self.testDir,),
413 'cd %s' % os.path.basename(self.testDir))
414 d.addCallback(check)
415 return d
416
417
418 def testLink(self):
419 """
420 Test that 'ln' creates a file which appears as a link in the output of
421 'ls'. Check that removing the new file succeeds without output.
422 """
423 def _check(results):
424 self.flushLoggedErrors()
425 self.assertEqual(results[0], '')
426 self.assertTrue(results[1].startswith('l'), 'link failed')
427 return self.runCommand('rm testLink')
428
429 d = self.runScript('ln testLink testfile1', 'ls -l testLink')
430 d.addCallback(_check)
431 d.addCallback(self.assertEqual, '')
432 return d
433
434
435 def testRemoteDirectory(self):
436 """
437 Test that we can create and remove directories with the cftp client.
438 """
439 def _check(results):
440 self.assertEqual(results[0], '')
441 self.assertTrue(results[1].startswith('d'))
442 return self.runCommand('rmdir testMakeDirectory')
443
444 d = self.runScript('mkdir testMakeDirectory',
445 'ls -l testMakeDirector?')
446 d.addCallback(_check)
447 d.addCallback(self.assertEqual, '')
448 return d
449
450
451 def testLocalDirectory(self):
452 """
453 Test that we can create a directory locally and remove it with the
454 cftp client. This test works because the 'remote' server is running
455 out of a local directory.
456 """
457 d = self.runCommand('lmkdir %s/testLocalDirectory' % (self.testDir,))
458 d.addCallback(self.assertEqual, '')
459 d.addCallback(lambda _: self.runCommand('rmdir testLocalDirectory'))
460 d.addCallback(self.assertEqual, '')
461 return d
462
463
464 def testRename(self):
465 """
466 Test that we can rename a file.
467 """
468 def _check(results):
469 self.assertEqual(results[0], '')
470 self.assertEqual(results[1], 'testfile2')
471 return self.runCommand('rename testfile2 testfile1')
472
473 d = self.runScript('rename testfile1 testfile2', 'ls testfile?')
474 d.addCallback(_check)
475 d.addCallback(self.assertEqual, '')
476 return d
477
478
479 def testCommand(self):
480 d = self.runCommand('!echo hello')
481 return d.addCallback(self.assertEqual, 'hello')
482
483
484 class TestOurServerBatchFile(CFTPClientTestBase):
485 def setUp(self):
486 CFTPClientTestBase.setUp(self)
487 self.startServer()
488
489 def tearDown(self):
490 CFTPClientTestBase.tearDown(self)
491 return self.stopServer()
492
493 def _getBatchOutput(self, f):
494 fn = self.mktemp()
495 open(fn, 'w').write(f)
496 l = []
497 port = self.server.getHost().port
498 cmds = ('-p %i -l testuser '
499 '--known-hosts kh_test '
500 '--user-authentications publickey '
501 '--host-key-algorithms ssh-rsa '
502 '-K direct '
503 '-i dsa_test '
504 '-a --nocache '
505 '-v -b %s 127.0.0.1') % (port, fn)
506 cmds = test_conch._makeArgs(cmds.split(), mod='cftp')[1:]
507 log.msg('running %s %s' % (sys.executable, cmds))
508 env = os.environ.copy()
509 env['PYTHONPATH'] = os.pathsep.join(sys.path)
510
511 self.server.factory.expectedLoseConnection = 1
512
513 d = getProcessOutputAndValue(sys.executable, cmds, env=env)
514
515 def _cleanup(res):
516 os.remove(fn)
517 return res
518
519 d.addCallback(lambda res: res[0])
520 d.addBoth(_cleanup)
521
522 return d
523
524 def testBatchFile(self):
525 """Test whether batch file function of cftp ('cftp -b batchfile').
526 This works by treating the file as a list of commands to be run.
527 """
528 cmds = """pwd
529 ls
530 exit
531 """
532 def _cbCheckResult(res):
533 res = res.split('\n')
534 log.msg('RES %s' % str(res))
535 self.failUnless(res[1].find(self.testDir) != -1, repr(res))
536 self.failUnlessEqual(res[3:-2], ['testDirectory', 'testRemoveFile',
537 'testRenameFile', 'testfile1'])
538
539 d = self._getBatchOutput(cmds)
540 d.addCallback(_cbCheckResult)
541 return d
542
543 def testError(self):
544 """Test that an error in the batch file stops running the batch.
545 """
546 cmds = """chown 0 missingFile
547 pwd
548 exit
549 """
550 def _cbCheckResult(res):
551 self.failIf(res.find(self.testDir) != -1)
552
553 d = self._getBatchOutput(cmds)
554 d.addCallback(_cbCheckResult)
555 return d
556
557 def testIgnoredError(self):
558 """Test that a minus sign '-' at the front of a line ignores
559 any errors.
560 """
561 cmds = """-chown 0 missingFile
562 pwd
563 exit
564 """
565 def _cbCheckResult(res):
566 self.failIf(res.find(self.testDir) == -1)
567
568 d = self._getBatchOutput(cmds)
569 d.addCallback(_cbCheckResult)
570 return d
571
572
573 class TestOurServerUnixClient(test_conch._UnixFixHome, CFTPClientTestBase):
574
575 def setUp(self):
576 test_conch._UnixFixHome.setUp(self)
577 CFTPClientTestBase.setUp(self)
578 self.startServer()
579 cmd1 = ('-p %i -l testuser '
580 '--known-hosts kh_test '
581 '--host-key-algorithms ssh-rsa '
582 '-a '
583 '-K direct '
584 '-i dsa_test '
585 '127.0.0.1'
586 )
587 port = self.server.getHost().port
588 cmds1 = (cmd1 % port).split()
589 o = options.ConchOptions()
590 def _(host, *args):
591 o['host'] = host
592 o.parseArgs = _
593 o.parseOptions(cmds1)
594 vhk = default.verifyHostKey
595 self.conn = conn = test_conch.SSHTestConnectionForUnix(None)
596 uao = default.SSHUserAuthClient(o['user'], o, conn)
597 return connect.connect(o['host'], int(o['port']), o, vhk, uao)
598
599 def tearDown(self):
600 CFTPClientTestBase.tearDown(self)
601 d = defer.maybeDeferred(self.conn.transport.loseConnection)
602 d.addCallback(lambda x : self.stopServer())
603 def clean(ign):
604 test_conch._UnixFixHome.tearDown(self)
605 return ign
606 return defer.gatherResults([d, self.conn.stopDeferred]).addBoth(clean)
607
608 def _getBatchOutput(self, f):
609 fn = self.mktemp()
610 open(fn, 'w').write(f)
611 port = self.server.getHost().port
612 cmds = ('-p %i -l testuser '
613 '-K unix '
614 '-a '
615 '-v -b %s 127.0.0.1') % (port, fn)
616 cmds = test_conch._makeArgs(cmds.split(), mod='cftp')[1:]
617 log.msg('running %s %s' % (sys.executable, cmds))
618 env = os.environ.copy()
619 env['PYTHONPATH'] = os.pathsep.join(sys.path)
620
621 self.server.factory.expectedLoseConnection = 1
622
623 d = getProcessOutputAndValue(sys.executable, cmds, env=env)
624
625 def _cleanup(res):
626 os.remove(fn)
627 return res
628
629 d.addCallback(lambda res: res[0])
630 d.addBoth(_cleanup)
631
632 return d
633
634 def testBatchFile(self):
635 """Test that the client works even over a UNIX connection.
636 """
637 cmds = """pwd
638 exit
639 """
640 d = self._getBatchOutput(cmds)
641 d.addCallback(
642 lambda res : self.failIf(res.find(self.testDir) == -1,
643 "%s not in %r" % (self.testDir, res)))
644 return d
645
646
647
648 class TestOurServerSftpClient(CFTPClientTestBase):
649 """
650 Test the sftp server against sftp command line client.
651 """
652
653 def setUp(self):
654 CFTPClientTestBase.setUp(self)
655 return self.startServer()
656
657
658 def tearDown(self):
659 return self.stopServer()
660
661
662 def test_extendedAttributes(self):
663 """
664 Test the return of extended attributes by the server: the sftp client
665 should ignore them, but still be able to parse the response correctly.
666
667 This test is mainly here to check that
668 L{filetransfer.FILEXFER_ATTR_EXTENDED} has the correct value.
669 """
670 fn = self.mktemp()
671 open(fn, 'w').write("ls .\nexit")
672 port = self.server.getHost().port
673
674 oldGetAttr = FileTransferForTestAvatar._getAttrs
675 def _getAttrs(self, s):
676 attrs = oldGetAttr(self, s)
677 attrs["ext_foo"] = "bar"
678 return attrs
679
680 self.patch(FileTransferForTestAvatar, "_getAttrs", _getAttrs)
681
682 self.server.factory.expectedLoseConnection = True
683 cmds = ('-o', 'IdentityFile=dsa_test',
684 '-o', 'UserKnownHostsFile=kh_test',
685 '-o', 'HostKeyAlgorithms=ssh-rsa',
686 '-o', 'Port=%i' % (port,), '-b', fn, 'testuser@127.0.0.1')
687 d = getProcessOutputAndValue("sftp", cmds)
688 def check(result):
689 self.assertEquals(result[2], 0)
690 for i in ['testDirectory', 'testRemoveFile',
691 'testRenameFile', 'testfile1']:
692 self.assertIn(i, result[0])
693 return d.addCallback(check)
694
695
696
697 if not unix or not Crypto or not interfaces.IReactorProcess(reactor, None):
698 TestOurServerCmdLineClient.skip = "don't run w/o spawnprocess or PyCrypto"
699 TestOurServerBatchFile.skip = "don't run w/o spawnProcess or PyCrypto"
700 TestOurServerUnixClient.skip = "don't run w/o spawnProcess or PyCrypto"
701 TestOurServerSftpClient.skip = "don't run w/o spawnProcess or PyCrypto"
702 else:
703 from twisted.python.procutils import which
704 if not which('sftp'):
705 TestOurServerSftpClient.skip = "no sftp command-line client available"
OLDNEW
« no previous file with comments | « third_party/twisted_8_1/twisted/conch/test/keydata.py ('k') | third_party/twisted_8_1/twisted/conch/test/test_channel.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698