class TestBranchRewriter(TestCaseWithFactory): layer = DatabaseFunctionalLayer def setUp(self): super(TestBranchRewriter, self).setUp() self.fake_time = FakeTime(0) def makeRewriter(self): return BranchRewriter(BufferLogger(), self.fake_time.now) def getLoggerOutput(self, rewriter): return rewriter.logger.getLogBuffer() def test_rewriteLine_found_dot_bzr(self): # Requests for /$branch_name/.bzr/... are redirected to where the # branches are served from by ID. rewriter = self.makeRewriter() branches = [ self.factory.makeProductBranch(), self.factory.makePersonalBranch(), self.factory.makePackageBranch() ] transaction.commit() output = [ rewriter.rewriteLine("/%s/.bzr/README" % branch.unique_name) for branch in branches ] expected = [ 'file:///var/tmp/bazaar.launchpad.dev/mirrors/%s/.bzr/README' % branch_id_to_path(branch.id) for branch in branches ] self.assertEqual(expected, output) def test_rewriteLine_found_not_dot_bzr(self): # Requests for /$branch_name/... that are not to .bzr directories are # redirected to codebrowse. rewriter = self.makeRewriter() branches = [ self.factory.makeProductBranch(), self.factory.makePersonalBranch(), self.factory.makePackageBranch() ] transaction.commit() output = [ rewriter.rewriteLine("/%s/changes" % branch.unique_name) for branch in branches ] expected = [ 'http://localhost:8080/%s/changes' % branch.unique_name for branch in branches ] self.assertEqual(expected, output) def test_rewriteLine_private(self): # All requests for /$branch_name/... for private branches are # rewritten to codebrowse, which will then redirect them to https and # handle them there. rewriter = self.makeRewriter() branch = self.factory.makeAnyBranch( information_type=InformationType.USERDATA) unique_name = removeSecurityProxy(branch).unique_name transaction.commit() output = [ rewriter.rewriteLine("/%s/changes" % unique_name), rewriter.rewriteLine("/%s/.bzr" % unique_name) ] self.assertEqual([ 'http://localhost:8080/%s/changes' % unique_name, 'http://localhost:8080/%s/.bzr' % unique_name ], output) def test_rewriteLine_id_alias_found_dot_bzr(self): # Requests for /+branch-id/$id/.bzr/... are redirected to where the # branches are served from by ID if they are public. rewriter = self.makeRewriter() branches = [ self.factory.makeProductBranch(), self.factory.makePersonalBranch(), self.factory.makePackageBranch() ] transaction.commit() output = [ rewriter.rewriteLine("%s/.bzr/README" % branch_id_alias(branch)) for branch in branches ] expected = [ 'file:///var/tmp/bazaar.launchpad.dev/mirrors/%s/.bzr/README' % branch_id_to_path(branch.id) for branch in branches ] self.assertEqual(expected, output) def test_rewriteLine_id_alias_private(self): # All requests for /+branch-id/$id/... for private branches return # 'NULL'. This is translated by apache to a 404. rewriter = self.makeRewriter() branch = self.factory.makeAnyBranch( information_type=InformationType.USERDATA) path = branch_id_alias(removeSecurityProxy(branch)) transaction.commit() output = [ rewriter.rewriteLine("%s/changes" % path), rewriter.rewriteLine("%s/.bzr" % path) ] self.assertEqual(['NULL', 'NULL'], output) def test_rewriteLine_id_alias_logs_cache_hit(self): # The second request for a branch using the alias hits the cache. rewriter = self.makeRewriter() branch = self.factory.makeAnyBranch() transaction.commit() path = "%s/.bzr/README" % branch_id_alias(branch) rewriter.rewriteLine(path) rewriter.rewriteLine(path) logging_output_lines = self.getLoggerOutput(rewriter).strip().split( '\n') self.assertEqual(2, len(logging_output_lines)) self.assertIsNot( None, re.match("INFO .* -> .* (.*s, cache: HIT)", logging_output_lines[-1]), "No hit found in %r" % logging_output_lines[-1]) def test_rewriteLine_static(self): # Requests to /static are rewritten to codebrowse urls. rewriter = self.makeRewriter() output = rewriter.rewriteLine("/static/foo") self.assertEqual('http://localhost:8080/static/foo', output) def test_rewriteLine_not_found(self): # If the request does not map to a branch, we redirect it to # codebrowse as it can generate a 404. rewriter = self.makeRewriter() not_found_path = "/~nouser/noproduct" output = rewriter.rewriteLine(not_found_path) self.assertEqual('http://localhost:8080%s' % not_found_path, output) def test_rewriteLine_logs_cache_miss(self): # The first request for a branch misses the cache and logs this fact. rewriter = self.makeRewriter() branch = self.factory.makeAnyBranch() transaction.commit() rewriter.rewriteLine('/' + branch.unique_name + '/.bzr/README') logging_output = self.getLoggerOutput(rewriter) self.assertIsNot( None, re.match("INFO .* -> .* (.*s, cache: MISS)", logging_output), "No miss found in %r" % logging_output) def test_rewriteLine_logs_cache_hit(self): # The second request for a branch misses the cache and logs this fact. rewriter = self.makeRewriter() branch = self.factory.makeAnyBranch() transaction.commit() rewriter.rewriteLine('/' + branch.unique_name + '/.bzr/README') rewriter.rewriteLine('/' + branch.unique_name + '/.bzr/README') logging_output_lines = self.getLoggerOutput(rewriter).strip().split( '\n') self.assertEqual(2, len(logging_output_lines)) self.assertIsNot( None, re.match("INFO .* -> .* (.*s, cache: HIT)", logging_output_lines[-1]), "No hit found in %r" % logging_output_lines[-1]) def test_rewriteLine_cache_expires(self): # The second request for a branch misses the cache and logs this fact. rewriter = self.makeRewriter() branch = self.factory.makeAnyBranch() transaction.commit() rewriter.rewriteLine('/' + branch.unique_name + '/.bzr/README') self.fake_time.advance( config.codehosting.branch_rewrite_cache_lifetime + 1) rewriter.rewriteLine('/' + branch.unique_name + '/.bzr/README') logging_output_lines = self.getLoggerOutput(rewriter).strip().split( '\n') self.assertEqual(2, len(logging_output_lines)) self.assertIsNot( None, re.match("INFO .* -> .* (.*s, cache: MISS)", logging_output_lines[-1]), "No miss found in %r" % logging_output_lines[-1]) def test_getBranchIdAndTrailingPath_cached(self): """When results come from cache, they should be the same.""" rewriter = self.makeRewriter() branch = self.factory.makeAnyBranch() transaction.commit() id_path = ( branch.id, u'/.bzr/README', ) result = rewriter._getBranchIdAndTrailingPath('/' + branch.unique_name + '/.bzr/README') self.assertEqual(id_path + ('MISS', ), result) result = rewriter._getBranchIdAndTrailingPath('/' + branch.unique_name + '/.bzr/README') self.assertEqual(id_path + ('HIT', ), result) def test_branch_id_alias_private(self): # Private branches are not found at all (this is for anonymous access) owner = self.factory.makePerson() branch = self.factory.makeAnyBranch( owner=owner, information_type=InformationType.USERDATA) with person_logged_in(owner): path = branch_id_alias(branch) result = self.makeRewriter()._getBranchIdAndTrailingPath(path) self.assertEqual((None, None, 'MISS'), result) def test_branch_id_alias_transitive_private(self): # Transitively private branches are not found at all # (this is for anonymous access) owner = self.factory.makePerson() private_branch = self.factory.makeAnyBranch( owner=owner, information_type=InformationType.USERDATA) branch = self.factory.makeAnyBranch(stacked_on=private_branch, owner=owner) with person_logged_in(owner): path = branch_id_alias(branch) result = self.makeRewriter()._getBranchIdAndTrailingPath(path) self.assertEqual((None, None, 'MISS'), result)
class TestLoggingUIFactory(TestCase): """Tests for `LoggingUIFactory`.""" def setUp(self): TestCase.setUp(self) self.fake_time = FakeTime(12345) self.logger = BufferLogger() def makeLoggingUIFactory(self): """Make a `LoggingUIFactory` with fake time and contained output.""" return LoggingUIFactory( time_source=self.fake_time.now, logger=self.logger) def test_first_progress_updates(self): # The first call to progress generates some output. factory = self.makeLoggingUIFactory() bar = factory.nested_progress_bar() bar.update("hi") self.assertEqual('INFO hi\n', self.logger.getLogBuffer()) def test_second_rapid_progress_doesnt_update(self): # The second of two progress calls that are less than the factory's # interval apart does not generate output. factory = self.makeLoggingUIFactory() bar = factory.nested_progress_bar() bar.update("hi") self.fake_time.advance(factory.interval / 2) bar.update("there") self.assertEqual('INFO hi\n', self.logger.getLogBuffer()) def test_second_slow_progress_updates(self): # The second of two progress calls that are more than the factory's # interval apart does generate output. factory = self.makeLoggingUIFactory() bar = factory.nested_progress_bar() bar.update("hi") self.fake_time.advance(factory.interval * 2) bar.update("there") self.assertEqual( 'INFO hi\n' 'INFO there\n', self.logger.getLogBuffer()) def test_first_progress_on_new_bar_updates(self): # The first progress on a new progress task always generates output. factory = self.makeLoggingUIFactory() bar = factory.nested_progress_bar() bar.update("hi") self.fake_time.advance(factory.interval / 2) bar2 = factory.nested_progress_bar() bar2.update("there") self.assertEqual( 'INFO hi\nINFO hi:there\n', self.logger.getLogBuffer()) def test_update_with_count_formats_nicely(self): # When more details are passed to update, they are formatted nicely. factory = self.makeLoggingUIFactory() bar = factory.nested_progress_bar() bar.update("hi", 1, 8) self.assertEqual('INFO hi 1/8\n', self.logger.getLogBuffer()) def test_report_transport_activity_reports_bytes_since_last_update(self): # If there is no call to _progress_updated for 'interval' seconds, the # next call to report_transport_activity will report however many # bytes have been transferred since the update. factory = self.makeLoggingUIFactory() bar = factory.nested_progress_bar() bar.update("hi", 1, 10) self.fake_time.advance(factory.interval / 2) # The bytes in this call will not be reported: factory.report_transport_activity(None, 1, 'read') self.fake_time.advance(factory.interval) bar.update("hi", 2, 10) self.fake_time.advance(factory.interval / 2) factory.report_transport_activity(None, 10, 'read') self.fake_time.advance(factory.interval) factory.report_transport_activity(None, 100, 'read') self.fake_time.advance(factory.interval * 2) # This call will cause output that does not include the transport # activity info. bar.update("hi", 3, 10) self.assertEqual( 'INFO hi 1/10\n' 'INFO hi 2/10\n' 'INFO 110 bytes transferred | hi 2/10\n' 'INFO hi 3/10\n', self.logger.getLogBuffer()) def test_note(self): factory = self.makeLoggingUIFactory() factory.note("Banja Luka") self.assertEqual('INFO Banja Luka\n', self.logger.getLogBuffer()) def test_show_error(self): factory = self.makeLoggingUIFactory() factory.show_error("Exploding Peaches") self.assertEqual( "ERROR Exploding Peaches\n", self.logger.getLogBuffer()) def test_confirm_action(self): factory = self.makeLoggingUIFactory() self.assertTrue(factory.confirm_action( "How are you %(when)s?", "wellness", {"when": "today"})) def test_show_message(self): factory = self.makeLoggingUIFactory() factory.show_message("Peaches") self.assertEqual("INFO Peaches\n", self.logger.getLogBuffer()) def test_get_username(self): factory = self.makeLoggingUIFactory() self.assertIs( None, factory.get_username("Who are you %(when)s?", when="today")) def test_get_password(self): factory = self.makeLoggingUIFactory() self.assertIs( None, factory.get_password("How is your %(drink)s", drink="coffee")) def test_show_warning(self): factory = self.makeLoggingUIFactory() factory.show_warning("Peaches") self.assertEqual("WARNING Peaches\n", self.logger.getLogBuffer()) def test_show_warning_unicode(self): factory = self.makeLoggingUIFactory() factory.show_warning(u"Peach\xeas") self.assertEqual( "WARNING Peach\xc3\xaas\n", self.logger.getLogBuffer()) def test_user_warning(self): factory = self.makeLoggingUIFactory() factory.show_user_warning('cross_format_fetch', from_format="athing", to_format="anotherthing") message = factory._user_warning_templates['cross_format_fetch'] % { "from_format": "athing", "to_format": "anotherthing", } self.assertEqual("WARNING %s\n" % message, self.logger.getLogBuffer()) def test_clear_term(self): factory = self.makeLoggingUIFactory() factory.clear_term() self.assertEqual("", self.logger.getLogBuffer())
class TestBranchFileSystemClient(TestCase): """Tests for `BranchFileSystemClient`.""" run_tests_with = AsynchronousDeferredRunTest def setUp(self): super(TestBranchFileSystemClient, self).setUp() frontend = InMemoryFrontend() self.factory = frontend.getLaunchpadObjectFactory() self.user = self.factory.makePerson() self._xmlrpc_client = XMLRPCWrapper(frontend.getCodehostingEndpoint()) self.fake_time = FakeTime(12345) def advanceTime(self, amount): """Advance the time seen by clients made by `makeClient` by 'amount'. """ self.fake_time.advance(amount) def makeClient(self, expiry_time=None, seen_new_branch_hook=None): """Make a `BranchFileSystemClient`. The created client interacts with the InMemoryFrontend. """ return BranchFileSystemClient( self._xmlrpc_client, self.user.id, expiry_time=expiry_time, seen_new_branch_hook=seen_new_branch_hook, _now=self.fake_time.now) def test_translatePath(self): branch = self.factory.makeAnyBranch() client = self.makeClient() deferred = client.translatePath('/' + branch.unique_name) deferred.addCallback( self.assertEqual, (BRANCH_TRANSPORT, dict(id=branch.id, writable=False), '')) return deferred def test_get_matched_part(self): # We cache results based on the part of the URL that the server # matched. _getMatchedPart returns that part, based on the path given # and the returned data. branch = self.factory.makeAnyBranch() client = self.makeClient() requested_path = '/%s/a/b' % branch.unique_name matched_part = client._getMatchedPart(requested_path, (BRANCH_TRANSPORT, { 'id': branch.id, 'writable': False }, 'a/b')) self.assertEqual('/%s' % branch.unique_name, matched_part) def test_get_matched_part_no_trailing_slash(self): # _getMatchedPart always returns the absolute path to the object that # the server matched, even if there is no trailing slash and no # trailing path. # # This test is added to exercise a corner case. branch = self.factory.makeAnyBranch() client = self.makeClient() requested_path = '/%s' % branch.unique_name matched_part = client._getMatchedPart(requested_path, (BRANCH_TRANSPORT, { 'id': branch.id, 'writable': False }, '')) self.assertEqual('/%s' % branch.unique_name, matched_part) def test_get_matched_part_no_trailing_path(self): # _getMatchedPart always returns the absolute path to the object that # the server matched, even if there is a trailing slash and no # trailing path. # # This test is added to exercise a corner case. branch = self.factory.makeAnyBranch() client = self.makeClient() requested_path = '/%s/' % branch.unique_name matched_part = client._getMatchedPart(requested_path, (BRANCH_TRANSPORT, { 'id': branch.id, 'writable': False }, '')) self.assertEqual('/%s' % branch.unique_name, matched_part) def test_path_translation_cache(self): # We can retrieve data that we've added to the cache. The data we # retrieve looks an awful lot like the data that the endpoint sends. branch = self.factory.makeAnyBranch() client = self.makeClient() fake_data = self.factory.getUniqueString() client._addToCache((BRANCH_TRANSPORT, fake_data, ''), '/%s' % branch.unique_name) result = client._getFromCache('/%s/foo/bar' % branch.unique_name) self.assertEqual((BRANCH_TRANSPORT, fake_data, 'foo/bar'), result) def test_path_translation_cache_within_expiry_time(self): # If the client treats cached values as having a limited lifetime, # repeated requests within that lifetime are served from the cache. branch = self.factory.makeAnyBranch() expiry_time = 2.0 client = self.makeClient(expiry_time=expiry_time) fake_data = self.factory.getUniqueString() client._addToCache((BRANCH_TRANSPORT, fake_data, ''), '/%s' % branch.unique_name) self.advanceTime(expiry_time / 2) result = client._getFromCache('/%s/foo/bar' % branch.unique_name) self.assertEqual((BRANCH_TRANSPORT, fake_data, 'foo/bar'), result) def test_path_translation_cache_after_expiry_time(self): # If the client treats cached values as having a limited lifetime, a # request longer than that lifetime after the first is not served from # the cache. branch = self.factory.makeAnyBranch() expiry_time = 2.0 client = self.makeClient(expiry_time=expiry_time) fake_data = self.factory.getUniqueString() client._addToCache((BRANCH_TRANSPORT, fake_data, ''), '/%s' % branch.unique_name) self.advanceTime(expiry_time * 2) self.assertRaises(NotInCache, client._getFromCache, '/%s/foo/bar' % branch.unique_name) def test_path_translation_cache_respects_path_segments(self): # We only get a value from the cache if the cached path is a parent of # the requested path. Simple string prefixing is not enough. Added to # trap bug 308077. branch = self.factory.makeAnyBranch() client = self.makeClient() fake_data = self.factory.getUniqueString() client._addToCache((BRANCH_TRANSPORT, fake_data, ''), '/%s' % branch.unique_name) self.assertRaises(NotInCache, client._getFromCache, '/%s-suffix' % branch.unique_name) def test_not_in_cache(self): # _getFromCache raises an error when the given path isn't in the # cache. client = self.makeClient() self.assertRaises(NotInCache, client._getFromCache, "foo") def test_translatePath_retrieves_from_cache(self): # If the path already has a prefix in the cache, we use that prefix to # translate the path. branch = self.factory.makeAnyBranch() client = self.makeClient() # We'll store fake data in the cache to show that we get data from # the cache if it's present. fake_data = self.factory.getUniqueString() client._addToCache((BRANCH_TRANSPORT, fake_data, ''), '/%s' % branch.unique_name) requested_path = '/%s/foo/bar' % branch.unique_name deferred = client.translatePath(requested_path) def path_translated((transport_type, data, trailing_path)): self.assertEqual(BRANCH_TRANSPORT, transport_type) self.assertEqual(fake_data, data) self.assertEqual('foo/bar', trailing_path) return deferred.addCallback(path_translated) def test_translatePath_adds_to_cache(self): # translatePath adds successful path translations to the cache, thus # allowing for future translations to be retrieved from the cache. branch = self.factory.makeAnyBranch() client = self.makeClient() deferred = client.translatePath('/' + branch.unique_name) deferred.addCallback(self.assertEqual, client._getFromCache('/' + branch.unique_name)) return deferred def test_translatePath_control_branch_cache_interaction(self): # We don't want the caching to make us mis-interpret paths in the # branch as paths into the control transport. branch = self.factory.makeAnyBranch() client = self.makeClient() self.factory.enableDefaultStackingForProduct(branch.product) deferred = client.translatePath('/~' + branch.owner.name + '/' + branch.product.name + '/.bzr/format') def call_translatePath_again(ignored): return client.translatePath('/' + branch.unique_name) def check_results((transport_type, data, trailing_path)): self.assertEqual(BRANCH_TRANSPORT, transport_type) deferred.addCallback(call_translatePath_again) deferred.addCallback(check_results) return deferred def test_errors_not_cached(self): # Don't cache failed translations. What would be the point? client = self.makeClient() deferred = client.translatePath('/foo/bar/baz') def translated_successfully(result): self.fail("Translated successfully. Expected error, got %r" % result) def failed_translation(failure): self.assertRaises(NotInCache, client._getFromCache, '/foo/bar/baz') return deferred.addCallbacks(translated_successfully, failed_translation) def test_seen_new_branch_hook_called_for_new_branch(self): # A callable passed as the seen_new_branch_hook when constructing a # BranchFileSystemClient will be called with a previously unseen # branch's unique_name when a path for a that branch is translated for # the first time. seen_branches = [] client = self.makeClient(seen_new_branch_hook=seen_branches.append) branch = self.factory.makeAnyBranch() client.translatePath('/' + branch.unique_name + '/trailing') self.assertEqual([branch.unique_name], seen_branches) def test_seen_new_branch_hook_called_for_each_branch(self): # The seen_new_branch_hook is called for a each branch that is # accessed. seen_branches = [] client = self.makeClient(seen_new_branch_hook=seen_branches.append) branch1 = self.factory.makeAnyBranch() branch2 = self.factory.makeAnyBranch() client.translatePath('/' + branch1.unique_name + '/trailing') client.translatePath('/' + branch2.unique_name + '/trailing') self.assertEqual([branch1.unique_name, branch2.unique_name], seen_branches) def test_seen_new_branch_hook_called_once_for_a_new_branch(self): # The seen_new_branch_hook is only called once for a given branch. seen_branches = [] client = self.makeClient(seen_new_branch_hook=seen_branches.append) branch1 = self.factory.makeAnyBranch() branch2 = self.factory.makeAnyBranch() client.translatePath('/' + branch1.unique_name + '/trailing') client.translatePath('/' + branch2.unique_name + '/trailing') client.translatePath('/' + branch1.unique_name + '/different') self.assertEqual([branch1.unique_name, branch2.unique_name], seen_branches)
class TestLoggingUIFactory(TestCase): """Tests for `LoggingUIFactory`.""" def setUp(self): TestCase.setUp(self) self.fake_time = FakeTime(12345) self.logger = BufferLogger() def makeLoggingUIFactory(self): """Make a `LoggingUIFactory` with fake time and contained output.""" return LoggingUIFactory(time_source=self.fake_time.now, logger=self.logger) def test_first_progress_updates(self): # The first call to progress generates some output. factory = self.makeLoggingUIFactory() bar = factory.nested_progress_bar() bar.update("hi") self.assertEqual('INFO hi\n', self.logger.getLogBuffer()) def test_second_rapid_progress_doesnt_update(self): # The second of two progress calls that are less than the factory's # interval apart does not generate output. factory = self.makeLoggingUIFactory() bar = factory.nested_progress_bar() bar.update("hi") self.fake_time.advance(factory.interval / 2) bar.update("there") self.assertEqual('INFO hi\n', self.logger.getLogBuffer()) def test_second_slow_progress_updates(self): # The second of two progress calls that are more than the factory's # interval apart does generate output. factory = self.makeLoggingUIFactory() bar = factory.nested_progress_bar() bar.update("hi") self.fake_time.advance(factory.interval * 2) bar.update("there") self.assertEqual('INFO hi\n' 'INFO there\n', self.logger.getLogBuffer()) def test_first_progress_on_new_bar_updates(self): # The first progress on a new progress task always generates output. factory = self.makeLoggingUIFactory() bar = factory.nested_progress_bar() bar.update("hi") self.fake_time.advance(factory.interval / 2) bar2 = factory.nested_progress_bar() bar2.update("there") self.assertEqual('INFO hi\nINFO hi:there\n', self.logger.getLogBuffer()) def test_update_with_count_formats_nicely(self): # When more details are passed to update, they are formatted nicely. factory = self.makeLoggingUIFactory() bar = factory.nested_progress_bar() bar.update("hi", 1, 8) self.assertEqual('INFO hi 1/8\n', self.logger.getLogBuffer()) def test_report_transport_activity_reports_bytes_since_last_update(self): # If there is no call to _progress_updated for 'interval' seconds, the # next call to report_transport_activity will report however many # bytes have been transferred since the update. factory = self.makeLoggingUIFactory() bar = factory.nested_progress_bar() bar.update("hi", 1, 10) self.fake_time.advance(factory.interval / 2) # The bytes in this call will not be reported: factory.report_transport_activity(None, 1, 'read') self.fake_time.advance(factory.interval) bar.update("hi", 2, 10) self.fake_time.advance(factory.interval / 2) factory.report_transport_activity(None, 10, 'read') self.fake_time.advance(factory.interval) factory.report_transport_activity(None, 100, 'read') self.fake_time.advance(factory.interval * 2) # This call will cause output that does not include the transport # activity info. bar.update("hi", 3, 10) self.assertEqual( 'INFO hi 1/10\n' 'INFO hi 2/10\n' 'INFO 110 bytes transferred | hi 2/10\n' 'INFO hi 3/10\n', self.logger.getLogBuffer()) def test_note(self): factory = self.makeLoggingUIFactory() factory.note("Banja Luka") self.assertEqual('INFO Banja Luka\n', self.logger.getLogBuffer()) def test_show_error(self): factory = self.makeLoggingUIFactory() factory.show_error("Exploding Peaches") self.assertEqual("ERROR Exploding Peaches\n", self.logger.getLogBuffer()) def test_confirm_action(self): factory = self.makeLoggingUIFactory() self.assertTrue( factory.confirm_action("How are you %(when)s?", "wellness", {"when": "today"})) def test_show_message(self): factory = self.makeLoggingUIFactory() factory.show_message("Peaches") self.assertEqual("INFO Peaches\n", self.logger.getLogBuffer()) def test_get_username(self): factory = self.makeLoggingUIFactory() self.assertIs( None, factory.get_username("Who are you %(when)s?", when="today")) def test_get_password(self): factory = self.makeLoggingUIFactory() self.assertIs( None, factory.get_password("How is your %(drink)s", drink="coffee")) def test_show_warning(self): factory = self.makeLoggingUIFactory() factory.show_warning("Peaches") self.assertEqual("WARNING Peaches\n", self.logger.getLogBuffer()) def test_show_warning_unicode(self): factory = self.makeLoggingUIFactory() factory.show_warning(u"Peach\xeas") self.assertEqual("WARNING Peach\xc3\xaas\n", self.logger.getLogBuffer()) def test_user_warning(self): factory = self.makeLoggingUIFactory() factory.show_user_warning('cross_format_fetch', from_format="athing", to_format="anotherthing") message = factory._user_warning_templates['cross_format_fetch'] % { "from_format": "athing", "to_format": "anotherthing", } self.assertEqual("WARNING %s\n" % message, self.logger.getLogBuffer()) def test_clear_term(self): factory = self.makeLoggingUIFactory() factory.clear_term() self.assertEqual("", self.logger.getLogBuffer())
class TestBranchFileSystemClient(TestCase): """Tests for `BranchFileSystemClient`.""" run_tests_with = AsynchronousDeferredRunTest def setUp(self): super(TestBranchFileSystemClient, self).setUp() frontend = InMemoryFrontend() self.factory = frontend.getLaunchpadObjectFactory() self.user = self.factory.makePerson() self._xmlrpc_client = XMLRPCWrapper(frontend.getCodehostingEndpoint()) self.fake_time = FakeTime(12345) def advanceTime(self, amount): """Advance the time seen by clients made by `makeClient` by 'amount'. """ self.fake_time.advance(amount) def makeClient(self, expiry_time=None, seen_new_branch_hook=None): """Make a `BranchFileSystemClient`. The created client interacts with the InMemoryFrontend. """ return BranchFileSystemClient( self._xmlrpc_client, self.user.id, expiry_time=expiry_time, seen_new_branch_hook=seen_new_branch_hook, _now=self.fake_time.now) def test_translatePath(self): branch = self.factory.makeAnyBranch() client = self.makeClient() deferred = client.translatePath('/' + branch.unique_name) deferred.addCallback( self.assertEqual, (BRANCH_TRANSPORT, dict(id=branch.id, writable=False), '')) return deferred def test_get_matched_part(self): # We cache results based on the part of the URL that the server # matched. _getMatchedPart returns that part, based on the path given # and the returned data. branch = self.factory.makeAnyBranch() client = self.makeClient() requested_path = '/%s/a/b' % branch.unique_name matched_part = client._getMatchedPart( requested_path, (BRANCH_TRANSPORT, {'id': branch.id, 'writable': False}, 'a/b')) self.assertEqual('/%s' % branch.unique_name, matched_part) def test_get_matched_part_no_trailing_slash(self): # _getMatchedPart always returns the absolute path to the object that # the server matched, even if there is no trailing slash and no # trailing path. # # This test is added to exercise a corner case. branch = self.factory.makeAnyBranch() client = self.makeClient() requested_path = '/%s' % branch.unique_name matched_part = client._getMatchedPart( requested_path, (BRANCH_TRANSPORT, {'id': branch.id, 'writable': False}, '')) self.assertEqual('/%s' % branch.unique_name, matched_part) def test_get_matched_part_no_trailing_path(self): # _getMatchedPart always returns the absolute path to the object that # the server matched, even if there is a trailing slash and no # trailing path. # # This test is added to exercise a corner case. branch = self.factory.makeAnyBranch() client = self.makeClient() requested_path = '/%s/' % branch.unique_name matched_part = client._getMatchedPart( requested_path, (BRANCH_TRANSPORT, {'id': branch.id, 'writable': False}, '')) self.assertEqual('/%s' % branch.unique_name, matched_part) def test_path_translation_cache(self): # We can retrieve data that we've added to the cache. The data we # retrieve looks an awful lot like the data that the endpoint sends. branch = self.factory.makeAnyBranch() client = self.makeClient() fake_data = self.factory.getUniqueString() client._addToCache( (BRANCH_TRANSPORT, fake_data, ''), '/%s' % branch.unique_name) result = client._getFromCache('/%s/foo/bar' % branch.unique_name) self.assertEqual( (BRANCH_TRANSPORT, fake_data, 'foo/bar'), result) def test_path_translation_cache_within_expiry_time(self): # If the client treats cached values as having a limited lifetime, # repeated requests within that lifetime are served from the cache. branch = self.factory.makeAnyBranch() expiry_time = 2.0 client = self.makeClient(expiry_time=expiry_time) fake_data = self.factory.getUniqueString() client._addToCache( (BRANCH_TRANSPORT, fake_data, ''), '/%s' % branch.unique_name) self.advanceTime(expiry_time/2) result = client._getFromCache('/%s/foo/bar' % branch.unique_name) self.assertEqual( (BRANCH_TRANSPORT, fake_data, 'foo/bar'), result) def test_path_translation_cache_after_expiry_time(self): # If the client treats cached values as having a limited lifetime, a # request longer than that lifetime after the first is not served from # the cache. branch = self.factory.makeAnyBranch() expiry_time = 2.0 client = self.makeClient(expiry_time=expiry_time) fake_data = self.factory.getUniqueString() client._addToCache( (BRANCH_TRANSPORT, fake_data, ''), '/%s' % branch.unique_name) self.advanceTime(expiry_time*2) self.assertRaises(NotInCache, client._getFromCache, '/%s/foo/bar' % branch.unique_name) def test_path_translation_cache_respects_path_segments(self): # We only get a value from the cache if the cached path is a parent of # the requested path. Simple string prefixing is not enough. Added to # trap bug 308077. branch = self.factory.makeAnyBranch() client = self.makeClient() fake_data = self.factory.getUniqueString() client._addToCache( (BRANCH_TRANSPORT, fake_data, ''), '/%s' % branch.unique_name) self.assertRaises( NotInCache, client._getFromCache, '/%s-suffix' % branch.unique_name) def test_not_in_cache(self): # _getFromCache raises an error when the given path isn't in the # cache. client = self.makeClient() self.assertRaises( NotInCache, client._getFromCache, "foo") def test_translatePath_retrieves_from_cache(self): # If the path already has a prefix in the cache, we use that prefix to # translate the path. branch = self.factory.makeAnyBranch() client = self.makeClient() # We'll store fake data in the cache to show that we get data from # the cache if it's present. fake_data = self.factory.getUniqueString() client._addToCache( (BRANCH_TRANSPORT, fake_data, ''), '/%s' % branch.unique_name) requested_path = '/%s/foo/bar' % branch.unique_name deferred = client.translatePath(requested_path) def path_translated((transport_type, data, trailing_path)): self.assertEqual(BRANCH_TRANSPORT, transport_type) self.assertEqual(fake_data, data) self.assertEqual('foo/bar', trailing_path) return deferred.addCallback(path_translated) def test_translatePath_adds_to_cache(self): # translatePath adds successful path translations to the cache, thus # allowing for future translations to be retrieved from the cache. branch = self.factory.makeAnyBranch() client = self.makeClient() deferred = client.translatePath('/' + branch.unique_name) deferred.addCallback( self.assertEqual, client._getFromCache('/' + branch.unique_name)) return deferred def test_translatePath_control_branch_cache_interaction(self): # We don't want the caching to make us mis-interpret paths in the # branch as paths into the control transport. branch = self.factory.makeAnyBranch() client = self.makeClient() self.factory.enableDefaultStackingForProduct(branch.product) deferred = client.translatePath( '/~' + branch.owner.name + '/' + branch.product.name + '/.bzr/format') def call_translatePath_again(ignored): return client.translatePath('/' + branch.unique_name) def check_results((transport_type, data, trailing_path)): self.assertEqual(BRANCH_TRANSPORT, transport_type) deferred.addCallback(call_translatePath_again) deferred.addCallback(check_results) return deferred def test_errors_not_cached(self): # Don't cache failed translations. What would be the point? client = self.makeClient() deferred = client.translatePath('/foo/bar/baz') def translated_successfully(result): self.fail( "Translated successfully. Expected error, got %r" % result) def failed_translation(failure): self.assertRaises( NotInCache, client._getFromCache, '/foo/bar/baz') return deferred.addCallbacks( translated_successfully, failed_translation) def test_seen_new_branch_hook_called_for_new_branch(self): # A callable passed as the seen_new_branch_hook when constructing a # BranchFileSystemClient will be called with a previously unseen # branch's unique_name when a path for a that branch is translated for # the first time. seen_branches = [] client = self.makeClient(seen_new_branch_hook=seen_branches.append) branch = self.factory.makeAnyBranch() client.translatePath('/' + branch.unique_name + '/trailing') self.assertEqual([branch.unique_name], seen_branches) def test_seen_new_branch_hook_called_for_each_branch(self): # The seen_new_branch_hook is called for a each branch that is # accessed. seen_branches = [] client = self.makeClient(seen_new_branch_hook=seen_branches.append) branch1 = self.factory.makeAnyBranch() branch2 = self.factory.makeAnyBranch() client.translatePath('/' + branch1.unique_name + '/trailing') client.translatePath('/' + branch2.unique_name + '/trailing') self.assertEqual( [branch1.unique_name, branch2.unique_name], seen_branches) def test_seen_new_branch_hook_called_once_for_a_new_branch(self): # The seen_new_branch_hook is only called once for a given branch. seen_branches = [] client = self.makeClient(seen_new_branch_hook=seen_branches.append) branch1 = self.factory.makeAnyBranch() branch2 = self.factory.makeAnyBranch() client.translatePath('/' + branch1.unique_name + '/trailing') client.translatePath('/' + branch2.unique_name + '/trailing') client.translatePath('/' + branch1.unique_name + '/different') self.assertEqual( [branch1.unique_name, branch2.unique_name], seen_branches)
class TestBranchRewriter(TestCaseWithFactory): layer = DatabaseFunctionalLayer def setUp(self): super(TestBranchRewriter, self).setUp() self.fake_time = FakeTime(0) def makeRewriter(self): return BranchRewriter(BufferLogger(), self.fake_time.now) def getLoggerOutput(self, rewriter): return rewriter.logger.getLogBuffer() def test_rewriteLine_found_dot_bzr(self): # Requests for /$branch_name/.bzr/... are redirected to where the # branches are served from by ID. rewriter = self.makeRewriter() branches = [ self.factory.makeProductBranch(), self.factory.makePersonalBranch(), self.factory.makePackageBranch(), ] transaction.commit() output = [rewriter.rewriteLine("/%s/.bzr/README" % branch.unique_name) for branch in branches] expected = [ "file:///var/tmp/bazaar.launchpad.dev/mirrors/%s/.bzr/README" % branch_id_to_path(branch.id) for branch in branches ] self.assertEqual(expected, output) def test_rewriteLine_found_not_dot_bzr(self): # Requests for /$branch_name/... that are not to .bzr directories are # redirected to codebrowse. rewriter = self.makeRewriter() branches = [ self.factory.makeProductBranch(), self.factory.makePersonalBranch(), self.factory.makePackageBranch(), ] transaction.commit() output = [rewriter.rewriteLine("/%s/changes" % branch.unique_name) for branch in branches] expected = ["http://localhost:8080/%s/changes" % branch.unique_name for branch in branches] self.assertEqual(expected, output) def test_rewriteLine_private(self): # All requests for /$branch_name/... for private branches are # rewritten to codebrowse, which will then redirect them to https and # handle them there. rewriter = self.makeRewriter() branch = self.factory.makeAnyBranch(information_type=InformationType.USERDATA) unique_name = removeSecurityProxy(branch).unique_name transaction.commit() output = [rewriter.rewriteLine("/%s/changes" % unique_name), rewriter.rewriteLine("/%s/.bzr" % unique_name)] self.assertEqual( ["http://localhost:8080/%s/changes" % unique_name, "http://localhost:8080/%s/.bzr" % unique_name], output ) def test_rewriteLine_id_alias_found_dot_bzr(self): # Requests for /+branch-id/$id/.bzr/... are redirected to where the # branches are served from by ID if they are public. rewriter = self.makeRewriter() branches = [ self.factory.makeProductBranch(), self.factory.makePersonalBranch(), self.factory.makePackageBranch(), ] transaction.commit() output = [rewriter.rewriteLine("%s/.bzr/README" % branch_id_alias(branch)) for branch in branches] expected = [ "file:///var/tmp/bazaar.launchpad.dev/mirrors/%s/.bzr/README" % branch_id_to_path(branch.id) for branch in branches ] self.assertEqual(expected, output) def test_rewriteLine_id_alias_private(self): # All requests for /+branch-id/$id/... for private branches return # 'NULL'. This is translated by apache to a 404. rewriter = self.makeRewriter() branch = self.factory.makeAnyBranch(information_type=InformationType.USERDATA) path = branch_id_alias(removeSecurityProxy(branch)) transaction.commit() output = [rewriter.rewriteLine("%s/changes" % path), rewriter.rewriteLine("%s/.bzr" % path)] self.assertEqual(["NULL", "NULL"], output) def test_rewriteLine_id_alias_logs_cache_hit(self): # The second request for a branch using the alias hits the cache. rewriter = self.makeRewriter() branch = self.factory.makeAnyBranch() transaction.commit() path = "%s/.bzr/README" % branch_id_alias(branch) rewriter.rewriteLine(path) rewriter.rewriteLine(path) logging_output_lines = self.getLoggerOutput(rewriter).strip().split("\n") self.assertEqual(2, len(logging_output_lines)) self.assertIsNot( None, re.match("INFO .* -> .* (.*s, cache: HIT)", logging_output_lines[-1]), "No hit found in %r" % logging_output_lines[-1], ) def test_rewriteLine_static(self): # Requests to /static are rewritten to codebrowse urls. rewriter = self.makeRewriter() output = rewriter.rewriteLine("/static/foo") self.assertEqual("http://localhost:8080/static/foo", output) def test_rewriteLine_not_found(self): # If the request does not map to a branch, we redirect it to # codebrowse as it can generate a 404. rewriter = self.makeRewriter() not_found_path = "/~nouser/noproduct" output = rewriter.rewriteLine(not_found_path) self.assertEqual("http://localhost:8080%s" % not_found_path, output) def test_rewriteLine_logs_cache_miss(self): # The first request for a branch misses the cache and logs this fact. rewriter = self.makeRewriter() branch = self.factory.makeAnyBranch() transaction.commit() rewriter.rewriteLine("/" + branch.unique_name + "/.bzr/README") logging_output = self.getLoggerOutput(rewriter) self.assertIsNot( None, re.match("INFO .* -> .* (.*s, cache: MISS)", logging_output), "No miss found in %r" % logging_output ) def test_rewriteLine_logs_cache_hit(self): # The second request for a branch misses the cache and logs this fact. rewriter = self.makeRewriter() branch = self.factory.makeAnyBranch() transaction.commit() rewriter.rewriteLine("/" + branch.unique_name + "/.bzr/README") rewriter.rewriteLine("/" + branch.unique_name + "/.bzr/README") logging_output_lines = self.getLoggerOutput(rewriter).strip().split("\n") self.assertEqual(2, len(logging_output_lines)) self.assertIsNot( None, re.match("INFO .* -> .* (.*s, cache: HIT)", logging_output_lines[-1]), "No hit found in %r" % logging_output_lines[-1], ) def test_rewriteLine_cache_expires(self): # The second request for a branch misses the cache and logs this fact. rewriter = self.makeRewriter() branch = self.factory.makeAnyBranch() transaction.commit() rewriter.rewriteLine("/" + branch.unique_name + "/.bzr/README") self.fake_time.advance(config.codehosting.branch_rewrite_cache_lifetime + 1) rewriter.rewriteLine("/" + branch.unique_name + "/.bzr/README") logging_output_lines = self.getLoggerOutput(rewriter).strip().split("\n") self.assertEqual(2, len(logging_output_lines)) self.assertIsNot( None, re.match("INFO .* -> .* (.*s, cache: MISS)", logging_output_lines[-1]), "No miss found in %r" % logging_output_lines[-1], ) def test_getBranchIdAndTrailingPath_cached(self): """When results come from cache, they should be the same.""" rewriter = self.makeRewriter() branch = self.factory.makeAnyBranch() transaction.commit() id_path = (branch.id, u"/.bzr/README") result = rewriter._getBranchIdAndTrailingPath("/" + branch.unique_name + "/.bzr/README") self.assertEqual(id_path + ("MISS",), result) result = rewriter._getBranchIdAndTrailingPath("/" + branch.unique_name + "/.bzr/README") self.assertEqual(id_path + ("HIT",), result) def test_branch_id_alias_private(self): # Private branches are not found at all (this is for anonymous access) owner = self.factory.makePerson() branch = self.factory.makeAnyBranch(owner=owner, information_type=InformationType.USERDATA) with person_logged_in(owner): path = branch_id_alias(branch) result = self.makeRewriter()._getBranchIdAndTrailingPath(path) self.assertEqual((None, None, "MISS"), result) def test_branch_id_alias_transitive_private(self): # Transitively private branches are not found at all # (this is for anonymous access) owner = self.factory.makePerson() private_branch = self.factory.makeAnyBranch(owner=owner, information_type=InformationType.USERDATA) branch = self.factory.makeAnyBranch(stacked_on=private_branch, owner=owner) with person_logged_in(owner): path = branch_id_alias(branch) result = self.makeRewriter()._getBranchIdAndTrailingPath(path) self.assertEqual((None, None, "MISS"), result)