def setUp(self): class TestExtension(Extension): """An empty, dummy extension for testing""" css_bundles = { 'default': { 'source_filenames': ['test.css'], } } js_bundles = { 'default': { 'source_filenames': ['test.js'], } } self.key = 'test_key' self.extension_class = TestExtension self.manager = ExtensionManager(self.key) self.fake_entrypoint = Mock() self.fake_entrypoint.load = Mock(return_value=self.extension_class) self.fake_entrypoint.dist = Mock() self.test_author = 'Test author lorem ipsum' self.test_description = 'Test description lorem ipsum' self.test_email = 'Test [email protected]' self.test_home_page = 'http://www.example.com' self.test_license = 'Test License MIT GPL Apache Drivers' self.test_module_name = 'testextension.dummy.dummy' self.test_module_to_app = 'testextension.dummy' self.test_project_name = 'TestProjectName' self.test_summary = 'Test summary lorem ipsum' self.test_version = '1.0' self.test_htdocs_path = os.path.join(settings.EXTENSIONS_STATIC_ROOT, self.test_project_name) self.test_metadata = { 'Name': self.test_project_name, 'Version': self.test_version, 'Summary': self.test_summary, 'Description': self.test_description, 'Author': self.test_author, 'Author-email': self.test_email, 'License': self.test_license, 'Home-page': self.test_home_page, } self.fake_entrypoint.dist.get_metadata_lines = Mock( return_value=[ "%s: %s" % (key, value) for key, value in six.iteritems(self.test_metadata) ]) self.fake_entrypoint.dist.project_name = self.test_project_name self.fake_entrypoint.dist.version = self.test_version self.manager._entrypoint_iterator = Mock( return_value=[self.fake_entrypoint] ) self.manager.load()
def setUp(self): manager = ExtensionManager('') self.extension = \ TestExtensionWithRegistration(extension_manager=manager) for index in range(0, 5): self.extension.hooks.add(Mock())
def setUpClass(cls): super(ExtensionEnabledWebAPIScopeDictionaryTests, cls).setUpClass() cls._extension_manager = ExtensionManager('') cls._resources = make_resource_tree( mixins=[ ResourceOAuth2TokenMixin, ], extension_manager=cls._extension_manager) class TestExtensionResource(cls._resources.base_resource): """An example resource on an extension.""" name = 'test-ext' allowed_methods = ('GET', 'POST', 'PUT', 'DELETE') cls._extension_resource = TestExtensionResource() class TestExtension(Extension): """An example extension.""" extension_id = \ 'djblets.webapi.tests.test_oauth2_scopes.TestExtension' resources = [cls._extension_resource] registration = RegisteredExtension() cls.extension_class = TestExtension
def get_extension_manager(): global _extension_manager if not _extension_manager: _extension_manager = ExtensionManager("reviewboard.extensions") return _extension_manager
def setUp(self): super(WebAPICapabilitiesHookTests, self).setUp() manager = ExtensionManager('') self.extension = WebAPICapabilitiesExtension( extension_manager=manager) self.url = get_root_url()
def setUp(self): manager = ExtensionManager('') self.test_extension = \ TestExtensionWithRegistration(extension_manager=manager) self.patterns = patterns( '', (r'^url_hook_test/', include('djblets.extensions.test.urls'))) self.url_hook = URLHook(self.test_extension, self.patterns)
def setUp(self): super(EmailHookTests, self).setUp() manager = ExtensionManager('') self.extension = DummyExtension(extension_manager=manager) mail.outbox = []
def setUp(self): self.mgr = ExtensionManager('') self.ext_resource = ExtensionResource(self.mgr) self.root_resource = TestRootResource([self.ext_resource]) self.request = RequestFactory().get('/') super(ExtensionRootResourceMixinTests, self).setUp()
def setUp(self): manager = ExtensionManager('') self.extension = \ TestExtensionWithRegistration(extension_manager=manager) self.extension_hook_class = TestExtensionHook self.dummy_hook = Mock() self.extension_hook_class.add_hook(self.dummy_hook)
def setUp(self): manager = ExtensionManager('') self.test_extension = \ TestExtensionWithRegistration(extension_manager=manager) self.signal = Signal() self.spy_on(self._on_signal_fired) self.spy_on(self._on_signal_exception)
def setUp(self): manager = ExtensionManager('') self.extension = \ TestExtensionWithRegistration(extension_manager=manager) for index in range(0, 5): hook = DummyHook(self.extension) self.spy_on(hook.shutdown) self.extension.hooks.add(hook)
def setUp(self): class TestExtension(Extension): """An empty, dummy extension for testing""" css_bundles = { 'default': { 'source_filenames': ['test.css'], } } js_bundles = { 'default': { 'source_filenames': ['test.js'], } } self.key = 'test_key' self.extension_class = TestExtension self.manager = ExtensionManager(self.key) self.fake_entrypoint = Mock() self.fake_entrypoint.load = Mock(return_value=self.extension_class) self.fake_entrypoint.dist = Mock() self.test_author = 'Test author lorem ipsum' self.test_description = 'Test description lorem ipsum' self.test_email = 'Test [email protected]' self.test_home_page = 'http://www.example.com' self.test_license = 'Test License MIT GPL Apache Drivers' self.test_module_name = 'testextension.dummy.dummy' self.test_module_to_app = 'testextension.dummy' self.test_project_name = 'TestProjectName' self.test_summary = 'Test summary lorem ipsum' self.test_version = '1.0' self.test_metadata = { 'Name': self.test_project_name, 'Version': self.test_version, 'Summary': self.test_summary, 'Description': self.test_description, 'Author': self.test_author, 'Author-email': self.test_email, 'License': self.test_license, 'Home-page': self.test_home_page, } self.fake_entrypoint.dist.get_metadata_lines = Mock( return_value=[ "%s: %s" % (key, value) for key, value in six.iteritems(self.test_metadata) ]) self.fake_entrypoint.dist.project_name = self.test_project_name self.fake_entrypoint.dist.version = self.test_version self.manager._entrypoint_iterator = Mock( return_value=[self.fake_entrypoint] ) self.manager.load()
def setUp(self): super(SandboxTests, self).setUp() manager = ExtensionManager('') self.extension = SandboxExtension(extension_manager=manager) self.factory = RequestFactory() self.user = User.objects.create_user(username='******', email='', password='******')
def get_extension_manager(self): """Return an extension manager for the test. The result will always be a new, un-keyed extension manager. Returns: djblets.extensions.manager.ExtensionManager: The new extension manager. """ return ExtensionManager('')
def setUp(self): super(RootResourceTemplateRegistrationTests, self).setUp() self.ext_mgr = ExtensionManager('') self.ext_res = ExtensionResource(self.ext_mgr) self.root_res = RootResource([self.ext_res]) self.root_res._registered_uri_templates = { self.ext_res: { 'extensions': 'http://localhost:8080/api/extensions/' }, None: { 'extensions': 'http://localhost:8080/api/extensions/none/' }, }
def setUp(self): class TestExtension(Extension): """An empty, dummy extension for testing""" css_bundles = {"default": {"source_filenames": ["test.css"]}} js_bundles = {"default": {"source_filenames": ["test.js"]}} self.key = "test_key" self.extension_class = TestExtension self.manager = ExtensionManager(self.key) self.fake_entrypoint = Mock() self.fake_entrypoint.load = Mock(return_value=self.extension_class) self.fake_entrypoint.dist = Mock() self.test_author = "Test author lorem ipsum" self.test_description = "Test description lorem ipsum" self.test_email = "Test [email protected]" self.test_home_page = "http://www.example.com" self.test_license = "Test License MIT GPL Apache Drivers" self.test_module_name = "testextension.dummy.dummy" self.test_module_to_app = "testextension.dummy" self.test_project_name = "TestProjectName" self.test_summary = "Test summary lorem ipsum" self.test_version = "1.0" self.test_htdocs_path = os.path.join(settings.EXTENSIONS_STATIC_ROOT, self.test_project_name) self.test_metadata = { "Name": self.test_project_name, "Version": self.test_version, "Summary": self.test_summary, "Description": self.test_description, "Author": self.test_author, "Author-email": self.test_email, "License": self.test_license, "Home-page": self.test_home_page, } self.fake_entrypoint.dist.get_metadata_lines = Mock( return_value=["%s: %s" % (key, value) for key, value in six.iteritems(self.test_metadata)] ) self.fake_entrypoint.dist.project_name = self.test_project_name self.fake_entrypoint.dist.version = self.test_version self.manager._entrypoint_iterator = Mock(return_value=[self.fake_entrypoint]) self.manager.load()
def setUp(self): manager = ExtensionManager('') self.extension = \ TestExtensionWithRegistration(extension_manager=manager) self.hook_with_applies_name = "template-hook-with-applies-name" self.hook_no_applies_name = "template-hook-no-applies-name" self.template_hook_no_applies = TemplateHook(self.extension, self.hook_no_applies_name, "test_module/some_template.html", []) self.template_hook_with_applies = TemplateHook(self.extension, self.hook_with_applies_name, "test_module/some_template.html", [ 'test-url-name', 'url_2', 'url_3', ] ) self.fake_request = Mock() self.fake_request._djblets_extensions_kwargs = {} self.fake_request.path_info = '/' self.context = { 'request': self.fake_request, }
def setUp(self): super(HookTests, self).setUp() manager = ExtensionManager('') self.extension = DummyExtension(extension_manager=manager)
def setUp(self): super(SandboxTests, self).setUp() manager = ExtensionManager('') self.extension = SandboxExtension(extension_manager=manager)
def test_load_blocks_sync_gen(self): """Testing ExtensionManager.load blocks bumping sync generation number """ key = 'check-expired-test' manager1 = ExtensionManager(key) manager2 = ExtensionManager(key) for manager in (manager1, manager2): manager._entrypoint_iterator = Mock( return_value=[self.fake_entrypoint] ) manager1.load() manager1.enable_extension(self.extension_class.id) manager2.load() self.assertEqual(manager1._last_sync_gen, manager2._last_sync_gen) # Trigger a save whenever the extension initializes. self.extension_class.initialize = lambda ext: ext.settings.save() # Bump the generation number. extension = manager2.get_enabled_extension(self.extension_class.id) extension.settings.save() self.assertNotEqual(manager1._last_sync_gen, manager2._last_sync_gen) # Loading now should retain the new sync generation number, instead # of bumping it. manager1.load(full_reload=True) self.assertEqual(manager1._last_sync_gen, manager2._last_sync_gen)
class ExtensionManagerTest(SpyAgency, TestCase): def setUp(self): class TestExtension(Extension): """An empty, dummy extension for testing""" css_bundles = { 'default': { 'source_filenames': ['test.css'], } } js_bundles = { 'default': { 'source_filenames': ['test.js'], } } self.key = 'test_key' self.extension_class = TestExtension self.manager = ExtensionManager(self.key) self.fake_entrypoint = Mock() self.fake_entrypoint.load = Mock(return_value=self.extension_class) self.fake_entrypoint.dist = Mock() self.test_author = 'Test author lorem ipsum' self.test_description = 'Test description lorem ipsum' self.test_email = 'Test [email protected]' self.test_home_page = 'http://www.example.com' self.test_license = 'Test License MIT GPL Apache Drivers' self.test_module_name = 'testextension.dummy.dummy' self.test_module_to_app = 'testextension.dummy' self.test_project_name = 'TestProjectName' self.test_summary = 'Test summary lorem ipsum' self.test_version = '1.0' self.test_metadata = { 'Name': self.test_project_name, 'Version': self.test_version, 'Summary': self.test_summary, 'Description': self.test_description, 'Author': self.test_author, 'Author-email': self.test_email, 'License': self.test_license, 'Home-page': self.test_home_page, } self.fake_entrypoint.dist.get_metadata_lines = Mock( return_value=[ "%s: %s" % (key, value) for key, value in six.iteritems(self.test_metadata) ]) self.fake_entrypoint.dist.project_name = self.test_project_name self.fake_entrypoint.dist.version = self.test_version self.manager._entrypoint_iterator = Mock( return_value=[self.fake_entrypoint] ) self.manager.load() def tearDown(self): self.manager.clear_sync_cache() def test_added_to_extension_managers(self): """Testing ExtensionManager registration""" self.assertTrue(self.manager in _extension_managers) def test_get_enabled_extensions_returns_empty(self): """Testing ExtensionManager.get_enabled_extensions with no extensions """ self.assertEqual(len(self.manager.get_enabled_extensions()), 0) def test_load(self): """Testing ExtensionManager.get_installed_extensions with loaded extensions """ self.assertEqual(len(self.manager.get_installed_extensions()), 1) self.assertTrue(self.extension_class in self.manager.get_installed_extensions()) self.assertTrue(hasattr(self.extension_class, 'info')) self.assertEqual(self.extension_class.info.name, self.test_project_name) self.assertTrue(hasattr(self.extension_class, 'registration')) self.assertEqual(self.extension_class.registration.name, self.test_project_name) def test_load_full_reload_hooks(self): """Testing ExtensionManager.load with full_reload=True""" self.assertEqual(len(self.manager.get_installed_extensions()), 1) extension = self.extension_class(extension_manager=self.manager) extension = self.manager.enable_extension(self.extension_class.id) URLHook(extension, ()) self.assertEqual(len(URLHook.hooks), 1) self.assertEqual(URLHook.hooks[0].extension, extension) self.manager.load(full_reload=True) self.assertEqual(len(URLHook.hooks), 0) def test_load_concurrent_threads(self): """Testing ExtensionManager.load with concurrent threads""" # There are a number of things that could go wrong both during # uninitialization and during initialization of extensions, if # two threads attempt to reload at the same time and locking isn't # properly implemented. # # Extension uninit could be called twice, resulting in one thread # attempting to access state that's already been destroyed. We # could end up hitting: # # "Extension's installed app <app> is missing a ref count." # "'<Extension>' object has no attribute 'info'." # # (Without locking, we end up hitting the latter in this test.) # # If an extension is being initialized twice simultaneously, then # it can hit other errors. An easy one to hit is this assertion: # # assert extension_id not in self._extension_instances # # With proper locking, these issues don't come up. That's what # this test case is attempting to check for. # Enable one extension. This extension's state will get a bit messed # up if the thread locking fails. We only need one to trigger this. self.assertEqual(len(self.manager.get_installed_extensions()), 1) self.manager.enable_extension(self.extension_class.id) self.spy_on(self.manager._load_extensions) self._spy_sleep_and_call(self.manager._init_extension) self._spy_sleep_and_call(self.manager._uninit_extension) self._run_thread_test(lambda: self.manager.load(full_reload=True)) self.assertEqual(len(self.manager._load_extensions.calls), 2) self.assertEqual(len(self.manager._uninit_extension.calls), 2) self.assertEqual(len(self.manager._init_extension.calls), 2) self.assertEqual(self.exceptions, []) def test_enable_registers_static_bundles(self): """Testing ExtensionManager registers static bundles when enabling extension """ settings.PIPELINE_CSS = {} settings.PIPELINE_JS = {} extension = self.extension_class(extension_manager=self.manager) extension = self.manager.enable_extension(self.extension_class.id) self.assertEqual(len(settings.PIPELINE_CSS), 1) self.assertEqual(len(settings.PIPELINE_JS), 1) key = '%s-default' % extension.id self.assertIn(key, settings.PIPELINE_CSS) self.assertIn(key, settings.PIPELINE_JS) css_bundle = settings.PIPELINE_CSS[key] js_bundle = settings.PIPELINE_JS[key] self.assertIn('source_filenames', css_bundle) self.assertEqual(css_bundle['source_filenames'], ['ext/%s/test.css' % extension.id]) self.assertIn('output_filename', css_bundle) self.assertEqual(css_bundle['output_filename'], 'ext/%s/css/default.min.css' % extension.id) self.assertIn('source_filenames', js_bundle) self.assertEqual(js_bundle['source_filenames'], ['ext/%s/test.js' % extension.id]) self.assertIn('output_filename', js_bundle) self.assertEqual(js_bundle['output_filename'], 'ext/%s/js/default.min.js' % extension.id) def test_install_extension_media_with_stale_version_key(self): """Testing ExtensionManager installing media for newly installed extension with existing stale version key """ extension = self.extension_class(extension_manager=self.manager) version_key = ExtensionManager.VERSION_SETTINGS_KEY self.assertFalse(extension.registration.installed) # Add a bad version key, perhaps copy/pasted by hand from an admin. # We'll set it to the current version. extension.settings.set(version_key, extension.info.version) extension.settings.save() # Enable the extension. It shouldn't blow up. extension = self.manager.enable_extension(self.extension_class.id) self.assertTrue(extension.registration.installed) self.assertIsNotNone(extension.settings.get(version_key)) def test_install_media_concurrent_threads(self): """Testing ExtensionManager updating media for existing extension with concurrent threads """ version_key = ExtensionManager.VERSION_SETTINGS_KEY extension = self.extension_class(extension_manager=self.manager) extension.registration.installed = True extension.registration.enabled = True extension.registration.save() extension.__class__.instance = extension extension.settings.set(version_key, '0.5') extension.settings.save() self.assertEqual(len(self.manager.get_installed_extensions()), 1) self.spy_on(self.manager._install_extension_media) self.spy_on(self.manager._install_extension_media_internal, call_original=False) self._run_thread_test( lambda: self.manager._install_extension_media(extension.__class__)) self.assertEqual( len(self.manager._install_extension_media.calls), 2) self.assertEqual( len(self.manager._install_extension_media_internal.calls), 1) self.assertEqual(self.exceptions, []) def test_disable_unregisters_static_bundles(self): """Testing ExtensionManager unregisters static bundles when disabling extension """ settings.PIPELINE_CSS = {} settings.PIPELINE_JS = {} extension = self.extension_class(extension_manager=self.manager) extension = self.manager.enable_extension(self.extension_class.id) self.assertEqual(len(settings.PIPELINE_CSS), 1) self.assertEqual(len(settings.PIPELINE_JS), 1) self.manager.disable_extension(extension.id) self.assertEqual(len(settings.PIPELINE_CSS), 0) self.assertEqual(len(settings.PIPELINE_JS), 0) def test_extension_list_sync(self): """Testing ExtensionManager extension list synchronization cross-process """ key = 'extension-list-sync' manager1 = ExtensionManager(key) manager2 = ExtensionManager(key) for manager in (manager1, manager2): manager._entrypoint_iterator = Mock( return_value=[self.fake_entrypoint] ) manager1.load() manager2.load() self.assertEqual(len(manager1.get_installed_extensions()), 1) self.assertEqual(len(manager2.get_installed_extensions()), 1) self.assertEqual(len(manager1.get_enabled_extensions()), 0) self.assertEqual(len(manager2.get_enabled_extensions()), 0) manager1.enable_extension(self.extension_class.id) self.assertEqual(len(manager1.get_enabled_extensions()), 1) self.assertEqual(len(manager2.get_enabled_extensions()), 0) self.assertFalse(manager1.is_expired()) self.assertTrue(manager2.is_expired()) manager2.load(full_reload=True) self.assertEqual(len(manager1.get_enabled_extensions()), 1) self.assertEqual(len(manager2.get_enabled_extensions()), 1) self.assertFalse(manager1.is_expired()) self.assertFalse(manager2.is_expired()) def test_extension_settings_sync(self): """Testing ExtensionManager extension settings synchronization cross-process """ key = 'extension-settings-sync' setting_key = 'foo' setting_val = 'abc123' manager1 = ExtensionManager(key) manager2 = ExtensionManager(key) for manager in (manager1, manager2): manager._entrypoint_iterator = Mock( return_value=[self.fake_entrypoint] ) manager1.load() extension1 = manager1.enable_extension(self.extension_class.id) manager2.load() self.assertFalse(manager1.is_expired()) self.assertFalse(manager2.is_expired()) extension2 = manager2.get_enabled_extension(self.extension_class.id) self.assertNotEqual(extension2, None) self.assertFalse(setting_key in extension1.settings) self.assertFalse(setting_key in extension2.settings) extension1.settings[setting_key] = setting_val extension1.settings.save() self.assertFalse(setting_key in extension2.settings) self.assertFalse(manager1.is_expired()) self.assertTrue(manager2.is_expired()) manager2.load(full_reload=True) extension2 = manager2.get_enabled_extension(self.extension_class.id) self.assertFalse(manager1.is_expired()) self.assertFalse(manager2.is_expired()) self.assertTrue(setting_key in extension1.settings) self.assertTrue(setting_key in extension2.settings) self.assertEqual(extension1.settings[setting_key], setting_val) self.assertEqual(extension2.settings[setting_key], setting_val) def test_load_blocks_sync_gen(self): """Testing ExtensionManager.load blocks bumping sync generation number """ key = 'check-expired-test' manager1 = ExtensionManager(key) manager2 = ExtensionManager(key) for manager in (manager1, manager2): manager._entrypoint_iterator = Mock( return_value=[self.fake_entrypoint] ) manager1.load() manager1.enable_extension(self.extension_class.id) manager2.load() self.assertEqual(manager1._last_sync_gen, manager2._last_sync_gen) # Trigger a save whenever the extension initializes. self.extension_class.initialize = lambda ext: ext.settings.save() # Bump the generation number. extension = manager2.get_enabled_extension(self.extension_class.id) extension.settings.save() self.assertNotEqual(manager1._last_sync_gen, manager2._last_sync_gen) # Loading now should retain the new sync generation number, instead # of bumping it. manager1.load(full_reload=True) self.assertEqual(manager1._last_sync_gen, manager2._last_sync_gen) def _run_thread_test(self, main_func): def _thread_main(main_connection, main_func): # Insert the connection from the main thread, so that we can # perform lookups. We never write. from django.db import connections connections['default'] = main_connection main_func() # Store the main connection. We're going to let the threads share it. # This trick courtesy of the Django unit tests # (django/tests/backends/tests.py). from django.db import connections main_connection = connections['default'] main_connection.allow_thread_sharing = True self.exceptions = [] t1 = threading.Thread(target=_thread_main, args=[main_connection, main_func]) t2 = threading.Thread(target=_thread_main, args=[main_connection, main_func]) t1.start() t2.start() t1.join() t2.join() def _sleep_and_call(self, manager, orig_func, *args, **kwargs): # This works well enough to throw a monkey wrench into things. # One thread will be slightly ahead of the other. time.sleep(0.2) try: orig_func(*args, **kwargs) except Exception as e: logging.error('%s\n', e, exc_info=1) self.exceptions.append(e) def _spy_sleep_and_call(self, func): def _call(manager, *args, **kwargs): self._sleep_and_call(manager, orig_func, *args, **kwargs) orig_func = func self.spy_on(func, call_fake=_call)
class ExtensionManagerTest(TestCase): def setUp(self): class TestExtension(Extension): """An empty, dummy extension for testing""" css_bundles = {"default": {"source_filenames": ["test.css"]}} js_bundles = {"default": {"source_filenames": ["test.js"]}} self.key = "test_key" self.extension_class = TestExtension self.manager = ExtensionManager(self.key) self.fake_entrypoint = Mock() self.fake_entrypoint.load = Mock(return_value=self.extension_class) self.fake_entrypoint.dist = Mock() self.test_author = "Test author lorem ipsum" self.test_description = "Test description lorem ipsum" self.test_email = "Test [email protected]" self.test_home_page = "http://www.example.com" self.test_license = "Test License MIT GPL Apache Drivers" self.test_module_name = "testextension.dummy.dummy" self.test_module_to_app = "testextension.dummy" self.test_project_name = "TestProjectName" self.test_summary = "Test summary lorem ipsum" self.test_version = "1.0" self.test_htdocs_path = os.path.join(settings.EXTENSIONS_STATIC_ROOT, self.test_project_name) self.test_metadata = { "Name": self.test_project_name, "Version": self.test_version, "Summary": self.test_summary, "Description": self.test_description, "Author": self.test_author, "Author-email": self.test_email, "License": self.test_license, "Home-page": self.test_home_page, } self.fake_entrypoint.dist.get_metadata_lines = Mock( return_value=["%s: %s" % (key, value) for key, value in six.iteritems(self.test_metadata)] ) self.fake_entrypoint.dist.project_name = self.test_project_name self.fake_entrypoint.dist.version = self.test_version self.manager._entrypoint_iterator = Mock(return_value=[self.fake_entrypoint]) self.manager.load() def tearDown(self): self.manager.clear_sync_cache() def test_added_to_extension_managers(self): """Testing ExtensionManager registration""" self.assertTrue(self.manager in _extension_managers) def test_get_enabled_extensions_returns_empty(self): """Testing ExtensionManager.get_enabled_extensions with no extensions""" self.assertEqual(len(self.manager.get_enabled_extensions()), 0) def test_load(self): """Testing ExtensionManager.get_installed_extensions with loaded extensions""" self.assertEqual(len(self.manager.get_installed_extensions()), 1) self.assertTrue(self.extension_class in self.manager.get_installed_extensions()) self.assertTrue(hasattr(self.extension_class, "info")) self.assertEqual(self.extension_class.info.name, self.test_project_name) self.assertTrue(hasattr(self.extension_class, "registration")) self.assertEqual(self.extension_class.registration.name, self.test_project_name) def test_load_full_reload_hooks(self): """Testing ExtensionManager.load with full_reload=True""" self.assertEqual(len(self.manager.get_installed_extensions()), 1) extension = self.extension_class(extension_manager=self.manager) extension = self.manager.enable_extension(self.extension_class.id) URLHook(extension, ()) self.assertEqual(len(URLHook.hooks), 1) self.assertEqual(URLHook.hooks[0].extension, extension) self.manager.load(full_reload=True) self.assertEqual(len(URLHook.hooks), 0) def test_enable_registers_static_bundles(self): """Testing ExtensionManager registers static bundles when enabling extension""" settings.PIPELINE_CSS = {} settings.PIPELINE_JS = {} extension = self.extension_class(extension_manager=self.manager) extension = self.manager.enable_extension(self.extension_class.id) self.assertEqual(len(settings.PIPELINE_CSS), 1) self.assertEqual(len(settings.PIPELINE_JS), 1) key = "%s-default" % extension.id self.assertIn(key, settings.PIPELINE_CSS) self.assertIn(key, settings.PIPELINE_JS) css_bundle = settings.PIPELINE_CSS[key] js_bundle = settings.PIPELINE_JS[key] self.assertIn("source_filenames", css_bundle) self.assertEqual(css_bundle["source_filenames"], ["ext/%s/test.css" % extension.id]) self.assertIn("output_filename", css_bundle) self.assertEqual(css_bundle["output_filename"], "%s/css/default.min.css" % extension.id) self.assertIn("source_filenames", js_bundle) self.assertEqual(js_bundle["source_filenames"], ["ext/%s/test.js" % extension.id]) self.assertIn("output_filename", js_bundle) self.assertEqual(js_bundle["output_filename"], "%s/js/default.min.js" % extension.id) def test_disable_unregisters_static_bundles(self): """Testing ExtensionManager unregisters static bundles when disabling extension""" settings.PIPELINE_CSS = {} settings.PIPELINE_JS = {} extension = self.extension_class(extension_manager=self.manager) extension = self.manager.enable_extension(self.extension_class.id) self.assertEqual(len(settings.PIPELINE_CSS), 1) self.assertEqual(len(settings.PIPELINE_JS), 1) self.manager.disable_extension(extension.id) self.assertEqual(len(settings.PIPELINE_CSS), 0) self.assertEqual(len(settings.PIPELINE_JS), 0) def test_extension_list_sync(self): """Testing ExtensionManager extension list synchronization cross-process.""" key = "extension-list-sync" manager1 = ExtensionManager(key) manager2 = ExtensionManager(key) for manager in (manager1, manager2): manager._entrypoint_iterator = Mock(return_value=[self.fake_entrypoint]) manager1.load() manager2.load() self.assertEqual(len(manager1.get_installed_extensions()), 1) self.assertEqual(len(manager2.get_installed_extensions()), 1) self.assertEqual(len(manager1.get_enabled_extensions()), 0) self.assertEqual(len(manager2.get_enabled_extensions()), 0) manager1.enable_extension(self.extension_class.id) self.assertEqual(len(manager1.get_enabled_extensions()), 1) self.assertEqual(len(manager2.get_enabled_extensions()), 0) self.assertFalse(manager1.is_expired()) self.assertTrue(manager2.is_expired()) manager2.load(full_reload=True) self.assertEqual(len(manager1.get_enabled_extensions()), 1) self.assertEqual(len(manager2.get_enabled_extensions()), 1) self.assertFalse(manager1.is_expired()) self.assertFalse(manager2.is_expired()) def test_extension_settings_sync(self): """Testing ExtensionManager extension settings synchronization cross-process.""" key = "extension-settings-sync" setting_key = "foo" setting_val = "abc123" manager1 = ExtensionManager(key) manager2 = ExtensionManager(key) for manager in (manager1, manager2): manager._entrypoint_iterator = Mock(return_value=[self.fake_entrypoint]) manager1.load() extension1 = manager1.enable_extension(self.extension_class.id) manager2.load() self.assertFalse(manager1.is_expired()) self.assertFalse(manager2.is_expired()) extension2 = manager2.get_enabled_extension(self.extension_class.id) self.assertNotEqual(extension2, None) self.assertFalse(setting_key in extension1.settings) self.assertFalse(setting_key in extension2.settings) extension1.settings[setting_key] = setting_val extension1.settings.save() self.assertFalse(setting_key in extension2.settings) self.assertFalse(manager1.is_expired()) self.assertTrue(manager2.is_expired()) manager2.load(full_reload=True) extension2 = manager2.get_enabled_extension(self.extension_class.id) self.assertFalse(manager1.is_expired()) self.assertFalse(manager2.is_expired()) self.assertTrue(setting_key in extension1.settings) self.assertTrue(setting_key in extension2.settings) self.assertEqual(extension1.settings[setting_key], setting_val) self.assertEqual(extension2.settings[setting_key], setting_val)
def setUp(self): manager = ExtensionManager('') self.extension = DummyExtension(extension_manager=manager)
def test_load_blocks_sync_gen(self): """Testing ExtensionManager.load blocks bumping sync generation number """ key = 'check-expired-test' manager1 = ExtensionManager(key) manager2 = ExtensionManager(key) for manager in (manager1, manager2): manager._entrypoint_iterator = Mock( return_value=[self.fake_entrypoint]) manager1.load() manager1.enable_extension(self.extension_class.id) manager2.load() self.assertEqual(manager1._last_sync_gen, manager2._last_sync_gen) # Trigger a save whenever the extension initializes. self.extension_class.initialize = lambda ext: ext.settings.save() # Bump the generation number. extension = manager2.get_enabled_extension(self.extension_class.id) extension.settings.save() self.assertNotEqual(manager1._last_sync_gen, manager2._last_sync_gen) # Loading now should retain the new sync generation number, instead # of bumping it. manager1.load(full_reload=True) self.assertEqual(manager1._last_sync_gen, manager2._last_sync_gen)
def test_extension_settings_sync(self): """Testing ExtensionManager extension settings synchronization cross-process.""" key = 'extension-settings-sync' setting_key = 'foo' setting_val = 'abc123' manager1 = ExtensionManager(key) manager2 = ExtensionManager(key) for manager in (manager1, manager2): manager._entrypoint_iterator = Mock( return_value=[self.fake_entrypoint] ) manager1.load() extension1 = manager1.enable_extension(self.extension_class.id) manager2.load() self.assertFalse(manager1.is_expired()) self.assertFalse(manager2.is_expired()) extension2 = manager2.get_enabled_extension(self.extension_class.id) self.assertNotEqual(extension2, None) self.assertFalse(setting_key in extension1.settings) self.assertFalse(setting_key in extension2.settings) extension1.settings[setting_key] = setting_val extension1.settings.save() self.assertFalse(setting_key in extension2.settings) self.assertFalse(manager1.is_expired()) self.assertTrue(manager2.is_expired()) manager2.load(full_reload=True) extension2 = manager2.get_enabled_extension(self.extension_class.id) self.assertFalse(manager1.is_expired()) self.assertFalse(manager2.is_expired()) self.assertTrue(setting_key in extension1.settings) self.assertTrue(setting_key in extension2.settings) self.assertEqual(extension1.settings[setting_key], setting_val) self.assertEqual(extension2.settings[setting_key], setting_val)
def setUp(self): self.manager = ExtensionManager('') self.extension = \ TestExtensionWithRegistration(extension_manager=self.manager)
def setUp(self): manager = ExtensionManager('') self.extension = \ TestExtensionWithRegistration(extension_manager=manager) self.extension_hook = TestExtensionHook(self.extension)
class ExtensionManagerTest(TestCase): def setUp(self): class TestExtension(Extension): """An empty, dummy extension for testing""" css_bundles = { 'default': { 'source_filenames': ['test.css'], } } js_bundles = { 'default': { 'source_filenames': ['test.js'], } } self.key = 'test_key' self.extension_class = TestExtension self.manager = ExtensionManager(self.key) self.fake_entrypoint = Mock() self.fake_entrypoint.load = Mock(return_value=self.extension_class) self.fake_entrypoint.dist = Mock() self.test_author = 'Test author lorem ipsum' self.test_description = 'Test description lorem ipsum' self.test_email = 'Test [email protected]' self.test_home_page = 'http://www.example.com' self.test_license = 'Test License MIT GPL Apache Drivers' self.test_module_name = 'testextension.dummy.dummy' self.test_module_to_app = 'testextension.dummy' self.test_project_name = 'TestProjectName' self.test_summary = 'Test summary lorem ipsum' self.test_version = '1.0' self.test_metadata = { 'Name': self.test_project_name, 'Version': self.test_version, 'Summary': self.test_summary, 'Description': self.test_description, 'Author': self.test_author, 'Author-email': self.test_email, 'License': self.test_license, 'Home-page': self.test_home_page, } self.fake_entrypoint.dist.get_metadata_lines = Mock( return_value=[ "%s: %s" % (key, value) for key, value in six.iteritems(self.test_metadata) ]) self.fake_entrypoint.dist.project_name = self.test_project_name self.fake_entrypoint.dist.version = self.test_version self.manager._entrypoint_iterator = Mock( return_value=[self.fake_entrypoint] ) self.manager.load() def tearDown(self): self.manager.clear_sync_cache() def test_added_to_extension_managers(self): """Testing ExtensionManager registration""" self.assertTrue(self.manager in _extension_managers) def test_get_enabled_extensions_returns_empty(self): """Testing ExtensionManager.get_enabled_extensions with no extensions""" self.assertEqual(len(self.manager.get_enabled_extensions()), 0) def test_load(self): """Testing ExtensionManager.get_installed_extensions with loaded extensions""" self.assertEqual(len(self.manager.get_installed_extensions()), 1) self.assertTrue(self.extension_class in self.manager.get_installed_extensions()) self.assertTrue(hasattr(self.extension_class, 'info')) self.assertEqual(self.extension_class.info.name, self.test_project_name) self.assertTrue(hasattr(self.extension_class, 'registration')) self.assertEqual(self.extension_class.registration.name, self.test_project_name) def test_load_full_reload_hooks(self): """Testing ExtensionManager.load with full_reload=True""" self.assertEqual(len(self.manager.get_installed_extensions()), 1) extension = self.extension_class(extension_manager=self.manager) extension = self.manager.enable_extension(self.extension_class.id) URLHook(extension, ()) self.assertEqual(len(URLHook.hooks), 1) self.assertEqual(URLHook.hooks[0].extension, extension) self.manager.load(full_reload=True) self.assertEqual(len(URLHook.hooks), 0) def test_enable_registers_static_bundles(self): """Testing ExtensionManager registers static bundles when enabling extension""" settings.PIPELINE_CSS = {} settings.PIPELINE_JS = {} extension = self.extension_class(extension_manager=self.manager) extension = self.manager.enable_extension(self.extension_class.id) self.assertEqual(len(settings.PIPELINE_CSS), 1) self.assertEqual(len(settings.PIPELINE_JS), 1) key = '%s-default' % extension.id self.assertIn(key, settings.PIPELINE_CSS) self.assertIn(key, settings.PIPELINE_JS) css_bundle = settings.PIPELINE_CSS[key] js_bundle = settings.PIPELINE_JS[key] self.assertIn('source_filenames', css_bundle) self.assertEqual(css_bundle['source_filenames'], ['ext/%s/test.css' % extension.id]) self.assertIn('output_filename', css_bundle) self.assertEqual(css_bundle['output_filename'], 'ext/%s/css/default.min.css' % extension.id) self.assertIn('source_filenames', js_bundle) self.assertEqual(js_bundle['source_filenames'], ['ext/%s/test.js' % extension.id]) self.assertIn('output_filename', js_bundle) self.assertEqual(js_bundle['output_filename'], 'ext/%s/js/default.min.js' % extension.id) def test_disable_unregisters_static_bundles(self): """Testing ExtensionManager unregisters static bundles when disabling extension""" settings.PIPELINE_CSS = {} settings.PIPELINE_JS = {} extension = self.extension_class(extension_manager=self.manager) extension = self.manager.enable_extension(self.extension_class.id) self.assertEqual(len(settings.PIPELINE_CSS), 1) self.assertEqual(len(settings.PIPELINE_JS), 1) self.manager.disable_extension(extension.id) self.assertEqual(len(settings.PIPELINE_CSS), 0) self.assertEqual(len(settings.PIPELINE_JS), 0) def test_extension_list_sync(self): """Testing ExtensionManager extension list synchronization cross-process.""" key = 'extension-list-sync' manager1 = ExtensionManager(key) manager2 = ExtensionManager(key) for manager in (manager1, manager2): manager._entrypoint_iterator = Mock( return_value=[self.fake_entrypoint] ) manager1.load() manager2.load() self.assertEqual(len(manager1.get_installed_extensions()), 1) self.assertEqual(len(manager2.get_installed_extensions()), 1) self.assertEqual(len(manager1.get_enabled_extensions()), 0) self.assertEqual(len(manager2.get_enabled_extensions()), 0) manager1.enable_extension(self.extension_class.id) self.assertEqual(len(manager1.get_enabled_extensions()), 1) self.assertEqual(len(manager2.get_enabled_extensions()), 0) self.assertFalse(manager1.is_expired()) self.assertTrue(manager2.is_expired()) manager2.load(full_reload=True) self.assertEqual(len(manager1.get_enabled_extensions()), 1) self.assertEqual(len(manager2.get_enabled_extensions()), 1) self.assertFalse(manager1.is_expired()) self.assertFalse(manager2.is_expired()) def test_extension_settings_sync(self): """Testing ExtensionManager extension settings synchronization cross-process.""" key = 'extension-settings-sync' setting_key = 'foo' setting_val = 'abc123' manager1 = ExtensionManager(key) manager2 = ExtensionManager(key) for manager in (manager1, manager2): manager._entrypoint_iterator = Mock( return_value=[self.fake_entrypoint] ) manager1.load() extension1 = manager1.enable_extension(self.extension_class.id) manager2.load() self.assertFalse(manager1.is_expired()) self.assertFalse(manager2.is_expired()) extension2 = manager2.get_enabled_extension(self.extension_class.id) self.assertNotEqual(extension2, None) self.assertFalse(setting_key in extension1.settings) self.assertFalse(setting_key in extension2.settings) extension1.settings[setting_key] = setting_val extension1.settings.save() self.assertFalse(setting_key in extension2.settings) self.assertFalse(manager1.is_expired()) self.assertTrue(manager2.is_expired()) manager2.load(full_reload=True) extension2 = manager2.get_enabled_extension(self.extension_class.id) self.assertFalse(manager1.is_expired()) self.assertFalse(manager2.is_expired()) self.assertTrue(setting_key in extension1.settings) self.assertTrue(setting_key in extension2.settings) self.assertEqual(extension1.settings[setting_key], setting_val) self.assertEqual(extension2.settings[setting_key], setting_val)
def test_extension_list_sync(self): """Testing ExtensionManager extension list synchronization cross-process """ key = 'extension-list-sync' manager1 = ExtensionManager(key) manager2 = ExtensionManager(key) for manager in (manager1, manager2): manager._entrypoint_iterator = Mock( return_value=[self.fake_entrypoint]) manager1.load() manager2.load() self.assertEqual(len(manager1.get_installed_extensions()), 1) self.assertEqual(len(manager2.get_installed_extensions()), 1) self.assertEqual(len(manager1.get_enabled_extensions()), 0) self.assertEqual(len(manager2.get_enabled_extensions()), 0) manager1.enable_extension(self.extension_class.id) self.assertEqual(len(manager1.get_enabled_extensions()), 1) self.assertEqual(len(manager2.get_enabled_extensions()), 0) self.assertFalse(manager1.is_expired()) self.assertTrue(manager2.is_expired()) manager2.load(full_reload=True) self.assertEqual(len(manager1.get_enabled_extensions()), 1) self.assertEqual(len(manager2.get_enabled_extensions()), 1) self.assertFalse(manager1.is_expired()) self.assertFalse(manager2.is_expired())
def test_extension_list_sync(self): """Testing ExtensionManager extension list synchronization cross-process.""" key = 'extension-list-sync' manager1 = ExtensionManager(key) manager2 = ExtensionManager(key) for manager in (manager1, manager2): manager._entrypoint_iterator = Mock( return_value=[self.fake_entrypoint] ) manager1.load() manager2.load() self.assertEqual(len(manager1.get_installed_extensions()), 1) self.assertEqual(len(manager2.get_installed_extensions()), 1) self.assertEqual(len(manager1.get_enabled_extensions()), 0) self.assertEqual(len(manager2.get_enabled_extensions()), 0) manager1.enable_extension(self.extension_class.id) self.assertEqual(len(manager1.get_enabled_extensions()), 1) self.assertEqual(len(manager2.get_enabled_extensions()), 0) self.assertFalse(manager1.is_expired()) self.assertTrue(manager2.is_expired()) manager2.load(full_reload=True) self.assertEqual(len(manager1.get_enabled_extensions()), 1) self.assertEqual(len(manager2.get_enabled_extensions()), 1) self.assertFalse(manager1.is_expired()) self.assertFalse(manager2.is_expired())
def test_extension_settings_sync(self): """Testing ExtensionManager extension settings synchronization cross-process """ key = 'extension-settings-sync' setting_key = 'foo' setting_val = 'abc123' manager1 = ExtensionManager(key) manager2 = ExtensionManager(key) for manager in (manager1, manager2): manager._entrypoint_iterator = Mock( return_value=[self.fake_entrypoint]) manager1.load() extension1 = manager1.enable_extension(self.extension_class.id) manager2.load() self.assertFalse(manager1.is_expired()) self.assertFalse(manager2.is_expired()) extension2 = manager2.get_enabled_extension(self.extension_class.id) self.assertNotEqual(extension2, None) self.assertFalse(setting_key in extension1.settings) self.assertFalse(setting_key in extension2.settings) extension1.settings[setting_key] = setting_val extension1.settings.save() self.assertFalse(setting_key in extension2.settings) self.assertFalse(manager1.is_expired()) self.assertTrue(manager2.is_expired()) manager2.load(full_reload=True) extension2 = manager2.get_enabled_extension(self.extension_class.id) self.assertFalse(manager1.is_expired()) self.assertFalse(manager2.is_expired()) self.assertTrue(setting_key in extension1.settings) self.assertTrue(setting_key in extension2.settings) self.assertEqual(extension1.settings[setting_key], setting_val) self.assertEqual(extension2.settings[setting_key], setting_val)
class ExtensionManagerTest(SpyAgency, TestCase): def setUp(self): class TestExtension(Extension): """An empty, dummy extension for testing""" css_bundles = { 'default': { 'source_filenames': ['test.css'], } } js_bundles = { 'default': { 'source_filenames': ['test.js'], } } self.key = 'test_key' self.extension_class = TestExtension self.manager = ExtensionManager(self.key) self.fake_entrypoint = Mock() self.fake_entrypoint.load = Mock(return_value=self.extension_class) self.fake_entrypoint.dist = Mock() self.test_author = 'Test author lorem ipsum' self.test_description = 'Test description lorem ipsum' self.test_email = 'Test [email protected]' self.test_home_page = 'http://www.example.com' self.test_license = 'Test License MIT GPL Apache Drivers' self.test_module_name = 'testextension.dummy.dummy' self.test_module_to_app = 'testextension.dummy' self.test_project_name = 'TestProjectName' self.test_summary = 'Test summary lorem ipsum' self.test_version = '1.0' self.test_metadata = { 'Name': self.test_project_name, 'Version': self.test_version, 'Summary': self.test_summary, 'Description': self.test_description, 'Author': self.test_author, 'Author-email': self.test_email, 'License': self.test_license, 'Home-page': self.test_home_page, } self.fake_entrypoint.dist.get_metadata_lines = Mock(return_value=[ "%s: %s" % (key, value) for key, value in six.iteritems(self.test_metadata) ]) self.fake_entrypoint.dist.project_name = self.test_project_name self.fake_entrypoint.dist.version = self.test_version self.manager._entrypoint_iterator = Mock( return_value=[self.fake_entrypoint]) self.manager.load() def tearDown(self): self.manager.clear_sync_cache() def test_added_to_extension_managers(self): """Testing ExtensionManager registration""" self.assertTrue(self.manager in _extension_managers) def test_get_enabled_extensions_returns_empty(self): """Testing ExtensionManager.get_enabled_extensions with no extensions """ self.assertEqual(len(self.manager.get_enabled_extensions()), 0) def test_load(self): """Testing ExtensionManager.get_installed_extensions with loaded extensions """ self.assertEqual(len(self.manager.get_installed_extensions()), 1) self.assertTrue( self.extension_class in self.manager.get_installed_extensions()) self.assertTrue(hasattr(self.extension_class, 'info')) self.assertEqual(self.extension_class.info.name, self.test_project_name) self.assertTrue(hasattr(self.extension_class, 'registration')) self.assertEqual(self.extension_class.registration.name, self.test_project_name) def test_load_full_reload_hooks(self): """Testing ExtensionManager.load with full_reload=True""" self.assertEqual(len(self.manager.get_installed_extensions()), 1) extension = self.extension_class(extension_manager=self.manager) extension = self.manager.enable_extension(self.extension_class.id) URLHook(extension, ()) self.assertEqual(len(URLHook.hooks), 1) self.assertEqual(URLHook.hooks[0].extension, extension) self.manager.load(full_reload=True) self.assertEqual(len(URLHook.hooks), 0) def test_load_concurrent_threads(self): """Testing ExtensionManager.load with concurrent threads""" # There are a number of things that could go wrong both during # uninitialization and during initialization of extensions, if # two threads attempt to reload at the same time and locking isn't # properly implemented. # # Extension uninit could be called twice, resulting in one thread # attempting to access state that's already been destroyed. We # could end up hitting: # # "Extension's installed app <app> is missing a ref count." # "'<Extension>' object has no attribute 'info'." # # (Without locking, we end up hitting the latter in this test.) # # If an extension is being initialized twice simultaneously, then # it can hit other errors. An easy one to hit is this assertion: # # assert extension_id not in self._extension_instances # # With proper locking, these issues don't come up. That's what # this test case is attempting to check for. # Enable one extension. This extension's state will get a bit messed # up if the thread locking fails. We only need one to trigger this. self.assertEqual(len(self.manager.get_installed_extensions()), 1) self.manager.enable_extension(self.extension_class.id) self.spy_on(self.manager._load_extensions) self._spy_sleep_and_call(self.manager._init_extension) self._spy_sleep_and_call(self.manager._uninit_extension) self._run_thread_test(lambda: self.manager.load(full_reload=True)) self.assertEqual(len(self.manager._load_extensions.calls), 2) self.assertEqual(len(self.manager._uninit_extension.calls), 2) self.assertEqual(len(self.manager._init_extension.calls), 2) self.assertEqual(self.exceptions, []) def test_enable_registers_static_bundles(self): """Testing ExtensionManager registers static bundles when enabling extension """ settings.PIPELINE_CSS = {} settings.PIPELINE_JS = {} extension = self.extension_class(extension_manager=self.manager) extension = self.manager.enable_extension(self.extension_class.id) self.assertEqual(len(settings.PIPELINE_CSS), 1) self.assertEqual(len(settings.PIPELINE_JS), 1) key = '%s-default' % extension.id self.assertIn(key, settings.PIPELINE_CSS) self.assertIn(key, settings.PIPELINE_JS) css_bundle = settings.PIPELINE_CSS[key] js_bundle = settings.PIPELINE_JS[key] self.assertIn('source_filenames', css_bundle) self.assertEqual(css_bundle['source_filenames'], ['ext/%s/test.css' % extension.id]) self.assertIn('output_filename', css_bundle) self.assertEqual(css_bundle['output_filename'], 'ext/%s/css/default.min.css' % extension.id) self.assertIn('source_filenames', js_bundle) self.assertEqual(js_bundle['source_filenames'], ['ext/%s/test.js' % extension.id]) self.assertIn('output_filename', js_bundle) self.assertEqual(js_bundle['output_filename'], 'ext/%s/js/default.min.js' % extension.id) def test_install_extension_media_with_stale_version_key(self): """Testing ExtensionManager installing media for newly installed extension with existing stale version key """ extension = self.extension_class(extension_manager=self.manager) version_key = ExtensionManager.VERSION_SETTINGS_KEY self.assertFalse(extension.registration.installed) # Add a bad version key, perhaps copy/pasted by hand from an admin. # We'll set it to the current version. extension.settings.set(version_key, extension.info.version) extension.settings.save() # Enable the extension. It shouldn't blow up. extension = self.manager.enable_extension(self.extension_class.id) self.assertTrue(extension.registration.installed) self.assertIsNotNone(extension.settings.get(version_key)) def test_install_media_concurrent_threads(self): """Testing ExtensionManager updating media for existing extension with concurrent threads """ version_key = ExtensionManager.VERSION_SETTINGS_KEY extension = self.extension_class(extension_manager=self.manager) extension.registration.installed = True extension.registration.enabled = True extension.registration.save() extension.__class__.instance = extension extension.settings.set(version_key, '0.5') extension.settings.save() self.assertEqual(len(self.manager.get_installed_extensions()), 1) self.spy_on(self.manager._install_extension_media) self.spy_on(self.manager._install_extension_media_internal, call_original=False) self._run_thread_test( lambda: self.manager._install_extension_media(extension.__class__)) self.assertEqual(len(self.manager._install_extension_media.calls), 2) self.assertEqual( len(self.manager._install_extension_media_internal.calls), 1) self.assertEqual(self.exceptions, []) def test_disable_unregisters_static_bundles(self): """Testing ExtensionManager unregisters static bundles when disabling extension """ settings.PIPELINE_CSS = {} settings.PIPELINE_JS = {} extension = self.extension_class(extension_manager=self.manager) extension = self.manager.enable_extension(self.extension_class.id) self.assertEqual(len(settings.PIPELINE_CSS), 1) self.assertEqual(len(settings.PIPELINE_JS), 1) self.manager.disable_extension(extension.id) self.assertEqual(len(settings.PIPELINE_CSS), 0) self.assertEqual(len(settings.PIPELINE_JS), 0) def test_extension_list_sync(self): """Testing ExtensionManager extension list synchronization cross-process """ key = 'extension-list-sync' manager1 = ExtensionManager(key) manager2 = ExtensionManager(key) for manager in (manager1, manager2): manager._entrypoint_iterator = Mock( return_value=[self.fake_entrypoint]) manager1.load() manager2.load() self.assertEqual(len(manager1.get_installed_extensions()), 1) self.assertEqual(len(manager2.get_installed_extensions()), 1) self.assertEqual(len(manager1.get_enabled_extensions()), 0) self.assertEqual(len(manager2.get_enabled_extensions()), 0) manager1.enable_extension(self.extension_class.id) self.assertEqual(len(manager1.get_enabled_extensions()), 1) self.assertEqual(len(manager2.get_enabled_extensions()), 0) self.assertFalse(manager1.is_expired()) self.assertTrue(manager2.is_expired()) manager2.load(full_reload=True) self.assertEqual(len(manager1.get_enabled_extensions()), 1) self.assertEqual(len(manager2.get_enabled_extensions()), 1) self.assertFalse(manager1.is_expired()) self.assertFalse(manager2.is_expired()) def test_extension_settings_sync(self): """Testing ExtensionManager extension settings synchronization cross-process """ key = 'extension-settings-sync' setting_key = 'foo' setting_val = 'abc123' manager1 = ExtensionManager(key) manager2 = ExtensionManager(key) for manager in (manager1, manager2): manager._entrypoint_iterator = Mock( return_value=[self.fake_entrypoint]) manager1.load() extension1 = manager1.enable_extension(self.extension_class.id) manager2.load() self.assertFalse(manager1.is_expired()) self.assertFalse(manager2.is_expired()) extension2 = manager2.get_enabled_extension(self.extension_class.id) self.assertNotEqual(extension2, None) self.assertFalse(setting_key in extension1.settings) self.assertFalse(setting_key in extension2.settings) extension1.settings[setting_key] = setting_val extension1.settings.save() self.assertFalse(setting_key in extension2.settings) self.assertFalse(manager1.is_expired()) self.assertTrue(manager2.is_expired()) manager2.load(full_reload=True) extension2 = manager2.get_enabled_extension(self.extension_class.id) self.assertFalse(manager1.is_expired()) self.assertFalse(manager2.is_expired()) self.assertTrue(setting_key in extension1.settings) self.assertTrue(setting_key in extension2.settings) self.assertEqual(extension1.settings[setting_key], setting_val) self.assertEqual(extension2.settings[setting_key], setting_val) def test_load_blocks_sync_gen(self): """Testing ExtensionManager.load blocks bumping sync generation number """ key = 'check-expired-test' manager1 = ExtensionManager(key) manager2 = ExtensionManager(key) for manager in (manager1, manager2): manager._entrypoint_iterator = Mock( return_value=[self.fake_entrypoint]) manager1.load() manager1.enable_extension(self.extension_class.id) manager2.load() self.assertEqual(manager1._last_sync_gen, manager2._last_sync_gen) # Trigger a save whenever the extension initializes. self.extension_class.initialize = lambda ext: ext.settings.save() # Bump the generation number. extension = manager2.get_enabled_extension(self.extension_class.id) extension.settings.save() self.assertNotEqual(manager1._last_sync_gen, manager2._last_sync_gen) # Loading now should retain the new sync generation number, instead # of bumping it. manager1.load(full_reload=True) self.assertEqual(manager1._last_sync_gen, manager2._last_sync_gen) def _run_thread_test(self, main_func): def _thread_main(main_connection, main_func, sleep_time): # Insert the connection from the main thread, so that we can # perform lookups. We never write. from django.db import connections connections['default'] = main_connection time.sleep(sleep_time) main_func() # Store the main connection. We're going to let the threads share it. # This trick courtesy of the Django unit tests # (django/tests/backends/tests.py). from django.db import connections main_connection = connections['default'] main_connection.allow_thread_sharing = True self.exceptions = [] t1 = threading.Thread(target=_thread_main, args=[main_connection, main_func, 0.2]) t2 = threading.Thread(target=_thread_main, args=[main_connection, main_func, 0.1]) t1.start() t2.start() t1.join() t2.join() def _sleep_and_call(self, manager, orig_func, *args, **kwargs): # This works well enough to throw a monkey wrench into things. # One thread will be slightly ahead of the other. time.sleep(0.2) try: orig_func(*args, **kwargs) except Exception as e: logging.error('%s\n', e, exc_info=1) self.exceptions.append(e) def _spy_sleep_and_call(self, func): def _call(manager, *args, **kwargs): self._sleep_and_call(manager, orig_func, *args, **kwargs) orig_func = func self.spy_on(func, call_fake=_call)
class ExtensionManagerTest(TestCase): def setUp(self): class TestExtension(Extension): """An empty, dummy extension for testing""" css_bundles = { 'default': { 'source_filenames': ['test.css'], } } js_bundles = { 'default': { 'source_filenames': ['test.js'], } } self.key = 'test_key' self.extension_class = TestExtension self.manager = ExtensionManager(self.key) self.fake_entrypoint = Mock() self.fake_entrypoint.load = Mock(return_value=self.extension_class) self.fake_entrypoint.dist = Mock() self.test_author = 'Test author lorem ipsum' self.test_description = 'Test description lorem ipsum' self.test_email = 'Test [email protected]' self.test_home_page = 'http://www.example.com' self.test_license = 'Test License MIT GPL Apache Drivers' self.test_module_name = 'testextension.dummy.dummy' self.test_module_to_app = 'testextension.dummy' self.test_project_name = 'TestProjectName' self.test_summary = 'Test summary lorem ipsum' self.test_version = '1.0' self.test_htdocs_path = os.path.join(settings.EXTENSIONS_STATIC_ROOT, self.test_project_name) self.test_metadata = { 'Name': self.test_project_name, 'Version': self.test_version, 'Summary': self.test_summary, 'Description': self.test_description, 'Author': self.test_author, 'Author-email': self.test_email, 'License': self.test_license, 'Home-page': self.test_home_page, } self.fake_entrypoint.dist.get_metadata_lines = Mock( return_value=[ "%s: %s" % (key, value) for key, value in six.iteritems(self.test_metadata) ]) self.fake_entrypoint.dist.project_name = self.test_project_name self.fake_entrypoint.dist.version = self.test_version self.manager._entrypoint_iterator = Mock( return_value=[self.fake_entrypoint] ) self.manager.load() def tearDown(self): self.manager.clear_sync_cache() def test_added_to_extension_managers(self): """Testing ExtensionManager registration""" self.assertTrue(self.manager in _extension_managers) def test_get_enabled_extensions_returns_empty(self): """Testing ExtensionManager.get_enabled_extensions with no extensions""" self.assertEqual(len(self.manager.get_enabled_extensions()), 0) def test_load(self): """Testing ExtensionManager.get_installed_extensions with loaded extensions""" self.assertEqual(len(self.manager.get_installed_extensions()), 1) self.assertTrue(self.extension_class in self.manager.get_installed_extensions()) self.assertTrue(hasattr(self.extension_class, 'info')) self.assertEqual(self.extension_class.info.name, self.test_project_name) self.assertTrue(hasattr(self.extension_class, 'registration')) self.assertEqual(self.extension_class.registration.name, self.test_project_name) def test_load_full_reload_hooks(self): """Testing ExtensionManager.load with full_reload=True""" self.assertEqual(len(self.manager.get_installed_extensions()), 1) extension = self.extension_class(extension_manager=self.manager) extension = self.manager.enable_extension(self.extension_class.id) URLHook(extension, ()) self.assertEqual(len(URLHook.hooks), 1) self.assertEqual(URLHook.hooks[0].extension, extension) self.manager.load(full_reload=True) self.assertEqual(len(URLHook.hooks), 0) def test_enable_registers_static_bundles(self): """Testing ExtensionManager registers static bundles when enabling extension""" settings.PIPELINE_CSS = {} settings.PIPELINE_JS = {} extension = self.extension_class(extension_manager=self.manager) extension = self.manager.enable_extension(self.extension_class.id) self.assertEqual(len(settings.PIPELINE_CSS), 1) self.assertEqual(len(settings.PIPELINE_JS), 1) key = '%s-default' % extension.id self.assertIn(key, settings.PIPELINE_CSS) self.assertIn(key, settings.PIPELINE_JS) css_bundle = settings.PIPELINE_CSS[key] js_bundle = settings.PIPELINE_JS[key] self.assertIn('source_filenames', css_bundle) self.assertEqual(css_bundle['source_filenames'], ['ext/%s/test.css' % extension.id]) self.assertIn('output_filename', css_bundle) self.assertEqual(css_bundle['output_filename'], '%s/css/default.min.css' % extension.id) self.assertIn('source_filenames', js_bundle) self.assertEqual(js_bundle['source_filenames'], ['ext/%s/test.js' % extension.id]) self.assertIn('output_filename', js_bundle) self.assertEqual(js_bundle['output_filename'], '%s/js/default.min.js' % extension.id) def test_disable_unregisters_static_bundles(self): """Testing ExtensionManager unregisters static bundles when disabling extension""" settings.PIPELINE_CSS = {} settings.PIPELINE_JS = {} extension = self.extension_class(extension_manager=self.manager) extension = self.manager.enable_extension(self.extension_class.id) self.assertEqual(len(settings.PIPELINE_CSS), 1) self.assertEqual(len(settings.PIPELINE_JS), 1) self.manager.disable_extension(extension.id) self.assertEqual(len(settings.PIPELINE_CSS), 0) self.assertEqual(len(settings.PIPELINE_JS), 0) def test_extension_list_sync(self): """Testing ExtensionManager extension list synchronization cross-process.""" key = 'extension-list-sync' manager1 = ExtensionManager(key) manager2 = ExtensionManager(key) for manager in (manager1, manager2): manager._entrypoint_iterator = Mock( return_value=[self.fake_entrypoint] ) manager1.load() manager2.load() self.assertEqual(len(manager1.get_installed_extensions()), 1) self.assertEqual(len(manager2.get_installed_extensions()), 1) self.assertEqual(len(manager1.get_enabled_extensions()), 0) self.assertEqual(len(manager2.get_enabled_extensions()), 0) manager1.enable_extension(self.extension_class.id) self.assertEqual(len(manager1.get_enabled_extensions()), 1) self.assertEqual(len(manager2.get_enabled_extensions()), 0) self.assertFalse(manager1.is_expired()) self.assertTrue(manager2.is_expired()) manager2.load(full_reload=True) self.assertEqual(len(manager1.get_enabled_extensions()), 1) self.assertEqual(len(manager2.get_enabled_extensions()), 1) self.assertFalse(manager1.is_expired()) self.assertFalse(manager2.is_expired()) def test_extension_settings_sync(self): """Testing ExtensionManager extension settings synchronization cross-process.""" key = 'extension-settings-sync' setting_key = 'foo' setting_val = 'abc123' manager1 = ExtensionManager(key) manager2 = ExtensionManager(key) for manager in (manager1, manager2): manager._entrypoint_iterator = Mock( return_value=[self.fake_entrypoint] ) manager1.load() extension1 = manager1.enable_extension(self.extension_class.id) manager2.load() self.assertFalse(manager1.is_expired()) self.assertFalse(manager2.is_expired()) extension2 = manager2.get_enabled_extension(self.extension_class.id) self.assertNotEqual(extension2, None) self.assertFalse(setting_key in extension1.settings) self.assertFalse(setting_key in extension2.settings) extension1.settings[setting_key] = setting_val extension1.settings.save() self.assertFalse(setting_key in extension2.settings) self.assertFalse(manager1.is_expired()) self.assertTrue(manager2.is_expired()) manager2.load(full_reload=True) extension2 = manager2.get_enabled_extension(self.extension_class.id) self.assertFalse(manager1.is_expired()) self.assertFalse(manager2.is_expired()) self.assertTrue(setting_key in extension1.settings) self.assertTrue(setting_key in extension2.settings) self.assertEqual(extension1.settings[setting_key], setting_val) self.assertEqual(extension2.settings[setting_key], setting_val)