code:
import collections
import lib.Hydrus.Constants as HC
import lib.Hydrus.Tags
import os
import unittest
import lib.Hydrus.Data
import lib.Hydrus.Caches
import lib.Hydrus.Constants.Local as CC
import lib.Hydrus.Media
import lib.Hydrus.Search
import lib.Hydrus.Globals as HG

class TestMergeTagsManagers( unittest.TestCase ):
	def test_merge( self ):
		first = lib.Hydrus.Data.GenerateKey()
		second = lib.Hydrus.Data.GenerateKey()
		third = lib.Hydrus.Data.GenerateKey()

		#

		service_keys_to_statuses_to_tags = collections.defaultdict( lib.Hydrus.Data.default_dict_set )

		service_keys_to_statuses_to_tags[ first ][ HC.CONTENT_STATUS_CURRENT ] = { 'current_1', 'series:blame!' }

		service_keys_to_statuses_to_tags[ second ][ HC.CONTENT_STATUS_CURRENT ] = { 'current_duplicate_1', 'character:cibo' }
		service_keys_to_statuses_to_tags[ second ][ HC.CONTENT_STATUS_DELETED ] = { 'current_1' }
		service_keys_to_statuses_to_tags[ second ][ HC.CONTENT_STATUS_PENDING ] = { 'pending_1', 'creator:tsutomu nihei' }
		service_keys_to_statuses_to_tags[ second ][ HC.CONTENT_STATUS_PETITIONED ] = { 'petitioned_1' }

		service_keys_to_statuses_to_tags[ third ][ HC.CONTENT_STATUS_CURRENT ] = { 'current_duplicate', 'current_duplicate_1' }
		service_keys_to_statuses_to_tags[ third ][ HC.CONTENT_STATUS_PENDING ] = { 'volume:3' }

		tags_manager_1 = lib.Hydrus.Media.TagsManager( service_keys_to_statuses_to_tags )

		#

		service_keys_to_statuses_to_tags = collections.defaultdict( lib.Hydrus.Data.default_dict_set )

		service_keys_to_statuses_to_tags[ first ][ HC.CONTENT_STATUS_CURRENT ] = { 'current_2', 'series:blame!', 'chapter:1' }
		service_keys_to_statuses_to_tags[ first ][ HC.CONTENT_STATUS_DELETED ] = { 'deleted_2' }

		service_keys_to_statuses_to_tags[ second ][ HC.CONTENT_STATUS_CURRENT ] = { 'current_duplicate'  }
		service_keys_to_statuses_to_tags[ second ][ HC.CONTENT_STATUS_PENDING ] = { 'architecture', 'chapter:2' }

		service_keys_to_statuses_to_tags[ third ][ HC.CONTENT_STATUS_CURRENT ] = { 'current_duplicate' }

		tags_manager_2 = lib.Hydrus.Media.TagsManager( service_keys_to_statuses_to_tags )

		#

		service_keys_to_statuses_to_tags = collections.defaultdict( lib.Hydrus.Data.default_dict_set )

		service_keys_to_statuses_to_tags[ second ][ HC.CONTENT_STATUS_CURRENT ] = { 'page:4', 'page:5' }
		service_keys_to_statuses_to_tags[ second ][ HC.CONTENT_STATUS_PENDING ] = { 'title:double page spread' }

		tags_manager_3 = lib.Hydrus.Media.TagsManager( service_keys_to_statuses_to_tags )

		#

		tags_managers = ( tags_manager_1, tags_manager_2, tags_manager_3 )

		tags_manager = lib.Hydrus.Media.MergeTagsManagers( tags_managers )

		#

		result = { 'creator' : { 'tsutomu nihei' }, 'series' : { 'blame!' }, 'title' : { 'double page spread' }, 'volume' : { '3' }, 'chapter' : { '1', '2' }, 'page' : { '4', '5' } }

		self.assertEqual( tags_manager.GetCombinedNamespaces( ( 'creator', 'series', 'title', 'volume', 'chapter', 'page' ) ), result )

		self.assertEqual( tags_manager.GetNamespaceSlice( ( 'character', ) ), frozenset( { 'character:cibo' } ) )

class TestTagsManager( unittest.TestCase ):
	@classmethod

	def setUpClass( cls ):
		cls._first_key = lib.Hydrus.Data.GenerateKey()
		cls._second_key = lib.Hydrus.Data.GenerateKey()
		cls._third_key = lib.Hydrus.Data.GenerateKey()

		service_keys_to_statuses_to_tags = collections.defaultdict( lib.Hydrus.Data.default_dict_set )

		service_keys_to_statuses_to_tags[ cls._first_key ][ HC.CONTENT_STATUS_CURRENT ] = { 'current', '\u2835', 'creator:tsutomu nihei', 'series:blame!', 'title:test title', 'volume:3', 'chapter:2', 'page:1' }
		service_keys_to_statuses_to_tags[ cls._first_key ][ HC.CONTENT_STATUS_DELETED ] = { 'deleted' }

		service_keys_to_statuses_to_tags[ cls._second_key ][ HC.CONTENT_STATUS_CURRENT ] = { 'deleted', '\u2835' }
		service_keys_to_statuses_to_tags[ cls._second_key ][ HC.CONTENT_STATUS_DELETED ] = { 'current' }
		service_keys_to_statuses_to_tags[ cls._second_key ][ HC.CONTENT_STATUS_PENDING ] = { 'pending' }
		service_keys_to_statuses_to_tags[ cls._second_key ][ HC.CONTENT_STATUS_PETITIONED ] = { 'petitioned' }

		service_keys_to_statuses_to_tags[ cls._third_key ][ HC.CONTENT_STATUS_CURRENT ] = { 'petitioned' }
		service_keys_to_statuses_to_tags[ cls._third_key ][ HC.CONTENT_STATUS_DELETED ] = { 'pending' }

		cls._tags_manager = lib.Hydrus.Media.TagsManager( service_keys_to_statuses_to_tags )

		cls._service_keys_to_statuses_to_tags = service_keys_to_statuses_to_tags

		#

		cls._pending_service_key = lib.Hydrus.Data.GenerateKey()
		cls._content_update_service_key = lib.Hydrus.Data.GenerateKey()
		cls._reset_service_key = lib.Hydrus.Data.GenerateKey()

		other_service_keys_to_statuses_to_tags = collections.defaultdict( lib.Hydrus.Data.default_dict_set )

		other_service_keys_to_statuses_to_tags[ cls._pending_service_key ][ HC.CONTENT_STATUS_PENDING ] = { 'pending' }
		other_service_keys_to_statuses_to_tags[ cls._pending_service_key ][ HC.CONTENT_STATUS_PETITIONED ] = { 'petitioned' }

		other_service_keys_to_statuses_to_tags[ cls._reset_service_key ][ HC.CONTENT_STATUS_CURRENT ] = { 'reset_current' }
		other_service_keys_to_statuses_to_tags[ cls._reset_service_key ][ HC.CONTENT_STATUS_DELETED ] = { 'reset_deleted' }
		other_service_keys_to_statuses_to_tags[ cls._reset_service_key ][ HC.CONTENT_STATUS_PENDING ] = { 'reset_pending' }
		other_service_keys_to_statuses_to_tags[ cls._reset_service_key ][ HC.CONTENT_STATUS_PETITIONED ] = { 'reset_petitioned' }

		cls._other_tags_manager = lib.Hydrus.Media.TagsManager( other_service_keys_to_statuses_to_tags )

		cls._other_service_keys_to_statuses_to_tags = other_service_keys_to_statuses_to_tags

	def test_get_cstvcp( self ):
		result = { 'creator' : { 'tsutomu nihei' }, 'series' : { 'blame!' }, 'title' : { 'test title' }, 'volume' : { '3' }, 'chapter' : { '2' }, 'page' : { '1' } }

		self.assertEqual( self._tags_manager.GetCombinedNamespaces( ( 'creator', 'series', 'title', 'volume', 'chapter', 'page' ) ), result )

	def test_delete_pending( self ):
		self.assertEqual( self._other_tags_manager.GetPending( self._pending_service_key ), { 'pending' } )
		self.assertEqual( self._other_tags_manager.GetPetitioned( self._pending_service_key ), { 'petitioned' } )

		self._other_tags_manager.DeletePending( self._pending_service_key )

		self.assertEqual( self._other_tags_manager.GetPending( self._pending_service_key ), set() )
		self.assertEqual( self._other_tags_manager.GetPetitioned( self._pending_service_key ), set() )

	def test_get_current( self ):
		self.assertEqual( self._tags_manager.GetCurrent( self._first_key ), { 'current', '\u2835', 'creator:tsutomu nihei', 'series:blame!', 'title:test title', 'volume:3', 'chapter:2', 'page:1' } )
		self.assertEqual( self._tags_manager.GetCurrent( self._second_key ), { 'deleted', '\u2835' } )
		self.assertEqual( self._tags_manager.GetCurrent( self._third_key ), { 'petitioned' } )

		self.assertEqual( self._tags_manager.GetCurrent(), { 'current', 'deleted', '\u2835', 'creator:tsutomu nihei', 'series:blame!', 'title:test title', 'volume:3', 'chapter:2', 'page:1', 'petitioned' } )

	def test_get_deleted( self ):
		self.assertEqual( self._tags_manager.GetDeleted( self._first_key ), { 'deleted' } )
		self.assertEqual( self._tags_manager.GetDeleted( self._second_key ), { 'current' } )
		self.assertEqual( self._tags_manager.GetDeleted( self._third_key ), { 'pending' } )

		self.assertEqual( self._tags_manager.GetDeleted(), { 'deleted', 'current', 'pending' } )

	def test_get_namespace_slice( self ):
		self.assertEqual( self._tags_manager.GetNamespaceSlice( ( 'creator', 'series' ) ), frozenset( { 'creator:tsutomu nihei', 'series:blame!' } ) )
		self.assertEqual( self._tags_manager.GetNamespaceSlice( () ), frozenset() )

	def test_get_num_tags( self ):
		self.assertEqual( self._tags_manager.GetNumTags( self._first_key, include_current_tags = False, include_pending_tags = False ), 0 )
		self.assertEqual( self._tags_manager.GetNumTags( self._first_key, include_current_tags = True, include_pending_tags = False ), 8 )
		self.assertEqual( self._tags_manager.GetNumTags( self._first_key, include_current_tags = False, include_pending_tags = True ), 0 )
		self.assertEqual( self._tags_manager.GetNumTags( self._first_key, include_current_tags = True, include_pending_tags = True ), 8 )

		self.assertEqual( self._tags_manager.GetNumTags( self._second_key, include_current_tags = False, include_pending_tags = False ), 0 )
		self.assertEqual( self._tags_manager.GetNumTags( self._second_key, include_current_tags = True, include_pending_tags = False ), 2 )
		self.assertEqual( self._tags_manager.GetNumTags( self._second_key, include_current_tags = False, include_pending_tags = True ), 1 )
		self.assertEqual( self._tags_manager.GetNumTags( self._second_key, include_current_tags = True, include_pending_tags = True ), 3 )

		self.assertEqual( self._tags_manager.GetNumTags( self._third_key, include_current_tags = False, include_pending_tags = False ), 0 )
		self.assertEqual( self._tags_manager.GetNumTags( self._third_key, include_current_tags = True, include_pending_tags = False ), 1 )
		self.assertEqual( self._tags_manager.GetNumTags( self._third_key, include_current_tags = False, include_pending_tags = True ), 0 )
		self.assertEqual( self._tags_manager.GetNumTags( self._third_key, include_current_tags = True, include_pending_tags = True ), 1 )

		self.assertEqual( self._tags_manager.GetNumTags( CC.COMBINED_TAG_SERVICE_KEY, include_current_tags = False, include_pending_tags = False ), 0 )
		self.assertEqual( self._tags_manager.GetNumTags( CC.COMBINED_TAG_SERVICE_KEY, include_current_tags = True, include_pending_tags = False ), 10 )
		self.assertEqual( self._tags_manager.GetNumTags( CC.COMBINED_TAG_SERVICE_KEY, include_current_tags = False, include_pending_tags = True ), 1 )
		self.assertEqual( self._tags_manager.GetNumTags( CC.COMBINED_TAG_SERVICE_KEY, include_current_tags = True, include_pending_tags = True ), 11 )

	def test_get_pending( self ):
		self.assertEqual( self._tags_manager.GetPending( self._first_key ), set() )
		self.assertEqual( self._tags_manager.GetPending( self._second_key ), { 'pending' } )
		self.assertEqual( self._tags_manager.GetPending( self._third_key ), set() )

		self.assertEqual( self._tags_manager.GetPending(), { 'pending' } )

	def test_get_petitioned( self ):
		self.assertEqual( self._tags_manager.GetPetitioned( self._first_key ), set() )
		self.assertEqual( self._tags_manager.GetPetitioned( self._second_key ), { 'petitioned' } )
		self.assertEqual( self._tags_manager.GetPetitioned( self._third_key ), set() )

		self.assertEqual( self._tags_manager.GetPetitioned(), { 'petitioned' } )

	def test_get_service_keys_to_statuses_to_tags( self ):
		s = self._tags_manager.GetServiceKeysToStatusesToTags()

		self.assertEqual( s[ self._first_key ], self._service_keys_to_statuses_to_tags[ self._first_key ] )
		self.assertEqual( s[ self._second_key ], self._service_keys_to_statuses_to_tags[ self._second_key ] )
		self.assertEqual( s[ self._third_key ], self._service_keys_to_statuses_to_tags[ self._third_key ] )

	def test_get_statuses_to_tags( self ):
		self.assertEqual( self._tags_manager.GetStatusesToTags( self._first_key ), self._service_keys_to_statuses_to_tags[ self._first_key ] )
		self.assertEqual( self._tags_manager.GetStatusesToTags( self._second_key ), self._service_keys_to_statuses_to_tags[ self._second_key ] )
		self.assertEqual( self._tags_manager.GetStatusesToTags( self._third_key ), self._service_keys_to_statuses_to_tags[ self._third_key ] )

	def test_has_tag( self ):
		self.assertTrue( self._tags_manager.HasTag( '\u2835' ) )
		self.assertFalse( self._tags_manager.HasTag( 'not_exist' ) )

	def test_process_content_update( self ):
		hashes = { lib.Hydrus.Data.GenerateKey() for i in range( 6 ) }

		#

		self.assertEqual( self._other_tags_manager.GetCurrent( self._content_update_service_key ), set() )
		self.assertEqual( self._other_tags_manager.GetDeleted( self._content_update_service_key ), set() )
		self.assertEqual( self._other_tags_manager.GetPending( self._content_update_service_key ), set() )
		self.assertEqual( self._other_tags_manager.GetPetitioned( self._content_update_service_key ), set() )

		self.assertNotIn( 'hello', self._other_tags_manager.GetCurrent() )
		self.assertNotIn( 'hello', self._other_tags_manager.GetPending() )

		#

		content_update = lib.Hydrus.Data.ContentUpdate( HC.CONTENT_TYPE_MAPPINGS, HC.CONTENT_UPDATE_DELETE, ( 'hello', hashes ) )

		self._other_tags_manager.ProcessContentUpdate( self._content_update_service_key, content_update )

		self.assertEqual( self._other_tags_manager.GetCurrent( self._content_update_service_key ), set() )
		self.assertEqual( self._other_tags_manager.GetDeleted( self._content_update_service_key ), { 'hello' } )
		self.assertEqual( self._other_tags_manager.GetPending( self._content_update_service_key ), set() )
		self.assertEqual( self._other_tags_manager.GetPetitioned( self._content_update_service_key ), set() )

		self.assertNotIn( 'hello', self._other_tags_manager.GetCurrent() )
		self.assertNotIn( 'hello', self._other_tags_manager.GetPending() )

		#

		content_update = lib.Hydrus.Data.ContentUpdate( HC.CONTENT_TYPE_MAPPINGS, HC.CONTENT_UPDATE_PEND, ( 'hello', hashes ) )

		self._other_tags_manager.ProcessContentUpdate( self._content_update_service_key, content_update )

		self.assertEqual( self._other_tags_manager.GetCurrent( self._content_update_service_key ), set() )
		self.assertEqual( self._other_tags_manager.GetDeleted( self._content_update_service_key ), { 'hello' } )
		self.assertEqual( self._other_tags_manager.GetPending( self._content_update_service_key ), { 'hello' } )
		self.assertEqual( self._other_tags_manager.GetPetitioned( self._content_update_service_key ), set() )

		self.assertNotIn( 'hello', self._other_tags_manager.GetCurrent() )
		self.assertIn( 'hello', self._other_tags_manager.GetPending() )

		#

		content_update = lib.Hydrus.Data.ContentUpdate( HC.CONTENT_TYPE_MAPPINGS, HC.CONTENT_UPDATE_RESCIND_PEND, ( 'hello', hashes ) )

		self._other_tags_manager.ProcessContentUpdate( self._content_update_service_key, content_update )

		self.assertEqual( self._other_tags_manager.GetCurrent( self._content_update_service_key ), set() )
		self.assertEqual( self._other_tags_manager.GetDeleted( self._content_update_service_key ), { 'hello' } )
		self.assertEqual( self._other_tags_manager.GetPending( self._content_update_service_key ), set() )
		self.assertEqual( self._other_tags_manager.GetPetitioned( self._content_update_service_key ), set() )

		self.assertNotIn( 'hello', self._other_tags_manager.GetCurrent() )
		self.assertNotIn( 'hello', self._other_tags_manager.GetPending() )

		#

		content_update = lib.Hydrus.Data.ContentUpdate( HC.CONTENT_TYPE_MAPPINGS, HC.CONTENT_UPDATE_PEND, ( 'hello', hashes ) )

		self._other_tags_manager.ProcessContentUpdate( self._content_update_service_key, content_update )

		self.assertEqual( self._other_tags_manager.GetCurrent( self._content_update_service_key ), set() )
		self.assertEqual( self._other_tags_manager.GetDeleted( self._content_update_service_key ), { 'hello' } )
		self.assertEqual( self._other_tags_manager.GetPending( self._content_update_service_key ), { 'hello' } )
		self.assertEqual( self._other_tags_manager.GetPetitioned( self._content_update_service_key ), set() )

		self.assertNotIn( 'hello', self._other_tags_manager.GetCurrent() )
		self.assertIn( 'hello', self._other_tags_manager.GetPending() )

		#

		content_update = lib.Hydrus.Data.ContentUpdate( HC.CONTENT_TYPE_MAPPINGS, HC.CONTENT_UPDATE_ADD, ( 'hello', hashes ) )

		self._other_tags_manager.ProcessContentUpdate( self._content_update_service_key, content_update )

		self.assertEqual( self._other_tags_manager.GetCurrent( self._content_update_service_key ), { 'hello' } )
		self.assertEqual( self._other_tags_manager.GetDeleted( self._content_update_service_key ), set() )
		self.assertEqual( self._other_tags_manager.GetPending( self._content_update_service_key ), set() )
		self.assertEqual( self._other_tags_manager.GetPetitioned( self._content_update_service_key ), set() )

		self.assertIn( 'hello', self._other_tags_manager.GetCurrent() )
		self.assertNotIn( 'hello', self._other_tags_manager.GetPending() )

		#

		content_update = lib.Hydrus.Data.ContentUpdate( HC.CONTENT_TYPE_MAPPINGS, HC.CONTENT_UPDATE_PETITION, ( 'hello', hashes ), reason = 'reason' )

		self._other_tags_manager.ProcessContentUpdate( self._content_update_service_key, content_update )

		self.assertEqual( self._other_tags_manager.GetCurrent( self._content_update_service_key ), { 'hello' } )
		self.assertEqual( self._other_tags_manager.GetDeleted( self._content_update_service_key ), set() )
		self.assertEqual( self._other_tags_manager.GetPending( self._content_update_service_key ), set() )
		self.assertEqual( self._other_tags_manager.GetPetitioned( self._content_update_service_key ), { 'hello' } )

		self.assertIn( 'hello', self._other_tags_manager.GetCurrent() )
		self.assertNotIn( 'hello', self._other_tags_manager.GetPending() )

		#

		content_update = lib.Hydrus.Data.ContentUpdate( HC.CONTENT_TYPE_MAPPINGS, HC.CONTENT_UPDATE_RESCIND_PETITION, ( 'hello', hashes ) )

		self._other_tags_manager.ProcessContentUpdate( self._content_update_service_key, content_update )

		self.assertEqual( self._other_tags_manager.GetCurrent( self._content_update_service_key ), { 'hello' } )
		self.assertEqual( self._other_tags_manager.GetDeleted( self._content_update_service_key ), set() )
		self.assertEqual( self._other_tags_manager.GetPending( self._content_update_service_key ), set() )
		self.assertEqual( self._other_tags_manager.GetPetitioned( self._content_update_service_key ), set() )

		self.assertIn( 'hello', self._other_tags_manager.GetCurrent() )
		self.assertNotIn( 'hello', self._other_tags_manager.GetPending() )

		#

		content_update = lib.Hydrus.Data.ContentUpdate( HC.CONTENT_TYPE_MAPPINGS, HC.CONTENT_UPDATE_PETITION, ( 'hello', hashes ), reason = 'reason' )

		self._other_tags_manager.ProcessContentUpdate( self._content_update_service_key, content_update )

		self.assertEqual( self._other_tags_manager.GetCurrent( self._content_update_service_key ), { 'hello' } )
		self.assertEqual( self._other_tags_manager.GetDeleted( self._content_update_service_key ), set() )
		self.assertEqual( self._other_tags_manager.GetPending( self._content_update_service_key ), set() )
		self.assertEqual( self._other_tags_manager.GetPetitioned( self._content_update_service_key ), { 'hello' } )

		self.assertIn( 'hello', self._other_tags_manager.GetCurrent() )
		self.assertNotIn( 'hello', self._other_tags_manager.GetPending() )

		#

		content_update = lib.Hydrus.Data.ContentUpdate( HC.CONTENT_TYPE_MAPPINGS, HC.CONTENT_UPDATE_DELETE, ( 'hello', hashes ) )

		self._other_tags_manager.ProcessContentUpdate( self._content_update_service_key, content_update )

		self.assertEqual( self._other_tags_manager.GetCurrent( self._content_update_service_key ), set() )
		self.assertEqual( self._other_tags_manager.GetDeleted( self._content_update_service_key ), { 'hello' } )
		self.assertEqual( self._other_tags_manager.GetPending( self._content_update_service_key ), set() )
		self.assertEqual( self._other_tags_manager.GetPetitioned( self._content_update_service_key ), set() )

		self.assertNotIn( 'hello', self._other_tags_manager.GetCurrent() )
		self.assertNotIn( 'hello', self._other_tags_manager.GetPending() )

	def test_reset_service( self ):
		self.assertEqual( self._other_tags_manager.GetCurrent( self._reset_service_key ), { 'reset_current' } )
		self.assertEqual( self._other_tags_manager.GetDeleted( self._reset_service_key ), { 'reset_deleted' } )
		self.assertEqual( self._other_tags_manager.GetPending( self._reset_service_key ), { 'reset_pending' } )
		self.assertEqual( self._other_tags_manager.GetPetitioned( self._reset_service_key ), { 'reset_petitioned' } )

		self._other_tags_manager.ResetService( self._reset_service_key )

		self.assertEqual( self._other_tags_manager.GetCurrent( self._reset_service_key ), set() )
		self.assertEqual( self._other_tags_manager.GetDeleted( self._reset_service_key ), set() )
		self.assertEqual( self._other_tags_manager.GetPending( self._reset_service_key ), set() )
		self.assertEqual( self._other_tags_manager.GetPetitioned( self._reset_service_key ), set() )

class TestTagObjects( unittest.TestCase ):
	def test_predicate_strings_and_namespaces( self ):
		p = lib.Hydrus.Search.Predicate( HC.PREDICATE_TYPE_TAG, 'tag' )

		self.assertEqual( p.ToString(), 'tag' )
		self.assertEqual( p.GetNamespace(), '' )
		self.assertEqual( p.GetTextsAndNamespaces(), [ ( p.ToString(), p.GetNamespace() ) ] )

		p = lib.Hydrus.Search.Predicate( HC.PREDICATE_TYPE_TAG, 'tag', min_current_count = 1, min_pending_count = 2 )

		self.assertEqual( p.ToString( with_count = False ), 'tag' )
		self.assertEqual( p.ToString( with_count = True ), 'tag (1) (+2)' )
		self.assertEqual( p.GetNamespace(), '' )
		self.assertEqual( p.GetTextsAndNamespaces(), [ ( p.ToString(), p.GetNamespace() ) ] )

		p = lib.Hydrus.Search.Predicate( HC.PREDICATE_TYPE_TAG, 'tag', False )

		self.assertEqual( p.ToString(), '-tag' )
		self.assertEqual( p.GetNamespace(), '' )
		self.assertEqual( p.GetTextsAndNamespaces(), [ ( p.ToString(), p.GetNamespace() ) ] )

		p = lib.Hydrus.Search.Predicate( HC.PREDICATE_TYPE_TAG, 'tag', False, 1, 2 )

		self.assertEqual( p.ToString( with_count = False ), '-tag' )
		self.assertEqual( p.ToString( with_count = True ), '-tag (1) (+2)' )
		self.assertEqual( p.GetNamespace(), '' )
		self.assertEqual( p.GetTextsAndNamespaces(), [ ( p.ToString(), p.GetNamespace() ) ] )

		#

		p = lib.Hydrus.Search.Predicate( HC.PREDICATE_TYPE_SYSTEM_AGE, ( '<', 'delta', ( 1, 2, 3, 4 ) ) )

		self.assertEqual( p.ToString(), 'system:time imported: since 1 year 2 months ago' )
		self.assertEqual( p.GetNamespace(), 'system' )
		self.assertEqual( p.GetTextsAndNamespaces(), [ ( p.ToString(), p.GetNamespace() ) ] )

		p = lib.Hydrus.Search.Predicate( HC.PREDICATE_TYPE_SYSTEM_AGE, ( '\u2248', 'delta', ( 1, 2, 3, 4 ) ) )

		self.assertEqual( p.ToString(), 'system:time imported: around 1 year 2 months ago' )
		self.assertEqual( p.GetNamespace(), 'system' )
		self.assertEqual( p.GetTextsAndNamespaces(), [ ( p.ToString(), p.GetNamespace() ) ] )

		p = lib.Hydrus.Search.Predicate( HC.PREDICATE_TYPE_SYSTEM_AGE, ( '>', 'delta', ( 1, 2, 3, 4 ) ) )

		self.assertEqual( p.ToString(), 'system:time imported: before 1 year 2 months ago' )
		self.assertEqual( p.GetNamespace(), 'system' )
		self.assertEqual( p.GetTextsAndNamespaces(), [ ( p.ToString(), p.GetNamespace() ) ] )

		p = lib.Hydrus.Search.Predicate( HC.PREDICATE_TYPE_SYSTEM_ARCHIVE, min_current_count = 1000 )

		self.assertEqual( p.ToString(), 'system:archive (1,000)' )
		self.assertEqual( p.GetNamespace(), 'system' )
		self.assertEqual( p.GetTextsAndNamespaces(), [ ( p.ToString(), p.GetNamespace() ) ] )

		p = lib.Hydrus.Search.Predicate( HC.PREDICATE_TYPE_SYSTEM_DURATION, ( '<', 200 ) )

		self.assertEqual( p.ToString(), 'system:duration < 200 milliseconds' )
		self.assertEqual( p.GetNamespace(), 'system' )
		self.assertEqual( p.GetTextsAndNamespaces(), [ ( p.ToString(), p.GetNamespace() ) ] )

		p = lib.Hydrus.Search.Predicate( HC.PREDICATE_TYPE_SYSTEM_EVERYTHING, min_current_count = 2000 )

		self.assertEqual( p.ToString(), 'system:everything (2,000)' )
		self.assertEqual( p.GetNamespace(), 'system' )
		self.assertEqual( p.GetTextsAndNamespaces(), [ ( p.ToString(), p.GetNamespace() ) ] )

		p = lib.Hydrus.Search.Predicate( HC.PREDICATE_TYPE_SYSTEM_FILE_SERVICE, ( True, HC.CONTENT_STATUS_CURRENT, CC.LOCAL_FILE_SERVICE_KEY ) )

		self.assertEqual( p.ToString(), 'system:is currently in my files' )
		self.assertEqual( p.GetNamespace(), 'system' )
		self.assertEqual( p.GetTextsAndNamespaces(), [ ( p.ToString(), p.GetNamespace() ) ] )

		p = lib.Hydrus.Search.Predicate( HC.PREDICATE_TYPE_SYSTEM_FILE_SERVICE, ( False, HC.CONTENT_STATUS_PENDING, CC.LOCAL_FILE_SERVICE_KEY ) )

		self.assertEqual( p.ToString(), 'system:is not pending to my files' )
		self.assertEqual( p.GetNamespace(), 'system' )
		self.assertEqual( p.GetTextsAndNamespaces(), [ ( p.ToString(), p.GetNamespace() ) ] )

		p = lib.Hydrus.Search.Predicate( HC.PREDICATE_TYPE_SYSTEM_HASH, ( bytes.fromhex( 'abcd' ), 'sha256' ) )

		self.assertEqual( p.ToString(), 'system:sha256 hash is abcd' )
		self.assertEqual( p.GetNamespace(), 'system' )
		self.assertEqual( p.GetTextsAndNamespaces(), [ ( p.ToString(), p.GetNamespace() ) ] )

		p = lib.Hydrus.Search.Predicate( HC.PREDICATE_TYPE_SYSTEM_HEIGHT, ( '<', 2000 ) )

		self.assertEqual( p.ToString(), 'system:height < 2,000' )
		self.assertEqual( p.GetNamespace(), 'system' )
		self.assertEqual( p.GetTextsAndNamespaces(), [ ( p.ToString(), p.GetNamespace() ) ] )

		p = lib.Hydrus.Search.Predicate( HC.PREDICATE_TYPE_SYSTEM_INBOX, min_current_count = 1000 )

		self.assertEqual( p.ToString(), 'system:inbox (1,000)' )
		self.assertEqual( p.GetNamespace(), 'system' )
		self.assertEqual( p.GetTextsAndNamespaces(), [ ( p.ToString(), p.GetNamespace() ) ] )

		p = lib.Hydrus.Search.Predicate( HC.PREDICATE_TYPE_SYSTEM_LIMIT, 2000 )

		self.assertEqual( p.ToString(), 'system:limit is 2,000' )
		self.assertEqual( p.GetNamespace(), 'system' )
		self.assertEqual( p.GetTextsAndNamespaces(), [ ( p.ToString(), p.GetNamespace() ) ] )

		p = lib.Hydrus.Search.Predicate( HC.PREDICATE_TYPE_SYSTEM_LOCAL, min_current_count = 100 )

		self.assertEqual( p.ToString(), 'system:local (100)' )
		self.assertEqual( p.GetNamespace(), 'system' )
		self.assertEqual( p.GetTextsAndNamespaces(), [ ( p.ToString(), p.GetNamespace() ) ] )

		p = lib.Hydrus.Search.Predicate( HC.PREDICATE_TYPE_SYSTEM_MIME, set( HC.IMAGES ).intersection( HC.SEARCHABLE_MIMES ) )

		self.assertEqual( p.ToString(), 'system:mime is image' )
		self.assertEqual( p.GetNamespace(), 'system' )
		self.assertEqual( p.GetTextsAndNamespaces(), [ ( p.ToString(), p.GetNamespace() ) ] )

		p = lib.Hydrus.Search.Predicate( HC.PREDICATE_TYPE_SYSTEM_MIME, ( HC.VIDEO_WEBM, ) )

		self.assertEqual( p.ToString(), 'system:mime is video/webm' )
		self.assertEqual( p.GetNamespace(), 'system' )
		self.assertEqual( p.GetTextsAndNamespaces(), [ ( p.ToString(), p.GetNamespace() ) ] )

		p = lib.Hydrus.Search.Predicate( HC.PREDICATE_TYPE_SYSTEM_MIME, ( HC.VIDEO_WEBM, HC.IMAGE_GIF ) )

		self.assertEqual( p.ToString(), 'system:mime is video/webm, image/gif' )
		self.assertEqual( p.GetNamespace(), 'system' )
		self.assertEqual( p.GetTextsAndNamespaces(), [ ( p.ToString(), p.GetNamespace() ) ] )

		p = lib.Hydrus.Search.Predicate( HC.PREDICATE_TYPE_SYSTEM_NOT_LOCAL, min_current_count = 100 )

		self.assertEqual( p.ToString(), 'system:not local (100)' )
		self.assertEqual( p.GetNamespace(), 'system' )
		self.assertEqual( p.GetTextsAndNamespaces(), [ ( p.ToString(), p.GetNamespace() ) ] )

		p = lib.Hydrus.Search.Predicate( HC.PREDICATE_TYPE_SYSTEM_NUM_TAGS, ( '<', 2 ) )

		self.assertEqual( p.ToString(), 'system:number of tags < 2' )
		self.assertEqual( p.GetNamespace(), 'system' )
		self.assertEqual( p.GetTextsAndNamespaces(), [ ( p.ToString(), p.GetNamespace() ) ] )

		p = lib.Hydrus.Search.Predicate( HC.PREDICATE_TYPE_SYSTEM_NUM_WORDS, ( '<', 5000 ) )

		self.assertEqual( p.ToString(), 'system:number of words < 5,000' )
		self.assertEqual( p.GetNamespace(), 'system' )
		self.assertEqual( p.GetTextsAndNamespaces(), [ ( p.ToString(), p.GetNamespace() ) ] )

		p = lib.Hydrus.Search.Predicate( HC.PREDICATE_TYPE_SYSTEM_RATING, ( '>', 0.2, CC.LOCAL_FILE_SERVICE_KEY ) )

		self.assertEqual( p.ToString(), 'system:rating for my files > 0.2' )
		self.assertEqual( p.GetNamespace(), 'system' )
		self.assertEqual( p.GetTextsAndNamespaces(), [ ( p.ToString(), p.GetNamespace() ) ] )

		p = lib.Hydrus.Search.Predicate( HC.PREDICATE_TYPE_SYSTEM_RATIO, ( '=', 16, 9 ) )

		self.assertEqual( p.ToString(), 'system:ratio = 16:9' )
		self.assertEqual( p.GetNamespace(), 'system' )
		self.assertEqual( p.GetTextsAndNamespaces(), [ ( p.ToString(), p.GetNamespace() ) ] )

		p = lib.Hydrus.Search.Predicate( HC.PREDICATE_TYPE_SYSTEM_SIMILAR_TO, ( bytes.fromhex( 'abcd' ), 5 ) )

		self.assertEqual( p.ToString(), 'system:similar to abcd using max hamming of 5' )
		self.assertEqual( p.GetNamespace(), 'system' )
		self.assertEqual( p.GetTextsAndNamespaces(), [ ( p.ToString(), p.GetNamespace() ) ] )

		p = lib.Hydrus.Search.Predicate( HC.PREDICATE_TYPE_SYSTEM_SIZE, ( '>', 5, 1048576 ) )

		self.assertEqual( p.ToString(), 'system:size > 5MB' )
		self.assertEqual( p.GetNamespace(), 'system' )
		self.assertEqual( p.GetTextsAndNamespaces(), [ ( p.ToString(), p.GetNamespace() ) ] )

		p = lib.Hydrus.Search.Predicate( HC.PREDICATE_TYPE_SYSTEM_UNTAGGED, HC.IMAGES )

		self.assertEqual( p.ToString(), 'system:untagged' )
		self.assertEqual( p.GetNamespace(), 'system' )
		self.assertEqual( p.GetTextsAndNamespaces(), [ ( p.ToString(), p.GetNamespace() ) ] )

		p = lib.Hydrus.Search.Predicate( HC.PREDICATE_TYPE_SYSTEM_WIDTH, ( '=', 1920 ) )

		self.assertEqual( p.ToString(), 'system:width = 1,920' )
		self.assertEqual( p.GetNamespace(), 'system' )
		self.assertEqual( p.GetTextsAndNamespaces(), [ ( p.ToString(), p.GetNamespace() ) ] )

		#

		p = lib.Hydrus.Search.Predicate( HC.PREDICATE_TYPE_NAMESPACE, 'series' )

		self.assertEqual( p.ToString(), 'series:*anything*' )
		self.assertEqual( p.GetNamespace(), 'series' )
		self.assertEqual( p.GetTextsAndNamespaces(), [ ( p.ToString(), p.GetNamespace() ) ] )

		p = lib.Hydrus.Search.Predicate( HC.PREDICATE_TYPE_TAG, 'series', False )

		self.assertEqual( p.ToString(), '-series' )
		self.assertEqual( p.GetNamespace(), '' )
		self.assertEqual( p.GetTextsAndNamespaces(), [ ( p.ToString(), p.GetNamespace() ) ] )

		#

		p = lib.Hydrus.Search.Predicate( HC.PREDICATE_TYPE_WILDCARD, 'a*i:o*' )

		self.assertEqual( p.ToString(), 'a*i:o* (wildcard search)' )
		self.assertEqual( p.GetNamespace(), 'a*i' )
		self.assertEqual( p.GetTextsAndNamespaces(), [ ( p.ToString(), p.GetNamespace() ) ] )

		p = lib.Hydrus.Search.Predicate( HC.PREDICATE_TYPE_TAG, 'a*i:o*', False )

		self.assertEqual( p.ToString(), '-a*i:o*' )
		self.assertEqual( p.GetNamespace(), 'a*i' )
		self.assertEqual( p.GetTextsAndNamespaces(), [ ( p.ToString(), p.GetNamespace() ) ] )

		#

		p = lib.Hydrus.Search.Predicate( HC.PREDICATE_TYPE_PARENT, 'series:game of thrones' )

		self.assertEqual( p.ToString(), '	series:game of thrones' )
		self.assertEqual( p.GetNamespace(), 'series' )
		self.assertEqual( p.GetTextsAndNamespaces(), [ ( p.ToString(), p.GetNamespace() ) ] )

		#

		p = lib.Hydrus.Search.Predicate( HC.PREDICATE_TYPE_OR_CONTAINER, [ lib.Hydrus.Search.Predicate( HC.PREDICATE_TYPE_SYSTEM_HEIGHT, ( '<', 2000 ) ), lib.Hydrus.Search.Predicate( HC.PREDICATE_TYPE_TAG, 'blue eyes' ), lib.Hydrus.Search.Predicate( HC.PREDICATE_TYPE_TAG, 'character:samus aran' ) ] )

		self.assertEqual( p.ToString(), 'system:height < 2,000 OR blue eyes OR character:samus aran' )
		self.assertEqual( p.GetNamespace(), '' )

		or_texts_and_namespaces = []

		or_texts_and_namespaces.append( ( 'system:height < 2,000', 'system' ) )
		or_texts_and_namespaces.append( ( ' OR ', 'system' ) )
		or_texts_and_namespaces.append( ( 'blue eyes', '' ) )
		or_texts_and_namespaces.append( ( ' OR ', 'system' ) )
		or_texts_and_namespaces.append( ( 'character:samus aran', 'character' ) )

		self.assertEqual( p.GetTextsAndNamespaces(), or_texts_and_namespaces )

class TestTagParents( unittest.TestCase ):
	@classmethod

	def setUpClass( cls ):
		cls._first_key = lib.Hydrus.Data.GenerateKey()
		cls._second_key = lib.Hydrus.Data.GenerateKey()
		cls._third_key = lib.Hydrus.Data.GenerateKey()

		first_dict = lib.Hydrus.Data.default_dict_set()

		first_dict[ HC.CONTENT_STATUS_CURRENT ] = { ( 'current_a', 'current_b' ), ( 'child', 'mother' ), ( 'child', 'father' ), ( 'sister', 'mother' ), ( 'sister', 'father' ), ( 'brother', 'mother' ), ( 'brother', 'father' ), ( 'mother', 'grandmother' ), ( 'mother', 'grandfather' ), ( 'aunt', 'grandmother' ), ( 'aunt', 'grandfather' ), ( 'cousin', 'aunt' ), ( 'cousin', 'uncle' ), ( 'closed_loop', 'closed_loop' ), ( 'loop_a', 'loop_b' ), ( 'loop_b', 'loop_c' ) }
		first_dict[ HC.CONTENT_STATUS_DELETED ] = { ( 'deleted_a', 'deleted_b' ) }

		second_dict = lib.Hydrus.Data.default_dict_set()

		second_dict[ HC.CONTENT_STATUS_CURRENT ] = { ( 'loop_c', 'loop_a' ) }
		second_dict[ HC.CONTENT_STATUS_DELETED ] = { ( 'current_a', 'current_b' ) }
		second_dict[ HC.CONTENT_STATUS_PENDING ] = { ( 'pending_a', 'pending_b' ) }
		second_dict[ HC.CONTENT_STATUS_PETITIONED ] = { ( 'petitioned_a', 'petitioned_b' ) }

		third_dict = lib.Hydrus.Data.default_dict_set()

		third_dict[ HC.CONTENT_STATUS_CURRENT ] = { ( 'petitioned_a', 'petitioned_b' ) }
		third_dict[ HC.CONTENT_STATUS_DELETED ] = { ( 'pending_a', 'pending_b' ) }

		tag_parents = collections.defaultdict( lib.Hydrus.Data.default_dict_set )

		tag_parents[ cls._first_key ] = first_dict
		tag_parents[ cls._second_key ] = second_dict
		tag_parents[ cls._third_key ] = third_dict

		HG.test_controller.SetRead( 'tag_parents', tag_parents )

		cls._tag_parents_manager = lib.Hydrus.Caches.TagParentsManager( HG.client_controller )

	def test_expand_predicates( self ):
		predicates = []

		predicates.append( lib.Hydrus.Search.Predicate( HC.PREDICATE_TYPE_TAG, 'grandmother', min_current_count = 10 ) )
		predicates.append( lib.Hydrus.Search.Predicate( HC.PREDICATE_TYPE_TAG, 'grandfather', min_current_count = 15 ) )
		predicates.append( lib.Hydrus.Search.Predicate( HC.PREDICATE_TYPE_TAG, 'not_exist', min_current_count = 20 ) )

		self.assertEqual( self._tag_parents_manager.ExpandPredicates( CC.COMBINED_TAG_SERVICE_KEY, predicates ), predicates )

		predicates = []

		predicates.append( lib.Hydrus.Search.Predicate( HC.PREDICATE_TYPE_TAG, 'child', min_current_count = 10 ) )

		results = []

		results.append( lib.Hydrus.Search.Predicate( HC.PREDICATE_TYPE_TAG, 'child', min_current_count = 10 ) )
		results.append( lib.Hydrus.Search.Predicate( HC.PREDICATE_TYPE_PARENT, 'mother' ) )
		results.append( lib.Hydrus.Search.Predicate( HC.PREDICATE_TYPE_PARENT, 'father' ) )
		results.append( lib.Hydrus.Search.Predicate( HC.PREDICATE_TYPE_PARENT, 'grandmother' ) )
		results.append( lib.Hydrus.Search.Predicate( HC.PREDICATE_TYPE_PARENT, 'grandfather' ) )

		self.assertEqual( set( self._tag_parents_manager.ExpandPredicates( CC.COMBINED_TAG_SERVICE_KEY, predicates ) ), set( results ) )

		predicates = []

		predicates.append( lib.Hydrus.Search.Predicate( HC.PREDICATE_TYPE_NAMESPACE, 'series' ) )
		predicates.append( lib.Hydrus.Search.Predicate( HC.PREDICATE_TYPE_TAG, 'child', min_current_count = 10 ) )
		predicates.append( lib.Hydrus.Search.Predicate( HC.PREDICATE_TYPE_TAG, 'cousin', min_current_count = 5 ) )

		results = []

		results.append( lib.Hydrus.Search.Predicate( HC.PREDICATE_TYPE_NAMESPACE, 'series' ) )
		results.append( lib.Hydrus.Search.Predicate( HC.PREDICATE_TYPE_TAG, 'child', min_current_count = 10 ) )
		results.append( lib.Hydrus.Search.Predicate( HC.PREDICATE_TYPE_PARENT, 'mother' ) )
		results.append( lib.Hydrus.Search.Predicate( HC.PREDICATE_TYPE_PARENT, 'father' ) )
		results.append( lib.Hydrus.Search.Predicate( HC.PREDICATE_TYPE_PARENT, 'grandmother' ) )
		results.append( lib.Hydrus.Search.Predicate( HC.PREDICATE_TYPE_PARENT, 'grandfather' ) )
		results.append( lib.Hydrus.Search.Predicate( HC.PREDICATE_TYPE_TAG, 'cousin', min_current_count = 5 ) )
		results.append( lib.Hydrus.Search.Predicate( HC.PREDICATE_TYPE_PARENT, 'aunt' ) )
		results.append( lib.Hydrus.Search.Predicate( HC.PREDICATE_TYPE_PARENT, 'uncle' ) )
		results.append( lib.Hydrus.Search.Predicate( HC.PREDICATE_TYPE_PARENT, 'grandmother' ) )
		results.append( lib.Hydrus.Search.Predicate( HC.PREDICATE_TYPE_PARENT, 'grandfather' ) )

		self.assertEqual( set( self._tag_parents_manager.ExpandPredicates( CC.COMBINED_TAG_SERVICE_KEY, predicates ) ), set( results ) )

	def test_expand_tags( self ):
		tags = { 'grandmother', 'grandfather' }

		self.assertEqual( self._tag_parents_manager.ExpandTags( CC.COMBINED_TAG_SERVICE_KEY, tags ), tags )

		tags = { 'child', 'cousin' }

		results = { 'child', 'mother', 'father', 'grandmother', 'grandfather', 'cousin', 'aunt', 'uncle', 'grandmother', 'grandfather' }

		self.assertEqual( self._tag_parents_manager.ExpandTags( CC.COMBINED_TAG_SERVICE_KEY, tags ), results )

	def test_grandparents( self ):
		self.assertEqual( set( self._tag_parents_manager.GetParents( CC.COMBINED_TAG_SERVICE_KEY, 'child' ) ), { 'mother', 'father', 'grandmother', 'grandfather' } )
		self.assertEqual( set( self._tag_parents_manager.GetParents( CC.COMBINED_TAG_SERVICE_KEY, 'mother' ) ), { 'grandmother', 'grandfather' } )
		self.assertEqual( set( self._tag_parents_manager.GetParents( CC.COMBINED_TAG_SERVICE_KEY, 'grandmother' ) ), set() )

	def test_current_overwrite( self ):
		self.assertEqual( self._tag_parents_manager.GetParents( CC.COMBINED_TAG_SERVICE_KEY, 'current_a' ), [ 'current_b' ] )
		self.assertEqual( self._tag_parents_manager.GetParents( CC.COMBINED_TAG_SERVICE_KEY, 'current_b' ), [] )

		self.assertEqual( self._tag_parents_manager.ExpandTags( CC.COMBINED_TAG_SERVICE_KEY, [ 'current_a' ] ), { 'current_a', 'current_b' } )
		self.assertEqual( self._tag_parents_manager.ExpandTags( CC.COMBINED_TAG_SERVICE_KEY, [ 'current_b' ] ), { 'current_b' } )

	def test_deleted( self ):
		self.assertEqual( self._tag_parents_manager.GetParents( CC.COMBINED_TAG_SERVICE_KEY, 'deleted_a' ), [] )
		self.assertEqual( self._tag_parents_manager.GetParents( CC.COMBINED_TAG_SERVICE_KEY, 'deleted_b' ), [] )

		self.assertEqual( self._tag_parents_manager.ExpandTags( CC.COMBINED_TAG_SERVICE_KEY, [ 'deleted_a' ] ), { 'deleted_a' } )
		self.assertEqual( self._tag_parents_manager.ExpandTags( CC.COMBINED_TAG_SERVICE_KEY, [ 'deleted_b' ] ), { 'deleted_b' } )

	def test_no_loop( self ):
		self.assertEqual( self._tag_parents_manager.GetParents( CC.COMBINED_TAG_SERVICE_KEY, 'closed_loop' ), [] )

		self.assertEqual( self._tag_parents_manager.ExpandTags( CC.COMBINED_TAG_SERVICE_KEY, [ 'closed_loop' ] ), { 'closed_loop' } )

	def test_not_exist( self ):
		self.assertEqual( self._tag_parents_manager.GetParents( CC.COMBINED_TAG_SERVICE_KEY, 'not_exist' ), [] )

		self.assertEqual( self._tag_parents_manager.ExpandTags( CC.COMBINED_TAG_SERVICE_KEY, [ 'not_exist' ] ), { 'not_exist' } )

	def test_pending_overwrite( self ):
		self.assertEqual( self._tag_parents_manager.GetParents( CC.COMBINED_TAG_SERVICE_KEY, 'pending_a' ), [ 'pending_b' ] )
		self.assertEqual( self._tag_parents_manager.GetParents( CC.COMBINED_TAG_SERVICE_KEY, 'pending_b' ), [] )

		self.assertEqual( self._tag_parents_manager.ExpandTags( CC.COMBINED_TAG_SERVICE_KEY, [ 'pending_a' ] ), { 'pending_a', 'pending_b' } )
		self.assertEqual( self._tag_parents_manager.ExpandTags( CC.COMBINED_TAG_SERVICE_KEY, [ 'pending_b' ] ), { 'pending_b' } )

class TestTagSiblings( unittest.TestCase ):
	@classmethod

	def setUpClass( cls ):
		cls._first_key = lib.Hydrus.Data.GenerateKey()
		cls._second_key = lib.Hydrus.Data.GenerateKey()

		tag_siblings = collections.defaultdict( lib.Hydrus.Data.default_dict_set )

		first_dict = lib.Hydrus.Data.default_dict_set()

		first_dict[ HC.CONTENT_STATUS_CURRENT ] = { ( 'ishygddt', 'i sure hope you guys don\'t do that' ), ( 'character:rei ayanami', 'character:ayanami rei' ), ( 'tree_1', 'tree_3' ), ( 'tree_2', 'tree_3' ), ( 'tree_3', 'tree_5' ), ( 'tree_4', 'tree_5' ), ( 'tree_5', 'tree_6' ), ( 'current_a', 'current_b' ), ( 'chain_a', 'chain_b' ), ( 'chain_b', 'chain_c' ), ( 'closed_loop', 'closed_loop' ), ( 'loop_a', 'loop_b' ), ( 'loop_b', 'loop_c' ), ( 'loop_c', 'loop_a' ) }
		first_dict[ HC.CONTENT_STATUS_DELETED ] = { ( 'deleted_a', 'deleted_b' ) }

		second_dict = lib.Hydrus.Data.default_dict_set()

		second_dict[ HC.CONTENT_STATUS_CURRENT ] = { ( 'loop_c', 'loop_a' ), ( 'current_a', 'current_b' ), ( 'petitioned_a', 'petitioned_b' ) }
		second_dict[ HC.CONTENT_STATUS_DELETED ] = { ( 'pending_a', 'pending_b' ) }
		second_dict[ HC.CONTENT_STATUS_PENDING ] = { ( 'pending_a', 'pending_b' ) }
		second_dict[ HC.CONTENT_STATUS_PETITIONED ] = { ( 'petitioned_a', 'petitioned_b' ) }

		tag_siblings[ cls._first_key ] = first_dict
		tag_siblings[ cls._second_key ] = second_dict

		HG.test_controller.SetRead( 'tag_siblings', tag_siblings )

		cls._tag_siblings_manager = lib.Hydrus.Caches.TagSiblingsManager( HG.client_controller )

	def test_collapse_predicates( self ):
		predicates = []

		predicates.append( lib.Hydrus.Search.Predicate( HC.PREDICATE_TYPE_TAG, 'chain_a', min_current_count = 10 ) )
		predicates.append( lib.Hydrus.Search.Predicate( HC.PREDICATE_TYPE_TAG, 'chain_b', min_current_count = 5 ) )
		predicates.append( lib.Hydrus.Search.Predicate( HC.PREDICATE_TYPE_TAG, 'chain_c', min_current_count = 20 ) )

		results = [ lib.Hydrus.Search.Predicate( HC.PREDICATE_TYPE_TAG, 'chain_c', min_current_count = 20, max_current_count = 35 ) ]

		self.assertEqual( self._tag_siblings_manager.CollapsePredicates( self._first_key, predicates ), results )

		predicates = []

		predicates.append( lib.Hydrus.Search.Predicate( HC.PREDICATE_TYPE_TAG, 'chain_a', min_current_count = 10 ) )
		predicates.append( lib.Hydrus.Search.Predicate( HC.PREDICATE_TYPE_TAG, 'chain_b', min_current_count = 5 ) )
		predicates.append( lib.Hydrus.Search.Predicate( HC.PREDICATE_TYPE_TAG, 'chain_c', min_current_count = 20 ) )

		( result, ) = self._tag_siblings_manager.CollapsePredicates( self._first_key, predicates )

		self.assertEqual( result.GetCount(), 20 )
		self.assertEqual( result.ToString(), 'chain_c (20-35)' )

	def test_chain( self ):
		self.assertEqual( self._tag_siblings_manager.GetSibling( self._first_key, 'chain_a' ), 'chain_c' )
		self.assertEqual( self._tag_siblings_manager.GetSibling( self._first_key, 'chain_b' ), 'chain_c' )
		self.assertEqual( self._tag_siblings_manager.GetSibling( self._first_key, 'chain_c' ), None )

		self.assertEqual( set( self._tag_siblings_manager.GetAllSiblings( self._first_key, 'chain_a' ) ), set( [ 'chain_a', 'chain_b', 'chain_c' ] ) )
		self.assertEqual( set( self._tag_siblings_manager.GetAllSiblings( self._first_key, 'chain_b' ) ), set( [ 'chain_a', 'chain_b', 'chain_c' ] ) )
		self.assertEqual( set( self._tag_siblings_manager.GetAllSiblings( self._first_key, 'chain_c' ) ), set( [ 'chain_a', 'chain_b', 'chain_c' ] ) )

		self.assertEqual( set( self._tag_siblings_manager.CollapseTags( self._first_key, [ 'chain_a', 'chain_b' ] ) ), set( [ 'chain_c' ] ) )
		self.assertEqual( set( self._tag_siblings_manager.CollapseTags( self._first_key, [ 'chain_a', 'chain_b', 'chain_c' ] ) ), set( [ 'chain_c' ] ) )

	def test_current( self ):
		self.assertEqual( self._tag_siblings_manager.GetSibling( self._second_key, 'current_a' ), 'current_b' )
		self.assertEqual( self._tag_siblings_manager.GetSibling( self._second_key, 'current_b' ), None )

		self.assertEqual( set( self._tag_siblings_manager.GetAllSiblings( self._second_key, 'current_a' ) ), set( [ 'current_a', 'current_b' ] ) )
		self.assertEqual( set( self._tag_siblings_manager.GetAllSiblings( self._second_key, 'current_b' ) ), set( [ 'current_a', 'current_b' ] ) )

		self.assertEqual( set( self._tag_siblings_manager.CollapseTags( self._first_key, [ 'chain_a', 'chain_b' ] ) ), set( [ 'chain_c' ] ) )
		self.assertEqual( set( self._tag_siblings_manager.CollapseTags( self._first_key, [ 'chain_a', 'chain_b', 'chain_c' ] ) ), set( [ 'chain_c' ] ) )

		self.assertEqual( self._tag_siblings_manager.CollapseTagsToCount( self._first_key, { 'chain_a' : 10, 'chain_b' : 5 } ), { 'chain_c' : 15 } )
		self.assertEqual( self._tag_siblings_manager.CollapseTagsToCount( self._first_key, { 'chain_a' : 10, 'chain_b' : 5, 'chain_c' : 20 } ), { 'chain_c' : 35 } )

	def test_deleted( self ):
		self.assertEqual( self._tag_siblings_manager.GetSibling( self._first_key, 'deleted_a' ), None )
		self.assertEqual( self._tag_siblings_manager.GetSibling( self._first_key, 'deleted_b' ), None )

		self.assertEqual( set( self._tag_siblings_manager.GetAllSiblings( self._first_key, 'deleted_a' ) ), set( [ 'deleted_a' ] ) )
		self.assertEqual( set( self._tag_siblings_manager.GetAllSiblings( self._first_key, 'deleted_b' ) ), set( [ 'deleted_b' ] ) )

		self.assertEqual( set( self._tag_siblings_manager.CollapseTags( self._first_key, [ 'deleted_a', 'deleted_b' ] ) ), set( [ 'deleted_a', 'deleted_b' ] ) )

		self.assertEqual( self._tag_siblings_manager.CollapseTagsToCount( self._first_key, { 'deleted_a' : 10, 'deleted_b' : 5 } ), { 'deleted_a' : 10, 'deleted_b' : 5 } )

	def test_no_loop( self ):
		self.assertEqual( self._tag_siblings_manager.GetSibling( self._first_key, 'closed_loop' ), None )

		self.assertEqual( set( self._tag_siblings_manager.GetAllSiblings( self._first_key, 'closed_loop' ) ), set( [ 'closed_loop' ] ) )

		self.assertEqual( set( self._tag_siblings_manager.CollapseTags( self._first_key, [ 'closed_loop' ] ) ), set( [ 'closed_loop' ] ) )

		self.assertEqual( self._tag_siblings_manager.CollapseTagsToCount( self._first_key, { 'closed_loop' : 10 } ), { 'closed_loop' : 10 } )

	def test_not_exist( self ):
		self.assertEqual( self._tag_siblings_manager.GetSibling( self._second_key, 'not_exist' ), None )

		self.assertEqual( set( self._tag_siblings_manager.GetAllSiblings( self._second_key, 'not_exist' ) ), set( [ 'not_exist' ] ) )

		self.assertEqual( set( self._tag_siblings_manager.CollapseTags( self._second_key, [ 'not_exist' ] ) ), set( [ 'not_exist' ] ) )

		self.assertEqual( self._tag_siblings_manager.CollapseTagsToCount( self._second_key, { 'not_exist' : 10 } ), { 'not_exist' : 10 } )

	def test_pending_overwrite( self ):
		self.assertEqual( self._tag_siblings_manager.GetSibling( self._second_key, 'pending_a' ), 'pending_b' )
		self.assertEqual( self._tag_siblings_manager.GetSibling( self._second_key, 'pending_b' ), None )

		self.assertEqual( set( self._tag_siblings_manager.GetAllSiblings( self._second_key, 'pending_a' ) ), set( [ 'pending_a', 'pending_b' ] ) )
		self.assertEqual( set( self._tag_siblings_manager.GetAllSiblings( self._second_key, 'pending_b' ) ), set( [ 'pending_a', 'pending_b' ] ) )

		self.assertEqual( set( self._tag_siblings_manager.CollapseTags( self._second_key, [ 'pending_a' ] ) ), set( [ 'pending_b' ] ) )
		self.assertEqual( set( self._tag_siblings_manager.CollapseTags( self._second_key, [ 'pending_a', 'pending_b' ] ) ), set( [ 'pending_b' ] ) )

		self.assertEqual( self._tag_siblings_manager.CollapseTagsToCount( self._second_key, { 'pending_a' : 10 } ), { 'pending_b' : 10 } )
		self.assertEqual( self._tag_siblings_manager.CollapseTagsToCount( self._second_key, { 'pending_a' : 10, 'pending_b' : 5 } ), { 'pending_b' : 15 } )

	def test_petitioned_no_overwrite( self ):
		self.assertEqual( self._tag_siblings_manager.GetSibling( self._second_key, 'petitioned_a' ), 'petitioned_b' )
		self.assertEqual( self._tag_siblings_manager.GetSibling( self._second_key, 'petitioned_b' ), None )

		self.assertEqual( set( self._tag_siblings_manager.GetAllSiblings( self._second_key, 'petitioned_a' ) ), set( [ 'petitioned_a', 'petitioned_b' ] ) )
		self.assertEqual( set( self._tag_siblings_manager.GetAllSiblings( self._second_key, 'petitioned_b' ) ), set( [ 'petitioned_a', 'petitioned_b' ] ) )

		self.assertEqual( set( self._tag_siblings_manager.CollapseTags( self._second_key, [ 'petitioned_a' ] ) ), set( [ 'petitioned_b' ] ) )
		self.assertEqual( set( self._tag_siblings_manager.CollapseTags( self._second_key, [ 'petitioned_a', 'petitioned_b' ] ) ), set( [ 'petitioned_b' ] ) )

		self.assertEqual( self._tag_siblings_manager.CollapseTagsToCount( self._second_key, { 'petitioned_a' : 10 } ), { 'petitioned_b' : 10 } )
		self.assertEqual( self._tag_siblings_manager.CollapseTagsToCount( self._second_key, { 'petitioned_a' : 10, 'petitioned_b' : 5 } ), { 'petitioned_b' : 15 } )

	def test_tree( self ):
		self.assertEqual( self._tag_siblings_manager.GetSibling( self._first_key, 'tree_1' ), 'tree_6' )
		self.assertEqual( self._tag_siblings_manager.GetSibling( self._first_key, 'tree_2' ), 'tree_6' )
		self.assertEqual( self._tag_siblings_manager.GetSibling( self._first_key, 'tree_3' ), 'tree_6' )
		self.assertEqual( self._tag_siblings_manager.GetSibling( self._first_key, 'tree_4' ), 'tree_6' )
		self.assertEqual( self._tag_siblings_manager.GetSibling( self._first_key, 'tree_5' ), 'tree_6' )
		self.assertEqual( self._tag_siblings_manager.GetSibling( self._first_key, 'tree_6' ), None )

		self.assertEqual( set( self._tag_siblings_manager.GetAllSiblings( self._first_key, 'tree_1' ) ), set( [ 'tree_1', 'tree_2', 'tree_3', 'tree_4', 'tree_5', 'tree_6' ] ) )
		self.assertEqual( set( self._tag_siblings_manager.GetAllSiblings( self._first_key, 'tree_2' ) ), set( [ 'tree_1', 'tree_2', 'tree_3', 'tree_4', 'tree_5', 'tree_6' ] ) )
		self.assertEqual( set( self._tag_siblings_manager.GetAllSiblings( self._first_key, 'tree_3' ) ), set( [ 'tree_1', 'tree_2', 'tree_3', 'tree_4', 'tree_5', 'tree_6' ] ) )
		self.assertEqual( set( self._tag_siblings_manager.GetAllSiblings( self._first_key, 'tree_4' ) ), set( [ 'tree_1', 'tree_2', 'tree_3', 'tree_4', 'tree_5', 'tree_6' ] ) )
		self.assertEqual( set( self._tag_siblings_manager.GetAllSiblings( self._first_key, 'tree_5' ) ), set( [ 'tree_1', 'tree_2', 'tree_3', 'tree_4', 'tree_5', 'tree_6' ] ) )
		self.assertEqual( set( self._tag_siblings_manager.GetAllSiblings( self._first_key, 'tree_6' ) ), set( [ 'tree_1', 'tree_2', 'tree_3', 'tree_4', 'tree_5', 'tree_6' ] ) )

		self.assertEqual( set( self._tag_siblings_manager.CollapseTags( self._first_key, [ 'tree_1' ] ) ), set( [ 'tree_6' ] ) )
		self.assertEqual( set( self._tag_siblings_manager.CollapseTags( self._first_key, [ 'tree_1', 'tree_3', 'tree_5' ] ) ), set( [ 'tree_6' ] ) )
		self.assertEqual( set( self._tag_siblings_manager.CollapseTags( self._first_key, [ 'tree_1', 'tree_2', 'tree_3', 'tree_4', 'tree_5', 'tree_6' ] ) ), set( [ 'tree_6' ] ) )

		self.assertEqual( self._tag_siblings_manager.CollapseTagsToCount( self._first_key, { 'tree_1' : 10 } ), { 'tree_6' : 10 } )
		self.assertEqual( self._tag_siblings_manager.CollapseTagsToCount( self._first_key, { 'tree_1' : 10, 'tree_3' : 5, 'tree_5' : 20 } ), { 'tree_6' : 35 } )
		self.assertEqual( self._tag_siblings_manager.CollapseTagsToCount( self._first_key, { 'tree_1' : 10, 'tree_2' : 3, 'tree_3' : 5, 'tree_4' : 2, 'tree_5' : 20, 'tree_6' : 30 } ), { 'tree_6' : 70 } )


code:
strings = (
	("lib.Actions.Importing", "lib.Actions.Import"),
	#(" SerialisableBase", " lib.Hydrus.Serialisable"),
	#("\nSerialisableBase", "\nlib.Hydrus.Serialisable"),
	#("    ", "\t")
)

license: wtfpl


code:
from filename import Aesthetic, Client, ClientGUI, ClientImport, Hydrus, Server, Test
from classname import Hydrus as HydrusClass
import Refactors

import os, glob, re

ToReplace = [
	Client,
	ClientGUI,
	ClientImport,
	Hydrus,
	Server,
	HydrusClass,
	#Test,
	Refactors
]

RegexReplace = [
	Aesthetic
]

def replace(file):
	filetext = file.read()
	origfile = filetext
	for prefix in ToReplace:
		for replacement in prefix.strings[::-1]:
			filetext = filetext.replace(replacement[0], replacement[1])

	for category in RegexReplace:
		for regex in category.strings[::-1]:
			filetext = re.sub(regex[0], regex[1], filetext)

	if filetext != origfile:
		file.seek(0)
		file.truncate()
		file.write(filetext)

for path in glob.iglob('lib/**', recursive=True):
	if os.path.isfile(path) and not "__" in path and ".py" in path:
		print(path)
		with open(path, "r+", encoding="utf-8") as file:
			replace(file)


code:
strings = (
	( "HydrusException ", "HydrusExceptionBase " ),
	( "HydrusController", "ControllerBase"),
	( "HydrusSerialisable", "SerialisableBase")
)


code:
strings = (
	( "ClientAPI.", "lib.Hydrus.API.Local." ),
	( "from . import ClientAPI", "import lib.Hydrus.API.Local" ),
	( "# ClientAPI", "import lib.Hydrus.API.Local" ),

	( "ClientCaches.", "lib.Hydrus.Caches." ),
	( "from . import ClientCaches", "import lib.Hydrus.Caches" ),
	( "# ClientCaches", "import lib.Hydrus.Caches" ),

	( "ClientConstants as CC.", "lib.Hydrus.Constants.Local as CC." ),
	( "from . import ClientConstants as CC", "import lib.Hydrus.Constants.Local as CC" ),
	( "# ClientConstants as CC", "import lib.Hydrus.Constants.Local as CC" ),

	( "ClientController.", "lib.Hydrus.Controller.Local." ),
	( "from . import ClientController", "import lib.Hydrus.Controller.Local" ),
	( "# ClientController", "import lib.Hydrus.Controller.Local" ),

	( "ClientDaemons.", "lib.Hydrus.Daemons." ),
	( "from . import ClientDaemons", "import lib.Hydrus.Daemons" ),
	( "# ClientDaemons", "import lib.Hydrus.Daemons" ),

	( "ClientData.", "lib.Hydrus.Data.Local." ),
	( "from . import ClientData", "import lib.Hydrus.Data.Local" ),
	( "# ClientData", "import lib.Hydrus.Data.Local" ),

	( "ClientDB.", "lib.Hydrus.DB.Local." ),
	( "from . import ClientDB", "import lib.Hydrus.DB.Local" ),
	( "# ClientDB", "import lib.Hydrus.DB.Local" ),

	( "ClientDefaults.", "lib.Hydrus.Defaults." ),
	( "from . import ClientDefaults", "import lib.Hydrus.Defaults" ),
	( "# ClientDefaults", "import lib.Hydrus.Defaults" ),

	( "ClientDownloading.", "lib.Networking.Downloading." ),
	( "from . import ClientDownloading", "import lib.Networking.Downloading" ),
	( "# ClientDownloading", "import lib.Networking.Downloading" ),

	( "ClientDragDrop.", "lib.GUI.DragDrop." ),
	( "from . import ClientDragDrop", "import lib.GUI.DragDrop" ),
	( "# ClientDragDrop", "import lib.GUI.DragDrop" ),

	( "ClientDuplicates.", "lib.Hydrus.Duplicates." ),
	( "from . import ClientDuplicates", "import lib.Hydrus.Duplicates" ),
	( "# ClientDuplicates", "import lib.Hydrus.Duplicates" ),

	( "ClientExporting.", "lib.Actions.Export." ),
	( "from . import ClientExporting", "import lib.Actions.Export" ),
	( "# ClientExporting", "import lib.Actions.Export" ),

	( "ClientFiles.", "lib.Hydrus.Files.Local." ),
	( "from . import ClientFiles", "import lib.Hydrus.Files.Local" ),
	( "# ClientFiles", "import lib.Hydrus.Files.Local" ),

	( "ClientLocalServer.", "lib.Hydrus.Server.Local." ),
	( "from . import ClientLocalServer", "import lib.Hydrus.Server.Local" ),
	( "# ClientLocalServer", "import lib.Hydrus.Server.Local" ),

	( "ClientLocalServerResources.", "lib.Hydrus.Server.LocalResources." ),
	( "from . import ClientLocalServerResources", "import lib.Hydrus.Server.LocalResources" ),
	( "# ClientLocalServerResources", "import lib.Hydrus.Server.LocalResources" ),

	( "ClientImageHandling.", "lib.Handling.Image.Local." ),
	( "from . import ClientImageHandling", "import lib.Handling.Image.Local" ),
	( "# ClientImageHandling", "import lib.Handling.Image.Local" ),

	( "ClientMedia.", "lib.Hydrus.Media." ),
	( "from . import ClientMedia", "import lib.Hydrus.Media" ),
	( "# ClientMedia", "import lib.Hydrus.Media" ),

	( "ClientNetworking.", "lib.Networking." ),
	( "from . import ClientNetworking", "import lib.Networking" ),
	( "# ClientNetworking", "import lib.Networking" ),

	( "ClientNetworkingBandwidth.", "lib.Networking.Bandwidth." ),
	( "from . import ClientNetworkingBandwidth", "import lib.Networking.Bandwidth" ),
	( "# ClientNetworkingBandwidth", "import lib.Networking.Bandwidth" ),

	( "ClientNetworkingContexts.", "lib.Networking.Contexts." ),
	( "from . import ClientNetworkingContexts", "import lib.Networking.Contexts" ),
	( "# ClientNetworkingContexts", "import lib.Networking.Contexts" ),

	( "ClientNetworkingDomain.", "lib.Networking.Domain." ),
	( "from . import ClientNetworkingDomain", "import lib.Networking.Domain" ),
	( "# ClientNetworkingDomain", "import lib.Networking.Domain" ),

	( "ClientNetworkingJobs.", "lib.Networking.Jobs." ),
	( "from . import ClientNetworkingJobs", "import lib.Networking.Jobs" ),
	( "# ClientNetworkingJobs", "import lib.Networking.Jobs" ),

	( "ClientNetworkingLogin.", "lib.Networking.Login." ),
	( "from . import ClientNetworkingLogin", "import lib.Networking.Login" ),
	( "# ClientNetworkingLogin", "import lib.Networking.Login" ),

	( "ClientNetworkingSessions.", "lib.Networking.Sessions." ),
	( "from . import ClientNetworkingSessions", "import lib.Networking.Sessions" ),
	( "# ClientNetworkingSessions", "import lib.Networking.Sessions" ),

	( "ClientOptions.", "lib.Hydrus.Options." ),
	( "from . import ClientOptions", "import lib.Hydrus.Options" ),
	( "# ClientOptions", "import lib.Hydrus.Options" ),

	( "ClientParsing.", "lib.Handling.Parsing." ),
	( "from . import ClientParsing", "import lib.Handling.Parsing" ),
	( "# ClientParsing", "import lib.Handling.Parsing" ),

	( "ClientPaths.", "lib.Hydrus.Paths.Local." ),
	( "from . import ClientPaths", "import lib.Hydrus.Paths.Local" ),
	( "# ClientPaths", "import lib.Hydrus.Paths.Local" ),

	( "ClientRatings.", "lib.Hydrus.Ratings." ),
	( "from . import ClientRatings", "import lib.Hydrus.Ratings" ),
	( "# ClientRatings", "import lib.Hydrus.Ratings" ),

	( "ClientRendering.", "lib.GUI.Rendering." ),
	( "from . import ClientRendering", "import lib.GUI.Rendering" ),
	( "# ClientRendering", "import lib.GUI.Rendering" ),

	( "ClientSearch.", "lib.Hydrus.Search." ),
	( "from . import ClientSearch", "import lib.Hydrus.Search" ),
	( "# ClientSearch", "import lib.Hydrus.Search" ),

	( "ClientSerialisable.", "lib.Hydrus.Serialisable.Local." ),
	( "from . import ClientSerialisable", "import lib.Hydrus.Serialisable.Local" ),
	( "# ClientSerialisable", "import lib.Hydrus.Serialisable.Local" ),

	( "ClientServices.", "lib.Hydrus.Services.Local." ),
	( "from . import ClientServices", "import lib.Hydrus.Services.Local" ),
	( "# ClientServices", "import lib.Hydrus.Services.Local" ),

	( "ClientTags.", "lib.Hydrus.Tags.Local." ),
	( "from . import ClientTags", "import lib.Hydrus.Tags.Local" ),
	( "# ClientTags", "import lib.Hydrus.Tags.Local" ),

	( "ClientThreading.", "lib.Hydrus.Threading." ),
	( "from . import ClientThreading", "import lib.Hydrus.Threading" ),
	( "# ClientThreading", "import lib.Hydrus.Threading" ),

	( "ClientVideoHandling.", "lib.Handling.Video.Local." ),
	( "from . import ClientVideoHandling", "import lib.Handling.Video.Local" ),
	( "# ClientVideoHandling", "import lib.Handling.Video.Local" ),
)


code:
strings = (
	( "ClientGUI.", "lib.GUI." ),
	( "from . import ClientGUI", "import lib.GUI" ),
	( "# ClientGUI", "import lib.GUI" ),

	( "ClientGUIACDropdown.", "lib.GUI.ACDropdown." ),
	( "from . import ClientGUIACDropdown", "import lib.GUI.ACDropdown" ),
	( "# ClientGUIACDropdown", "import lib.GUI.ACDropdown" ),

	( "ClientGUIAPI.", "lib.GUI.API." ),
	( "from . import ClientGUIAPI", "import lib.GUI.API" ),
	( "# ClientGUIAPI", "import lib.GUI.API" ),

	( "ClientGUICanvas.", "lib.GUI.Canvas." ),
	( "from . import ClientGUICanvas", "import lib.GUI.Canvas" ),
	( "# ClientGUICanvas", "import lib.GUI.Canvas" ),

	( "ClientGUICommon.", "lib.GUI.Common." ),
	( "from . import ClientGUICommon", "import lib.GUI.Common" ),
	( "# ClientGUICommon", "import lib.GUI.Common" ),

	( "ClientGUIControls.", "lib.GUI.Controls." ),
	( "from . import ClientGUIControls", "import lib.GUI.Controls" ),
	( "# ClientGUIControls", "import lib.GUI.Controls" ),

	( "ClientGUIDialogs.", "lib.GUI.Dialogs." ),
	( "from . import ClientGUIDialogs", "import lib.GUI.Dialogs" ),
	( "# ClientGUIDialogs", "import lib.GUI.Dialogs" ),

	( "ClientGUIDialogsManage.", "lib.GUI.Dialogs.Manage." ),
	( "from . import ClientGUIDialogsManage", "import lib.GUI.Dialogs.Manage" ),
	( "# ClientGUIDialogsManage", "import lib.GUI.Dialogs.Manage" ),

	( "ClientGUIDialogsQuick.", "lib.GUI.Dialogs.Quick." ),
	( "from . import ClientGUIDialogsQuick", "import lib.GUI.Dialogs.Quick" ),
	( "# ClientGUIDialogsQuick", "import lib.GUI.Dialogs.Quick" ),

	( "ClientGUIExport.", "lib.GUI.Export." ),
	( "from . import ClientGUIExport", "import lib.GUI.Export" ),
	( "# ClientGUIExport", "import lib.GUI.Export" ),

	( "ClientGUIFileSeedCache.", "lib.GUI.FileSeedCache." ),
	( "from . import ClientGUIFileSeedCache", "import lib.GUI.FileSeedCache" ),
	( "# ClientGUIFileSeedCache", "import lib.GUI.FileSeedCache" ),

	( "ClientGUIFrames.", "lib.GUI.Frames." ),
	( "from . import ClientGUIFrames", "import lib.GUI.Frames" ),
	( "# ClientGUIFrames", "import lib.GUI.Frames" ),

	( "ClientGUIGallerySeedLog.", "lib.GUI.GallerySeedLog." ),
	( "from . import ClientGUIGallerySeedLog", "import lib.GUI.GallerySeedLog" ),
	( "# ClientGUIGallerySeedLog", "import lib.GUI.GallerySeedLog" ),

	( "ClientGUIHoverFrames.", "lib.GUI.HoverFrames." ),
	( "from . import ClientGUIHoverFrames", "import lib.GUI.HoverFrames" ),
	( "# ClientGUIHoverFrames", "import lib.GUI.HoverFrames" ),

	( "ClientGUIImport.", "lib.GUI.Import." ),
	( "from . import ClientGUIImport", "import lib.GUI.Import" ),
	( "# ClientGUIImport", "import lib.GUI.Import" ),

	( "ClientGUIListBoxes.", "lib.GUI.ListBoxes." ),
	( "from . import ClientGUIListBoxes", "import lib.GUI.ListBoxes" ),
	( "# ClientGUIListBoxes", "import lib.GUI.ListBoxes" ),

	( "ClientGUIListCtrl.", "lib.GUI.ListCtrl." ),
	( "from . import ClientGUIListCtrl", "import lib.GUI.ListCtrl" ),
	( "# ClientGUIListCtrl", "import lib.GUI.ListCtrl" ),

	( "ClientGUILogin.", "lib.GUI.Login." ),
	( "from . import ClientGUILogin", "import lib.GUI.Login" ),
	( "# ClientGUILogin", "import lib.GUI.Login" ),

	( "ClientGUIManagement.", "lib.GUI.Management." ),
	( "from . import ClientGUIManagement", "import lib.GUI.Management" ),
	( "# ClientGUIManagement", "import lib.GUI.Management" ),

	( "ClientGUIMatPlotLib.", "lib.GUI.MatPlotLib." ),
	( "from . import ClientGUIMatPlotLib", "import lib.GUI.MatPlotLib" ),
	( "# ClientGUIMatPlotLib", "import lib.GUI.MatPlotLib" ),

	( "ClientGUIMedia.", "lib.GUI.Media." ),
	( "from . import ClientGUIMedia", "import lib.GUI.Media" ),
	( "# ClientGUIMedia", "import lib.GUI.Media" ),

	( "ClientGUIMenus.", "lib.GUI.Menus." ),
	( "from . import ClientGUIMenus", "import lib.GUI.Menus" ),
	( "# ClientGUIMenus", "import lib.GUI.Menus" ),

	( "ClientGUIOptionsPanels.", "lib.GUI.OptionsPanels." ),
	( "from . import ClientGUIOptionsPanels", "import lib.GUI.OptionsPanels" ),
	( "# ClientGUIOptionsPanels", "import lib.GUI.OptionsPanels" ),

	( "ClientGUIParsing.", "lib.GUI.Parsing." ),
	( "from . import ClientGUIParsing", "import lib.GUI.Parsing" ),
	( "# ClientGUIParsing", "import lib.GUI.Parsing" ),

	( "ClientGUIPopupMessages.", "lib.GUI.PopupMessages." ),
	( "from . import ClientGUIPopupMessages", "import lib.GUI.PopupMessages" ),
	( "# ClientGUIPopupMessages", "import lib.GUI.PopupMessages" ),

	( "ClientGUIPredicates.", "lib.GUI.Predicates." ),
	( "from . import ClientGUIPredicates", "import lib.GUI.Predicates" ),
	( "# ClientGUIPredicates", "import lib.GUI.Predicates" ),

	( "ClientGUIScrolledPanels.", "lib.GUI.ScrolledPanels." ),
	( "from . import ClientGUIScrolledPanels", "import lib.GUI.ScrolledPanels" ),
	( "# ClientGUIScrolledPanels", "import lib.GUI.ScrolledPanels" ),

	( "ClientGUIScrolledPanelsEdit.", "lib.GUI.ScrolledPanels.Edit." ),
	( "from . import ClientGUIScrolledPanelsEdit", "import lib.GUI.ScrolledPanels.Edit" ),
	( "# ClientGUIScrolledPanelsEdit", "import lib.GUI.ScrolledPanels.Edit" ),

	( "ClientGUIScrolledPanelsManagement.", "lib.GUI.ScrolledPanels.Management." ),
	( "from . import ClientGUIScrolledPanelsManagement", "import lib.GUI.ScrolledPanels.Management" ),
	( "# ClientGUIScrolledPanelsManagement", "import lib.GUI.ScrolledPanels.Management" ),

	( "ClientGUIScrolledPanelsReview.", "lib.GUI.ScrolledPanels.Review." ),
	( "from . import ClientGUIScrolledPanelsReview", "import lib.GUI.ScrolledPanels.Review" ),
	( "# ClientGUIScrolledPanelsReview", "import lib.GUI.ScrolledPanels.Review" ),

	( "ClientGUISerialisable.", "lib.GUI.Serialisable." ),
	( "from . import ClientGUISerialisable", "import lib.GUI.Serialisable" ),
	( "# ClientGUISerialisable", "import lib.GUI.Serialisable" ),

	( "ClientGUIShortcuts.", "lib.GUI.Shortcuts." ),
	( "from . import ClientGUIShortcuts", "import lib.GUI.Shortcuts" ),
	( "# ClientGUIShortcuts", "import lib.GUI.Shortcuts" ),

	( "ClientGUITagSuggestions.", "lib.GUI.TagSuggestions." ),
	( "from . import ClientGUITagSuggestions", "import lib.GUI.TagSuggestions" ),
	( "# ClientGUITagSuggestions", "import lib.GUI.TagSuggestions" ),

	( "ClientGUITags.", "lib.GUI.Tags." ),
	( "from . import ClientGUITags", "import lib.GUI.Tags" ),
	( "# ClientGUITags", "import lib.GUI.Tags" ),

	( "ClientGUITime.", "lib.GUI.Time." ),
	( "from . import ClientGUITime", "import lib.GUI.Time" ),
	( "# ClientGUITime", "import lib.GUI.Time" ),

	( "ClientGUITopLevelWindows.", "lib.GUI.TopLevelWindows." ),
	( "from . import ClientGUITopLevelWindows", "import lib.GUI.TopLevelWindows" ),
	( "# ClientGUITopLevelWindows", "import lib.GUI.TopLevelWindows" ),
)


code:
strings = (
	( "ClientImportFileSeeds.", "lib.Actions.Import.FileSeeds." ),
	( "from . import ClientImportFileSeeds", "import lib.Actions.Import.FileSeeds" ),
	( "# ClientImportFileSeeds", "import lib.Actions.Import.FileSeeds" ),

	( "ClientImportGallery.", "lib.Actions.Import.Gallery." ),
	( "from . import ClientImportGallery", "import lib.Actions.Import.Gallery" ),
	( "# ClientImportGallery", "import lib.Actions.Import.Gallery" ),

	( "ClientImportGallerySeeds.", "lib.Actions.Import.GallerySeeds." ),
	( "from . import ClientImportGallerySeeds", "import lib.Actions.Import.GallerySeeds" ),
	( "# ClientImportGallerySeeds", "import lib.Actions.Import.GallerySeeds" ),

	( "ClientImportLocal.", "lib.Actions.Import.Local." ),
	( "from . import ClientImportLocal", "import lib.Actions.Import.Local" ),
	( "# ClientImportLocal", "import lib.Actions.Import.Local" ),

	( "ClientImportOptions.", "lib.Actions.Import.Options." ),
	( "from . import ClientImportOptions", "import lib.Actions.Import.Options" ),
	( "# ClientImportOptions", "import lib.Actions.Import.Options" ),

	( "ClientImportSimpleURLs.", "lib.Actions.Import.SimpleURLs." ),
	( "from . import ClientImportSimpleURLs", "import lib.Actions.Import.SimpleURLs" ),
	( "# ClientImportSimpleURLs", "import lib.Actions.Import.SimpleURLs" ),

	( "ClientImportSubscriptions.", "lib.Actions.Import.Subscriptions." ),
	( "from . import ClientImportSubscriptions", "import lib.Actions.Import.Subscriptions" ),
	( "# ClientImportSubscriptions", "import lib.Actions.Import.Subscriptions" ),

	( "ClientImportWatchers.", "lib.Actions.Import.Watchers." ),
	( "from . import ClientImportWatchers", "import lib.Actions.Import.Watchers" ),
	( "# ClientImportWatchers", "import lib.Actions.Import.Watchers" ),

	( "ClientImporting.", "lib.Actions.Importing." ),
	( "from . import ClientImporting", "import lib.Actions.Importing" ),
	( "# ClientImporting", "import lib.Actions.Importing" ),
)


code:
strings = (
	( "HydrusAudioHandling.", "lib.Handling.Audio." ),
	( "from . import HydrusAudioHandling", "import lib.Handling.Audio" ),
	( "# HydrusAudioHandling", "import lib.Handling.Audio" ),

	( "HydrusConstants as HC.", "lib.Hydrus.Constants as HC." ),
	( "from . import HydrusConstants as HC", "import lib.Hydrus.Constants as HC" ),
	( "# HydrusConstants as HC", "import lib.Hydrus.Constants as HC" ),

	( "HydrusController.", "lib.Hydrus.Controller." ),
	( "from . import HydrusController", "import lib.Hydrus.Controller" ),
	( "# HydrusController", "import lib.Hydrus.Controller" ),

	( "HydrusData.", "lib.Hydrus.Data." ),
	( "from . import HydrusData", "import lib.Hydrus.Data" ),
	( "# HydrusData", "import lib.Hydrus.Data" ),

	( "HydrusDB.", "lib.Hydrus.DB." ),
	( "from . import HydrusDB", "import lib.Hydrus.DB" ),
	( "# HydrusDB", "import lib.Hydrus.DB" ),

	( "HydrusDocumentHandling.", "lib.Handling.Document." ),
	( "from . import HydrusDocumentHandling", "import lib.Handling.Document" ),
	( "# HydrusDocumentHandling", "import lib.Handling.Document" ),

	( "HydrusEncryption.", "lib.Hydrus.Encryption." ),
	( "from . import HydrusEncryption", "import lib.Hydrus.Encryption" ),
	( "# HydrusEncryption", "import lib.Hydrus.Encryption" ),

	( "HydrusExceptions.", "lib.Hydrus.Exceptions." ),
	( "from . import HydrusExceptions", "import lib.Hydrus.Exceptions" ),
	( "# HydrusExceptions", "import lib.Hydrus.Exceptions" ),

	( "HydrusFileHandling.", "lib.Handling." ),
	( "from . import HydrusFileHandling", "import lib.Handling" ),
	( "# HydrusFileHandling", "import lib.Handling" ),

	( "HydrusFlashHandling.", "lib.Handling.Flash." ),
	( "from . import HydrusFlashHandling", "import lib.Handling.Flash" ),
	( "# HydrusFlashHandling", "import lib.Handling.Flash" ),

	( "HydrusGlobals as HG.", "lib.Hydrus.Globals as HG." ),
	( "from . import HydrusGlobals as HG", "import lib.Hydrus.Globals as HG" ),
	( "# HydrusGlobals as HG", "import lib.Hydrus.Globals as HG" ),

	( "HydrusImageHandling.", "lib.Handling.Image." ),
	( "from . import HydrusImageHandling", "import lib.Handling.Image" ),
	( "# HydrusImageHandling", "import lib.Handling.Image" ),

	( "HydrusLogger.", "lib.Hydrus.Logger." ),
	( "from . import HydrusLogger", "import lib.Hydrus.Logger" ),
	( "# HydrusLogger", "import lib.Hydrus.Logger" ),

	( "HydrusNATPunch.", "lib.Networking.NATPunch." ),
	( "from . import HydrusNATPunch", "import lib.Networking.NATPunch" ),
	( "# HydrusNATPunch", "import lib.Networking.NATPunch" ),

	( "HydrusNetwork.", "lib.Networking." ),
	( "from . import HydrusNetwork", "import lib.Networking" ),
	( "# HydrusNetwork", "import lib.Networking" ),

	( "HydrusNetworking.", "lib.Networking." ),
	( "from . import HydrusNetworking", "import lib.Networking" ),
	( "# HydrusNetworking", "import lib.Networking" ),

	( "HydrusPaths.", "lib.Hydrus.Paths." ),
	( "from . import HydrusPaths", "import lib.Hydrus.Paths" ),
	( "# HydrusPaths", "import lib.Hydrus.Paths" ),

	( "HydrusPubSub.", "lib.Networking.PubSub." ),
	( "from . import HydrusPubSub", "import lib.Networking.PubSub" ),
	( "# HydrusPubSub", "import lib.Networking.PubSub" ),

	( "HydrusPy2To3.", "lib.Test.Py2To3 ." ),
	( "from . import HydrusPy2To3", "import lib.Test.Py2To3" ),
	( "# HydrusPy2To3", "import lib.Test.Py2To3 " ),

	( "HydrusRatingArchive.", "lib.Hydrus.Ratings.Archive." ),
	( "from . import HydrusRatingArchive", "import lib.Hydrus.Ratings.Archive" ),
	( "# HydrusRatingArchive", "import lib.Hydrus.Ratings.Archive" ),

	( "HydrusSerialisable.", "lib.Hydrus.Serialisable." ),
	( "from . import HydrusSerialisable", "import lib.Hydrus.Serialisable" ),
	( "# HydrusSerialisable", "import lib.Hydrus.Serialisable" ),

	( "HydrusServer.", "lib.Hydrus.Server." ),
	( "from . import HydrusServer", "import lib.Hydrus.Server" ),
	( "# HydrusServer", "import lib.Hydrus.Server" ),

	( "HydrusServerAMP.", "lib.Hydrus.Server.AMP." ),
	( "from . import HydrusServerAMP", "import lib.Hydrus.Server.AMP" ),
	( "# HydrusServerAMP", "import lib.Hydrus.Server.AMP" ),

	( "HydrusServerResources.", "lib.Hydrus.Server.Resources." ),
	( "from . import HydrusServerResources", "import lib.Hydrus.Server.Resources" ),
	( "# HydrusServerResources", "import lib.Hydrus.Server.Resources" ),

	( "HydrusSessions.", "lib.Hydrus.Sessions." ),
	( "from . import HydrusSessions", "import lib.Hydrus.Sessions" ),
	( "# HydrusSessions", "import lib.Hydrus.Sessions" ),

	( "HydrusTags.", "lib.Hydrus.Tags." ),
	( "from . import HydrusTags", "import lib.Hydrus.Tags" ),
	( "# HydrusTags", "import lib.Hydrus.Tags" ),

	( "HydrusTagArchive.", "lib.Hydrus.Tags.Archive." ),
	( "from . import HydrusTagArchive", "import lib.Hydrus.Tags.Archive" ),
	( "# HydrusTagArchive", "import lib.Hydrus.Tags.Archive" ),

	( "HydrusText.", "lib.Hydrus.Text." ),
	( "from . import HydrusText", "import lib.Hydrus.Text" ),
	( "# HydrusText", "import lib.Hydrus.Text" ),

	( "HydrusThreading.", "lib.Hydrus.Threading." ),
	( "from . import HydrusThreading", "import lib.Hydrus.Threading" ),
	( "# HydrusThreading", "import lib.Hydrus.Threading" ),

	( "HydrusSerialisable.", "lib.Hydrus.Serialisable." ),
	( "from . import HydrusSerialisable", "import lib.Hydrus.Serialisable" ),
	( "# HydrusSerialisable", "import lib.Hydrus.Serialisable" ),

	( "HydrusVideoHandling.", "lib.Handling.Video." ),
	( "from . import HydrusVideoHandling", "import lib.Handling.Video" ),
	( "# HydrusVideoHandling", "import lib.Handling.Video" ),
)


code:
strings = (
	( "ServerController.", "lib.Hydrus.Controller.Remote." ),
	( "from . import ServerController", "import lib.Hydrus.Controller.Remote" ),
	( "# ServerController", "import lib.Hydrus.Controller.Remote" ),

	( "ServerDB.", "lib.Hydrus.DB.Remote." ),
	( "from . import ServerDB", "import lib.Hydrus.DB.Remote" ),
	( "# ServerDB", "import lib.Hydrus.DB.Remote" ),

	( "ServerFiles.", "lib.Hydrus.Files.Remote." ),
	( "from . import ServerFiles", "import lib.Hydrus.Files.Remote" ),
	( "# ServerFiles", "import lib.Hydrus.Files.Remote" ),

	( "ServerServer.", "lib.Hydrus.Server." ),
	( "from . import ServerServer", "import lib.Hydrus.Server" ),
	( "# ServerServer", "import lib.Hydrus.Server" ),

	( "ServerServerResources.", "lib.Hydrus.Server.RemoteResources." ),
	( "from . import ServerServerResources", "import lib.Hydrus.Server.RemoteResources" ),
	( "# ServerServerResources", "import lib.Hydrus.Server.RemoteResources" ),

	( "ServerServices.", "lib.Hydrus.Services.Remote." ),
	( "from . import ServerServices", "import lib.Hydrus.Services.Remote" ),
	( "# ServerServices", "import lib.Hydrus.Services.Remote" ),
)


code:
from filename import Client, ClientGUI, ClientImport, Hydrus, Server, Test
from classname import Hydrus as HydrusClass
import Refactors

import os, glob, re

ToReplace = [
	Client,
	ClientGUI,
	ClientImport,
	Hydrus,
	Server,
	HydrusClass,
	# Test,
	# Refactors
]

def replace(file):
	filetext = file.read()
	origfile = filetext
	for prefix in ToReplace:
		for replacement in prefix.strings[::-1]:
			filetext = filetext.replace(replacement[0], replacement[1])

	if filetext != origfile:
		file.seek(0)
		file.truncate()
		file.write(filetext)

for path in glob.iglob('lib/**', recursive=True):
	if os.path.isfile(path) and not "__" in path:
		print(path)
		with open(path, "r+", encoding="utf-8") as file:
			replace(file)


code:
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->

<svg
   xmlns:dc="http://purl.org/dc/elements/1.1/"
   xmlns:cc="http://creativecommons.org/ns#"
   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
   xmlns:svg="http://www.w3.org/2000/svg"
   xmlns="http://www.w3.org/2000/svg"
   xmlns:xlink="http://www.w3.org/1999/xlink"
   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
   width="744.09448819"
   height="1052.3622047"
   id="svg2"
   sodipodi:version="0.32"
   inkscape:version="0.48.2 r9819"
   sodipodi:docname="Icons.svg"
   inkscape:output_extension="org.inkscape.output.svg.inkscape"
   inkscape:export-filename="C:\projects\code\Hydrus\static\Hydrus Icon.png"
   inkscape:export-xdpi="63.415401"
   inkscape:export-ydpi="63.415401"
   sodipodi:modified="true"
   version="1.1">
  <defs
     id="defs4">
    <linearGradient
       id="linearGradient3200">
      <stop
         style="stop-color:#000000;stop-opacity:1;"
         offset="0"
         id="stop3202" />
      <stop
         style="stop-color:#ffffff;stop-opacity:1;"
         offset="1"
         id="stop3204" />
    </linearGradient>
    <linearGradient
       inkscape:collect="always"
       xlink:href="#linearGradient3200"
       id="linearGradient3206"
       x1="368.57813"
       y1="319.00314"
       x2="383.23392"
       y2="703.36621"
       gradientUnits="userSpaceOnUse" />
    <linearGradient
       inkscape:collect="always"
       xlink:href="#linearGradient3200"
       id="linearGradient3210"
       gradientUnits="userSpaceOnUse"
       x1="368.57813"
       y1="319.00314"
       x2="383.23392"
       y2="703.36621" />
    <linearGradient
       inkscape:collect="always"
       xlink:href="#linearGradient3200"
       id="linearGradient3221"
       gradientUnits="userSpaceOnUse"
       x1="368.57813"
       y1="319.00314"
       x2="383.23392"
       y2="703.36621" />
    <linearGradient
       inkscape:collect="always"
       xlink:href="#linearGradient3200"
       id="linearGradient3225"
       gradientUnits="userSpaceOnUse"
       x1="368.57813"
       y1="319.00314"
       x2="383.23392"
       y2="703.36621" />
    <linearGradient
       inkscape:collect="always"
       xlink:href="#linearGradient3200"
       id="linearGradient3229"
       gradientUnits="userSpaceOnUse"
       x1="368.57813"
       y1="319.00314"
       x2="383.23392"
       y2="703.36621" />
    <linearGradient
       inkscape:collect="always"
       xlink:href="#linearGradient3200"
       id="linearGradient3233"
       gradientUnits="userSpaceOnUse"
       x1="368.57813"
       y1="319.00314"
       x2="383.23392"
       y2="703.36621" />
    <filter
       color-interpolation-filters="sRGB"
       inkscape:collect="always"
       id="filter3172">
      <feGaussianBlur
         inkscape:collect="always"
         stdDeviation="1.831882"
         id="feGaussianBlur3174" />
    </filter>
    <filter
       inkscape:collect="always"
       id="filter3839">
      <feGaussianBlur
         inkscape:collect="always"
         stdDeviation="2.5184367"
         id="feGaussianBlur3841" />
    </filter>
    <filter
       color-interpolation-filters="sRGB"
       inkscape:collect="always"
       id="filter3839-4">
      <feGaussianBlur
         inkscape:collect="always"
         stdDeviation="2.5184367"
         id="feGaussianBlur3841-8" />
    </filter>
    <filter
       color-interpolation-filters="sRGB"
       inkscape:collect="always"
       id="filter3839-1">
      <feGaussianBlur
         inkscape:collect="always"
         stdDeviation="2.5184367"
         id="feGaussianBlur3841-7" />
    </filter>
    <filter
       color-interpolation-filters="sRGB"
       inkscape:collect="always"
       id="filter3839-48">
      <feGaussianBlur
         inkscape:collect="always"
         stdDeviation="2.5184367"
         id="feGaussianBlur3841-82" />
    </filter>
    <filter
       color-interpolation-filters="sRGB"
       inkscape:collect="always"
       id="filter3839-40">
      <feGaussianBlur
         inkscape:collect="always"
         stdDeviation="2.5184367"
         id="feGaussianBlur3841-9" />
    </filter>
    <filter
       color-interpolation-filters="sRGB"
       inkscape:collect="always"
       id="filter3839-17">
      <feGaussianBlur
         inkscape:collect="always"
         stdDeviation="2.5184367"
         id="feGaussianBlur3841-4" />
    </filter>
  </defs>
  <sodipodi:namedview
     id="base"
     pagecolor="#ffffff"
     bordercolor="#666666"
     borderopacity="1.0"
     gridtolerance="10000"
     guidetolerance="10"
     objecttolerance="10"
     inkscape:pageopacity="0.0"
     inkscape:pageshadow="2"
     inkscape:zoom="0.98994949"
     inkscape:cx="381.80586"
     inkscape:cy="494.18593"
     inkscape:document-units="px"
     inkscape:current-layer="layer1"
     inkscape:window-width="1200"
     inkscape:window-height="1904"
     inkscape:window-x="-4"
     inkscape:window-y="-4"
     showgrid="false"
     inkscape:window-maximized="1" />
  <metadata
     id="metadata7">
    <rdf:RDF>
      <cc:Work
         rdf:about="">
        <dc:format>image/svg+xml</dc:format>
        <dc:type
           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
        <dc:title></dc:title>
      </cc:Work>
    </rdf:RDF>
  </metadata>
  <g
     inkscape:label="Layer 1"
     inkscape:groupmode="layer"
     id="layer1">
    <rect
       style="color:#000000;fill:#000000;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate;fill-opacity:0.0627451;fill-rule:nonzero"
       id="rect2164-9-4"
       width="199.00005"
       height="199.00005"
       x="523.7641"
       y="400.30872"
       inkscape:export-filename="C:\code\Hydrus\static\metadata.png"
       inkscape:export-xdpi="90"
       inkscape:export-ydpi="90" />
    <text
       xml:space="preserve"
       style="font-size:219.01309204px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;filter:url(#filter3172);font-family:Cambria"
       x="86.413773"
       y="566.73431"
       id="text2160"
       inkscape:export-filename="C:\projects\code\Hydrus\static\Librarium Icon.png"
       inkscape:export-xdpi="90"
       inkscape:export-ydpi="90"><tspan
         sodipodi:role="line"
         id="tspan2162"
         x="86.413773"
         y="566.73431">L</tspan></text>
    <rect
       style="color:#000000;fill:#000000;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate;fill-opacity:0.0627451;fill-rule:nonzero"
       id="rect2164"
       width="199.00005"
       height="199.00005"
       x="48.992416"
       y="394.2478"
       inkscape:export-filename="C:\code\Hydrus\static\librarium.png"
       inkscape:export-xdpi="90"
       inkscape:export-ydpi="90" />
    <text
       xml:space="preserve"
       style="font-size:219.01309204px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;filter:url(#filter3839);font-family:Cambria"
       x="403.60165"
       y="555.62262"
       id="text2160-4"
       transform="matrix(0.80588956,0,0,0.80588956,9.5273763,90.997406)"
       inkscape:export-filename="C:\projects\code\Hydrus\static\Content Icon.png"
       inkscape:export-xdpi="90"
       inkscape:export-ydpi="90"><tspan
         sodipodi:role="line"
         id="tspan2162-0"
         x="403.60165"
         y="555.62262">ψ</tspan></text>
    <rect
       style="color:#000000;fill:#000000;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate;fill-opacity:0.06274510000000000;fill-rule:nonzero"
       id="rect2164-9"
       width="199.00005"
       height="199.00005"
       x="298.50009"
       y="398.28839"
       inkscape:export-filename="C:\code\Hydrus\static\content.png"
       inkscape:export-xdpi="90"
       inkscape:export-ydpi="90" />
    <text
       xml:space="preserve"
       style="font-size:219.01309203999998000px;font-style:normal;font-weight:normal;fill:#ffffff;fill-opacity:1;stroke:none;filter:url(#filter3839-4);font-family:Cambria"
       x="403.60165"
       y="555.62262"
       id="text2160-4-8"
       transform="matrix(0.80588956,0,0,0.80588956,234.79138,93.017722)"
       inkscape:export-filename="C:\projects\code\Hydrus\static\Metadata Icon.png"
       inkscape:export-xdpi="90"
       inkscape:export-ydpi="90"><tspan
         sodipodi:role="line"
         id="tspan2162-0-2"
         x="403.60165"
         y="555.62262">ψ</tspan></text>
    <text
       xml:space="preserve"
       style="font-size:219.01309204px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;filter:url(#filter3839-1);font-family:Cambria"
       x="403.60165"
       y="555.62262"
       id="text2160-4-4"
       transform="matrix(0.80588956,0,0,0.80588956,10.537514,308.18021)"
       inkscape:export-filename="C:\projects\code\Hydrus\static\Content Icon.png"
       inkscape:export-xdpi="90"
       inkscape:export-ydpi="90"><tspan
         sodipodi:role="line"
         id="tspan2162-0-0"
         x="403.60165"
         y="555.62262">ψ</tspan></text>
    <rect
       style="color:#000000;fill:#00ff00;fill-opacity:0.1254902;fill-rule:nonzero;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
       id="rect2164-9-9"
       width="199.00005"
       height="199.00005"
       x="299.51025"
       y="615.47119"
       inkscape:export-filename="C:\code\Hydrus\static\file_repository_pending.png"
       inkscape:export-xdpi="90"
       inkscape:export-ydpi="90" />
    <text
       xml:space="preserve"
       style="font-size:219.01309204px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;filter:url(#filter3839-48);font-family:Cambria"
       x="403.60165"
       y="555.62262"
       id="text2160-4-45"
       transform="matrix(0.80588956,0,0,0.80588956,8.5172085,524.35286)"
       inkscape:export-filename="C:\projects\code\Hydrus\static\Content Icon.png"
       inkscape:export-xdpi="90"
       inkscape:export-ydpi="90"><tspan
         sodipodi:role="line"
         id="tspan2162-0-5"
         x="403.60165"
         y="555.62262">ψ</tspan></text>
    <rect
       style="color:#000000;fill:#ff0000;fill-opacity:0.1254902;fill-rule:nonzero;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
       id="rect2164-9-1"
       width="199.00005"
       height="199.00005"
       x="297.48993"
       y="831.6438"
       inkscape:export-filename="C:\code\Hydrus\static\file_repository_petitioned.png"
       inkscape:export-xdpi="90"
       inkscape:export-ydpi="90" />
    <text
       xml:space="preserve"
       style="font-size:219.01309204px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;filter:url(#filter3839-40);font-family:Cambria"
       x="403.60165"
       y="555.62262"
       id="text2160-4-48"
       transform="matrix(0.80588956,0,0,0.80588956,8.5172084,-126.18538)"
       inkscape:export-filename="C:\code\Hydrus\static\hydrus_splash.png"
       inkscape:export-xdpi="90"
       inkscape:export-ydpi="90"><tspan
         sodipodi:role="line"
         id="tspan2162-0-8"
         x="403.60165"
         y="555.62262">ψ</tspan></text>
    <rect
       style="color:#000000;fill:#efefef;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
       id="rect2164-9-48"
       width="199.00005"
       height="199.00005"
       x="43.941639"
       y="617.49152"
       inkscape:export-filename="C:\code\Hydrus\static\hydrus_non-transparent.png"
       inkscape:export-xdpi="90"
       inkscape:export-ydpi="90" />
    <text
       xml:space="preserve"
       style="font-size:219.01309204px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;filter:url(#filter3839-17);font-family:Cambria"
       x="403.60165"
       y="555.62262"
       id="text2160-4-0"
       transform="matrix(0.80588956,0,0,0.80588956,-245.03108,310.20053)"
       inkscape:export-filename="C:\projects\code\Hydrus\static\Content Icon.png"
       inkscape:export-xdpi="90"
       inkscape:export-ydpi="90"><tspan
         sodipodi:role="line"
         id="tspan2162-0-9"
         x="403.60165"
         y="555.62262">ψ</tspan></text>
    <text
       xml:space="preserve"
       style="font-size:18px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans;-inkscape-font-specification:Sans"
       x="75.761436"
       y="838.2099"
       id="text3820"
       sodipodi:linespacing="125%"><tspan
         sodipodi:role="line"
         id="tspan3822"
         x="75.761436"
         y="838.2099">non-transparent</tspan></text>
  </g>
</svg>


code:
a { color: #222; text-decoration: none; font-weight: bold; }
a:hover { color: #555 }
body { font-family: "Calibri", Arial, sans-serif; color: #555; line-height: 1.5; }
h3 { color: #222; }
ul li { list-style: none; margin: 1.0em 0em; }
ul.bulletpoint li { list-style: disc; margin: 1.0em 0em; }

.dealwithit { color: #ff0000; text-shadow:
1px 1px 0 #ff7f00,
2px 2px 0 #ffff00,
3px 3px 0 #00ff00,
4px 4px 0 #0000ff,
5px 5px 0 #6600ff,
6px 6px 0 #8b00ff; }
.warning { color: #d00 }
.lololol { text-decoration: line-through; }
.right { text-align: right; }
.screenshot { float: right; clear: both; margin: 10px; }

.footer { text-align: center; font-size: 80% }
.media { clear: both; }
.thumbnail { margin: 1px; border: 1px rgb( 223, 227, 230 ) solid; display: inline-block; }
.thumbnail_container { text-align: center; display: table-cell; vertical-align: middle; }
.thumbnail_container img { display: block; margin-left: auto; margin-right: auto; }
.timeout { font-size: 80%; float: right; margin: 2px }


code:
#!/usr/bin/env python3

import locale

try: locale.setlocale( locale.LC_ALL, '' )
except Exception: pass

from include import HydrusConstants as HC
from include import HydrusData
from include import HydrusGlobals as HG
from include import TestController
import sys
import threading
import traceback
import wx
from twisted.internet import reactor

if __name__ == '__main__':
    args = sys.argv[1:]

    if len( args ) > 0:
        only_run = args[0]

    else:
        only_run = None

    try:
        threading.Thread( target = reactor.run, kwargs = { 'installSignalHandlers' : 0 } ).start()

        app = wx.App()

        try:
            # we run the tests on the wx thread atm
            # keep a window alive the whole time so the app doesn't finish its mainloop

            win = wx.Frame( None, title = 'Running tests...' )

            controller = TestController.Controller( win, only_run )

            def do_it():
                controller.Run( win )

            wx.CallAfter( do_it )

            app.MainLoop()

        except Exception:
            HydrusData.DebugPrint( traceback.format_exc() )

        finally:
            HG.view_shutdown = True

            controller.pubimmediate( 'wake_daemons' )

            HG.model_shutdown = True

            controller.pubimmediate( 'wake_daemons' )

            controller.TidyUp()

    except Exception:
        HydrusData.DebugPrint( traceback.format_exc() )

    finally:
        reactor.callFromThread( reactor.stop )

        print( 'This was version ' + str( HC.SOFTWARE_VERSION ) )

        input()