commit 74ba2e9db42508a2bc2873dd078ae5bf2cbd140e
Author: Damian Johnson <atagar(a)torproject.org>
Date: Fri Aug 24 12:38:20 2012 -0700
Revisions for EXTENDCIRCUIT addition
Couple minor changes:
* The EXTENDCIRCUIT integ tests fails if we don't have an internet connection.
Making it dependent on the ONLINE testing target.
* Revising the pydocs a bit. For instance, there's no point in saying "If the
purpose isn't provided, "general" circuits are built." when we can simply
provide a default for the argument. ;)
However, there's still a couple mysteries...
* When I was offline I got the following stacktrace...
======================================================================
ERROR: test_extendcircuit
----------------------------------------------------------------------
Traceback:
File "/home/atagar/Desktop/stem/test/integ/control/controller.py", line 373, in test_extendcircuit
circ_id = controller.extend_circuit(0)
File "/home/atagar/Desktop/stem/stem/control.py", line 1109, in extend_circuit
raise stem.socket.ProtocolError("EXTENDCIRCUIT returned unexpected response code: %s" % response.code)
ProtocolError: EXTENDCIRCUIT returned unexpected response code: 512
----------------------------------------------------------------------
However, according to the control-spec the 512 response code is for "Syntax
error in command argument". That doesn't make sense if we're failing because
we lack a connection.
* Is the 'path' argument for EXTENDCIRCUIT the circuits that we build through,
or the relays to be chosen from when building a single hop? The EXTENDCIRCUIT
description doesn't say, which seems to me to be a weakness in the spec.
---
stem/control.py | 37 ++++++++++++++++---------------------
test/integ/control/controller.py | 11 ++++-------
test/runner.py | 15 +++++++++++++++
3 files changed, 35 insertions(+), 28 deletions(-)
diff --git a/stem/control.py b/stem/control.py
index 2f0c773..ece7f03 100644
--- a/stem/control.py
+++ b/stem/control.py
@@ -1055,43 +1055,39 @@ class Controller(BaseController):
raise stem.socket.ProtocolError("SIGNAL response contained unrecognized status code: %s" % response.code)
- def new_circuit(self, relays = None, purpose = None):
+ def new_circuit(self, path = None, purpose = "general"):
"""
- Requests Tor to build a new circuit.
+ Requests a new circuit. If the path isn't provided, one is automatically
+ selected.
- If the path isn't provided, one is automatically selected. If the purpose
- isn't provided, "general" circuits are built.
-
- :param list,str relays: list of relay nicknames/longnames or a single nickname/longname
+ :param list,str path: one or more relays to make a circuit through
:param str purpose: "general" or "controller"
- :returns: Circuit id of the newly created circuit
+ :returns: int of the circuit id of the newly created circuit
"""
- return self.extend_circuit(0, relays, purpose)
+ return self.extend_circuit(0, path, purpose)
- def extend_circuit(self, circuit=0, relays = None, purpose = None):
+ def extend_circuit(self, circuit = 0, path = None, purpose = "general"):
"""
- Requests Tor to build a new circuit or extend an existing circuit.
+ Either requests a new circuit or extend an existing one.
- When called without any arguments, a new general purpose circuit is created.
- If circuit is zero, a new circuit is created. If circuit is non-zero, Tor
- extends the existing circuit with that id . If the path isn't provided, one
- is automatically selected. If the purpose isn't provided, "general" circuits
- are built.
+ When called with a circuit value of zero (the default) a new circuit is
+ created, and when non-zero the circuit with that id is extended. If the
+ path isn't provided, one is automatically selected.
- :param int circuit: id of the circuit which needs extending
- :param list,str relays: list of relay nicknames/longnames or a single nickname/longname
+ :param int circuit: id of a circuit to be extended
+ :param list,str path: one or more relays to make a circuit through
:param str purpose: "general" or "controller"
- :returns: Circuit id of the created/extended circuit
+ :returns: int of the circuit id of the created or extended circuit
:raises: :class:`stem.socket.InvalidRequest` if one of the parameters were invalid
"""
args = [str(circuit)]
- if type(relays) == str: relays = [relays]
- if relays: args.append(",".join(relays))
+ if type(path) == str: path = [path]
+ if path: args.append(",".join(path))
if purpose: args.append("purpose=%s" % purpose)
response = self.msg("EXTENDCIRCUIT %s" % " ".join(args))
@@ -1136,4 +1132,3 @@ def _case_insensitive_lookup(entries, key, default = UNDEFINED):
if default != UNDEFINED: return default
else: raise ValueError("key '%s' doesn't exist in dict: %s" % (key, entries))
-
diff --git a/test/integ/control/controller.py b/test/integ/control/controller.py
index 9a64d36..e1e24d8 100644
--- a/test/integ/control/controller.py
+++ b/test/integ/control/controller.py
@@ -336,8 +336,7 @@ class TestController(unittest.TestCase):
# the orconn-status results will be empty if we don't have a connection
if orconn_output == '':
- test.runner.skip(self, "(no tor connections)")
- return
+ if test.runner.require_online(self): return
self.assertTrue(re.match("\$[0-9a-fA-F]{40}[~=].*", controller.get_info('orconn-status').split()[0]))
self.assertTrue("VERBOSE_NAMES" in controller.enabled_features)
@@ -352,9 +351,8 @@ class TestController(unittest.TestCase):
"""
Test controller.signal with valid and invalid signals.
"""
- runner = test.runner.get_runner()
- with runner.get_tor_controller() as controller:
+ with test.runner.get_runner().get_tor_controller() as controller:
# valid signal
controller.signal("CLEARDNSCACHE")
@@ -366,10 +364,9 @@ class TestController(unittest.TestCase):
def test_extendcircuit(self):
if test.runner.require_control(self): return
+ elif test.runner.require_online(self): return
- runner = test.runner.get_runner()
-
- with runner.get_tor_controller() as controller:
+ with test.runner.get_runner().get_tor_controller() as controller:
circ_id = controller.extend_circuit(0)
# check if our circuit was created
self.assertTrue(filter(lambda x: int(x.split()[0]) == circ_id, controller.get_info('circuit-status').splitlines()))
diff --git a/test/runner.py b/test/runner.py
index 0d7ba2f..cc8cf7b 100644
--- a/test/runner.py
+++ b/test/runner.py
@@ -11,6 +11,7 @@ about the tor test instance they're running against.
skip - skips the current test if we can
require_control - skips the test unless tor provides a controller endpoint
require_version - skips the test unless we meet a tor version requirement
+ require_online - skips unless targets allow for online tests
exercise_controller - basic sanity check that a controller connection can be used
get_runner - Singleton for fetching our runtime context.
@@ -141,6 +142,20 @@ def require_version(test_case, req_version):
skip(test_case, "(requires %s)" % req_version)
return True
+def require_online(test_case):
+ """
+ Skips the test if we weren't started with the ONLINE target, which indicates
+ that tests requiring network connectivity should run.
+
+ :param unittest.TestCase test_case: test being ran
+
+ :returns: True if test should be skipped, False otherwise
+ """
+
+ if not CONFIG["integ.target.online"]:
+ skip(test_case, "(requires online target)")
+ return True
+
def only_run_once(test_case, test_name):
"""
Skips the test if it has ran before. If it hasn't then flags it as being ran.