mirror of
https://github.com/facebook/sapling.git
synced 2024-10-08 07:49:11 +03:00
Add integration test for rebasing in Hg.
Summary: This is a relatively simple test that takes two branches with no conflicts and rebases one on top of the other. It also provides modest checks to ensure Eden does not load a bunch of inodes unnecessarily when updating to the new head. This also introduces `EdenServerInspector`, which provides convenience methods for inspecting the Eden server via Thrift. Reviewed By: simpkins Differential Revision: D5504741 fbshipit-source-id: 6636c431658f24a850d0e5404d1a0e4f0528a781
This commit is contained in:
parent
b9b9ba32e9
commit
31eac649e2
@ -8,9 +8,10 @@
|
||||
from __future__ import (absolute_import, division,
|
||||
print_function, unicode_literals)
|
||||
|
||||
from .client import EdenNotRunningError, create_thrift_client
|
||||
from .client import EdenClient, EdenNotRunningError, create_thrift_client
|
||||
|
||||
__all__ = [
|
||||
'EdenClient',
|
||||
'EdenNotRunningError',
|
||||
'create_thrift_client',
|
||||
]
|
||||
|
91
eden/integration/hg/rebase_test.py
Normal file
91
eden/integration/hg/rebase_test.py
Normal file
@ -0,0 +1,91 @@
|
||||
#!/usr/bin/env python3
|
||||
#
|
||||
# Copyright (c) 2016-present, Facebook, Inc.
|
||||
# All rights reserved.
|
||||
#
|
||||
# This source code is licensed under the BSD-style license found in the
|
||||
# LICENSE file in the root directory of this source tree. An additional grant
|
||||
# of patent rights can be found in the PATENTS file in the same directory.
|
||||
|
||||
from .lib.hg_extension_test_base import HgExtensionTestBase
|
||||
from ..lib import eden_server_inspector
|
||||
|
||||
|
||||
class RebaseTest(HgExtensionTestBase):
|
||||
def populate_backing_repo(self, repo):
|
||||
repo.mkdir('numbers')
|
||||
repo.write_file('numbers/README', 'this will have two directories')
|
||||
self._base_commit = repo.commit('commit')
|
||||
|
||||
repo.mkdir('numbers/1')
|
||||
repo.write_file('numbers/1/11', '')
|
||||
self._c11 = repo.commit('c11')
|
||||
repo.write_file('numbers/1/12', '')
|
||||
self._c12 = repo.commit('c12')
|
||||
repo.write_file('numbers/1/13', '')
|
||||
self._c13 = repo.commit('c13')
|
||||
repo.write_file('numbers/1/14', '')
|
||||
self._c14 = repo.commit('c14')
|
||||
repo.write_file('numbers/1/15', '')
|
||||
self._c15 = repo.commit('c15')
|
||||
|
||||
repo.update(self._base_commit)
|
||||
repo.mkdir('numbers/2')
|
||||
repo.write_file('numbers/2/21', '')
|
||||
self._c21 = repo.commit('c21')
|
||||
repo.write_file('numbers/2/22', '')
|
||||
self._c22 = repo.commit('c22')
|
||||
repo.write_file('numbers/2/23', '')
|
||||
self._c23 = repo.commit('c23')
|
||||
repo.write_file('numbers/2/24', '')
|
||||
self._c24 = repo.commit('c24')
|
||||
repo.write_file('numbers/2/25', '')
|
||||
self._c25 = repo.commit('c25')
|
||||
|
||||
repo.update(self._base_commit)
|
||||
|
||||
def test_rebase_commit_with_independent_folder(self):
|
||||
stdout = self.hg('rebase', '-s', self._c11, '-d', self._c25)
|
||||
expected_stdout = f'''\
|
||||
rebasing 1:{self._c11[:12]} "c11"
|
||||
rebasing 2:{self._c12[:12]} "c12"
|
||||
rebasing 3:{self._c13[:12]} "c13"
|
||||
rebasing 4:{self._c14[:12]} "c14"
|
||||
rebasing 5:{self._c15[:12]} "c15"
|
||||
'''
|
||||
self.assertEqual(expected_stdout, stdout)
|
||||
|
||||
# Get the hash of the new head created as a result of the rebase.
|
||||
new_head = self.hg(
|
||||
'log', '-r', f'successors({self._c15})', '-T', '{node}'
|
||||
)
|
||||
|
||||
# Record the pre-update inode count.
|
||||
inspector = eden_server_inspector.EdenServerInspector(self.repo.path)
|
||||
inspector.unload_inode_for_path('numbers')
|
||||
pre_update_count = inspector.get_inode_count('numbers')
|
||||
print(f'loaded inode count before `hg update`: {pre_update_count}')
|
||||
|
||||
# Verify that updating to the new head that was created as a result of
|
||||
# the rebase leaves Hg in the correct state.
|
||||
self.assertEqual(1, len(self.repo.log()), msg=(
|
||||
'At the base commit, `hg log` should have only one entry.'
|
||||
))
|
||||
self.hg('update', new_head)
|
||||
self.assertEqual(11, len(self.repo.log()), msg=(
|
||||
'The new head should include all the commits.'
|
||||
))
|
||||
|
||||
# Verify the post-update inode count.
|
||||
post_update_count = inspector.get_inode_count('numbers')
|
||||
print(f'loaded inode count after `hg update`: {post_update_count}')
|
||||
self.assertGreaterEqual(post_update_count, pre_update_count, msg=(
|
||||
'The inode count should not decrease due to `hg update`.'
|
||||
))
|
||||
num_new_inodes = post_update_count - pre_update_count
|
||||
self.assertLessEqual(num_new_inodes, 2, msg=(
|
||||
'There should be no more than 2 new inodes as a result of the '
|
||||
'update. At the time this test was created, num_new_inodes is 0, '
|
||||
'but if we included unloaded inodes, there would be 2: one for '
|
||||
'numbers/1 and one for numbers/2.'
|
||||
))
|
54
eden/integration/lib/eden_server_inspector.py
Normal file
54
eden/integration/lib/eden_server_inspector.py
Normal file
@ -0,0 +1,54 @@
|
||||
#!/usr/bin/env python3
|
||||
#
|
||||
# Copyright (c) 2016-present, Facebook, Inc.
|
||||
# All rights reserved.
|
||||
#
|
||||
# This source code is licensed under the BSD-style license found in the
|
||||
# LICENSE file in the root directory of this source tree. An additional grant
|
||||
# of patent rights can be found in the PATENTS file in the same directory.
|
||||
|
||||
import eden.thrift
|
||||
from typing import Iterable
|
||||
|
||||
'''Utilities for inspecting the state of the Eden server via Thrift.
|
||||
|
||||
This utility is parameterized by a specific mount point so that it need not be
|
||||
specified for each instance method.
|
||||
'''
|
||||
|
||||
|
||||
class EdenServerInspector(object):
|
||||
def __init__(self, mount_point: str) -> None:
|
||||
self._mount_point = mount_point
|
||||
|
||||
def create_thrift_client(self) -> eden.thrift.EdenClient:
|
||||
return eden.thrift.create_thrift_client(mounted_path=self._mount_point)
|
||||
|
||||
def unload_inode_for_path(self, path: str='') -> None:
|
||||
'''path: relative path to a directory under the mount.'''
|
||||
with self.create_thrift_client() as client:
|
||||
client.unloadInodeForPath(self._mount_point, path)
|
||||
|
||||
def get_inode_count(self, path: str='') -> int:
|
||||
'''path: relative path to a directory under the mount.
|
||||
|
||||
Use '' for the root. Note that this will include the inode count for
|
||||
the root .hg and .eden entries.
|
||||
'''
|
||||
with self.create_thrift_client() as client:
|
||||
debug_info = client.debugInodeStatus(self._mount_point, path)
|
||||
count = 0
|
||||
for tree_inode_debug_info in debug_info:
|
||||
count += sum(1 for entry in tree_inode_debug_info.entries
|
||||
if entry.loaded)
|
||||
return count
|
||||
|
||||
def get_paths_for_inodes(self, path: str='') -> Iterable[str]:
|
||||
'''path: relative path to a directory under the mount.'''
|
||||
with self.create_thrift_client() as client:
|
||||
debug_info = client.debugInodeStatus(self._mount_point, path)
|
||||
for tree_inode_debug_info in debug_info:
|
||||
parent_dir = tree_inode_debug_info.path.decode('utf-8')
|
||||
for entry in tree_inode_debug_info.entries:
|
||||
if entry.loaded:
|
||||
yield f'{parent_dir}/{entry.name.decode("utf-8")}'
|
Loading…
Reference in New Issue
Block a user