def test_is_running(self, call_mock): """ Exercises multiple use cases for the is_running function. """ # mock response with a linux and bsd resolver running_commands = [str_type('irssi'), str_type('moc'), str_type('tor'), str_type('ps'), str_type(' firefox ')] for ps_cmd in (system.IS_RUNNING_PS_LINUX, system.IS_RUNNING_PS_BSD): call_mock.side_effect = mock_call(ps_cmd, running_commands) self.assertTrue(system.is_running('irssi')) self.assertTrue(system.is_running('moc')) self.assertTrue(system.is_running('tor')) self.assertTrue(system.is_running('ps')) self.assertTrue(system.is_running('firefox')) self.assertEqual(False, system.is_running('something_else')) # mock both calls failing call_mock.return_value = None call_mock.side_effect = None self.assertFalse(system.is_running('irssi')) self.assertEqual(None, system.is_running('irssi'))
def test_is_running(self, call_mock): """ Exercises multiple use cases for the is_running function. """ # mock response with a linux and bsd resolver running_commands = [ str_type('irssi'), str_type('moc'), str_type('tor'), str_type('ps'), str_type(' firefox ') ] for ps_cmd in (system.IS_RUNNING_PS_LINUX, system.IS_RUNNING_PS_BSD): call_mock.side_effect = mock_call(ps_cmd, running_commands) self.assertTrue(system.is_running('irssi')) self.assertTrue(system.is_running('moc')) self.assertTrue(system.is_running('tor')) self.assertTrue(system.is_running('ps')) self.assertTrue(system.is_running('firefox')) self.assertEqual(False, system.is_running('something_else')) # mock both calls failing call_mock.return_value = None call_mock.side_effect = None self.assertFalse(system.is_running('irssi')) self.assertEqual(None, system.is_running('irssi'))
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.assertEqual(FIRST_ONION_KEY, router.onion_key) self.assertEqual(None, router.ntor_onion_key) self.assertEqual([], router.or_addresses) self.assertEqual([], router.family) self.assertEqual( stem.exit_policy.MicroExitPolicy('reject 1-65535'), router.exit_policy) self.assertEqual({b'@last-listed': b'2013-02-24 00:18:36'}, router.get_annotations()) self.assertEqual([b'@last-listed 2013-02-24 00:18:36'], router.get_annotation_lines()) router = next(descriptors) self.assertEqual(SECOND_ONION_KEY, router.onion_key) self.assertEqual( str_type('r5572HzD+PMPBbXlZwBhsm6YEbxnYgis8vhZ1jmdI2k='), router.ntor_onion_key) self.assertEqual([], router.or_addresses) self.assertEqual(['$6141629FA0D15A6AEAEF3A1BEB76E64C767B3174'], router.family) self.assertEqual( stem.exit_policy.MicroExitPolicy('reject 1-65535'), router.exit_policy) self.assertEqual({b'@last-listed': b'2013-02-24 00:18:37'}, router.get_annotations()) self.assertEqual([b'@last-listed 2013-02-24 00:18:37'], router.get_annotation_lines()) router = next(descriptors) self.assertEqual(THIRD_ONION_KEY, router.onion_key) self.assertEqual(None, router.ntor_onion_key) self.assertEqual([(str_type('2001:6b0:7:125::242'), 9001, True)], router.or_addresses) self.assertEqual([], router.family) self.assertEqual(stem.exit_policy.MicroExitPolicy('accept 80,443'), router.exit_policy) self.assertEqual({b'@last-listed': b'2013-02-24 00:18:36'}, router.get_annotations()) self.assertEqual([b'@last-listed 2013-02-24 00:18:36'], router.get_annotation_lines())
def test_load_processed_files(self, open_mock): """ Successful load of content. """ test_lines = ( str_type("/dir/ 0"), str_type("/dir/file 12345"), str_type("/dir/file with spaces 7138743"), str_type(" /dir/with extra space 12345 "), str_type(" \t "), str_type(""), str_type("/dir/after empty line 12345"), ) expected_value = { "/dir/": 0, "/dir/file": 12345, "/dir/file with spaces": 7138743, "/dir/with extra space": 12345, "/dir/after empty line": 12345, } open_mock.return_value = io.StringIO(str_type("\n".join(test_lines))) self.assertEqual(expected_value, stem.descriptor.reader.load_processed_files(""))
def test_load_processed_files(self, open_mock): """ Successful load of content. """ test_lines = ( str_type('/dir/ 0'), str_type('/dir/file 12345'), str_type('/dir/file with spaces 7138743'), str_type(' /dir/with extra space 12345 '), str_type(' \t '), str_type(''), str_type('/dir/after empty line 12345'), ) expected_value = { '/dir/': 0, '/dir/file': 12345, '/dir/file with spaces': 7138743, '/dir/with extra space': 12345, '/dir/after empty line': 12345, } open_mock.return_value = io.StringIO(str_type('\n'.join(test_lines))) self.assertEqual(expected_value, stem.descriptor.reader.load_processed_files(''))
def test_load_processed_files_empty(self, open_mock): """ Tests the load_processed_files() function with an empty file. """ open_mock.return_value = io.StringIO(str_type('')) self.assertEqual({}, stem.descriptor.reader.load_processed_files(''))
def test_load_processed_files_no_timestamp(self, open_mock): """ Tests the load_processed_files() function content that is malformed because it is missing the timestamp. """ open_mock.return_value = io.StringIO(str_type("/dir/file ")) self.assertRaises(TypeError, stem.descriptor.reader.load_processed_files, "")
def _parse_exit_policy(descriptor, entries): if hasattr(descriptor, '_unparsed_exit_policy'): if descriptor._unparsed_exit_policy == [str_type('reject *:*')]: descriptor.exit_policy = REJECT_ALL_POLICY else: descriptor.exit_policy = stem.exit_policy.ExitPolicy(*descriptor._unparsed_exit_policy) del descriptor._unparsed_exit_policy
def test_load_processed_files_malformed_timestamp(self, open_mock): """ Tests the load_processed_files() function content that is malformed because it has a non-numeric timestamp. """ open_mock.return_value = io.StringIO(str_type('/dir/file 123a')) self.assertRaises(TypeError, stem.descriptor.reader.load_processed_files, '')
def test_load_processed_files_malformed_file(self, open_mock): """ Tests the load_processed_files() function content that is malformed because it has an invalid file path. """ open_mock.return_value = io.StringIO(str_type('not_an_absolute_file 12345')) self.assertRaises(TypeError, stem.descriptor.reader.load_processed_files, '')
def _parse_exit_policy(descriptor, entries): if hasattr(descriptor, '_unparsed_exit_policy'): if descriptor._unparsed_exit_policy == [str_type('reject *:*')]: descriptor.exit_policy = REJECT_ALL_POLICY else: descriptor.exit_policy = stem.exit_policy.ExitPolicy( *descriptor._unparsed_exit_policy) del descriptor._unparsed_exit_policy
def test_load_processed_files_no_file(self, open_mock): """ Tests the load_processed_files() function content that is malformed because it is missing the file path. """ open_mock.return_value = io.StringIO(str_type(' 12345')) self.assertRaises(TypeError, stem.descriptor.reader.load_processed_files, '')
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.assertEqual(FIRST_ONION_KEY, router.onion_key) self.assertEqual(None, router.ntor_onion_key) self.assertEqual([], router.or_addresses) self.assertEqual([], router.family) self.assertEqual(stem.exit_policy.MicroExitPolicy('reject 1-65535'), router.exit_policy) self.assertEqual({b'@last-listed': b'2013-02-24 00:18:36'}, router.get_annotations()) self.assertEqual([b'@last-listed 2013-02-24 00:18:36'], router.get_annotation_lines()) router = next(descriptors) self.assertEqual(SECOND_ONION_KEY, router.onion_key) self.assertEqual(str_type('r5572HzD+PMPBbXlZwBhsm6YEbxnYgis8vhZ1jmdI2k='), router.ntor_onion_key) self.assertEqual([], router.or_addresses) self.assertEqual(['$6141629FA0D15A6AEAEF3A1BEB76E64C767B3174'], router.family) self.assertEqual(stem.exit_policy.MicroExitPolicy('reject 1-65535'), router.exit_policy) self.assertEqual({b'@last-listed': b'2013-02-24 00:18:37'}, router.get_annotations()) self.assertEqual([b'@last-listed 2013-02-24 00:18:37'], router.get_annotation_lines()) router = next(descriptors) self.assertEqual(THIRD_ONION_KEY, router.onion_key) self.assertEqual(None, router.ntor_onion_key) self.assertEqual([(str_type('2001:6b0:7:125::242'), 9001, True)], router.or_addresses) self.assertEqual([], router.family) self.assertEqual(stem.exit_policy.MicroExitPolicy('accept 80,443'), router.exit_policy) self.assertEqual({b'@last-listed': b'2013-02-24 00:18:36'}, router.get_annotations()) self.assertEqual([b'@last-listed 2013-02-24 00:18:36'], router.get_annotation_lines())
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([ str_type('B6D83EC2D9E18B0A7A33428F8CFA9C536769E209'), str_type('E0BD57A11F00041A9789577C53A1B784473669E4'), str_type('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))
def test_flag_thresholds(self): """ Parses the flag-thresholds entry. """ test_values = ( ('', {}), ('fast-speed=40960', {str_type('fast-speed'): 40960}), # numeric value ('guard-wfu=94.669%', {str_type('guard-wfu'): 0.94669}), # percentage value ('guard-wfu=94.669% guard-tk=691200', {str_type('guard-wfu'): 0.94669, str_type('guard-tk'): 691200}), # multiple values ) for test_value, expected_value in test_values: document = get_network_status_document_v3({'vote-status': 'vote', 'flag-thresholds': test_value}) self.assertEqual(expected_value, document.flag_thresholds) # parses a full entry found in an actual vote full_line = 'stable-uptime=693369 stable-mtbf=153249 fast-speed=40960 guard-wfu=94.669% guard-tk=691200 guard-bw-inc-exits=174080 guard-bw-exc-exits=184320 enough-mtbf=1' expected_value = { str_type('stable-uptime'): 693369, str_type('stable-mtbf'): 153249, str_type('fast-speed'): 40960, str_type('guard-wfu'): 0.94669, str_type('guard-tk'): 691200, str_type('guard-bw-inc-exits'): 174080, str_type('guard-bw-exc-exits'): 184320, str_type('enough-mtbf'): 1, } document = get_network_status_document_v3({'vote-status': 'vote', 'flag-thresholds': full_line}) self.assertEqual(expected_value, document.flag_thresholds) test_values = ( 'stable-uptime 693369', # not a key=value mapping 'stable-uptime=a693369', # non-numeric value 'guard-wfu=94.669%%', # double quote 'stable-uptime=693369\tstable-mtbf=153249', # non-space divider ) for test_value in test_values: content = get_network_status_document_v3({'vote-status': 'vote', 'flag-thresholds': test_value}, content = True) self.assertRaises(ValueError, NetworkStatusDocumentV3, content, True) document = NetworkStatusDocumentV3(content, False) self.assertEqual({}, document.flag_thresholds)
def __init__(self, raw_contents, validate=True, annotations=None): """ Server descriptor constructor, created from an individual relay's descriptor content (as provided by 'GETINFO desc/*', cached descriptors, and metrics). By default this validates the descriptor's content as it's parsed. This validation can be disables to either improve performance or be accepting of malformed data. :param str raw_contents: descriptor content provided by the relay :param bool validate: checks the validity of the descriptor's content if **True**, skips these checks otherwise :param list annotations: lines that appeared prior to the descriptor :raises: **ValueError** if the contents is malformed and validate is True """ super(ServerDescriptor, self).__init__(raw_contents) # Only a few things can be arbitrary bytes according to the dir-spec, so # parsing them separately. self.platform = _get_bytes_field('platform', raw_contents) self.contact = _get_bytes_field('contact', raw_contents) raw_contents = stem.util.str_tools._to_unicode(raw_contents) self.nickname = None self.fingerprint = None self.published = None self.address = None self.or_port = None self.socks_port = None self.dir_port = None self.tor_version = None self.operating_system = None self.uptime = None self.exit_policy = None self.exit_policy_v6 = DEFAULT_IPV6_EXIT_POLICY self.family = set() self.average_bandwidth = None self.burst_bandwidth = None self.observed_bandwidth = None self.link_protocols = None self.circuit_protocols = None self.hibernating = False self.allow_single_hop_exits = False self.extra_info_cache = False self.extra_info_digest = None self.hidden_service_dir = None self.eventdns = None self.or_addresses = [] self.read_history_end = None self.read_history_interval = None self.read_history_values = None self.write_history_end = None self.write_history_interval = None self.write_history_values = None self._unrecognized_lines = [] self._annotation_lines = annotations if annotations else [] # A descriptor contains a series of 'keyword lines' which are simply a # keyword followed by an optional value. Lines can also be followed by a # signature block. # # We care about the ordering of 'accept' and 'reject' entries because this # influences the resulting exit policy, but for everything else the order # does not matter so breaking it into key / value pairs. entries, policy = _get_descriptor_components(raw_contents, validate, ('accept', 'reject')) if policy == [str_type('reject *:*')]: self.exit_policy = REJECT_ALL_POLICY else: self.exit_policy = stem.exit_policy.ExitPolicy(*policy) self._parse(entries, validate) if validate: self._check_constraints(entries)
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([ str_type('B6D83EC2D9E18B0A7A33428F8CFA9C536769E209'), str_type('E0BD57A11F00041A9789577C53A1B784473669E4'), str_type('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])
CIRC_CONTENT = '650 CIRC %d %s \ %s \ PURPOSE=%s' PATH_CONTENT = '$%s=%s,$%s=%s,$%s=%s' LIST_CIRCUITS_OUTPUT = str_type("""\ Circuit 4 (GENERAL) |- B1FA7D51B8B6F0CB585D944F450E7C06EDE7E44C (ByTORAndTheSnowDog, 173.209.180.61) |- 0DD9935C5E939CFA1E07B8DDA6D91C1A2A9D9338 (afo02, 87.238.194.176) +- DB3B1CFBD3E4D97B84B548ADD5B9A31451EEC4CC (edwardsnowden3, 109.163.234.10) Circuit 6 (GENERAL) |- B1FA7D51B8B6F0CB585D944F450E7C06EDE7E44C (ByTORAndTheSnowDog, 173.209.180.61) |- EC01CB4766BADC1611678555CE793F2A7EB2D723 (sprockets, 46.165.197.96) +- 9EA317EECA56BDF30CAEB208A253FB456EDAB1A0 (bolobolo1, 96.47.226.20) Circuit 10 (GENERAL) |- B1FA7D51B8B6F0CB585D944F450E7C06EDE7E44C (ByTORAndTheSnowDog, 173.209.180.61) |- 00C2C2A16AEDB51D5E5FB7D6168FC66B343D822F (ph3x, 86.59.119.83) +- 65242C91BFF30F165DA4D132C81A9EBA94B71D62 (torexit16, 176.67.169.171) """) EXIT_USED_OUTPUT = str_type("""\ Tracking requests for tor exits. Press 'enter' to end. Exit relay for our connection to 64.15.112.44:80 address: 31.172.30.2:443 fingerprint: A59E1E7C7EAEE083D756EE1FF6EC31CA3D8651D7