commit b514fafccaa6d42d823f9b528d7335b85422c1cc
Author: Damian Johnson <atagar(a)torproject.org>
Date: Thu Jan 11 09:55:22 2018 -0800
Test fallback persistence
Adding a unit test to test persistance and reloading fallbacks. As expected,
presently doesn't have the new fields.
---
cache_fallback_directories.py | 18 +--------------
stem/descriptor/remote.py | 37 ++++++++++++++++++++++++++++--
stem/util/conf.py | 7 ++++++
test/unit/descriptor/remote.py | 52 +++++++++++++++++++++++++++++++++++++++++-
4 files changed, 94 insertions(+), 20 deletions(-)
diff --git a/cache_fallback_directories.py b/cache_fallback_directories.py
index 0e61cf21..c5926c8c 100755
--- a/cache_fallback_directories.py
+++ b/cache_fallback_directories.py
@@ -10,7 +10,6 @@ import re
import sys
import stem.descriptor.remote
-import stem.util.conf
import stem.util.system
try:
@@ -49,19 +48,4 @@ if __name__ == '__main__':
print('Differences detected...\n')
print(stem.descriptor.remote._fallback_directory_differences(cached_fallback_directories, latest_fallback_directories))
-
- conf = stem.util.conf.Config()
- conf.set('tor_commit', fallback_dir_commit)
- conf.set('stem_commit', stem_commit)
-
- for directory in sorted(latest_fallback_directories.values(), key = lambda x: x.fingerprint):
- fingerprint = directory.fingerprint
- conf.set('%s.address' % fingerprint, directory.address)
- conf.set('%s.or_port' % fingerprint, str(directory.or_port))
- conf.set('%s.dir_port' % fingerprint, str(directory.dir_port))
-
- if directory.orport_v6:
- conf.set('%s.orport6_address' % fingerprint, str(directory.orport_v6[0]))
- conf.set('%s.orport6_port' % fingerprint, str(directory.orport_v6[1]))
-
- conf.save(stem.descriptor.remote.CACHE_PATH)
+ stem.descriptor.remote.FallbackDirectory._write(latest_fallback_directories, fallback_dir_commit, stem_commit)
diff --git a/stem/descriptor/remote.py b/stem/descriptor/remote.py
index 8f5e5cc3..453061e0 100644
--- a/stem/descriptor/remote.py
+++ b/stem/descriptor/remote.py
@@ -969,7 +969,7 @@ class FallbackDirectory(Directory):
self.header = header if header else {}
@staticmethod
- def from_cache():
+ def from_cache(path = CACHE_PATH):
"""
Provides fallback directory information cached with Stem. Unlike
:func:`~stem.descriptor.remote.FallbackDirectory.from_remote` this doesn't
@@ -977,12 +977,17 @@ class FallbackDirectory(Directory):
these fallback directories are only as up to date as the Stem release we're
using.
+ .. versionchanged:: 1.7.0
+ Added the path argument.
+
+ :param str path: cache file to load from
+
:returns: **dict** of **str** fingerprints to their
:class:`~stem.descriptor.remote.FallbackDirectory`
"""
conf = stem.util.conf.Config()
- conf.load(CACHE_PATH)
+ conf.load(path)
results = {}
@@ -1179,6 +1184,34 @@ class FallbackDirectory(Directory):
return section_lines
+ @staticmethod
+ def _write(fallbacks, tor_commit, stem_commit, path = CACHE_PATH):
+ """
+ Persists fallback directories to a location in a way that can be read by
+ from_cache().
+
+ :param dict fallbacks: mapping of fingerprints to their fallback directory
+ :param str tor_commit: tor commit the fallbacks came from
+ :param str stem_commit: stem commit the fallbacks came from
+ :param str path: location fallbacks will be persisted to
+ """
+
+ conf = stem.util.conf.Config()
+ conf.set('tor_commit', tor_commit)
+ conf.set('stem_commit', stem_commit)
+
+ for directory in sorted(fallbacks.values(), key = lambda x: x.fingerprint):
+ fingerprint = directory.fingerprint
+ conf.set('%s.address' % fingerprint, directory.address)
+ conf.set('%s.or_port' % fingerprint, str(directory.or_port))
+ conf.set('%s.dir_port' % fingerprint, str(directory.dir_port))
+
+ if directory.orport_v6:
+ conf.set('%s.orport6_address' % fingerprint, str(directory.orport_v6[0]))
+ conf.set('%s.orport6_port' % fingerprint, str(directory.orport_v6[1]))
+
+ conf.save(path)
+
def __hash__(self):
return _hash_attr(self, 'address', 'or_port', 'dir_port', 'fingerprint', 'nickname', 'has_extrainfo', 'orport_v6', 'header', parent = Directory)
diff --git a/stem/util/conf.py b/stem/util/conf.py
index d4af467d..a902cae1 100644
--- a/stem/util/conf.py
+++ b/stem/util/conf.py
@@ -447,6 +447,9 @@ class Config(object):
#
# Information for what values fail to load and why are reported to
# 'stem.util.log'.
+
+ .. versionchanged:: 1.7.0
+ Class can now be used as a dictionary.
"""
def __init__(self):
@@ -768,3 +771,7 @@ class Config(object):
message_id = 'stem.util.conf.missing_config_key_%s' % key
log.log_once(message_id, log.TRACE, "config entry '%s' not found, defaulting to '%s'" % (key, default))
return default
+
+ def __getitem__(self, key):
+ with self._contents_lock:
+ return self._contents[key]
diff --git a/test/unit/descriptor/remote.py b/test/unit/descriptor/remote.py
index cc0d51b1..8bfdffc1 100644
--- a/test/unit/descriptor/remote.py
+++ b/test/unit/descriptor/remote.py
@@ -4,10 +4,12 @@ Unit tests for stem.descriptor.remote.
import io
import socket
+import tempfile
import unittest
-import stem.prereq
import stem.descriptor.remote
+import stem.prereq
+import stem.util.conf
try:
# added in python 3.3
@@ -236,6 +238,54 @@ class TestDescriptorDownloader(unittest.TestCase):
self.assertEqual(expected, fallback_directories)
+ def test_fallback_persistence(self):
+ header = {'type': 'fallback', 'version': '2.0.0', 'timestamp': '20170526090242'}
+
+ expected = {
+ '0756B7CD4DFC8182BE23143FAC0642F515182CEB': stem.descriptor.remote.FallbackDirectory(
+ address = '5.9.110.236',
+ or_port = 9001,
+ dir_port = 9030,
+ fingerprint = '0756B7CD4DFC8182BE23143FAC0642F515182CEB',
+ nickname = 'rueckgrat',
+ has_extrainfo = True,
+ orport_v6 = ('2a01:4f8:162:51e2::2', 9001),
+ header = header,
+ ),
+ '01A9258A46E97FF8B2CAC7910577862C14F2C524': stem.descriptor.remote.FallbackDirectory(
+ address = '193.171.202.146',
+ or_port = 9001,
+ dir_port = 9030,
+ fingerprint = '01A9258A46E97FF8B2CAC7910577862C14F2C524',
+ nickname = None,
+ has_extrainfo = False,
+ orport_v6 = None,
+ header = header,
+ ),
+ }
+
+ excepted_config = {
+ 'tor_commit': ['abc'],
+ 'stem_commit': ['def'],
+ '01A9258A46E97FF8B2CAC7910577862C14F2C524.address': ['193.171.202.146'],
+ '01A9258A46E97FF8B2CAC7910577862C14F2C524.or_port': ['9001'],
+ '01A9258A46E97FF8B2CAC7910577862C14F2C524.dir_port': ['9030'],
+ '0756B7CD4DFC8182BE23143FAC0642F515182CEB.address': ['5.9.110.236'],
+ '0756B7CD4DFC8182BE23143FAC0642F515182CEB.or_port': ['9001'],
+ '0756B7CD4DFC8182BE23143FAC0642F515182CEB.dir_port': ['9030'],
+ '0756B7CD4DFC8182BE23143FAC0642F515182CEB.orport6_address': ['2a01:4f8:162:51e2::2'],
+ '0756B7CD4DFC8182BE23143FAC0642F515182CEB.orport6_port': ['9001'],
+ }
+
+ with tempfile.NamedTemporaryFile(prefix = 'fallbacks.') as tmp:
+ stem.descriptor.remote.FallbackDirectory._write(expected, 'abc', 'def', tmp.name)
+
+ conf = stem.util.conf.Config()
+ conf.load(tmp.name)
+ self.assertEqual(excepted_config, dict(conf))
+
+ #self.assertEqual(expected, stem.descriptor.remote.FallbackDirectory.from_cache(tmp.name))
+
@patch(URL_OPEN)
def test_fallback_directories_from_remote_empty(self, urlopen_mock):
urlopen_mock.return_value = io.BytesIO('')