[tor-commits] [stem/master] Early unit test builds with lots of holes to fill.

atagar at torproject.org atagar at torproject.org
Sat Aug 4 19:19:49 UTC 2012


commit 4ea5cf2e2d8f7bb27db96d35d8fc0538b2745e70
Author: Erik <eislo at wesleyan.edu>
Date:   Tue Jul 17 17:46:16 2012 -0400

    Early unit test builds with lots of holes to fill.
    
    Problems encountered include mocking functions that utilize keyword args,
    passing descriptor objects to functions being tested rather than just
    dictionaries, and determining if, in the case of the user defining both
    include_fields and exclude_fields, an error should be thrown or the
    functions should simply remove any overlap from the two lists.
---
 run_tests.py                   |    4 ++
 stem/descriptor/export.py      |   93 ++++++++++++++++++++--------------------
 test/mocking.py                |    3 +-
 test/unit/descriptor/export.py |   52 ++++++++++++++++++++++
 4 files changed, 105 insertions(+), 47 deletions(-)

diff --git a/run_tests.py b/run_tests.py
index 8a9a37d..bed8e50 100755
--- a/run_tests.py
+++ b/run_tests.py
@@ -50,6 +50,8 @@ import test.integ.util.proc
 import test.integ.util.system
 import test.integ.process
 import test.integ.version
+#TODO move this by other descriptor tests
+import test.unit.descriptor.export
 
 import stem.prereq
 import stem.util.conf
@@ -121,7 +123,9 @@ UNIT_TESTS = (
   test.unit.response.protocolinfo.TestProtocolInfoResponse,
   test.unit.response.authchallenge.TestAuthChallengeResponse,
   test.unit.connection.authentication.TestAuthenticate,
+  test.unit.descriptor.export.TestExport,
 )
+#  TODO move export test to proper location
 
 INTEG_TESTS = (
   test.integ.util.conf.TestConf,
diff --git a/stem/descriptor/export.py b/stem/descriptor/export.py
index c847b81..dd7f324 100644
--- a/stem/descriptor/export.py
+++ b/stem/descriptor/export.py
@@ -1,80 +1,82 @@
 import os, csv, sets, cStringIO
 
 
-def get_csv_line(descriptor, include_fields=[], exclude_fields=[]):
+def export_csv(descriptor, include_fields=[], exclude_fields=[]):
   """
   Takes a single descriptor object, puts it in a list, and passes it to
   descriptors_csv_exp to build a csv.
-
-  :param object descr: single descriptor object to export as csv.
+  
+  :param object descriptor: single descriptor whose.
+  :param list include_fields: list of attribute fields to include in the csv string.
+  :param list exclude_fields: list of attribute fields to exclude from csv string.
+  
+  :returns: single csv line as a string with one descriptor attribute per cell.
   """
   descr = [descriptor]
-  for desc in get_csv_lines(descr, include_fields, exclude_fields):
-    return desc
+  return export_csvs(descr, include_fields=include_fields, exclude_fields=exclude_fields)
 
 
-def get_csv_lines(descriptors, include_fields=[], exclude_fields=[], head=False):
+def export_csvs(descriptors, include_fields=[], exclude_fields=[], header=False):
   """
-  Builds a csv file based on attributes of descriptors.
-
-  :param list descrs: List of descriptor objects to export as a csv line.
-  :param list incl_fields: list of attribute fields to include in the csv line.
-  :param list excl_fields: list of attribute fields to exclude from csv line.
-  :param bool head: whether or not a header is requested; shouldn't be needed
-    outside of csv_file_exp's call of this function.
-
-  :returns: generator for csv strings, one line per descr object.
+  Returns a string that is in csv format, ready to be placed in a .csv file.
+  
+  :param list descrs: List of descriptor objects whose attributes will be written.
+  :param list include_fields: list of attribute fields to include in the csv string.
+  :param list exclude_fields: list of attribute fields to exclude from csv string.
+  :param bool header: whether or not a header is requested; probably won't be
+    needed outside of csv_file_exp's call of this function.
+  
+  :returns: csv string with one descriptor per line and one attribute per cell.
   """
-
+  
   _temp_file = cStringIO.StringIO()
-
+  
   first = True
-
+  
   for desc in descriptors:
     attr = vars(desc)
-
+    
     # Defining incl_fields and the dwriter object requires having access
     # to a descriptor object.
     if first:
+      # All descriptor objects should be of the same type
+      # (i.e. server_descriptor.RelayDescriptor)
+      desc_type = type(desc)
+      
       # define incl_fields, 4 cases where final case is incl_fields already
       # defined and excl_fields left blank, so no action is necessary.
       if not include_fields and exclude_fields:
         _incl = sets.Set(attr.keys())
         include_fields = list(_incl.difference(exclude_fields))
-
+      
       elif not include_fields and not exclude_fields:
         include_fields = attr.keys()
-
+      
       elif include_fields and exclude_fields:
         _incl = sets.Set(include_fields)
         include_fields = list(_incl.difference(exclude_fields))
-
-      dwriter = csv.DictWriter(_temp_file, include_fields)
+      
+      dwriter = csv.DictWriter(_temp_file, include_fields, extrasaction='ignore')
       first = False
-
-      if head:
+      
+      if header:
         dwriter.writeheader()
-
-    # Need to remove fields that aren't wanted for dwriter.
-    final = {}
-    for at in attr:
-      if at in include_fields:
-        final[at] = attr[at]
-
-    dwriter.writerow(final)
-    yield _temp_file.getvalue()
     
-    # Clear cString wrapper for new descriptor.
-    _temp_file.reset()
-    _temp_file.truncate()
-
-      
-  _temp_file.close()
-
-def csv_file_exp(descriptors, document_location, header=True, include_fields=[], exclude_fields=[]):
+    if desc_type == type(desc):
+      dwriter.writerow(attr)
+    else:
+      raise ValueError('More than one type of descriptor was provided.')
+  
+  return _temp_file.getvalue()
+  # cStringIO files are closed automatically when the current scope is exited.
+
+def export_csv_file(descriptors, document_location, header=True, include_fields=[], exclude_fields=[]):
   """
   Writes descriptor attributes to a csv file on disk.
-
+  
+  Calls get_csv_lines with the given argument, then writes the returned string
+  to a file location specified by document_location.
+  
   :param list descrs: descriptor objects with attributes to export as csv file.
   :param str doc_loc: location and file name for csv file to be written to.
     This overwrites existing files.
@@ -83,7 +85,6 @@ def csv_file_exp(descriptors, document_location, header=True, include_fields=[],
   :param list excl_f: list of attribute fields to exclude from csv line.
   """
   doc = open(document_location, 'w')
-
-  for line in get_csv_lines(descriptors, include_fields=include_fields, exclude_fields=exclude_fields, head=header):
+  
+  for line in export_csvs(descriptors, include_fields=include_fields, exclude_fields=exclude_fields, head=header):
     doc.write(line)
-
diff --git a/test/mocking.py b/test/mocking.py
index f4198ec..198bf70 100644
--- a/test/mocking.py
+++ b/test/mocking.py
@@ -50,7 +50,8 @@ def no_op():
   return _no_op
 
 def return_value(value):
-  def _return_value(*args): return value
+  def _return_value(*args, **kwargs):
+    return value
   return _return_value
 
 def return_true(): return return_value(True)
diff --git a/test/unit/descriptor/export.py b/test/unit/descriptor/export.py
new file mode 100644
index 0000000..b1b78e3
--- /dev/null
+++ b/test/unit/descriptor/export.py
@@ -0,0 +1,52 @@
+"""
+Unit testing code for the stem.descriptor.export module
+"""
+import unittest
+import stem.descriptor.export as export
+import test.mocking as mocking
+
+SINGLE_DESCR_DICT = {'average_bandwidth': 5242880, 'onion_key': 'RSA PUB = JAIK', 'address': '79.139.135.90', '_digest': None, 'exit_policy': ['reject *:*'], 'fingerprint': '0045EB8B820DC410197B'}
+
+
+
+class TestExport(unittest.TestCase):
+  def tearDown(self):
+    mocking.revert_mocking()
+  
+  def test_export_csv(self):
+    """
+    Tests the export_csv function which takes a single descriptor object.
+    """
+    
+    # TODO we should be passing descriptor objects not just dicts.
+    csv_string = '5242880, RSA PUB = JAIK, 79.139.135.90,,[\'reject *:*\'], 0045EB8B820DC410197B'
+    mocking.mock(export.export_csvs, mocking.return_value(csv_string))
+    self.assertEqual(csv_string, export.export_csv(SINGLE_DESCR_DICT))
+    
+    csv_string = '79.139.135.90,,[\'reject *:*\'], 0045EB8B820DC410197B'
+    mocking.mock(export.export_csvs, mocking.return_value(csv_string))
+    self.assertEqual(csv_string, export.export_csv(SINGLE_DESCR_DICT, exclude_fields=['average_bandwidth', 'onion_key']))
+    
+    csv_string = 'RSA PUB = JAIK, 79.139.135.90,'
+    mocking.mock(export.export_csvs, mocking.return_value(csv_string))
+    self.assertEqual(csv_string, export.export_csv(SINGLE_DESCR_DICT, include_fields=['onion_key', 'address']))
+    
+    # TODO 1 or two more cases to handle (subcases of overlap/no overlap
+    # incl & excl.)
+    
+    
+    # TODO Make sure to undo mocking here or we won't be testing the next function.
+    
+  def test_export_csvs(self):
+    """
+    Test the export_csvs function which takes a list of descriptor objects.
+    """
+    pass
+  
+  def test_export_csv_file(self):
+    """
+    Tests the export_csv_file function.
+    """
+    pass
+    # mocking.mock(open, mocking.return_for_args(##))
+    # mocking.mock(export.export_csvs, ##)





More information about the tor-commits mailing list