[Pkg-privacy-commits] [obfsproxy] 67/353: Add a simple integration tester.

Ximin Luo infinity0 at moszumanska.debian.org
Sat Aug 22 13:01:40 UTC 2015


This is an automated email from the git hooks/post-receive script.

infinity0 pushed a commit to branch master
in repository obfsproxy.

commit 8210799507f8e91c594c0c1629c193d41769c1c4
Author: George Kadianakis <desnacked at riseup.net>
Date:   Mon Sep 17 04:10:56 2012 +0300

    Add a simple integration tester.
    
    Ported from obfsproxy.
---
 tester.py | 281 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 281 insertions(+)

diff --git a/tester.py b/tester.py
new file mode 100644
index 0000000..39ccec4
--- /dev/null
+++ b/tester.py
@@ -0,0 +1,281 @@
+#!/usr/bin/python
+
+"""@package tester.py.in
+Integration tests for obfsproxy.
+
+The obfsproxy binary is assumed to exist in the current working
+directory, and you need to have Python 2.6 or better (but not 3).
+You need to be able to make connections to arbitrary high-numbered
+TCP ports on the loopback interface.
+"""
+
+import difflib
+import errno
+import multiprocessing
+import Queue
+import re
+import signal
+import socket
+import struct
+import subprocess
+import time
+import traceback
+import unittest
+
+def diff(label, expected, received):
+    """
+    Helper: generate unified-format diffs between two named strings.
+    Pythonic escaped-string syntax is used for unprintable characters.
+    """
+    if expected == received:
+        return ""
+    else:
+        return (label + "\n"
+                + "\n".join(s.encode("string_escape")
+                            for s in
+                            difflib.unified_diff(expected.split("\n"),
+                                                 received.split("\n"),
+                                                 "expected", "received",
+                                                 lineterm=""))
+                + "\n")
+
+class Obfsproxy(subprocess.Popen):
+    """
+    Helper: Run obfsproxy instances and confirm that they have
+    completed without any errors.
+    """
+    def __init__(self, *args, **kwargs):
+        """Spawns obfsproxy with 'args'"""
+        argv = ["./src/cli.py", "--no-log"]
+        if len(args) == 1 and (isinstance(args[0], list) or
+                               isinstance(args[0], tuple)):
+            argv.extend(args[0])
+        else:
+            argv.extend(args)
+
+        subprocess.Popen.__init__(self, argv,
+                                  stdin=open("/dev/null", "r"),
+                                  stdout=subprocess.PIPE,
+                                  stderr=subprocess.PIPE,
+                                  **kwargs)
+
+    severe_error_re = re.compile(r"\[(?:warn|err(?:or)?)\]")
+
+    def check_completion(self, label, force_stderr):
+        """
+        Checks the output and exit status of obfsproxy to see if
+        everything went fine.
+
+        Returns an empty string if the test was good, otherwise it
+        returns a report that should be printed to the user.
+        """
+        if self.poll() is None:
+            self.send_signal(signal.SIGINT)
+
+        (out, err) = self.communicate()
+
+        report = ""
+        def indent(s):
+            return "| " + "\n| ".join(s.strip().split("\n"))
+
+        # exit status should be zero
+        if self.returncode > 0:
+            report += label + " exit code: %d\n" % self.returncode
+        elif self.returncode < 0:
+            report += label + " killed: signal %d\n" % -self.returncode
+
+        # there should be nothing on stdout
+        if out != "":
+            report += label + " stdout:\n%s\n" % indent(out)
+
+        # there will be debugging messages on stderr, but there should be
+        # no [warn], [err], or [error] messages.
+        if force_stderr or self.severe_error_re.search(err):
+            report += label + " stderr:\n%s\n" % indent(err)
+
+        return report
+
+    def stop(self):
+        """Terminates obfsproxy."""
+        if self.poll() is None:
+            self.terminate()
+
+def connect_with_retry(addr):
+    """
+    Helper: Repeatedly try to connect to the specified server socket
+    until either it succeeds or one full second has elapsed.  (Surely
+    there is a better way to do this?)
+    """
+
+    retry = 0
+    while True:
+        try:
+            return socket.create_connection(addr)
+        except socket.error, e:
+            if e.errno != errno.ECONNREFUSED: raise
+            if retry == 20: raise
+            retry += 1
+            time.sleep(0.05)
+
+SOCKET_TIMEOUT = 1.0
+
+class ReadWorker(object):
+    """
+    Helper: In a separate process (to avoid deadlock), listen on a
+    specified socket.  The first time something connects to that socket,
+    read all available data, stick it in a string, and post the string
+    to the output queue.  Then close both sockets and exit.
+    """
+
+    @staticmethod
+    def work(address, oq):
+        listener = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+        listener.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
+        listener.bind(address)
+        listener.listen(1)
+        (conn, remote) = listener.accept()
+        listener.close()
+        conn.settimeout(SOCKET_TIMEOUT)
+        data = ""
+        try:
+            while True:
+                chunk = conn.recv(4096)
+                if chunk == "": break
+                data += chunk
+        except socket.timeout:
+            pass
+        except Exception, e:
+            data += "|RECV ERROR: " + e
+        conn.close()
+        oq.put(data)
+
+    def __init__(self, address):
+        self.oq = multiprocessing.Queue()
+        self.worker = multiprocessing.Process(target=self.work,
+                                              args=(address, self.oq))
+        self.worker.start()
+
+    def get(self):
+        """
+        Get a chunk of data from the ReadWorker's queue.
+        """
+        rv = self.oq.get(timeout=SOCKET_TIMEOUT+0.1)
+        self.worker.join()
+        return rv
+
+    def stop(self):
+        if self.worker.is_alive(): self.worker.terminate()
+
+# Right now this is a direct translation of the former int_test.sh
+# (except that I have fleshed out the SOCKS test a bit).
+# It will be made more general and parametric Real Soon.
+
+ENTRY_PORT  = 4999
+SERVER_PORT = 5000
+EXIT_PORT   = 5001
+
+#
+# Test base classes.  They do _not_ inherit from unittest.TestCase
+# so that they are not scanned directly for test functions (some of
+# them do provide test functions, but not in a usable state without
+# further code from subclasses).
+#
+
+class DirectTest(object):
+    def setUp(self):
+        self.output_reader = ReadWorker(("127.0.0.1", EXIT_PORT))
+        self.obfs_server = Obfsproxy(self.server_args)
+        self.obfs_client = Obfsproxy(self.client_args)
+        self.input_chan = connect_with_retry(("127.0.0.1", ENTRY_PORT))
+        self.input_chan.settimeout(SOCKET_TIMEOUT)
+
+    def tearDown(self):
+        self.obfs_client.stop()
+        self.obfs_server.stop()
+        self.output_reader.stop()
+        self.input_chan.close()
+
+    def test_direct_transfer(self):
+        # Open a server and a simple client (in the same process) and
+        # transfer a file.  Then check whether the output is the same
+        # as the input.
+        self.input_chan.sendall(TEST_FILE)
+        try:
+            output = self.output_reader.get()
+        except Queue.Empty:
+            output = ""
+
+        self.input_chan.close()
+
+        report = diff("errors in transfer:", TEST_FILE, output)
+
+        report += self.obfs_client.check_completion("obfsproxy client", report!="")
+        report += self.obfs_server.check_completion("obfsproxy server", report!="")
+
+        if report != "":
+            self.fail("\n" + report)
+
+#
+# Concrete test classes specialize the above base classes for each protocol.
+#
+
+class DirectDummy(DirectTest, unittest.TestCase):
+    server_args = ("dummy", "server",
+                   "127.0.0.1:%d" % SERVER_PORT,
+                   "--dest=127.0.0.1:%d" % EXIT_PORT)
+    client_args = ("dummy", "client",
+                   "127.0.0.1:%d" % ENTRY_PORT,
+                   "--dest=127.0.0.1:%d" % SERVER_PORT)
+
+class DirectB64(DirectTest, unittest.TestCase):
+    server_args = ("b64", "server",
+                   "127.0.0.1:%d" % SERVER_PORT,
+                   "--dest=127.0.0.1:%d" % EXIT_PORT)
+    client_args = ("b64", "client",
+                   "127.0.0.1:%d" % ENTRY_PORT,
+                   "--dest=127.0.0.1:%d" % SERVER_PORT)
+
+
+TEST_FILE = """\
+THIS IS A TEST FILE. IT'S USED BY THE INTEGRATION TESTS.
+THIS IS A TEST FILE. IT'S USED BY THE INTEGRATION TESTS.
+THIS IS A TEST FILE. IT'S USED BY THE INTEGRATION TESTS.
+THIS IS A TEST FILE. IT'S USED BY THE INTEGRATION TESTS.
+
+"Can entropy ever be reversed?"
+"THERE IS AS YET INSUFFICIENT DATA FOR A MEANINGFUL ANSWER."
+"Can entropy ever be reversed?"
+"THERE IS AS YET INSUFFICIENT DATA FOR A MEANINGFUL ANSWER."
+"Can entropy ever be reversed?"
+"THERE IS AS YET INSUFFICIENT DATA FOR A MEANINGFUL ANSWER."
+"Can entropy ever be reversed?"
+"THERE IS AS YET INSUFFICIENT DATA FOR A MEANINGFUL ANSWER."
+"Can entropy ever be reversed?"
+"THERE IS AS YET INSUFFICIENT DATA FOR A MEANINGFUL ANSWER."
+"Can entropy ever be reversed?"
+"THERE IS AS YET INSUFFICIENT DATA FOR A MEANINGFUL ANSWER."
+"Can entropy ever be reversed?"
+"THERE IS AS YET INSUFFICIENT DATA FOR A MEANINGFUL ANSWER."
+"Can entropy ever be reversed?"
+"THERE IS AS YET INSUFFICIENT DATA FOR A MEANINGFUL ANSWER."
+
+    In obfuscatory age geeky warfare did I wage
+      For hiding bits from nasty censors' sight
+    I was hacker to my set in that dim dark age of net
+      And I hacked from noon till three or four at night
+
+    Then a rival from Helsinki said my protocol was dinky
+      So I flamed him with a condescending laugh,
+    Saying his designs for stego might as well be made of lego
+      And that my bikeshed was prettier by half.
+
+    But Claude Shannon saw my shame. From his noiseless channel came
+       A message sent with not a wasted byte
+    "There are nine and sixty ways to disguise communiques
+       And RATHER MORE THAN ONE OF THEM IS RIGHT"
+
+		    (apologies to Rudyard Kipling.)
+"""
+
+if __name__ == '__main__':
+    unittest.main()

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-privacy/packages/obfsproxy.git



More information about the Pkg-privacy-commits mailing list