[tor-commits] [stem/master] Changing on-disk descriptor tests to be unit tests

atagar at torproject.org atagar at torproject.org
Mon Jun 9 01:59:18 UTC 2014


commit a0878eed645625b71541b73049074e1b75c697d5
Author: Damian Johnson <atagar at torproject.org>
Date:   Sun Jun 8 18:53:49 2014 -0700

    Changing on-disk descriptor tests to be unit tests
    
    We have three types of descriptor tests:
    
      * tests for particular parsing functionality
      * tests reading real descriptors we bundle with our tests
      * tests that read tor's data directory or from the controller
    
    Of these only the last group are actually integration tests with our tor
    instance. Reading descriptors from disk defininitely belongs with our unit
    tests.
    
    They're quick (~0.5 seconds), and greatly improve our test coverage this is a
    pretty nice change. :)
---
 test/integ/descriptor/__init__.py                  |   13 -
 test/integ/descriptor/data/bridge_descriptor       |   15 -
 test/integ/descriptor/data/bridge_network_status   |   18 -
 test/integ/descriptor/data/cached-consensus        |  130 -----
 test/integ/descriptor/data/cached-consensus-v2     |   28 -
 test/integ/descriptor/data/cached-microdescs       |   25 -
 test/integ/descriptor/data/cr_in_contact_line      |   48 --
 test/integ/descriptor/data/descriptor_archive.tar  |  Bin 20480 -> 0 bytes
 .../descriptor/data/descriptor_archive.tar.bz2     |  Bin 3352 -> 0 bytes
 .../descriptor/data/descriptor_archive.tar.gz      |  Bin 2874 -> 0 bytes
 test/integ/descriptor/data/example_descriptor      |   31 --
 .../descriptor/data/extrainfo_bridge_descriptor    |   22 -
 .../descriptor/data/extrainfo_relay_descriptor     |   13 -
 test/integ/descriptor/data/metrics_cert            |   34 --
 test/integ/descriptor/data/metrics_consensus       |  121 -----
 .../descriptor/data/metrics_server_desc_multiple   |   87 ---
 test/integ/descriptor/data/metrics_vote            |   85 ---
 test/integ/descriptor/data/negative_uptime         |   46 --
 test/integ/descriptor/data/new_metrics_type        |    3 -
 test/integ/descriptor/data/non-ascii_descriptor    |   30 --
 test/integ/descriptor/data/old_descriptor          |   48 --
 test/integ/descriptor/data/riddle                  |   20 -
 test/integ/descriptor/data/tiny.png                |  Bin 188 -> 0 bytes
 test/integ/descriptor/data/vote                    |  100 ----
 test/integ/descriptor/extrainfo_descriptor.py      |  110 ----
 test/integ/descriptor/microdescriptor.py           |   64 ---
 test/integ/descriptor/networkstatus.py             |  394 --------------
 test/integ/descriptor/reader.py                    |  562 --------------------
 test/integ/descriptor/server_descriptor.py         |  297 +----------
 test/settings.cfg                                  |    1 -
 test/unit/descriptor/__init__.py                   |   12 +
 test/unit/descriptor/data/bridge_descriptor        |   15 +
 test/unit/descriptor/data/bridge_network_status    |   18 +
 test/unit/descriptor/data/cached-consensus         |  130 +++++
 test/unit/descriptor/data/cached-consensus-v2      |   28 +
 test/unit/descriptor/data/cached-microdescs        |   25 +
 test/unit/descriptor/data/cr_in_contact_line       |   48 ++
 test/unit/descriptor/data/descriptor_archive.tar   |  Bin 0 -> 20480 bytes
 .../descriptor/data/descriptor_archive.tar.bz2     |  Bin 0 -> 3352 bytes
 .../unit/descriptor/data/descriptor_archive.tar.gz |  Bin 0 -> 2874 bytes
 test/unit/descriptor/data/example_descriptor       |   31 ++
 .../descriptor/data/extrainfo_bridge_descriptor    |   22 +
 .../descriptor/data/extrainfo_relay_descriptor     |   13 +
 test/unit/descriptor/data/metrics_cert             |   34 ++
 test/unit/descriptor/data/metrics_consensus        |  121 +++++
 .../descriptor/data/metrics_server_desc_multiple   |   87 +++
 test/unit/descriptor/data/metrics_vote             |   85 +++
 test/unit/descriptor/data/negative_uptime          |   46 ++
 test/unit/descriptor/data/new_metrics_type         |    3 +
 test/unit/descriptor/data/non-ascii_descriptor     |   30 ++
 test/unit/descriptor/data/old_descriptor           |   48 ++
 test/unit/descriptor/data/riddle                   |   20 +
 test/unit/descriptor/data/tiny.png                 |  Bin 0 -> 188 bytes
 test/unit/descriptor/data/vote                     |  100 ++++
 test/unit/descriptor/extrainfo_descriptor.py       |  111 ++++
 test/unit/descriptor/microdescriptor.py            |   65 +++
 .../descriptor/networkstatus/bridge_document.py    |   71 +++
 test/unit/descriptor/networkstatus/document_v2.py  |   79 +++
 test/unit/descriptor/networkstatus/document_v3.py  |  251 +++++++++
 test/unit/descriptor/reader.py                     |  548 +++++++++++++++++++
 test/unit/descriptor/server_descriptor.py          |  290 ++++++++++
 61 files changed, 2332 insertions(+), 2344 deletions(-)

diff --git a/test/integ/descriptor/__init__.py b/test/integ/descriptor/__init__.py
index 3a0f156..b2f7121 100644
--- a/test/integ/descriptor/__init__.py
+++ b/test/integ/descriptor/__init__.py
@@ -3,22 +3,9 @@ Integration tests for stem.descriptor.* contents.
 """
 
 __all__ = [
-  'reader',
   'extrainfo_descriptor',
   'microdescriptor',
   'server_descriptor',
   'get_resource',
   'open_desc',
 ]
-
-import os
-
-DESCRIPTOR_TEST_DATA = os.path.join(os.path.dirname(__file__), 'data')
-
-
-def get_resource(filename):
-  """
-  Provides the path for a file in our descriptor data directory.
-  """
-
-  return os.path.join(DESCRIPTOR_TEST_DATA, filename)
diff --git a/test/integ/descriptor/data/bridge_descriptor b/test/integ/descriptor/data/bridge_descriptor
deleted file mode 100644
index b7dbd92..0000000
--- a/test/integ/descriptor/data/bridge_descriptor
+++ /dev/null
@@ -1,15 +0,0 @@
- at type bridge-server-descriptor 1.0
-router Unnamed 10.45.227.253 9001 0 0
-router-digest 006FD96BA35E7785A6A3B8B75FE2E2435A13BDB4
-or-address [fd9f:2e19:3bcf::02:9970]:9001
-platform Tor 0.2.3.12-alpha (git-800942b4176ca31c) on Linux x86_64
-opt protocols Link 1 2 Circuit 1
-published 2012-03-22 17:34:38
-opt fingerprint AE54 E28E D069 CDF4 5F30 09F9 63EE 3B3D 6FA2 6A2E
-uptime 186
-bandwidth 409600 819200 5120
-opt extra-info-digest 134F81F7A0D270B85FCD481DD10CEA34BA7B15C9
-family $CE396C72A3D0880F74C064FEA79D68C15BD380B9 $AB8B00C00B1347BA80A88E548FAC9EDF701D7D0E $8C8A470D7C23151665A7B84E75E89FCC205A3304
-opt hidden-service-dir
-contact somebody
-reject *:*
diff --git a/test/integ/descriptor/data/bridge_network_status b/test/integ/descriptor/data/bridge_network_status
deleted file mode 100644
index ad07672..0000000
--- a/test/integ/descriptor/data/bridge_network_status
+++ /dev/null
@@ -1,18 +0,0 @@
- at type bridge-network-status 1.0
-published 2012-06-01 04:07:04
-r Unnamed ABSiBVJ42z6w5Z6nAXQUFq8YVVg FI74aFuNJZZQrgln0f+OaocMd0M 2012-05-31 15:57:00 10.97.236.247 443 0
-s Valid
-w Bandwidth=55
-p reject 1-65535
-r TolFuin AFn9TveYjdtZEsgh7QsWp3qC5kU 1Sw8RPx2Tq/w+VHL+pZipiJUG5k 2012-05-31 18:12:39 10.99.47.37 80 0
-s Fast Guard Running Stable Valid
-w Bandwidth=32
-p reject 1-65535
-r sidvic AGzeXR+V8NSBE3wTE4m7gBYbjWM 7enyDQ20UjliwCPN2JmTl6zrCdM 2012-05-31 22:56:12 10.145.17.182 80 0
-s Fast Guard Running Stable Valid
-w Bandwidth=56
-p reject 1-65535
-r AntoniusBlock ANk5reA3WwTV62ZpyMo+NWtrvAw gEgv8I1E4LWrs394Xx/cRYQUL5A 2012-05-31 17:21:20 10.95.68.0 443 0
-s Fast Guard Running Stable Valid
-w Bandwidth=55
-p reject 1-65535
diff --git a/test/integ/descriptor/data/cached-consensus b/test/integ/descriptor/data/cached-consensus
deleted file mode 100644
index adcf13b..0000000
--- a/test/integ/descriptor/data/cached-consensus
+++ /dev/null
@@ -1,130 +0,0 @@
-network-status-version 3
-vote-status consensus
-consensus-method 12
-valid-after 2012-07-12 10:00:00
-fresh-until 2012-07-12 11:00:00
-valid-until 2012-07-12 13:00:00
-voting-delay 300 300
-client-versions 0.2.2.35,0.2.2.36,0.2.2.37,0.2.3.10-alpha,0.2.3.11-alpha,0.2.3.12-alpha,0.2.3.13-alpha,0.2.3.14-alpha,0.2.3.15-alpha,0.2.3.16-alpha,0.2.3.17-beta,0.2.3.18-rc,0.2.3.19-rc
-server-versions 0.2.2.35,0.2.2.36,0.2.2.37,0.2.3.10-alpha,0.2.3.11-alpha,0.2.3.12-alpha,0.2.3.13-alpha,0.2.3.14-alpha,0.2.3.15-alpha,0.2.3.16-alpha,0.2.3.17-beta,0.2.3.18-rc,0.2.3.19-rc
-known-flags Authority BadExit Exit Fast Guard HSDir Named Running Stable Unnamed V2Dir Valid
-params CircuitPriorityHalflifeMsec=30000 bwauthpid=1
-dir-source tor26 14C131DFC5C6F93646BE72FA1401C02A8DF2E8B4 86.59.21.38 86.59.21.38 80 443
-contact Peter Palfrader
-vote-digest 0B6D1E9A300B895AA2D0B427F92917B6995C3C1C
-dir-source turtles 27B6B5996C426270A5C95488AA5BCEB6BCC86956 76.73.17.194 76.73.17.194 9030 9090
-contact Mike Perry <mikeperryTAfsckedTODorg>
-vote-digest 904B1974B9879D02B4ADFB81D7E9B4E07D768A5A
-dir-source maatuska 49015F787433103580E3B66A1707A00E60F2D15B 171.25.193.9 171.25.193.9 443 80
-contact 4096R/23291265 Linus Nordberg <linus at nordberg.se>
-vote-digest A8839355BAC373320B8CEDD0A0A09DAAA1637E3A
-dir-source dannenberg 585769C78764D58426B8B52B6651A5A71137189A dannenberg.ccc.de 193.23.244.244 80 443
-contact Andreas Lehner <anonymizer at ccc.de>
-vote-digest 416B73C49E717B0A5D61A4F634DCCF94611802E7
-dir-source urras 80550987E1D626E3EBA5E5E75A458DE0626D088C 208.83.223.34 208.83.223.34 443 80
-contact 4096R/E012B42D Jacob Appelbaum <jacob at appelbaum.net>
-vote-digest 08B1F8E4910F136E7FB7DFD52ABB2A9EDE939F0B
-dir-source moria1 D586D18309DED4CD6D57C18FDB97EFA96D330566 128.31.0.34 128.31.0.34 9131 9101
-contact 1024D/28988BF5 arma mit edu
-vote-digest 5006931FB78F7AE42B602697591DBA82AACEF533
-dir-source dizum E8A9C45EDE6D711294FADF8E7951F4DE6CA56B58 194.109.206.212 194.109.206.212 80 443
-contact 1024R/8D56913D Alex de Joode <adejoode at sabotage.org>
-vote-digest 3F1F1E071EC5F54115CB8EA9723E30A9386AB8CA
-dir-source gabelmoo ED03BB616EB2F60BEC80151114BB25CEF515B226 212.112.245.170 212.112.245.170 80 443
-contact 4096R/C5AA446D Sebastian Hahn <tor at sebastianhahn.net>
-vote-digest DF2EC9AD207831DED1D01BB889A9C4478DE2CFB9
-r sumkledi ABPSI4nNUNC3hKPkBhyzHozozrU 8mCr8Sl7RF4ENU4jb0FZFA/3do8 2012-07-12 04:01:55 178.218.213.229 80 0
-s Exit Fast Named Running Valid
-v Tor 0.2.2.35
-w Bandwidth=38
-p accept 80,443
-r Unnamed AEXri4INxBAZeyi0wvJZoC58nZs 9t4U465paxGASh0x5Tds8a8YiKo 2012-07-12 04:48:09 79.139.135.90 443 22
-s Fast HSDir Running V2Dir Valid
-v Tor 0.2.2.37
-w Bandwidth=35
-p reject 1-65535
-r ANONIONROUTER AHhuQ8zFQJdT8l42Axxc6m6kNwI uI7+jQ/T3kFVnl7H7TYE/7WJxi4 2012-07-12 04:40:31 93.128.55.236 24051 24052
-s Fast Named Running V2Dir Valid
-v Tor 0.2.2.37
-w Bandwidth=108
-p reject 1-65535
-r ph3x AMLCoWrttR1eX7fWFo/GazQ9gi8 ZJJnmKK6+9B2KOUSIPV49+Vprxs 2012-07-11 19:44:22 86.59.119.83 443 80
-s Fast Guard HSDir Named Running Stable V2Dir Valid
-v Tor 0.2.3.18-rc
-w Bandwidth=55300
-p reject 1-65535
-r nargothrond ANi/r5RGhUxfZ3simlDXFrf2O68 DsP6REKOns/vAUYNp3rfkCOSJFM 2012-07-11 18:25:37 173.11.83.10 9001 0
-s Fast Guard Named Running Stable Valid
-v Tor 0.2.3.18-rc
-w Bandwidth=543
-p reject 1-65535
-r default AN1sc6ymJ4WcSJ95VITqL0B5wDQ I9HQ2zph5Nuvf4FKANoKDf5vPV8 2012-07-11 18:48:22 82.243.60.52 443 9030
-s Fast Running V2Dir Valid
-v Tor 0.2.2.35
-w Bandwidth=92
-p reject 1-65535
-r catfesh AOTNBUkB8Lob/wiz7h9gtuDoT2Q 0Ycp54MgG+Ns+oEd3BIubFJdGGw 2012-07-12 08:26:51 80.177.151.82 9001 9030
-s Fast HSDir Running V2Dir Valid
-v Tor 0.2.2.37
-w Bandwidth=61
-p reject 1-65535
-r 111111 AO70G2tKrAacjjIITtSmzAFrSs8 l/yzMBbM4crBHivPKh69pDXuTRM 2012-07-12 04:03:06 178.170.144.91 80 0
-s Exit Fast Named Running Valid
-v Tor 0.2.2.34
-w Bandwidth=29
-p accept 80,443
-r Unnamed AP1onm4+6g+gIQMs1u9r6CeLX80 HY8Ud5ffEX28pglH+Vqvfle1xDQ 2012-07-12 09:09:15 189.41.71.79 443 0
-s Running Valid
-v Tor 0.2.2.37
-w Bandwidth=0
-p reject 1-65535
-directory-footer
-bandwidth-weights Wbd=3335 Wbe=0 Wbg=3536 Wbm=10000 Wdb=10000 Web=10000 Wed=3329 Wee=10000 Weg=3329 Wem=10000 Wgb=10000 Wgd=3335 Wgg=6464 Wgm=6464 Wmb=10000 Wmd=3335 Wme=0 Wmg=3536 Wmm=10000
-directory-signature 14C131DFC5C6F93646BE72FA1401C02A8DF2E8B4 BF112F1C6D5543CFD0A32215ACABD4197B5279AD
------BEGIN SIGNATURE-----
-HFXB4497LzESysYJ/4jJY83E5vLjhv+igIxD9LU6lf6ftkGeF+lNmIAIEKaMts8H
-mfWcW0b+jsrXcJoCxV5IrwCDF3u1aC3diwZY6yiG186pwWbOwE41188XI2DeYPwE
-I/TJmV928na7RLZe2mGHCAW3VQOvV+QkCfj05VZ8CsY=
------END SIGNATURE-----
-directory-signature 27B6B5996C426270A5C95488AA5BCEB6BCC86956 D5C30C15BB3F1DA27669C2D88439939E8F418FCF
------BEGIN SIGNATURE-----
-VAL+VWqcJiJtTZjFDz5/rS4WLfh8dOSnU2HYUb1ZgqM8PR1rFsoxpvaK9USrtkx9
-Byctu/flD3YOqGg+GpYQwU8w9tm7BGelD+dqg97DkJXmlPaXe/Z0nKW1UnCN9m93
-svyWCAqglEzxlK4H7ZfMlQbkMu7EFjXGzrn1gRVGOwg=
------END SIGNATURE-----
-directory-signature 49015F787433103580E3B66A1707A00E60F2D15B 1C915B9493589F97BAC764D1885A34BFC18C7E26
------BEGIN SIGNATURE-----
-fHAC5vdqotMtTVIxfqoNrlob2jAi3PP/urvsVA0xmaOzgYtJFIjY2iEWrrU4fRwe
-0M1vyCw+oztBrPKYukedkefE9ly/R30KVW2ezo5WpOO4y6oZpelb/jRKFoSRfbyB
-WdKsHSe2xlXPA0ySu1klpuMOZiQ8wgxh4x3oLGXnL5Q=
------END SIGNATURE-----
-directory-signature 585769C78764D58426B8B52B6651A5A71137189A 499D7CE5A1356045D629F43271EBF600D6F2CC9C
------BEGIN SIGNATURE-----
-IDOUDykw+tdCyyVmPSGUDahIeEEPMWxarEoH2gPuyExDqZkUc0ah6Eh736rVSD5Z
-R4nCjDNTQNr5byDfJk6cMDN9A/5P8uz421pnmLfs9SasLUjTdJt921jxJnSvSBeF
-hSZPNi5wl++Uw3j2zeclOXvAkkAEGi9Pi5Zf6QNlWFI=
------END SIGNATURE-----
-directory-signature 80550987E1D626E3EBA5E5E75A458DE0626D088C 2B9B419BB44728A5BE01651D6D1553FD14B6CFFB
------BEGIN SIGNATURE-----
-D2wVGni7cYHyXNqt9RvW/CUd8r7TgkfEp9OAJKojUyweiGMJOMEqDBE01e4Ov9Pd
-O9D46fjxWYGE9fN72xvD8CGoNcQgTtLpvypEfB96tKM3JYr5j4MCsdcOrQBkKGp7
-qf1Qfiw7aXahk8IfbgvmAvROlAMAxln7nVE0qenQWu4=
------END SIGNATURE-----
-directory-signature D586D18309DED4CD6D57C18FDB97EFA96D330566 8F0DEA35D1732D867FB94F5130364927DBCCBB8E
------BEGIN SIGNATURE-----
-cmrV1VUfCo9Smlc8BUblkBuSFqJdQkX/gd3ROOnpdRfogbsylq6xA7srCBGp1Z39
-To5Vk71AI0PIy031S6gKfOLgn9E5Bl0Agr60diFxjNn0ejR49MKJTjoDI+DmBlv4
-do+Bej+D8afl27LNH/QIYyzSkOl0eXSGtOEEuCQg/3A=
------END SIGNATURE-----
-directory-signature E8A9C45EDE6D711294FADF8E7951F4DE6CA56B58 9BE9A4CF520B6880EB42C5702BC521F03A6CF0FC
------BEGIN SIGNATURE-----
-UVXzEFkkjCpszLmoqQxAxSU83IS+fqrkIC4DCQZCEjRcXEvx3c56HUyTsew5WTFR
-XANCJn+V3DaxYLuL6L8xW7r9xOQNU970nGwocuJckxyDcLHloL8E226vIAn6mLmt
-a1Z6y8NzaQpv4fhdqhT7ETJo+chmf8bSX8qLLmaCIac=
------END SIGNATURE-----
-directory-signature ED03BB616EB2F60BEC80151114BB25CEF515B226 845CF1D0B370CA443A8579D18E7987E7E532F639
------BEGIN SIGNATURE-----
-DILsRCrtn6rDbNo3DF+L1/VVAd+V86PdZKg3Q9QooqVOGgU/7HrspV/K4lFbWcTT
-Zm+quRQfuKmB4xljwXpeRlABQR5eainlZBtrTFg056/dDrJqYXSwV/C391tAIDZs
-2TANs/4uLi94q6Ov+zE9zYUiF8jwnyXl/q/jKOYM8bE=
------END SIGNATURE-----
diff --git a/test/integ/descriptor/data/cached-consensus-v2 b/test/integ/descriptor/data/cached-consensus-v2
deleted file mode 100644
index ad64d40..0000000
--- a/test/integ/descriptor/data/cached-consensus-v2
+++ /dev/null
@@ -1,28 +0,0 @@
- at type network-status-2 1.0
-network-status-version 2
-dir-source 18.244.0.114 18.244.0.114 80
-fingerprint 719BE45DE224B607C53707D0E2143E2D423E74CF
-contact arma at mit dot edu
-published 2005-12-16 00:13:46
-dir-options Names Versions
-client-versions 0.0.9rc2,0.0.9rc3,0.0.9rc4-cvs,0.0.9rc4,0.0.9rc5-cvs,0.0.9rc5,0.0.9rc6-cvs,0.0.9rc6,0.0.9rc7-cvs,0.0.9rc7,0.0.9,0.0.9.1,0.0.9.2,0.0.9.3,0.0.9.4,0.0.9.5,0.0.9.6,0.0.9.7,0.0.9.8,0.0.9.9,0.0.9.10,0.1.0.0-alpha-cvs,0.1.0.1-rc,0.1.0.1-rc-cvs,0.1.0.2-rc,0.1.0.2-rc-cvs,0.1.0.3-rc,0.1.0.3-rc-cvs,0.1.0.4-rc,0.1.0.4-rc-cvs,0.1.0.5-rc,0.1.0.5-rc-cvs,0.1.0.6-rc,0.1.0.6-rc-cvs,0.1.0.7-rc,0.1.0.7-rc-cvs,0.1.0.8-rc,0.1.0.8-rc-cvs,0.1.0.9-rc,0.1.0.10,0.1.0.11,0.1.0.12,0.1.0.13,0.1.0.14,0.1.0.15,0.1.0.16,0.1.1.0-alpha-cvs,0.1.1.1-alpha,0.1.1.1-alpha-cvs,0.1.1.2-alpha,0.1.1.2-alpha-cvs,0.1.1.3-alpha,0.1.1.3-alpha-cvs,0.1.1.4-alpha,0.1.1.4-alpha-cvs,0.1.1.5-alpha,0.1.1.5-alpha-cvs,0.1.1.6-alpha,0.1.1.6-alpha-cvs,0.1.1.7-alpha,0.1.1.7-alpha-cvs,0.1.1.8-alpha,0.1.1.8-alpha-cvs,0.1.1.9-alpha,0.1.1.9-alpha-cvs,0.1.1.10-alpha,0.1.1.10-alpha-cvs
-server-versions 0.0.9rc2,0.0.9rc3,0.0.9rc4-cvs,0.0.9rc4,0.0.9rc5-cvs,0.0.9rc5,0.0.9rc6-cvs,0.0.9rc6,0.0.9rc7-cvs,0.0.9rc7,0.0.9,0.0.9.1,0.0.9.2,0.0.9.3,0.0.9.4,0.0.9.5,0.0.9.6,0.0.9.7,0.0.9.8,0.0.9.9,0.0.9.10,0.1.0.0-alpha-cvs,0.1.0.1-rc,0.1.0.1-rc-cvs,0.1.0.2-rc,0.1.0.2-rc-cvs,0.1.0.3-rc,0.1.0.3-rc-cvs,0.1.0.4-rc,0.1.0.4-rc-cvs,0.1.0.5-rc,0.1.0.5-rc-cvs,0.1.0.6-rc,0.1.0.6-rc-cvs,0.1.0.7-rc,0.1.0.7-rc-cvs,0.1.0.8-rc,0.1.0.8-rc-cvs,0.1.0.9-rc,0.1.0.10,0.1.0.11,0.1.0.12,0.1.0.13,0.1.0.14,0.1.0.15,0.1.0.16,0.1.1.0-alpha-cvs,0.1.1.1-alpha,0.1.1.1-alpha-cvs,0.1.1.2-alpha,0.1.1.2-alpha-cvs,0.1.1.3-alpha,0.1.1.3-alpha-cvs,0.1.1.4-alpha,0.1.1.4-alpha-cvs,0.1.1.5-alpha,0.1.1.5-alpha-cvs,0.1.1.6-alpha,0.1.1.6-alpha-cvs,0.1.1.7-alpha,0.1.1.7-alpha-cvs,0.1.1.8-alpha,0.1.1.8-alpha-cvs,0.1.1.9-alpha,0.1.1.9-alpha-cvs,0.1.1.10-alpha,0.1.1.10-alpha-cvs
-dir-signing-key
------BEGIN RSA PUBLIC KEY-----
-MIGJAoGBAOcrht/y5rkaahfX7sMe2qnpqoPibsjTSJaDvsUtaNP/Bq0MgNDGOR48
-rtwfqTRff275Edkp/UYw3G3vSgKCJr76/bqOHCmkiZrnPV1zxNfrK18gNw2Cxre0
-nTA+fD8JQqpPtb8b0SnG9kwy75eS//sRu7TErie2PzGMxrf9LH0LAgMBAAE=
------END RSA PUBLIC KEY-----
-
-r moria2 cZvkXeIktgfFNwfQ4hQ+LUI+dM8 t/Pwl1uHiJ3RKF/Vehsbthf2VDI 2005-12-15 06:57:18 18.244.0.114 443 80
-s Authority Fast Named Running Valid V2Dir
-r stnv CSi6RnBWxKaJ/uTvXXFIK2KJw9U ItGn7UGZvaftbEFu7NdpwY4fKlo 2005-12-15 16:24:42 84.16.236.173 9001 0
-s Named Valid
-r nggrplz CehYL/Dm+F4rjkHA3AucncRuaWg swLCsByU85jj7ziTlSawZR+CTdY 2005-12-15 23:25:50 194.109.109.109 9001 0
-s Fast Stable Running Valid
-directory-signature moria2
------BEGIN SIGNATURE-----
-2nXCxVje3wzn6HrIFRNMc0nc48AhMVpHZyPwRKGXkuYfTQG55uvwQDaFgJHud4RT
-27QhWltau3K1evhnzhKcpbTXwkVv1TBYJSzL6rEeAn8cQ7ZiCyqf4EJCaNcem3d2
-TpQQk3nNQF8z6UIvdlvP+DnJV4izWVkQEZgUZgIVM0E=
------END SIGNATURE-----
diff --git a/test/integ/descriptor/data/cached-microdescs b/test/integ/descriptor/data/cached-microdescs
deleted file mode 100644
index bd85ed6..0000000
--- a/test/integ/descriptor/data/cached-microdescs
+++ /dev/null
@@ -1,25 +0,0 @@
- at last-listed 2013-02-24 00:18:36
-onion-key
------BEGIN RSA PUBLIC KEY-----
-MIGJAoGBAMhPQtZPaxP3ukybV5LfofKQr20/ljpRk0e9IlGWWMSTkfVvBcHsa6IM
-H2KE6s4uuPHp7FqhakXAzJbODobnPHY8l1E4efyrqMQZXEQk2IMhgSNtG6YqUrVF
-CxdSKSSy0mmcBe2TOyQsahlGZ9Pudxfnrey7KcfqnArEOqNH09RpAgMBAAE=
------END RSA PUBLIC KEY-----
- at last-listed 2013-02-24 00:18:37
-onion-key
------BEGIN RSA PUBLIC KEY-----
-MIGJAoGBALCOxZdpMI2WO496njSQ2M7b4IgAGATqpJmH3So7lXOa25sK6o7JipgP
-qQE83K/t/xsMIpxQ/hHkft3G78HkeXXFc9lVUzH0HmHwYEu0M+PMVULSkG36MfEl
-7WeSZzaG+Tlnh9OySAzVyTsv1ZJsTQFHH9V8wuM0GOMo9X8DFC+NAgMBAAE=
------END RSA PUBLIC KEY-----
-ntor-onion-key r5572HzD+PMPBbXlZwBhsm6YEbxnYgis8vhZ1jmdI2k=
-family $6141629FA0D15A6AEAEF3A1BEB76E64C767B3174
- at last-listed 2013-02-24 00:18:36
-onion-key
------BEGIN RSA PUBLIC KEY-----
-MIGJAoGBAOWFQHxO+5kGuhwPUX5jB7wJCrTbSU0fZwolNV1t9UaDdjGDvIjIhdit
-y2sMbyd9K8lbQO7x9rQjNst5ZicuaSOs854XQddSjm++vMdjYbOcVMqnKGSztvpd
-w/1LVWFfhcBnsGi4JMGbmP+KUZG9A8kI9deSyJhfi35jA7UepiHHAgMBAAE=
------END RSA PUBLIC KEY-----
-a [2001:6b0:7:125::242]:9001
-p accept 80,443
diff --git a/test/integ/descriptor/data/cr_in_contact_line b/test/integ/descriptor/data/cr_in_contact_line
deleted file mode 100644
index ca6636c..0000000
--- a/test/integ/descriptor/data/cr_in_contact_line
+++ /dev/null
@@ -1,48 +0,0 @@
- at type server-descriptor 1.0
-router pogonip 75.5.248.48 9001 0 0
-platform Tor 0.1.2.17 on Darwin i386
-published 2007-09-03 10:15:53
-opt fingerprint 6DAB D62B C65D 4E6F E620 2931 57FC 7696 8DAB 9C9B
-uptime 5423
-bandwidth 98304 196608 0
-onion-key
------BEGIN RSA PUBLIC KEY-----
-MIGJAoGBAO6pORBI9rGRCPSb2lLwzl2630eFMlBXLgLtwQvYLY2s58cWpKjhI+ZF
-Q43+0e50pgC//JyS3Zac4O7MA5+BUfMvNOzKUdSIIGc6QjwST/larbDy14k9XalQ
-+G8rdnKb7gDNLrHjxhw5tC0JSTSTXI08bNsFeN1MoY8sFBjRlyDxAgMBAAE=
------END RSA PUBLIC KEY-----
-signing-key
------BEGIN RSA PUBLIC KEY-----
-MIGJAoGBALL0zokdBKjVcZA1P0K2jWK7afkvmB2bpRAdx5x1KRPZtB0mRxyjvgwL
-Tp0z3jLwhB4gzYY4LmS8p5RBH53gv4GsNW1rPSr99q4wjWyEY5AfjOdJZtFo/nhP
-881leOraicfcXmqrgtfbXZ0Jj+G9Ko216yEv4vBN/UV87kZHb/4tAgMBAAE=
------END RSA PUBLIC KEY-----
-opt write-history 2007-09-03 10:01:27 (900 s) 471040,874496,354304,0,0,0,0,0,0,0,0,60416,1160192,2017280,3396608,1585152,5990400,5469184,10896384,20568064,23362560,43372544,36531200,43439104,47890432,54303744,38624256,40414208,26827776,0,0,0,0,50176,611328,3126272,2952192,33792,28672
-opt read-history 2007-09-03 10:01:27 (900 s) 6890496,2631680,2260992,0,0,0,0,0,0,0,0,1096704,1856512,1133568,1038336,1390592,3272704,2115584,6868992,13585408,13746176,30726144,25562112,28556288,36113408,35810304,25213952,30357504,13510656,0,0,0,0,964608,568320,966656,1991680,17408,140288
-opt hibernating 1
-contact jie1 at pacbell dot net -----BEGIN PGP PUBLIC KEY BLOCK-----Version: GnuPG v1 dot 4 dot 7 (Darwin)mQGiBEbb0rcRBADqBiUXsmtpJifh74irNnkHbhKMj8O4TqenaZYhdjLWouZsZd07mTQoP40G4zqOrVEOOcXpdSiRnHWJYfgTnkibNZrOZEZLn3H1ywpovEgESmoGEdAXoid3XuIYRpRnqoafbFg9sg+OofX/mJuntVuoIdHfkHYnHlcShfKYQX8x4wCgqVSLVrjINDUy9YRznpj+TeoflG8EALoM4xHmIAhSKhviZr6pd86OAYF5bB8iY+koiQtQ066Upypj+dUMKeYWKkWp3loY5KwtkA0rBd2+nhf/PRJyn387mfSiZ91NZ+Pph29dERida1naMWOgv+XZnqDH0bk9BD12QqM/btawGiyOf3fdtLLseNzuiHr9gO2NjKxR5jjgBACop3bFpx2nfELj/NzS/umtUaQJ139BI0BJOHrVeSffMAcuH3v5B3WJVJ5vJb5kvvD9L7fIZNkAicpLhMUG4Lz6zNXMNGSUlCAOvHoQzBUMz6EVE7Ip73qX9viuLARFMf25qi+SsZJB18l3t/I+UgW+VTimZZgoHmVtX0nVZL+yvbQWSklFIDxqaWUxQHBhY2JlbGwubmV0PohgBBMRAgAgBQJG29K3AhsjBgsJCAcDAgQVAggDBBYCAwECHgECF4AACgkQv5Dmts313gKv4ACcDggg0o/+ys4Djkbltvwoa9IGFoEAn1yjv8W1Kj5WkdrV8oZudjAq2zjNuQQNBEbb0rcQEAC6mKhgeRAQtJLI7JGEqjO6hOu5mq1+UYgpZnuQq8FxCkG2MQ0sqOjXcZPIS55wWBuUNT+G+6B+cACJHqKo+W44zXtEXaTBdHVEi135cBv037fNtH73fRK/D43QKHu+r9luxW//mMQ
 dLhODNFSiowY8ZlOtPEYzqBiJBz0Shr0vN4EM3pkQZh7Jnmntw0Gu/hQIQONIhBFKNYYg6Zuar8j7kTHZJvDnZh5N6C7B9pilZsAp6vuCF7CHeUx5hbyzubfUHqpqnMr2pP3gnSkC/tbxsWPxWhd723SfRZpbK9TFJg65n4Qr2rYF1ccGe5mpRXRWxMA4sNVmmUOPgEjsbjMgvjUBmiAdeU+xEnNGJPyamPnwvKl5MIR1Tn2BbsY+o9g6RqsK7F6ql+RP4ya1oblluOkZMbjxF8BB6Hbe0xQzp0mSKJxSJN4wY+9xDa9SKAn9MgeI23hA9B9iwhWeqz+YvLNJu82UfBPSrr8xfM5htvNHapeGaRc6QsLcyMLecZ9/Rwpjp8iO4BNFrXGErgpc5SwzZFzMbeu2ymTZQ5lXXGBDNjK0C90+Z15MuUIyXswVeCP08iWgZCbPv6Y2zlVoYEuyG1BKUAkig3vzcG7iVp0HT1J191KwIPfdAS6DnXcSsb39mfR9/47zYlLsePrbHyrA/p9z4mBDHpsqlKqUTwADBQ//a500OJpnrVhjvXqJy6/6WI4zKKq56p0KDmGitKWlexEZuolfeJXxg2F4Y997XkOCTZjRlJEeoAQ1n4S5Vs54dlSS0Fsf1DOPflP5zmpEr3nV6MFUkNpHUINvgxYhTPBxNq6k7DRXklBZu5aq8QbNzgbTlnS5tANMcYjlRGrKqjwmZb7gmO3EiMp5tH8L71fA02zAyfVStpjsWBEoISrZHyoWkOP6v+JAIhLss9lSCqRT1EPdX/Zhn+bsWjfvPig+TOIAlbEVHtLREIVvjm3Okjt4hTrSGXeff6naeuGDPSOkDfFCiUTNjqP6uUmQMMR3F0HdycOuHCp/V4X/3cwKMqSoVBC278AOPptNijwBkR+1tGMH6g3+s8VO4EsuOYpnfTFhbSPuEGBzppD56Ezu9daWm2hcxqoKNmCBx+HYfOmW
 PXB8R8JRiftEPxpCGnb6qCAzrfIpjjBHtwASIQ4IbJds7mjobiBQGC93mOt5sUNkplixY77kv8h9ZYgBITJryo719IPOTG6nXCcPfke5Jp1uAx7pYpqTZqWfrio0TCT0q6tP7+3ZGecFDN6xV3JQ4hICkTW4uOtfV3803NGM8bmZCV65EWRlzY7gOLM+Wat2G31soA5M7cAe/Q1ro7yqBDzAIAA3DFT58lRJY/BCXkJPDJ2ABFl/O3OHUD++pReISQQYEQIACQUCRtvStwIbDAAKCRC/kOa2zfXeAl9/AJ4lCZTgLIAwfh2Kgg8RowxlGrO+5ACfagQ9rlfx2oxCWijYwpYFRk3NhCY==Xaw3-----END PGP PUBLIC KEY BLOCK-----
-reject 0.0.0.0/8:*
-reject 169.254.0.0/16:*
-reject 127.0.0.0/8:*
-reject 192.168.0.0/16:*
-reject 10.0.0.0/8:*
-reject 172.16.0.0/12:*
-reject *:25
-reject *:119
-reject *:135-139
-reject *:445
-reject *:465
-reject *:563
-reject *:587
-reject *:1214
-reject *:4661-4666
-reject *:6346-6429
-reject *:6699
-reject *:6881-6999
-accept *:*
-router-signature
------BEGIN SIGNATURE-----
-PrL8cocQcTTmQp0wy0dxlFOczGv8s2/hZzAafOhjdM0QqLMLh9FW/4RqH0jcDz+P
-t1QU3xIF3JV1fsW0rx4vFYKXG7WrLrrQp3gGuJvrXO+132UAsrs7eJKk3yxEi2lq
-FNY26XGZqTDPY4WPl08/JgXXfckgFFg76E4Jcl5/tY8=
------END SIGNATURE-----
diff --git a/test/integ/descriptor/data/descriptor_archive.tar b/test/integ/descriptor/data/descriptor_archive.tar
deleted file mode 100644
index 55d29d1..0000000
Binary files a/test/integ/descriptor/data/descriptor_archive.tar and /dev/null differ
diff --git a/test/integ/descriptor/data/descriptor_archive.tar.bz2 b/test/integ/descriptor/data/descriptor_archive.tar.bz2
deleted file mode 100644
index 2950c57..0000000
Binary files a/test/integ/descriptor/data/descriptor_archive.tar.bz2 and /dev/null differ
diff --git a/test/integ/descriptor/data/descriptor_archive.tar.gz b/test/integ/descriptor/data/descriptor_archive.tar.gz
deleted file mode 100644
index 325adb7..0000000
Binary files a/test/integ/descriptor/data/descriptor_archive.tar.gz and /dev/null differ
diff --git a/test/integ/descriptor/data/example_descriptor b/test/integ/descriptor/data/example_descriptor
deleted file mode 100644
index 6ee442c..0000000
--- a/test/integ/descriptor/data/example_descriptor
+++ /dev/null
@@ -1,31 +0,0 @@
- at type server-descriptor 1.0
-router caerSidi 71.35.133.197 9001 0 0
-platform Tor 0.2.1.30 on Linux x86_64
-opt protocols Link 1 2 Circuit 1
-published 2012-03-01 17:15:27
-opt fingerprint A756 9A83 B570 6AB1 B1A9 CB52 EFF7 D2D3 2E45 53EB
-uptime 588217
-bandwidth 153600 256000 104590
-opt extra-info-digest D225B728768D7EA4B5587C13A7A9D22EBBEE6E66
-onion-key
------BEGIN RSA PUBLIC KEY-----
-MIGJAoGBAJv5IIWQ+WDWYUdyA/0L8qbIkEVH/cwryZWoIaPAzINfrw1WfNZGtBmg
-skFtXhOHHqTRN4GPPrZsAIUOQGzQtGb66IQgT4tO/pj+P6QmSCCdTfhvGfgTCsC+
-WPi4Fl2qryzTb3QO5r5x7T8OsG2IBUET1bLQzmtbC560SYR49IvVAgMBAAE=
------END RSA PUBLIC KEY-----
-signing-key
------BEGIN RSA PUBLIC KEY-----
-MIGJAoGBAKwvOXyztVKnuYvpTKt+nS3XIKeO8dVungi8qGoeS+6gkR6lDtGfBTjd
-uE9UIkdAl9zi8/1Ic2wsUNHE9jiS0VgeupITGZY8YOyMJJ/xtV1cqgiWhq1dUYaq
-51TOtUogtAPgXPh4J+V8HbFFIcCzIh3qCO/xXo+DSHhv7SSif1VpAgMBAAE=
------END RSA PUBLIC KEY-----
-family $0CE3CFB1E9CC47B63EA8869813BF6FAB7D4540C1 $1FD187E8F69A9B74C9202DC16A25B9E7744AB9F6 $74FB5EFA6A46DE4060431D515DC9A790E6AD9A7C $77001D8DA9BF445B0F81AA427A675F570D222E6A $B6D83EC2D9E18B0A7A33428F8CFA9C536769E209 $D2F37F46182C23AB747787FD657E680B34EAF892 $E0BD57A11F00041A9789577C53A1B784473669E4 $E5E3E9A472EAF7BE9682B86E92305DB4C71048EF
-opt hidden-service-dir
-contact www.atagar.com/contact
-reject *:*
-router-signature
------BEGIN SIGNATURE-----
-dskLSPz8beUW7bzwDjR6EVNGpyoZde83Ejvau+5F2c6cGnlu91fiZN3suE88iE6e
-758b9ldq5eh5mapb8vuuV3uO+0Xsud7IEOqfxdkmk0GKnUX8ouru7DSIUzUL0zqq
-Qlx9HNCqCY877ztFRC624ja2ql6A2hBcuoYMbkHjcQ4=
------END SIGNATURE-----
diff --git a/test/integ/descriptor/data/extrainfo_bridge_descriptor b/test/integ/descriptor/data/extrainfo_bridge_descriptor
deleted file mode 100644
index e78c354..0000000
--- a/test/integ/descriptor/data/extrainfo_bridge_descriptor
+++ /dev/null
@@ -1,22 +0,0 @@
- at type bridge-extra-info 1.0
-extra-info ec2bridgereaac65a3 1EC248422B57D9C0BD751892FE787585407479A4
-published 2012-06-08 02:21:27
-write-history 2012-06-08 02:10:38 (900 s) 343040,991232,5649408,49548288,24411136,486400,465920,975872,930816,758784,5650432,1844224,4387840,7417856,789504,623616,7376896,3940352,10039296,6054912,2051072,462848,4260864,5298176,500736,717824,714752,1370112,760832,576512,1521664,3986432,724992,615424,7079936,20995072,17465344,2592768,1014784,3082240,11468800,2770944,5660672,7646208,3385344,5404672,1158144,7741440,3275776,5239808,5291008,2197504,6070272,4919296,1180672,2245632,2362368,1641472,6153216,4660224,9184256,1488896,9736192,768000,65536,176128,2268160,3023872,704512,905216,866304,2375680,3688448,5181440,5433344,5259264,8285184,9391104,7015424,7180288,3436544,3713024,4473856,10705920,10529792,3154944,1815552,3443712,4566016,2668544,2688000,2391040,3181568,2749440,3214336,8974336
-read-history 2012-06-08 02:10:38 (900 s) 337920,437248,3995648,48726016,24125440,479232,477184,1010688,988160,772096,6388736,776192,3093504,7187456,787456,657408,7360512,3588096,9319424,5211136,2034688,474112,4222976,5154816,490496,1878016,610304,757760,667648,593920,1484800,3638272,1307648,672768,6614016,19262464,14863360,2612224,525312,1494016,11119616,3160064,1986560,5619712,3290112,5457920,1145856,7060480,3262464,5570560,5933056,2245632,4548608,3614720,829440,1765376,2379776,2531328,4306944,4244480,9225216,1054720,9806848,880640,66560,192512,2135040,3038208,830464,804864,941056,2386944,3660800,4835328,6406144,5152768,8714240,9145344,6902784,6781952,3648512,3876864,5474304,9958400,10851328,3322880,2239488,4790272,4343808,2404352,2615296,1551360,3295232,2869248,2924544,4941824
-dirreq-write-history 2012-06-08 02:10:38 (900 s) 5120,664576,2419712,578560,3072,5120,5120,3072,11264,5120,54272,1130496,1381376,202752,47104,0,6144,1064960,716800,835584,2048,0,3072,128000,4096,1024,222208,604160,129024,7168,118784,353280,0,0,761856,2236416,2574336,3072,526336,2037760,214016,726016,3592192,2205696,134144,1024,86016,656384,8192,9216,402432,14336,1617920,1436672,445440,481280,7168,1024,1959936,442368,14336,544768,4096,1024,0,4096,1052672,78848,0,223232,51200,5120,5120,1191936,1024,579584,2048,231424,68608,1305600,38912,4096,8192,1433600,5120,4096,330752,574464,382976,225280,940032,1185792,6144,1024,374784,4173824
-dirreq-read-history 2012-06-08 02:10:38 (900 s) 0,71680,99328,25600,0,0,0,0,0,0,3072,45056,74752,13312,2048,0,0,69632,18432,39936,0,0,0,8192,0,0,16384,25600,9216,0,7168,8192,0,0,96256,92160,138240,0,18432,128000,0,81920,149504,113664,8192,0,11264,46080,2048,0,25600,0,75776,77824,0,6144,19456,0,58368,14336,0,21504,0,0,0,0,25600,6144,0,0,5120,4096,0,54272,0,23552,0,0,4096,64512,2048,0,0,34816,0,0,6144,8192,10240,0,31744,48128,0,0,10240,236544
-geoip-db-digest A27BE984989AB31C50D0861C7106B17A7EEC3756
-dirreq-stats-end 2012-06-07 06:33:46 (86400 s)
-dirreq-v3-ips 
-dirreq-v2-ips 
-dirreq-v3-reqs 
-dirreq-v2-reqs 
-dirreq-v3-resp ok=72,not-enough-sigs=0,unavailable=0,not-found=0,not-modified=0,busy=0
-dirreq-v2-resp ok=0,unavailable=0,not-found=0,not-modified=0,busy=0
-dirreq-v3-direct-dl complete=0,timeout=0,running=0
-dirreq-v2-direct-dl complete=0,timeout=0,running=0
-dirreq-v3-tunneled-dl complete=68,timeout=4,running=0,min=2626,d1=7795,d2=14369,q1=18695,d3=29117,d4=52562,md=70626,d6=102271,d7=164175,q3=181522,d8=271682,d9=563791,max=32136142
-dirreq-v2-tunneled-dl complete=0,timeout=0,running=0
-bridge-stats-end 2012-06-07 06:33:53 (86400 s)
-bridge-ips cn=16,ir=16,sy=16,us=16,??=8,bn=8,br=8,by=8,cu=8,dk=8,dz=8,es=8,lb=8,ph=8,sd=8,tw=8
-router-digest 00A2AECCEAD3FEE033CFE29893387143146728EC
diff --git a/test/integ/descriptor/data/extrainfo_relay_descriptor b/test/integ/descriptor/data/extrainfo_relay_descriptor
deleted file mode 100644
index ecaabe0..0000000
--- a/test/integ/descriptor/data/extrainfo_relay_descriptor
+++ /dev/null
@@ -1,13 +0,0 @@
- at type extra-info 1.0
-extra-info NINJA B2289C3EAB83ECD6EB916A2F481A02E6B76A0A48
-published 2012-05-05 17:03:50
-write-history 2012-05-05 17:02:45 (900 s) 1082368,19456,50176,272384,485376,1850368,1132544,1790976,2459648,4091904,6310912,13701120,3209216,3871744,7873536,5440512,7287808,10561536,9979904,11247616,11982848,7590912,10611712,20728832,38534144,6839296,3173376,16678912
-read-history 2012-05-05 17:02:45 (900 s) 3309568,9216,41984,27648,123904,2004992,364544,576512,1607680,3808256,4672512,12783616,2938880,2562048,7348224,3574784,6488064,10954752,9359360,4438016,6286336,6438912,4502528,10720256,38165504,1524736,2336768,8186880
-dirreq-write-history 2012-05-05 17:02:45 (900 s) 0,0,0,227328,349184,382976,738304,1171456,850944,657408,1675264,987136,702464,1335296,587776,1941504,893952,533504,695296,6828032,6326272,1287168,6310912,10085376,1048576,5372928,894976,8610816
-dirreq-read-history 2012-05-05 17:02:45 (900 s) 0,0,0,0,33792,27648,48128,46080,60416,51200,63488,64512,45056,27648,37888,48128,57344,34816,46080,50176,37888,51200,25600,33792,39936,32768,28672,30720
-router-signature
------BEGIN SIGNATURE-----
-K5FSywk7qvw/boA4DQcqkls6Ize5vcBYfhQ8JnOeRQC9+uDxbnpm3qaYN9jZ8myj
-k0d2aofcVbHr4fPQOSST0LXDrhFl5Fqo5um296zpJGvRUeO6S44U/EfJAGShtqWw
-7LZqklu+gVvhMKREpchVqlAwXkWR44VENm24Hs+mT3M=
------END SIGNATURE-----
diff --git a/test/integ/descriptor/data/metrics_cert b/test/integ/descriptor/data/metrics_cert
deleted file mode 100644
index 510bfd8..0000000
--- a/test/integ/descriptor/data/metrics_cert
+++ /dev/null
@@ -1,34 +0,0 @@
- at type dir-key-certificate-3 1.0
-dir-key-certificate-version 3
-fingerprint 14C131DFC5C6F93646BE72FA1401C02A8DF2E8B4
-dir-key-published 2008-05-09 21:13:26
-dir-key-expires 2009-05-09 21:13:26
-dir-identity-key
------BEGIN RSA PUBLIC KEY-----
-MIIBigKCAYEA7cZXvDRxfjDYtr9/9UsQ852+6cmHMr8VVh8GkLwbq3RzqjkULwQ2
-R9mFvG4FnqMcMKXi62rYYA3fZL1afhT804cpvyp/D3dPM8QxW88fafFAgIFP4LiD
-0JYjnF8cva5qZ0nzlWnMXLb32IXSvsGSE2FRyAV0YN9a6k967LSgCfUnZ+IKMezW
-1vhL9YK4QIfsDowgtVsavg63GzGmA7JvZmn77+/J5wKz11vGr7Wttf8XABbH2taX
-O9j/KGBOX2OKhoF3mXfZSmUO2dV9NMwtkJ7zD///Ny6sfApWV6kVP4O9TdG3bAsl
-+fHCoCKgF/jAAWzh6VckQTOPzQZaH5aMWfXrDlzFWg17MjonI+bBTD2Ex2pHczzJ
-bN7coDMRH2SuOXv8wFf27KdUxZ/GcrXSRGzlRLygxqlripUanjVGN2JvrVQVr0kz
-pjNjiZl2z8ZyZ5d4zQuBi074JPGgx62xAstP37v1mPw14sIWfLgY16ewYuS5bCxV
-lyS28jsPht9VAgMBAAE=
------END RSA PUBLIC KEY-----
-dir-signing-key
------BEGIN RSA PUBLIC KEY-----
-MIGJAoGBAOeE3Qr1Km97gTgiB3io0EU0fqHW2ESMXVHeQuNDtCWBa0XSCEG6gx4B
-ZkkHjfVWqGQ7TmmzjYP9L9uCgtoKfhSvJA2w9NUMtMl8sgZmF4lcGpXXvGY9a566
-Bn+3wP0lMhb/I8CPVPX+NWEjgl1noZxo1C59SO/iALGQOpxRYgmbAgMBAAE=
------END RSA PUBLIC KEY-----
-dir-key-certification
------BEGIN SIGNATURE-----
-asvWwaMq34OfHoWUhAwh4+JDOuEUZJVIHQnedOYfQH8asS2QvW3Ma93OhrwVOC6b
-FyKmTJmJsl0MJGiC7tcEOlL6knsKE4CsuIw/PEcu2Rnm+R9zWxQuMYiHvGQMoDxl
-giOhLLs4LlzAAJlbfbd3hjF4STVAtTwmxYuIjb1Mq/JfAsx/wH3TLXgVZwj32w9s
-zUd9KZwwLzFiiHpC+U7zh6+wRsZfo2tlpmcaP1dTSINgVbdzPJ/DOUlx9nwTCBsE
-AQpUx2DpAikwrpw0zDqpQvYulcQlNLWFN/y/PkmiK8mIJk0OBMiQA7JgqWamnnk4
-PwqaGv483LkBF+25JFGJmnUVve3RMc+s61+2kBcjfUMed4QaHkeCMHqlRqpfQVkk
-RY22NXCwrJvSMEwiy7acC8FGysqwHRyE356+Rw6TB43g3Tno9KaHEK7MHXjSHwNs
-GM9hAsAMRX9Ogqhq5UjDNqEsvDKuyVeyh7unSZEOip9Zr6K/+7VsVPNb8vfBRBjo
------END SIGNATURE-----
diff --git a/test/integ/descriptor/data/metrics_consensus b/test/integ/descriptor/data/metrics_consensus
deleted file mode 100644
index 22107bd..0000000
--- a/test/integ/descriptor/data/metrics_consensus
+++ /dev/null
@@ -1,121 +0,0 @@
- at type network-status-consensus-3 1.0
-network-status-version 3
-vote-status consensus
-consensus-method 12
-valid-after 2012-07-12 10:00:00
-fresh-until 2012-07-12 11:00:00
-valid-until 2012-07-12 13:00:00
-voting-delay 300 300
-client-versions 0.2.2.35,0.2.2.36,0.2.2.37,0.2.3.10-alpha,0.2.3.11-alpha,0.2.3.12-alpha,0.2.3.13-alpha,0.2.3.14-alpha,0.2.3.15-alpha,0.2.3.16-alpha,0.2.3.17-beta,0.2.3.18-rc,0.2.3.19-rc
-server-versions 0.2.2.35,0.2.2.36,0.2.2.37,0.2.3.10-alpha,0.2.3.11-alpha,0.2.3.12-alpha,0.2.3.13-alpha,0.2.3.14-alpha,0.2.3.15-alpha,0.2.3.16-alpha,0.2.3.17-beta,0.2.3.18-rc,0.2.3.19-rc
-known-flags Authority BadExit Exit Fast Guard HSDir Named Running Stable Unnamed V2Dir Valid
-params CircuitPriorityHalflifeMsec=30000 bwauthpid=1
-dir-source tor26 14C131DFC5C6F93646BE72FA1401C02A8DF2E8B4 86.59.21.38 86.59.21.38 80 443
-contact Peter Palfrader
-vote-digest 0B6D1E9A300B895AA2D0B427F92917B6995C3C1C
-dir-source turtles 27B6B5996C426270A5C95488AA5BCEB6BCC86956 76.73.17.194 76.73.17.194 9030 9090
-contact Mike Perry <mikeperryTAfsckedTODorg>
-vote-digest 904B1974B9879D02B4ADFB81D7E9B4E07D768A5A
-dir-source maatuska 49015F787433103580E3B66A1707A00E60F2D15B 171.25.193.9 171.25.193.9 443 80
-contact 4096R/23291265 Linus Nordberg <linus at nordberg.se>
-vote-digest A8839355BAC373320B8CEDD0A0A09DAAA1637E3A
-dir-source dannenberg 585769C78764D58426B8B52B6651A5A71137189A dannenberg.ccc.de 193.23.244.244 80 443
-contact Andreas Lehner <anonymizer at ccc.de>
-vote-digest 416B73C49E717B0A5D61A4F634DCCF94611802E7
-dir-source urras 80550987E1D626E3EBA5E5E75A458DE0626D088C 208.83.223.34 208.83.223.34 443 80
-contact 4096R/E012B42D Jacob Appelbaum <jacob at appelbaum.net>
-vote-digest 08B1F8E4910F136E7FB7DFD52ABB2A9EDE939F0B
-dir-source moria1 D586D18309DED4CD6D57C18FDB97EFA96D330566 128.31.0.34 128.31.0.34 9131 9101
-contact 1024D/28988BF5 arma mit edu
-vote-digest 5006931FB78F7AE42B602697591DBA82AACEF533
-dir-source dizum E8A9C45EDE6D711294FADF8E7951F4DE6CA56B58 194.109.206.212 194.109.206.212 80 443
-contact 1024R/8D56913D Alex de Joode <adejoode at sabotage.org>
-vote-digest 3F1F1E071EC5F54115CB8EA9723E30A9386AB8CA
-dir-source gabelmoo ED03BB616EB2F60BEC80151114BB25CEF515B226 212.112.245.170 212.112.245.170 80 443
-contact 4096R/C5AA446D Sebastian Hahn <tor at sebastianhahn.net>
-vote-digest DF2EC9AD207831DED1D01BB889A9C4478DE2CFB9
-r sumkledi ABPSI4nNUNC3hKPkBhyzHozozrU 8mCr8Sl7RF4ENU4jb0FZFA/3do8 2012-07-12 04:01:55 178.218.213.229 80 0
-s Exit Fast Named Running Valid
-v Tor 0.2.2.35
-w Bandwidth=38
-p accept 80,443
-r Unnamed AEXri4INxBAZeyi0wvJZoC58nZs 9t4U465paxGASh0x5Tds8a8YiKo 2012-07-12 04:48:09 79.139.135.90 443 22
-s Fast HSDir Running V2Dir Valid
-v Tor 0.2.2.37
-w Bandwidth=35
-p reject 1-65535
-r ANONIONROUTER AHhuQ8zFQJdT8l42Axxc6m6kNwI uI7+jQ/T3kFVnl7H7TYE/7WJxi4 2012-07-12 04:40:31 93.128.55.236 24051 24052
-s Fast Named Running V2Dir Valid
-v Tor 0.2.2.37
-w Bandwidth=108
-p reject 1-65535
-r ph3x AMLCoWrttR1eX7fWFo/GazQ9gi8 ZJJnmKK6+9B2KOUSIPV49+Vprxs 2012-07-11 19:44:22 86.59.119.83 443 80
-s Fast Guard HSDir Named Running Stable V2Dir Valid
-v Tor 0.2.3.18-rc
-w Bandwidth=55300
-p reject 1-65535
-r nargothrond ANi/r5RGhUxfZ3simlDXFrf2O68 DsP6REKOns/vAUYNp3rfkCOSJFM 2012-07-11 18:25:37 173.11.83.10 9001 0
-s Fast Guard Named Running Stable Valid
-v Tor 0.2.3.18-rc
-w Bandwidth=543
-p reject 1-65535
-r default AN1sc6ymJ4WcSJ95VITqL0B5wDQ I9HQ2zph5Nuvf4FKANoKDf5vPV8 2012-07-11 18:48:22 82.243.60.52 443 9030
-s Fast Running V2Dir Valid
-v Tor 0.2.2.35
-w Bandwidth=92
-p reject 1-65535
-r catfesh AOTNBUkB8Lob/wiz7h9gtuDoT2Q 0Ycp54MgG+Ns+oEd3BIubFJdGGw 2012-07-12 08:26:51 80.177.151.82 9001 9030
-s Fast HSDir Running V2Dir Valid
-v Tor 0.2.2.37
-w Bandwidth=61
-p reject 1-65535
-directory-footer
-bandwidth-weights Wbd=3335 Wbe=0 Wbg=3536 Wbm=10000 Wdb=10000 Web=10000 Wed=3329 Wee=10000 Weg=3329 Wem=10000 Wgb=10000 Wgd=3335 Wgg=6464 Wgm=6464 Wmb=10000 Wmd=3335 Wme=0 Wmg=3536 Wmm=10000
-directory-signature 14C131DFC5C6F93646BE72FA1401C02A8DF2E8B4 BF112F1C6D5543CFD0A32215ACABD4197B5279AD
------BEGIN SIGNATURE-----
-HFXB4497LzESysYJ/4jJY83E5vLjhv+igIxD9LU6lf6ftkGeF+lNmIAIEKaMts8H
-mfWcW0b+jsrXcJoCxV5IrwCDF3u1aC3diwZY6yiG186pwWbOwE41188XI2DeYPwE
-I/TJmV928na7RLZe2mGHCAW3VQOvV+QkCfj05VZ8CsY=
------END SIGNATURE-----
-directory-signature 27B6B5996C426270A5C95488AA5BCEB6BCC86956 D5C30C15BB3F1DA27669C2D88439939E8F418FCF
------BEGIN SIGNATURE-----
-VAL+VWqcJiJtTZjFDz5/rS4WLfh8dOSnU2HYUb1ZgqM8PR1rFsoxpvaK9USrtkx9
-Byctu/flD3YOqGg+GpYQwU8w9tm7BGelD+dqg97DkJXmlPaXe/Z0nKW1UnCN9m93
-svyWCAqglEzxlK4H7ZfMlQbkMu7EFjXGzrn1gRVGOwg=
------END SIGNATURE-----
-directory-signature 49015F787433103580E3B66A1707A00E60F2D15B 1C915B9493589F97BAC764D1885A34BFC18C7E26
------BEGIN SIGNATURE-----
-fHAC5vdqotMtTVIxfqoNrlob2jAi3PP/urvsVA0xmaOzgYtJFIjY2iEWrrU4fRwe
-0M1vyCw+oztBrPKYukedkefE9ly/R30KVW2ezo5WpOO4y6oZpelb/jRKFoSRfbyB
-WdKsHSe2xlXPA0ySu1klpuMOZiQ8wgxh4x3oLGXnL5Q=
------END SIGNATURE-----
-directory-signature 585769C78764D58426B8B52B6651A5A71137189A 499D7CE5A1356045D629F43271EBF600D6F2CC9C
------BEGIN SIGNATURE-----
-IDOUDykw+tdCyyVmPSGUDahIeEEPMWxarEoH2gPuyExDqZkUc0ah6Eh736rVSD5Z
-R4nCjDNTQNr5byDfJk6cMDN9A/5P8uz421pnmLfs9SasLUjTdJt921jxJnSvSBeF
-hSZPNi5wl++Uw3j2zeclOXvAkkAEGi9Pi5Zf6QNlWFI=
------END SIGNATURE-----
-directory-signature 80550987E1D626E3EBA5E5E75A458DE0626D088C 2B9B419BB44728A5BE01651D6D1553FD14B6CFFB
------BEGIN SIGNATURE-----
-D2wVGni7cYHyXNqt9RvW/CUd8r7TgkfEp9OAJKojUyweiGMJOMEqDBE01e4Ov9Pd
-O9D46fjxWYGE9fN72xvD8CGoNcQgTtLpvypEfB96tKM3JYr5j4MCsdcOrQBkKGp7
-qf1Qfiw7aXahk8IfbgvmAvROlAMAxln7nVE0qenQWu4=
------END SIGNATURE-----
-directory-signature D586D18309DED4CD6D57C18FDB97EFA96D330566 8F0DEA35D1732D867FB94F5130364927DBCCBB8E
------BEGIN SIGNATURE-----
-cmrV1VUfCo9Smlc8BUblkBuSFqJdQkX/gd3ROOnpdRfogbsylq6xA7srCBGp1Z39
-To5Vk71AI0PIy031S6gKfOLgn9E5Bl0Agr60diFxjNn0ejR49MKJTjoDI+DmBlv4
-do+Bej+D8afl27LNH/QIYyzSkOl0eXSGtOEEuCQg/3A=
------END SIGNATURE-----
-directory-signature E8A9C45EDE6D711294FADF8E7951F4DE6CA56B58 9BE9A4CF520B6880EB42C5702BC521F03A6CF0FC
------BEGIN SIGNATURE-----
-UVXzEFkkjCpszLmoqQxAxSU83IS+fqrkIC4DCQZCEjRcXEvx3c56HUyTsew5WTFR
-XANCJn+V3DaxYLuL6L8xW7r9xOQNU970nGwocuJckxyDcLHloL8E226vIAn6mLmt
-a1Z6y8NzaQpv4fhdqhT7ETJo+chmf8bSX8qLLmaCIac=
------END SIGNATURE-----
-directory-signature ED03BB616EB2F60BEC80151114BB25CEF515B226 845CF1D0B370CA443A8579D18E7987E7E532F639
------BEGIN SIGNATURE-----
-DILsRCrtn6rDbNo3DF+L1/VVAd+V86PdZKg3Q9QooqVOGgU/7HrspV/K4lFbWcTT
-Zm+quRQfuKmB4xljwXpeRlABQR5eainlZBtrTFg056/dDrJqYXSwV/C391tAIDZs
-2TANs/4uLi94q6Ov+zE9zYUiF8jwnyXl/q/jKOYM8bE=
------END SIGNATURE-----
diff --git a/test/integ/descriptor/data/metrics_server_desc_multiple b/test/integ/descriptor/data/metrics_server_desc_multiple
deleted file mode 100644
index aa3d044..0000000
--- a/test/integ/descriptor/data/metrics_server_desc_multiple
+++ /dev/null
@@ -1,87 +0,0 @@
- at type server-descriptor 1.0
-router anonion 31.54.58.167 443 0 0
-platform Tor 0.2.3.22-rc on Windows XP
-opt protocols Link 1 2 Circuit 1
-published 2012-09-17 07:28:01
-opt fingerprint 9A5E C5BB 8665 17E5 3962 AF4D 3E77 6536 694B 069E
-uptime 0
-bandwidth 8388608 8388608 442368
-opt extra-info-digest 0DED759EDA005BFD735E91D3E2DBF22D2D685F12
-onion-key
------BEGIN RSA PUBLIC KEY-----
-MIGJAoGBAM+vu//ysOgcbCwri0RnunH8xNwswnTPbbusad7im2lXJuPjMbTFlkcn
-3yPxbL1aD3p/PHPfLKOOPcFDVlCma/N8tJ6CEeJn6pZcrvgsU/d1EASQyrhpXFgL
-oH/+zfg4ir52dZp4gS03P16i9m4HB4YTewo62cIuZoZKq1smReMzAgMBAAE=
------END RSA PUBLIC KEY-----
-signing-key
------BEGIN RSA PUBLIC KEY-----
-MIGJAoGBALSrtq1TqL5TImtctKm/5CvZHb54n5JpkmD8jtmdajgpHF0eaSaaw/Qw
-IKaoQn17etuGzbeXF1vgphv2T4xyIm04MiLOszbLGXK1Fsa5uAO+qs1bIy6mLpwg
-XrYhyuddYyw1s4U5ZNBNK/0rlwo7hoIZjxcej0AvTGewFx+Rv6UlAgMBAAE=
------END RSA PUBLIC KEY-----
-opt hidden-service-dir
-contact anonion at nym dot hush dot com
-reject 0.0.0.0/8:*
-reject 169.254.0.0/16:*
-reject 127.0.0.0/8:*
-reject 192.168.0.0/16:*
-reject 10.0.0.0/8:*
-reject 172.16.0.0/12:*
-reject 31.54.58.167:*
-accept *:53
-accept *:80
-accept *:443
-accept *:3128
-accept *:8080
-accept *:8000
-accept *:50
-accept *:51
-accept *:389
-accept *:636
-accept *:2123
-accept *:2152
-accept *:11370
-accept *:11371
-accept *:2301
-accept *:2381
-accept *:6518
-accept *:9100
-accept *:1433
-accept *:1434
-accept *:3389
-accept *:5901
-reject *:*
-router-signature
------BEGIN SIGNATURE-----
-XzcWkD6QV6CHtcdKuM6mVGGl+m9JsmLtYhpPMxRANxbRLMLaI1+XkO58QS0zvl6f
-0vhmifJhJSu+FV+mOkg/aCsPyleSMd8vLOjCzhVv0yCzcn252ujsjGc0HrmyPE1R
-p2bQE2u2SpgLKw6oylyr6qLcZkGIWDYhnnyZTg2lXm8=
------END SIGNATURE-----
-router Unnamed 122.60.235.157 9001 0 0
-platform Tor 0.2.2.37 (git-fce6eb1c44e87bc2) on Darwin x86_64
-opt protocols Link 1 2 Circuit 1
-published 2012-09-17 14:57:28
-opt fingerprint 5366 F1D1 9875 9F88 94EA 6E5F F768 C667 F59A FD24
-uptime 542717
-bandwidth 32768 65536 58012
-opt extra-info-digest 33C8C462BFA5A9C6E825A0A717BD63072AB68E59
-onion-key
------BEGIN RSA PUBLIC KEY-----
-MIGJAoGBAKCt4oloDEzaHKzuljXGIJujbTbQfl/XZPjc/84eWp8Ka2Vmpa9DL3cV
-+bO3wttgA6ZtkGUB6d6AHcfOoRT7tP/wBeQSHSDxh9OmKzGKZiHB76HNxVny8aJd
-ngVPgMmolXajdnzCIAIRLdUW6SHvIinyeghLuVSwLZU+eEN5XSXrAgMBAAE=
------END RSA PUBLIC KEY-----
-signing-key
------BEGIN RSA PUBLIC KEY-----
-MIGJAoGBAJ5avJflNjXZjGukC0aACUeTMHsQEHsQKCFPwXFVBCX6gDmrfivqT+EB
-efT8XTktk44EFJbCAWj65kuj+aoXBfUvrr21at4WAEJ33BgmitwCrzwkKWVX/wLM
-+NMIGObvs88HYyaTsoyZ/TToLE5nPQJj77bNyabHgNvYEXyFxooJAgMBAAE=
------END RSA PUBLIC KEY-----
-opt hidden-service-dir
-reject *:*
-router-signature
------BEGIN SIGNATURE-----
-m0Q/dr4HvyGShKG8DdKyabcTO4lZtGMXNGX+TEM1D43+5mw4D5UgiIdaK9a2tMQA
-sBGFCymv3ZHpmPRtOhSMRafwcZDYXeCSdvZXmfGzpyIRrBde3uRIJj97eqA+oOHd
-5TlnK2Z0tF+M1bUD0xYHOde5a/lAaiQaw5+mley6i6M=
------END SIGNATURE-----
diff --git a/test/integ/descriptor/data/metrics_vote b/test/integ/descriptor/data/metrics_vote
deleted file mode 100644
index f5c9666..0000000
--- a/test/integ/descriptor/data/metrics_vote
+++ /dev/null
@@ -1,85 +0,0 @@
- at type network-status-vote-3 1.0
-network-status-version 3
-vote-status vote
-consensus-methods 1 2 3 4 5 6 7 8 9 10 11 12
-published 2012-07-11 23:50:01
-valid-after 2012-07-12 00:00:00
-fresh-until 2012-07-12 01:00:00
-valid-until 2012-07-12 03:00:00
-voting-delay 300 300
-client-versions 0.2.2.35,0.2.2.36,0.2.2.37,0.2.3.10-alpha,0.2.3.11-alpha,0.2.3.12-alpha,0.2.3.13-alpha,0.2.3.14-alpha,0.2.3.15-alpha,0.2.3.16-alpha,0.2.3.17-beta,0.2.3.18-rc,0.2.3.19-rc
-server-versions 0.2.2.35,0.2.2.36,0.2.2.37,0.2.3.10-alpha,0.2.3.11-alpha,0.2.3.12-alpha,0.2.3.13-alpha,0.2.3.14-alpha,0.2.3.15-alpha,0.2.3.16-alpha,0.2.3.17-beta,0.2.3.18-rc,0.2.3.19-rc
-known-flags Authority BadExit Exit Fast Guard HSDir Named Running Stable Unnamed V2Dir Valid
-params CircuitPriorityHalflifeMsec=30000 bwauthpid=1
-dir-source tor26 14C131DFC5C6F93646BE72FA1401C02A8DF2E8B4 86.59.21.38 86.59.21.38 80 443
-contact Peter Palfrader
-dir-key-certificate-version 3
-fingerprint 14C131DFC5C6F93646BE72FA1401C02A8DF2E8B4
-dir-key-published 2012-04-29 21:21:25
-dir-key-expires 2013-05-29 21:21:25
-dir-identity-key
------BEGIN RSA PUBLIC KEY-----
-MIIBigKCAYEA7cZXvDRxfjDYtr9/9UsQ852+6cmHMr8VVh8GkLwbq3RzqjkULwQ2
-R9mFvG4FnqMcMKXi62rYYA3fZL1afhT804cpvyp/D3dPM8QxW88fafFAgIFP4LiD
-0JYjnF8cva5qZ0nzlWnMXLb32IXSvsGSE2FRyAV0YN9a6k967LSgCfUnZ+IKMezW
-1vhL9YK4QIfsDowgtVsavg63GzGmA7JvZmn77+/J5wKz11vGr7Wttf8XABbH2taX
-O9j/KGBOX2OKhoF3mXfZSmUO2dV9NMwtkJ7zD///Ny6sfApWV6kVP4O9TdG3bAsl
-+fHCoCKgF/jAAWzh6VckQTOPzQZaH5aMWfXrDlzFWg17MjonI+bBTD2Ex2pHczzJ
-bN7coDMRH2SuOXv8wFf27KdUxZ/GcrXSRGzlRLygxqlripUanjVGN2JvrVQVr0kz
-pjNjiZl2z8ZyZ5d4zQuBi074JPGgx62xAstP37v1mPw14sIWfLgY16ewYuS5bCxV
-lyS28jsPht9VAgMBAAE=
------END RSA PUBLIC KEY-----
-dir-signing-key
------BEGIN RSA PUBLIC KEY-----
-MIGJAoGBANROlyEQEWJ20RC+DogV0Ww8mrUw9viscZ7eqAkyhDZKIt++2kVXszUI
-Zd2NoWG025f3np4zlTuIkz2PShn1yAYmKBlRcwbexgR6dXBToL3Zxe1Mezig/UuL
-kX75imfneBiLdtCRzaZs/Fa5cwd3msUNPVsq5Zmk0R7rCmBqiLDbAgMBAAE=
------END RSA PUBLIC KEY-----
-dir-key-crosscert
------BEGIN ID SIGNATURE-----
-UuK7wNT0weVelnsMYgoj2apYzr5pJqaI7pM+IzWiSGq1NipqMDEwZqx0WVWR/izB
-kLQm1mxU0+84QxAtNNmwNo5uHXR1h2fZJQ2yHf8JLIjIEsaOeOfvDLkn9IyHwmIA
-OBPgHmAetivVUYc8lqejBO5l4xg8BpG7KK16yjTDflc=
------END ID SIGNATURE-----
-dir-key-certification
------BEGIN SIGNATURE-----
-t5PLDPgT2TJiM8fNmqXkXY0rK6u5LZEtMNqQ4BCQCrvUN87BPBpF4U0GptqVjmdK
-aKS0v6BMRBbSQhyro42jv8iaP8X57V39rJAEbQsWJVNnhBmgLPxY9LYS/he8dUPZ
-0LuNjNuBaekvUoPr6iunHGYUjVMZa3aOf+SeUxdS4ZudlozegDyBrkMeEVH/7DC/
-dx/VsqR5hIZ8/e+u3XQcSIYCmBbJxPNeOWNERi4npbyY0Iy2AJ1eJyX2onUsk2Qq
-jnMxPtuCSDAWEebChwtKTTV6EAnZzYcCZLxNLXcd4Tvfm2EgSw8xKtiDn+eABpcv
-7FaKEkuUBGLe+sN3jt3K05qOAOvVtCi0hSO/94+DO7PiXFxmb6Om6vEbKiGxagH2
-3jE6rNpJJsO0TspO3KTD7fpXJU7gtXIurcQwziXUGchK84aNFjdX/G1ra5M5kMHf
-hJt6p7rCqPyEVOwW+DIGVfucLfjtJu/h7XuWw9/htrE0TNx0Hah8V2+7Ok5XA10N
------END SIGNATURE-----
-r sumkledi ABPSI4nNUNC3hKPkBhyzHozozrU B5n4BiALAF8B5AqafxohyYiuj7E 2012-07-11 04:22:53 178.218.213.229 80 0
-s Exit Named Valid
-opt v Tor 0.2.2.35
-w Bandwidth=51
-p accept 80,443
-m 8,9,10,11,12 sha256=g1vx9si329muxV3tquWIXXySNOIwRGMeAESKs/v4DWs
-r Unnamed AEXri4INxBAZeyi0wvJZoC58nZs csTseFzwBV0RLsEdlyxDw6jTZ1o 2012-07-11 10:47:26 79.139.135.90 443 22
-s Fast HSDir Running V2Dir Valid
-opt v Tor 0.2.2.37
-w Bandwidth=92
-p reject 1-65535
-m 8,9,10,11,12 sha256=iZfmvEGdBG/qc9KYZUh4JsWEHbn9Bw2jy5i0AtCoRB0
-r default AFU4pMWKyq+eTPuBO0D9W179kQQ OZzEaq9N5+VX5yxzi051zqyEcPk 2012-07-11 08:17:21 2.90.115.167 443 9030
-s V2Dir Valid
-opt v Tor 0.2.3.15-alpha
-w Bandwidth=53
-p reject 1-65535
-m 8,9,10,11,12 sha256=m/RouK3TqoaYBBHCxgmzMPuGGCIEr0ufSTB1i85zicM
-r satoshi11 AFzRyEo3Ibdko7vl4rReoU3BDfM rWaYhhU9Kym4sXAVe+uE99VGpJs 2012-07-11 08:10:01 80.218.153.44 443 9030
-s V2Dir Valid
-opt v Tor 0.2.2.35
-w Bandwidth=196
-p reject 1-65535
-m 8,9,10,11,12 sha256=yxuOPz7IraIzzmU/qeZRJHOJrutNmvPwDwj2AmsHZSI
-directory-footer
-directory-signature 14C131DFC5C6F93646BE72FA1401C02A8DF2E8B4 BF112F1C6D5543CFD0A32215ACABD4197B5279AD
------BEGIN SIGNATURE-----
-02UCTf06pLYkuLgUSR+L0vHOJ7gUyidKscg88q8TWUAiP57x27o+BlZdEMSu/ArT
-yBOz+0ZOWggb04maA2cwJQQEK2LkzxzBijmfoC6RhQx3wS8llhyOMaoR6rkobE1D
-oLqVZPDNPX87DkhmQfgBM2I3vYQX13YJwZc0jNCEGpU=
------END SIGNATURE-----
diff --git a/test/integ/descriptor/data/negative_uptime b/test/integ/descriptor/data/negative_uptime
deleted file mode 100644
index 388d44b..0000000
--- a/test/integ/descriptor/data/negative_uptime
+++ /dev/null
@@ -1,46 +0,0 @@
- at type server-descriptor 1.0
-router TipTor 62.99.247.83 9001 0 9030
-platform Tor 0.1.1.25 on Darwin Power Macintosh
-published 2006-12-18 22:42:40
-opt fingerprint 1379 62D4 931D BF08 A24E 8432 88B8 A155 D6D2 AEDD
-uptime -31081285
-bandwidth 102400 153600 124131
-onion-key
------BEGIN RSA PUBLIC KEY-----
-MIGJAoGBAOAG0OI7xLpz3gAkJoJdRzKYOvOZ7PL5dQQZJGw+pXVUnXfchwTXLm5o
-rAoYolziFjUU+EDaDXCHq1MrQBNbT1kQHqv+OcVjTwuameNVOCgPTIDOxiXfxxoO
-zDAEQ91gzcDIBZdV9V64KV8Oz4+NYK3zrGDkIg00pAQjNuwvmZqlAgMBAAE=
------END RSA PUBLIC KEY-----
-signing-key
------BEGIN RSA PUBLIC KEY-----
-MIGJAoGBAKjCkU3eAwbW9ZU42B1l+Itqy4yKoQpRjt6N42mfNFfKZ8Ywd789/jiK
-J31QYBhW3JBn1ZFkRwpfKYlbOm0XS2CjlqHuT0Ycp9iphtCIHADIM+3shaSKAIwA
-8/wQ2GN9zYiTneUNWRJN5sDja+oU7XfkRI46cpV1Y1lVJyEoKQWpAgMBAAE=
------END RSA PUBLIC KEY-----
-opt write-history 2006-12-18 22:36:20 (900 s) 85896196,91917257,89966503,91506428,91213104,88921400,91039183,91716023,91147326,86173405,88624130,87211145,90976203,91765274,91904016,92475836,91436900,91811938,86244782,91228827,91939844,89829330,89107239,89019495,88925780,91247327,90406742,91088221,91065780,91770795,91408136,89981571,88663478,91555300,91664187,91916304,92079918,87826691,87836934,91162457,91904383,91508890,90270067,91314943,91856008,84648838,86133485,81010341,78078954,85039584,91951911,91327645,92042781,87120087,76812012,87759151,91627062,91881873,91965911,91523086,86332304,86299595,90915708,89321373,91093137,91654532,91836129,91819440,92049427,92014514,89481483,91098381,88852717,91601581,89729803,91256453,89604862,91881660,91942891,91769572,91896895,92224718,91431715,91762404,91709273,92046002,91866341,91561459,91673622,91700186,91659511,92402879,90926431,87060230,91076389,89573325
-opt read-history 2006-12-18 22:36:20 (900 s) 69091643,77959428,74322746,75132861,74637737,68249949,73224421,72744339,73893094,69049631,71480325,68334060,72775510,79560093,78273066,75799097,76810596,78186192,67593073,75998559,77654399,70612520,74383546,74734217,74966593,75402221,80000519,80112418,79005070,80831698,76082779,73232671,71056247,75135941,71903141,78060009,79742551,70250313,66205769,78319432,73988994,74372922,71546410,73545171,74220510,63051559,66646384,68538308,59360208,63812166,75673274,72347320,76357687,66600939,56487705,70648976,70968064,74698003,75944759,74122368,67485399,63305556,68515366,71145401,69625041,68993028,71537081,75025172,75155755,70871549,69489568,74027234,72162369,74396503,76005043,72708060,70567406,73291632,75536033,73865170,69322880,73918987,72020474,76681275,76604577,77208720,75252120,75305805,76861569,77278013,77057424,76937746,72642470,65754315,70138397,73236479
-contact rbr at tip-informatik.at
-reject 0.0.0.0/8:*
-reject 169.254.0.0/16:*
-reject 127.0.0.0/8:*
-reject 192.168.0.0/16:*
-reject 10.0.0.0/8:*
-reject 172.16.0.0/12:*
-reject *:25
-reject *:119
-reject *:135-139
-reject *:445
-reject *:465
-reject *:587
-reject *:1214
-reject *:4661-4666
-reject *:6346-6429
-reject *:6699
-reject *:6881-6999
-accept *:*
-router-signature
------BEGIN SIGNATURE-----
-XAy56NzQm+LGMnaxBZC0ulmyZxoTNTFxZN0834V/hrfBdEOwMuBZMSsS613OfRK1
-3K/StL29NdLChHt71XCPyxsnbzguBE0Pma6cX4aSNa1WJO4wpc1WipmWdmuVKXHD
-RoxIVSJU3qwfjzSbwJiNSLPs4mmQFecsVmG94cZSITY=
------END SIGNATURE-----
diff --git a/test/integ/descriptor/data/new_metrics_type b/test/integ/descriptor/data/new_metrics_type
deleted file mode 100644
index 95bd669..0000000
--- a/test/integ/descriptor/data/new_metrics_type
+++ /dev/null
@@ -1,3 +0,0 @@
- at type non-existant-type 1.0
-valid metrics header, but a type we shouldn't recognize
-
diff --git a/test/integ/descriptor/data/non-ascii_descriptor b/test/integ/descriptor/data/non-ascii_descriptor
deleted file mode 100644
index da1d8a5..0000000
--- a/test/integ/descriptor/data/non-ascii_descriptor
+++ /dev/null
@@ -1,30 +0,0 @@
- at type server-descriptor 1.0
-router Coruscant 88.182.161.122 9001 0 9030
-platform Tor 0.2.3.25 on Linux
-opt protocols Link 1 2 Circuit 1
-published 2013-05-18 11:16:19
-opt fingerprint 0B98 2154 5C48 E496 AEED 9ECC 0DB5 06C4 9FF8 158D
-uptime 259738
-bandwidth 102400 204800 122818
-opt extra-info-digest 56403D838DE152421CD401B8E57DAD4483A3D56B
-onion-key
------BEGIN RSA PUBLIC KEY-----
-MIGJAoGBAK+rjPilZJVzvWn6slOpak4eUab+VXiGNkRAIKgGAqbpex7lNMcZeXbu
-8uYhiDUdjRaHQUbBjLaj6nAjtlHUl3NCbIpo2p03KEcxfAGi3k6dUMBJ7aIYb8lL
-SDK95huLBpyyNPikeDIiTKS1HNtmrai/iVtUiJS2I4fs3lP1TxevAgMBAAE=
------END RSA PUBLIC KEY-----
-signing-key
------BEGIN RSA PUBLIC KEY-----
-MIGJAoGBANGDDMHoCtTeL4UTEGip4T+37jd4lEjBgla7lgL8W1gtVTX/uMMo+HGE
-m1sD/FgBI3w8w7zpZlJgYeB/94YPUrw11iAEYAooJVmGDmel2g6CFofDs7KmrzqR
-1FQDIWeY9sQd4+L+jWMquqcA3FyzuZz28gi56hIombjkfr0RwCsNAgMBAAE=
------END RSA PUBLIC KEY-----
-opt hidden-service-dir
-contact 1024D/04D2E818 Lénaïc Huard <lenaic dot huard AT laposte dot net>
-reject *:*
-router-signature
------BEGIN SIGNATURE-----
-hk4jKRtI0LbjYWdGhjTuev9Hk9AilyBmBtK9nfpBPyamQZDZVirEUuP7PdtxM2U9
-EdCg+KnkrLoKoxh18YumBD2HvwHa2J1VZtezWkwxGB+HPkECTU/byl+dn6ZRncY7
-r0IrVeGUnlK/UaWpswx6amH7FEKXmBZYHxTxyuwwybQ=
------END SIGNATURE-----
diff --git a/test/integ/descriptor/data/old_descriptor b/test/integ/descriptor/data/old_descriptor
deleted file mode 100644
index 8be82f9..0000000
--- a/test/integ/descriptor/data/old_descriptor
+++ /dev/null
@@ -1,48 +0,0 @@
- at type server-descriptor 1.0
-router krypton 212.37.39.59 8000 0 0
-platform Tor 0.1.0.14 on FreeBSD i386
-published 2005-12-16 18:01:03
-opt fingerprint 3E2F 63E2 356F 5231 8B53 6A12 B644 5373 808A 5D6C
-uptime 64820
-bandwidth 102400 10485760 0
-onion-key
------BEGIN RSA PUBLIC KEY-----
-MIGJAoGBAOewVPvehUE/Iy06e/NmUyzwBdkL89DP3q+I68hJJsxQgd1Fqm2CVN4E
-X0l3U14e3yyihr8xz1qmvQenWyKMx6B5ZjHrRAk774nTXzqqZMbcEKGnoWKTAgfC
-SrZYD4YdO2vPIA3OIBLBvj6HlyZWPVulQANpETK1TOACKeqs6vlhAgMBAAE=
------END RSA PUBLIC KEY-----
-signing-key
------BEGIN RSA PUBLIC KEY-----
-MIGJAoGBAJjsGNwSkG7xCzWEPTr2NBrC4k2Bx0SnTv8RfgUwqRg8rLss5CoZw7BV
-GL3g6PNe13rWcu7rYg27mT81T4cehrl0omnx4FrhDPmnJY4d+yjUiLKujTYaijyZ
-8ll4Mr1Ua3M2tTa+79sA8rXAg3zMf+BtTfIcK4avm+csipZ75r9BAgMBAAE=
------END RSA PUBLIC KEY-----
-opt write-history 2005-12-16 18:00:48 (900 s) 81,8848,8927,8927,83,8848,8931,8929,81,8846,8929,8931,81,8848,8929,8927,81,8846,8929,8929,83,8848,8931,8927,9544,23145,118733,12471330,23681225,32974322,36669050,50028191,60119352,43959405,104125876,93764839,67886465,56660787,69614721,49677518,72758978,69080350,68451562,56135957,66072248,44186182,63136414,78723340,84085493,73800911,61322609,54493047,40058835,35991325,33933884,43033805,30004913,31375439,29870384,39901793,24405790,46428634,32855040,31738054,32096150,34571772,47317095,43861120,1725009,2709813,3632379,7206137,4376659,3499688,2148089,2420819,3039400,3271931,2661625,2387027,943794,138137,167325,152659,120259,119828,120342,116812,118734,117279,116769,114764,115673,117709,117201,110668
-opt read-history  2005-12-16 18:00:48 (900 s) 20774,489973,510022,511163,20949,490665,509936,510266,20919,490429,512036,508640,20666,485937,506582,505071,20662,484684,507452,506131,20565,485822,505385,505766,28300,499433,627237,12392493,20716078,25654673,25415889,39581836,49991512,31545049,96363762,80390334,60930455,45885095,57137777,42643192,59698768,54573481,58379825,45862920,58781990,33448161,53507694,73363721,70825862,61283831,49325261,44674036,28451429,25009272,22895835,34914097,21103340,19156546,21131703,33036587,18445151,30464430,21857164,27329112,22507170,29536769,37525461,42026081,1778746,3200903,4137862,7712377,4397719,3980947,2650315,2440799,3522111,3774977,3165517,2407670,1427206,644034,672441,173113,609713,630447,632529,136977,611086,629716,629248,135385,607471,627380,625011,131334
-opt hibernating 1
-reject 169.254.0.0/255.255.0.0:*
-reject 127.0.0.0/255.0.0.0:*
-reject 192.168.0.0/255.255.0.0:*
-reject 10.0.0.0/255.0.0.0:*
-reject 172.16.0.0/255.240.0.0:*
-accept *:20-22
-accept *:53
-accept *:79-81
-accept *:110
-accept *:143
-accept *:443
-accept *:706
-accept *:873
-accept *:993
-accept *:995
-accept *:6660-6669
-accept *:8008
-accept *:8080
-accept *:8888
-reject *:*
-router-signature
------BEGIN SIGNATURE-----
-mHTlJGu2d2ZZgXfoI0CZBiLMCKbHox2n+Q3OGcivLj0kcYfJ/7/jk4o5ABRgyOHM
-zJ7FnC2wSg19MB8jeAvKkxdd1hp0OZQp+tCRq/7p11+6E0qvJcio+RwdeZAb0dQf
-suUOPIU/lHzfmraZMO3ovK4VjiWKQ7PgE1vAH7sTyZA=
------END SIGNATURE-----
diff --git a/test/integ/descriptor/data/riddle b/test/integ/descriptor/data/riddle
deleted file mode 100644
index ac08f36..0000000
--- a/test/integ/descriptor/data/riddle
+++ /dev/null
@@ -1,20 +0,0 @@
-Riddle by Damian Johnson (May, 2009)
-
-Curtains raise, they take the stage,
-  mute actors that never age.
-The clouds are cotton, the floor is pine,
-  the souls bound up in fine twine.
-
-Crowds will cheer, the players dance,
-  flowing scene to scene as in a trance.
-The world is perfect, they never whine,
-  for there's not a single will, save mine.
-
-The play has ended, we take a bow,
-  but the viewers are the actors now.
-When freed from decision, of thought and blame,
-  we'll walk to another drum just the same. 
-
-answer:
-http://www.atagar.com/riddles/answer10.php
-
diff --git a/test/integ/descriptor/data/tiny.png b/test/integ/descriptor/data/tiny.png
deleted file mode 100644
index e86715f..0000000
Binary files a/test/integ/descriptor/data/tiny.png and /dev/null differ
diff --git a/test/integ/descriptor/data/vote b/test/integ/descriptor/data/vote
deleted file mode 100644
index ac2a6fc..0000000
--- a/test/integ/descriptor/data/vote
+++ /dev/null
@@ -1,100 +0,0 @@
-network-status-version 3
-vote-status vote
-consensus-methods 1 2 3 4 5 6 7 8 9 10 11 12
-published 2012-07-11 23:50:01
-valid-after 2012-07-12 00:00:00
-fresh-until 2012-07-12 01:00:00
-valid-until 2012-07-12 03:00:00
-voting-delay 300 300
-known-flags Authority BadExit Exit Fast Guard HSDir Running Stable V2Dir Valid
-params CircuitPriorityHalflifeMsec=30000 bwauthpid=1
-dir-source turtles 27B6B5996C426270A5C95488AA5BCEB6BCC86956 76.73.17.194 76.73.17.194 9030 9090
-contact Mike Perry <email>
-dir-key-certificate-version 3
-fingerprint 27B6B5996C426270A5C95488AA5BCEB6BCC86956
-dir-key-published 2011-11-28 21:51:04
-dir-key-expires 2012-11-28 21:51:04
-dir-identity-key
------BEGIN RSA PUBLIC KEY-----
-MIIBigKCAYEA6uSmsoxj2MiJ3qyZq0qYXlRoG8o82SNqg+22m+t1c7MlQOZWPJYn
-XeMcBCt8xrTeIt2ZI+Q/Kt2QJSeD9WZRevTKk/kn5Tg2+xXPogalUU47y5tUohGz
-+Q8+CxtRSXpDxBHL2P8rLHvGrI69wbNHGoQkce/7gJy9vw5Ie2qzbyXk1NG6V8Fb
-pr6A885vHo6TbhUnolz2Wqt/kN+UorjLkN2H3fV+iGcQFv42SyHYGDLa0WwL3PJJ
-r/veu36S3VaHBrfhutfioi+d3d4Ya0bKwiWi5Lm2CHuuRTgMpHLU9vlci8Hunuxq
-HsULe2oMsr4VEic7sW5SPC5Obpx6hStHdNv1GxoSEm3/vIuPM8pINpU5ZYAyH9yO
-Ef22ZHeiVMMKmpV9TtFyiFqvlI6GpQn3mNbsQqF1y3XCA3Q4vlRAkpgJVUSvTxFP
-2bNDobOyVCpCM/rwxU1+RCNY5MFJ/+oktUY+0ydvTen3gFdZdgNqCYjKPLfBNm9m
-RGL7jZunMUNvAgMBAAE=
------END RSA PUBLIC KEY-----
-dir-signing-key
------BEGIN RSA PUBLIC KEY-----
-MIGJAoGBAJ5itcJRYNEM3Qf1OVWLRkwjqf84oXPc2ZusaJ5zOe7TVvBMra9GNyc0
-NM9y6zVkHCAePAjr4KbW/8P1olA6FUE2LV9bozaU1jFf6K8B2OELKs5FUEW+n+ic
-GM0x6MhngyXonWOcKt5Gj+mAu5lrno9tpNbPkz2Utr/Pi0nsDhWlAgMBAAE=
------END RSA PUBLIC KEY-----
-dir-key-crosscert
------BEGIN ID SIGNATURE-----
-RHYImGTwg36wmEdAn7qaRg2sAfql7ZCtPIL/O3lU5OIdXXp0tNn/K00Bamqohjk+
-Tz4FKsKXGDlbGv67PQcZPOK6NF0GRkNh4pk89prrDO4XwtEn7rkHHdBH6/qQ7IRG
-GdDZHtZ1a69oFZvPWD3hUaB50xeIe7GoKdKIfdNNJ+8=
------END ID SIGNATURE-----
-dir-key-certification
------BEGIN SIGNATURE-----
-fasWOGyUZ3iMCYpDfJ+0JcMiTH25sXPWzvlHorEOyOMbaMqRYpZU4GHzt1jLgdl6
-AAoR6KdamsLg5VE8xzst48a4UFuzHFlklZ5O8om2rcvDd5DhSnWWYZnYJecqB+bo
-dNisPmaIVSAWb29U8BpNRj4GMC9KAgGYUj8aE/KtutAeEekFfFEHTfWZ2fFp4j3m
-9rY8FWraqyiF+Emq1T8pAAgMQ+79R3oZxq0TXS42Z4Anhms735ccauKhI3pDKjbl
-tD5vAzIHOyjAOXj7a6jY/GrnaBNuJ4qe/4Hf9UmzK/jKKwG95BPJtPTT4LoFwEB0
-KG2OUeQUNoCck4nDpsZwFqPlrWCHcHfTV2iDYFV1HQWDTtZz/qf+GtB8NXsq+I1w
-brADmvReM2BD6p/13h0QURCI5hq7ZYlIKcKrBa0jn1d9cduULl7vgKsRCJDls/ID
-emBZ6pUxMpBmV0v+PrA3v9w4DlE7GHAq61FF/zju2kpqj6MInbEvI/E+e438sWsL
------END SIGNATURE-----
-r sumkledi ABPSI4nNUNC3hKPkBhyzHozozrU B5n4BiALAF8B5AqafxohyYiuj7E 2012-07-11 04:22:53 178.218.213.229 80 0
-s Exit Valid
-opt v Tor 0.2.2.35
-w Bandwidth=51 Measured=36
-p accept 80,443
-m 8,9,10,11,12 sha256=g1vx9si329muxV3tquWIXXySNOIwRGMeAESKs/v4DWs
-r Unnamed AEXri4INxBAZeyi0wvJZoC58nZs csTseFzwBV0RLsEdlyxDw6jTZ1o 2012-07-11 10:47:26 79.139.135.90 443 22
-s Fast HSDir Running V2Dir Valid
-opt v Tor 0.2.2.37
-w Bandwidth=92 Measured=15
-p reject 1-65535
-m 8,9,10,11,12 sha256=iZfmvEGdBG/qc9KYZUh4JsWEHbn9Bw2jy5i0AtCoRB0
-r default AFU4pMWKyq+eTPuBO0D9W179kQQ OZzEaq9N5+VX5yxzi051zqyEcPk 2012-07-11 08:17:21 2.90.115.167 443 9030
-s V2Dir Valid
-opt v Tor 0.2.3.15-alpha
-w Bandwidth=53
-p reject 1-65535
-m 8,9,10,11,12 sha256=m/RouK3TqoaYBBHCxgmzMPuGGCIEr0ufSTB1i85zicM
-r satoshi11 AFzRyEo3Ibdko7vl4rReoU3BDfM rWaYhhU9Kym4sXAVe+uE99VGpJs 2012-07-11 08:10:01 80.218.153.44 443 9030
-s V2Dir Valid
-opt v Tor 0.2.2.35
-w Bandwidth=196 Measured=113
-p reject 1-65535
-m 8,9,10,11,12 sha256=yxuOPz7IraIzzmU/qeZRJHOJrutNmvPwDwj2AmsHZSI
-r JapanAnon AGw/p8P246zRPQ3ZsQx9+pM8I3s FpQ4fP9FE2j4AnpZTcPiUbaIjwQ 2012-07-11 15:55:42 220.0.231.71 443 9030
-s Exit V2Dir Valid
-opt v Tor 0.2.2.37
-w Bandwidth=41 Measured=5
-p accept 110,143,443,706,993,995,1863,5050,5190,5222-5223,6660-6669,6697,7000-7001,8300,8888
-m 8,9,10,11,12 sha256=fdV6McsFl7ZaX8YJjRCchKa7B7ImTr6++AE3e7zCkK8
-r ANONIONROUTER AHhuQ8zFQJdT8l42Axxc6m6kNwI UUUhUJVXZ5b+8Lizh1ghVkUTGo0 2012-07-11 20:13:16 93.128.114.184 24051 24052
-s Fast Running V2Dir Valid
-opt v Tor 0.2.2.37
-w Bandwidth=196 Measured=100
-p reject 1-65535
-m 8,9,10,11,12 sha256=ooi27oMS4TBXes4FY4JyULgZYZcFzAyvB1JicaQ8zxs
-r pornosteffi AJrkZLNAIMRi7J3Q5os1iBJTM38 tV2vC76eZNwVpSFO8+r9lCqJSVw 2012-07-11 11:00:29 88.78.83.210 443 0
-s Fast Running Valid
-opt v Tor 0.2.2.35
-w Bandwidth=32 Measured=14
-p reject 1-65535
-m 8,9,10,11,12 sha256=JiARdiiTbey4aLkt4x1DUgEU3oaD2oDB2dtP7SGl3EU
-directory-footer
-directory-signature 27B6B5996C426270A5C95488AA5BCEB6BCC86956 D5C30C15BB3F1DA27669C2D88439939E8F418FCF
------BEGIN SIGNATURE-----
-fskXN84wB3mXfo+yKGSt0AcDaaPuU3NwMR3ROxWgLN0KjAaVi2eV9PkPCsQkcgw3
-JZ/1HL9sHyZfo6bwaC6YSM9PNiiY6L7rnGpS7UkHiFI+M96VCMorvjm5YPs3FioJ
-DnN5aFtYKiTc19qIC7Nmo+afPdDEf0MlJvEOP5EWl3w=
------END SIGNATURE-----
diff --git a/test/integ/descriptor/extrainfo_descriptor.py b/test/integ/descriptor/extrainfo_descriptor.py
index 377173a..7c24c3c 100644
--- a/test/integ/descriptor/extrainfo_descriptor.py
+++ b/test/integ/descriptor/extrainfo_descriptor.py
@@ -2,124 +2,14 @@
 Integration tests for stem.descriptor.extrainfo_descriptor.
 """
 
-import datetime
 import os
 import unittest
 
 import stem.descriptor
 import test.runner
 
-from stem.descriptor.extrainfo_descriptor import DirResponse
-from test.integ.descriptor import get_resource
-
 
 class TestExtraInfoDescriptor(unittest.TestCase):
-  def test_metrics_relay_descriptor(self):
-    """
-    Parses and checks our results against an extrainfo descriptor from metrics.
-    """
-
-    descriptor_file = open(get_resource('extrainfo_relay_descriptor'), 'rb')
-
-    expected_signature = """-----BEGIN SIGNATURE-----
-K5FSywk7qvw/boA4DQcqkls6Ize5vcBYfhQ8JnOeRQC9+uDxbnpm3qaYN9jZ8myj
-k0d2aofcVbHr4fPQOSST0LXDrhFl5Fqo5um296zpJGvRUeO6S44U/EfJAGShtqWw
-7LZqklu+gVvhMKREpchVqlAwXkWR44VENm24Hs+mT3M=
------END SIGNATURE-----"""
-
-    desc = next(stem.descriptor.parse_file(descriptor_file, 'extra-info 1.0'))
-    self.assertEquals('NINJA', desc.nickname)
-    self.assertEquals('B2289C3EAB83ECD6EB916A2F481A02E6B76A0A48', desc.fingerprint)
-    self.assertEquals(datetime.datetime(2012, 5, 5, 17, 3, 50), desc.published)
-    self.assertEquals(datetime.datetime(2012, 5, 5, 17, 2, 45), desc.read_history_end)
-    self.assertEquals(900, desc.read_history_interval)
-    self.assertEquals(datetime.datetime(2012, 5, 5, 17, 2, 45), desc.write_history_end)
-    self.assertEquals(900, desc.write_history_interval)
-    self.assertEquals(datetime.datetime(2012, 5, 5, 17, 2, 45), desc.dir_read_history_end)
-    self.assertEquals(900, desc.dir_read_history_interval)
-    self.assertEquals(datetime.datetime(2012, 5, 5, 17, 2, 45), desc.dir_write_history_end)
-    self.assertEquals(900, desc.dir_write_history_interval)
-    self.assertEquals(expected_signature, desc.signature)
-    self.assertEquals('00A57A9AAB5EA113898E2DD02A755E31AFC27227', desc.digest())
-    self.assertEquals([], desc.get_unrecognized_lines())
-
-    # The read-history, write-history, dirreq-read-history, and
-    # dirreq-write-history lines are pretty long so just checking
-    # the initial contents for the line and parsed values.
-
-    read_values_start = [3309568, 9216, 41984, 27648, 123904]
-    self.assertEquals(read_values_start, desc.read_history_values[:5])
-
-    write_values_start = [1082368, 19456, 50176, 272384, 485376]
-    self.assertEquals(write_values_start, desc.write_history_values[:5])
-
-    dir_read_values_start = [0, 0, 0, 0, 33792, 27648, 48128]
-    self.assertEquals(dir_read_values_start, desc.dir_read_history_values[:7])
-
-    dir_write_values_start = [0, 0, 0, 227328, 349184, 382976, 738304]
-    self.assertEquals(dir_write_values_start, desc.dir_write_history_values[:7])
-
-  def test_metrics_bridge_descriptor(self):
-    """
-    Parses and checks our results against an extrainfo bridge descriptor from
-    metrics.
-    """
-
-    descriptor_file = open(get_resource('extrainfo_bridge_descriptor'), 'rb')
-
-    expected_dir_v2_responses = {
-      DirResponse.OK: 0,
-      DirResponse.UNAVAILABLE: 0,
-      DirResponse.NOT_FOUND: 0,
-      DirResponse.NOT_MODIFIED: 0,
-      DirResponse.BUSY: 0,
-    }
-
-    expected_dir_v3_responses = {
-      DirResponse.OK: 72,
-      DirResponse.NOT_ENOUGH_SIGS: 0,
-      DirResponse.UNAVAILABLE: 0,
-      DirResponse.NOT_FOUND: 0,
-      DirResponse.NOT_MODIFIED: 0,
-      DirResponse.BUSY: 0,
-    }
-
-    desc = next(stem.descriptor.parse_file(descriptor_file, 'bridge-extra-info 1.0'))
-    self.assertEquals('ec2bridgereaac65a3', desc.nickname)
-    self.assertEquals('1EC248422B57D9C0BD751892FE787585407479A4', desc.fingerprint)
-    self.assertEquals(datetime.datetime(2012, 6, 8, 2, 21, 27), desc.published)
-    self.assertEquals(datetime.datetime(2012, 6, 8, 2, 10, 38), desc.read_history_end)
-    self.assertEquals(900, desc.read_history_interval)
-    self.assertEquals(datetime.datetime(2012, 6, 8, 2, 10, 38), desc.write_history_end)
-    self.assertEquals(900, desc.write_history_interval)
-    self.assertEquals(datetime.datetime(2012, 6, 8, 2, 10, 38), desc.dir_read_history_end)
-    self.assertEquals(900, desc.dir_read_history_interval)
-    self.assertEquals(datetime.datetime(2012, 6, 8, 2, 10, 38), desc.dir_write_history_end)
-    self.assertEquals(900, desc.dir_write_history_interval)
-    self.assertEquals('00A2AECCEAD3FEE033CFE29893387143146728EC', desc.digest())
-    self.assertEquals([], desc.get_unrecognized_lines())
-
-    read_values_start = [337920, 437248, 3995648, 48726016]
-    self.assertEquals(read_values_start, desc.read_history_values[:4])
-
-    write_values_start = [343040, 991232, 5649408, 49548288]
-    self.assertEquals(write_values_start, desc.write_history_values[:4])
-
-    dir_read_values_start = [0, 71680, 99328, 25600]
-    self.assertEquals(dir_read_values_start, desc.dir_read_history_values[:4])
-
-    dir_write_values_start = [5120, 664576, 2419712, 578560]
-    self.assertEquals(dir_write_values_start, desc.dir_write_history_values[:4])
-
-    self.assertEquals({}, desc.dir_v2_requests)
-    self.assertEquals({}, desc.dir_v3_requests)
-
-    self.assertEquals(expected_dir_v2_responses, desc.dir_v2_responses)
-    self.assertEquals(expected_dir_v3_responses, desc.dir_v3_responses)
-
-    self.assertEquals({}, desc.dir_v2_responses_unknown)
-    self.assertEquals({}, desc.dir_v2_responses_unknown)
-
   def test_cached_descriptor(self):
     """
     Parses the cached descriptor file in our data directory, checking that it
diff --git a/test/integ/descriptor/microdescriptor.py b/test/integ/descriptor/microdescriptor.py
index f7dadfd..5eb8edf 100644
--- a/test/integ/descriptor/microdescriptor.py
+++ b/test/integ/descriptor/microdescriptor.py
@@ -6,35 +6,8 @@ import os
 import unittest
 
 import stem.descriptor
-import stem.exit_policy
 import test.runner
 
-from test.integ.descriptor import get_resource
-
-FIRST_ONION_KEY = """\
------BEGIN RSA PUBLIC KEY-----
-MIGJAoGBAMhPQtZPaxP3ukybV5LfofKQr20/ljpRk0e9IlGWWMSTkfVvBcHsa6IM
-H2KE6s4uuPHp7FqhakXAzJbODobnPHY8l1E4efyrqMQZXEQk2IMhgSNtG6YqUrVF
-CxdSKSSy0mmcBe2TOyQsahlGZ9Pudxfnrey7KcfqnArEOqNH09RpAgMBAAE=
------END RSA PUBLIC KEY-----\
-"""
-
-SECOND_ONION_KEY = """\
------BEGIN RSA PUBLIC KEY-----
-MIGJAoGBALCOxZdpMI2WO496njSQ2M7b4IgAGATqpJmH3So7lXOa25sK6o7JipgP
-qQE83K/t/xsMIpxQ/hHkft3G78HkeXXFc9lVUzH0HmHwYEu0M+PMVULSkG36MfEl
-7WeSZzaG+Tlnh9OySAzVyTsv1ZJsTQFHH9V8wuM0GOMo9X8DFC+NAgMBAAE=
------END RSA PUBLIC KEY-----\
-"""
-
-THIRD_ONION_KEY = """\
------BEGIN RSA PUBLIC KEY-----
-MIGJAoGBAOWFQHxO+5kGuhwPUX5jB7wJCrTbSU0fZwolNV1t9UaDdjGDvIjIhdit
-y2sMbyd9K8lbQO7x9rQjNst5ZicuaSOs854XQddSjm++vMdjYbOcVMqnKGSztvpd
-w/1LVWFfhcBnsGi4JMGbmP+KUZG9A8kI9deSyJhfi35jA7UepiHHAgMBAAE=
------END RSA PUBLIC KEY-----\
-"""
-
 
 class TestMicrodescriptor(unittest.TestCase):
   def test_cached_microdescriptors(self):
@@ -59,40 +32,3 @@ class TestMicrodescriptor(unittest.TestCase):
 
         if unrecognized_lines:
           self.fail('Unrecognized microdescriptor content: %s' % unrecognized_lines)
-
-  def test_local_microdescriptors(self):
-    """
-    Checks a small microdescriptor file with known contents.
-    """
-
-    descriptor_path = get_resource('cached-microdescs')
-
-    with open(descriptor_path, 'rb') as descriptor_file:
-      descriptors = stem.descriptor.parse_file(descriptor_file, 'microdescriptor 1.0')
-
-      router = next(descriptors)
-      self.assertEquals(FIRST_ONION_KEY, router.onion_key)
-      self.assertEquals(None, router.ntor_onion_key)
-      self.assertEquals([], router.or_addresses)
-      self.assertEquals([], router.family)
-      self.assertEquals(stem.exit_policy.MicroExitPolicy('reject 1-65535'), router.exit_policy)
-      self.assertEquals({b'@last-listed': b'2013-02-24 00:18:36'}, router.get_annotations())
-      self.assertEquals([b'@last-listed 2013-02-24 00:18:36'], router.get_annotation_lines())
-
-      router = next(descriptors)
-      self.assertEquals(SECOND_ONION_KEY, router.onion_key)
-      self.assertEquals(u'r5572HzD+PMPBbXlZwBhsm6YEbxnYgis8vhZ1jmdI2k=', router.ntor_onion_key)
-      self.assertEquals([], router.or_addresses)
-      self.assertEquals(['$6141629FA0D15A6AEAEF3A1BEB76E64C767B3174'], router.family)
-      self.assertEquals(stem.exit_policy.MicroExitPolicy('reject 1-65535'), router.exit_policy)
-      self.assertEquals({b'@last-listed': b'2013-02-24 00:18:37'}, router.get_annotations())
-      self.assertEquals([b'@last-listed 2013-02-24 00:18:37'], router.get_annotation_lines())
-
-      router = next(descriptors)
-      self.assertEquals(THIRD_ONION_KEY, router.onion_key)
-      self.assertEquals(None, router.ntor_onion_key)
-      self.assertEquals([(u'2001:6b0:7:125::242', 9001, True)], router.or_addresses)
-      self.assertEquals([], router.family)
-      self.assertEquals(stem.exit_policy.MicroExitPolicy('accept 80,443'), router.exit_policy)
-      self.assertEquals({b'@last-listed': b'2013-02-24 00:18:36'}, router.get_annotations())
-      self.assertEquals([b'@last-listed 2013-02-24 00:18:36'], router.get_annotation_lines())
diff --git a/test/integ/descriptor/networkstatus.py b/test/integ/descriptor/networkstatus.py
index 3054eea..83a3ff6 100644
--- a/test/integ/descriptor/networkstatus.py
+++ b/test/integ/descriptor/networkstatus.py
@@ -2,7 +2,6 @@
 Integration tests for stem.descriptor.networkstatus.
 """
 
-import datetime
 import os
 import unittest
 
@@ -12,8 +11,6 @@ import stem.descriptor.networkstatus
 import stem.version
 import test.runner
 
-from test.integ.descriptor import get_resource
-
 
 class TestNetworkStatus(unittest.TestCase):
   def test_cached_consensus(self):
@@ -97,394 +94,3 @@ class TestNetworkStatus(unittest.TestCase):
           self.fail('Unrecognized descriptor content: %s' % unrecognized_lines)
 
     self.assertTrue(count > 100)
-
-  def test_metrics_consensus(self):
-    """
-    Checks if consensus documents from Metrics are parsed properly.
-    """
-
-    consensus_path = get_resource('metrics_consensus')
-
-    for specify_type in (True, False):
-      with open(consensus_path, 'rb') as descriptor_file:
-        if specify_type:
-          descriptors = stem.descriptor.parse_file(descriptor_file, 'network-status-consensus-3 1.0')
-        else:
-          descriptors = stem.descriptor.parse_file(descriptor_file)
-
-        router = next(descriptors)
-        self.assertEquals('sumkledi', router.nickname)
-        self.assertEquals('0013D22389CD50D0B784A3E4061CB31E8CE8CEB5', router.fingerprint)
-        self.assertEquals('F260ABF1297B445E04354E236F4159140FF7768F', router.digest)
-        self.assertEquals(datetime.datetime(2012, 7, 12, 4, 1, 55), router.published)
-        self.assertEquals('178.218.213.229', router.address)
-        self.assertEquals(80, router.or_port)
-        self.assertEquals(None, router.dir_port)
-
-  def test_metrics_bridge_consensus(self):
-    """
-    Checks if the bridge documents from Metrics are parsed properly.
-    """
-
-    consensus_path = get_resource('bridge_network_status')
-
-    with open(consensus_path, 'rb') as descriptor_file:
-      router = next(stem.descriptor.parse_file(descriptor_file))
-      self.assertEquals('Unnamed', router.nickname)
-      self.assertEquals('0014A2055278DB3EB0E59EA701741416AF185558', router.fingerprint)
-      self.assertEquals('148EF8685B8D259650AE0967D1FF8E6A870C7743', router.digest)
-      self.assertEquals(datetime.datetime(2012, 5, 31, 15, 57, 0), router.published)
-      self.assertEquals('10.97.236.247', router.address)
-      self.assertEquals(443, router.or_port)
-      self.assertEquals(None, router.dir_port)
-
-  def test_metrics_cert(self):
-    """
-    Checks if consensus documents from Metrics are parsed properly.
-    """
-
-    expected_identity_key = """-----BEGIN RSA PUBLIC KEY-----
-MIIBigKCAYEA7cZXvDRxfjDYtr9/9UsQ852+6cmHMr8VVh8GkLwbq3RzqjkULwQ2
-R9mFvG4FnqMcMKXi62rYYA3fZL1afhT804cpvyp/D3dPM8QxW88fafFAgIFP4LiD
-0JYjnF8cva5qZ0nzlWnMXLb32IXSvsGSE2FRyAV0YN9a6k967LSgCfUnZ+IKMezW
-1vhL9YK4QIfsDowgtVsavg63GzGmA7JvZmn77+/J5wKz11vGr7Wttf8XABbH2taX
-O9j/KGBOX2OKhoF3mXfZSmUO2dV9NMwtkJ7zD///Ny6sfApWV6kVP4O9TdG3bAsl
-+fHCoCKgF/jAAWzh6VckQTOPzQZaH5aMWfXrDlzFWg17MjonI+bBTD2Ex2pHczzJ
-bN7coDMRH2SuOXv8wFf27KdUxZ/GcrXSRGzlRLygxqlripUanjVGN2JvrVQVr0kz
-pjNjiZl2z8ZyZ5d4zQuBi074JPGgx62xAstP37v1mPw14sIWfLgY16ewYuS5bCxV
-lyS28jsPht9VAgMBAAE=
------END RSA PUBLIC KEY-----"""
-
-    expected_signing_key = """-----BEGIN RSA PUBLIC KEY-----
-MIGJAoGBAOeE3Qr1Km97gTgiB3io0EU0fqHW2ESMXVHeQuNDtCWBa0XSCEG6gx4B
-ZkkHjfVWqGQ7TmmzjYP9L9uCgtoKfhSvJA2w9NUMtMl8sgZmF4lcGpXXvGY9a566
-Bn+3wP0lMhb/I8CPVPX+NWEjgl1noZxo1C59SO/iALGQOpxRYgmbAgMBAAE=
------END RSA PUBLIC KEY-----"""
-
-    expected_key_cert = """-----BEGIN SIGNATURE-----
-asvWwaMq34OfHoWUhAwh4+JDOuEUZJVIHQnedOYfQH8asS2QvW3Ma93OhrwVOC6b
-FyKmTJmJsl0MJGiC7tcEOlL6knsKE4CsuIw/PEcu2Rnm+R9zWxQuMYiHvGQMoDxl
-giOhLLs4LlzAAJlbfbd3hjF4STVAtTwmxYuIjb1Mq/JfAsx/wH3TLXgVZwj32w9s
-zUd9KZwwLzFiiHpC+U7zh6+wRsZfo2tlpmcaP1dTSINgVbdzPJ/DOUlx9nwTCBsE
-AQpUx2DpAikwrpw0zDqpQvYulcQlNLWFN/y/PkmiK8mIJk0OBMiQA7JgqWamnnk4
-PwqaGv483LkBF+25JFGJmnUVve3RMc+s61+2kBcjfUMed4QaHkeCMHqlRqpfQVkk
-RY22NXCwrJvSMEwiy7acC8FGysqwHRyE356+Rw6TB43g3Tno9KaHEK7MHXjSHwNs
-GM9hAsAMRX9Ogqhq5UjDNqEsvDKuyVeyh7unSZEOip9Zr6K/+7VsVPNb8vfBRBjo
------END SIGNATURE-----"""
-
-    cert_path = get_resource('metrics_cert')
-
-    with open(cert_path, 'rb') as cert_file:
-      cert = next(stem.descriptor.parse_file(cert_file))
-      self.assertEquals(3, cert.version)
-      self.assertEquals(None, cert.address)
-      self.assertEquals(None, cert.dir_port)
-      self.assertEquals('14C131DFC5C6F93646BE72FA1401C02A8DF2E8B4', cert.fingerprint)
-      self.assertEquals(expected_identity_key, cert.identity_key)
-      self.assertEquals(datetime.datetime(2008, 5, 9, 21, 13, 26), cert.published)
-      self.assertEquals(datetime.datetime(2009, 5, 9, 21, 13, 26), cert.expires)
-      self.assertEquals(expected_signing_key, cert.signing_key)
-      self.assertEquals(None, cert.crosscert)
-      self.assertEquals(expected_key_cert, cert.certification)
-      self.assertEquals([], cert.get_unrecognized_lines())
-
-  def test_consensus_v3(self):
-    """
-    Checks that version 3 consensus documents are properly parsed.
-    """
-
-    # the document's expected client and server versions are the same
-    expected_versions = [stem.version.Version(v) for v in (
-      '0.2.2.35',
-      '0.2.2.36',
-      '0.2.2.37',
-      '0.2.3.10-alpha',
-      '0.2.3.11-alpha',
-      '0.2.3.12-alpha',
-      '0.2.3.13-alpha',
-      '0.2.3.14-alpha',
-      '0.2.3.15-alpha',
-      '0.2.3.16-alpha',
-      '0.2.3.17-beta',
-      '0.2.3.18-rc',
-      '0.2.3.19-rc',
-    )]
-
-    expected_flags = set(
-      ['Authority', 'BadExit', 'Exit', 'Fast', 'Guard', 'HSDir',
-       'Named', 'Running', 'Stable', 'Unnamed', 'V2Dir', 'Valid'])
-
-    expected_bandwidth_weights = {
-      'Wbd': 3335, 'Wbe': 0, 'Wbg': 3536, 'Wbm': 10000, 'Wdb': 10000,
-      'Web': 10000, 'Wed': 3329, 'Wee': 10000, 'Weg': 3329, 'Wem': 10000,
-      'Wgb': 10000, 'Wgd': 3335, 'Wgg': 6464, 'Wgm': 6464, 'Wmb': 10000,
-      'Wmd': 3335, 'Wme': 0, 'Wmg': 3536, 'Wmm': 10000
-    }
-
-    expected_signature = """-----BEGIN SIGNATURE-----
-HFXB4497LzESysYJ/4jJY83E5vLjhv+igIxD9LU6lf6ftkGeF+lNmIAIEKaMts8H
-mfWcW0b+jsrXcJoCxV5IrwCDF3u1aC3diwZY6yiG186pwWbOwE41188XI2DeYPwE
-I/TJmV928na7RLZe2mGHCAW3VQOvV+QkCfj05VZ8CsY=
------END SIGNATURE-----"""
-
-    with open(get_resource('cached-consensus'), 'rb') as descriptor_file:
-      document = stem.descriptor.networkstatus.NetworkStatusDocumentV3(descriptor_file.read(), default_params = False)
-
-      self.assertEquals(3, document.version)
-      self.assertEquals(None, document.version_flavor)
-      self.assertEquals(True, document.is_consensus)
-      self.assertEquals(False, document.is_vote)
-      self.assertEquals(False, document.is_microdescriptor)
-      self.assertEquals(datetime.datetime(2012, 7, 12, 10, 0, 0), document.valid_after)
-      self.assertEquals(datetime.datetime(2012, 7, 12, 11, 0, 0), document.fresh_until)
-      self.assertEquals(datetime.datetime(2012, 7, 12, 13, 0, 0), document.valid_until)
-      self.assertEquals(300, document.vote_delay)
-      self.assertEquals(300, document.dist_delay)
-      self.assertEquals(expected_versions, document.client_versions)
-      self.assertEquals(expected_versions, document.server_versions)
-      self.assertEquals(expected_flags, set(document.known_flags))
-      self.assertEquals({'CircuitPriorityHalflifeMsec': 30000, 'bwauthpid': 1}, document.params)
-
-      self.assertEquals(12, document.consensus_method)
-      self.assertEquals(expected_bandwidth_weights, document.bandwidth_weights)
-      self.assertEquals([], document.consensus_methods)
-      self.assertEquals(None, document.published)
-      self.assertEquals([], document.get_unrecognized_lines())
-
-      router = document.routers['0013D22389CD50D0B784A3E4061CB31E8CE8CEB5']
-      self.assertEquals('sumkledi', router.nickname)
-      self.assertEquals('0013D22389CD50D0B784A3E4061CB31E8CE8CEB5', router.fingerprint)
-      self.assertEquals('F260ABF1297B445E04354E236F4159140FF7768F', router.digest)
-      self.assertEquals(datetime.datetime(2012, 7, 12, 4, 1, 55), router.published)
-      self.assertEquals('178.218.213.229', router.address)
-      self.assertEquals(80, router.or_port)
-      self.assertEquals(None, router.dir_port)
-      self.assertEquals(set(['Exit', 'Fast', 'Named', 'Running', 'Valid']), set(router.flags))
-
-      authority = document.directory_authorities[0]
-      self.assertEquals(8, len(document.directory_authorities))
-      self.assertEquals('tor26', authority.nickname)
-      self.assertEquals('14C131DFC5C6F93646BE72FA1401C02A8DF2E8B4', authority.fingerprint)
-      self.assertEquals('86.59.21.38', authority.hostname)
-      self.assertEquals('86.59.21.38', authority.address)
-      self.assertEquals(80, authority.dir_port)
-      self.assertEquals(443, authority.or_port)
-      self.assertEquals('Peter Palfrader', authority.contact)
-      self.assertEquals('0B6D1E9A300B895AA2D0B427F92917B6995C3C1C', authority.vote_digest)
-      self.assertEquals(None, authority.legacy_dir_key)
-      self.assertEquals(None, authority.key_certificate)
-
-      signature = document.signatures[0]
-      self.assertEquals(8, len(document.signatures))
-      self.assertEquals('sha1', signature.method)
-      self.assertEquals('14C131DFC5C6F93646BE72FA1401C02A8DF2E8B4', signature.identity)
-      self.assertEquals('BF112F1C6D5543CFD0A32215ACABD4197B5279AD', signature.key_digest)
-      self.assertEquals(expected_signature, signature.signature)
-
-  def test_consensus_v2(self):
-    """
-    Checks that version 2 consensus documents are properly parsed.
-    """
-
-    expected_signing_key = """-----BEGIN RSA PUBLIC KEY-----
-MIGJAoGBAOcrht/y5rkaahfX7sMe2qnpqoPibsjTSJaDvsUtaNP/Bq0MgNDGOR48
-rtwfqTRff275Edkp/UYw3G3vSgKCJr76/bqOHCmkiZrnPV1zxNfrK18gNw2Cxre0
-nTA+fD8JQqpPtb8b0SnG9kwy75eS//sRu7TErie2PzGMxrf9LH0LAgMBAAE=
------END RSA PUBLIC KEY-----"""
-
-    expected_signature = """-----BEGIN SIGNATURE-----
-2nXCxVje3wzn6HrIFRNMc0nc48AhMVpHZyPwRKGXkuYfTQG55uvwQDaFgJHud4RT
-27QhWltau3K1evhnzhKcpbTXwkVv1TBYJSzL6rEeAn8cQ7ZiCyqf4EJCaNcem3d2
-TpQQk3nNQF8z6UIvdlvP+DnJV4izWVkQEZgUZgIVM0E=
------END SIGNATURE-----"""
-
-    with open(get_resource('cached-consensus-v2'), 'rb') as descriptor_file:
-      descriptor_file.readline()  # strip header
-      document = stem.descriptor.networkstatus.NetworkStatusDocumentV2(descriptor_file.read())
-
-      self.assertEquals(2, document.version)
-      self.assertEquals('18.244.0.114', document.hostname)
-      self.assertEquals('18.244.0.114', document.address)
-      self.assertEquals(80, document.dir_port)
-      self.assertEquals('719BE45DE224B607C53707D0E2143E2D423E74CF', document.fingerprint)
-      self.assertEquals('arma at mit dot edu', document.contact)
-      self.assertEquals(expected_signing_key, document.signing_key)
-
-      self.assertEquals(67, len(document.client_versions))
-      self.assertEquals('0.0.9rc2', document.client_versions[0])
-      self.assertEquals('0.1.1.10-alpha-cvs', document.client_versions[-1])
-
-      self.assertEquals(67, len(document.server_versions))
-      self.assertEquals('0.0.9rc2', document.server_versions[0])
-      self.assertEquals('0.1.1.10-alpha-cvs', document.server_versions[-1])
-
-      self.assertEquals(datetime.datetime(2005, 12, 16, 0, 13, 46), document.published)
-      self.assertEquals(['Names', 'Versions'], document.options)
-      self.assertEquals('moria2', document.signing_authority)
-      self.assertEquals(expected_signature, document.signature)
-      self.assertEquals([], document.get_unrecognized_lines())
-
-      self.assertEqual(3, len(document.routers))
-
-      router1 = document.routers['719BE45DE224B607C53707D0E2143E2D423E74CF']
-      self.assertEquals('moria2', router1.nickname)
-      self.assertEquals('719BE45DE224B607C53707D0E2143E2D423E74CF', router1.fingerprint)
-      self.assertEquals('B7F3F0975B87889DD1285FD57A1B1BB617F65432', router1.digest)
-      self.assertEquals(datetime.datetime(2005, 12, 15, 6, 57, 18), router1.published)
-      self.assertEquals('18.244.0.114', router1.address)
-      self.assertEquals(443, router1.or_port)
-      self.assertEquals(80, router1.dir_port)
-      self.assertEquals(set(['Authority', 'Fast', 'Named', 'Running', 'Valid', 'V2Dir']), set(router1.flags))
-
-      router2 = document.routers['0928BA467056C4A689FEE4EF5D71482B6289C3D5']
-      self.assertEquals('stnv', router2.nickname)
-      self.assertEquals('0928BA467056C4A689FEE4EF5D71482B6289C3D5', router2.fingerprint)
-      self.assertEquals('22D1A7ED4199BDA7ED6C416EECD769C18E1F2A5A', router2.digest)
-      self.assertEquals(datetime.datetime(2005, 12, 15, 16, 24, 42), router2.published)
-      self.assertEquals('84.16.236.173', router2.address)
-      self.assertEquals(9001, router2.or_port)
-      self.assertEquals(None, router2.dir_port)
-      self.assertEquals(set(['Named', 'Valid']), set(router2.flags))
-
-      router3 = document.routers['09E8582FF0E6F85E2B8E41C0DC0B9C9DC46E6968']
-      self.assertEquals('nggrplz', router3.nickname)
-      self.assertEquals('09E8582FF0E6F85E2B8E41C0DC0B9C9DC46E6968', router3.fingerprint)
-      self.assertEquals('B302C2B01C94F398E3EF38939526B0651F824DD6', router3.digest)
-      self.assertEquals(datetime.datetime(2005, 12, 15, 23, 25, 50), router3.published)
-      self.assertEquals('194.109.109.109', router3.address)
-      self.assertEquals(9001, router3.or_port)
-      self.assertEquals(None, router3.dir_port)
-      self.assertEquals(set(['Fast', 'Stable', 'Running', 'Valid']), set(router3.flags))
-
-  def test_metrics_vote(self):
-    """
-    Checks if vote documents from Metrics are parsed properly.
-    """
-
-    vote_path = get_resource('metrics_vote')
-
-    with open(vote_path, 'rb') as descriptor_file:
-      descriptors = stem.descriptor.parse_file(descriptor_file)
-
-      router = next(descriptors)
-      self.assertEquals('sumkledi', router.nickname)
-      self.assertEquals('0013D22389CD50D0B784A3E4061CB31E8CE8CEB5', router.fingerprint)
-      self.assertEquals('0799F806200B005F01E40A9A7F1A21C988AE8FB1', router.digest)
-      self.assertEquals(datetime.datetime(2012, 7, 11, 4, 22, 53), router.published)
-      self.assertEquals('178.218.213.229', router.address)
-      self.assertEquals(80, router.or_port)
-      self.assertEquals(None, router.dir_port)
-
-  def test_vote(self):
-    """
-    Checks that vote documents are properly parsed.
-    """
-
-    expected_flags = set(
-      ['Authority', 'BadExit', 'Exit', 'Fast', 'Guard', 'HSDir',
-       'Running', 'Stable', 'V2Dir', 'Valid'])
-
-    expected_identity_key = """-----BEGIN RSA PUBLIC KEY-----
-MIIBigKCAYEA6uSmsoxj2MiJ3qyZq0qYXlRoG8o82SNqg+22m+t1c7MlQOZWPJYn
-XeMcBCt8xrTeIt2ZI+Q/Kt2QJSeD9WZRevTKk/kn5Tg2+xXPogalUU47y5tUohGz
-+Q8+CxtRSXpDxBHL2P8rLHvGrI69wbNHGoQkce/7gJy9vw5Ie2qzbyXk1NG6V8Fb
-pr6A885vHo6TbhUnolz2Wqt/kN+UorjLkN2H3fV+iGcQFv42SyHYGDLa0WwL3PJJ
-r/veu36S3VaHBrfhutfioi+d3d4Ya0bKwiWi5Lm2CHuuRTgMpHLU9vlci8Hunuxq
-HsULe2oMsr4VEic7sW5SPC5Obpx6hStHdNv1GxoSEm3/vIuPM8pINpU5ZYAyH9yO
-Ef22ZHeiVMMKmpV9TtFyiFqvlI6GpQn3mNbsQqF1y3XCA3Q4vlRAkpgJVUSvTxFP
-2bNDobOyVCpCM/rwxU1+RCNY5MFJ/+oktUY+0ydvTen3gFdZdgNqCYjKPLfBNm9m
-RGL7jZunMUNvAgMBAAE=
------END RSA PUBLIC KEY-----"""
-
-    expected_signing_key = """-----BEGIN RSA PUBLIC KEY-----
-MIGJAoGBAJ5itcJRYNEM3Qf1OVWLRkwjqf84oXPc2ZusaJ5zOe7TVvBMra9GNyc0
-NM9y6zVkHCAePAjr4KbW/8P1olA6FUE2LV9bozaU1jFf6K8B2OELKs5FUEW+n+ic
-GM0x6MhngyXonWOcKt5Gj+mAu5lrno9tpNbPkz2Utr/Pi0nsDhWlAgMBAAE=
------END RSA PUBLIC KEY-----"""
-
-    expected_key_crosscert = """-----BEGIN ID SIGNATURE-----
-RHYImGTwg36wmEdAn7qaRg2sAfql7ZCtPIL/O3lU5OIdXXp0tNn/K00Bamqohjk+
-Tz4FKsKXGDlbGv67PQcZPOK6NF0GRkNh4pk89prrDO4XwtEn7rkHHdBH6/qQ7IRG
-GdDZHtZ1a69oFZvPWD3hUaB50xeIe7GoKdKIfdNNJ+8=
------END ID SIGNATURE-----"""
-
-    expected_key_certification = """-----BEGIN SIGNATURE-----
-fasWOGyUZ3iMCYpDfJ+0JcMiTH25sXPWzvlHorEOyOMbaMqRYpZU4GHzt1jLgdl6
-AAoR6KdamsLg5VE8xzst48a4UFuzHFlklZ5O8om2rcvDd5DhSnWWYZnYJecqB+bo
-dNisPmaIVSAWb29U8BpNRj4GMC9KAgGYUj8aE/KtutAeEekFfFEHTfWZ2fFp4j3m
-9rY8FWraqyiF+Emq1T8pAAgMQ+79R3oZxq0TXS42Z4Anhms735ccauKhI3pDKjbl
-tD5vAzIHOyjAOXj7a6jY/GrnaBNuJ4qe/4Hf9UmzK/jKKwG95BPJtPTT4LoFwEB0
-KG2OUeQUNoCck4nDpsZwFqPlrWCHcHfTV2iDYFV1HQWDTtZz/qf+GtB8NXsq+I1w
-brADmvReM2BD6p/13h0QURCI5hq7ZYlIKcKrBa0jn1d9cduULl7vgKsRCJDls/ID
-emBZ6pUxMpBmV0v+PrA3v9w4DlE7GHAq61FF/zju2kpqj6MInbEvI/E+e438sWsL
------END SIGNATURE-----"""
-
-    expected_signature = """-----BEGIN SIGNATURE-----
-fskXN84wB3mXfo+yKGSt0AcDaaPuU3NwMR3ROxWgLN0KjAaVi2eV9PkPCsQkcgw3
-JZ/1HL9sHyZfo6bwaC6YSM9PNiiY6L7rnGpS7UkHiFI+M96VCMorvjm5YPs3FioJ
-DnN5aFtYKiTc19qIC7Nmo+afPdDEf0MlJvEOP5EWl3w=
------END SIGNATURE-----"""
-
-    with open(get_resource('vote'), 'rb') as descriptor_file:
-      document = stem.descriptor.networkstatus.NetworkStatusDocumentV3(descriptor_file.read(), default_params = False)
-
-      self.assertEquals(3, document.version)
-      self.assertEquals(None, document.version_flavor)
-      self.assertEquals(False, document.is_consensus)
-      self.assertEquals(True, document.is_vote)
-      self.assertEquals(False, document.is_microdescriptor)
-      self.assertEquals(datetime.datetime(2012, 7, 12, 0, 0, 0), document.valid_after)
-      self.assertEquals(datetime.datetime(2012, 7, 12, 1, 0, 0), document.fresh_until)
-      self.assertEquals(datetime.datetime(2012, 7, 12, 3, 0, 0), document.valid_until)
-      self.assertEquals(300, document.vote_delay)
-      self.assertEquals(300, document.dist_delay)
-      self.assertEquals([], document.client_versions)
-      self.assertEquals([], document.server_versions)
-      self.assertEquals(expected_flags, set(document.known_flags))
-      self.assertEquals({'CircuitPriorityHalflifeMsec': 30000, 'bwauthpid': 1}, document.params)
-
-      self.assertEquals(None, document.consensus_method)
-      self.assertEquals({}, document.bandwidth_weights)
-      self.assertEquals(range(1, 13), document.consensus_methods)
-      self.assertEquals(datetime.datetime(2012, 7, 11, 23, 50, 1), document.published)
-      self.assertEquals([], document.get_unrecognized_lines())
-
-      router = document.routers['0013D22389CD50D0B784A3E4061CB31E8CE8CEB5']
-      self.assertEquals('sumkledi', router.nickname)
-      self.assertEquals('0013D22389CD50D0B784A3E4061CB31E8CE8CEB5', router.fingerprint)
-      self.assertEquals('0799F806200B005F01E40A9A7F1A21C988AE8FB1', router.digest)
-      self.assertEquals(datetime.datetime(2012, 7, 11, 4, 22, 53), router.published)
-      self.assertEquals('178.218.213.229', router.address)
-      self.assertEquals(80, router.or_port)
-      self.assertEquals(None, router.dir_port)
-
-      authority = document.directory_authorities[0]
-      self.assertEquals(1, len(document.directory_authorities))
-      self.assertEquals('turtles', authority.nickname)
-      self.assertEquals('27B6B5996C426270A5C95488AA5BCEB6BCC86956', authority.fingerprint)
-      self.assertEquals('76.73.17.194', authority.hostname)
-      self.assertEquals('76.73.17.194', authority.address)
-      self.assertEquals(9030, authority.dir_port)
-      self.assertEquals(9090, authority.or_port)
-      self.assertEquals('Mike Perry <email>', authority.contact)
-      self.assertEquals(None, authority.vote_digest)
-      self.assertEquals(None, authority.legacy_dir_key)
-
-      certificate = authority.key_certificate
-      self.assertEquals(3, certificate.version)
-      self.assertEquals(None, certificate.address)
-      self.assertEquals(None, certificate.dir_port)
-      self.assertEquals('27B6B5996C426270A5C95488AA5BCEB6BCC86956', certificate.fingerprint)
-      self.assertEquals(expected_identity_key, certificate.identity_key)
-      self.assertEquals(datetime.datetime(2011, 11, 28, 21, 51, 4), certificate.published)
-      self.assertEquals(datetime.datetime(2012, 11, 28, 21, 51, 4), certificate.expires)
-      self.assertEquals(expected_signing_key, certificate.signing_key)
-      self.assertEquals(expected_key_crosscert, certificate.crosscert)
-      self.assertEquals(expected_key_certification, certificate.certification)
-
-      signature = document.signatures[0]
-      self.assertEquals(1, len(document.signatures))
-      self.assertEquals('sha1', signature.method)
-      self.assertEquals('27B6B5996C426270A5C95488AA5BCEB6BCC86956', signature.identity)
-      self.assertEquals('D5C30C15BB3F1DA27669C2D88439939E8F418FCF', signature.key_digest)
-      self.assertEquals(expected_signature, signature.signature)
diff --git a/test/integ/descriptor/reader.py b/test/integ/descriptor/reader.py
deleted file mode 100644
index 7791f3e..0000000
--- a/test/integ/descriptor/reader.py
+++ /dev/null
@@ -1,562 +0,0 @@
-"""
-Integration tests for stem.descriptor.reader.
-"""
-
-import getpass
-import os
-import signal
-import sys
-import tarfile
-import time
-import unittest
-
-import stem.descriptor.reader
-import test.runner
-
-from stem.util import system
-
-BASIC_LISTING = """
-/tmp 123
-/bin/grep 4567
-/file with spaces/and \\ stuff 890
-"""
-
-my_dir = os.path.dirname(__file__)
-DESCRIPTOR_TEST_DATA = os.path.join(my_dir, 'data')
-
-TAR_DESCRIPTORS = None
-
-
-def _get_processed_files_path():
-  return test.runner.get_runner().get_test_dir('descriptor_processed_files')
-
-
-def _make_processed_files_listing(contents):
-  """
-  Writes the given 'processed file' listing to disk, returning the path where
-  it is located.
-  """
-
-  test_listing_path = _get_processed_files_path()
-
-  test_listing_file = open(test_listing_path, 'w')
-  test_listing_file.write(contents)
-  test_listing_file.close()
-
-  return test_listing_path
-
-
-def _get_raw_tar_descriptors():
-  global TAR_DESCRIPTORS
-
-  if not TAR_DESCRIPTORS:
-    test_path = os.path.join(DESCRIPTOR_TEST_DATA, 'descriptor_archive.tar')
-    raw_descriptors = []
-
-    # TODO: revert to using the 'with' keyword for this when dropping python
-    # 2.6 support
-
-    tar_file = None
-
-    try:
-      tar_file = tarfile.open(test_path)
-
-      for tar_entry in tar_file:
-        if tar_entry.isfile():
-          entry = tar_file.extractfile(tar_entry)
-          entry.readline()  # strip header
-          raw_descriptors.append(entry.read().decode('utf-8', 'replace'))
-          entry.close()
-    finally:
-      if tar_file:
-        tar_file.close()
-
-    TAR_DESCRIPTORS = raw_descriptors
-
-  return TAR_DESCRIPTORS
-
-
-class SkipListener:
-  def __init__(self):
-    self.results = []  # (path, exception) tuples that we've received
-
-  def listener(self, path, exception):
-    self.results.append((path, exception))
-
-
-class TestDescriptorReader(unittest.TestCase):
-  def tearDown(self):
-    # cleans up 'processed file' listings that we made
-    test_listing_path = _get_processed_files_path()
-
-    if os.path.exists(test_listing_path):
-      os.remove(test_listing_path)
-
-  def test_load_processed_files(self):
-    """
-    Basic sanity test for loading a processed files listing from disk.
-    """
-
-    test_listing_path = _make_processed_files_listing(BASIC_LISTING)
-    loaded_listing = stem.descriptor.reader.load_processed_files(test_listing_path)
-
-    expected_listing = {
-      '/tmp': 123,
-      '/bin/grep': 4567,
-      '/file with spaces/and \\ stuff': 890,
-    }
-
-    self.assertEquals(expected_listing, loaded_listing)
-
-  def test_load_processed_files_missing(self):
-    """
-    Tests the load_processed_files() function with a file that doesn't exist.
-    """
-
-    self.assertRaises(IOError, stem.descriptor.reader.load_processed_files, '/non-existant/path')
-
-  def test_load_processed_files_permissions(self):
-    """
-    Tests the load_processed_files() function with a file that can't be read
-    due to permissions.
-    """
-
-    # test relies on being unable to read a file
-
-    if getpass.getuser() == 'root':
-      test.runner.skip(self, '(running as root)')
-      return
-
-    # Skip the test on windows, since you can only set the file's
-    # read-only flag with os.chmod(). For more information see...
-    # http://docs.python.org/library/os.html#os.chmod
-
-    if system.is_windows():
-      test.runner.skip(self, '(chmod not functional)')
-
-    test_listing_path = _make_processed_files_listing(BASIC_LISTING)
-    os.chmod(test_listing_path, 0077)  # remove read permissions
-    self.assertRaises(IOError, stem.descriptor.reader.load_processed_files, test_listing_path)
-
-  def test_save_processed_files(self):
-    """
-    Basic sanity test for persisting files listings to disk.
-    """
-
-    initial_listing = {
-      '/tmp': 123,
-      '/bin/grep': 4567,
-      '/file with spaces/and \\ stuff': 890,
-    }
-
-    # saves the initial_listing to a file then reloads it
-    test_listing_path = _get_processed_files_path()
-    stem.descriptor.reader.save_processed_files(test_listing_path, initial_listing)
-    loaded_listing = stem.descriptor.reader.load_processed_files(test_listing_path)
-
-    self.assertEquals(initial_listing, loaded_listing)
-
-  def test_save_processed_files_malformed(self):
-    """
-    Tests the save_processed_files() function with malformed data.
-    """
-
-    missing_filename = {'': 123}
-    relative_filename = {'foobar': 123}
-    string_timestamp = {'/tmp': '123a'}
-
-    for listing in (missing_filename, relative_filename, string_timestamp):
-      self.assertRaises(TypeError, stem.descriptor.reader.save_processed_files, '/tmp/foo', listing)
-
-    # Though our attempts to save the processed files fail we'll write an empty
-    # file. Cleaning it up.
-
-    try:
-      os.remove('/tmp/foo')
-    except:
-      pass
-
-  def test_basic_example(self):
-    """
-    Exercises something similar to the first example in the header
-    documentation, checking that some of the contents match what we'd expect.
-    """
-
-    # snag some of the plaintext descriptors so we can later make sure that we
-    # iterate over them
-
-    descriptor_entries = []
-
-    descriptor_path = os.path.join(DESCRIPTOR_TEST_DATA, 'example_descriptor')
-
-    with open(descriptor_path) as descriptor_file:
-      descriptor_file.readline()  # strip header
-      descriptor_entries.append(descriptor_file.read())
-
-    # running this test multiple times to flush out concurrency issues
-
-    for _ in xrange(15):
-      remaining_entries = list(descriptor_entries)
-
-      with stem.descriptor.reader.DescriptorReader(descriptor_path) as reader:
-        for descriptor in reader:
-          descriptor_str = str(descriptor)
-
-          if descriptor_str in remaining_entries:
-            remaining_entries.remove(descriptor_str)
-          else:
-            # iterator is providing output that we didn't expect
-            self.fail()
-
-      # check that we've seen all of the descriptor_entries
-      self.assertTrue(len(remaining_entries) == 0)
-
-  def test_multiple_runs(self):
-    """
-    Runs a DescriptorReader instance multiple times over the same content,
-    making sure that it can be used repeatedly.
-    """
-
-    descriptor_path = os.path.join(DESCRIPTOR_TEST_DATA, 'example_descriptor')
-    reader = stem.descriptor.reader.DescriptorReader(descriptor_path)
-
-    with reader:
-      self.assertEquals(1, len(list(reader)))
-
-    # run it a second time, this shouldn't provide any descriptors because we
-    # have already read it
-
-    with reader:
-      self.assertEquals(0, len(list(reader)))
-
-    # clear the DescriptorReader's memory of seeing the file and run it again
-
-    reader.set_processed_files([])
-
-    with reader:
-      self.assertEquals(1, len(list(reader)))
-
-  def test_buffer_size(self):
-    """
-    Checks that we can process sets of descriptors larger than our buffer size,
-    that we don't exceed it, and that we can still stop midway through reading
-    them.
-    """
-
-    reader = stem.descriptor.reader.DescriptorReader(DESCRIPTOR_TEST_DATA, buffer_size = 2)
-
-    with reader:
-      self.assertTrue(reader.get_buffered_descriptor_count() <= 2)
-      time.sleep(0.01)
-      self.assertTrue(reader.get_buffered_descriptor_count() <= 2)
-
-  def test_persistence_path(self):
-    """
-    Check that the persistence_path argument loads and saves a a processed
-    files listing.
-    """
-
-    persistence_path = _get_processed_files_path()
-    descriptor_path = os.path.join(DESCRIPTOR_TEST_DATA, 'example_descriptor')
-
-    # First run where the persistence_path doesn't yet exist. This just tests
-    # the saving functionality.
-
-    reader = stem.descriptor.reader.DescriptorReader(descriptor_path, persistence_path = persistence_path)
-
-    with reader:
-      self.assertEqual(1, len(list(reader)))
-
-    # check that we've saved reading example_descriptor
-    self.assertTrue(os.path.exists(persistence_path))
-
-    with open(persistence_path) as persistence_file:
-      persistance_file_contents = persistence_file.read()
-      self.assertTrue(persistance_file_contents.startswith(descriptor_path))
-
-    # Try running again with a new reader but the same persistance path, if it
-    # reads and takes the persistence_path into account then it won't read the
-    # descriptor file. This in essence just tests its loading functionality.
-
-    reader = stem.descriptor.reader.DescriptorReader(descriptor_path, persistence_path = persistence_path)
-
-    with reader:
-      self.assertEqual(0, len(list(reader)))
-
-  def test_archived_paths(self):
-    """
-    Checks the get_path() and get_archive_path() for a tarball.
-    """
-
-    expected_archive_paths = (
-      'descriptor_archive/0/2/02c311d3d789f3f55c0880b5c85f3c196343552c',
-      'descriptor_archive/1/b/1bb798cae15e21479db0bc700767eee4733e9d4a',
-      'descriptor_archive/1/b/1ef75fef564180d8b3f72c6f8635ff0cd855f92c',
-    )
-
-    test_path = os.path.join(DESCRIPTOR_TEST_DATA, 'descriptor_archive.tar')
-
-    with stem.descriptor.reader.DescriptorReader(test_path) as reader:
-      for desc in reader:
-        self.assertEqual(test_path, desc.get_path())
-        self.assertTrue(desc.get_archive_path() in expected_archive_paths)
-
-  def test_archived_uncompressed(self):
-    """
-    Checks that we can read descriptors from an uncompressed archive.
-    """
-
-    expected_results = _get_raw_tar_descriptors()
-    test_path = os.path.join(DESCRIPTOR_TEST_DATA, 'descriptor_archive.tar')
-
-    with stem.descriptor.reader.DescriptorReader(test_path) as reader:
-      read_descriptors = [str(desc) for desc in list(reader)]
-      self.assertEquals(expected_results, read_descriptors)
-
-  def test_archived_gzip(self):
-    """
-    Checks that we can read descriptors from a gzipped archive.
-    """
-
-    expected_results = _get_raw_tar_descriptors()
-    test_path = os.path.join(DESCRIPTOR_TEST_DATA, 'descriptor_archive.tar.gz')
-
-    with stem.descriptor.reader.DescriptorReader(test_path) as reader:
-      read_descriptors = [str(desc) for desc in list(reader)]
-      self.assertEquals(expected_results, read_descriptors)
-
-  def test_archived_bz2(self):
-    """
-    Checks that we can read descriptors from an bzipped archive.
-    """
-
-    expected_results = _get_raw_tar_descriptors()
-    test_path = os.path.join(DESCRIPTOR_TEST_DATA, 'descriptor_archive.tar.bz2')
-
-    with stem.descriptor.reader.DescriptorReader(test_path) as reader:
-      read_descriptors = [str(desc) for desc in list(reader)]
-      self.assertEquals(expected_results, read_descriptors)
-
-  def test_stop(self):
-    """
-    Runs a DescriptorReader over the root directory, then checks that calling
-    stop() makes it terminate in a timely fashion.
-    """
-
-    # Skip on windows since SIGALRM is unavailable
-
-    if system.is_windows():
-      test.runner.skip(self, '(SIGALRM unavailable)')
-
-    is_test_running = True
-    reader = stem.descriptor.reader.DescriptorReader('/usr')
-
-    # Fails the test after a couple seconds if we don't finish successfully.
-    # Depending on what we're blocked on this might not work when the test
-    # fails, requiring that we give a manual kill to the test.
-
-    def timeout_handler(signum, frame):
-      if is_test_running:
-        self.fail()
-
-    signal.signal(signal.SIGALRM, timeout_handler)
-    signal.alarm(2)
-
-    reader.start()
-    time.sleep(0.1)
-    reader.stop()
-    is_test_running = False
-
-  def test_get_processed_files(self):
-    """
-    Checks that get_processed_files() provides the expected results after
-    iterating over our test data.
-    """
-
-    expected_results = {}
-
-    for root, _, files in os.walk(DESCRIPTOR_TEST_DATA):
-      for filename in files:
-        path = os.path.join(root, filename)
-        last_modified = int(os.stat(path).st_mtime)
-        expected_results[path] = last_modified
-
-    reader = stem.descriptor.reader.DescriptorReader(DESCRIPTOR_TEST_DATA)
-
-    with reader:
-      list(reader)  # iterates over all of the descriptors
-
-    self.assertEquals(expected_results, reader.get_processed_files())
-
-  def test_skip_nondescriptor_contents(self):
-    """
-    Checks that the reader properly reports when it skips both binary and
-    plaintext non-descriptor files.
-    """
-
-    skip_listener = SkipListener()
-    reader = stem.descriptor.reader.DescriptorReader(DESCRIPTOR_TEST_DATA)
-    reader.register_skip_listener(skip_listener.listener)
-
-    expected_skip_files = ('riddle', 'tiny.png', 'vote', 'new_metrics_type')
-
-    with reader:
-      list(reader)  # iterates over all of the descriptors
-
-    # strip anything with a .swp suffix (vim tmp files)
-
-    skip_listener.results = [(path, exc) for (path, exc) in skip_listener.results if not path.endswith('.swp')]
-
-    if len(skip_listener.results) != len(expected_skip_files):
-      expected_label = ',\n  '.join(expected_skip_files)
-      results_label = ',\n  '.join(['%s (%s)' % (path, exc) for (path, exc) in skip_listener.results])
-
-      self.fail('Skipped files that we should have been able to parse.\n\nExpected:\n  %s\n\nResult:\n  %s' % (expected_label, results_label))
-
-    for skip_path, skip_exception in skip_listener.results:
-      if not os.path.basename(skip_path) in expected_skip_files:
-        self.fail('Unexpected non-descriptor content: %s' % skip_path)
-
-      self.assertTrue(isinstance(skip_exception, stem.descriptor.reader.UnrecognizedType))
-
-  def test_skip_listener_already_read(self):
-    """
-    Checks that calling set_processed_files() prior to reading makes us skip
-    those files. This also doubles for testing that skip listeners are notified
-    of files that we've already read.
-    """
-
-    # path that we want the DescriptorReader to skip
-
-    test_path = os.path.join(DESCRIPTOR_TEST_DATA, 'example_descriptor')
-    initial_processed_files = {test_path: sys.maxint}
-
-    skip_listener = SkipListener()
-    reader = stem.descriptor.reader.DescriptorReader(test_path)
-    reader.register_skip_listener(skip_listener.listener)
-    reader.set_processed_files(initial_processed_files)
-
-    self.assertEquals(initial_processed_files, reader.get_processed_files())
-
-    with reader:
-      list(reader)  # iterates over all of the descriptors
-
-    self.assertEquals(1, len(skip_listener.results))
-
-    skipped_path, skip_exception = skip_listener.results[0]
-    self.assertEqual(test_path, skipped_path)
-    self.assertTrue(isinstance(skip_exception, stem.descriptor.reader.AlreadyRead))
-    self.assertEqual(sys.maxint, skip_exception.last_modified_when_read)
-
-  def test_skip_listener_unrecognized_type(self):
-    """
-    Listens for a file that's skipped because its file type isn't recognized.
-    """
-
-    # types are solely based on file extensions so making something that looks
-    # like an png image
-
-    test_path = test.runner.get_runner().get_test_dir('test.png')
-
-    try:
-      test_file = open(test_path, 'w')
-      test_file.write('test data for test_skip_listener_unrecognized_type()')
-      test_file.close()
-
-      skip_listener = SkipListener()
-      reader = stem.descriptor.reader.DescriptorReader(test_path)
-      reader.register_skip_listener(skip_listener.listener)
-
-      with reader:
-        list(reader)  # iterates over all of the descriptors
-
-      self.assertEqual(1, len(skip_listener.results))
-
-      skipped_path, skip_exception = skip_listener.results[0]
-      self.assertEqual(test_path, skipped_path)
-      self.assertTrue(isinstance(skip_exception, stem.descriptor.reader.UnrecognizedType))
-      self.assertTrue(skip_exception.mime_type in (('image/png', None), ('image/x-png', None)))
-    finally:
-      if os.path.exists(test_path):
-        os.remove(test_path)
-
-  def test_skip_listener_read_failure(self):
-    """
-    Listens for a file that's skipped because we lack read permissions.
-    """
-
-    # test relies on being unable to read a file
-
-    if getpass.getuser() == 'root':
-      test.runner.skip(self, '(running as root)')
-      return
-    elif system.is_windows():
-      test.runner.skip(self, '(chmod not functional)')
-      return
-
-    test_path = test.runner.get_runner().get_test_dir('secret_file')
-
-    try:
-      test_file = open(test_path, 'w')
-      test_file.write('test data for test_skip_listener_unrecognized_type()')
-      test_file.close()
-
-      os.chmod(test_path, 0077)  # remove read permissions
-
-      skip_listener = SkipListener()
-      reader = stem.descriptor.reader.DescriptorReader(test_path)
-      reader.register_skip_listener(skip_listener.listener)
-
-      with reader:
-        list(reader)  # iterates over all of the descriptors
-
-      self.assertEqual(1, len(skip_listener.results))
-
-      skipped_path, skip_exception = skip_listener.results[0]
-      self.assertEqual(test_path, skipped_path)
-      self.assertTrue(isinstance(skip_exception, stem.descriptor.reader.ReadFailed))
-      self.assertTrue(isinstance(skip_exception.exception, IOError))
-    finally:
-      if os.path.exists(test_path):
-        os.remove(test_path)
-
-  def test_skip_listener_file_missing(self):
-    """
-    Listens for a file that's skipped because the file doesn't exist.
-    """
-
-    test_path = '/non-existant/path'
-
-    skip_listener = SkipListener()
-    reader = stem.descriptor.reader.DescriptorReader(test_path)
-    reader.register_skip_listener(skip_listener.listener)
-
-    with reader:
-      list(reader)  # iterates over all of the descriptors
-
-    self.assertEqual(1, len(skip_listener.results))
-
-    skipped_path, skip_exception = skip_listener.results[0]
-    self.assertEqual(test_path, skipped_path)
-    self.assertTrue(isinstance(skip_exception, stem.descriptor.reader.FileMissing))
-
-  def test_unrecognized_metrics_type(self):
-    """
-    Parses a file that has a valid metrics header, but an unrecognized type.
-    """
-
-    test_path = test.integ.descriptor.get_resource('new_metrics_type')
-
-    skip_listener = SkipListener()
-    reader = stem.descriptor.reader.DescriptorReader(test_path)
-    reader.register_skip_listener(skip_listener.listener)
-
-    with reader:
-      list(reader)  # iterates over all of the descriptors
-
-    self.assertEqual(1, len(skip_listener.results))
-
-    skipped_path, skip_exception = skip_listener.results[0]
-    self.assertEqual(test_path, skipped_path)
-    self.assertTrue(isinstance(skip_exception, stem.descriptor.reader.UnrecognizedType))
-    self.assertEqual((None, None), skip_exception.mime_type)
diff --git a/test/integ/descriptor/server_descriptor.py b/test/integ/descriptor/server_descriptor.py
index fef9264..6882c58 100644
--- a/test/integ/descriptor/server_descriptor.py
+++ b/test/integ/descriptor/server_descriptor.py
@@ -2,185 +2,15 @@
 Integration tests for stem.descriptor.server_descriptor.
 """
 
-import datetime
 import os
-import tarfile
 import unittest
 
-import stem.control
 import stem.descriptor
-import stem.descriptor.server_descriptor
-import stem.exit_policy
-import stem.version
-import test.runner
-
-from test.integ.descriptor import get_resource
 
-TARFILE_PATH = os.path.join(os.path.dirname(__file__), 'data', 'descriptor_archive.tar')
-TARFILE_FINGERPRINTS = set([
-  u'B6D83EC2D9E18B0A7A33428F8CFA9C536769E209',
-  u'E0BD57A11F00041A9789577C53A1B784473669E4',
-  u'1F43EE37A0670301AD9CB555D94AFEC2C89FDE86',
-])
+import test.runner
 
 
 class TestServerDescriptor(unittest.TestCase):
-  def test_with_tarfile_path(self):
-    """
-    Fetch server descriptors via parse_file() for a tarfile path.
-    """
-
-    descriptors = list(stem.descriptor.parse_file(TARFILE_PATH))
-    self.assertEqual(3, len(descriptors))
-
-    fingerprints = set([desc.fingerprint for desc in descriptors])
-    self.assertEqual(TARFILE_FINGERPRINTS, fingerprints)
-
-  def test_with_tarfile_object(self):
-    """
-    Fetch server descriptors via parse_file() for a tarfile object.
-    """
-
-    # TODO: When dropping python 2.6 support we can go back to using the 'with'
-    # keyword here.
-
-    tar_file = tarfile.open(TARFILE_PATH)
-    descriptors = list(stem.descriptor.parse_file(tar_file))
-    self.assertEqual(3, len(descriptors))
-
-    fingerprints = set([desc.fingerprint for desc in descriptors])
-    self.assertEqual(TARFILE_FINGERPRINTS, fingerprints)
-    tar_file.close()
-
-  def test_metrics_descriptor(self):
-    """
-    Parses and checks our results against a server descriptor from metrics.
-    """
-
-    descriptor_file = open(get_resource('example_descriptor'), 'rb')
-
-    expected_family = set([
-      '$0CE3CFB1E9CC47B63EA8869813BF6FAB7D4540C1',
-      '$1FD187E8F69A9B74C9202DC16A25B9E7744AB9F6',
-      '$74FB5EFA6A46DE4060431D515DC9A790E6AD9A7C',
-      '$77001D8DA9BF445B0F81AA427A675F570D222E6A',
-      '$B6D83EC2D9E18B0A7A33428F8CFA9C536769E209',
-      '$D2F37F46182C23AB747787FD657E680B34EAF892',
-      '$E0BD57A11F00041A9789577C53A1B784473669E4',
-      '$E5E3E9A472EAF7BE9682B86E92305DB4C71048EF',
-    ])
-
-    expected_onion_key = """-----BEGIN RSA PUBLIC KEY-----
-MIGJAoGBAJv5IIWQ+WDWYUdyA/0L8qbIkEVH/cwryZWoIaPAzINfrw1WfNZGtBmg
-skFtXhOHHqTRN4GPPrZsAIUOQGzQtGb66IQgT4tO/pj+P6QmSCCdTfhvGfgTCsC+
-WPi4Fl2qryzTb3QO5r5x7T8OsG2IBUET1bLQzmtbC560SYR49IvVAgMBAAE=
------END RSA PUBLIC KEY-----"""
-
-    expected_signing_key = """-----BEGIN RSA PUBLIC KEY-----
-MIGJAoGBAKwvOXyztVKnuYvpTKt+nS3XIKeO8dVungi8qGoeS+6gkR6lDtGfBTjd
-uE9UIkdAl9zi8/1Ic2wsUNHE9jiS0VgeupITGZY8YOyMJJ/xtV1cqgiWhq1dUYaq
-51TOtUogtAPgXPh4J+V8HbFFIcCzIh3qCO/xXo+DSHhv7SSif1VpAgMBAAE=
------END RSA PUBLIC KEY-----"""
-
-    expected_signature = """-----BEGIN SIGNATURE-----
-dskLSPz8beUW7bzwDjR6EVNGpyoZde83Ejvau+5F2c6cGnlu91fiZN3suE88iE6e
-758b9ldq5eh5mapb8vuuV3uO+0Xsud7IEOqfxdkmk0GKnUX8ouru7DSIUzUL0zqq
-Qlx9HNCqCY877ztFRC624ja2ql6A2hBcuoYMbkHjcQ4=
------END SIGNATURE-----"""
-
-    desc = next(stem.descriptor.parse_file(descriptor_file, 'server-descriptor 1.0'))
-    self.assertEquals('caerSidi', desc.nickname)
-    self.assertEquals('A7569A83B5706AB1B1A9CB52EFF7D2D32E4553EB', desc.fingerprint)
-    self.assertEquals('71.35.133.197', desc.address)
-    self.assertEquals(9001, desc.or_port)
-    self.assertEquals(None, desc.socks_port)
-    self.assertEquals(None, desc.dir_port)
-    self.assertEquals(b'Tor 0.2.1.30 on Linux x86_64', desc.platform)
-    self.assertEquals(stem.version.Version('0.2.1.30'), desc.tor_version)
-    self.assertEquals('Linux x86_64', desc.operating_system)
-    self.assertEquals(588217, desc.uptime)
-    self.assertEquals(datetime.datetime(2012, 3, 1, 17, 15, 27), desc.published)
-    self.assertEquals(b'www.atagar.com/contact', desc.contact)
-    self.assertEquals(['1', '2'], desc.link_protocols)
-    self.assertEquals(['1'], desc.circuit_protocols)
-    self.assertEquals(False, desc.hibernating)
-    self.assertEquals(False, desc.allow_single_hop_exits)
-    self.assertEquals(False, desc.extra_info_cache)
-    self.assertEquals('D225B728768D7EA4B5587C13A7A9D22EBBEE6E66', desc.extra_info_digest)
-    self.assertEquals(['2'], desc.hidden_service_dir)
-    self.assertEquals(expected_family, desc.family)
-    self.assertEquals(153600, desc.average_bandwidth)
-    self.assertEquals(256000, desc.burst_bandwidth)
-    self.assertEquals(104590, desc.observed_bandwidth)
-    self.assertEquals(stem.exit_policy.ExitPolicy('reject *:*'), desc.exit_policy)
-    self.assertEquals(expected_onion_key, desc.onion_key)
-    self.assertEquals(expected_signing_key, desc.signing_key)
-    self.assertEquals(expected_signature, desc.signature)
-    self.assertEquals([], desc.get_unrecognized_lines())
-    self.assertEquals('2C7B27BEAB04B4E2459D89CA6D5CD1CC5F95A689', desc.digest())
-
-  def test_metrics_descriptor_multiple(self):
-    """
-    Parses and checks our results against a server descriptor from metrics.
-    """
-
-    with open(get_resource('metrics_server_desc_multiple'), 'rb') as descriptor_file:
-      descriptors = list(stem.descriptor.parse_file(descriptor_file, 'server-descriptor 1.0'))
-
-      self.assertEquals(2, len(descriptors))
-
-      self.assertEquals('anonion', descriptors[0].nickname)
-      self.assertEquals('9A5EC5BB866517E53962AF4D3E776536694B069E', descriptors[0].fingerprint)
-
-      self.assertEquals('Unnamed', descriptors[1].nickname)
-      self.assertEquals('5366F1D198759F8894EA6E5FF768C667F59AFD24', descriptors[1].fingerprint)
-
-  def test_old_descriptor(self):
-    """
-    Parses a relay server descriptor from 2005.
-    """
-
-    descriptor_file = open(get_resource('old_descriptor'), 'rb')
-
-    desc = next(stem.descriptor.parse_file(descriptor_file, 'server-descriptor 1.0'))
-    self.assertEquals('krypton', desc.nickname)
-    self.assertEquals('3E2F63E2356F52318B536A12B6445373808A5D6C', desc.fingerprint)
-    self.assertEquals('212.37.39.59', desc.address)
-    self.assertEquals(8000, desc.or_port)
-    self.assertEquals(None, desc.socks_port)
-    self.assertEquals(None, desc.dir_port)
-    self.assertEquals(b'Tor 0.1.0.14 on FreeBSD i386', desc.platform)
-    self.assertEquals(stem.version.Version('0.1.0.14'), desc.tor_version)
-    self.assertEquals('FreeBSD i386', desc.operating_system)
-    self.assertEquals(64820, desc.uptime)
-    self.assertEquals(datetime.datetime(2005, 12, 16, 18, 1, 3), desc.published)
-    self.assertEquals(None, desc.contact)
-    self.assertEquals(None, desc.link_protocols)
-    self.assertEquals(None, desc.circuit_protocols)
-    self.assertEquals(True, desc.hibernating)
-    self.assertEquals(False, desc.allow_single_hop_exits)
-    self.assertEquals(False, desc.extra_info_cache)
-    self.assertEquals(None, desc.extra_info_digest)
-    self.assertEquals(None, desc.hidden_service_dir)
-    self.assertEquals(set(), desc.family)
-    self.assertEquals(102400, desc.average_bandwidth)
-    self.assertEquals(10485760, desc.burst_bandwidth)
-    self.assertEquals(0, desc.observed_bandwidth)
-    self.assertEquals(datetime.datetime(2005, 12, 16, 18, 0, 48), desc.read_history_end)
-    self.assertEquals(900, desc.read_history_interval)
-    self.assertEquals(datetime.datetime(2005, 12, 16, 18, 0, 48), desc.write_history_end)
-    self.assertEquals(900, desc.write_history_interval)
-    self.assertEquals([], desc.get_unrecognized_lines())
-
-    # The read-history and write-history lines are pretty long so just checking
-    # the initial contents for the line and parsed values.
-
-    read_values_start = [20774, 489973, 510022, 511163, 20949]
-    self.assertEquals(read_values_start, desc.read_history_values[:5])
-
-    write_values_start = [81, 8848, 8927, 8927, 83, 8848, 8931, 8929, 81, 8846]
-    self.assertEquals(write_values_start, desc.write_history_values[:10])
-
   def test_cached_descriptor(self):
     """
     Parses the cached descriptor file in our data directory, checking that it
@@ -217,128 +47,3 @@ Qlx9HNCqCY877ztFRC624ja2ql6A2hBcuoYMbkHjcQ4=
           # failing to get our attention if it does.
 
           self.fail('Unrecognized descriptor content: %s' % unrecognized_lines)
-
-  def test_non_ascii_descriptor(self):
-    """
-    Parses a descriptor with non-ascii content.
-    """
-
-    descriptor_file = open(get_resource('non-ascii_descriptor'), 'rb')
-
-    expected_contact = b'1024D/04D2E818 L\xc3\xa9na\xc3\xafc Huard <lenaic dot huard AT laposte dot net>'
-
-    desc = next(stem.descriptor.parse_file(descriptor_file, 'server-descriptor 1.0'))
-    self.assertEquals('Coruscant', desc.nickname)
-    self.assertEquals('0B9821545C48E496AEED9ECC0DB506C49FF8158D', desc.fingerprint)
-    self.assertEquals('88.182.161.122', desc.address)
-    self.assertEquals(9001, desc.or_port)
-    self.assertEquals(None, desc.socks_port)
-    self.assertEquals(9030, desc.dir_port)
-    self.assertEquals(b'Tor 0.2.3.25 on Linux', desc.platform)
-    self.assertEquals(stem.version.Version('0.2.3.25'), desc.tor_version)
-    self.assertEquals('Linux', desc.operating_system)
-    self.assertEquals(259738, desc.uptime)
-    self.assertEquals(datetime.datetime(2013, 5, 18, 11, 16, 19), desc.published)
-    self.assertEquals(expected_contact, desc.contact)
-    self.assertEquals(['1', '2'], desc.link_protocols)
-    self.assertEquals(['1'], desc.circuit_protocols)
-    self.assertEquals(False, desc.hibernating)
-    self.assertEquals(False, desc.allow_single_hop_exits)
-    self.assertEquals(False, desc.extra_info_cache)
-    self.assertEquals('56403D838DE152421CD401B8E57DAD4483A3D56B', desc.extra_info_digest)
-    self.assertEquals(['2'], desc.hidden_service_dir)
-    self.assertEquals(set(), desc.family)
-    self.assertEquals(102400, desc.average_bandwidth)
-    self.assertEquals(204800, desc.burst_bandwidth)
-    self.assertEquals(122818, desc.observed_bandwidth)
-    self.assertEquals(stem.exit_policy.ExitPolicy('reject *:*'), desc.exit_policy)
-    self.assertEquals([], desc.get_unrecognized_lines())
-
-    # Make sure that we can get a string representation for this descriptor
-    # (having non-unicode content risks a UnicodeEncodeError)...
-    #
-    # https://trac.torproject.org/8265
-
-    self.assertTrue(isinstance(str(desc), str))
-
-  def test_cr_in_contact_line(self):
-    """
-    Parses a descriptor with a huge contact line containing anomalous carriage
-    returns ('\r' entries).
-    """
-
-    descriptor_file = open(get_resource('cr_in_contact_line'), 'rb')
-    desc = next(stem.descriptor.parse_file(descriptor_file, 'server-descriptor 1.0'))
-
-    self.assertEquals('pogonip', desc.nickname)
-    self.assertEquals('6DABD62BC65D4E6FE620293157FC76968DAB9C9B', desc.fingerprint)
-    self.assertEquals('75.5.248.48', desc.address)
-
-    # the contact info block is huge so just checking the start and end,
-    # including some of the embedded carriage returns
-
-    contact_start = b'jie1 at pacbell dot net -----BEGIN PGP PUBLIC KEY BLOCK-----\rVersion:'
-    contact_end = b'YFRk3NhCY=\r=Xaw3\r-----END PGP PUBLIC KEY BLOCK-----'
-
-    self.assertTrue(desc.contact.startswith(contact_start))
-    self.assertTrue(desc.contact.endswith(contact_end))
-
-  def test_negative_uptime(self):
-    """
-    Parses a descriptor where we are tolerant of a negative uptime, and another
-    where we shouldn't be.
-    """
-
-    descriptor_file = open(get_resource('negative_uptime'), 'rb')
-    desc = next(stem.descriptor.parse_file(descriptor_file, 'server-descriptor 1.0'))
-
-    self.assertEquals('TipTor', desc.nickname)
-    self.assertEquals('137962D4931DBF08A24E843288B8A155D6D2AEDD', desc.fingerprint)
-    self.assertEquals('62.99.247.83', desc.address)
-
-    # modify the relay version so it's after when the negative uptime bug
-    # should appear
-
-    descriptor_contents = str(desc).replace('Tor 0.1.1.25', 'Tor 0.1.2.7')
-    self.assertRaises(ValueError, stem.descriptor.server_descriptor.RelayDescriptor, descriptor_contents)
-
-  def test_bridge_descriptor(self):
-    """
-    Parses a bridge descriptor.
-    """
-
-    descriptor_file = open(get_resource('bridge_descriptor'), 'rb')
-
-    expected_family = set([
-      '$CE396C72A3D0880F74C064FEA79D68C15BD380B9',
-      '$AB8B00C00B1347BA80A88E548FAC9EDF701D7D0E',
-      '$8C8A470D7C23151665A7B84E75E89FCC205A3304',
-    ])
-
-    desc = next(stem.descriptor.parse_file(descriptor_file, 'bridge-server-descriptor 1.0'))
-    self.assertEquals('Unnamed', desc.nickname)
-    self.assertEquals('AE54E28ED069CDF45F3009F963EE3B3D6FA26A2E', desc.fingerprint)
-    self.assertEquals('10.45.227.253', desc.address)
-    self.assertEquals(9001, desc.or_port)
-    self.assertEquals(None, desc.socks_port)
-    self.assertEquals(None, desc.dir_port)
-    self.assertEquals(b'Tor 0.2.3.12-alpha (git-800942b4176ca31c) on Linux x86_64', desc.platform)
-    self.assertEquals(stem.version.Version('0.2.3.12-alpha'), desc.tor_version)
-    self.assertEquals('Linux x86_64', desc.operating_system)
-    self.assertEquals(186, desc.uptime)
-    self.assertEquals(datetime.datetime(2012, 3, 22, 17, 34, 38), desc.published)
-    self.assertEquals(b'somebody', desc.contact)
-    self.assertEquals(['1', '2'], desc.link_protocols)
-    self.assertEquals(['1'], desc.circuit_protocols)
-    self.assertEquals(False, desc.hibernating)
-    self.assertEquals(False, desc.allow_single_hop_exits)
-    self.assertEquals(False, desc.extra_info_cache)
-    self.assertEquals('134F81F7A0D270B85FCD481DD10CEA34BA7B15C9', desc.extra_info_digest)
-    self.assertEquals(['2'], desc.hidden_service_dir)
-    self.assertEquals(expected_family, desc.family)
-    self.assertEquals(409600, desc.average_bandwidth)
-    self.assertEquals(819200, desc.burst_bandwidth)
-    self.assertEquals(5120, desc.observed_bandwidth)
-    self.assertEquals(stem.exit_policy.ExitPolicy('reject *:*'), desc.exit_policy)
-    self.assertEquals('006FD96BA35E7785A6A3B8B75FE2E2435A13BDB4', desc.digest())
-    self.assertEquals([], desc.get_unrecognized_lines())
diff --git a/test/settings.cfg b/test/settings.cfg
index 2ef3736..3dbdde5 100644
--- a/test/settings.cfg
+++ b/test/settings.cfg
@@ -194,7 +194,6 @@ test.integ_tests
 |test.integ.util.connection.TestConnection
 |test.integ.util.proc.TestProc
 |test.integ.util.system.TestSystem
-|test.integ.descriptor.reader.TestDescriptorReader
 |test.integ.descriptor.remote.TestDescriptorDownloader
 |test.integ.descriptor.server_descriptor.TestServerDescriptor
 |test.integ.descriptor.extrainfo_descriptor.TestExtraInfoDescriptor
diff --git a/test/unit/descriptor/__init__.py b/test/unit/descriptor/__init__.py
index 86ec0fd..b182f42 100644
--- a/test/unit/descriptor/__init__.py
+++ b/test/unit/descriptor/__init__.py
@@ -11,3 +11,15 @@ __all__ = [
   'router_status_entry',
   'server_descriptor',
 ]
+
+import os
+
+DESCRIPTOR_TEST_DATA = os.path.join(os.path.dirname(__file__), 'data')
+
+
+def get_resource(filename):
+  """
+  Provides the path for a file in our descriptor data directory.
+  """
+
+  return os.path.join(DESCRIPTOR_TEST_DATA, filename)
diff --git a/test/unit/descriptor/data/bridge_descriptor b/test/unit/descriptor/data/bridge_descriptor
new file mode 100644
index 0000000..b7dbd92
--- /dev/null
+++ b/test/unit/descriptor/data/bridge_descriptor
@@ -0,0 +1,15 @@
+ at type bridge-server-descriptor 1.0
+router Unnamed 10.45.227.253 9001 0 0
+router-digest 006FD96BA35E7785A6A3B8B75FE2E2435A13BDB4
+or-address [fd9f:2e19:3bcf::02:9970]:9001
+platform Tor 0.2.3.12-alpha (git-800942b4176ca31c) on Linux x86_64
+opt protocols Link 1 2 Circuit 1
+published 2012-03-22 17:34:38
+opt fingerprint AE54 E28E D069 CDF4 5F30 09F9 63EE 3B3D 6FA2 6A2E
+uptime 186
+bandwidth 409600 819200 5120
+opt extra-info-digest 134F81F7A0D270B85FCD481DD10CEA34BA7B15C9
+family $CE396C72A3D0880F74C064FEA79D68C15BD380B9 $AB8B00C00B1347BA80A88E548FAC9EDF701D7D0E $8C8A470D7C23151665A7B84E75E89FCC205A3304
+opt hidden-service-dir
+contact somebody
+reject *:*
diff --git a/test/unit/descriptor/data/bridge_network_status b/test/unit/descriptor/data/bridge_network_status
new file mode 100644
index 0000000..ad07672
--- /dev/null
+++ b/test/unit/descriptor/data/bridge_network_status
@@ -0,0 +1,18 @@
+ at type bridge-network-status 1.0
+published 2012-06-01 04:07:04
+r Unnamed ABSiBVJ42z6w5Z6nAXQUFq8YVVg FI74aFuNJZZQrgln0f+OaocMd0M 2012-05-31 15:57:00 10.97.236.247 443 0
+s Valid
+w Bandwidth=55
+p reject 1-65535
+r TolFuin AFn9TveYjdtZEsgh7QsWp3qC5kU 1Sw8RPx2Tq/w+VHL+pZipiJUG5k 2012-05-31 18:12:39 10.99.47.37 80 0
+s Fast Guard Running Stable Valid
+w Bandwidth=32
+p reject 1-65535
+r sidvic AGzeXR+V8NSBE3wTE4m7gBYbjWM 7enyDQ20UjliwCPN2JmTl6zrCdM 2012-05-31 22:56:12 10.145.17.182 80 0
+s Fast Guard Running Stable Valid
+w Bandwidth=56
+p reject 1-65535
+r AntoniusBlock ANk5reA3WwTV62ZpyMo+NWtrvAw gEgv8I1E4LWrs394Xx/cRYQUL5A 2012-05-31 17:21:20 10.95.68.0 443 0
+s Fast Guard Running Stable Valid
+w Bandwidth=55
+p reject 1-65535
diff --git a/test/unit/descriptor/data/cached-consensus b/test/unit/descriptor/data/cached-consensus
new file mode 100644
index 0000000..adcf13b
--- /dev/null
+++ b/test/unit/descriptor/data/cached-consensus
@@ -0,0 +1,130 @@
+network-status-version 3
+vote-status consensus
+consensus-method 12
+valid-after 2012-07-12 10:00:00
+fresh-until 2012-07-12 11:00:00
+valid-until 2012-07-12 13:00:00
+voting-delay 300 300
+client-versions 0.2.2.35,0.2.2.36,0.2.2.37,0.2.3.10-alpha,0.2.3.11-alpha,0.2.3.12-alpha,0.2.3.13-alpha,0.2.3.14-alpha,0.2.3.15-alpha,0.2.3.16-alpha,0.2.3.17-beta,0.2.3.18-rc,0.2.3.19-rc
+server-versions 0.2.2.35,0.2.2.36,0.2.2.37,0.2.3.10-alpha,0.2.3.11-alpha,0.2.3.12-alpha,0.2.3.13-alpha,0.2.3.14-alpha,0.2.3.15-alpha,0.2.3.16-alpha,0.2.3.17-beta,0.2.3.18-rc,0.2.3.19-rc
+known-flags Authority BadExit Exit Fast Guard HSDir Named Running Stable Unnamed V2Dir Valid
+params CircuitPriorityHalflifeMsec=30000 bwauthpid=1
+dir-source tor26 14C131DFC5C6F93646BE72FA1401C02A8DF2E8B4 86.59.21.38 86.59.21.38 80 443
+contact Peter Palfrader
+vote-digest 0B6D1E9A300B895AA2D0B427F92917B6995C3C1C
+dir-source turtles 27B6B5996C426270A5C95488AA5BCEB6BCC86956 76.73.17.194 76.73.17.194 9030 9090
+contact Mike Perry <mikeperryTAfsckedTODorg>
+vote-digest 904B1974B9879D02B4ADFB81D7E9B4E07D768A5A
+dir-source maatuska 49015F787433103580E3B66A1707A00E60F2D15B 171.25.193.9 171.25.193.9 443 80
+contact 4096R/23291265 Linus Nordberg <linus at nordberg.se>
+vote-digest A8839355BAC373320B8CEDD0A0A09DAAA1637E3A
+dir-source dannenberg 585769C78764D58426B8B52B6651A5A71137189A dannenberg.ccc.de 193.23.244.244 80 443
+contact Andreas Lehner <anonymizer at ccc.de>
+vote-digest 416B73C49E717B0A5D61A4F634DCCF94611802E7
+dir-source urras 80550987E1D626E3EBA5E5E75A458DE0626D088C 208.83.223.34 208.83.223.34 443 80
+contact 4096R/E012B42D Jacob Appelbaum <jacob at appelbaum.net>
+vote-digest 08B1F8E4910F136E7FB7DFD52ABB2A9EDE939F0B
+dir-source moria1 D586D18309DED4CD6D57C18FDB97EFA96D330566 128.31.0.34 128.31.0.34 9131 9101
+contact 1024D/28988BF5 arma mit edu
+vote-digest 5006931FB78F7AE42B602697591DBA82AACEF533
+dir-source dizum E8A9C45EDE6D711294FADF8E7951F4DE6CA56B58 194.109.206.212 194.109.206.212 80 443
+contact 1024R/8D56913D Alex de Joode <adejoode at sabotage.org>
+vote-digest 3F1F1E071EC5F54115CB8EA9723E30A9386AB8CA
+dir-source gabelmoo ED03BB616EB2F60BEC80151114BB25CEF515B226 212.112.245.170 212.112.245.170 80 443
+contact 4096R/C5AA446D Sebastian Hahn <tor at sebastianhahn.net>
+vote-digest DF2EC9AD207831DED1D01BB889A9C4478DE2CFB9
+r sumkledi ABPSI4nNUNC3hKPkBhyzHozozrU 8mCr8Sl7RF4ENU4jb0FZFA/3do8 2012-07-12 04:01:55 178.218.213.229 80 0
+s Exit Fast Named Running Valid
+v Tor 0.2.2.35
+w Bandwidth=38
+p accept 80,443
+r Unnamed AEXri4INxBAZeyi0wvJZoC58nZs 9t4U465paxGASh0x5Tds8a8YiKo 2012-07-12 04:48:09 79.139.135.90 443 22
+s Fast HSDir Running V2Dir Valid
+v Tor 0.2.2.37
+w Bandwidth=35
+p reject 1-65535
+r ANONIONROUTER AHhuQ8zFQJdT8l42Axxc6m6kNwI uI7+jQ/T3kFVnl7H7TYE/7WJxi4 2012-07-12 04:40:31 93.128.55.236 24051 24052
+s Fast Named Running V2Dir Valid
+v Tor 0.2.2.37
+w Bandwidth=108
+p reject 1-65535
+r ph3x AMLCoWrttR1eX7fWFo/GazQ9gi8 ZJJnmKK6+9B2KOUSIPV49+Vprxs 2012-07-11 19:44:22 86.59.119.83 443 80
+s Fast Guard HSDir Named Running Stable V2Dir Valid
+v Tor 0.2.3.18-rc
+w Bandwidth=55300
+p reject 1-65535
+r nargothrond ANi/r5RGhUxfZ3simlDXFrf2O68 DsP6REKOns/vAUYNp3rfkCOSJFM 2012-07-11 18:25:37 173.11.83.10 9001 0
+s Fast Guard Named Running Stable Valid
+v Tor 0.2.3.18-rc
+w Bandwidth=543
+p reject 1-65535
+r default AN1sc6ymJ4WcSJ95VITqL0B5wDQ I9HQ2zph5Nuvf4FKANoKDf5vPV8 2012-07-11 18:48:22 82.243.60.52 443 9030
+s Fast Running V2Dir Valid
+v Tor 0.2.2.35
+w Bandwidth=92
+p reject 1-65535
+r catfesh AOTNBUkB8Lob/wiz7h9gtuDoT2Q 0Ycp54MgG+Ns+oEd3BIubFJdGGw 2012-07-12 08:26:51 80.177.151.82 9001 9030
+s Fast HSDir Running V2Dir Valid
+v Tor 0.2.2.37
+w Bandwidth=61
+p reject 1-65535
+r 111111 AO70G2tKrAacjjIITtSmzAFrSs8 l/yzMBbM4crBHivPKh69pDXuTRM 2012-07-12 04:03:06 178.170.144.91 80 0
+s Exit Fast Named Running Valid
+v Tor 0.2.2.34
+w Bandwidth=29
+p accept 80,443
+r Unnamed AP1onm4+6g+gIQMs1u9r6CeLX80 HY8Ud5ffEX28pglH+Vqvfle1xDQ 2012-07-12 09:09:15 189.41.71.79 443 0
+s Running Valid
+v Tor 0.2.2.37
+w Bandwidth=0
+p reject 1-65535
+directory-footer
+bandwidth-weights Wbd=3335 Wbe=0 Wbg=3536 Wbm=10000 Wdb=10000 Web=10000 Wed=3329 Wee=10000 Weg=3329 Wem=10000 Wgb=10000 Wgd=3335 Wgg=6464 Wgm=6464 Wmb=10000 Wmd=3335 Wme=0 Wmg=3536 Wmm=10000
+directory-signature 14C131DFC5C6F93646BE72FA1401C02A8DF2E8B4 BF112F1C6D5543CFD0A32215ACABD4197B5279AD
+-----BEGIN SIGNATURE-----
+HFXB4497LzESysYJ/4jJY83E5vLjhv+igIxD9LU6lf6ftkGeF+lNmIAIEKaMts8H
+mfWcW0b+jsrXcJoCxV5IrwCDF3u1aC3diwZY6yiG186pwWbOwE41188XI2DeYPwE
+I/TJmV928na7RLZe2mGHCAW3VQOvV+QkCfj05VZ8CsY=
+-----END SIGNATURE-----
+directory-signature 27B6B5996C426270A5C95488AA5BCEB6BCC86956 D5C30C15BB3F1DA27669C2D88439939E8F418FCF
+-----BEGIN SIGNATURE-----
+VAL+VWqcJiJtTZjFDz5/rS4WLfh8dOSnU2HYUb1ZgqM8PR1rFsoxpvaK9USrtkx9
+Byctu/flD3YOqGg+GpYQwU8w9tm7BGelD+dqg97DkJXmlPaXe/Z0nKW1UnCN9m93
+svyWCAqglEzxlK4H7ZfMlQbkMu7EFjXGzrn1gRVGOwg=
+-----END SIGNATURE-----
+directory-signature 49015F787433103580E3B66A1707A00E60F2D15B 1C915B9493589F97BAC764D1885A34BFC18C7E26
+-----BEGIN SIGNATURE-----
+fHAC5vdqotMtTVIxfqoNrlob2jAi3PP/urvsVA0xmaOzgYtJFIjY2iEWrrU4fRwe
+0M1vyCw+oztBrPKYukedkefE9ly/R30KVW2ezo5WpOO4y6oZpelb/jRKFoSRfbyB
+WdKsHSe2xlXPA0ySu1klpuMOZiQ8wgxh4x3oLGXnL5Q=
+-----END SIGNATURE-----
+directory-signature 585769C78764D58426B8B52B6651A5A71137189A 499D7CE5A1356045D629F43271EBF600D6F2CC9C
+-----BEGIN SIGNATURE-----
+IDOUDykw+tdCyyVmPSGUDahIeEEPMWxarEoH2gPuyExDqZkUc0ah6Eh736rVSD5Z
+R4nCjDNTQNr5byDfJk6cMDN9A/5P8uz421pnmLfs9SasLUjTdJt921jxJnSvSBeF
+hSZPNi5wl++Uw3j2zeclOXvAkkAEGi9Pi5Zf6QNlWFI=
+-----END SIGNATURE-----
+directory-signature 80550987E1D626E3EBA5E5E75A458DE0626D088C 2B9B419BB44728A5BE01651D6D1553FD14B6CFFB
+-----BEGIN SIGNATURE-----
+D2wVGni7cYHyXNqt9RvW/CUd8r7TgkfEp9OAJKojUyweiGMJOMEqDBE01e4Ov9Pd
+O9D46fjxWYGE9fN72xvD8CGoNcQgTtLpvypEfB96tKM3JYr5j4MCsdcOrQBkKGp7
+qf1Qfiw7aXahk8IfbgvmAvROlAMAxln7nVE0qenQWu4=
+-----END SIGNATURE-----
+directory-signature D586D18309DED4CD6D57C18FDB97EFA96D330566 8F0DEA35D1732D867FB94F5130364927DBCCBB8E
+-----BEGIN SIGNATURE-----
+cmrV1VUfCo9Smlc8BUblkBuSFqJdQkX/gd3ROOnpdRfogbsylq6xA7srCBGp1Z39
+To5Vk71AI0PIy031S6gKfOLgn9E5Bl0Agr60diFxjNn0ejR49MKJTjoDI+DmBlv4
+do+Bej+D8afl27LNH/QIYyzSkOl0eXSGtOEEuCQg/3A=
+-----END SIGNATURE-----
+directory-signature E8A9C45EDE6D711294FADF8E7951F4DE6CA56B58 9BE9A4CF520B6880EB42C5702BC521F03A6CF0FC
+-----BEGIN SIGNATURE-----
+UVXzEFkkjCpszLmoqQxAxSU83IS+fqrkIC4DCQZCEjRcXEvx3c56HUyTsew5WTFR
+XANCJn+V3DaxYLuL6L8xW7r9xOQNU970nGwocuJckxyDcLHloL8E226vIAn6mLmt
+a1Z6y8NzaQpv4fhdqhT7ETJo+chmf8bSX8qLLmaCIac=
+-----END SIGNATURE-----
+directory-signature ED03BB616EB2F60BEC80151114BB25CEF515B226 845CF1D0B370CA443A8579D18E7987E7E532F639
+-----BEGIN SIGNATURE-----
+DILsRCrtn6rDbNo3DF+L1/VVAd+V86PdZKg3Q9QooqVOGgU/7HrspV/K4lFbWcTT
+Zm+quRQfuKmB4xljwXpeRlABQR5eainlZBtrTFg056/dDrJqYXSwV/C391tAIDZs
+2TANs/4uLi94q6Ov+zE9zYUiF8jwnyXl/q/jKOYM8bE=
+-----END SIGNATURE-----
diff --git a/test/unit/descriptor/data/cached-consensus-v2 b/test/unit/descriptor/data/cached-consensus-v2
new file mode 100644
index 0000000..ad64d40
--- /dev/null
+++ b/test/unit/descriptor/data/cached-consensus-v2
@@ -0,0 +1,28 @@
+ at type network-status-2 1.0
+network-status-version 2
+dir-source 18.244.0.114 18.244.0.114 80
+fingerprint 719BE45DE224B607C53707D0E2143E2D423E74CF
+contact arma at mit dot edu
+published 2005-12-16 00:13:46
+dir-options Names Versions
+client-versions 0.0.9rc2,0.0.9rc3,0.0.9rc4-cvs,0.0.9rc4,0.0.9rc5-cvs,0.0.9rc5,0.0.9rc6-cvs,0.0.9rc6,0.0.9rc7-cvs,0.0.9rc7,0.0.9,0.0.9.1,0.0.9.2,0.0.9.3,0.0.9.4,0.0.9.5,0.0.9.6,0.0.9.7,0.0.9.8,0.0.9.9,0.0.9.10,0.1.0.0-alpha-cvs,0.1.0.1-rc,0.1.0.1-rc-cvs,0.1.0.2-rc,0.1.0.2-rc-cvs,0.1.0.3-rc,0.1.0.3-rc-cvs,0.1.0.4-rc,0.1.0.4-rc-cvs,0.1.0.5-rc,0.1.0.5-rc-cvs,0.1.0.6-rc,0.1.0.6-rc-cvs,0.1.0.7-rc,0.1.0.7-rc-cvs,0.1.0.8-rc,0.1.0.8-rc-cvs,0.1.0.9-rc,0.1.0.10,0.1.0.11,0.1.0.12,0.1.0.13,0.1.0.14,0.1.0.15,0.1.0.16,0.1.1.0-alpha-cvs,0.1.1.1-alpha,0.1.1.1-alpha-cvs,0.1.1.2-alpha,0.1.1.2-alpha-cvs,0.1.1.3-alpha,0.1.1.3-alpha-cvs,0.1.1.4-alpha,0.1.1.4-alpha-cvs,0.1.1.5-alpha,0.1.1.5-alpha-cvs,0.1.1.6-alpha,0.1.1.6-alpha-cvs,0.1.1.7-alpha,0.1.1.7-alpha-cvs,0.1.1.8-alpha,0.1.1.8-alpha-cvs,0.1.1.9-alpha,0.1.1.9-alpha-cvs,0.1.1.10-alpha,0.1.1.10-alpha-cvs
+server-versions 0.0.9rc2,0.0.9rc3,0.0.9rc4-cvs,0.0.9rc4,0.0.9rc5-cvs,0.0.9rc5,0.0.9rc6-cvs,0.0.9rc6,0.0.9rc7-cvs,0.0.9rc7,0.0.9,0.0.9.1,0.0.9.2,0.0.9.3,0.0.9.4,0.0.9.5,0.0.9.6,0.0.9.7,0.0.9.8,0.0.9.9,0.0.9.10,0.1.0.0-alpha-cvs,0.1.0.1-rc,0.1.0.1-rc-cvs,0.1.0.2-rc,0.1.0.2-rc-cvs,0.1.0.3-rc,0.1.0.3-rc-cvs,0.1.0.4-rc,0.1.0.4-rc-cvs,0.1.0.5-rc,0.1.0.5-rc-cvs,0.1.0.6-rc,0.1.0.6-rc-cvs,0.1.0.7-rc,0.1.0.7-rc-cvs,0.1.0.8-rc,0.1.0.8-rc-cvs,0.1.0.9-rc,0.1.0.10,0.1.0.11,0.1.0.12,0.1.0.13,0.1.0.14,0.1.0.15,0.1.0.16,0.1.1.0-alpha-cvs,0.1.1.1-alpha,0.1.1.1-alpha-cvs,0.1.1.2-alpha,0.1.1.2-alpha-cvs,0.1.1.3-alpha,0.1.1.3-alpha-cvs,0.1.1.4-alpha,0.1.1.4-alpha-cvs,0.1.1.5-alpha,0.1.1.5-alpha-cvs,0.1.1.6-alpha,0.1.1.6-alpha-cvs,0.1.1.7-alpha,0.1.1.7-alpha-cvs,0.1.1.8-alpha,0.1.1.8-alpha-cvs,0.1.1.9-alpha,0.1.1.9-alpha-cvs,0.1.1.10-alpha,0.1.1.10-alpha-cvs
+dir-signing-key
+-----BEGIN RSA PUBLIC KEY-----
+MIGJAoGBAOcrht/y5rkaahfX7sMe2qnpqoPibsjTSJaDvsUtaNP/Bq0MgNDGOR48
+rtwfqTRff275Edkp/UYw3G3vSgKCJr76/bqOHCmkiZrnPV1zxNfrK18gNw2Cxre0
+nTA+fD8JQqpPtb8b0SnG9kwy75eS//sRu7TErie2PzGMxrf9LH0LAgMBAAE=
+-----END RSA PUBLIC KEY-----
+
+r moria2 cZvkXeIktgfFNwfQ4hQ+LUI+dM8 t/Pwl1uHiJ3RKF/Vehsbthf2VDI 2005-12-15 06:57:18 18.244.0.114 443 80
+s Authority Fast Named Running Valid V2Dir
+r stnv CSi6RnBWxKaJ/uTvXXFIK2KJw9U ItGn7UGZvaftbEFu7NdpwY4fKlo 2005-12-15 16:24:42 84.16.236.173 9001 0
+s Named Valid
+r nggrplz CehYL/Dm+F4rjkHA3AucncRuaWg swLCsByU85jj7ziTlSawZR+CTdY 2005-12-15 23:25:50 194.109.109.109 9001 0
+s Fast Stable Running Valid
+directory-signature moria2
+-----BEGIN SIGNATURE-----
+2nXCxVje3wzn6HrIFRNMc0nc48AhMVpHZyPwRKGXkuYfTQG55uvwQDaFgJHud4RT
+27QhWltau3K1evhnzhKcpbTXwkVv1TBYJSzL6rEeAn8cQ7ZiCyqf4EJCaNcem3d2
+TpQQk3nNQF8z6UIvdlvP+DnJV4izWVkQEZgUZgIVM0E=
+-----END SIGNATURE-----
diff --git a/test/unit/descriptor/data/cached-microdescs b/test/unit/descriptor/data/cached-microdescs
new file mode 100644
index 0000000..bd85ed6
--- /dev/null
+++ b/test/unit/descriptor/data/cached-microdescs
@@ -0,0 +1,25 @@
+ at last-listed 2013-02-24 00:18:36
+onion-key
+-----BEGIN RSA PUBLIC KEY-----
+MIGJAoGBAMhPQtZPaxP3ukybV5LfofKQr20/ljpRk0e9IlGWWMSTkfVvBcHsa6IM
+H2KE6s4uuPHp7FqhakXAzJbODobnPHY8l1E4efyrqMQZXEQk2IMhgSNtG6YqUrVF
+CxdSKSSy0mmcBe2TOyQsahlGZ9Pudxfnrey7KcfqnArEOqNH09RpAgMBAAE=
+-----END RSA PUBLIC KEY-----
+ at last-listed 2013-02-24 00:18:37
+onion-key
+-----BEGIN RSA PUBLIC KEY-----
+MIGJAoGBALCOxZdpMI2WO496njSQ2M7b4IgAGATqpJmH3So7lXOa25sK6o7JipgP
+qQE83K/t/xsMIpxQ/hHkft3G78HkeXXFc9lVUzH0HmHwYEu0M+PMVULSkG36MfEl
+7WeSZzaG+Tlnh9OySAzVyTsv1ZJsTQFHH9V8wuM0GOMo9X8DFC+NAgMBAAE=
+-----END RSA PUBLIC KEY-----
+ntor-onion-key r5572HzD+PMPBbXlZwBhsm6YEbxnYgis8vhZ1jmdI2k=
+family $6141629FA0D15A6AEAEF3A1BEB76E64C767B3174
+ at last-listed 2013-02-24 00:18:36
+onion-key
+-----BEGIN RSA PUBLIC KEY-----
+MIGJAoGBAOWFQHxO+5kGuhwPUX5jB7wJCrTbSU0fZwolNV1t9UaDdjGDvIjIhdit
+y2sMbyd9K8lbQO7x9rQjNst5ZicuaSOs854XQddSjm++vMdjYbOcVMqnKGSztvpd
+w/1LVWFfhcBnsGi4JMGbmP+KUZG9A8kI9deSyJhfi35jA7UepiHHAgMBAAE=
+-----END RSA PUBLIC KEY-----
+a [2001:6b0:7:125::242]:9001
+p accept 80,443
diff --git a/test/unit/descriptor/data/cr_in_contact_line b/test/unit/descriptor/data/cr_in_contact_line
new file mode 100644
index 0000000..ca6636c
--- /dev/null
+++ b/test/unit/descriptor/data/cr_in_contact_line
@@ -0,0 +1,48 @@
+ at type server-descriptor 1.0
+router pogonip 75.5.248.48 9001 0 0
+platform Tor 0.1.2.17 on Darwin i386
+published 2007-09-03 10:15:53
+opt fingerprint 6DAB D62B C65D 4E6F E620 2931 57FC 7696 8DAB 9C9B
+uptime 5423
+bandwidth 98304 196608 0
+onion-key
+-----BEGIN RSA PUBLIC KEY-----
+MIGJAoGBAO6pORBI9rGRCPSb2lLwzl2630eFMlBXLgLtwQvYLY2s58cWpKjhI+ZF
+Q43+0e50pgC//JyS3Zac4O7MA5+BUfMvNOzKUdSIIGc6QjwST/larbDy14k9XalQ
++G8rdnKb7gDNLrHjxhw5tC0JSTSTXI08bNsFeN1MoY8sFBjRlyDxAgMBAAE=
+-----END RSA PUBLIC KEY-----
+signing-key
+-----BEGIN RSA PUBLIC KEY-----
+MIGJAoGBALL0zokdBKjVcZA1P0K2jWK7afkvmB2bpRAdx5x1KRPZtB0mRxyjvgwL
+Tp0z3jLwhB4gzYY4LmS8p5RBH53gv4GsNW1rPSr99q4wjWyEY5AfjOdJZtFo/nhP
+881leOraicfcXmqrgtfbXZ0Jj+G9Ko216yEv4vBN/UV87kZHb/4tAgMBAAE=
+-----END RSA PUBLIC KEY-----
+opt write-history 2007-09-03 10:01:27 (900 s) 471040,874496,354304,0,0,0,0,0,0,0,0,60416,1160192,2017280,3396608,1585152,5990400,5469184,10896384,20568064,23362560,43372544,36531200,43439104,47890432,54303744,38624256,40414208,26827776,0,0,0,0,50176,611328,3126272,2952192,33792,28672
+opt read-history 2007-09-03 10:01:27 (900 s) 6890496,2631680,2260992,0,0,0,0,0,0,0,0,1096704,1856512,1133568,1038336,1390592,3272704,2115584,6868992,13585408,13746176,30726144,25562112,28556288,36113408,35810304,25213952,30357504,13510656,0,0,0,0,964608,568320,966656,1991680,17408,140288
+opt hibernating 1
+contact jie1 at pacbell dot net -----BEGIN PGP PUBLIC KEY BLOCK-----Version: GnuPG v1 dot 4 dot 7 (Darwin)mQGiBEbb0rcRBADqBiUXsmtpJifh74irNnkHbhKMj8O4TqenaZYhdjLWouZsZd07mTQoP40G4zqOrVEOOcXpdSiRnHWJYfgTnkibNZrOZEZLn3H1ywpovEgESmoGEdAXoid3XuIYRpRnqoafbFg9sg+OofX/mJuntVuoIdHfkHYnHlcShfKYQX8x4wCgqVSLVrjINDUy9YRznpj+TeoflG8EALoM4xHmIAhSKhviZr6pd86OAYF5bB8iY+koiQtQ066Upypj+dUMKeYWKkWp3loY5KwtkA0rBd2+nhf/PRJyn387mfSiZ91NZ+Pph29dERida1naMWOgv+XZnqDH0bk9BD12QqM/btawGiyOf3fdtLLseNzuiHr9gO2NjKxR5jjgBACop3bFpx2nfELj/NzS/umtUaQJ139BI0BJOHrVeSffMAcuH3v5B3WJVJ5vJb5kvvD9L7fIZNkAicpLhMUG4Lz6zNXMNGSUlCAOvHoQzBUMz6EVE7Ip73qX9viuLARFMf25qi+SsZJB18l3t/I+UgW+VTimZZgoHmVtX0nVZL+yvbQWSklFIDxqaWUxQHBhY2JlbGwubmV0PohgBBMRAgAgBQJG29K3AhsjBgsJCAcDAgQVAggDBBYCAwECHgECF4AACgkQv5Dmts313gKv4ACcDggg0o/+ys4Djkbltvwoa9IGFoEAn1yjv8W1Kj5WkdrV8oZudjAq2zjNuQQNBEbb0rcQEAC6mKhgeRAQtJLI7JGEqjO6hOu5mq1+UYgpZnuQq8FxCkG2MQ0sqOjXcZPIS55wWBuUNT+G+6B+cACJHqKo+W44zXtEXaTBdHVEi135cBv037fNtH73fRK/D43QKHu+r9luxW//mMQ
 dLhODNFSiowY8ZlOtPEYzqBiJBz0Shr0vN4EM3pkQZh7Jnmntw0Gu/hQIQONIhBFKNYYg6Zuar8j7kTHZJvDnZh5N6C7B9pilZsAp6vuCF7CHeUx5hbyzubfUHqpqnMr2pP3gnSkC/tbxsWPxWhd723SfRZpbK9TFJg65n4Qr2rYF1ccGe5mpRXRWxMA4sNVmmUOPgEjsbjMgvjUBmiAdeU+xEnNGJPyamPnwvKl5MIR1Tn2BbsY+o9g6RqsK7F6ql+RP4ya1oblluOkZMbjxF8BB6Hbe0xQzp0mSKJxSJN4wY+9xDa9SKAn9MgeI23hA9B9iwhWeqz+YvLNJu82UfBPSrr8xfM5htvNHapeGaRc6QsLcyMLecZ9/Rwpjp8iO4BNFrXGErgpc5SwzZFzMbeu2ymTZQ5lXXGBDNjK0C90+Z15MuUIyXswVeCP08iWgZCbPv6Y2zlVoYEuyG1BKUAkig3vzcG7iVp0HT1J191KwIPfdAS6DnXcSsb39mfR9/47zYlLsePrbHyrA/p9z4mBDHpsqlKqUTwADBQ//a500OJpnrVhjvXqJy6/6WI4zKKq56p0KDmGitKWlexEZuolfeJXxg2F4Y997XkOCTZjRlJEeoAQ1n4S5Vs54dlSS0Fsf1DOPflP5zmpEr3nV6MFUkNpHUINvgxYhTPBxNq6k7DRXklBZu5aq8QbNzgbTlnS5tANMcYjlRGrKqjwmZb7gmO3EiMp5tH8L71fA02zAyfVStpjsWBEoISrZHyoWkOP6v+JAIhLss9lSCqRT1EPdX/Zhn+bsWjfvPig+TOIAlbEVHtLREIVvjm3Okjt4hTrSGXeff6naeuGDPSOkDfFCiUTNjqP6uUmQMMR3F0HdycOuHCp/V4X/3cwKMqSoVBC278AOPptNijwBkR+1tGMH6g3+s8VO4EsuOYpnfTFhbSPuEGBzppD56Ezu9daWm2hcxqoKNmCBx+HYfOmW
 PXB8R8JRiftEPxpCGnb6qCAzrfIpjjBHtwASIQ4IbJds7mjobiBQGC93mOt5sUNkplixY77kv8h9ZYgBITJryo719IPOTG6nXCcPfke5Jp1uAx7pYpqTZqWfrio0TCT0q6tP7+3ZGecFDN6xV3JQ4hICkTW4uOtfV3803NGM8bmZCV65EWRlzY7gOLM+Wat2G31soA5M7cAe/Q1ro7yqBDzAIAA3DFT58lRJY/BCXkJPDJ2ABFl/O3OHUD++pReISQQYEQIACQUCRtvStwIbDAAKCRC/kOa2zfXeAl9/AJ4lCZTgLIAwfh2Kgg8RowxlGrO+5ACfagQ9rlfx2oxCWijYwpYFRk3NhCY==Xaw3-----END PGP PUBLIC KEY BLOCK-----
+reject 0.0.0.0/8:*
+reject 169.254.0.0/16:*
+reject 127.0.0.0/8:*
+reject 192.168.0.0/16:*
+reject 10.0.0.0/8:*
+reject 172.16.0.0/12:*
+reject *:25
+reject *:119
+reject *:135-139
+reject *:445
+reject *:465
+reject *:563
+reject *:587
+reject *:1214
+reject *:4661-4666
+reject *:6346-6429
+reject *:6699
+reject *:6881-6999
+accept *:*
+router-signature
+-----BEGIN SIGNATURE-----
+PrL8cocQcTTmQp0wy0dxlFOczGv8s2/hZzAafOhjdM0QqLMLh9FW/4RqH0jcDz+P
+t1QU3xIF3JV1fsW0rx4vFYKXG7WrLrrQp3gGuJvrXO+132UAsrs7eJKk3yxEi2lq
+FNY26XGZqTDPY4WPl08/JgXXfckgFFg76E4Jcl5/tY8=
+-----END SIGNATURE-----
diff --git a/test/unit/descriptor/data/descriptor_archive.tar b/test/unit/descriptor/data/descriptor_archive.tar
new file mode 100644
index 0000000..55d29d1
Binary files /dev/null and b/test/unit/descriptor/data/descriptor_archive.tar differ
diff --git a/test/unit/descriptor/data/descriptor_archive.tar.bz2 b/test/unit/descriptor/data/descriptor_archive.tar.bz2
new file mode 100644
index 0000000..2950c57
Binary files /dev/null and b/test/unit/descriptor/data/descriptor_archive.tar.bz2 differ
diff --git a/test/unit/descriptor/data/descriptor_archive.tar.gz b/test/unit/descriptor/data/descriptor_archive.tar.gz
new file mode 100644
index 0000000..325adb7
Binary files /dev/null and b/test/unit/descriptor/data/descriptor_archive.tar.gz differ
diff --git a/test/unit/descriptor/data/example_descriptor b/test/unit/descriptor/data/example_descriptor
new file mode 100644
index 0000000..6ee442c
--- /dev/null
+++ b/test/unit/descriptor/data/example_descriptor
@@ -0,0 +1,31 @@
+ at type server-descriptor 1.0
+router caerSidi 71.35.133.197 9001 0 0
+platform Tor 0.2.1.30 on Linux x86_64
+opt protocols Link 1 2 Circuit 1
+published 2012-03-01 17:15:27
+opt fingerprint A756 9A83 B570 6AB1 B1A9 CB52 EFF7 D2D3 2E45 53EB
+uptime 588217
+bandwidth 153600 256000 104590
+opt extra-info-digest D225B728768D7EA4B5587C13A7A9D22EBBEE6E66
+onion-key
+-----BEGIN RSA PUBLIC KEY-----
+MIGJAoGBAJv5IIWQ+WDWYUdyA/0L8qbIkEVH/cwryZWoIaPAzINfrw1WfNZGtBmg
+skFtXhOHHqTRN4GPPrZsAIUOQGzQtGb66IQgT4tO/pj+P6QmSCCdTfhvGfgTCsC+
+WPi4Fl2qryzTb3QO5r5x7T8OsG2IBUET1bLQzmtbC560SYR49IvVAgMBAAE=
+-----END RSA PUBLIC KEY-----
+signing-key
+-----BEGIN RSA PUBLIC KEY-----
+MIGJAoGBAKwvOXyztVKnuYvpTKt+nS3XIKeO8dVungi8qGoeS+6gkR6lDtGfBTjd
+uE9UIkdAl9zi8/1Ic2wsUNHE9jiS0VgeupITGZY8YOyMJJ/xtV1cqgiWhq1dUYaq
+51TOtUogtAPgXPh4J+V8HbFFIcCzIh3qCO/xXo+DSHhv7SSif1VpAgMBAAE=
+-----END RSA PUBLIC KEY-----
+family $0CE3CFB1E9CC47B63EA8869813BF6FAB7D4540C1 $1FD187E8F69A9B74C9202DC16A25B9E7744AB9F6 $74FB5EFA6A46DE4060431D515DC9A790E6AD9A7C $77001D8DA9BF445B0F81AA427A675F570D222E6A $B6D83EC2D9E18B0A7A33428F8CFA9C536769E209 $D2F37F46182C23AB747787FD657E680B34EAF892 $E0BD57A11F00041A9789577C53A1B784473669E4 $E5E3E9A472EAF7BE9682B86E92305DB4C71048EF
+opt hidden-service-dir
+contact www.atagar.com/contact
+reject *:*
+router-signature
+-----BEGIN SIGNATURE-----
+dskLSPz8beUW7bzwDjR6EVNGpyoZde83Ejvau+5F2c6cGnlu91fiZN3suE88iE6e
+758b9ldq5eh5mapb8vuuV3uO+0Xsud7IEOqfxdkmk0GKnUX8ouru7DSIUzUL0zqq
+Qlx9HNCqCY877ztFRC624ja2ql6A2hBcuoYMbkHjcQ4=
+-----END SIGNATURE-----
diff --git a/test/unit/descriptor/data/extrainfo_bridge_descriptor b/test/unit/descriptor/data/extrainfo_bridge_descriptor
new file mode 100644
index 0000000..e78c354
--- /dev/null
+++ b/test/unit/descriptor/data/extrainfo_bridge_descriptor
@@ -0,0 +1,22 @@
+ at type bridge-extra-info 1.0
+extra-info ec2bridgereaac65a3 1EC248422B57D9C0BD751892FE787585407479A4
+published 2012-06-08 02:21:27
+write-history 2012-06-08 02:10:38 (900 s) 343040,991232,5649408,49548288,24411136,486400,465920,975872,930816,758784,5650432,1844224,4387840,7417856,789504,623616,7376896,3940352,10039296,6054912,2051072,462848,4260864,5298176,500736,717824,714752,1370112,760832,576512,1521664,3986432,724992,615424,7079936,20995072,17465344,2592768,1014784,3082240,11468800,2770944,5660672,7646208,3385344,5404672,1158144,7741440,3275776,5239808,5291008,2197504,6070272,4919296,1180672,2245632,2362368,1641472,6153216,4660224,9184256,1488896,9736192,768000,65536,176128,2268160,3023872,704512,905216,866304,2375680,3688448,5181440,5433344,5259264,8285184,9391104,7015424,7180288,3436544,3713024,4473856,10705920,10529792,3154944,1815552,3443712,4566016,2668544,2688000,2391040,3181568,2749440,3214336,8974336
+read-history 2012-06-08 02:10:38 (900 s) 337920,437248,3995648,48726016,24125440,479232,477184,1010688,988160,772096,6388736,776192,3093504,7187456,787456,657408,7360512,3588096,9319424,5211136,2034688,474112,4222976,5154816,490496,1878016,610304,757760,667648,593920,1484800,3638272,1307648,672768,6614016,19262464,14863360,2612224,525312,1494016,11119616,3160064,1986560,5619712,3290112,5457920,1145856,7060480,3262464,5570560,5933056,2245632,4548608,3614720,829440,1765376,2379776,2531328,4306944,4244480,9225216,1054720,9806848,880640,66560,192512,2135040,3038208,830464,804864,941056,2386944,3660800,4835328,6406144,5152768,8714240,9145344,6902784,6781952,3648512,3876864,5474304,9958400,10851328,3322880,2239488,4790272,4343808,2404352,2615296,1551360,3295232,2869248,2924544,4941824
+dirreq-write-history 2012-06-08 02:10:38 (900 s) 5120,664576,2419712,578560,3072,5120,5120,3072,11264,5120,54272,1130496,1381376,202752,47104,0,6144,1064960,716800,835584,2048,0,3072,128000,4096,1024,222208,604160,129024,7168,118784,353280,0,0,761856,2236416,2574336,3072,526336,2037760,214016,726016,3592192,2205696,134144,1024,86016,656384,8192,9216,402432,14336,1617920,1436672,445440,481280,7168,1024,1959936,442368,14336,544768,4096,1024,0,4096,1052672,78848,0,223232,51200,5120,5120,1191936,1024,579584,2048,231424,68608,1305600,38912,4096,8192,1433600,5120,4096,330752,574464,382976,225280,940032,1185792,6144,1024,374784,4173824
+dirreq-read-history 2012-06-08 02:10:38 (900 s) 0,71680,99328,25600,0,0,0,0,0,0,3072,45056,74752,13312,2048,0,0,69632,18432,39936,0,0,0,8192,0,0,16384,25600,9216,0,7168,8192,0,0,96256,92160,138240,0,18432,128000,0,81920,149504,113664,8192,0,11264,46080,2048,0,25600,0,75776,77824,0,6144,19456,0,58368,14336,0,21504,0,0,0,0,25600,6144,0,0,5120,4096,0,54272,0,23552,0,0,4096,64512,2048,0,0,34816,0,0,6144,8192,10240,0,31744,48128,0,0,10240,236544
+geoip-db-digest A27BE984989AB31C50D0861C7106B17A7EEC3756
+dirreq-stats-end 2012-06-07 06:33:46 (86400 s)
+dirreq-v3-ips 
+dirreq-v2-ips 
+dirreq-v3-reqs 
+dirreq-v2-reqs 
+dirreq-v3-resp ok=72,not-enough-sigs=0,unavailable=0,not-found=0,not-modified=0,busy=0
+dirreq-v2-resp ok=0,unavailable=0,not-found=0,not-modified=0,busy=0
+dirreq-v3-direct-dl complete=0,timeout=0,running=0
+dirreq-v2-direct-dl complete=0,timeout=0,running=0
+dirreq-v3-tunneled-dl complete=68,timeout=4,running=0,min=2626,d1=7795,d2=14369,q1=18695,d3=29117,d4=52562,md=70626,d6=102271,d7=164175,q3=181522,d8=271682,d9=563791,max=32136142
+dirreq-v2-tunneled-dl complete=0,timeout=0,running=0
+bridge-stats-end 2012-06-07 06:33:53 (86400 s)
+bridge-ips cn=16,ir=16,sy=16,us=16,??=8,bn=8,br=8,by=8,cu=8,dk=8,dz=8,es=8,lb=8,ph=8,sd=8,tw=8
+router-digest 00A2AECCEAD3FEE033CFE29893387143146728EC
diff --git a/test/unit/descriptor/data/extrainfo_relay_descriptor b/test/unit/descriptor/data/extrainfo_relay_descriptor
new file mode 100644
index 0000000..ecaabe0
--- /dev/null
+++ b/test/unit/descriptor/data/extrainfo_relay_descriptor
@@ -0,0 +1,13 @@
+ at type extra-info 1.0
+extra-info NINJA B2289C3EAB83ECD6EB916A2F481A02E6B76A0A48
+published 2012-05-05 17:03:50
+write-history 2012-05-05 17:02:45 (900 s) 1082368,19456,50176,272384,485376,1850368,1132544,1790976,2459648,4091904,6310912,13701120,3209216,3871744,7873536,5440512,7287808,10561536,9979904,11247616,11982848,7590912,10611712,20728832,38534144,6839296,3173376,16678912
+read-history 2012-05-05 17:02:45 (900 s) 3309568,9216,41984,27648,123904,2004992,364544,576512,1607680,3808256,4672512,12783616,2938880,2562048,7348224,3574784,6488064,10954752,9359360,4438016,6286336,6438912,4502528,10720256,38165504,1524736,2336768,8186880
+dirreq-write-history 2012-05-05 17:02:45 (900 s) 0,0,0,227328,349184,382976,738304,1171456,850944,657408,1675264,987136,702464,1335296,587776,1941504,893952,533504,695296,6828032,6326272,1287168,6310912,10085376,1048576,5372928,894976,8610816
+dirreq-read-history 2012-05-05 17:02:45 (900 s) 0,0,0,0,33792,27648,48128,46080,60416,51200,63488,64512,45056,27648,37888,48128,57344,34816,46080,50176,37888,51200,25600,33792,39936,32768,28672,30720
+router-signature
+-----BEGIN SIGNATURE-----
+K5FSywk7qvw/boA4DQcqkls6Ize5vcBYfhQ8JnOeRQC9+uDxbnpm3qaYN9jZ8myj
+k0d2aofcVbHr4fPQOSST0LXDrhFl5Fqo5um296zpJGvRUeO6S44U/EfJAGShtqWw
+7LZqklu+gVvhMKREpchVqlAwXkWR44VENm24Hs+mT3M=
+-----END SIGNATURE-----
diff --git a/test/unit/descriptor/data/metrics_cert b/test/unit/descriptor/data/metrics_cert
new file mode 100644
index 0000000..510bfd8
--- /dev/null
+++ b/test/unit/descriptor/data/metrics_cert
@@ -0,0 +1,34 @@
+ at type dir-key-certificate-3 1.0
+dir-key-certificate-version 3
+fingerprint 14C131DFC5C6F93646BE72FA1401C02A8DF2E8B4
+dir-key-published 2008-05-09 21:13:26
+dir-key-expires 2009-05-09 21:13:26
+dir-identity-key
+-----BEGIN RSA PUBLIC KEY-----
+MIIBigKCAYEA7cZXvDRxfjDYtr9/9UsQ852+6cmHMr8VVh8GkLwbq3RzqjkULwQ2
+R9mFvG4FnqMcMKXi62rYYA3fZL1afhT804cpvyp/D3dPM8QxW88fafFAgIFP4LiD
+0JYjnF8cva5qZ0nzlWnMXLb32IXSvsGSE2FRyAV0YN9a6k967LSgCfUnZ+IKMezW
+1vhL9YK4QIfsDowgtVsavg63GzGmA7JvZmn77+/J5wKz11vGr7Wttf8XABbH2taX
+O9j/KGBOX2OKhoF3mXfZSmUO2dV9NMwtkJ7zD///Ny6sfApWV6kVP4O9TdG3bAsl
++fHCoCKgF/jAAWzh6VckQTOPzQZaH5aMWfXrDlzFWg17MjonI+bBTD2Ex2pHczzJ
+bN7coDMRH2SuOXv8wFf27KdUxZ/GcrXSRGzlRLygxqlripUanjVGN2JvrVQVr0kz
+pjNjiZl2z8ZyZ5d4zQuBi074JPGgx62xAstP37v1mPw14sIWfLgY16ewYuS5bCxV
+lyS28jsPht9VAgMBAAE=
+-----END RSA PUBLIC KEY-----
+dir-signing-key
+-----BEGIN RSA PUBLIC KEY-----
+MIGJAoGBAOeE3Qr1Km97gTgiB3io0EU0fqHW2ESMXVHeQuNDtCWBa0XSCEG6gx4B
+ZkkHjfVWqGQ7TmmzjYP9L9uCgtoKfhSvJA2w9NUMtMl8sgZmF4lcGpXXvGY9a566
+Bn+3wP0lMhb/I8CPVPX+NWEjgl1noZxo1C59SO/iALGQOpxRYgmbAgMBAAE=
+-----END RSA PUBLIC KEY-----
+dir-key-certification
+-----BEGIN SIGNATURE-----
+asvWwaMq34OfHoWUhAwh4+JDOuEUZJVIHQnedOYfQH8asS2QvW3Ma93OhrwVOC6b
+FyKmTJmJsl0MJGiC7tcEOlL6knsKE4CsuIw/PEcu2Rnm+R9zWxQuMYiHvGQMoDxl
+giOhLLs4LlzAAJlbfbd3hjF4STVAtTwmxYuIjb1Mq/JfAsx/wH3TLXgVZwj32w9s
+zUd9KZwwLzFiiHpC+U7zh6+wRsZfo2tlpmcaP1dTSINgVbdzPJ/DOUlx9nwTCBsE
+AQpUx2DpAikwrpw0zDqpQvYulcQlNLWFN/y/PkmiK8mIJk0OBMiQA7JgqWamnnk4
+PwqaGv483LkBF+25JFGJmnUVve3RMc+s61+2kBcjfUMed4QaHkeCMHqlRqpfQVkk
+RY22NXCwrJvSMEwiy7acC8FGysqwHRyE356+Rw6TB43g3Tno9KaHEK7MHXjSHwNs
+GM9hAsAMRX9Ogqhq5UjDNqEsvDKuyVeyh7unSZEOip9Zr6K/+7VsVPNb8vfBRBjo
+-----END SIGNATURE-----
diff --git a/test/unit/descriptor/data/metrics_consensus b/test/unit/descriptor/data/metrics_consensus
new file mode 100644
index 0000000..22107bd
--- /dev/null
+++ b/test/unit/descriptor/data/metrics_consensus
@@ -0,0 +1,121 @@
+ at type network-status-consensus-3 1.0
+network-status-version 3
+vote-status consensus
+consensus-method 12
+valid-after 2012-07-12 10:00:00
+fresh-until 2012-07-12 11:00:00
+valid-until 2012-07-12 13:00:00
+voting-delay 300 300
+client-versions 0.2.2.35,0.2.2.36,0.2.2.37,0.2.3.10-alpha,0.2.3.11-alpha,0.2.3.12-alpha,0.2.3.13-alpha,0.2.3.14-alpha,0.2.3.15-alpha,0.2.3.16-alpha,0.2.3.17-beta,0.2.3.18-rc,0.2.3.19-rc
+server-versions 0.2.2.35,0.2.2.36,0.2.2.37,0.2.3.10-alpha,0.2.3.11-alpha,0.2.3.12-alpha,0.2.3.13-alpha,0.2.3.14-alpha,0.2.3.15-alpha,0.2.3.16-alpha,0.2.3.17-beta,0.2.3.18-rc,0.2.3.19-rc
+known-flags Authority BadExit Exit Fast Guard HSDir Named Running Stable Unnamed V2Dir Valid
+params CircuitPriorityHalflifeMsec=30000 bwauthpid=1
+dir-source tor26 14C131DFC5C6F93646BE72FA1401C02A8DF2E8B4 86.59.21.38 86.59.21.38 80 443
+contact Peter Palfrader
+vote-digest 0B6D1E9A300B895AA2D0B427F92917B6995C3C1C
+dir-source turtles 27B6B5996C426270A5C95488AA5BCEB6BCC86956 76.73.17.194 76.73.17.194 9030 9090
+contact Mike Perry <mikeperryTAfsckedTODorg>
+vote-digest 904B1974B9879D02B4ADFB81D7E9B4E07D768A5A
+dir-source maatuska 49015F787433103580E3B66A1707A00E60F2D15B 171.25.193.9 171.25.193.9 443 80
+contact 4096R/23291265 Linus Nordberg <linus at nordberg.se>
+vote-digest A8839355BAC373320B8CEDD0A0A09DAAA1637E3A
+dir-source dannenberg 585769C78764D58426B8B52B6651A5A71137189A dannenberg.ccc.de 193.23.244.244 80 443
+contact Andreas Lehner <anonymizer at ccc.de>
+vote-digest 416B73C49E717B0A5D61A4F634DCCF94611802E7
+dir-source urras 80550987E1D626E3EBA5E5E75A458DE0626D088C 208.83.223.34 208.83.223.34 443 80
+contact 4096R/E012B42D Jacob Appelbaum <jacob at appelbaum.net>
+vote-digest 08B1F8E4910F136E7FB7DFD52ABB2A9EDE939F0B
+dir-source moria1 D586D18309DED4CD6D57C18FDB97EFA96D330566 128.31.0.34 128.31.0.34 9131 9101
+contact 1024D/28988BF5 arma mit edu
+vote-digest 5006931FB78F7AE42B602697591DBA82AACEF533
+dir-source dizum E8A9C45EDE6D711294FADF8E7951F4DE6CA56B58 194.109.206.212 194.109.206.212 80 443
+contact 1024R/8D56913D Alex de Joode <adejoode at sabotage.org>
+vote-digest 3F1F1E071EC5F54115CB8EA9723E30A9386AB8CA
+dir-source gabelmoo ED03BB616EB2F60BEC80151114BB25CEF515B226 212.112.245.170 212.112.245.170 80 443
+contact 4096R/C5AA446D Sebastian Hahn <tor at sebastianhahn.net>
+vote-digest DF2EC9AD207831DED1D01BB889A9C4478DE2CFB9
+r sumkledi ABPSI4nNUNC3hKPkBhyzHozozrU 8mCr8Sl7RF4ENU4jb0FZFA/3do8 2012-07-12 04:01:55 178.218.213.229 80 0
+s Exit Fast Named Running Valid
+v Tor 0.2.2.35
+w Bandwidth=38
+p accept 80,443
+r Unnamed AEXri4INxBAZeyi0wvJZoC58nZs 9t4U465paxGASh0x5Tds8a8YiKo 2012-07-12 04:48:09 79.139.135.90 443 22
+s Fast HSDir Running V2Dir Valid
+v Tor 0.2.2.37
+w Bandwidth=35
+p reject 1-65535
+r ANONIONROUTER AHhuQ8zFQJdT8l42Axxc6m6kNwI uI7+jQ/T3kFVnl7H7TYE/7WJxi4 2012-07-12 04:40:31 93.128.55.236 24051 24052
+s Fast Named Running V2Dir Valid
+v Tor 0.2.2.37
+w Bandwidth=108
+p reject 1-65535
+r ph3x AMLCoWrttR1eX7fWFo/GazQ9gi8 ZJJnmKK6+9B2KOUSIPV49+Vprxs 2012-07-11 19:44:22 86.59.119.83 443 80
+s Fast Guard HSDir Named Running Stable V2Dir Valid
+v Tor 0.2.3.18-rc
+w Bandwidth=55300
+p reject 1-65535
+r nargothrond ANi/r5RGhUxfZ3simlDXFrf2O68 DsP6REKOns/vAUYNp3rfkCOSJFM 2012-07-11 18:25:37 173.11.83.10 9001 0
+s Fast Guard Named Running Stable Valid
+v Tor 0.2.3.18-rc
+w Bandwidth=543
+p reject 1-65535
+r default AN1sc6ymJ4WcSJ95VITqL0B5wDQ I9HQ2zph5Nuvf4FKANoKDf5vPV8 2012-07-11 18:48:22 82.243.60.52 443 9030
+s Fast Running V2Dir Valid
+v Tor 0.2.2.35
+w Bandwidth=92
+p reject 1-65535
+r catfesh AOTNBUkB8Lob/wiz7h9gtuDoT2Q 0Ycp54MgG+Ns+oEd3BIubFJdGGw 2012-07-12 08:26:51 80.177.151.82 9001 9030
+s Fast HSDir Running V2Dir Valid
+v Tor 0.2.2.37
+w Bandwidth=61
+p reject 1-65535
+directory-footer
+bandwidth-weights Wbd=3335 Wbe=0 Wbg=3536 Wbm=10000 Wdb=10000 Web=10000 Wed=3329 Wee=10000 Weg=3329 Wem=10000 Wgb=10000 Wgd=3335 Wgg=6464 Wgm=6464 Wmb=10000 Wmd=3335 Wme=0 Wmg=3536 Wmm=10000
+directory-signature 14C131DFC5C6F93646BE72FA1401C02A8DF2E8B4 BF112F1C6D5543CFD0A32215ACABD4197B5279AD
+-----BEGIN SIGNATURE-----
+HFXB4497LzESysYJ/4jJY83E5vLjhv+igIxD9LU6lf6ftkGeF+lNmIAIEKaMts8H
+mfWcW0b+jsrXcJoCxV5IrwCDF3u1aC3diwZY6yiG186pwWbOwE41188XI2DeYPwE
+I/TJmV928na7RLZe2mGHCAW3VQOvV+QkCfj05VZ8CsY=
+-----END SIGNATURE-----
+directory-signature 27B6B5996C426270A5C95488AA5BCEB6BCC86956 D5C30C15BB3F1DA27669C2D88439939E8F418FCF
+-----BEGIN SIGNATURE-----
+VAL+VWqcJiJtTZjFDz5/rS4WLfh8dOSnU2HYUb1ZgqM8PR1rFsoxpvaK9USrtkx9
+Byctu/flD3YOqGg+GpYQwU8w9tm7BGelD+dqg97DkJXmlPaXe/Z0nKW1UnCN9m93
+svyWCAqglEzxlK4H7ZfMlQbkMu7EFjXGzrn1gRVGOwg=
+-----END SIGNATURE-----
+directory-signature 49015F787433103580E3B66A1707A00E60F2D15B 1C915B9493589F97BAC764D1885A34BFC18C7E26
+-----BEGIN SIGNATURE-----
+fHAC5vdqotMtTVIxfqoNrlob2jAi3PP/urvsVA0xmaOzgYtJFIjY2iEWrrU4fRwe
+0M1vyCw+oztBrPKYukedkefE9ly/R30KVW2ezo5WpOO4y6oZpelb/jRKFoSRfbyB
+WdKsHSe2xlXPA0ySu1klpuMOZiQ8wgxh4x3oLGXnL5Q=
+-----END SIGNATURE-----
+directory-signature 585769C78764D58426B8B52B6651A5A71137189A 499D7CE5A1356045D629F43271EBF600D6F2CC9C
+-----BEGIN SIGNATURE-----
+IDOUDykw+tdCyyVmPSGUDahIeEEPMWxarEoH2gPuyExDqZkUc0ah6Eh736rVSD5Z
+R4nCjDNTQNr5byDfJk6cMDN9A/5P8uz421pnmLfs9SasLUjTdJt921jxJnSvSBeF
+hSZPNi5wl++Uw3j2zeclOXvAkkAEGi9Pi5Zf6QNlWFI=
+-----END SIGNATURE-----
+directory-signature 80550987E1D626E3EBA5E5E75A458DE0626D088C 2B9B419BB44728A5BE01651D6D1553FD14B6CFFB
+-----BEGIN SIGNATURE-----
+D2wVGni7cYHyXNqt9RvW/CUd8r7TgkfEp9OAJKojUyweiGMJOMEqDBE01e4Ov9Pd
+O9D46fjxWYGE9fN72xvD8CGoNcQgTtLpvypEfB96tKM3JYr5j4MCsdcOrQBkKGp7
+qf1Qfiw7aXahk8IfbgvmAvROlAMAxln7nVE0qenQWu4=
+-----END SIGNATURE-----
+directory-signature D586D18309DED4CD6D57C18FDB97EFA96D330566 8F0DEA35D1732D867FB94F5130364927DBCCBB8E
+-----BEGIN SIGNATURE-----
+cmrV1VUfCo9Smlc8BUblkBuSFqJdQkX/gd3ROOnpdRfogbsylq6xA7srCBGp1Z39
+To5Vk71AI0PIy031S6gKfOLgn9E5Bl0Agr60diFxjNn0ejR49MKJTjoDI+DmBlv4
+do+Bej+D8afl27LNH/QIYyzSkOl0eXSGtOEEuCQg/3A=
+-----END SIGNATURE-----
+directory-signature E8A9C45EDE6D711294FADF8E7951F4DE6CA56B58 9BE9A4CF520B6880EB42C5702BC521F03A6CF0FC
+-----BEGIN SIGNATURE-----
+UVXzEFkkjCpszLmoqQxAxSU83IS+fqrkIC4DCQZCEjRcXEvx3c56HUyTsew5WTFR
+XANCJn+V3DaxYLuL6L8xW7r9xOQNU970nGwocuJckxyDcLHloL8E226vIAn6mLmt
+a1Z6y8NzaQpv4fhdqhT7ETJo+chmf8bSX8qLLmaCIac=
+-----END SIGNATURE-----
+directory-signature ED03BB616EB2F60BEC80151114BB25CEF515B226 845CF1D0B370CA443A8579D18E7987E7E532F639
+-----BEGIN SIGNATURE-----
+DILsRCrtn6rDbNo3DF+L1/VVAd+V86PdZKg3Q9QooqVOGgU/7HrspV/K4lFbWcTT
+Zm+quRQfuKmB4xljwXpeRlABQR5eainlZBtrTFg056/dDrJqYXSwV/C391tAIDZs
+2TANs/4uLi94q6Ov+zE9zYUiF8jwnyXl/q/jKOYM8bE=
+-----END SIGNATURE-----
diff --git a/test/unit/descriptor/data/metrics_server_desc_multiple b/test/unit/descriptor/data/metrics_server_desc_multiple
new file mode 100644
index 0000000..aa3d044
--- /dev/null
+++ b/test/unit/descriptor/data/metrics_server_desc_multiple
@@ -0,0 +1,87 @@
+ at type server-descriptor 1.0
+router anonion 31.54.58.167 443 0 0
+platform Tor 0.2.3.22-rc on Windows XP
+opt protocols Link 1 2 Circuit 1
+published 2012-09-17 07:28:01
+opt fingerprint 9A5E C5BB 8665 17E5 3962 AF4D 3E77 6536 694B 069E
+uptime 0
+bandwidth 8388608 8388608 442368
+opt extra-info-digest 0DED759EDA005BFD735E91D3E2DBF22D2D685F12
+onion-key
+-----BEGIN RSA PUBLIC KEY-----
+MIGJAoGBAM+vu//ysOgcbCwri0RnunH8xNwswnTPbbusad7im2lXJuPjMbTFlkcn
+3yPxbL1aD3p/PHPfLKOOPcFDVlCma/N8tJ6CEeJn6pZcrvgsU/d1EASQyrhpXFgL
+oH/+zfg4ir52dZp4gS03P16i9m4HB4YTewo62cIuZoZKq1smReMzAgMBAAE=
+-----END RSA PUBLIC KEY-----
+signing-key
+-----BEGIN RSA PUBLIC KEY-----
+MIGJAoGBALSrtq1TqL5TImtctKm/5CvZHb54n5JpkmD8jtmdajgpHF0eaSaaw/Qw
+IKaoQn17etuGzbeXF1vgphv2T4xyIm04MiLOszbLGXK1Fsa5uAO+qs1bIy6mLpwg
+XrYhyuddYyw1s4U5ZNBNK/0rlwo7hoIZjxcej0AvTGewFx+Rv6UlAgMBAAE=
+-----END RSA PUBLIC KEY-----
+opt hidden-service-dir
+contact anonion at nym dot hush dot com
+reject 0.0.0.0/8:*
+reject 169.254.0.0/16:*
+reject 127.0.0.0/8:*
+reject 192.168.0.0/16:*
+reject 10.0.0.0/8:*
+reject 172.16.0.0/12:*
+reject 31.54.58.167:*
+accept *:53
+accept *:80
+accept *:443
+accept *:3128
+accept *:8080
+accept *:8000
+accept *:50
+accept *:51
+accept *:389
+accept *:636
+accept *:2123
+accept *:2152
+accept *:11370
+accept *:11371
+accept *:2301
+accept *:2381
+accept *:6518
+accept *:9100
+accept *:1433
+accept *:1434
+accept *:3389
+accept *:5901
+reject *:*
+router-signature
+-----BEGIN SIGNATURE-----
+XzcWkD6QV6CHtcdKuM6mVGGl+m9JsmLtYhpPMxRANxbRLMLaI1+XkO58QS0zvl6f
+0vhmifJhJSu+FV+mOkg/aCsPyleSMd8vLOjCzhVv0yCzcn252ujsjGc0HrmyPE1R
+p2bQE2u2SpgLKw6oylyr6qLcZkGIWDYhnnyZTg2lXm8=
+-----END SIGNATURE-----
+router Unnamed 122.60.235.157 9001 0 0
+platform Tor 0.2.2.37 (git-fce6eb1c44e87bc2) on Darwin x86_64
+opt protocols Link 1 2 Circuit 1
+published 2012-09-17 14:57:28
+opt fingerprint 5366 F1D1 9875 9F88 94EA 6E5F F768 C667 F59A FD24
+uptime 542717
+bandwidth 32768 65536 58012
+opt extra-info-digest 33C8C462BFA5A9C6E825A0A717BD63072AB68E59
+onion-key
+-----BEGIN RSA PUBLIC KEY-----
+MIGJAoGBAKCt4oloDEzaHKzuljXGIJujbTbQfl/XZPjc/84eWp8Ka2Vmpa9DL3cV
++bO3wttgA6ZtkGUB6d6AHcfOoRT7tP/wBeQSHSDxh9OmKzGKZiHB76HNxVny8aJd
+ngVPgMmolXajdnzCIAIRLdUW6SHvIinyeghLuVSwLZU+eEN5XSXrAgMBAAE=
+-----END RSA PUBLIC KEY-----
+signing-key
+-----BEGIN RSA PUBLIC KEY-----
+MIGJAoGBAJ5avJflNjXZjGukC0aACUeTMHsQEHsQKCFPwXFVBCX6gDmrfivqT+EB
+efT8XTktk44EFJbCAWj65kuj+aoXBfUvrr21at4WAEJ33BgmitwCrzwkKWVX/wLM
++NMIGObvs88HYyaTsoyZ/TToLE5nPQJj77bNyabHgNvYEXyFxooJAgMBAAE=
+-----END RSA PUBLIC KEY-----
+opt hidden-service-dir
+reject *:*
+router-signature
+-----BEGIN SIGNATURE-----
+m0Q/dr4HvyGShKG8DdKyabcTO4lZtGMXNGX+TEM1D43+5mw4D5UgiIdaK9a2tMQA
+sBGFCymv3ZHpmPRtOhSMRafwcZDYXeCSdvZXmfGzpyIRrBde3uRIJj97eqA+oOHd
+5TlnK2Z0tF+M1bUD0xYHOde5a/lAaiQaw5+mley6i6M=
+-----END SIGNATURE-----
diff --git a/test/unit/descriptor/data/metrics_vote b/test/unit/descriptor/data/metrics_vote
new file mode 100644
index 0000000..f5c9666
--- /dev/null
+++ b/test/unit/descriptor/data/metrics_vote
@@ -0,0 +1,85 @@
+ at type network-status-vote-3 1.0
+network-status-version 3
+vote-status vote
+consensus-methods 1 2 3 4 5 6 7 8 9 10 11 12
+published 2012-07-11 23:50:01
+valid-after 2012-07-12 00:00:00
+fresh-until 2012-07-12 01:00:00
+valid-until 2012-07-12 03:00:00
+voting-delay 300 300
+client-versions 0.2.2.35,0.2.2.36,0.2.2.37,0.2.3.10-alpha,0.2.3.11-alpha,0.2.3.12-alpha,0.2.3.13-alpha,0.2.3.14-alpha,0.2.3.15-alpha,0.2.3.16-alpha,0.2.3.17-beta,0.2.3.18-rc,0.2.3.19-rc
+server-versions 0.2.2.35,0.2.2.36,0.2.2.37,0.2.3.10-alpha,0.2.3.11-alpha,0.2.3.12-alpha,0.2.3.13-alpha,0.2.3.14-alpha,0.2.3.15-alpha,0.2.3.16-alpha,0.2.3.17-beta,0.2.3.18-rc,0.2.3.19-rc
+known-flags Authority BadExit Exit Fast Guard HSDir Named Running Stable Unnamed V2Dir Valid
+params CircuitPriorityHalflifeMsec=30000 bwauthpid=1
+dir-source tor26 14C131DFC5C6F93646BE72FA1401C02A8DF2E8B4 86.59.21.38 86.59.21.38 80 443
+contact Peter Palfrader
+dir-key-certificate-version 3
+fingerprint 14C131DFC5C6F93646BE72FA1401C02A8DF2E8B4
+dir-key-published 2012-04-29 21:21:25
+dir-key-expires 2013-05-29 21:21:25
+dir-identity-key
+-----BEGIN RSA PUBLIC KEY-----
+MIIBigKCAYEA7cZXvDRxfjDYtr9/9UsQ852+6cmHMr8VVh8GkLwbq3RzqjkULwQ2
+R9mFvG4FnqMcMKXi62rYYA3fZL1afhT804cpvyp/D3dPM8QxW88fafFAgIFP4LiD
+0JYjnF8cva5qZ0nzlWnMXLb32IXSvsGSE2FRyAV0YN9a6k967LSgCfUnZ+IKMezW
+1vhL9YK4QIfsDowgtVsavg63GzGmA7JvZmn77+/J5wKz11vGr7Wttf8XABbH2taX
+O9j/KGBOX2OKhoF3mXfZSmUO2dV9NMwtkJ7zD///Ny6sfApWV6kVP4O9TdG3bAsl
++fHCoCKgF/jAAWzh6VckQTOPzQZaH5aMWfXrDlzFWg17MjonI+bBTD2Ex2pHczzJ
+bN7coDMRH2SuOXv8wFf27KdUxZ/GcrXSRGzlRLygxqlripUanjVGN2JvrVQVr0kz
+pjNjiZl2z8ZyZ5d4zQuBi074JPGgx62xAstP37v1mPw14sIWfLgY16ewYuS5bCxV
+lyS28jsPht9VAgMBAAE=
+-----END RSA PUBLIC KEY-----
+dir-signing-key
+-----BEGIN RSA PUBLIC KEY-----
+MIGJAoGBANROlyEQEWJ20RC+DogV0Ww8mrUw9viscZ7eqAkyhDZKIt++2kVXszUI
+Zd2NoWG025f3np4zlTuIkz2PShn1yAYmKBlRcwbexgR6dXBToL3Zxe1Mezig/UuL
+kX75imfneBiLdtCRzaZs/Fa5cwd3msUNPVsq5Zmk0R7rCmBqiLDbAgMBAAE=
+-----END RSA PUBLIC KEY-----
+dir-key-crosscert
+-----BEGIN ID SIGNATURE-----
+UuK7wNT0weVelnsMYgoj2apYzr5pJqaI7pM+IzWiSGq1NipqMDEwZqx0WVWR/izB
+kLQm1mxU0+84QxAtNNmwNo5uHXR1h2fZJQ2yHf8JLIjIEsaOeOfvDLkn9IyHwmIA
+OBPgHmAetivVUYc8lqejBO5l4xg8BpG7KK16yjTDflc=
+-----END ID SIGNATURE-----
+dir-key-certification
+-----BEGIN SIGNATURE-----
+t5PLDPgT2TJiM8fNmqXkXY0rK6u5LZEtMNqQ4BCQCrvUN87BPBpF4U0GptqVjmdK
+aKS0v6BMRBbSQhyro42jv8iaP8X57V39rJAEbQsWJVNnhBmgLPxY9LYS/he8dUPZ
+0LuNjNuBaekvUoPr6iunHGYUjVMZa3aOf+SeUxdS4ZudlozegDyBrkMeEVH/7DC/
+dx/VsqR5hIZ8/e+u3XQcSIYCmBbJxPNeOWNERi4npbyY0Iy2AJ1eJyX2onUsk2Qq
+jnMxPtuCSDAWEebChwtKTTV6EAnZzYcCZLxNLXcd4Tvfm2EgSw8xKtiDn+eABpcv
+7FaKEkuUBGLe+sN3jt3K05qOAOvVtCi0hSO/94+DO7PiXFxmb6Om6vEbKiGxagH2
+3jE6rNpJJsO0TspO3KTD7fpXJU7gtXIurcQwziXUGchK84aNFjdX/G1ra5M5kMHf
+hJt6p7rCqPyEVOwW+DIGVfucLfjtJu/h7XuWw9/htrE0TNx0Hah8V2+7Ok5XA10N
+-----END SIGNATURE-----
+r sumkledi ABPSI4nNUNC3hKPkBhyzHozozrU B5n4BiALAF8B5AqafxohyYiuj7E 2012-07-11 04:22:53 178.218.213.229 80 0
+s Exit Named Valid
+opt v Tor 0.2.2.35
+w Bandwidth=51
+p accept 80,443
+m 8,9,10,11,12 sha256=g1vx9si329muxV3tquWIXXySNOIwRGMeAESKs/v4DWs
+r Unnamed AEXri4INxBAZeyi0wvJZoC58nZs csTseFzwBV0RLsEdlyxDw6jTZ1o 2012-07-11 10:47:26 79.139.135.90 443 22
+s Fast HSDir Running V2Dir Valid
+opt v Tor 0.2.2.37
+w Bandwidth=92
+p reject 1-65535
+m 8,9,10,11,12 sha256=iZfmvEGdBG/qc9KYZUh4JsWEHbn9Bw2jy5i0AtCoRB0
+r default AFU4pMWKyq+eTPuBO0D9W179kQQ OZzEaq9N5+VX5yxzi051zqyEcPk 2012-07-11 08:17:21 2.90.115.167 443 9030
+s V2Dir Valid
+opt v Tor 0.2.3.15-alpha
+w Bandwidth=53
+p reject 1-65535
+m 8,9,10,11,12 sha256=m/RouK3TqoaYBBHCxgmzMPuGGCIEr0ufSTB1i85zicM
+r satoshi11 AFzRyEo3Ibdko7vl4rReoU3BDfM rWaYhhU9Kym4sXAVe+uE99VGpJs 2012-07-11 08:10:01 80.218.153.44 443 9030
+s V2Dir Valid
+opt v Tor 0.2.2.35
+w Bandwidth=196
+p reject 1-65535
+m 8,9,10,11,12 sha256=yxuOPz7IraIzzmU/qeZRJHOJrutNmvPwDwj2AmsHZSI
+directory-footer
+directory-signature 14C131DFC5C6F93646BE72FA1401C02A8DF2E8B4 BF112F1C6D5543CFD0A32215ACABD4197B5279AD
+-----BEGIN SIGNATURE-----
+02UCTf06pLYkuLgUSR+L0vHOJ7gUyidKscg88q8TWUAiP57x27o+BlZdEMSu/ArT
+yBOz+0ZOWggb04maA2cwJQQEK2LkzxzBijmfoC6RhQx3wS8llhyOMaoR6rkobE1D
+oLqVZPDNPX87DkhmQfgBM2I3vYQX13YJwZc0jNCEGpU=
+-----END SIGNATURE-----
diff --git a/test/unit/descriptor/data/negative_uptime b/test/unit/descriptor/data/negative_uptime
new file mode 100644
index 0000000..388d44b
--- /dev/null
+++ b/test/unit/descriptor/data/negative_uptime
@@ -0,0 +1,46 @@
+ at type server-descriptor 1.0
+router TipTor 62.99.247.83 9001 0 9030
+platform Tor 0.1.1.25 on Darwin Power Macintosh
+published 2006-12-18 22:42:40
+opt fingerprint 1379 62D4 931D BF08 A24E 8432 88B8 A155 D6D2 AEDD
+uptime -31081285
+bandwidth 102400 153600 124131
+onion-key
+-----BEGIN RSA PUBLIC KEY-----
+MIGJAoGBAOAG0OI7xLpz3gAkJoJdRzKYOvOZ7PL5dQQZJGw+pXVUnXfchwTXLm5o
+rAoYolziFjUU+EDaDXCHq1MrQBNbT1kQHqv+OcVjTwuameNVOCgPTIDOxiXfxxoO
+zDAEQ91gzcDIBZdV9V64KV8Oz4+NYK3zrGDkIg00pAQjNuwvmZqlAgMBAAE=
+-----END RSA PUBLIC KEY-----
+signing-key
+-----BEGIN RSA PUBLIC KEY-----
+MIGJAoGBAKjCkU3eAwbW9ZU42B1l+Itqy4yKoQpRjt6N42mfNFfKZ8Ywd789/jiK
+J31QYBhW3JBn1ZFkRwpfKYlbOm0XS2CjlqHuT0Ycp9iphtCIHADIM+3shaSKAIwA
+8/wQ2GN9zYiTneUNWRJN5sDja+oU7XfkRI46cpV1Y1lVJyEoKQWpAgMBAAE=
+-----END RSA PUBLIC KEY-----
+opt write-history 2006-12-18 22:36:20 (900 s) 85896196,91917257,89966503,91506428,91213104,88921400,91039183,91716023,91147326,86173405,88624130,87211145,90976203,91765274,91904016,92475836,91436900,91811938,86244782,91228827,91939844,89829330,89107239,89019495,88925780,91247327,90406742,91088221,91065780,91770795,91408136,89981571,88663478,91555300,91664187,91916304,92079918,87826691,87836934,91162457,91904383,91508890,90270067,91314943,91856008,84648838,86133485,81010341,78078954,85039584,91951911,91327645,92042781,87120087,76812012,87759151,91627062,91881873,91965911,91523086,86332304,86299595,90915708,89321373,91093137,91654532,91836129,91819440,92049427,92014514,89481483,91098381,88852717,91601581,89729803,91256453,89604862,91881660,91942891,91769572,91896895,92224718,91431715,91762404,91709273,92046002,91866341,91561459,91673622,91700186,91659511,92402879,90926431,87060230,91076389,89573325
+opt read-history 2006-12-18 22:36:20 (900 s) 69091643,77959428,74322746,75132861,74637737,68249949,73224421,72744339,73893094,69049631,71480325,68334060,72775510,79560093,78273066,75799097,76810596,78186192,67593073,75998559,77654399,70612520,74383546,74734217,74966593,75402221,80000519,80112418,79005070,80831698,76082779,73232671,71056247,75135941,71903141,78060009,79742551,70250313,66205769,78319432,73988994,74372922,71546410,73545171,74220510,63051559,66646384,68538308,59360208,63812166,75673274,72347320,76357687,66600939,56487705,70648976,70968064,74698003,75944759,74122368,67485399,63305556,68515366,71145401,69625041,68993028,71537081,75025172,75155755,70871549,69489568,74027234,72162369,74396503,76005043,72708060,70567406,73291632,75536033,73865170,69322880,73918987,72020474,76681275,76604577,77208720,75252120,75305805,76861569,77278013,77057424,76937746,72642470,65754315,70138397,73236479
+contact rbr at tip-informatik.at
+reject 0.0.0.0/8:*
+reject 169.254.0.0/16:*
+reject 127.0.0.0/8:*
+reject 192.168.0.0/16:*
+reject 10.0.0.0/8:*
+reject 172.16.0.0/12:*
+reject *:25
+reject *:119
+reject *:135-139
+reject *:445
+reject *:465
+reject *:587
+reject *:1214
+reject *:4661-4666
+reject *:6346-6429
+reject *:6699
+reject *:6881-6999
+accept *:*
+router-signature
+-----BEGIN SIGNATURE-----
+XAy56NzQm+LGMnaxBZC0ulmyZxoTNTFxZN0834V/hrfBdEOwMuBZMSsS613OfRK1
+3K/StL29NdLChHt71XCPyxsnbzguBE0Pma6cX4aSNa1WJO4wpc1WipmWdmuVKXHD
+RoxIVSJU3qwfjzSbwJiNSLPs4mmQFecsVmG94cZSITY=
+-----END SIGNATURE-----
diff --git a/test/unit/descriptor/data/new_metrics_type b/test/unit/descriptor/data/new_metrics_type
new file mode 100644
index 0000000..95bd669
--- /dev/null
+++ b/test/unit/descriptor/data/new_metrics_type
@@ -0,0 +1,3 @@
+ at type non-existant-type 1.0
+valid metrics header, but a type we shouldn't recognize
+
diff --git a/test/unit/descriptor/data/non-ascii_descriptor b/test/unit/descriptor/data/non-ascii_descriptor
new file mode 100644
index 0000000..da1d8a5
--- /dev/null
+++ b/test/unit/descriptor/data/non-ascii_descriptor
@@ -0,0 +1,30 @@
+ at type server-descriptor 1.0
+router Coruscant 88.182.161.122 9001 0 9030
+platform Tor 0.2.3.25 on Linux
+opt protocols Link 1 2 Circuit 1
+published 2013-05-18 11:16:19
+opt fingerprint 0B98 2154 5C48 E496 AEED 9ECC 0DB5 06C4 9FF8 158D
+uptime 259738
+bandwidth 102400 204800 122818
+opt extra-info-digest 56403D838DE152421CD401B8E57DAD4483A3D56B
+onion-key
+-----BEGIN RSA PUBLIC KEY-----
+MIGJAoGBAK+rjPilZJVzvWn6slOpak4eUab+VXiGNkRAIKgGAqbpex7lNMcZeXbu
+8uYhiDUdjRaHQUbBjLaj6nAjtlHUl3NCbIpo2p03KEcxfAGi3k6dUMBJ7aIYb8lL
+SDK95huLBpyyNPikeDIiTKS1HNtmrai/iVtUiJS2I4fs3lP1TxevAgMBAAE=
+-----END RSA PUBLIC KEY-----
+signing-key
+-----BEGIN RSA PUBLIC KEY-----
+MIGJAoGBANGDDMHoCtTeL4UTEGip4T+37jd4lEjBgla7lgL8W1gtVTX/uMMo+HGE
+m1sD/FgBI3w8w7zpZlJgYeB/94YPUrw11iAEYAooJVmGDmel2g6CFofDs7KmrzqR
+1FQDIWeY9sQd4+L+jWMquqcA3FyzuZz28gi56hIombjkfr0RwCsNAgMBAAE=
+-----END RSA PUBLIC KEY-----
+opt hidden-service-dir
+contact 1024D/04D2E818 Lénaïc Huard <lenaic dot huard AT laposte dot net>
+reject *:*
+router-signature
+-----BEGIN SIGNATURE-----
+hk4jKRtI0LbjYWdGhjTuev9Hk9AilyBmBtK9nfpBPyamQZDZVirEUuP7PdtxM2U9
+EdCg+KnkrLoKoxh18YumBD2HvwHa2J1VZtezWkwxGB+HPkECTU/byl+dn6ZRncY7
+r0IrVeGUnlK/UaWpswx6amH7FEKXmBZYHxTxyuwwybQ=
+-----END SIGNATURE-----
diff --git a/test/unit/descriptor/data/old_descriptor b/test/unit/descriptor/data/old_descriptor
new file mode 100644
index 0000000..8be82f9
--- /dev/null
+++ b/test/unit/descriptor/data/old_descriptor
@@ -0,0 +1,48 @@
+ at type server-descriptor 1.0
+router krypton 212.37.39.59 8000 0 0
+platform Tor 0.1.0.14 on FreeBSD i386
+published 2005-12-16 18:01:03
+opt fingerprint 3E2F 63E2 356F 5231 8B53 6A12 B644 5373 808A 5D6C
+uptime 64820
+bandwidth 102400 10485760 0
+onion-key
+-----BEGIN RSA PUBLIC KEY-----
+MIGJAoGBAOewVPvehUE/Iy06e/NmUyzwBdkL89DP3q+I68hJJsxQgd1Fqm2CVN4E
+X0l3U14e3yyihr8xz1qmvQenWyKMx6B5ZjHrRAk774nTXzqqZMbcEKGnoWKTAgfC
+SrZYD4YdO2vPIA3OIBLBvj6HlyZWPVulQANpETK1TOACKeqs6vlhAgMBAAE=
+-----END RSA PUBLIC KEY-----
+signing-key
+-----BEGIN RSA PUBLIC KEY-----
+MIGJAoGBAJjsGNwSkG7xCzWEPTr2NBrC4k2Bx0SnTv8RfgUwqRg8rLss5CoZw7BV
+GL3g6PNe13rWcu7rYg27mT81T4cehrl0omnx4FrhDPmnJY4d+yjUiLKujTYaijyZ
+8ll4Mr1Ua3M2tTa+79sA8rXAg3zMf+BtTfIcK4avm+csipZ75r9BAgMBAAE=
+-----END RSA PUBLIC KEY-----
+opt write-history 2005-12-16 18:00:48 (900 s) 81,8848,8927,8927,83,8848,8931,8929,81,8846,8929,8931,81,8848,8929,8927,81,8846,8929,8929,83,8848,8931,8927,9544,23145,118733,12471330,23681225,32974322,36669050,50028191,60119352,43959405,104125876,93764839,67886465,56660787,69614721,49677518,72758978,69080350,68451562,56135957,66072248,44186182,63136414,78723340,84085493,73800911,61322609,54493047,40058835,35991325,33933884,43033805,30004913,31375439,29870384,39901793,24405790,46428634,32855040,31738054,32096150,34571772,47317095,43861120,1725009,2709813,3632379,7206137,4376659,3499688,2148089,2420819,3039400,3271931,2661625,2387027,943794,138137,167325,152659,120259,119828,120342,116812,118734,117279,116769,114764,115673,117709,117201,110668
+opt read-history  2005-12-16 18:00:48 (900 s) 20774,489973,510022,511163,20949,490665,509936,510266,20919,490429,512036,508640,20666,485937,506582,505071,20662,484684,507452,506131,20565,485822,505385,505766,28300,499433,627237,12392493,20716078,25654673,25415889,39581836,49991512,31545049,96363762,80390334,60930455,45885095,57137777,42643192,59698768,54573481,58379825,45862920,58781990,33448161,53507694,73363721,70825862,61283831,49325261,44674036,28451429,25009272,22895835,34914097,21103340,19156546,21131703,33036587,18445151,30464430,21857164,27329112,22507170,29536769,37525461,42026081,1778746,3200903,4137862,7712377,4397719,3980947,2650315,2440799,3522111,3774977,3165517,2407670,1427206,644034,672441,173113,609713,630447,632529,136977,611086,629716,629248,135385,607471,627380,625011,131334
+opt hibernating 1
+reject 169.254.0.0/255.255.0.0:*
+reject 127.0.0.0/255.0.0.0:*
+reject 192.168.0.0/255.255.0.0:*
+reject 10.0.0.0/255.0.0.0:*
+reject 172.16.0.0/255.240.0.0:*
+accept *:20-22
+accept *:53
+accept *:79-81
+accept *:110
+accept *:143
+accept *:443
+accept *:706
+accept *:873
+accept *:993
+accept *:995
+accept *:6660-6669
+accept *:8008
+accept *:8080
+accept *:8888
+reject *:*
+router-signature
+-----BEGIN SIGNATURE-----
+mHTlJGu2d2ZZgXfoI0CZBiLMCKbHox2n+Q3OGcivLj0kcYfJ/7/jk4o5ABRgyOHM
+zJ7FnC2wSg19MB8jeAvKkxdd1hp0OZQp+tCRq/7p11+6E0qvJcio+RwdeZAb0dQf
+suUOPIU/lHzfmraZMO3ovK4VjiWKQ7PgE1vAH7sTyZA=
+-----END SIGNATURE-----
diff --git a/test/unit/descriptor/data/riddle b/test/unit/descriptor/data/riddle
new file mode 100644
index 0000000..ac08f36
--- /dev/null
+++ b/test/unit/descriptor/data/riddle
@@ -0,0 +1,20 @@
+Riddle by Damian Johnson (May, 2009)
+
+Curtains raise, they take the stage,
+  mute actors that never age.
+The clouds are cotton, the floor is pine,
+  the souls bound up in fine twine.
+
+Crowds will cheer, the players dance,
+  flowing scene to scene as in a trance.
+The world is perfect, they never whine,
+  for there's not a single will, save mine.
+
+The play has ended, we take a bow,
+  but the viewers are the actors now.
+When freed from decision, of thought and blame,
+  we'll walk to another drum just the same. 
+
+answer:
+http://www.atagar.com/riddles/answer10.php
+
diff --git a/test/unit/descriptor/data/tiny.png b/test/unit/descriptor/data/tiny.png
new file mode 100644
index 0000000..e86715f
Binary files /dev/null and b/test/unit/descriptor/data/tiny.png differ
diff --git a/test/unit/descriptor/data/vote b/test/unit/descriptor/data/vote
new file mode 100644
index 0000000..ac2a6fc
--- /dev/null
+++ b/test/unit/descriptor/data/vote
@@ -0,0 +1,100 @@
+network-status-version 3
+vote-status vote
+consensus-methods 1 2 3 4 5 6 7 8 9 10 11 12
+published 2012-07-11 23:50:01
+valid-after 2012-07-12 00:00:00
+fresh-until 2012-07-12 01:00:00
+valid-until 2012-07-12 03:00:00
+voting-delay 300 300
+known-flags Authority BadExit Exit Fast Guard HSDir Running Stable V2Dir Valid
+params CircuitPriorityHalflifeMsec=30000 bwauthpid=1
+dir-source turtles 27B6B5996C426270A5C95488AA5BCEB6BCC86956 76.73.17.194 76.73.17.194 9030 9090
+contact Mike Perry <email>
+dir-key-certificate-version 3
+fingerprint 27B6B5996C426270A5C95488AA5BCEB6BCC86956
+dir-key-published 2011-11-28 21:51:04
+dir-key-expires 2012-11-28 21:51:04
+dir-identity-key
+-----BEGIN RSA PUBLIC KEY-----
+MIIBigKCAYEA6uSmsoxj2MiJ3qyZq0qYXlRoG8o82SNqg+22m+t1c7MlQOZWPJYn
+XeMcBCt8xrTeIt2ZI+Q/Kt2QJSeD9WZRevTKk/kn5Tg2+xXPogalUU47y5tUohGz
++Q8+CxtRSXpDxBHL2P8rLHvGrI69wbNHGoQkce/7gJy9vw5Ie2qzbyXk1NG6V8Fb
+pr6A885vHo6TbhUnolz2Wqt/kN+UorjLkN2H3fV+iGcQFv42SyHYGDLa0WwL3PJJ
+r/veu36S3VaHBrfhutfioi+d3d4Ya0bKwiWi5Lm2CHuuRTgMpHLU9vlci8Hunuxq
+HsULe2oMsr4VEic7sW5SPC5Obpx6hStHdNv1GxoSEm3/vIuPM8pINpU5ZYAyH9yO
+Ef22ZHeiVMMKmpV9TtFyiFqvlI6GpQn3mNbsQqF1y3XCA3Q4vlRAkpgJVUSvTxFP
+2bNDobOyVCpCM/rwxU1+RCNY5MFJ/+oktUY+0ydvTen3gFdZdgNqCYjKPLfBNm9m
+RGL7jZunMUNvAgMBAAE=
+-----END RSA PUBLIC KEY-----
+dir-signing-key
+-----BEGIN RSA PUBLIC KEY-----
+MIGJAoGBAJ5itcJRYNEM3Qf1OVWLRkwjqf84oXPc2ZusaJ5zOe7TVvBMra9GNyc0
+NM9y6zVkHCAePAjr4KbW/8P1olA6FUE2LV9bozaU1jFf6K8B2OELKs5FUEW+n+ic
+GM0x6MhngyXonWOcKt5Gj+mAu5lrno9tpNbPkz2Utr/Pi0nsDhWlAgMBAAE=
+-----END RSA PUBLIC KEY-----
+dir-key-crosscert
+-----BEGIN ID SIGNATURE-----
+RHYImGTwg36wmEdAn7qaRg2sAfql7ZCtPIL/O3lU5OIdXXp0tNn/K00Bamqohjk+
+Tz4FKsKXGDlbGv67PQcZPOK6NF0GRkNh4pk89prrDO4XwtEn7rkHHdBH6/qQ7IRG
+GdDZHtZ1a69oFZvPWD3hUaB50xeIe7GoKdKIfdNNJ+8=
+-----END ID SIGNATURE-----
+dir-key-certification
+-----BEGIN SIGNATURE-----
+fasWOGyUZ3iMCYpDfJ+0JcMiTH25sXPWzvlHorEOyOMbaMqRYpZU4GHzt1jLgdl6
+AAoR6KdamsLg5VE8xzst48a4UFuzHFlklZ5O8om2rcvDd5DhSnWWYZnYJecqB+bo
+dNisPmaIVSAWb29U8BpNRj4GMC9KAgGYUj8aE/KtutAeEekFfFEHTfWZ2fFp4j3m
+9rY8FWraqyiF+Emq1T8pAAgMQ+79R3oZxq0TXS42Z4Anhms735ccauKhI3pDKjbl
+tD5vAzIHOyjAOXj7a6jY/GrnaBNuJ4qe/4Hf9UmzK/jKKwG95BPJtPTT4LoFwEB0
+KG2OUeQUNoCck4nDpsZwFqPlrWCHcHfTV2iDYFV1HQWDTtZz/qf+GtB8NXsq+I1w
+brADmvReM2BD6p/13h0QURCI5hq7ZYlIKcKrBa0jn1d9cduULl7vgKsRCJDls/ID
+emBZ6pUxMpBmV0v+PrA3v9w4DlE7GHAq61FF/zju2kpqj6MInbEvI/E+e438sWsL
+-----END SIGNATURE-----
+r sumkledi ABPSI4nNUNC3hKPkBhyzHozozrU B5n4BiALAF8B5AqafxohyYiuj7E 2012-07-11 04:22:53 178.218.213.229 80 0
+s Exit Valid
+opt v Tor 0.2.2.35
+w Bandwidth=51 Measured=36
+p accept 80,443
+m 8,9,10,11,12 sha256=g1vx9si329muxV3tquWIXXySNOIwRGMeAESKs/v4DWs
+r Unnamed AEXri4INxBAZeyi0wvJZoC58nZs csTseFzwBV0RLsEdlyxDw6jTZ1o 2012-07-11 10:47:26 79.139.135.90 443 22
+s Fast HSDir Running V2Dir Valid
+opt v Tor 0.2.2.37
+w Bandwidth=92 Measured=15
+p reject 1-65535
+m 8,9,10,11,12 sha256=iZfmvEGdBG/qc9KYZUh4JsWEHbn9Bw2jy5i0AtCoRB0
+r default AFU4pMWKyq+eTPuBO0D9W179kQQ OZzEaq9N5+VX5yxzi051zqyEcPk 2012-07-11 08:17:21 2.90.115.167 443 9030
+s V2Dir Valid
+opt v Tor 0.2.3.15-alpha
+w Bandwidth=53
+p reject 1-65535
+m 8,9,10,11,12 sha256=m/RouK3TqoaYBBHCxgmzMPuGGCIEr0ufSTB1i85zicM
+r satoshi11 AFzRyEo3Ibdko7vl4rReoU3BDfM rWaYhhU9Kym4sXAVe+uE99VGpJs 2012-07-11 08:10:01 80.218.153.44 443 9030
+s V2Dir Valid
+opt v Tor 0.2.2.35
+w Bandwidth=196 Measured=113
+p reject 1-65535
+m 8,9,10,11,12 sha256=yxuOPz7IraIzzmU/qeZRJHOJrutNmvPwDwj2AmsHZSI
+r JapanAnon AGw/p8P246zRPQ3ZsQx9+pM8I3s FpQ4fP9FE2j4AnpZTcPiUbaIjwQ 2012-07-11 15:55:42 220.0.231.71 443 9030
+s Exit V2Dir Valid
+opt v Tor 0.2.2.37
+w Bandwidth=41 Measured=5
+p accept 110,143,443,706,993,995,1863,5050,5190,5222-5223,6660-6669,6697,7000-7001,8300,8888
+m 8,9,10,11,12 sha256=fdV6McsFl7ZaX8YJjRCchKa7B7ImTr6++AE3e7zCkK8
+r ANONIONROUTER AHhuQ8zFQJdT8l42Axxc6m6kNwI UUUhUJVXZ5b+8Lizh1ghVkUTGo0 2012-07-11 20:13:16 93.128.114.184 24051 24052
+s Fast Running V2Dir Valid
+opt v Tor 0.2.2.37
+w Bandwidth=196 Measured=100
+p reject 1-65535
+m 8,9,10,11,12 sha256=ooi27oMS4TBXes4FY4JyULgZYZcFzAyvB1JicaQ8zxs
+r pornosteffi AJrkZLNAIMRi7J3Q5os1iBJTM38 tV2vC76eZNwVpSFO8+r9lCqJSVw 2012-07-11 11:00:29 88.78.83.210 443 0
+s Fast Running Valid
+opt v Tor 0.2.2.35
+w Bandwidth=32 Measured=14
+p reject 1-65535
+m 8,9,10,11,12 sha256=JiARdiiTbey4aLkt4x1DUgEU3oaD2oDB2dtP7SGl3EU
+directory-footer
+directory-signature 27B6B5996C426270A5C95488AA5BCEB6BCC86956 D5C30C15BB3F1DA27669C2D88439939E8F418FCF
+-----BEGIN SIGNATURE-----
+fskXN84wB3mXfo+yKGSt0AcDaaPuU3NwMR3ROxWgLN0KjAaVi2eV9PkPCsQkcgw3
+JZ/1HL9sHyZfo6bwaC6YSM9PNiiY6L7rnGpS7UkHiFI+M96VCMorvjm5YPs3FioJ
+DnN5aFtYKiTc19qIC7Nmo+afPdDEf0MlJvEOP5EWl3w=
+-----END SIGNATURE-----
diff --git a/test/unit/descriptor/extrainfo_descriptor.py b/test/unit/descriptor/extrainfo_descriptor.py
index 0471bb8..a8b0ed7 100644
--- a/test/unit/descriptor/extrainfo_descriptor.py
+++ b/test/unit/descriptor/extrainfo_descriptor.py
@@ -5,11 +5,122 @@ Unit tests for stem.descriptor.extrainfo_descriptor.
 import datetime
 import unittest
 
+import stem.descriptor
+
 from stem.descriptor.extrainfo_descriptor import RelayExtraInfoDescriptor, DirResponse, DirStat
+
 from test.mocking import get_relay_extrainfo_descriptor, get_bridge_extrainfo_descriptor, CRYPTO_BLOB
 
+from test.unit.descriptor import get_resource
+
 
 class TestExtraInfoDescriptor(unittest.TestCase):
+  def test_metrics_relay_descriptor(self):
+    """
+    Parses and checks our results against an extrainfo descriptor from metrics.
+    """
+
+    descriptor_file = open(get_resource('extrainfo_relay_descriptor'), 'rb')
+
+    expected_signature = """-----BEGIN SIGNATURE-----
+K5FSywk7qvw/boA4DQcqkls6Ize5vcBYfhQ8JnOeRQC9+uDxbnpm3qaYN9jZ8myj
+k0d2aofcVbHr4fPQOSST0LXDrhFl5Fqo5um296zpJGvRUeO6S44U/EfJAGShtqWw
+7LZqklu+gVvhMKREpchVqlAwXkWR44VENm24Hs+mT3M=
+-----END SIGNATURE-----"""
+
+    desc = next(stem.descriptor.parse_file(descriptor_file, 'extra-info 1.0'))
+    self.assertEquals('NINJA', desc.nickname)
+    self.assertEquals('B2289C3EAB83ECD6EB916A2F481A02E6B76A0A48', desc.fingerprint)
+    self.assertEquals(datetime.datetime(2012, 5, 5, 17, 3, 50), desc.published)
+    self.assertEquals(datetime.datetime(2012, 5, 5, 17, 2, 45), desc.read_history_end)
+    self.assertEquals(900, desc.read_history_interval)
+    self.assertEquals(datetime.datetime(2012, 5, 5, 17, 2, 45), desc.write_history_end)
+    self.assertEquals(900, desc.write_history_interval)
+    self.assertEquals(datetime.datetime(2012, 5, 5, 17, 2, 45), desc.dir_read_history_end)
+    self.assertEquals(900, desc.dir_read_history_interval)
+    self.assertEquals(datetime.datetime(2012, 5, 5, 17, 2, 45), desc.dir_write_history_end)
+    self.assertEquals(900, desc.dir_write_history_interval)
+    self.assertEquals(expected_signature, desc.signature)
+    self.assertEquals('00A57A9AAB5EA113898E2DD02A755E31AFC27227', desc.digest())
+    self.assertEquals([], desc.get_unrecognized_lines())
+
+    # The read-history, write-history, dirreq-read-history, and
+    # dirreq-write-history lines are pretty long so just checking
+    # the initial contents for the line and parsed values.
+
+    read_values_start = [3309568, 9216, 41984, 27648, 123904]
+    self.assertEquals(read_values_start, desc.read_history_values[:5])
+
+    write_values_start = [1082368, 19456, 50176, 272384, 485376]
+    self.assertEquals(write_values_start, desc.write_history_values[:5])
+
+    dir_read_values_start = [0, 0, 0, 0, 33792, 27648, 48128]
+    self.assertEquals(dir_read_values_start, desc.dir_read_history_values[:7])
+
+    dir_write_values_start = [0, 0, 0, 227328, 349184, 382976, 738304]
+    self.assertEquals(dir_write_values_start, desc.dir_write_history_values[:7])
+
+  def test_metrics_bridge_descriptor(self):
+    """
+    Parses and checks our results against an extrainfo bridge descriptor from
+    metrics.
+    """
+
+    descriptor_file = open(get_resource('extrainfo_bridge_descriptor'), 'rb')
+
+    expected_dir_v2_responses = {
+      DirResponse.OK: 0,
+      DirResponse.UNAVAILABLE: 0,
+      DirResponse.NOT_FOUND: 0,
+      DirResponse.NOT_MODIFIED: 0,
+      DirResponse.BUSY: 0,
+    }
+
+    expected_dir_v3_responses = {
+      DirResponse.OK: 72,
+      DirResponse.NOT_ENOUGH_SIGS: 0,
+      DirResponse.UNAVAILABLE: 0,
+      DirResponse.NOT_FOUND: 0,
+      DirResponse.NOT_MODIFIED: 0,
+      DirResponse.BUSY: 0,
+    }
+
+    desc = next(stem.descriptor.parse_file(descriptor_file, 'bridge-extra-info 1.0'))
+    self.assertEquals('ec2bridgereaac65a3', desc.nickname)
+    self.assertEquals('1EC248422B57D9C0BD751892FE787585407479A4', desc.fingerprint)
+    self.assertEquals(datetime.datetime(2012, 6, 8, 2, 21, 27), desc.published)
+    self.assertEquals(datetime.datetime(2012, 6, 8, 2, 10, 38), desc.read_history_end)
+    self.assertEquals(900, desc.read_history_interval)
+    self.assertEquals(datetime.datetime(2012, 6, 8, 2, 10, 38), desc.write_history_end)
+    self.assertEquals(900, desc.write_history_interval)
+    self.assertEquals(datetime.datetime(2012, 6, 8, 2, 10, 38), desc.dir_read_history_end)
+    self.assertEquals(900, desc.dir_read_history_interval)
+    self.assertEquals(datetime.datetime(2012, 6, 8, 2, 10, 38), desc.dir_write_history_end)
+    self.assertEquals(900, desc.dir_write_history_interval)
+    self.assertEquals('00A2AECCEAD3FEE033CFE29893387143146728EC', desc.digest())
+    self.assertEquals([], desc.get_unrecognized_lines())
+
+    read_values_start = [337920, 437248, 3995648, 48726016]
+    self.assertEquals(read_values_start, desc.read_history_values[:4])
+
+    write_values_start = [343040, 991232, 5649408, 49548288]
+    self.assertEquals(write_values_start, desc.write_history_values[:4])
+
+    dir_read_values_start = [0, 71680, 99328, 25600]
+    self.assertEquals(dir_read_values_start, desc.dir_read_history_values[:4])
+
+    dir_write_values_start = [5120, 664576, 2419712, 578560]
+    self.assertEquals(dir_write_values_start, desc.dir_write_history_values[:4])
+
+    self.assertEquals({}, desc.dir_v2_requests)
+    self.assertEquals({}, desc.dir_v3_requests)
+
+    self.assertEquals(expected_dir_v2_responses, desc.dir_v2_responses)
+    self.assertEquals(expected_dir_v3_responses, desc.dir_v3_responses)
+
+    self.assertEquals({}, desc.dir_v2_responses_unknown)
+    self.assertEquals({}, desc.dir_v2_responses_unknown)
+
   def test_minimal_extrainfo_descriptor(self):
     """
     Basic sanity check that we can parse an extrainfo descriptor with minimal
diff --git a/test/unit/descriptor/microdescriptor.py b/test/unit/descriptor/microdescriptor.py
index e126c8c..2193731 100644
--- a/test/unit/descriptor/microdescriptor.py
+++ b/test/unit/descriptor/microdescriptor.py
@@ -6,12 +6,77 @@ import unittest
 
 import stem.exit_policy
 
+import stem.descriptor
+
 from stem.descriptor.microdescriptor import Microdescriptor
 from test.mocking import get_microdescriptor, \
                          CRYPTO_BLOB
 
+from test.unit.descriptor import get_resource
+
+FIRST_ONION_KEY = """\
+-----BEGIN RSA PUBLIC KEY-----
+MIGJAoGBAMhPQtZPaxP3ukybV5LfofKQr20/ljpRk0e9IlGWWMSTkfVvBcHsa6IM
+H2KE6s4uuPHp7FqhakXAzJbODobnPHY8l1E4efyrqMQZXEQk2IMhgSNtG6YqUrVF
+CxdSKSSy0mmcBe2TOyQsahlGZ9Pudxfnrey7KcfqnArEOqNH09RpAgMBAAE=
+-----END RSA PUBLIC KEY-----\
+"""
+
+SECOND_ONION_KEY = """\
+-----BEGIN RSA PUBLIC KEY-----
+MIGJAoGBALCOxZdpMI2WO496njSQ2M7b4IgAGATqpJmH3So7lXOa25sK6o7JipgP
+qQE83K/t/xsMIpxQ/hHkft3G78HkeXXFc9lVUzH0HmHwYEu0M+PMVULSkG36MfEl
+7WeSZzaG+Tlnh9OySAzVyTsv1ZJsTQFHH9V8wuM0GOMo9X8DFC+NAgMBAAE=
+-----END RSA PUBLIC KEY-----\
+"""
+
+THIRD_ONION_KEY = """\
+-----BEGIN RSA PUBLIC KEY-----
+MIGJAoGBAOWFQHxO+5kGuhwPUX5jB7wJCrTbSU0fZwolNV1t9UaDdjGDvIjIhdit
+y2sMbyd9K8lbQO7x9rQjNst5ZicuaSOs854XQddSjm++vMdjYbOcVMqnKGSztvpd
+w/1LVWFfhcBnsGi4JMGbmP+KUZG9A8kI9deSyJhfi35jA7UepiHHAgMBAAE=
+-----END RSA PUBLIC KEY-----\
+"""
+
 
 class TestMicrodescriptor(unittest.TestCase):
+  def test_local_microdescriptors(self):
+    """
+    Checks a small microdescriptor file with known contents.
+    """
+
+    descriptor_path = get_resource('cached-microdescs')
+
+    with open(descriptor_path, 'rb') as descriptor_file:
+      descriptors = stem.descriptor.parse_file(descriptor_file, 'microdescriptor 1.0')
+
+      router = next(descriptors)
+      self.assertEquals(FIRST_ONION_KEY, router.onion_key)
+      self.assertEquals(None, router.ntor_onion_key)
+      self.assertEquals([], router.or_addresses)
+      self.assertEquals([], router.family)
+      self.assertEquals(stem.exit_policy.MicroExitPolicy('reject 1-65535'), router.exit_policy)
+      self.assertEquals({b'@last-listed': b'2013-02-24 00:18:36'}, router.get_annotations())
+      self.assertEquals([b'@last-listed 2013-02-24 00:18:36'], router.get_annotation_lines())
+
+      router = next(descriptors)
+      self.assertEquals(SECOND_ONION_KEY, router.onion_key)
+      self.assertEquals(u'r5572HzD+PMPBbXlZwBhsm6YEbxnYgis8vhZ1jmdI2k=', router.ntor_onion_key)
+      self.assertEquals([], router.or_addresses)
+      self.assertEquals(['$6141629FA0D15A6AEAEF3A1BEB76E64C767B3174'], router.family)
+      self.assertEquals(stem.exit_policy.MicroExitPolicy('reject 1-65535'), router.exit_policy)
+      self.assertEquals({b'@last-listed': b'2013-02-24 00:18:37'}, router.get_annotations())
+      self.assertEquals([b'@last-listed 2013-02-24 00:18:37'], router.get_annotation_lines())
+
+      router = next(descriptors)
+      self.assertEquals(THIRD_ONION_KEY, router.onion_key)
+      self.assertEquals(None, router.ntor_onion_key)
+      self.assertEquals([(u'2001:6b0:7:125::242', 9001, True)], router.or_addresses)
+      self.assertEquals([], router.family)
+      self.assertEquals(stem.exit_policy.MicroExitPolicy('accept 80,443'), router.exit_policy)
+      self.assertEquals({b'@last-listed': b'2013-02-24 00:18:36'}, router.get_annotations())
+      self.assertEquals([b'@last-listed 2013-02-24 00:18:36'], router.get_annotation_lines())
+
   def test_minimal_microdescriptor(self):
     """
     Basic sanity check that we can parse a microdescriptor with minimal
diff --git a/test/unit/descriptor/networkstatus/bridge_document.py b/test/unit/descriptor/networkstatus/bridge_document.py
index c5aaba6..a45e802 100644
--- a/test/unit/descriptor/networkstatus/bridge_document.py
+++ b/test/unit/descriptor/networkstatus/bridge_document.py
@@ -5,8 +5,12 @@ Unit tests for the BridgeNetworkStatusDocument of stem.descriptor.networkstatus.
 import datetime
 import unittest
 
+import stem.descriptor
+
 from stem.descriptor.networkstatus import BridgeNetworkStatusDocument
 
+from test.unit.descriptor import get_resource
+
 DOCUMENT = b"""\
 published 2012-06-01 04:07:04
 r Unnamed ABSiBVJ42z6w5Z6nAXQUFq8YVVg FI74aFuNJZZQrgln0f+OaocMd0M 2012-05-31 15:57:00 10.97.236.247 443 0
@@ -21,6 +25,73 @@ p reject 1-65535
 
 
 class TestBridgeNetworkStatusDocument(unittest.TestCase):
+  def test_metrics_bridge_consensus(self):
+    """
+    Checks if the bridge documents from Metrics are parsed properly.
+    """
+
+    consensus_path = get_resource('bridge_network_status')
+
+    with open(consensus_path, 'rb') as descriptor_file:
+      router = next(stem.descriptor.parse_file(descriptor_file))
+      self.assertEquals('Unnamed', router.nickname)
+      self.assertEquals('0014A2055278DB3EB0E59EA701741416AF185558', router.fingerprint)
+      self.assertEquals('148EF8685B8D259650AE0967D1FF8E6A870C7743', router.digest)
+      self.assertEquals(datetime.datetime(2012, 5, 31, 15, 57, 0), router.published)
+      self.assertEquals('10.97.236.247', router.address)
+      self.assertEquals(443, router.or_port)
+      self.assertEquals(None, router.dir_port)
+
+  def test_metrics_cert(self):
+    """
+    Checks if consensus documents from Metrics are parsed properly.
+    """
+
+    expected_identity_key = """-----BEGIN RSA PUBLIC KEY-----
+MIIBigKCAYEA7cZXvDRxfjDYtr9/9UsQ852+6cmHMr8VVh8GkLwbq3RzqjkULwQ2
+R9mFvG4FnqMcMKXi62rYYA3fZL1afhT804cpvyp/D3dPM8QxW88fafFAgIFP4LiD
+0JYjnF8cva5qZ0nzlWnMXLb32IXSvsGSE2FRyAV0YN9a6k967LSgCfUnZ+IKMezW
+1vhL9YK4QIfsDowgtVsavg63GzGmA7JvZmn77+/J5wKz11vGr7Wttf8XABbH2taX
+O9j/KGBOX2OKhoF3mXfZSmUO2dV9NMwtkJ7zD///Ny6sfApWV6kVP4O9TdG3bAsl
++fHCoCKgF/jAAWzh6VckQTOPzQZaH5aMWfXrDlzFWg17MjonI+bBTD2Ex2pHczzJ
+bN7coDMRH2SuOXv8wFf27KdUxZ/GcrXSRGzlRLygxqlripUanjVGN2JvrVQVr0kz
+pjNjiZl2z8ZyZ5d4zQuBi074JPGgx62xAstP37v1mPw14sIWfLgY16ewYuS5bCxV
+lyS28jsPht9VAgMBAAE=
+-----END RSA PUBLIC KEY-----"""
+
+    expected_signing_key = """-----BEGIN RSA PUBLIC KEY-----
+MIGJAoGBAOeE3Qr1Km97gTgiB3io0EU0fqHW2ESMXVHeQuNDtCWBa0XSCEG6gx4B
+ZkkHjfVWqGQ7TmmzjYP9L9uCgtoKfhSvJA2w9NUMtMl8sgZmF4lcGpXXvGY9a566
+Bn+3wP0lMhb/I8CPVPX+NWEjgl1noZxo1C59SO/iALGQOpxRYgmbAgMBAAE=
+-----END RSA PUBLIC KEY-----"""
+
+    expected_key_cert = """-----BEGIN SIGNATURE-----
+asvWwaMq34OfHoWUhAwh4+JDOuEUZJVIHQnedOYfQH8asS2QvW3Ma93OhrwVOC6b
+FyKmTJmJsl0MJGiC7tcEOlL6knsKE4CsuIw/PEcu2Rnm+R9zWxQuMYiHvGQMoDxl
+giOhLLs4LlzAAJlbfbd3hjF4STVAtTwmxYuIjb1Mq/JfAsx/wH3TLXgVZwj32w9s
+zUd9KZwwLzFiiHpC+U7zh6+wRsZfo2tlpmcaP1dTSINgVbdzPJ/DOUlx9nwTCBsE
+AQpUx2DpAikwrpw0zDqpQvYulcQlNLWFN/y/PkmiK8mIJk0OBMiQA7JgqWamnnk4
+PwqaGv483LkBF+25JFGJmnUVve3RMc+s61+2kBcjfUMed4QaHkeCMHqlRqpfQVkk
+RY22NXCwrJvSMEwiy7acC8FGysqwHRyE356+Rw6TB43g3Tno9KaHEK7MHXjSHwNs
+GM9hAsAMRX9Ogqhq5UjDNqEsvDKuyVeyh7unSZEOip9Zr6K/+7VsVPNb8vfBRBjo
+-----END SIGNATURE-----"""
+
+    cert_path = get_resource('metrics_cert')
+
+    with open(cert_path, 'rb') as cert_file:
+      cert = next(stem.descriptor.parse_file(cert_file))
+      self.assertEquals(3, cert.version)
+      self.assertEquals(None, cert.address)
+      self.assertEquals(None, cert.dir_port)
+      self.assertEquals('14C131DFC5C6F93646BE72FA1401C02A8DF2E8B4', cert.fingerprint)
+      self.assertEquals(expected_identity_key, cert.identity_key)
+      self.assertEquals(datetime.datetime(2008, 5, 9, 21, 13, 26), cert.published)
+      self.assertEquals(datetime.datetime(2009, 5, 9, 21, 13, 26), cert.expires)
+      self.assertEquals(expected_signing_key, cert.signing_key)
+      self.assertEquals(None, cert.crosscert)
+      self.assertEquals(expected_key_cert, cert.certification)
+      self.assertEquals([], cert.get_unrecognized_lines())
+
   def test_empty_document(self):
     """
     Parse a document without any router status entries.
diff --git a/test/unit/descriptor/networkstatus/document_v2.py b/test/unit/descriptor/networkstatus/document_v2.py
index 1b98491..7ecea87 100644
--- a/test/unit/descriptor/networkstatus/document_v2.py
+++ b/test/unit/descriptor/networkstatus/document_v2.py
@@ -5,10 +5,89 @@ Unit tests for the NetworkStatusDocumentV2 of stem.descriptor.networkstatus.
 import datetime
 import unittest
 
+import stem.descriptor
+
 from test.mocking import get_network_status_document_v2, NETWORK_STATUS_DOCUMENT_HEADER_V2, NETWORK_STATUS_DOCUMENT_FOOTER_V2
 
+from test.unit.descriptor import get_resource
+
 
 class TestNetworkStatusDocument(unittest.TestCase):
+  def test_consensus_v2(self):
+    """
+    Checks that version 2 consensus documents are properly parsed.
+    """
+
+    expected_signing_key = """-----BEGIN RSA PUBLIC KEY-----
+MIGJAoGBAOcrht/y5rkaahfX7sMe2qnpqoPibsjTSJaDvsUtaNP/Bq0MgNDGOR48
+rtwfqTRff275Edkp/UYw3G3vSgKCJr76/bqOHCmkiZrnPV1zxNfrK18gNw2Cxre0
+nTA+fD8JQqpPtb8b0SnG9kwy75eS//sRu7TErie2PzGMxrf9LH0LAgMBAAE=
+-----END RSA PUBLIC KEY-----"""
+
+    expected_signature = """-----BEGIN SIGNATURE-----
+2nXCxVje3wzn6HrIFRNMc0nc48AhMVpHZyPwRKGXkuYfTQG55uvwQDaFgJHud4RT
+27QhWltau3K1evhnzhKcpbTXwkVv1TBYJSzL6rEeAn8cQ7ZiCyqf4EJCaNcem3d2
+TpQQk3nNQF8z6UIvdlvP+DnJV4izWVkQEZgUZgIVM0E=
+-----END SIGNATURE-----"""
+
+    with open(get_resource('cached-consensus-v2'), 'rb') as descriptor_file:
+      descriptor_file.readline()  # strip header
+      document = stem.descriptor.networkstatus.NetworkStatusDocumentV2(descriptor_file.read())
+
+      self.assertEquals(2, document.version)
+      self.assertEquals('18.244.0.114', document.hostname)
+      self.assertEquals('18.244.0.114', document.address)
+      self.assertEquals(80, document.dir_port)
+      self.assertEquals('719BE45DE224B607C53707D0E2143E2D423E74CF', document.fingerprint)
+      self.assertEquals('arma at mit dot edu', document.contact)
+      self.assertEquals(expected_signing_key, document.signing_key)
+
+      self.assertEquals(67, len(document.client_versions))
+      self.assertEquals('0.0.9rc2', document.client_versions[0])
+      self.assertEquals('0.1.1.10-alpha-cvs', document.client_versions[-1])
+
+      self.assertEquals(67, len(document.server_versions))
+      self.assertEquals('0.0.9rc2', document.server_versions[0])
+      self.assertEquals('0.1.1.10-alpha-cvs', document.server_versions[-1])
+
+      self.assertEquals(datetime.datetime(2005, 12, 16, 0, 13, 46), document.published)
+      self.assertEquals(['Names', 'Versions'], document.options)
+      self.assertEquals('moria2', document.signing_authority)
+      self.assertEquals(expected_signature, document.signature)
+      self.assertEquals([], document.get_unrecognized_lines())
+
+      self.assertEqual(3, len(document.routers))
+
+      router1 = document.routers['719BE45DE224B607C53707D0E2143E2D423E74CF']
+      self.assertEquals('moria2', router1.nickname)
+      self.assertEquals('719BE45DE224B607C53707D0E2143E2D423E74CF', router1.fingerprint)
+      self.assertEquals('B7F3F0975B87889DD1285FD57A1B1BB617F65432', router1.digest)
+      self.assertEquals(datetime.datetime(2005, 12, 15, 6, 57, 18), router1.published)
+      self.assertEquals('18.244.0.114', router1.address)
+      self.assertEquals(443, router1.or_port)
+      self.assertEquals(80, router1.dir_port)
+      self.assertEquals(set(['Authority', 'Fast', 'Named', 'Running', 'Valid', 'V2Dir']), set(router1.flags))
+
+      router2 = document.routers['0928BA467056C4A689FEE4EF5D71482B6289C3D5']
+      self.assertEquals('stnv', router2.nickname)
+      self.assertEquals('0928BA467056C4A689FEE4EF5D71482B6289C3D5', router2.fingerprint)
+      self.assertEquals('22D1A7ED4199BDA7ED6C416EECD769C18E1F2A5A', router2.digest)
+      self.assertEquals(datetime.datetime(2005, 12, 15, 16, 24, 42), router2.published)
+      self.assertEquals('84.16.236.173', router2.address)
+      self.assertEquals(9001, router2.or_port)
+      self.assertEquals(None, router2.dir_port)
+      self.assertEquals(set(['Named', 'Valid']), set(router2.flags))
+
+      router3 = document.routers['09E8582FF0E6F85E2B8E41C0DC0B9C9DC46E6968']
+      self.assertEquals('nggrplz', router3.nickname)
+      self.assertEquals('09E8582FF0E6F85E2B8E41C0DC0B9C9DC46E6968', router3.fingerprint)
+      self.assertEquals('B302C2B01C94F398E3EF38939526B0651F824DD6', router3.digest)
+      self.assertEquals(datetime.datetime(2005, 12, 15, 23, 25, 50), router3.published)
+      self.assertEquals('194.109.109.109', router3.address)
+      self.assertEquals(9001, router3.or_port)
+      self.assertEquals(None, router3.dir_port)
+      self.assertEquals(set(['Fast', 'Stable', 'Running', 'Valid']), set(router3.flags))
+
   def test_minimal_document(self):
     """
     Parses a minimal v2 network status document.
diff --git a/test/unit/descriptor/networkstatus/document_v3.py b/test/unit/descriptor/networkstatus/document_v3.py
index e10a7ca..7ba034c 100644
--- a/test/unit/descriptor/networkstatus/document_v3.py
+++ b/test/unit/descriptor/networkstatus/document_v3.py
@@ -30,6 +30,8 @@ from test.mocking import get_router_status_entry_v3, \
                          DOC_SIG, \
                          NETWORK_STATUS_DOCUMENT_FOOTER
 
+from test.unit.descriptor import get_resource
+
 BANDWIDTH_WEIGHT_ENTRIES = (
   'Wbd', 'Wbe', 'Wbg', 'Wbm',
   'Wdb',
@@ -40,6 +42,255 @@ BANDWIDTH_WEIGHT_ENTRIES = (
 
 
 class TestNetworkStatusDocument(unittest.TestCase):
+  def test_metrics_consensus(self):
+    """
+    Checks if consensus documents from Metrics are parsed properly.
+    """
+
+    consensus_path = get_resource('metrics_consensus')
+
+    for specify_type in (True, False):
+      with open(consensus_path, 'rb') as descriptor_file:
+        if specify_type:
+          descriptors = stem.descriptor.parse_file(descriptor_file, 'network-status-consensus-3 1.0')
+        else:
+          descriptors = stem.descriptor.parse_file(descriptor_file)
+
+        router = next(descriptors)
+        self.assertEquals('sumkledi', router.nickname)
+        self.assertEquals('0013D22389CD50D0B784A3E4061CB31E8CE8CEB5', router.fingerprint)
+        self.assertEquals('F260ABF1297B445E04354E236F4159140FF7768F', router.digest)
+        self.assertEquals(datetime.datetime(2012, 7, 12, 4, 1, 55), router.published)
+        self.assertEquals('178.218.213.229', router.address)
+        self.assertEquals(80, router.or_port)
+        self.assertEquals(None, router.dir_port)
+
+  def test_consensus_v3(self):
+    """
+    Checks that version 3 consensus documents are properly parsed.
+    """
+
+    # the document's expected client and server versions are the same
+    expected_versions = [stem.version.Version(v) for v in (
+      '0.2.2.35',
+      '0.2.2.36',
+      '0.2.2.37',
+      '0.2.3.10-alpha',
+      '0.2.3.11-alpha',
+      '0.2.3.12-alpha',
+      '0.2.3.13-alpha',
+      '0.2.3.14-alpha',
+      '0.2.3.15-alpha',
+      '0.2.3.16-alpha',
+      '0.2.3.17-beta',
+      '0.2.3.18-rc',
+      '0.2.3.19-rc',
+    )]
+
+    expected_flags = set(
+      ['Authority', 'BadExit', 'Exit', 'Fast', 'Guard', 'HSDir',
+       'Named', 'Running', 'Stable', 'Unnamed', 'V2Dir', 'Valid'])
+
+    expected_bandwidth_weights = {
+      'Wbd': 3335, 'Wbe': 0, 'Wbg': 3536, 'Wbm': 10000, 'Wdb': 10000,
+      'Web': 10000, 'Wed': 3329, 'Wee': 10000, 'Weg': 3329, 'Wem': 10000,
+      'Wgb': 10000, 'Wgd': 3335, 'Wgg': 6464, 'Wgm': 6464, 'Wmb': 10000,
+      'Wmd': 3335, 'Wme': 0, 'Wmg': 3536, 'Wmm': 10000
+    }
+
+    expected_signature = """-----BEGIN SIGNATURE-----
+HFXB4497LzESysYJ/4jJY83E5vLjhv+igIxD9LU6lf6ftkGeF+lNmIAIEKaMts8H
+mfWcW0b+jsrXcJoCxV5IrwCDF3u1aC3diwZY6yiG186pwWbOwE41188XI2DeYPwE
+I/TJmV928na7RLZe2mGHCAW3VQOvV+QkCfj05VZ8CsY=
+-----END SIGNATURE-----"""
+
+    with open(get_resource('cached-consensus'), 'rb') as descriptor_file:
+      document = stem.descriptor.networkstatus.NetworkStatusDocumentV3(descriptor_file.read(), default_params = False)
+
+      self.assertEquals(3, document.version)
+      self.assertEquals(None, document.version_flavor)
+      self.assertEquals(True, document.is_consensus)
+      self.assertEquals(False, document.is_vote)
+      self.assertEquals(False, document.is_microdescriptor)
+      self.assertEquals(datetime.datetime(2012, 7, 12, 10, 0, 0), document.valid_after)
+      self.assertEquals(datetime.datetime(2012, 7, 12, 11, 0, 0), document.fresh_until)
+      self.assertEquals(datetime.datetime(2012, 7, 12, 13, 0, 0), document.valid_until)
+      self.assertEquals(300, document.vote_delay)
+      self.assertEquals(300, document.dist_delay)
+      self.assertEquals(expected_versions, document.client_versions)
+      self.assertEquals(expected_versions, document.server_versions)
+      self.assertEquals(expected_flags, set(document.known_flags))
+      self.assertEquals({'CircuitPriorityHalflifeMsec': 30000, 'bwauthpid': 1}, document.params)
+
+      self.assertEquals(12, document.consensus_method)
+      self.assertEquals(expected_bandwidth_weights, document.bandwidth_weights)
+      self.assertEquals([], document.consensus_methods)
+      self.assertEquals(None, document.published)
+      self.assertEquals([], document.get_unrecognized_lines())
+
+      router = document.routers['0013D22389CD50D0B784A3E4061CB31E8CE8CEB5']
+      self.assertEquals('sumkledi', router.nickname)
+      self.assertEquals('0013D22389CD50D0B784A3E4061CB31E8CE8CEB5', router.fingerprint)
+      self.assertEquals('F260ABF1297B445E04354E236F4159140FF7768F', router.digest)
+      self.assertEquals(datetime.datetime(2012, 7, 12, 4, 1, 55), router.published)
+      self.assertEquals('178.218.213.229', router.address)
+      self.assertEquals(80, router.or_port)
+      self.assertEquals(None, router.dir_port)
+      self.assertEquals(set(['Exit', 'Fast', 'Named', 'Running', 'Valid']), set(router.flags))
+
+      authority = document.directory_authorities[0]
+      self.assertEquals(8, len(document.directory_authorities))
+      self.assertEquals('tor26', authority.nickname)
+      self.assertEquals('14C131DFC5C6F93646BE72FA1401C02A8DF2E8B4', authority.fingerprint)
+      self.assertEquals('86.59.21.38', authority.hostname)
+      self.assertEquals('86.59.21.38', authority.address)
+      self.assertEquals(80, authority.dir_port)
+      self.assertEquals(443, authority.or_port)
+      self.assertEquals('Peter Palfrader', authority.contact)
+      self.assertEquals('0B6D1E9A300B895AA2D0B427F92917B6995C3C1C', authority.vote_digest)
+      self.assertEquals(None, authority.legacy_dir_key)
+      self.assertEquals(None, authority.key_certificate)
+
+      signature = document.signatures[0]
+      self.assertEquals(8, len(document.signatures))
+      self.assertEquals('sha1', signature.method)
+      self.assertEquals('14C131DFC5C6F93646BE72FA1401C02A8DF2E8B4', signature.identity)
+      self.assertEquals('BF112F1C6D5543CFD0A32215ACABD4197B5279AD', signature.key_digest)
+      self.assertEquals(expected_signature, signature.signature)
+
+  def test_metrics_vote(self):
+    """
+    Checks if vote documents from Metrics are parsed properly.
+    """
+
+    vote_path = get_resource('metrics_vote')
+
+    with open(vote_path, 'rb') as descriptor_file:
+      descriptors = stem.descriptor.parse_file(descriptor_file)
+
+      router = next(descriptors)
+      self.assertEquals('sumkledi', router.nickname)
+      self.assertEquals('0013D22389CD50D0B784A3E4061CB31E8CE8CEB5', router.fingerprint)
+      self.assertEquals('0799F806200B005F01E40A9A7F1A21C988AE8FB1', router.digest)
+      self.assertEquals(datetime.datetime(2012, 7, 11, 4, 22, 53), router.published)
+      self.assertEquals('178.218.213.229', router.address)
+      self.assertEquals(80, router.or_port)
+      self.assertEquals(None, router.dir_port)
+
+  def test_vote(self):
+    """
+    Checks that vote documents are properly parsed.
+    """
+
+    expected_flags = set(
+      ['Authority', 'BadExit', 'Exit', 'Fast', 'Guard', 'HSDir',
+       'Running', 'Stable', 'V2Dir', 'Valid'])
+
+    expected_identity_key = """-----BEGIN RSA PUBLIC KEY-----
+MIIBigKCAYEA6uSmsoxj2MiJ3qyZq0qYXlRoG8o82SNqg+22m+t1c7MlQOZWPJYn
+XeMcBCt8xrTeIt2ZI+Q/Kt2QJSeD9WZRevTKk/kn5Tg2+xXPogalUU47y5tUohGz
++Q8+CxtRSXpDxBHL2P8rLHvGrI69wbNHGoQkce/7gJy9vw5Ie2qzbyXk1NG6V8Fb
+pr6A885vHo6TbhUnolz2Wqt/kN+UorjLkN2H3fV+iGcQFv42SyHYGDLa0WwL3PJJ
+r/veu36S3VaHBrfhutfioi+d3d4Ya0bKwiWi5Lm2CHuuRTgMpHLU9vlci8Hunuxq
+HsULe2oMsr4VEic7sW5SPC5Obpx6hStHdNv1GxoSEm3/vIuPM8pINpU5ZYAyH9yO
+Ef22ZHeiVMMKmpV9TtFyiFqvlI6GpQn3mNbsQqF1y3XCA3Q4vlRAkpgJVUSvTxFP
+2bNDobOyVCpCM/rwxU1+RCNY5MFJ/+oktUY+0ydvTen3gFdZdgNqCYjKPLfBNm9m
+RGL7jZunMUNvAgMBAAE=
+-----END RSA PUBLIC KEY-----"""
+
+    expected_signing_key = """-----BEGIN RSA PUBLIC KEY-----
+MIGJAoGBAJ5itcJRYNEM3Qf1OVWLRkwjqf84oXPc2ZusaJ5zOe7TVvBMra9GNyc0
+NM9y6zVkHCAePAjr4KbW/8P1olA6FUE2LV9bozaU1jFf6K8B2OELKs5FUEW+n+ic
+GM0x6MhngyXonWOcKt5Gj+mAu5lrno9tpNbPkz2Utr/Pi0nsDhWlAgMBAAE=
+-----END RSA PUBLIC KEY-----"""
+
+    expected_key_crosscert = """-----BEGIN ID SIGNATURE-----
+RHYImGTwg36wmEdAn7qaRg2sAfql7ZCtPIL/O3lU5OIdXXp0tNn/K00Bamqohjk+
+Tz4FKsKXGDlbGv67PQcZPOK6NF0GRkNh4pk89prrDO4XwtEn7rkHHdBH6/qQ7IRG
+GdDZHtZ1a69oFZvPWD3hUaB50xeIe7GoKdKIfdNNJ+8=
+-----END ID SIGNATURE-----"""
+
+    expected_key_certification = """-----BEGIN SIGNATURE-----
+fasWOGyUZ3iMCYpDfJ+0JcMiTH25sXPWzvlHorEOyOMbaMqRYpZU4GHzt1jLgdl6
+AAoR6KdamsLg5VE8xzst48a4UFuzHFlklZ5O8om2rcvDd5DhSnWWYZnYJecqB+bo
+dNisPmaIVSAWb29U8BpNRj4GMC9KAgGYUj8aE/KtutAeEekFfFEHTfWZ2fFp4j3m
+9rY8FWraqyiF+Emq1T8pAAgMQ+79R3oZxq0TXS42Z4Anhms735ccauKhI3pDKjbl
+tD5vAzIHOyjAOXj7a6jY/GrnaBNuJ4qe/4Hf9UmzK/jKKwG95BPJtPTT4LoFwEB0
+KG2OUeQUNoCck4nDpsZwFqPlrWCHcHfTV2iDYFV1HQWDTtZz/qf+GtB8NXsq+I1w
+brADmvReM2BD6p/13h0QURCI5hq7ZYlIKcKrBa0jn1d9cduULl7vgKsRCJDls/ID
+emBZ6pUxMpBmV0v+PrA3v9w4DlE7GHAq61FF/zju2kpqj6MInbEvI/E+e438sWsL
+-----END SIGNATURE-----"""
+
+    expected_signature = """-----BEGIN SIGNATURE-----
+fskXN84wB3mXfo+yKGSt0AcDaaPuU3NwMR3ROxWgLN0KjAaVi2eV9PkPCsQkcgw3
+JZ/1HL9sHyZfo6bwaC6YSM9PNiiY6L7rnGpS7UkHiFI+M96VCMorvjm5YPs3FioJ
+DnN5aFtYKiTc19qIC7Nmo+afPdDEf0MlJvEOP5EWl3w=
+-----END SIGNATURE-----"""
+
+    with open(get_resource('vote'), 'rb') as descriptor_file:
+      document = stem.descriptor.networkstatus.NetworkStatusDocumentV3(descriptor_file.read(), default_params = False)
+
+      self.assertEquals(3, document.version)
+      self.assertEquals(None, document.version_flavor)
+      self.assertEquals(False, document.is_consensus)
+      self.assertEquals(True, document.is_vote)
+      self.assertEquals(False, document.is_microdescriptor)
+      self.assertEquals(datetime.datetime(2012, 7, 12, 0, 0, 0), document.valid_after)
+      self.assertEquals(datetime.datetime(2012, 7, 12, 1, 0, 0), document.fresh_until)
+      self.assertEquals(datetime.datetime(2012, 7, 12, 3, 0, 0), document.valid_until)
+      self.assertEquals(300, document.vote_delay)
+      self.assertEquals(300, document.dist_delay)
+      self.assertEquals([], document.client_versions)
+      self.assertEquals([], document.server_versions)
+      self.assertEquals(expected_flags, set(document.known_flags))
+      self.assertEquals({'CircuitPriorityHalflifeMsec': 30000, 'bwauthpid': 1}, document.params)
+
+      self.assertEquals(None, document.consensus_method)
+      self.assertEquals({}, document.bandwidth_weights)
+      self.assertEquals(range(1, 13), document.consensus_methods)
+      self.assertEquals(datetime.datetime(2012, 7, 11, 23, 50, 1), document.published)
+      self.assertEquals([], document.get_unrecognized_lines())
+
+      router = document.routers['0013D22389CD50D0B784A3E4061CB31E8CE8CEB5']
+      self.assertEquals('sumkledi', router.nickname)
+      self.assertEquals('0013D22389CD50D0B784A3E4061CB31E8CE8CEB5', router.fingerprint)
+      self.assertEquals('0799F806200B005F01E40A9A7F1A21C988AE8FB1', router.digest)
+      self.assertEquals(datetime.datetime(2012, 7, 11, 4, 22, 53), router.published)
+      self.assertEquals('178.218.213.229', router.address)
+      self.assertEquals(80, router.or_port)
+      self.assertEquals(None, router.dir_port)
+
+      authority = document.directory_authorities[0]
+      self.assertEquals(1, len(document.directory_authorities))
+      self.assertEquals('turtles', authority.nickname)
+      self.assertEquals('27B6B5996C426270A5C95488AA5BCEB6BCC86956', authority.fingerprint)
+      self.assertEquals('76.73.17.194', authority.hostname)
+      self.assertEquals('76.73.17.194', authority.address)
+      self.assertEquals(9030, authority.dir_port)
+      self.assertEquals(9090, authority.or_port)
+      self.assertEquals('Mike Perry <email>', authority.contact)
+      self.assertEquals(None, authority.vote_digest)
+      self.assertEquals(None, authority.legacy_dir_key)
+
+      certificate = authority.key_certificate
+      self.assertEquals(3, certificate.version)
+      self.assertEquals(None, certificate.address)
+      self.assertEquals(None, certificate.dir_port)
+      self.assertEquals('27B6B5996C426270A5C95488AA5BCEB6BCC86956', certificate.fingerprint)
+      self.assertEquals(expected_identity_key, certificate.identity_key)
+      self.assertEquals(datetime.datetime(2011, 11, 28, 21, 51, 4), certificate.published)
+      self.assertEquals(datetime.datetime(2012, 11, 28, 21, 51, 4), certificate.expires)
+      self.assertEquals(expected_signing_key, certificate.signing_key)
+      self.assertEquals(expected_key_crosscert, certificate.crosscert)
+      self.assertEquals(expected_key_certification, certificate.certification)
+
+      signature = document.signatures[0]
+      self.assertEquals(1, len(document.signatures))
+      self.assertEquals('sha1', signature.method)
+      self.assertEquals('27B6B5996C426270A5C95488AA5BCEB6BCC86956', signature.identity)
+      self.assertEquals('D5C30C15BB3F1DA27669C2D88439939E8F418FCF', signature.key_digest)
+      self.assertEquals(expected_signature, signature.signature)
+
   def test_minimal_consensus(self):
     """
     Parses a minimal network status document.
diff --git a/test/unit/descriptor/reader.py b/test/unit/descriptor/reader.py
index 34cd8ce..f19c0ec 100644
--- a/test/unit/descriptor/reader.py
+++ b/test/unit/descriptor/reader.py
@@ -2,10 +2,22 @@
 Unit tests for stem.descriptor.reader.
 """
 
+import getpass
 import io
+import os
+import shutil
+import signal
+import sys
+import tarfile
+import tempfile
+import time
 import unittest
 
 import stem.descriptor.reader
+import test.runner
+import test.unit.descriptor
+
+from stem.util import system
 
 try:
   # added in python 3.3
@@ -13,8 +25,64 @@ try:
 except ImportError:
   from mock import patch
 
+BASIC_LISTING = """
+/tmp 123
+/bin/grep 4567
+/file with spaces/and \\ stuff 890
+"""
+
+my_dir = os.path.dirname(__file__)
+DESCRIPTOR_TEST_DATA = os.path.join(my_dir, 'data')
+
+TAR_DESCRIPTORS = None
+
+
+def _get_raw_tar_descriptors():
+  global TAR_DESCRIPTORS
+
+  if not TAR_DESCRIPTORS:
+    test_path = os.path.join(DESCRIPTOR_TEST_DATA, 'descriptor_archive.tar')
+    raw_descriptors = []
+
+    # TODO: revert to using the 'with' keyword for this when dropping python
+    # 2.6 support
+
+    tar_file = None
+
+    try:
+      tar_file = tarfile.open(test_path)
+
+      for tar_entry in tar_file:
+        if tar_entry.isfile():
+          entry = tar_file.extractfile(tar_entry)
+          entry.readline()  # strip header
+          raw_descriptors.append(entry.read().decode('utf-8', 'replace'))
+          entry.close()
+    finally:
+      if tar_file:
+        tar_file.close()
+
+    TAR_DESCRIPTORS = raw_descriptors
+
+  return TAR_DESCRIPTORS
+
+
+class SkipListener:
+  def __init__(self):
+    self.results = []  # (path, exception) tuples that we've received
+
+  def listener(self, path, exception):
+    self.results.append((path, exception))
+
 
 class TestDescriptorReader(unittest.TestCase):
+  def setUp(self):
+    self.temp_directory = tempfile.mkdtemp()
+    self.test_listing_path = os.path.join(self.temp_directory, 'descriptor_processed_files')
+
+  def tearDown(self):
+    shutil.rmtree(self.temp_directory)
+
   @patch('stem.descriptor.reader.open', create = True)
   def test_load_processed_files(self, open_mock):
     """
@@ -90,3 +158,483 @@ class TestDescriptorReader(unittest.TestCase):
 
     open_mock.return_value = io.StringIO(u'/dir/file 123a')
     self.assertRaises(TypeError, stem.descriptor.reader.load_processed_files, '')
+
+  def test_load_processed_files_from_data(self):
+    """
+    Basic sanity test for loading a processed files listing from disk.
+    """
+
+    test_listing_path = self._make_processed_files_listing(BASIC_LISTING)
+    loaded_listing = stem.descriptor.reader.load_processed_files(test_listing_path)
+
+    expected_listing = {
+      '/tmp': 123,
+      '/bin/grep': 4567,
+      '/file with spaces/and \\ stuff': 890,
+    }
+
+    self.assertEquals(expected_listing, loaded_listing)
+
+  def test_load_processed_files_missing(self):
+    """
+    Tests the load_processed_files() function with a file that doesn't exist.
+    """
+
+    self.assertRaises(IOError, stem.descriptor.reader.load_processed_files, '/non-existant/path')
+
+  def test_load_processed_files_permissions(self):
+    """
+    Tests the load_processed_files() function with a file that can't be read
+    due to permissions.
+    """
+
+    # test relies on being unable to read a file
+
+    if getpass.getuser() == 'root':
+      test.runner.skip(self, '(running as root)')
+      return
+
+    # Skip the test on windows, since you can only set the file's
+    # read-only flag with os.chmod(). For more information see...
+    # http://docs.python.org/library/os.html#os.chmod
+
+    if system.is_windows():
+      test.runner.skip(self, '(chmod not functional)')
+
+    test_listing_path = self._make_processed_files_listing(BASIC_LISTING)
+    os.chmod(test_listing_path, 0077)  # remove read permissions
+    self.assertRaises(IOError, stem.descriptor.reader.load_processed_files, test_listing_path)
+
+  def test_save_processed_files(self):
+    """
+    Basic sanity test for persisting files listings to disk.
+    """
+
+    initial_listing = {
+      '/tmp': 123,
+      '/bin/grep': 4567,
+      '/file with spaces/and \\ stuff': 890,
+    }
+
+    # saves the initial_listing to a file then reloads it
+
+    stem.descriptor.reader.save_processed_files(self.test_listing_path, initial_listing)
+    loaded_listing = stem.descriptor.reader.load_processed_files(self.test_listing_path)
+
+    self.assertEquals(initial_listing, loaded_listing)
+
+  def test_save_processed_files_malformed(self):
+    """
+    Tests the save_processed_files() function with malformed data.
+    """
+
+    missing_filename = {'': 123}
+    relative_filename = {'foobar': 123}
+    string_timestamp = {'/tmp': '123a'}
+
+    for listing in (missing_filename, relative_filename, string_timestamp):
+      self.assertRaises(TypeError, stem.descriptor.reader.save_processed_files, '/tmp/foo', listing)
+
+    # Though our attempts to save the processed files fail we'll write an empty
+    # file. Cleaning it up.
+
+    try:
+      os.remove('/tmp/foo')
+    except:
+      pass
+
+  def test_basic_example(self):
+    """
+    Exercises something similar to the first example in the header
+    documentation, checking that some of the contents match what we'd expect.
+    """
+
+    # snag some of the plaintext descriptors so we can later make sure that we
+    # iterate over them
+
+    descriptor_entries = []
+
+    descriptor_path = os.path.join(DESCRIPTOR_TEST_DATA, 'example_descriptor')
+
+    with open(descriptor_path) as descriptor_file:
+      descriptor_file.readline()  # strip header
+      descriptor_entries.append(descriptor_file.read())
+
+    # running this test multiple times to flush out concurrency issues
+
+    for _ in xrange(15):
+      remaining_entries = list(descriptor_entries)
+
+      with stem.descriptor.reader.DescriptorReader(descriptor_path) as reader:
+        for descriptor in reader:
+          descriptor_str = str(descriptor)
+
+          if descriptor_str in remaining_entries:
+            remaining_entries.remove(descriptor_str)
+          else:
+            # iterator is providing output that we didn't expect
+            self.fail()
+
+      # check that we've seen all of the descriptor_entries
+      self.assertTrue(len(remaining_entries) == 0)
+
+  def test_multiple_runs(self):
+    """
+    Runs a DescriptorReader instance multiple times over the same content,
+    making sure that it can be used repeatedly.
+    """
+
+    descriptor_path = os.path.join(DESCRIPTOR_TEST_DATA, 'example_descriptor')
+    reader = stem.descriptor.reader.DescriptorReader(descriptor_path)
+
+    with reader:
+      self.assertEquals(1, len(list(reader)))
+
+    # run it a second time, this shouldn't provide any descriptors because we
+    # have already read it
+
+    with reader:
+      self.assertEquals(0, len(list(reader)))
+
+    # clear the DescriptorReader's memory of seeing the file and run it again
+
+    reader.set_processed_files([])
+
+    with reader:
+      self.assertEquals(1, len(list(reader)))
+
+  def test_buffer_size(self):
+    """
+    Checks that we can process sets of descriptors larger than our buffer size,
+    that we don't exceed it, and that we can still stop midway through reading
+    them.
+    """
+
+    reader = stem.descriptor.reader.DescriptorReader(DESCRIPTOR_TEST_DATA, buffer_size = 2)
+
+    with reader:
+      self.assertTrue(reader.get_buffered_descriptor_count() <= 2)
+      time.sleep(0.01)
+      self.assertTrue(reader.get_buffered_descriptor_count() <= 2)
+
+  def test_persistence_path(self):
+    """
+    Check that the persistence_path argument loads and saves a a processed
+    files listing.
+    """
+
+    descriptor_path = os.path.join(DESCRIPTOR_TEST_DATA, 'example_descriptor')
+
+    # First run where the persistence_path doesn't yet exist. This just tests
+    # the saving functionality.
+
+    reader = stem.descriptor.reader.DescriptorReader(descriptor_path, persistence_path = self.test_listing_path)
+
+    with reader:
+      self.assertEqual(1, len(list(reader)))
+
+    # check that we've saved reading example_descriptor
+    self.assertTrue(os.path.exists(self.test_listing_path))
+
+    with open(self.test_listing_path) as persistence_file:
+      persistance_file_contents = persistence_file.read()
+      self.assertTrue(persistance_file_contents.startswith(descriptor_path))
+
+    # Try running again with a new reader but the same persistance path, if it
+    # reads and takes the persistence_path into account then it won't read the
+    # descriptor file. This in essence just tests its loading functionality.
+
+    reader = stem.descriptor.reader.DescriptorReader(descriptor_path, persistence_path = self.test_listing_path)
+
+    with reader:
+      self.assertEqual(0, len(list(reader)))
+
+  def test_archived_paths(self):
+    """
+    Checks the get_path() and get_archive_path() for a tarball.
+    """
+
+    expected_archive_paths = (
+      'descriptor_archive/0/2/02c311d3d789f3f55c0880b5c85f3c196343552c',
+      'descriptor_archive/1/b/1bb798cae15e21479db0bc700767eee4733e9d4a',
+      'descriptor_archive/1/b/1ef75fef564180d8b3f72c6f8635ff0cd855f92c',
+    )
+
+    test_path = os.path.join(DESCRIPTOR_TEST_DATA, 'descriptor_archive.tar')
+
+    with stem.descriptor.reader.DescriptorReader(test_path) as reader:
+      for desc in reader:
+        self.assertEqual(test_path, desc.get_path())
+        self.assertTrue(desc.get_archive_path() in expected_archive_paths)
+
+  def test_archived_uncompressed(self):
+    """
+    Checks that we can read descriptors from an uncompressed archive.
+    """
+
+    expected_results = _get_raw_tar_descriptors()
+    test_path = os.path.join(DESCRIPTOR_TEST_DATA, 'descriptor_archive.tar')
+
+    with stem.descriptor.reader.DescriptorReader(test_path) as reader:
+      read_descriptors = [str(desc) for desc in list(reader)]
+      self.assertEquals(expected_results, read_descriptors)
+
+  def test_archived_gzip(self):
+    """
+    Checks that we can read descriptors from a gzipped archive.
+    """
+
+    expected_results = _get_raw_tar_descriptors()
+    test_path = os.path.join(DESCRIPTOR_TEST_DATA, 'descriptor_archive.tar.gz')
+
+    with stem.descriptor.reader.DescriptorReader(test_path) as reader:
+      read_descriptors = [str(desc) for desc in list(reader)]
+      self.assertEquals(expected_results, read_descriptors)
+
+  def test_archived_bz2(self):
+    """
+    Checks that we can read descriptors from an bzipped archive.
+    """
+
+    expected_results = _get_raw_tar_descriptors()
+    test_path = os.path.join(DESCRIPTOR_TEST_DATA, 'descriptor_archive.tar.bz2')
+
+    with stem.descriptor.reader.DescriptorReader(test_path) as reader:
+      read_descriptors = [str(desc) for desc in list(reader)]
+      self.assertEquals(expected_results, read_descriptors)
+
+  def test_stop(self):
+    """
+    Runs a DescriptorReader over the root directory, then checks that calling
+    stop() makes it terminate in a timely fashion.
+    """
+
+    # Skip on windows since SIGALRM is unavailable
+
+    if system.is_windows():
+      test.runner.skip(self, '(SIGALRM unavailable)')
+
+    is_test_running = True
+    reader = stem.descriptor.reader.DescriptorReader('/usr')
+
+    # Fails the test after a couple seconds if we don't finish successfully.
+    # Depending on what we're blocked on this might not work when the test
+    # fails, requiring that we give a manual kill to the test.
+
+    def timeout_handler(signum, frame):
+      if is_test_running:
+        self.fail()
+
+    signal.signal(signal.SIGALRM, timeout_handler)
+    signal.alarm(2)
+
+    reader.start()
+    time.sleep(0.1)
+    reader.stop()
+    is_test_running = False
+
+  def test_get_processed_files(self):
+    """
+    Checks that get_processed_files() provides the expected results after
+    iterating over our test data.
+    """
+
+    expected_results = {}
+
+    for root, _, files in os.walk(DESCRIPTOR_TEST_DATA):
+      for filename in files:
+        path = os.path.join(root, filename)
+        last_modified = int(os.stat(path).st_mtime)
+        expected_results[path] = last_modified
+
+    reader = stem.descriptor.reader.DescriptorReader(DESCRIPTOR_TEST_DATA)
+
+    with reader:
+      list(reader)  # iterates over all of the descriptors
+
+    self.assertEquals(expected_results, reader.get_processed_files())
+
+  def test_skip_nondescriptor_contents(self):
+    """
+    Checks that the reader properly reports when it skips both binary and
+    plaintext non-descriptor files.
+    """
+
+    skip_listener = SkipListener()
+    reader = stem.descriptor.reader.DescriptorReader(DESCRIPTOR_TEST_DATA)
+    reader.register_skip_listener(skip_listener.listener)
+
+    expected_skip_files = ('riddle', 'tiny.png', 'vote', 'new_metrics_type')
+
+    with reader:
+      list(reader)  # iterates over all of the descriptors
+
+    # strip anything with a .swp suffix (vim tmp files)
+
+    skip_listener.results = [(path, exc) for (path, exc) in skip_listener.results if not path.endswith('.swp')]
+
+    if len(skip_listener.results) != len(expected_skip_files):
+      expected_label = ',\n  '.join(expected_skip_files)
+      results_label = ',\n  '.join(['%s (%s)' % (path, exc) for (path, exc) in skip_listener.results])
+
+      self.fail('Skipped files that we should have been able to parse.\n\nExpected:\n  %s\n\nResult:\n  %s' % (expected_label, results_label))
+
+    for skip_path, skip_exception in skip_listener.results:
+      if not os.path.basename(skip_path) in expected_skip_files:
+        self.fail('Unexpected non-descriptor content: %s' % skip_path)
+
+      self.assertTrue(isinstance(skip_exception, stem.descriptor.reader.UnrecognizedType))
+
+  def test_skip_listener_already_read(self):
+    """
+    Checks that calling set_processed_files() prior to reading makes us skip
+    those files. This also doubles for testing that skip listeners are notified
+    of files that we've already read.
+    """
+
+    # path that we want the DescriptorReader to skip
+
+    test_path = os.path.join(DESCRIPTOR_TEST_DATA, 'example_descriptor')
+    initial_processed_files = {test_path: sys.maxint}
+
+    skip_listener = SkipListener()
+    reader = stem.descriptor.reader.DescriptorReader(test_path)
+    reader.register_skip_listener(skip_listener.listener)
+    reader.set_processed_files(initial_processed_files)
+
+    self.assertEquals(initial_processed_files, reader.get_processed_files())
+
+    with reader:
+      list(reader)  # iterates over all of the descriptors
+
+    self.assertEquals(1, len(skip_listener.results))
+
+    skipped_path, skip_exception = skip_listener.results[0]
+    self.assertEqual(test_path, skipped_path)
+    self.assertTrue(isinstance(skip_exception, stem.descriptor.reader.AlreadyRead))
+    self.assertEqual(sys.maxint, skip_exception.last_modified_when_read)
+
+  def test_skip_listener_unrecognized_type(self):
+    """
+    Listens for a file that's skipped because its file type isn't recognized.
+    """
+
+    # types are solely based on file extensions so making something that looks
+    # like an png image
+
+    test_path = os.path.join(self.temp_directory, 'test.png')
+
+    try:
+      test_file = open(test_path, 'w')
+      test_file.write('test data for test_skip_listener_unrecognized_type()')
+      test_file.close()
+
+      skip_listener = SkipListener()
+      reader = stem.descriptor.reader.DescriptorReader(test_path)
+      reader.register_skip_listener(skip_listener.listener)
+
+      with reader:
+        list(reader)  # iterates over all of the descriptors
+
+      self.assertEqual(1, len(skip_listener.results))
+
+      skipped_path, skip_exception = skip_listener.results[0]
+      self.assertEqual(test_path, skipped_path)
+      self.assertTrue(isinstance(skip_exception, stem.descriptor.reader.UnrecognizedType))
+      self.assertTrue(skip_exception.mime_type in (('image/png', None), ('image/x-png', None)))
+    finally:
+      if os.path.exists(test_path):
+        os.remove(test_path)
+
+  def test_skip_listener_read_failure(self):
+    """
+    Listens for a file that's skipped because we lack read permissions.
+    """
+
+    # test relies on being unable to read a file
+
+    if getpass.getuser() == 'root':
+      test.runner.skip(self, '(running as root)')
+      return
+    elif system.is_windows():
+      test.runner.skip(self, '(chmod not functional)')
+      return
+
+    test_path = os.path.join(self.temp_directory, 'secret_file')
+
+    try:
+      test_file = open(test_path, 'w')
+      test_file.write('test data for test_skip_listener_unrecognized_type()')
+      test_file.close()
+
+      os.chmod(test_path, 0077)  # remove read permissions
+
+      skip_listener = SkipListener()
+      reader = stem.descriptor.reader.DescriptorReader(test_path)
+      reader.register_skip_listener(skip_listener.listener)
+
+      with reader:
+        list(reader)  # iterates over all of the descriptors
+
+      self.assertEqual(1, len(skip_listener.results))
+
+      skipped_path, skip_exception = skip_listener.results[0]
+      self.assertEqual(test_path, skipped_path)
+      self.assertTrue(isinstance(skip_exception, stem.descriptor.reader.ReadFailed))
+      self.assertTrue(isinstance(skip_exception.exception, IOError))
+    finally:
+      if os.path.exists(test_path):
+        os.remove(test_path)
+
+  def test_skip_listener_file_missing(self):
+    """
+    Listens for a file that's skipped because the file doesn't exist.
+    """
+
+    test_path = '/non-existant/path'
+
+    skip_listener = SkipListener()
+    reader = stem.descriptor.reader.DescriptorReader(test_path)
+    reader.register_skip_listener(skip_listener.listener)
+
+    with reader:
+      list(reader)  # iterates over all of the descriptors
+
+    self.assertEqual(1, len(skip_listener.results))
+
+    skipped_path, skip_exception = skip_listener.results[0]
+    self.assertEqual(test_path, skipped_path)
+    self.assertTrue(isinstance(skip_exception, stem.descriptor.reader.FileMissing))
+
+  def test_unrecognized_metrics_type(self):
+    """
+    Parses a file that has a valid metrics header, but an unrecognized type.
+    """
+
+    test_path = test.unit.descriptor.get_resource('new_metrics_type')
+
+    skip_listener = SkipListener()
+    reader = stem.descriptor.reader.DescriptorReader(test_path)
+    reader.register_skip_listener(skip_listener.listener)
+
+    with reader:
+      list(reader)  # iterates over all of the descriptors
+
+    self.assertEqual(1, len(skip_listener.results))
+
+    skipped_path, skip_exception = skip_listener.results[0]
+    self.assertEqual(test_path, skipped_path)
+    self.assertTrue(isinstance(skip_exception, stem.descriptor.reader.UnrecognizedType))
+    self.assertEqual((None, None), skip_exception.mime_type)
+
+  def _make_processed_files_listing(self, contents):
+    """
+    Writes the given 'processed file' listing to disk, returning the path where
+    it is located.
+    """
+
+    test_listing_file = open(self.test_listing_path, 'w')
+    test_listing_file.write(contents)
+    test_listing_file.close()
+
+    return self.test_listing_path
diff --git a/test/unit/descriptor/server_descriptor.py b/test/unit/descriptor/server_descriptor.py
index 4d01219..c3f429a 100644
--- a/test/unit/descriptor/server_descriptor.py
+++ b/test/unit/descriptor/server_descriptor.py
@@ -4,6 +4,7 @@ Unit tests for stem.descriptor.server_descriptor.
 
 import datetime
 import io
+import tarfile
 import unittest
 
 import stem.descriptor.server_descriptor
@@ -18,14 +19,303 @@ from test.mocking import get_relay_server_descriptor, \
                          get_bridge_server_descriptor, \
                          CRYPTO_BLOB
 
+from test.unit.descriptor import get_resource
+
 try:
   # added in python 3.3
   from unittest.mock import Mock, patch
 except ImportError:
   from mock import Mock, patch
 
+TARFILE_FINGERPRINTS = set([
+  u'B6D83EC2D9E18B0A7A33428F8CFA9C536769E209',
+  u'E0BD57A11F00041A9789577C53A1B784473669E4',
+  u'1F43EE37A0670301AD9CB555D94AFEC2C89FDE86',
+])
+
 
 class TestServerDescriptor(unittest.TestCase):
+  def test_with_tarfile_path(self):
+    """
+    Fetch server descriptors via parse_file() for a tarfile path.
+    """
+
+    descriptors = list(stem.descriptor.parse_file(get_resource('descriptor_archive.tar')))
+    self.assertEqual(3, len(descriptors))
+
+    fingerprints = set([desc.fingerprint for desc in descriptors])
+    self.assertEqual(TARFILE_FINGERPRINTS, fingerprints)
+
+  def test_with_tarfile_object(self):
+    """
+    Fetch server descriptors via parse_file() for a tarfile object.
+    """
+
+    # TODO: When dropping python 2.6 support we can go back to using the 'with'
+    # keyword here.
+
+    tar_file = tarfile.open(get_resource('descriptor_archive.tar'))
+    descriptors = list(stem.descriptor.parse_file(tar_file))
+    self.assertEqual(3, len(descriptors))
+
+    fingerprints = set([desc.fingerprint for desc in descriptors])
+    self.assertEqual(TARFILE_FINGERPRINTS, fingerprints)
+    tar_file.close()
+
+  def test_metrics_descriptor(self):
+    """
+    Parses and checks our results against a server descriptor from metrics.
+    """
+
+    descriptor_file = open(get_resource('example_descriptor'), 'rb')
+
+    expected_family = set([
+      '$0CE3CFB1E9CC47B63EA8869813BF6FAB7D4540C1',
+      '$1FD187E8F69A9B74C9202DC16A25B9E7744AB9F6',
+      '$74FB5EFA6A46DE4060431D515DC9A790E6AD9A7C',
+      '$77001D8DA9BF445B0F81AA427A675F570D222E6A',
+      '$B6D83EC2D9E18B0A7A33428F8CFA9C536769E209',
+      '$D2F37F46182C23AB747787FD657E680B34EAF892',
+      '$E0BD57A11F00041A9789577C53A1B784473669E4',
+      '$E5E3E9A472EAF7BE9682B86E92305DB4C71048EF',
+    ])
+
+    expected_onion_key = """-----BEGIN RSA PUBLIC KEY-----
+MIGJAoGBAJv5IIWQ+WDWYUdyA/0L8qbIkEVH/cwryZWoIaPAzINfrw1WfNZGtBmg
+skFtXhOHHqTRN4GPPrZsAIUOQGzQtGb66IQgT4tO/pj+P6QmSCCdTfhvGfgTCsC+
+WPi4Fl2qryzTb3QO5r5x7T8OsG2IBUET1bLQzmtbC560SYR49IvVAgMBAAE=
+-----END RSA PUBLIC KEY-----"""
+
+    expected_signing_key = """-----BEGIN RSA PUBLIC KEY-----
+MIGJAoGBAKwvOXyztVKnuYvpTKt+nS3XIKeO8dVungi8qGoeS+6gkR6lDtGfBTjd
+uE9UIkdAl9zi8/1Ic2wsUNHE9jiS0VgeupITGZY8YOyMJJ/xtV1cqgiWhq1dUYaq
+51TOtUogtAPgXPh4J+V8HbFFIcCzIh3qCO/xXo+DSHhv7SSif1VpAgMBAAE=
+-----END RSA PUBLIC KEY-----"""
+
+    expected_signature = """-----BEGIN SIGNATURE-----
+dskLSPz8beUW7bzwDjR6EVNGpyoZde83Ejvau+5F2c6cGnlu91fiZN3suE88iE6e
+758b9ldq5eh5mapb8vuuV3uO+0Xsud7IEOqfxdkmk0GKnUX8ouru7DSIUzUL0zqq
+Qlx9HNCqCY877ztFRC624ja2ql6A2hBcuoYMbkHjcQ4=
+-----END SIGNATURE-----"""
+
+    desc = next(stem.descriptor.parse_file(descriptor_file, 'server-descriptor 1.0'))
+    self.assertEquals('caerSidi', desc.nickname)
+    self.assertEquals('A7569A83B5706AB1B1A9CB52EFF7D2D32E4553EB', desc.fingerprint)
+    self.assertEquals('71.35.133.197', desc.address)
+    self.assertEquals(9001, desc.or_port)
+    self.assertEquals(None, desc.socks_port)
+    self.assertEquals(None, desc.dir_port)
+    self.assertEquals(b'Tor 0.2.1.30 on Linux x86_64', desc.platform)
+    self.assertEquals(stem.version.Version('0.2.1.30'), desc.tor_version)
+    self.assertEquals('Linux x86_64', desc.operating_system)
+    self.assertEquals(588217, desc.uptime)
+    self.assertEquals(datetime.datetime(2012, 3, 1, 17, 15, 27), desc.published)
+    self.assertEquals(b'www.atagar.com/contact', desc.contact)
+    self.assertEquals(['1', '2'], desc.link_protocols)
+    self.assertEquals(['1'], desc.circuit_protocols)
+    self.assertEquals(False, desc.hibernating)
+    self.assertEquals(False, desc.allow_single_hop_exits)
+    self.assertEquals(False, desc.extra_info_cache)
+    self.assertEquals('D225B728768D7EA4B5587C13A7A9D22EBBEE6E66', desc.extra_info_digest)
+    self.assertEquals(['2'], desc.hidden_service_dir)
+    self.assertEquals(expected_family, desc.family)
+    self.assertEquals(153600, desc.average_bandwidth)
+    self.assertEquals(256000, desc.burst_bandwidth)
+    self.assertEquals(104590, desc.observed_bandwidth)
+    self.assertEquals(stem.exit_policy.ExitPolicy('reject *:*'), desc.exit_policy)
+    self.assertEquals(expected_onion_key, desc.onion_key)
+    self.assertEquals(expected_signing_key, desc.signing_key)
+    self.assertEquals(expected_signature, desc.signature)
+    self.assertEquals([], desc.get_unrecognized_lines())
+    self.assertEquals('2C7B27BEAB04B4E2459D89CA6D5CD1CC5F95A689', desc.digest())
+
+  def test_metrics_descriptor_multiple(self):
+    """
+    Parses and checks our results against a server descriptor from metrics.
+    """
+
+    with open(get_resource('metrics_server_desc_multiple'), 'rb') as descriptor_file:
+      descriptors = list(stem.descriptor.parse_file(descriptor_file, 'server-descriptor 1.0'))
+
+      self.assertEquals(2, len(descriptors))
+
+      self.assertEquals('anonion', descriptors[0].nickname)
+      self.assertEquals('9A5EC5BB866517E53962AF4D3E776536694B069E', descriptors[0].fingerprint)
+
+      self.assertEquals('Unnamed', descriptors[1].nickname)
+      self.assertEquals('5366F1D198759F8894EA6E5FF768C667F59AFD24', descriptors[1].fingerprint)
+
+  def test_old_descriptor(self):
+    """
+    Parses a relay server descriptor from 2005.
+    """
+
+    descriptor_file = open(get_resource('old_descriptor'), 'rb')
+
+    desc = next(stem.descriptor.parse_file(descriptor_file, 'server-descriptor 1.0'))
+    self.assertEquals('krypton', desc.nickname)
+    self.assertEquals('3E2F63E2356F52318B536A12B6445373808A5D6C', desc.fingerprint)
+    self.assertEquals('212.37.39.59', desc.address)
+    self.assertEquals(8000, desc.or_port)
+    self.assertEquals(None, desc.socks_port)
+    self.assertEquals(None, desc.dir_port)
+    self.assertEquals(b'Tor 0.1.0.14 on FreeBSD i386', desc.platform)
+    self.assertEquals(stem.version.Version('0.1.0.14'), desc.tor_version)
+    self.assertEquals('FreeBSD i386', desc.operating_system)
+    self.assertEquals(64820, desc.uptime)
+    self.assertEquals(datetime.datetime(2005, 12, 16, 18, 1, 3), desc.published)
+    self.assertEquals(None, desc.contact)
+    self.assertEquals(None, desc.link_protocols)
+    self.assertEquals(None, desc.circuit_protocols)
+    self.assertEquals(True, desc.hibernating)
+    self.assertEquals(False, desc.allow_single_hop_exits)
+    self.assertEquals(False, desc.extra_info_cache)
+    self.assertEquals(None, desc.extra_info_digest)
+    self.assertEquals(None, desc.hidden_service_dir)
+    self.assertEquals(set(), desc.family)
+    self.assertEquals(102400, desc.average_bandwidth)
+    self.assertEquals(10485760, desc.burst_bandwidth)
+    self.assertEquals(0, desc.observed_bandwidth)
+    self.assertEquals(datetime.datetime(2005, 12, 16, 18, 0, 48), desc.read_history_end)
+    self.assertEquals(900, desc.read_history_interval)
+    self.assertEquals(datetime.datetime(2005, 12, 16, 18, 0, 48), desc.write_history_end)
+    self.assertEquals(900, desc.write_history_interval)
+    self.assertEquals([], desc.get_unrecognized_lines())
+
+    # The read-history and write-history lines are pretty long so just checking
+    # the initial contents for the line and parsed values.
+
+    read_values_start = [20774, 489973, 510022, 511163, 20949]
+    self.assertEquals(read_values_start, desc.read_history_values[:5])
+
+    write_values_start = [81, 8848, 8927, 8927, 83, 8848, 8931, 8929, 81, 8846]
+    self.assertEquals(write_values_start, desc.write_history_values[:10])
+
+  def test_non_ascii_descriptor(self):
+    """
+    Parses a descriptor with non-ascii content.
+    """
+
+    descriptor_file = open(get_resource('non-ascii_descriptor'), 'rb')
+
+    expected_contact = b'1024D/04D2E818 L\xc3\xa9na\xc3\xafc Huard <lenaic dot huard AT laposte dot net>'
+
+    desc = next(stem.descriptor.parse_file(descriptor_file, 'server-descriptor 1.0'))
+    self.assertEquals('Coruscant', desc.nickname)
+    self.assertEquals('0B9821545C48E496AEED9ECC0DB506C49FF8158D', desc.fingerprint)
+    self.assertEquals('88.182.161.122', desc.address)
+    self.assertEquals(9001, desc.or_port)
+    self.assertEquals(None, desc.socks_port)
+    self.assertEquals(9030, desc.dir_port)
+    self.assertEquals(b'Tor 0.2.3.25 on Linux', desc.platform)
+    self.assertEquals(stem.version.Version('0.2.3.25'), desc.tor_version)
+    self.assertEquals('Linux', desc.operating_system)
+    self.assertEquals(259738, desc.uptime)
+    self.assertEquals(datetime.datetime(2013, 5, 18, 11, 16, 19), desc.published)
+    self.assertEquals(expected_contact, desc.contact)
+    self.assertEquals(['1', '2'], desc.link_protocols)
+    self.assertEquals(['1'], desc.circuit_protocols)
+    self.assertEquals(False, desc.hibernating)
+    self.assertEquals(False, desc.allow_single_hop_exits)
+    self.assertEquals(False, desc.extra_info_cache)
+    self.assertEquals('56403D838DE152421CD401B8E57DAD4483A3D56B', desc.extra_info_digest)
+    self.assertEquals(['2'], desc.hidden_service_dir)
+    self.assertEquals(set(), desc.family)
+    self.assertEquals(102400, desc.average_bandwidth)
+    self.assertEquals(204800, desc.burst_bandwidth)
+    self.assertEquals(122818, desc.observed_bandwidth)
+    self.assertEquals(stem.exit_policy.ExitPolicy('reject *:*'), desc.exit_policy)
+    self.assertEquals([], desc.get_unrecognized_lines())
+
+    # Make sure that we can get a string representation for this descriptor
+    # (having non-unicode content risks a UnicodeEncodeError)...
+    #
+    # https://trac.torproject.org/8265
+
+    self.assertTrue(isinstance(str(desc), str))
+
+  def test_cr_in_contact_line(self):
+    """
+    Parses a descriptor with a huge contact line containing anomalous carriage
+    returns ('\r' entries).
+    """
+
+    descriptor_file = open(get_resource('cr_in_contact_line'), 'rb')
+    desc = next(stem.descriptor.parse_file(descriptor_file, 'server-descriptor 1.0'))
+
+    self.assertEquals('pogonip', desc.nickname)
+    self.assertEquals('6DABD62BC65D4E6FE620293157FC76968DAB9C9B', desc.fingerprint)
+    self.assertEquals('75.5.248.48', desc.address)
+
+    # the contact info block is huge so just checking the start and end,
+    # including some of the embedded carriage returns
+
+    contact_start = b'jie1 at pacbell dot net -----BEGIN PGP PUBLIC KEY BLOCK-----\rVersion:'
+    contact_end = b'YFRk3NhCY=\r=Xaw3\r-----END PGP PUBLIC KEY BLOCK-----'
+
+    self.assertTrue(desc.contact.startswith(contact_start))
+    self.assertTrue(desc.contact.endswith(contact_end))
+
+  def test_negative_uptime(self):
+    """
+    Parses a descriptor where we are tolerant of a negative uptime, and another
+    where we shouldn't be.
+    """
+
+    descriptor_file = open(get_resource('negative_uptime'), 'rb')
+    desc = next(stem.descriptor.parse_file(descriptor_file, 'server-descriptor 1.0'))
+
+    self.assertEquals('TipTor', desc.nickname)
+    self.assertEquals('137962D4931DBF08A24E843288B8A155D6D2AEDD', desc.fingerprint)
+    self.assertEquals('62.99.247.83', desc.address)
+
+    # modify the relay version so it's after when the negative uptime bug
+    # should appear
+
+    descriptor_contents = str(desc).replace('Tor 0.1.1.25', 'Tor 0.1.2.7')
+    self.assertRaises(ValueError, stem.descriptor.server_descriptor.RelayDescriptor, descriptor_contents)
+
+  def test_bridge_descriptor(self):
+    """
+    Parses a bridge descriptor.
+    """
+
+    descriptor_file = open(get_resource('bridge_descriptor'), 'rb')
+
+    expected_family = set([
+      '$CE396C72A3D0880F74C064FEA79D68C15BD380B9',
+      '$AB8B00C00B1347BA80A88E548FAC9EDF701D7D0E',
+      '$8C8A470D7C23151665A7B84E75E89FCC205A3304',
+    ])
+
+    desc = next(stem.descriptor.parse_file(descriptor_file, 'bridge-server-descriptor 1.0'))
+    self.assertEquals('Unnamed', desc.nickname)
+    self.assertEquals('AE54E28ED069CDF45F3009F963EE3B3D6FA26A2E', desc.fingerprint)
+    self.assertEquals('10.45.227.253', desc.address)
+    self.assertEquals(9001, desc.or_port)
+    self.assertEquals(None, desc.socks_port)
+    self.assertEquals(None, desc.dir_port)
+    self.assertEquals(b'Tor 0.2.3.12-alpha (git-800942b4176ca31c) on Linux x86_64', desc.platform)
+    self.assertEquals(stem.version.Version('0.2.3.12-alpha'), desc.tor_version)
+    self.assertEquals('Linux x86_64', desc.operating_system)
+    self.assertEquals(186, desc.uptime)
+    self.assertEquals(datetime.datetime(2012, 3, 22, 17, 34, 38), desc.published)
+    self.assertEquals(b'somebody', desc.contact)
+    self.assertEquals(['1', '2'], desc.link_protocols)
+    self.assertEquals(['1'], desc.circuit_protocols)
+    self.assertEquals(False, desc.hibernating)
+    self.assertEquals(False, desc.allow_single_hop_exits)
+    self.assertEquals(False, desc.extra_info_cache)
+    self.assertEquals('134F81F7A0D270B85FCD481DD10CEA34BA7B15C9', desc.extra_info_digest)
+    self.assertEquals(['2'], desc.hidden_service_dir)
+    self.assertEquals(expected_family, desc.family)
+    self.assertEquals(409600, desc.average_bandwidth)
+    self.assertEquals(819200, desc.burst_bandwidth)
+    self.assertEquals(5120, desc.observed_bandwidth)
+    self.assertEquals(stem.exit_policy.ExitPolicy('reject *:*'), desc.exit_policy)
+    self.assertEquals('006FD96BA35E7785A6A3B8B75FE2E2435A13BDB4', desc.digest())
+    self.assertEquals([], desc.get_unrecognized_lines())
+
   def test_minimal_relay_descriptor(self):
     """
     Basic sanity check that we can parse a relay server descriptor with minimal



More information about the tor-commits mailing list