text: #!/usr/bin/env python
  # vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4
  
  
  import libtorrent as lt
  
  import unittest
  import time
  import datetime
  import os
  import shutil
  import binascii
  import subprocess as sub
  import sys
  import pickle
  import threading
  
  # include terminal interface for travis parallel executions of scripts which use
  # terminal features: fix multiple stdin assignment at termios.tcgetattr
  if os.name != 'nt':
      import pty
  
  HAVE_DEPRECATED_APIS = hasattr(lt, 'version')
  
  settings = {
      'alert_mask': lt.alert.category_t.all_categories,
      'enable_dht': False, 'enable_lsd': False, 'enable_natpmp': False,
      'enable_upnp': False, 'listen_interfaces': '0.0.0.0:0', 'file_pool_size': 1}
  
  
  class test_create_torrent(unittest.TestCase):
  
      def test_from_torrent_info(self):
          ti = lt.torrent_info('unordered.torrent')
          ct = lt.create_torrent(ti)
          entry = ct.generate()
          content = lt.bencode(entry).strip()
          with open('unordered.torrent', 'rb') as f:
              file_content = bytearray(f.read().strip())
              print(content)
              print(file_content)
              print(entry)
              self.assertEqual(content, file_content)
  
  
  class test_session_stats(unittest.TestCase):
  
      def test_add_torrent_params(self):
          atp = lt.add_torrent_params()
  
          for field_name in dir(atp):
              field = getattr(atp, field_name)
              print(field_name, field)
  
      def test_unique(self):
          metrics = lt.session_stats_metrics()
          self.assertTrue(len(metrics) > 40)
          idx = set()
          for m in metrics:
              self.assertTrue(m.value_index not in idx)
              idx.add(m.value_index)
  
      def test_find_idx(self):
          self.assertEqual(lt.find_metric_idx("peer.error_peers"), 0)
  
  
  class test_torrent_handle(unittest.TestCase):
  
      def setup(self):
          self.ses = lt.session(settings)
          self.ti = lt.torrent_info('url_seed_multi.torrent')
          self.h = self.ses.add_torrent({
              'ti': self.ti, 'save_path': os.getcwd(),
              'flags': lt.torrent_flags.default_flags})
  
      def test_torrent_handle(self):
          self.setup()
          self.assertEqual(self.h.get_file_priorities(), [4, 4])
          self.assertEqual(self.h.get_piece_priorities(), [4])
  
          self.h.prioritize_files([0, 1])
          # workaround for asynchronous priority update
          time.sleep(1)
          self.assertEqual(self.h.get_file_priorities(), [0, 1])
  
          self.h.prioritize_pieces([0])
          self.assertEqual(self.h.get_piece_priorities(), [0])
  
          # also test the overload that takes a list of piece->priority mappings
          self.h.prioritize_pieces([(0, 1)])
          self.assertEqual(self.h.get_piece_priorities(), [1])
          self.h.connect_peer(('127.0.0.1', 6881))
          self.h.connect_peer(('127.0.0.2', 6881), source=4)
          self.h.connect_peer(('127.0.0.3', 6881), flags=2)
          self.h.connect_peer(('127.0.0.4', 6881), flags=2, source=4)
  
          print(self.h.queue_position())
  
      def test_torrent_handle_in_set(self):
          self.setup()
          torrents = set()
          torrents.add(self.h)
  
          # get another instance of a torrent_handle that represents the same
          # torrent. Make sure that when we add it to a set, it just replaces the
          # existing object
          t = self.ses.get_torrents()
          self.assertEqual(len(t), 1)
          for h in t:
              torrents.add(h)
  
          self.assertEqual(len(torrents), 1)
  
      def test_torrent_handle_in_dict(self):
          self.setup()
          torrents = {}
          torrents[self.h] = 'foo'
  
          # get another instance of a torrent_handle that represents the same
          # torrent. Make sure that when we add it to a dict, it just replaces the
          # existing object
          t = self.ses.get_torrents()
          self.assertEqual(len(t), 1)
          for h in t:
              torrents[h] = 'bar'
  
          self.assertEqual(len(torrents), 1)
          self.assertEqual(torrents[self.h], 'bar')
  
      def test_replace_trackers(self):
          self.setup()
          trackers = []
          for idx, tracker_url in enumerate(('udp://tracker1.com', 'udp://tracker2.com')):
              tracker = lt.announce_entry(tracker_url)
              tracker.tier = idx
              tracker.fail_limit = 2
              trackers.append(tracker)
          self.h.replace_trackers(trackers)
          new_trackers = self.h.trackers()
          self.assertEqual(new_trackers[0]['url'], 'udp://tracker1.com')
          self.assertEqual(new_trackers[1]['tier'], 1)
          self.assertEqual(new_trackers[1]['fail_limit'], 2)
  
      def test_pickle_trackers(self):
          """Test lt objects convertors are working and trackers can be pickled"""
          self.setup()
          tracker = lt.announce_entry('udp://tracker1.com')
          tracker.tier = 0
          tracker.fail_limit = 1
          trackers = [tracker]
          self.h.replace_trackers(trackers)
          # wait a bit until the endpoints list gets populated
          while len(self.h.trackers()[0]['endpoints']) == 0:
              time.sleep(0.1)
          pickled_trackers = pickle.dumps(self.h.trackers())
          unpickled_trackers = pickle.loads(pickled_trackers)
          self.assertEqual(unpickled_trackers[0]['url'], 'udp://tracker1.com')
          self.assertEqual(unpickled_trackers[0]['endpoints'][0]['last_error']['value'], 0)
  
      def test_file_status(self):
          self.setup()
          status = self.h.file_status()
          print(status)
  
      def test_piece_deadlines(self):
          self.setup()
          self.h.clear_piece_deadlines()
  
      def test_status_last_uploaded_dowloaded(self):
          # we want to check at seconds precision but can't control session
          # time, wait for next full second to prevent second increment
          time.sleep(1 - datetime.datetime.now().microsecond / 1000000.0)
  
          self.setup()
          st = self.h.status()
          for attr in dir(st):
              print('%s: %s' % (attr, getattr(st, attr)))
          # last upload and download times are at session start time
          self.assertEqual(st.last_upload, None)
          self.assertEqual(st.last_download, None)
  
      def test_serialize_trackers(self):
          """Test to ensure the dict contains only python built-in types"""
          self.setup()
          self.h.add_tracker({'url': 'udp://tracker1.com'})
          tr = self.h.trackers()[0]
          # wait a bit until the endpoints list gets populated
          while len(tr['endpoints']) == 0:
              time.sleep(0.1)
              tr = self.h.trackers()[0]
          import json
          print(json.dumps(self.h.trackers()[0]))
  
      def test_torrent_status(self):
          self.setup()
          st = self.h.status()
          ti = st.handle
          self.assertEqual(ti.info_hash(), self.ti.info_hash())
          # make sure we can compare torrent_status objects
          st2 = self.h.status()
          self.assertEqual(st2, st)
          print(st2)
  
      def test_read_resume_data(self):
  
          resume_data = lt.bencode({
              'file-format': 'libtorrent resume file',
              'info-hash': 'abababababababababab',
              'name': 'test',
              'save_path': '.',
              'peers': '\x01\x01\x01\x01\x00\x01\x02\x02\x02\x02\x00\x02',
              'file_priority': [0, 1, 1]})
          tp = lt.read_resume_data(resume_data)
  
          self.assertEqual(tp.name, 'test')
          self.assertEqual(tp.info_hash, lt.sha1_hash('abababababababababab'))
          self.assertEqual(tp.file_priorities, [0, 1, 1])
          self.assertEqual(tp.peers, [('1.1.1.1', 1), ('2.2.2.2', 2)])
  
          ses = lt.session(settings)
          h = ses.add_torrent(tp)
          for attr in dir(tp):
              print('%s: %s' % (attr, getattr(tp, attr)))
  
          h.connect_peer(('3.3.3.3', 3))
  
          for i in range(0, 10):
              alerts = ses.pop_alerts()
              for a in alerts:
                  print(a.message())
              time.sleep(0.1)
  
      def test_scrape(self):
          self.setup()
          # this is just to make sure this function can be called like this
          # from python
          self.h.scrape_tracker()
  
      def test_cache_info(self):
          self.setup()
          cs = self.ses.get_cache_info(self.h)
          self.assertEqual(cs.pieces, [])
  
      def test_unknown_torrent_parameter(self):
          self.ses = lt.session(settings)
          try:
              self.h = self.ses.add_torrent({'unexpected-key-name': ''})
              self.assertFalse('should have thrown an exception')
          except KeyError as e:
              print(e)
  
      def test_torrent_parameter(self):
          self.ses = lt.session(settings)
          self.ti = lt.torrent_info('url_seed_multi.torrent')
          self.h = self.ses.add_torrent({
              'ti': self.ti,
              'save_path': os.getcwd(),
              'trackers': ['http://test.com/announce'],
              'dht_nodes': [('1.2.3.4', 6881), ('4.3.2.1', 6881)],
              'file_priorities': [1, 1],
              'http_seeds': ['http://test.com/file3'],
              'url_seeds': ['http://test.com/announce-url'],
              'peers': [('5.6.7.8', 6881)],
              'banned_peers': [('8.7.6.5', 6881)],
              'renamed_files': {0: 'test.txt', 2: 'test.txt'}
          })
          self.st = self.h.status()
          self.assertEqual(self.st.save_path, os.getcwd())
          trackers = self.h.trackers()
          self.assertEqual(len(trackers), 1)
          self.assertEqual(trackers[0].get('url'), 'http://test.com/announce')
          self.assertEqual(trackers[0].get('tier'), 0)
          self.assertEqual(self.h.get_file_priorities(), [1, 1])
          self.assertEqual(self.h.http_seeds(), ['http://test.com/file3'])
          # url_seeds was already set, test that it did not get overwritten
          self.assertEqual(self.h.url_seeds(),
                           ['http://test.com/announce-url/', 'http://test.com/file/'])
          # piece priorities weren't set explicitly, but they were updated by the
          # file priorities being set
          self.assertEqual(self.h.get_piece_priorities(), [1])
          self.assertEqual(self.ti.merkle_tree(), [])
          self.assertEqual(self.st.verified_pieces, [])
  
  
  class test_torrent_info(unittest.TestCase):
  
      def test_bencoded_constructor(self):
          info = lt.torrent_info({'info': {
              'name': 'test_torrent', 'length': 1234,
              'piece length': 16 * 1024,
              'pieces': 'aaaaaaaaaaaaaaaaaaaa'}})
  
          self.assertEqual(info.num_files(), 1)
  
          f = info.files()
          self.assertEqual(f.file_path(0), 'test_torrent')
          self.assertEqual(f.file_name(0), 'test_torrent')
          self.assertEqual(f.file_size(0), 1234)
          self.assertEqual(info.total_size(), 1234)
          self.assertEqual(info.creation_date(), 0)
  
      def test_metadata(self):
          ti = lt.torrent_info('base.torrent')
  
          self.assertTrue(len(ti.metadata()) != 0)
          self.assertTrue(len(ti.hash_for_piece(0)) != 0)
  
      def test_web_seeds(self):
          ti = lt.torrent_info('base.torrent')
  
          ws = [{'url': 'http://foo/test', 'auth': '', 'type': 0},
                {'url': 'http://bar/test', 'auth': '', 'type': 1}]
          ti.set_web_seeds(ws)
          web_seeds = ti.web_seeds()
          self.assertEqual(len(ws), len(web_seeds))
          for i in range(len(web_seeds)):
              self.assertEqual(web_seeds[i]["url"], ws[i]["url"])
              self.assertEqual(web_seeds[i]["auth"], ws[i]["auth"])
              self.assertEqual(web_seeds[i]["type"], ws[i]["type"])
  
      def test_iterable_files(self):
          # the file_strage object is only iterable for backwards compatibility
          if not HAVE_DEPRECATED_APIS:
              return
  
          lt.session(settings)
          ti = lt.torrent_info('url_seed_multi.torrent')
          files = ti.files()
  
          idx = 0
          expected = ['bar.txt', 'var.txt']
          for f in files:
              print(f.path)
  
              self.assertEqual(os.path.split(f.path)[1], expected[idx])
              self.assertEqual(os.path.split(f.path)[0],
                               os.path.join('temp', 'foo'))
              idx += 1
  
      def test_announce_entry(self):
          ae = lt.announce_entry('test')
          self.assertEqual(ae.url, 'test')
          self.assertEqual(ae.tier, 0)
          self.assertEqual(ae.verified, False)
          self.assertEqual(ae.source, 0)
  
  
  class test_alerts(unittest.TestCase):
  
      def test_alert(self):
  
          ses = lt.session(settings)
          ti = lt.torrent_info('base.torrent')
          h = ses.add_torrent({'ti': ti, 'save_path': os.getcwd()})
          st = h.status()
          time.sleep(1)
          ses.remove_torrent(h)
          ses.wait_for_alert(1000)  # milliseconds
          alerts = ses.pop_alerts()
          for a in alerts:
              if a.what() == 'add_torrent_alert':
                  self.assertEqual(a.torrent_name, 'temp')
              print(a.message())
              for field_name in dir(a):
                  if field_name.startswith('__'):
                      continue
                  field = getattr(a, field_name)
                  if callable(field):
                      print('  ', field_name, ' = ', field())
                  else:
                      print('  ', field_name, ' = ', field)
  
          print(st.next_announce)
          self.assertEqual(st.name, 'temp')
          print(st.errc.message())
          print(st.pieces)
          print(st.last_seen_complete)
          print(st.completed_time)
          print(st.progress)
          print(st.num_pieces)
          print(st.distributed_copies)
          if HAVE_DEPRECATED_APIS:
              print(st.paused)
          print(st.info_hash)
          print(st.seeding_duration)
          print(st.last_upload)
          print(st.last_download)
          self.assertEqual(st.save_path, os.getcwd())
  
      def test_pop_alerts(self):
          ses = lt.session(settings)
          ses.async_add_torrent(
              {"ti": lt.torrent_info("base.torrent"), "save_path": "."})
  
  # this will cause an error (because of duplicate torrents) and the
  # torrent_info object created here will be deleted once the alert goes out
  # of scope. When that happens, it will decrement the python object, to allow
  # it to release the object.
  # we're trying to catch the error described in this post, with regards to
  # torrent_info.
  # https://mail.python.org/pipermail/cplusplus-sig/2007-June/012130.html
          ses.async_add_torrent(
              {"ti": lt.torrent_info("base.torrent"), "save_path": "."})
          time.sleep(1)
          for i in range(0, 10):
              alerts = ses.pop_alerts()
              for a in alerts:
                  print(a.message())
              time.sleep(0.1)
  
      def test_alert_notify(self):
          ses = lt.session(settings)
          event = threading.Event()
  
          def callback():
              event.set()
  
          ses.set_alert_notify(callback)
          ses.async_add_torrent(
              {"ti": lt.torrent_info("base.torrent"), "save_path": "."})
          event.wait()
  
  
  class test_bencoder(unittest.TestCase):
  
      def test_bencode(self):
  
          encoded = lt.bencode({'a': 1, 'b': [1, 2, 3], 'c': 'foo'})
          self.assertEqual(encoded, b'd1:ai1e1:bli1ei2ei3ee1:c3:fooe')
  
      def test_bdecode(self):
  
          encoded = b'd1:ai1e1:bli1ei2ei3ee1:c3:fooe'
          decoded = lt.bdecode(encoded)
          self.assertEqual(decoded, {b'a': 1, b'b': [1, 2, 3], b'c': b'foo'})
  
  
  class test_sha1hash(unittest.TestCase):
  
      def test_sha1hash(self):
          h = 'a0' * 20
          s = lt.sha1_hash(binascii.unhexlify(h))
          self.assertEqual(h, str(s))
  
  
  class test_magnet_link(unittest.TestCase):
  
      def test_parse_magnet_uri(self):
          ses = lt.session({})
          magnet = 'magnet:?xt=urn:btih:C6EIF4CCYDBTIJVG3APAGM7M4NDONCTI'
          p = lt.parse_magnet_uri(magnet)
          self.assertEqual(str(p.info_hash), '178882f042c0c33426a6d81e0333ece346e68a68')
          p.save_path = '.'
          h = ses.add_torrent(p)
          self.assertEqual(str(h.info_hash()), '178882f042c0c33426a6d81e0333ece346e68a68')
  
      def test_parse_magnet_uri_dict(self):
          ses = lt.session({})
          magnet = 'magnet:?xt=urn:btih:C6EIF4CCYDBTIJVG3APAGM7M4NDONCTI'
          p = lt.parse_magnet_uri_dict(magnet)
          self.assertEqual(binascii.hexlify(p['info_hash']), b'178882f042c0c33426a6d81e0333ece346e68a68')
          p['save_path'] = '.'
          h = ses.add_torrent(p)
          self.assertEqual(str(h.info_hash()), '178882f042c0c33426a6d81e0333ece346e68a68')
  
  
  class test_peer_class(unittest.TestCase):
  
      def test_peer_class_ids(self):
          s = lt.session(settings)
  
          print('global_peer_class_id:', lt.session.global_peer_class_id)
          print('tcp_peer_class_id:', lt.session.tcp_peer_class_id)
          print('local_peer_class_id:', lt.session.local_peer_class_id)
  
          print('global: ', s.get_peer_class(s.global_peer_class_id))
          print('tcp: ', s.get_peer_class(s.local_peer_class_id))
          print('local: ', s.get_peer_class(s.local_peer_class_id))
  
      def test_peer_class(self):
          s = lt.session(settings)
  
          c = s.create_peer_class('test class')
          print('new class: ', s.get_peer_class(c))
  
          nfo = s.get_peer_class(c)
          self.assertEqual(nfo['download_limit'], 0)
          self.assertEqual(nfo['upload_limit'], 0)
          self.assertEqual(nfo['ignore_unchoke_slots'], False)
          self.assertEqual(nfo['connection_limit_factor'], 100)
          self.assertEqual(nfo['download_priority'], 1)
          self.assertEqual(nfo['upload_priority'], 1)
          self.assertEqual(nfo['label'], 'test class')
  
          nfo['download_limit'] = 1337
          nfo['upload_limit'] = 1338
          nfo['ignore_unchoke_slots'] = True
          nfo['connection_limit_factor'] = 42
          nfo['download_priority'] = 2
          nfo['upload_priority'] = 3
  
          s.set_peer_class(c, nfo)
  
          nfo2 = s.get_peer_class(c)
          self.assertEqual(nfo, nfo2)
  
      def test_peer_class_filter(self):
          filt = lt.peer_class_type_filter()
          filt.add(lt.peer_class_type_filter.tcp_socket, lt.session.global_peer_class_id)
          filt.remove(lt.peer_class_type_filter.utp_socket, lt.session.local_peer_class_id)
  
          filt.disallow(lt.peer_class_type_filter.tcp_socket, lt.session.global_peer_class_id)
          filt.allow(lt.peer_class_type_filter.utp_socket, lt.session.local_peer_class_id)
  
      def test_peer_class_ip_filter(self):
          s = lt.session(settings)
          s.set_peer_class_type_filter(lt.peer_class_type_filter())
          s.set_peer_class_filter(lt.ip_filter())
  
  
  class test_session(unittest.TestCase):
  
      def test_add_torrent(self):
          s = lt.session(settings)
          s.add_torrent({'ti': lt.torrent_info('base.torrent'),
                         'save_path': '.',
                         'dht_nodes': [('1.2.3.4', 6881), ('4.3.2.1', 6881)],
                         'http_seeds': ['http://test.com/seed'],
                         'peers': [('5.6.7.8', 6881)],
                         'banned_peers': [('8.7.6.5', 6881)],
                         'file_priorities': [1, 1, 1, 2, 0]})
  
      def test_apply_settings(self):
  
          s = lt.session(settings)
          s.apply_settings({'num_want': 66, 'user_agent': 'test123'})
          self.assertEqual(s.get_settings()['num_want'], 66)
          self.assertEqual(s.get_settings()['user_agent'], 'test123')
  
      def test_post_session_stats(self):
          s = lt.session({'alert_mask': lt.alert.category_t.stats_notification,
                          'enable_dht': False})
          s.post_session_stats()
          alerts = []
          # first the stats headers log line. but not if logging is disabled
          while len(alerts) == 0:
              s.wait_for_alert(1000)
              alerts = s.pop_alerts()
  
          while len(alerts) > 0:
              a = alerts.pop(0)
              print(a)
              if isinstance(a, lt.session_stats_header_alert):
                  break
          self.assertTrue(isinstance(a, lt.session_stats_header_alert))
          # then the actual stats values
          while len(alerts) == 0:
              s.wait_for_alert(1000)
              alerts = s.pop_alerts()
          a = alerts.pop(0)
          print(a)
          self.assertTrue(isinstance(a, lt.session_stats_alert))
          self.assertTrue(isinstance(a.values, dict))
          self.assertTrue(len(a.values) > 0)
  
      def test_post_dht_stats(self):
          s = lt.session({'alert_mask': lt.alert.category_t.stats_notification, 'enable_dht': False})
          s.post_dht_stats()
          alerts = []
          # first the stats headers log line. but not if logging is disabled
          while len(alerts) == 0:
              s.wait_for_alert(1000)
              alerts = s.pop_alerts()
          a = alerts.pop(0)
          self.assertTrue(isinstance(a, lt.dht_stats_alert))
          self.assertTrue(isinstance(a.active_requests, list))
          self.assertTrue(isinstance(a.routing_table, list))
  
      def test_unknown_settings(self):
          try:
              lt.session({'unexpected-key-name': 42})
              self.assertFalse('should have thrown an exception')
          except KeyError as e:
              print(e)
  
      def test_fingerprint(self):
          self.assertEqual(lt.generate_fingerprint('LT', 0, 1, 2, 3), '-LT0123-')
          self.assertEqual(lt.generate_fingerprint('..', 10, 1, 2, 3), '-..A123-')
  
      def test_min_memory_preset(self):
          min_mem = lt.min_memory_usage()
          print(min_mem)
  
          self.assertTrue('connection_speed' in min_mem)
          self.assertTrue('file_pool_size' in min_mem)
  
      def test_seed_mode_preset(self):
          seed_mode = lt.high_performance_seed()
          print(seed_mode)
  
          self.assertTrue('alert_queue_size' in seed_mode)
          self.assertTrue('connection_speed' in seed_mode)
          self.assertTrue('file_pool_size' in seed_mode)
  
      def test_default_settings(self):
  
          default = lt.default_settings()
          print(default)
  
  
  class test_example_client(unittest.TestCase):
  
      def test_execute_client(self):
          if os.name == 'nt':
              # TODO: fix windows includes of client.py
              return
          my_stdin = sys.stdin
          if os.name != 'nt':
              master_fd, slave_fd = pty.openpty()
              # slave_fd fix multiple stdin assignment at termios.tcgetattr
              my_stdin = slave_fd
  
          process = sub.Popen(
              [sys.executable, "client.py", "url_seed_multi.torrent"],
              stdin=my_stdin, stdout=sub.PIPE, stderr=sub.PIPE)
          # python2 has no Popen.wait() timeout
          time.sleep(5)
          returncode = process.poll()
          if returncode is None:
              # this is an expected use-case
              process.kill()
          err = process.stderr.read().decode("utf-8")
          self.assertEqual('', err, 'process throw errors: \n' + err)
          # check error code if process did unexpected end
          if returncode is not None:
              # in case of error return: output stdout if nothing was on stderr
              if returncode != 0:
                  print("stdout:\n" + process.stdout.read().decode("utf-8"))
              self.assertEqual(returncode, 0, "returncode: " + str(returncode) + "\n"
                               + "stderr: empty\n"
                               + "some configuration does not output errors like missing module members,"
                               + "try to call it manually to get the error message\n")
  
      def test_execute_simple_client(self):
          process = sub.Popen(
              [sys.executable, "simple_client.py", "url_seed_multi.torrent"],
              stdout=sub.PIPE, stderr=sub.PIPE)
          # python2 has no Popen.wait() timeout
          time.sleep(5)
          returncode = process.poll()
          if returncode is None:
              # this is an expected use-case
              process.kill()
          err = process.stderr.read().decode("utf-8")
          self.assertEqual('', err, 'process throw errors: \n' + err)
          # check error code if process did unexpected end
          if returncode is not None:
              # in case of error return: output stdout if nothing was on stderr
              if returncode != 0:
                  print("stdout:\n" + process.stdout.read().decode("utf-8"))
              self.assertEqual(returncode, 0, "returncode: " + str(returncode) + "\n"
                               + "stderr: empty\n"
                               + "some configuration does not output errors like missing module members,"
                               + "try to call it manually to get the error message\n")
  
      def test_execute_make_torrent(self):
          process = sub.Popen(
              [sys.executable, "make_torrent.py", "url_seed_multi.torrent",
               "http://test.com/test"], stdout=sub.PIPE, stderr=sub.PIPE)
          returncode = process.wait()
          # python2 has no Popen.wait() timeout
          err = process.stderr.read().decode("utf-8")
          self.assertEqual('', err, 'process throw errors: \n' + err)
          # in case of error return: output stdout if nothing was on stderr
          if returncode != 0:
              print("stdout:\n" + process.stdout.read().decode("utf-8"))
          self.assertEqual(returncode, 0, "returncode: " + str(returncode) + "\n"
                           + "stderr: empty\n"
                           + "some configuration does not output errors like missing module members,"
                           + "try to call it manually to get the error message\n")
  
      def test_default_settings(self):
  
          default = lt.default_settings()
          print(default)
  
  
  class test_operation_t(unittest.TestCase):
  
      def test_enum(self):
          self.assertEqual(lt.operation_name(lt.operation_t.sock_accept), "sock_accept")
          self.assertEqual(lt.operation_name(lt.operation_t.unknown), "unknown")
          self.assertEqual(lt.operation_name(lt.operation_t.mkdir), "mkdir")
          self.assertEqual(lt.operation_name(lt.operation_t.partfile_write), "partfile_write")
          self.assertEqual(lt.operation_name(lt.operation_t.hostname_lookup), "hostname_lookup")
  
  
  class test_error_code(unittest.TestCase):
  
      def test_error_code(self):
  
          a = lt.error_code()
          a = lt.error_code(10, lt.libtorrent_category())
          self.assertEqual(a.category().name(), 'libtorrent')
  
          self.assertEqual(lt.libtorrent_category().name(), 'libtorrent')
          self.assertEqual(lt.upnp_category().name(), 'upnp')
          self.assertEqual(lt.http_category().name(), 'http')
          self.assertEqual(lt.socks_category().name(), 'socks')
          self.assertEqual(lt.bdecode_category().name(), 'bdecode')
          self.assertEqual(lt.generic_category().name(), 'generic')
          self.assertEqual(lt.system_category().name(), 'system')
  
  
  if __name__ == '__main__':
      print(lt.__version__)
      shutil.copy(os.path.join('..', '..', 'test', 'test_torrents',
                               'url_seed_multi.torrent'), '.')
      shutil.copy(os.path.join('..', '..', 'test', 'test_torrents',
                               'base.torrent'), '.')
      shutil.copy(os.path.join('..', '..', 'test', 'test_torrents',
                               'unordered.torrent'), '.')
      unittest.main()
  