1920 lines
78 KiB
Python
1920 lines
78 KiB
Python
#!/usr/bin/env python
|
|
|
|
"""Unit test driver for checkout_externals
|
|
|
|
Note: this script assume the path to the manic and
|
|
checkout_externals module is already in the python path. This is
|
|
usually handled by the makefile. If you call it directly, you may need
|
|
to adjust your path.
|
|
|
|
NOTE(bja, 2017-11) If a test fails, we want to keep the repo for that
|
|
test. But the tests will keep running, so we need a unique name. Also,
|
|
tearDown is always called after each test. I haven't figured out how
|
|
to determine if an assertion failed and whether it is safe to clean up
|
|
the test repos.
|
|
|
|
So the solution is:
|
|
|
|
* assign a unique id to each test repo.
|
|
|
|
* never cleanup during the run.
|
|
|
|
* Erase any existing repos at the begining of the module in
|
|
setUpModule.
|
|
|
|
"""
|
|
|
|
# NOTE(bja, 2017-11) pylint complains that the module is too big, but
|
|
# I'm still working on how to break up the tests and still have the
|
|
# temporary directory be preserved....
|
|
# pylint: disable=too-many-lines
|
|
|
|
|
|
from __future__ import absolute_import
|
|
from __future__ import unicode_literals
|
|
from __future__ import print_function
|
|
|
|
import logging
|
|
import os
|
|
import os.path
|
|
import shutil
|
|
import unittest
|
|
|
|
from manic.externals_description import ExternalsDescription
|
|
from manic.externals_description import DESCRIPTION_SECTION, VERSION_ITEM
|
|
from manic.externals_description import git_submodule_status
|
|
from manic.externals_status import ExternalStatus
|
|
from manic.repository_git import GitRepository
|
|
from manic.utils import printlog, execute_subprocess
|
|
from manic.global_constants import LOCAL_PATH_INDICATOR, VERBOSITY_DEFAULT
|
|
from manic.global_constants import LOG_FILE_NAME
|
|
from manic import checkout
|
|
|
|
# ConfigParser was renamed in python2 to configparser. In python2,
|
|
# ConfigParser returns byte strings, str, instead of unicode. We need
|
|
# unicode to be compatible with xml and json parser and python3.
|
|
try:
|
|
# python2
|
|
from ConfigParser import SafeConfigParser as config_parser
|
|
except ImportError:
|
|
# python3
|
|
from configparser import ConfigParser as config_parser
|
|
|
|
# ---------------------------------------------------------------------
|
|
#
|
|
# Global constants
|
|
#
|
|
# ---------------------------------------------------------------------
|
|
|
|
# environment variable names
|
|
MANIC_TEST_BARE_REPO_ROOT = 'MANIC_TEST_BARE_REPO_ROOT'
|
|
MANIC_TEST_TMP_REPO_ROOT = 'MANIC_TEST_TMP_REPO_ROOT'
|
|
|
|
# directory names
|
|
TMP_REPO_DIR_NAME = 'tmp'
|
|
BARE_REPO_ROOT_NAME = 'repos'
|
|
CONTAINER_REPO_NAME = 'container.git'
|
|
MIXED_REPO_NAME = 'mixed-cont-ext.git'
|
|
SIMPLE_REPO_NAME = 'simple-ext.git'
|
|
SIMPLE_FORK_NAME = 'simple-ext-fork.git'
|
|
SIMPLE_LOCAL_ONLY_NAME = '.'
|
|
ERROR_REPO_NAME = 'error'
|
|
EXTERNALS_NAME = 'externals'
|
|
SUB_EXTERNALS_PATH = 'src'
|
|
CFG_NAME = 'externals.cfg'
|
|
CFG_SUB_NAME = 'sub-externals.cfg'
|
|
README_NAME = 'readme.txt'
|
|
REMOTE_BRANCH_FEATURE2 = 'feature2'
|
|
|
|
SVN_TEST_REPO = 'https://github.com/escomp/cesm'
|
|
|
|
# Disable too-many-public-methods error
|
|
# pylint: disable=R0904
|
|
|
|
def setUpModule(): # pylint: disable=C0103
|
|
"""Setup for all tests in this module. It is called once per module!
|
|
"""
|
|
logging.basicConfig(filename=LOG_FILE_NAME,
|
|
format='%(levelname)s : %(asctime)s : %(message)s',
|
|
datefmt='%Y-%m-%d %H:%M:%S',
|
|
level=logging.DEBUG)
|
|
repo_root = os.path.join(os.getcwd(), TMP_REPO_DIR_NAME)
|
|
repo_root = os.path.abspath(repo_root)
|
|
# delete if it exists from previous runs
|
|
try:
|
|
shutil.rmtree(repo_root)
|
|
except BaseException:
|
|
pass
|
|
# create clean dir for this run
|
|
os.mkdir(repo_root)
|
|
# set into the environment so var will be expanded in externals
|
|
# filess when executables are run
|
|
os.environ[MANIC_TEST_TMP_REPO_ROOT] = repo_root
|
|
|
|
|
|
class GenerateExternalsDescriptionCfgV1(object):
|
|
"""Class to provide building blocks to create
|
|
ExternalsDescriptionCfgV1 files.
|
|
|
|
Includes predefined files used in tests.
|
|
|
|
"""
|
|
|
|
def __init__(self):
|
|
self._schema_version = '1.1.0'
|
|
self._config = None
|
|
|
|
def container_full(self, dest_dir):
|
|
"""Create the full container config file with simple and mixed use
|
|
externals
|
|
|
|
"""
|
|
self.create_config()
|
|
self.create_section(SIMPLE_REPO_NAME, 'simp_tag',
|
|
tag='tag1')
|
|
|
|
self.create_section(SIMPLE_REPO_NAME, 'simp_branch',
|
|
branch=REMOTE_BRANCH_FEATURE2)
|
|
|
|
self.create_section(SIMPLE_REPO_NAME, 'simp_opt',
|
|
tag='tag1', required=False)
|
|
|
|
self.create_section(MIXED_REPO_NAME, 'mixed_req',
|
|
branch='master', externals=CFG_SUB_NAME)
|
|
|
|
self.write_config(dest_dir)
|
|
|
|
def container_simple_required(self, dest_dir):
|
|
"""Create a container externals file with only simple externals.
|
|
|
|
"""
|
|
self.create_config()
|
|
self.create_section(SIMPLE_REPO_NAME, 'simp_tag',
|
|
tag='tag1')
|
|
|
|
self.create_section(SIMPLE_REPO_NAME, 'simp_branch',
|
|
branch=REMOTE_BRANCH_FEATURE2)
|
|
|
|
self.create_section(SIMPLE_REPO_NAME, 'simp_hash',
|
|
ref_hash='60b1cc1a38d63')
|
|
|
|
self.write_config(dest_dir)
|
|
|
|
def container_simple_optional(self, dest_dir):
|
|
"""Create a container externals file with optional simple externals
|
|
|
|
"""
|
|
self.create_config()
|
|
self.create_section(SIMPLE_REPO_NAME, 'simp_req',
|
|
tag='tag1')
|
|
|
|
self.create_section(SIMPLE_REPO_NAME, 'simp_opt',
|
|
tag='tag1', required=False)
|
|
|
|
self.write_config(dest_dir)
|
|
|
|
def container_simple_svn(self, dest_dir):
|
|
"""Create a container externals file with only simple externals.
|
|
|
|
"""
|
|
self.create_config()
|
|
self.create_section(SIMPLE_REPO_NAME, 'simp_tag', tag='tag1')
|
|
|
|
self.create_svn_external('svn_branch', branch='trunk')
|
|
self.create_svn_external('svn_tag', tag='tags/cesm2.0.beta07')
|
|
|
|
self.write_config(dest_dir)
|
|
|
|
def container_sparse(self, dest_dir):
|
|
"""Create a container with a full external and a sparse external
|
|
|
|
"""
|
|
# Create a file for a sparse pattern match
|
|
sparse_filename = 'sparse_checkout'
|
|
with open(os.path.join(dest_dir, sparse_filename), 'w') as sfile:
|
|
sfile.write('readme.txt')
|
|
|
|
self.create_config()
|
|
self.create_section(SIMPLE_REPO_NAME, 'simp_tag',
|
|
tag='tag2')
|
|
|
|
sparse_relpath = '../../{}'.format(sparse_filename)
|
|
self.create_section(SIMPLE_REPO_NAME, 'simp_sparse',
|
|
tag='tag2', sparse=sparse_relpath)
|
|
|
|
self.write_config(dest_dir)
|
|
|
|
def mixed_simple_base(self, dest_dir):
|
|
"""Create a mixed-use base externals file with only simple externals.
|
|
|
|
"""
|
|
self.create_config()
|
|
self.create_section_ext_only('mixed_base')
|
|
self.create_section(SIMPLE_REPO_NAME, 'simp_tag',
|
|
tag='tag1')
|
|
|
|
self.create_section(SIMPLE_REPO_NAME, 'simp_branch',
|
|
branch=REMOTE_BRANCH_FEATURE2)
|
|
|
|
self.create_section(SIMPLE_REPO_NAME, 'simp_hash',
|
|
ref_hash='60b1cc1a38d63')
|
|
|
|
self.write_config(dest_dir)
|
|
|
|
def mixed_simple_sub(self, dest_dir):
|
|
"""Create a mixed-use sub externals file with only simple externals.
|
|
|
|
"""
|
|
self.create_config()
|
|
self.create_section(SIMPLE_REPO_NAME, 'simp_tag',
|
|
tag='tag1', path=SUB_EXTERNALS_PATH)
|
|
|
|
self.create_section(SIMPLE_REPO_NAME, 'simp_branch',
|
|
branch=REMOTE_BRANCH_FEATURE2,
|
|
path=SUB_EXTERNALS_PATH)
|
|
|
|
self.write_config(dest_dir, filename=CFG_SUB_NAME)
|
|
|
|
def write_config(self, dest_dir, filename=CFG_NAME):
|
|
"""Write the configuration file to disk
|
|
|
|
"""
|
|
dest_path = os.path.join(dest_dir, filename)
|
|
with open(dest_path, 'w') as configfile:
|
|
self._config.write(configfile)
|
|
|
|
def create_config(self):
|
|
"""Create an config object and add the required metadata section
|
|
|
|
"""
|
|
self._config = config_parser()
|
|
self.create_metadata()
|
|
|
|
def create_metadata(self):
|
|
"""Create the metadata section of the config file
|
|
"""
|
|
self._config.add_section(DESCRIPTION_SECTION)
|
|
|
|
self._config.set(DESCRIPTION_SECTION, VERSION_ITEM,
|
|
self._schema_version)
|
|
|
|
def create_section(self, repo_type, name, tag='', branch='',
|
|
ref_hash='', required=True, path=EXTERNALS_NAME,
|
|
externals='', repo_path=None, from_submodule=False,
|
|
sparse=''):
|
|
# pylint: disable=too-many-branches
|
|
"""Create a config section with autofilling some items and handling
|
|
optional items.
|
|
|
|
"""
|
|
# pylint: disable=R0913
|
|
self._config.add_section(name)
|
|
if not from_submodule:
|
|
self._config.set(name, ExternalsDescription.PATH,
|
|
os.path.join(path, name))
|
|
|
|
self._config.set(name, ExternalsDescription.PROTOCOL,
|
|
ExternalsDescription.PROTOCOL_GIT)
|
|
|
|
# from_submodules is incompatible with some other options, turn them off
|
|
if (from_submodule and
|
|
((repo_path is not None) or tag or ref_hash or branch)):
|
|
printlog('create_section: "from_submodule" is incompatible with '
|
|
'"repo_url", "tag", "hash", and "branch" options;\n'
|
|
'Ignoring those options for {}'.format(name))
|
|
repo_url = None
|
|
tag = ''
|
|
ref_hash = ''
|
|
branch = ''
|
|
|
|
if repo_path is not None:
|
|
repo_url = repo_path
|
|
else:
|
|
repo_url = os.path.join('${MANIC_TEST_BARE_REPO_ROOT}', repo_type)
|
|
|
|
if not from_submodule:
|
|
self._config.set(name, ExternalsDescription.REPO_URL, repo_url)
|
|
|
|
self._config.set(name, ExternalsDescription.REQUIRED, str(required))
|
|
|
|
if tag:
|
|
self._config.set(name, ExternalsDescription.TAG, tag)
|
|
|
|
if branch:
|
|
self._config.set(name, ExternalsDescription.BRANCH, branch)
|
|
|
|
if ref_hash:
|
|
self._config.set(name, ExternalsDescription.HASH, ref_hash)
|
|
|
|
if externals:
|
|
self._config.set(name, ExternalsDescription.EXTERNALS, externals)
|
|
|
|
if sparse:
|
|
self._config.set(name, ExternalsDescription.SPARSE, sparse)
|
|
|
|
if from_submodule:
|
|
self._config.set(name, ExternalsDescription.SUBMODULE, "True")
|
|
|
|
def create_section_ext_only(self, name,
|
|
required=True, externals=CFG_SUB_NAME):
|
|
"""Create a config section with autofilling some items and handling
|
|
optional items.
|
|
|
|
"""
|
|
# pylint: disable=R0913
|
|
self._config.add_section(name)
|
|
self._config.set(name, ExternalsDescription.PATH, LOCAL_PATH_INDICATOR)
|
|
|
|
self._config.set(name, ExternalsDescription.PROTOCOL,
|
|
ExternalsDescription.PROTOCOL_EXTERNALS_ONLY)
|
|
|
|
self._config.set(name, ExternalsDescription.REPO_URL,
|
|
LOCAL_PATH_INDICATOR)
|
|
|
|
self._config.set(name, ExternalsDescription.REQUIRED, str(required))
|
|
|
|
if externals:
|
|
self._config.set(name, ExternalsDescription.EXTERNALS, externals)
|
|
|
|
def create_svn_external(self, name, tag='', branch=''):
|
|
"""Create a config section for an svn repository.
|
|
|
|
"""
|
|
self._config.add_section(name)
|
|
self._config.set(name, ExternalsDescription.PATH,
|
|
os.path.join(EXTERNALS_NAME, name))
|
|
|
|
self._config.set(name, ExternalsDescription.PROTOCOL,
|
|
ExternalsDescription.PROTOCOL_SVN)
|
|
|
|
self._config.set(name, ExternalsDescription.REPO_URL, SVN_TEST_REPO)
|
|
|
|
self._config.set(name, ExternalsDescription.REQUIRED, str(True))
|
|
|
|
if tag:
|
|
self._config.set(name, ExternalsDescription.TAG, tag)
|
|
|
|
if branch:
|
|
self._config.set(name, ExternalsDescription.BRANCH, branch)
|
|
|
|
@staticmethod
|
|
def create_branch(dest_dir, repo_name, branch, with_commit=False):
|
|
"""Update a repository branch, and potentially the remote.
|
|
"""
|
|
# pylint: disable=R0913
|
|
cwd = os.getcwd()
|
|
repo_root = os.path.join(dest_dir, EXTERNALS_NAME)
|
|
repo_root = os.path.join(repo_root, repo_name)
|
|
os.chdir(repo_root)
|
|
cmd = ['git', 'checkout', '-b', branch, ]
|
|
execute_subprocess(cmd)
|
|
if with_commit:
|
|
msg = 'start work on {0}'.format(branch)
|
|
with open(README_NAME, 'a') as handle:
|
|
handle.write(msg)
|
|
cmd = ['git', 'add', README_NAME, ]
|
|
execute_subprocess(cmd)
|
|
cmd = ['git', 'commit', '-m', msg, ]
|
|
execute_subprocess(cmd)
|
|
os.chdir(cwd)
|
|
|
|
@staticmethod
|
|
def create_commit(dest_dir, repo_name, local_tracking_branch=None):
|
|
"""Make a commit on whatever is currently checked out.
|
|
|
|
This is used to test sync state changes from local commits on
|
|
detached heads and tracking branches.
|
|
|
|
"""
|
|
cwd = os.getcwd()
|
|
repo_root = os.path.join(dest_dir, EXTERNALS_NAME)
|
|
repo_root = os.path.join(repo_root, repo_name)
|
|
os.chdir(repo_root)
|
|
if local_tracking_branch:
|
|
cmd = ['git', 'checkout', '-b', local_tracking_branch, ]
|
|
execute_subprocess(cmd)
|
|
|
|
msg = 'work on great new feature!'
|
|
with open(README_NAME, 'a') as handle:
|
|
handle.write(msg)
|
|
cmd = ['git', 'add', README_NAME, ]
|
|
execute_subprocess(cmd)
|
|
cmd = ['git', 'commit', '-m', msg, ]
|
|
execute_subprocess(cmd)
|
|
os.chdir(cwd)
|
|
|
|
def update_branch(self, dest_dir, name, branch, repo_type=None,
|
|
filename=CFG_NAME):
|
|
"""Update a repository branch, and potentially the remote.
|
|
"""
|
|
# pylint: disable=R0913
|
|
self._config.set(name, ExternalsDescription.BRANCH, branch)
|
|
|
|
if repo_type:
|
|
if repo_type == SIMPLE_LOCAL_ONLY_NAME:
|
|
repo_url = SIMPLE_LOCAL_ONLY_NAME
|
|
else:
|
|
repo_url = os.path.join('${MANIC_TEST_BARE_REPO_ROOT}',
|
|
repo_type)
|
|
self._config.set(name, ExternalsDescription.REPO_URL, repo_url)
|
|
|
|
try:
|
|
# remove the tag if it existed
|
|
self._config.remove_option(name, ExternalsDescription.TAG)
|
|
except BaseException:
|
|
pass
|
|
|
|
self.write_config(dest_dir, filename)
|
|
|
|
def update_svn_branch(self, dest_dir, name, branch, filename=CFG_NAME):
|
|
"""Update a repository branch, and potentially the remote.
|
|
"""
|
|
# pylint: disable=R0913
|
|
self._config.set(name, ExternalsDescription.BRANCH, branch)
|
|
|
|
try:
|
|
# remove the tag if it existed
|
|
self._config.remove_option(name, ExternalsDescription.TAG)
|
|
except BaseException:
|
|
pass
|
|
|
|
self.write_config(dest_dir, filename)
|
|
|
|
def update_tag(self, dest_dir, name, tag, repo_type=None,
|
|
filename=CFG_NAME, remove_branch=True):
|
|
"""Update a repository tag, and potentially the remote
|
|
|
|
NOTE(bja, 2017-11) remove_branch=False should result in an
|
|
overspecified external with both a branch and tag. This is
|
|
used for error condition testing.
|
|
|
|
"""
|
|
# pylint: disable=R0913
|
|
self._config.set(name, ExternalsDescription.TAG, tag)
|
|
|
|
if repo_type:
|
|
repo_url = os.path.join('${MANIC_TEST_BARE_REPO_ROOT}', repo_type)
|
|
self._config.set(name, ExternalsDescription.REPO_URL, repo_url)
|
|
|
|
try:
|
|
# remove the branch if it existed
|
|
if remove_branch:
|
|
self._config.remove_option(name, ExternalsDescription.BRANCH)
|
|
except BaseException:
|
|
pass
|
|
|
|
self.write_config(dest_dir, filename)
|
|
|
|
def update_underspecify_branch_tag(self, dest_dir, name,
|
|
filename=CFG_NAME):
|
|
"""Update a repository protocol, and potentially the remote
|
|
"""
|
|
# pylint: disable=R0913
|
|
try:
|
|
# remove the branch if it existed
|
|
self._config.remove_option(name, ExternalsDescription.BRANCH)
|
|
except BaseException:
|
|
pass
|
|
|
|
try:
|
|
# remove the tag if it existed
|
|
self._config.remove_option(name, ExternalsDescription.TAG)
|
|
except BaseException:
|
|
pass
|
|
|
|
self.write_config(dest_dir, filename)
|
|
|
|
def update_underspecify_remove_url(self, dest_dir, name,
|
|
filename=CFG_NAME):
|
|
"""Update a repository protocol, and potentially the remote
|
|
"""
|
|
# pylint: disable=R0913
|
|
try:
|
|
# remove the repo url if it existed
|
|
self._config.remove_option(name, ExternalsDescription.REPO_URL)
|
|
except BaseException:
|
|
pass
|
|
|
|
self.write_config(dest_dir, filename)
|
|
|
|
def update_protocol(self, dest_dir, name, protocol, repo_type=None,
|
|
filename=CFG_NAME):
|
|
"""Update a repository protocol, and potentially the remote
|
|
"""
|
|
# pylint: disable=R0913
|
|
self._config.set(name, ExternalsDescription.PROTOCOL, protocol)
|
|
|
|
if repo_type:
|
|
repo_url = os.path.join('${MANIC_TEST_BARE_REPO_ROOT}', repo_type)
|
|
self._config.set(name, ExternalsDescription.REPO_URL, repo_url)
|
|
|
|
self.write_config(dest_dir, filename)
|
|
|
|
|
|
class BaseTestSysCheckout(unittest.TestCase):
|
|
"""Base class of reusable systems level test setup for
|
|
checkout_externals
|
|
|
|
"""
|
|
# NOTE(bja, 2017-11) pylint complains about long method names, but
|
|
# it is hard to differentiate tests without making them more
|
|
# cryptic.
|
|
# pylint: disable=invalid-name
|
|
|
|
status_args = ['--status']
|
|
checkout_args = []
|
|
optional_args = ['--optional']
|
|
verbose_args = ['--status', '--verbose']
|
|
|
|
def setUp(self):
|
|
"""Setup for all individual checkout_externals tests
|
|
"""
|
|
# directory we want to return to after the test system and
|
|
# checkout_externals are done cd'ing all over the place.
|
|
self._return_dir = os.getcwd()
|
|
|
|
self._test_id = self.id().split('.')[-1]
|
|
|
|
# path to the executable
|
|
self._checkout = os.path.join('../checkout_externals')
|
|
self._checkout = os.path.abspath(self._checkout)
|
|
|
|
# directory where we have test repositories
|
|
self._bare_root = os.path.join(os.getcwd(), BARE_REPO_ROOT_NAME)
|
|
self._bare_root = os.path.abspath(self._bare_root)
|
|
|
|
# set into the environment so var will be expanded in externals files
|
|
os.environ[MANIC_TEST_BARE_REPO_ROOT] = self._bare_root
|
|
|
|
# set the input file generator
|
|
self._generator = GenerateExternalsDescriptionCfgV1()
|
|
# set the input file generator for secondary externals
|
|
self._sub_generator = GenerateExternalsDescriptionCfgV1()
|
|
|
|
def tearDown(self):
|
|
"""Tear down for individual tests
|
|
"""
|
|
# remove the env var we added in setup
|
|
del os.environ[MANIC_TEST_BARE_REPO_ROOT]
|
|
|
|
# return to our common starting point
|
|
os.chdir(self._return_dir)
|
|
|
|
def setup_test_repo(self, parent_repo_name, dest_dir_in=None):
|
|
"""Setup the paths and clone the base test repo
|
|
|
|
"""
|
|
# unique repo for this test
|
|
test_dir_name = self._test_id
|
|
print("Test repository name: {0}".format(test_dir_name))
|
|
|
|
parent_repo_dir = os.path.join(self._bare_root, parent_repo_name)
|
|
if dest_dir_in is None:
|
|
dest_dir = os.path.join(os.environ[MANIC_TEST_TMP_REPO_ROOT],
|
|
test_dir_name)
|
|
else:
|
|
dest_dir = dest_dir_in
|
|
|
|
# pylint: disable=W0212
|
|
GitRepository._git_clone(parent_repo_dir, dest_dir, VERBOSITY_DEFAULT)
|
|
return dest_dir
|
|
|
|
@staticmethod
|
|
def _add_file_to_repo(under_test_dir, filename, tracked):
|
|
"""Add a file to the repository so we can put it into a dirty state
|
|
|
|
"""
|
|
cwd = os.getcwd()
|
|
os.chdir(under_test_dir)
|
|
with open(filename, 'w') as tmp:
|
|
tmp.write('Hello, world!')
|
|
|
|
if tracked:
|
|
# NOTE(bja, 2018-01) brittle hack to obtain repo dir and
|
|
# file name
|
|
path_data = filename.split('/')
|
|
repo_dir = os.path.join(path_data[0], path_data[1])
|
|
os.chdir(repo_dir)
|
|
tracked_file = path_data[2]
|
|
cmd = ['git', 'add', tracked_file]
|
|
execute_subprocess(cmd)
|
|
|
|
os.chdir(cwd)
|
|
|
|
@staticmethod
|
|
def execute_cmd_in_dir(under_test_dir, args):
|
|
"""Extecute the checkout command in the appropriate repo dir with the
|
|
specified additional args
|
|
|
|
Note that we are calling the command line processing and main
|
|
routines and not using a subprocess call so that we get code
|
|
coverage results!
|
|
|
|
"""
|
|
cwd = os.getcwd()
|
|
checkout_path = os.path.abspath('{0}/../../checkout_externals')
|
|
os.chdir(under_test_dir)
|
|
cmdline = ['--externals', CFG_NAME, ]
|
|
cmdline += args
|
|
repo_root = 'MANIC_TEST_BARE_REPO_ROOT={root}'.format(
|
|
root=os.environ[MANIC_TEST_BARE_REPO_ROOT])
|
|
manual_cmd = ('Test cmd:\npushd {cwd}; {env} {checkout} {args}'.format(
|
|
cwd=under_test_dir, env=repo_root, checkout=checkout_path,
|
|
args=' '.join(cmdline)))
|
|
printlog(manual_cmd)
|
|
options = checkout.commandline_arguments(cmdline)
|
|
overall_status, tree_status = checkout.main(options)
|
|
os.chdir(cwd)
|
|
return overall_status, tree_status
|
|
|
|
# ----------------------------------------------------------------
|
|
#
|
|
# Check results for generic perturbation of states
|
|
#
|
|
# ----------------------------------------------------------------
|
|
def _check_generic_empty_default_required(self, tree, name):
|
|
self.assertEqual(tree[name].sync_state, ExternalStatus.EMPTY)
|
|
self.assertEqual(tree[name].clean_state, ExternalStatus.DEFAULT)
|
|
self.assertEqual(tree[name].source_type, ExternalStatus.MANAGED)
|
|
|
|
def _check_generic_ok_clean_required(self, tree, name):
|
|
self.assertEqual(tree[name].sync_state, ExternalStatus.STATUS_OK)
|
|
self.assertEqual(tree[name].clean_state, ExternalStatus.STATUS_OK)
|
|
self.assertEqual(tree[name].source_type, ExternalStatus.MANAGED)
|
|
|
|
def _check_generic_ok_dirty_required(self, tree, name):
|
|
self.assertEqual(tree[name].sync_state, ExternalStatus.STATUS_OK)
|
|
self.assertEqual(tree[name].clean_state, ExternalStatus.DIRTY)
|
|
self.assertEqual(tree[name].source_type, ExternalStatus.MANAGED)
|
|
|
|
def _check_generic_modified_ok_required(self, tree, name):
|
|
self.assertEqual(tree[name].sync_state, ExternalStatus.MODEL_MODIFIED)
|
|
self.assertEqual(tree[name].clean_state, ExternalStatus.STATUS_OK)
|
|
self.assertEqual(tree[name].source_type, ExternalStatus.MANAGED)
|
|
|
|
def _check_generic_empty_default_optional(self, tree, name):
|
|
self.assertEqual(tree[name].sync_state, ExternalStatus.EMPTY)
|
|
self.assertEqual(tree[name].clean_state, ExternalStatus.DEFAULT)
|
|
self.assertEqual(tree[name].source_type, ExternalStatus.OPTIONAL)
|
|
|
|
def _check_generic_ok_clean_optional(self, tree, name):
|
|
self.assertEqual(tree[name].sync_state, ExternalStatus.STATUS_OK)
|
|
self.assertEqual(tree[name].clean_state, ExternalStatus.STATUS_OK)
|
|
self.assertEqual(tree[name].source_type, ExternalStatus.OPTIONAL)
|
|
|
|
# ----------------------------------------------------------------
|
|
#
|
|
# Check results for individual named externals
|
|
#
|
|
# ----------------------------------------------------------------
|
|
def _check_simple_tag_empty(self, tree, directory=EXTERNALS_NAME):
|
|
name = './{0}/simp_tag'.format(directory)
|
|
self._check_generic_empty_default_required(tree, name)
|
|
|
|
def _check_simple_tag_ok(self, tree, directory=EXTERNALS_NAME):
|
|
name = './{0}/simp_tag'.format(directory)
|
|
self._check_generic_ok_clean_required(tree, name)
|
|
|
|
def _check_simple_tag_dirty(self, tree, directory=EXTERNALS_NAME):
|
|
name = './{0}/simp_tag'.format(directory)
|
|
self._check_generic_ok_dirty_required(tree, name)
|
|
|
|
def _check_simple_tag_modified(self, tree, directory=EXTERNALS_NAME):
|
|
name = './{0}/simp_tag'.format(directory)
|
|
self._check_generic_modified_ok_required(tree, name)
|
|
|
|
def _check_simple_branch_empty(self, tree, directory=EXTERNALS_NAME):
|
|
name = './{0}/simp_branch'.format(directory)
|
|
self._check_generic_empty_default_required(tree, name)
|
|
|
|
def _check_simple_branch_ok(self, tree, directory=EXTERNALS_NAME):
|
|
name = './{0}/simp_branch'.format(directory)
|
|
self._check_generic_ok_clean_required(tree, name)
|
|
|
|
def _check_simple_branch_modified(self, tree, directory=EXTERNALS_NAME):
|
|
name = './{0}/simp_branch'.format(directory)
|
|
self._check_generic_modified_ok_required(tree, name)
|
|
|
|
def _check_simple_hash_empty(self, tree, directory=EXTERNALS_NAME):
|
|
name = './{0}/simp_hash'.format(directory)
|
|
self._check_generic_empty_default_required(tree, name)
|
|
|
|
def _check_simple_hash_ok(self, tree, directory=EXTERNALS_NAME):
|
|
name = './{0}/simp_hash'.format(directory)
|
|
self._check_generic_ok_clean_required(tree, name)
|
|
|
|
def _check_simple_hash_modified(self, tree, directory=EXTERNALS_NAME):
|
|
name = './{0}/simp_hash'.format(directory)
|
|
self._check_generic_modified_ok_required(tree, name)
|
|
|
|
def _check_simple_req_empty(self, tree, directory=EXTERNALS_NAME):
|
|
name = './{0}/simp_req'.format(directory)
|
|
self._check_generic_empty_default_required(tree, name)
|
|
|
|
def _check_simple_req_ok(self, tree, directory=EXTERNALS_NAME):
|
|
name = './{0}/simp_req'.format(directory)
|
|
self._check_generic_ok_clean_required(tree, name)
|
|
|
|
def _check_simple_opt_empty(self, tree, directory=EXTERNALS_NAME):
|
|
name = './{0}/simp_opt'.format(directory)
|
|
self._check_generic_empty_default_optional(tree, name)
|
|
|
|
def _check_simple_opt_ok(self, tree, directory=EXTERNALS_NAME):
|
|
name = './{0}/simp_opt'.format(directory)
|
|
self._check_generic_ok_clean_optional(tree, name)
|
|
|
|
def _check_mixed_ext_branch_empty(self, tree, directory=EXTERNALS_NAME):
|
|
name = './{0}/mixed_req'.format(directory)
|
|
self._check_generic_empty_default_required(tree, name)
|
|
|
|
def _check_mixed_ext_branch_ok(self, tree, directory=EXTERNALS_NAME):
|
|
name = './{0}/mixed_req'.format(directory)
|
|
self._check_generic_ok_clean_required(tree, name)
|
|
|
|
def _check_mixed_ext_branch_modified(self, tree, directory=EXTERNALS_NAME):
|
|
name = './{0}/mixed_req'.format(directory)
|
|
self._check_generic_modified_ok_required(tree, name)
|
|
|
|
def _check_simple_sparse_empty(self, tree, directory=EXTERNALS_NAME):
|
|
name = './{0}/simp_sparse'.format(directory)
|
|
self._check_generic_empty_default_required(tree, name)
|
|
|
|
def _check_simple_sparse_ok(self, tree, directory=EXTERNALS_NAME):
|
|
name = './{0}/simp_sparse'.format(directory)
|
|
self._check_generic_ok_clean_required(tree, name)
|
|
|
|
# ----------------------------------------------------------------
|
|
#
|
|
# Check results for groups of externals under specific conditions
|
|
#
|
|
# ----------------------------------------------------------------
|
|
def _check_container_simple_required_pre_checkout(self, overall, tree):
|
|
self.assertEqual(overall, 0)
|
|
self._check_simple_tag_empty(tree)
|
|
self._check_simple_branch_empty(tree)
|
|
self._check_simple_hash_empty(tree)
|
|
|
|
def _check_container_simple_required_checkout(self, overall, tree):
|
|
# Note, this is the internal tree status just before checkout
|
|
self.assertEqual(overall, 0)
|
|
self._check_simple_tag_empty(tree)
|
|
self._check_simple_branch_empty(tree)
|
|
self._check_simple_hash_empty(tree)
|
|
|
|
def _check_container_simple_required_post_checkout(self, overall, tree):
|
|
self.assertEqual(overall, 0)
|
|
self._check_simple_tag_ok(tree)
|
|
self._check_simple_branch_ok(tree)
|
|
self._check_simple_hash_ok(tree)
|
|
|
|
def _check_container_simple_required_out_of_sync(self, overall, tree):
|
|
self.assertEqual(overall, 0)
|
|
self._check_simple_tag_modified(tree)
|
|
self._check_simple_branch_modified(tree)
|
|
self._check_simple_hash_modified(tree)
|
|
|
|
def _check_container_simple_optional_pre_checkout(self, overall, tree):
|
|
self.assertEqual(overall, 0)
|
|
self._check_simple_req_empty(tree)
|
|
self._check_simple_opt_empty(tree)
|
|
|
|
def _check_container_simple_optional_checkout(self, overall, tree):
|
|
self.assertEqual(overall, 0)
|
|
self._check_simple_req_empty(tree)
|
|
self._check_simple_opt_empty(tree)
|
|
|
|
def _check_container_simple_optional_post_checkout(self, overall, tree):
|
|
self.assertEqual(overall, 0)
|
|
self._check_simple_req_ok(tree)
|
|
self._check_simple_opt_empty(tree)
|
|
|
|
def _check_container_simple_optional_post_optional(self, overall, tree):
|
|
self.assertEqual(overall, 0)
|
|
self._check_simple_req_ok(tree)
|
|
self._check_simple_opt_ok(tree)
|
|
|
|
def _check_container_simple_required_sb_modified(self, overall, tree):
|
|
self.assertEqual(overall, 0)
|
|
self._check_simple_tag_ok(tree)
|
|
self._check_simple_branch_modified(tree)
|
|
self._check_simple_hash_ok(tree)
|
|
|
|
def _check_container_simple_optional_st_dirty(self, overall, tree):
|
|
self.assertEqual(overall, 0)
|
|
self._check_simple_tag_dirty(tree)
|
|
self._check_simple_branch_ok(tree)
|
|
|
|
def _check_container_full_pre_checkout(self, overall, tree):
|
|
self.assertEqual(overall, 0)
|
|
self._check_simple_tag_empty(tree)
|
|
self._check_simple_branch_empty(tree)
|
|
self._check_simple_opt_empty(tree)
|
|
self._check_mixed_ext_branch_required_pre_checkout(overall, tree)
|
|
|
|
def _check_container_component_post_checkout(self, overall, tree):
|
|
self.assertEqual(overall, 0)
|
|
self._check_simple_opt_ok(tree)
|
|
self._check_simple_tag_empty(tree)
|
|
self._check_simple_branch_empty(tree)
|
|
|
|
def _check_container_component_post_checkout2(self, overall, tree):
|
|
self.assertEqual(overall, 0)
|
|
self._check_simple_opt_ok(tree)
|
|
self._check_simple_tag_empty(tree)
|
|
self._check_simple_branch_ok(tree)
|
|
|
|
def _check_container_full_post_checkout(self, overall, tree):
|
|
self.assertEqual(overall, 0)
|
|
self._check_simple_tag_ok(tree)
|
|
self._check_simple_branch_ok(tree)
|
|
self._check_simple_opt_empty(tree)
|
|
self._check_mixed_ext_branch_required_post_checkout(overall, tree)
|
|
|
|
def _check_container_full_pre_checkout_ext_change(self, overall, tree):
|
|
self.assertEqual(overall, 0)
|
|
self._check_simple_tag_ok(tree)
|
|
self._check_simple_branch_ok(tree)
|
|
self._check_simple_opt_empty(tree)
|
|
self._check_mixed_ext_branch_required_pre_checkout_ext_change(
|
|
overall, tree)
|
|
|
|
def _check_container_full_post_checkout_subext_modified(
|
|
self, overall, tree):
|
|
self.assertEqual(overall, 0)
|
|
self._check_simple_tag_ok(tree)
|
|
self._check_simple_branch_ok(tree)
|
|
self._check_simple_opt_empty(tree)
|
|
self._check_mixed_ext_branch_required_post_checkout_subext_modified(
|
|
overall, tree)
|
|
|
|
def _check_mixed_ext_branch_required_pre_checkout(self, overall, tree):
|
|
# Note, this is the internal tree status just before checkout
|
|
self.assertEqual(overall, 0)
|
|
self._check_mixed_ext_branch_empty(tree, directory=EXTERNALS_NAME)
|
|
# NOTE: externals/mixed_req/src should not exist in the tree
|
|
# since this is the status before checkout of mixed_req.
|
|
|
|
def _check_mixed_ext_branch_required_post_checkout(self, overall, tree):
|
|
# Note, this is the internal tree status just before checkout
|
|
self.assertEqual(overall, 0)
|
|
self._check_mixed_ext_branch_ok(tree, directory=EXTERNALS_NAME)
|
|
check_dir = "{0}/{1}/{2}".format(EXTERNALS_NAME, "mixed_req",
|
|
SUB_EXTERNALS_PATH)
|
|
self._check_simple_branch_ok(tree, directory=check_dir)
|
|
|
|
def _check_mixed_ext_branch_required_pre_checkout_ext_change(
|
|
self, overall, tree):
|
|
# Note, this is the internal tree status just after change the
|
|
# externals description file, but before checkout
|
|
self.assertEqual(overall, 0)
|
|
self._check_mixed_ext_branch_modified(tree, directory=EXTERNALS_NAME)
|
|
check_dir = "{0}/{1}/{2}".format(EXTERNALS_NAME, "mixed_req",
|
|
SUB_EXTERNALS_PATH)
|
|
self._check_simple_branch_ok(tree, directory=check_dir)
|
|
|
|
def _check_mixed_ext_branch_required_post_checkout_subext_modified(
|
|
self, overall, tree):
|
|
# Note, this is the internal tree status just after change the
|
|
# externals description file, but before checkout
|
|
self.assertEqual(overall, 0)
|
|
self._check_mixed_ext_branch_ok(tree, directory=EXTERNALS_NAME)
|
|
check_dir = "{0}/{1}/{2}".format(EXTERNALS_NAME, "mixed_req",
|
|
SUB_EXTERNALS_PATH)
|
|
self._check_simple_branch_modified(tree, directory=check_dir)
|
|
|
|
def _check_mixed_cont_simple_required_pre_checkout(self, overall, tree):
|
|
# Note, this is the internal tree status just before checkout
|
|
self.assertEqual(overall, 0)
|
|
self._check_simple_tag_empty(tree, directory=EXTERNALS_NAME)
|
|
self._check_simple_branch_empty(tree, directory=EXTERNALS_NAME)
|
|
self._check_simple_branch_empty(tree, directory=SUB_EXTERNALS_PATH)
|
|
|
|
def _check_mixed_cont_simple_required_checkout(self, overall, tree):
|
|
# Note, this is the internal tree status just before checkout
|
|
self.assertEqual(overall, 0)
|
|
self._check_simple_tag_empty(tree, directory=EXTERNALS_NAME)
|
|
self._check_simple_branch_empty(tree, directory=EXTERNALS_NAME)
|
|
self._check_simple_branch_empty(tree, directory=SUB_EXTERNALS_PATH)
|
|
|
|
def _check_mixed_cont_simple_required_post_checkout(self, overall, tree):
|
|
# Note, this is the internal tree status just before checkout
|
|
self.assertEqual(overall, 0)
|
|
self._check_simple_tag_ok(tree, directory=EXTERNALS_NAME)
|
|
self._check_simple_branch_ok(tree, directory=EXTERNALS_NAME)
|
|
self._check_simple_branch_ok(tree, directory=SUB_EXTERNALS_PATH)
|
|
|
|
def _check_container_sparse_pre_checkout(self, overall, tree):
|
|
self.assertEqual(overall, 0)
|
|
self._check_simple_tag_empty(tree)
|
|
self._check_simple_sparse_empty(tree)
|
|
|
|
def _check_container_sparse_post_checkout(self, overall, tree):
|
|
self.assertEqual(overall, 0)
|
|
self._check_simple_tag_ok(tree)
|
|
self._check_simple_sparse_ok(tree)
|
|
|
|
def _check_file_exists(self, repo_dir, pathname):
|
|
"Check that <pathname> exists in <repo_dir>"
|
|
self.assertTrue(os.path.exists(os.path.join(repo_dir, pathname)))
|
|
|
|
def _check_file_absent(self, repo_dir, pathname):
|
|
"Check that <pathname> does not exist in <repo_dir>"
|
|
self.assertFalse(os.path.exists(os.path.join(repo_dir, pathname)))
|
|
|
|
class TestSysCheckout(BaseTestSysCheckout):
|
|
"""Run systems level tests of checkout_externals
|
|
|
|
"""
|
|
# NOTE(bja, 2017-11) pylint complains about long method names, but
|
|
# it is hard to differentiate tests without making them more
|
|
# cryptic.
|
|
# pylint: disable=invalid-name
|
|
|
|
# ----------------------------------------------------------------
|
|
#
|
|
# Run systems tests
|
|
#
|
|
# ----------------------------------------------------------------
|
|
def test_container_simple_required(self):
|
|
"""Verify that a container with simple subrepos
|
|
generates the correct initial status.
|
|
|
|
"""
|
|
# create repo
|
|
under_test_dir = self.setup_test_repo(CONTAINER_REPO_NAME)
|
|
self._generator.container_simple_required(under_test_dir)
|
|
|
|
# status of empty repo
|
|
overall, tree = self.execute_cmd_in_dir(under_test_dir,
|
|
self.status_args)
|
|
self._check_container_simple_required_pre_checkout(overall, tree)
|
|
|
|
# checkout
|
|
overall, tree = self.execute_cmd_in_dir(under_test_dir,
|
|
self.checkout_args)
|
|
self._check_container_simple_required_checkout(overall, tree)
|
|
|
|
# status clean checked out
|
|
overall, tree = self.execute_cmd_in_dir(under_test_dir,
|
|
self.status_args)
|
|
self._check_container_simple_required_post_checkout(overall, tree)
|
|
|
|
def test_container_simple_optional(self):
|
|
"""Verify that container with an optional simple subrepos
|
|
generates the correct initial status.
|
|
|
|
"""
|
|
# create repo
|
|
under_test_dir = self.setup_test_repo(CONTAINER_REPO_NAME)
|
|
self._generator.container_simple_optional(under_test_dir)
|
|
|
|
# check status of empty repo
|
|
overall, tree = self.execute_cmd_in_dir(under_test_dir,
|
|
self.status_args)
|
|
self._check_container_simple_optional_pre_checkout(overall, tree)
|
|
|
|
# checkout required
|
|
overall, tree = self.execute_cmd_in_dir(under_test_dir,
|
|
self.checkout_args)
|
|
self._check_container_simple_optional_checkout(overall, tree)
|
|
|
|
# status
|
|
overall, tree = self.execute_cmd_in_dir(under_test_dir,
|
|
self.status_args)
|
|
self._check_container_simple_optional_post_checkout(overall, tree)
|
|
|
|
# checkout optional
|
|
overall, tree = self.execute_cmd_in_dir(under_test_dir,
|
|
self.optional_args)
|
|
self._check_container_simple_optional_post_checkout(overall, tree)
|
|
|
|
# status
|
|
overall, tree = self.execute_cmd_in_dir(under_test_dir,
|
|
self.status_args)
|
|
self._check_container_simple_optional_post_optional(overall, tree)
|
|
|
|
def test_container_simple_verbose(self):
|
|
"""Verify that container with simple subrepos runs with verbose status
|
|
output and generates the correct initial status.
|
|
|
|
"""
|
|
# create repo
|
|
under_test_dir = self.setup_test_repo(CONTAINER_REPO_NAME)
|
|
self._generator.container_simple_required(under_test_dir)
|
|
|
|
# checkout
|
|
overall, tree = self.execute_cmd_in_dir(under_test_dir,
|
|
self.checkout_args)
|
|
self._check_container_simple_required_checkout(overall, tree)
|
|
|
|
# check verbose status
|
|
overall, tree = self.execute_cmd_in_dir(under_test_dir,
|
|
self.verbose_args)
|
|
self._check_container_simple_required_post_checkout(overall, tree)
|
|
|
|
def test_container_simple_dirty(self):
|
|
"""Verify that a container with simple subrepos
|
|
and a dirty status exits gracefully.
|
|
|
|
"""
|
|
under_test_dir = self.setup_test_repo(CONTAINER_REPO_NAME)
|
|
self._generator.container_simple_required(under_test_dir)
|
|
|
|
# checkout
|
|
overall, tree = self.execute_cmd_in_dir(under_test_dir,
|
|
self.checkout_args)
|
|
self._check_container_simple_required_checkout(overall, tree)
|
|
|
|
# add a file to the repo
|
|
tracked = True
|
|
self._add_file_to_repo(under_test_dir, 'externals/simp_tag/tmp.txt',
|
|
tracked)
|
|
|
|
# checkout: pre-checkout status should be dirty, did not
|
|
# modify working copy.
|
|
overall, tree = self.execute_cmd_in_dir(under_test_dir,
|
|
self.checkout_args)
|
|
self._check_container_simple_optional_st_dirty(overall, tree)
|
|
|
|
# verify status is still dirty
|
|
overall, tree = self.execute_cmd_in_dir(under_test_dir,
|
|
self.status_args)
|
|
self._check_container_simple_optional_st_dirty(overall, tree)
|
|
|
|
def test_container_simple_untracked(self):
|
|
"""Verify that a container with simple subrepos and a untracked files
|
|
is not considered 'dirty' and will attempt an update.
|
|
|
|
"""
|
|
under_test_dir = self.setup_test_repo(CONTAINER_REPO_NAME)
|
|
self._generator.container_simple_required(under_test_dir)
|
|
|
|
# checkout
|
|
overall, tree = self.execute_cmd_in_dir(under_test_dir,
|
|
self.checkout_args)
|
|
self._check_container_simple_required_checkout(overall, tree)
|
|
|
|
# add a file to the repo
|
|
tracked = False
|
|
self._add_file_to_repo(under_test_dir, 'externals/simp_tag/tmp.txt',
|
|
tracked)
|
|
|
|
# checkout: pre-checkout status should be clean, ignoring the
|
|
# untracked file.
|
|
overall, tree = self.execute_cmd_in_dir(under_test_dir,
|
|
self.checkout_args)
|
|
self._check_container_simple_required_post_checkout(overall, tree)
|
|
|
|
# verify status is still clean
|
|
overall, tree = self.execute_cmd_in_dir(under_test_dir,
|
|
self.status_args)
|
|
self._check_container_simple_required_post_checkout(overall, tree)
|
|
|
|
def test_container_simple_detached_sync(self):
|
|
"""Verify that a container with simple subrepos generates the correct
|
|
out of sync status when making commits from a detached head
|
|
state.
|
|
|
|
"""
|
|
# create repo
|
|
under_test_dir = self.setup_test_repo(CONTAINER_REPO_NAME)
|
|
self._generator.container_simple_required(under_test_dir)
|
|
|
|
# status of empty repo
|
|
overall, tree = self.execute_cmd_in_dir(under_test_dir,
|
|
self.status_args)
|
|
self._check_container_simple_required_pre_checkout(overall, tree)
|
|
|
|
# checkout
|
|
overall, tree = self.execute_cmd_in_dir(under_test_dir,
|
|
self.checkout_args)
|
|
self._check_container_simple_required_checkout(overall, tree)
|
|
|
|
# make a commit on the detached head of the tag and hash externals
|
|
self._generator.create_commit(under_test_dir, 'simp_tag')
|
|
self._generator.create_commit(under_test_dir, 'simp_hash')
|
|
self._generator.create_commit(under_test_dir, 'simp_branch')
|
|
|
|
# status of repo, branch, tag and hash should all be out of sync!
|
|
overall, tree = self.execute_cmd_in_dir(under_test_dir,
|
|
self.status_args)
|
|
self._check_container_simple_required_out_of_sync(overall, tree)
|
|
|
|
# checkout
|
|
overall, tree = self.execute_cmd_in_dir(under_test_dir,
|
|
self.checkout_args)
|
|
# same pre-checkout out of sync status
|
|
self._check_container_simple_required_out_of_sync(overall, tree)
|
|
|
|
# now status should be in-sync
|
|
overall, tree = self.execute_cmd_in_dir(under_test_dir,
|
|
self.status_args)
|
|
self._check_container_simple_required_post_checkout(overall, tree)
|
|
|
|
def test_container_remote_branch(self):
|
|
"""Verify that a container with remote branch change works
|
|
|
|
"""
|
|
# create repo
|
|
under_test_dir = self.setup_test_repo(CONTAINER_REPO_NAME)
|
|
self._generator.container_simple_required(under_test_dir)
|
|
|
|
# checkout
|
|
overall, tree = self.execute_cmd_in_dir(under_test_dir,
|
|
self.checkout_args)
|
|
self._check_container_simple_required_checkout(overall, tree)
|
|
|
|
# update the config file to point to a different remote with
|
|
# the same branch
|
|
self._generator.update_branch(under_test_dir, 'simp_branch',
|
|
REMOTE_BRANCH_FEATURE2, SIMPLE_FORK_NAME)
|
|
|
|
# status of simp_branch should be out of sync
|
|
overall, tree = self.execute_cmd_in_dir(under_test_dir,
|
|
self.status_args)
|
|
self._check_container_simple_required_sb_modified(overall, tree)
|
|
|
|
# checkout new externals
|
|
overall, tree = self.execute_cmd_in_dir(under_test_dir,
|
|
self.checkout_args)
|
|
self._check_container_simple_required_sb_modified(overall, tree)
|
|
|
|
# status should be synced
|
|
overall, tree = self.execute_cmd_in_dir(under_test_dir,
|
|
self.status_args)
|
|
self._check_container_simple_required_post_checkout(overall, tree)
|
|
|
|
def test_container_remote_tag_same_branch(self):
|
|
"""Verify that a container with remote tag change works. The new tag
|
|
should not be in the original repo, only the new remote
|
|
fork. The new tag is automatically fetched because it is on
|
|
the branch.
|
|
|
|
"""
|
|
# create repo
|
|
under_test_dir = self.setup_test_repo(CONTAINER_REPO_NAME)
|
|
self._generator.container_simple_required(under_test_dir)
|
|
|
|
# checkout
|
|
overall, tree = self.execute_cmd_in_dir(under_test_dir,
|
|
self.checkout_args)
|
|
self._check_container_simple_required_checkout(overall, tree)
|
|
|
|
# update the config file to point to a different remote with
|
|
# the tag instead of branch. Tag MUST NOT be in the original
|
|
# repo!
|
|
self._generator.update_tag(under_test_dir, 'simp_branch',
|
|
'forked-feature-v1', SIMPLE_FORK_NAME)
|
|
|
|
# status of simp_branch should be out of sync
|
|
overall, tree = self.execute_cmd_in_dir(under_test_dir,
|
|
self.status_args)
|
|
self._check_container_simple_required_sb_modified(overall, tree)
|
|
|
|
# checkout new externals
|
|
overall, tree = self.execute_cmd_in_dir(under_test_dir,
|
|
self.checkout_args)
|
|
self._check_container_simple_required_sb_modified(overall, tree)
|
|
|
|
# status should be synced
|
|
overall, tree = self.execute_cmd_in_dir(under_test_dir,
|
|
self.status_args)
|
|
self._check_container_simple_required_post_checkout(overall, tree)
|
|
|
|
def test_container_remote_tag_fetch_all(self):
|
|
"""Verify that a container with remote tag change works. The new tag
|
|
should not be in the original repo, only the new remote
|
|
fork. It should also not be on a branch that will be fetch,
|
|
and therefore not fetched by default with 'git fetch'. It will
|
|
only be retreived by 'git fetch --tags'
|
|
|
|
"""
|
|
# create repo
|
|
under_test_dir = self.setup_test_repo(CONTAINER_REPO_NAME)
|
|
self._generator.container_simple_required(under_test_dir)
|
|
|
|
# checkout
|
|
overall, tree = self.execute_cmd_in_dir(under_test_dir,
|
|
self.checkout_args)
|
|
self._check_container_simple_required_checkout(overall, tree)
|
|
|
|
# update the config file to point to a different remote with
|
|
# the tag instead of branch. Tag MUST NOT be in the original
|
|
# repo!
|
|
self._generator.update_tag(under_test_dir, 'simp_branch',
|
|
'abandoned-feature', SIMPLE_FORK_NAME)
|
|
|
|
# status of simp_branch should be out of sync
|
|
overall, tree = self.execute_cmd_in_dir(under_test_dir,
|
|
self.status_args)
|
|
self._check_container_simple_required_sb_modified(overall, tree)
|
|
|
|
# checkout new externals
|
|
overall, tree = self.execute_cmd_in_dir(under_test_dir,
|
|
self.checkout_args)
|
|
self._check_container_simple_required_sb_modified(overall, tree)
|
|
|
|
# status should be synced
|
|
overall, tree = self.execute_cmd_in_dir(under_test_dir,
|
|
self.status_args)
|
|
self._check_container_simple_required_post_checkout(overall, tree)
|
|
|
|
def test_container_preserve_dot(self):
|
|
"""Verify that after inital checkout, modifying an external git repo
|
|
url to '.' and the current branch will leave it unchanged.
|
|
|
|
"""
|
|
# create repo
|
|
under_test_dir = self.setup_test_repo(CONTAINER_REPO_NAME)
|
|
self._generator.container_simple_required(under_test_dir)
|
|
|
|
# checkout
|
|
overall, tree = self.execute_cmd_in_dir(under_test_dir,
|
|
self.checkout_args)
|
|
self._check_container_simple_required_checkout(overall, tree)
|
|
|
|
# update the config file to point to a different remote with
|
|
# the same branch
|
|
self._generator.update_branch(under_test_dir, 'simp_branch',
|
|
REMOTE_BRANCH_FEATURE2, SIMPLE_FORK_NAME)
|
|
# checkout
|
|
overall, tree = self.execute_cmd_in_dir(under_test_dir,
|
|
self.checkout_args)
|
|
|
|
# verify status is clean and unmodified
|
|
overall, tree = self.execute_cmd_in_dir(under_test_dir,
|
|
self.status_args)
|
|
self._check_container_simple_required_post_checkout(overall, tree)
|
|
|
|
# update branch to point to a new branch that only exists in
|
|
# the local fork
|
|
self._generator.create_branch(under_test_dir, 'simp_branch',
|
|
'private-feature', with_commit=True)
|
|
self._generator.update_branch(under_test_dir, 'simp_branch',
|
|
'private-feature',
|
|
SIMPLE_LOCAL_ONLY_NAME)
|
|
overall, tree = self.execute_cmd_in_dir(under_test_dir,
|
|
self.checkout_args)
|
|
|
|
# verify status is clean and unmodified
|
|
overall, tree = self.execute_cmd_in_dir(under_test_dir,
|
|
self.status_args)
|
|
self._check_container_simple_required_post_checkout(overall, tree)
|
|
|
|
def test_container_full(self):
|
|
"""Verify that 'full' container with simple and mixed subrepos
|
|
generates the correct initial status.
|
|
|
|
The mixed subrepo has a sub-externals file with different
|
|
sub-externals on different branches.
|
|
|
|
"""
|
|
# create the test repository
|
|
under_test_dir = self.setup_test_repo(CONTAINER_REPO_NAME)
|
|
|
|
# create the top level externals file
|
|
self._generator.container_full(under_test_dir)
|
|
|
|
# inital checkout
|
|
overall, tree = self.execute_cmd_in_dir(under_test_dir,
|
|
self.checkout_args)
|
|
self._check_container_full_pre_checkout(overall, tree)
|
|
|
|
overall, tree = self.execute_cmd_in_dir(under_test_dir,
|
|
self.status_args)
|
|
self._check_container_full_post_checkout(overall, tree)
|
|
|
|
# Check existance of some files
|
|
subrepo_path = os.path.join('externals', 'simp_tag')
|
|
self._check_file_exists(under_test_dir,
|
|
os.path.join(subrepo_path, 'readme.txt'))
|
|
self._check_file_absent(under_test_dir, os.path.join(subrepo_path,
|
|
'simple_subdir',
|
|
'subdir_file.txt'))
|
|
|
|
# update the mixed-use repo to point to different branch
|
|
self._generator.update_branch(under_test_dir, 'mixed_req',
|
|
'new-feature', MIXED_REPO_NAME)
|
|
|
|
# check status out of sync for mixed_req, but sub-externals
|
|
# are still in sync
|
|
overall, tree = self.execute_cmd_in_dir(under_test_dir,
|
|
self.status_args)
|
|
self._check_container_full_pre_checkout_ext_change(overall, tree)
|
|
|
|
# run the checkout. Now the mixed use external and it's
|
|
# sub-exterals should be changed. Returned status is
|
|
# pre-checkout!
|
|
overall, tree = self.execute_cmd_in_dir(under_test_dir,
|
|
self.checkout_args)
|
|
self._check_container_full_pre_checkout_ext_change(overall, tree)
|
|
|
|
# check status out of sync for mixed_req, and sub-externals
|
|
# are in sync.
|
|
overall, tree = self.execute_cmd_in_dir(under_test_dir,
|
|
self.status_args)
|
|
self._check_container_full_post_checkout(overall, tree)
|
|
|
|
def test_container_component(self):
|
|
"""Verify that optional component checkout works
|
|
"""
|
|
# create the test repository
|
|
under_test_dir = self.setup_test_repo(CONTAINER_REPO_NAME)
|
|
|
|
# create the top level externals file
|
|
self._generator.container_full(under_test_dir)
|
|
|
|
# inital checkout, first try a nonexistant component argument noref
|
|
checkout_args = ['simp_opt', 'noref']
|
|
checkout_args.extend(self.checkout_args)
|
|
|
|
with self.assertRaises(RuntimeError):
|
|
self.execute_cmd_in_dir(under_test_dir, checkout_args)
|
|
|
|
checkout_args = ['simp_opt']
|
|
checkout_args.extend(self.checkout_args)
|
|
|
|
overall, tree = self.execute_cmd_in_dir(under_test_dir,
|
|
checkout_args)
|
|
|
|
overall, tree = self.execute_cmd_in_dir(under_test_dir,
|
|
self.status_args)
|
|
self._check_container_component_post_checkout(overall, tree)
|
|
checkout_args.append('simp_branch')
|
|
overall, tree = self.execute_cmd_in_dir(under_test_dir,
|
|
checkout_args)
|
|
overall, tree = self.execute_cmd_in_dir(under_test_dir,
|
|
self.status_args)
|
|
self._check_container_component_post_checkout2(overall, tree)
|
|
|
|
def test_mixed_simple(self):
|
|
"""Verify that a mixed use repo can serve as a 'full' container,
|
|
pulling in a set of externals and a seperate set of sub-externals.
|
|
|
|
"""
|
|
#import pdb; pdb.set_trace()
|
|
# create repository
|
|
under_test_dir = self.setup_test_repo(MIXED_REPO_NAME)
|
|
# create top level externals file
|
|
self._generator.mixed_simple_base(under_test_dir)
|
|
# NOTE: sub-externals file is already in the repo so we can
|
|
# switch branches during testing. Since this is a mixed-repo
|
|
# serving as the top level container repo, we can't switch
|
|
# during this test.
|
|
|
|
# checkout
|
|
overall, tree = self.execute_cmd_in_dir(under_test_dir,
|
|
self.checkout_args)
|
|
self._check_mixed_cont_simple_required_checkout(overall, tree)
|
|
|
|
# verify status is clean and unmodified
|
|
overall, tree = self.execute_cmd_in_dir(under_test_dir,
|
|
self.status_args)
|
|
self._check_mixed_cont_simple_required_post_checkout(overall, tree)
|
|
|
|
def test_container_sparse(self):
|
|
"""Verify that 'full' container with simple subrepo
|
|
can run a sparse checkout and generate the correct initial status.
|
|
|
|
"""
|
|
# create the test repository
|
|
under_test_dir = self.setup_test_repo(CONTAINER_REPO_NAME)
|
|
|
|
# create the top level externals file
|
|
self._generator.container_sparse(under_test_dir)
|
|
|
|
# inital checkout
|
|
overall, tree = self.execute_cmd_in_dir(under_test_dir,
|
|
self.checkout_args)
|
|
self._check_container_sparse_pre_checkout(overall, tree)
|
|
|
|
overall, tree = self.execute_cmd_in_dir(under_test_dir,
|
|
self.status_args)
|
|
self._check_container_sparse_post_checkout(overall, tree)
|
|
|
|
# Check existance of some files
|
|
subrepo_path = os.path.join('externals', 'simp_tag')
|
|
self._check_file_exists(under_test_dir,
|
|
os.path.join(subrepo_path, 'readme.txt'))
|
|
self._check_file_exists(under_test_dir, os.path.join(subrepo_path,
|
|
'simple_subdir',
|
|
'subdir_file.txt'))
|
|
subrepo_path = os.path.join('externals', 'simp_sparse')
|
|
self._check_file_exists(under_test_dir,
|
|
os.path.join(subrepo_path, 'readme.txt'))
|
|
self._check_file_absent(under_test_dir, os.path.join(subrepo_path,
|
|
'simple_subdir',
|
|
'subdir_file.txt'))
|
|
|
|
|
|
class TestSysCheckoutSVN(BaseTestSysCheckout):
|
|
"""Run systems level tests of checkout_externals accessing svn repositories
|
|
|
|
SVN tests - these tests use the svn repository interface. Since
|
|
they require an active network connection, they are significantly
|
|
slower than the git tests. But svn testing is critical. So try to
|
|
design the tests to only test svn repository functionality
|
|
(checkout, switch) and leave generic testing of functionality like
|
|
'optional' to the fast git tests.
|
|
|
|
Example timing as of 2017-11:
|
|
|
|
* All other git and unit tests combined take between 4-5 seconds
|
|
|
|
* Just checking if svn is available for a single test takes 2 seconds.
|
|
|
|
* The single svn test typically takes between 10 and 25 seconds
|
|
(depending on the network)!
|
|
|
|
NOTE(bja, 2017-11) To enable CI testing we can't use a real remote
|
|
repository that restricts access and it seems inappropriate to hit
|
|
a random open source repo. For now we are just hitting one of our
|
|
own github repos using the github svn server interface. This
|
|
should be "good enough" for basic checkout and swich
|
|
functionality. But if additional svn functionality is required, a
|
|
better solution will be necessary. I think eventually we want to
|
|
create a small local svn repository on the fly (doesn't require an
|
|
svn server or network connection!) and use it for testing.
|
|
|
|
"""
|
|
|
|
def _check_svn_branch_ok(self, tree, directory=EXTERNALS_NAME):
|
|
name = './{0}/svn_branch'.format(directory)
|
|
self._check_generic_ok_clean_required(tree, name)
|
|
|
|
def _check_svn_branch_dirty(self, tree, directory=EXTERNALS_NAME):
|
|
name = './{0}/svn_branch'.format(directory)
|
|
self._check_generic_ok_dirty_required(tree, name)
|
|
|
|
def _check_svn_tag_ok(self, tree, directory=EXTERNALS_NAME):
|
|
name = './{0}/svn_tag'.format(directory)
|
|
self._check_generic_ok_clean_required(tree, name)
|
|
|
|
def _check_svn_tag_modified(self, tree, directory=EXTERNALS_NAME):
|
|
name = './{0}/svn_tag'.format(directory)
|
|
self._check_generic_modified_ok_required(tree, name)
|
|
|
|
def _check_container_simple_svn_post_checkout(self, overall, tree):
|
|
self.assertEqual(overall, 0)
|
|
self._check_simple_tag_ok(tree)
|
|
self._check_svn_branch_ok(tree)
|
|
self._check_svn_tag_ok(tree)
|
|
|
|
def _check_container_simple_svn_sb_dirty_st_mod(self, overall, tree):
|
|
self.assertEqual(overall, 0)
|
|
self._check_simple_tag_ok(tree)
|
|
self._check_svn_tag_modified(tree)
|
|
self._check_svn_branch_dirty(tree)
|
|
|
|
def _check_container_simple_svn_sb_clean_st_mod(self, overall, tree):
|
|
self.assertEqual(overall, 0)
|
|
self._check_simple_tag_ok(tree)
|
|
self._check_svn_tag_modified(tree)
|
|
self._check_svn_branch_ok(tree)
|
|
|
|
@staticmethod
|
|
def have_svn_access():
|
|
"""Check if we have svn access so we can enable tests that use svn.
|
|
|
|
"""
|
|
have_svn = False
|
|
cmd = ['svn', 'ls', SVN_TEST_REPO, ]
|
|
try:
|
|
execute_subprocess(cmd)
|
|
have_svn = True
|
|
except BaseException:
|
|
pass
|
|
return have_svn
|
|
|
|
def skip_if_no_svn_access(self):
|
|
"""Function decorator to disable svn tests when svn isn't available
|
|
"""
|
|
have_svn = self.have_svn_access()
|
|
if not have_svn:
|
|
raise unittest.SkipTest("No svn access")
|
|
|
|
def test_container_simple_svn(self):
|
|
"""Verify that a container repo can pull in an svn branch and svn tag.
|
|
|
|
"""
|
|
self.skip_if_no_svn_access()
|
|
# create repo
|
|
under_test_dir = self.setup_test_repo(CONTAINER_REPO_NAME)
|
|
self._generator.container_simple_svn(under_test_dir)
|
|
|
|
# checkout
|
|
overall, tree = self.execute_cmd_in_dir(under_test_dir,
|
|
self.checkout_args)
|
|
|
|
# verify status is clean and unmodified
|
|
overall, tree = self.execute_cmd_in_dir(under_test_dir,
|
|
self.status_args)
|
|
self._check_container_simple_svn_post_checkout(overall, tree)
|
|
|
|
# update description file to make the tag into a branch and
|
|
# trigger a switch
|
|
self._generator.update_svn_branch(under_test_dir, 'svn_tag', 'trunk')
|
|
|
|
# checkout
|
|
overall, tree = self.execute_cmd_in_dir(under_test_dir,
|
|
self.checkout_args)
|
|
|
|
# verify status is clean and unmodified
|
|
overall, tree = self.execute_cmd_in_dir(under_test_dir,
|
|
self.status_args)
|
|
self._check_container_simple_svn_post_checkout(overall, tree)
|
|
|
|
# add an untracked file to the repo
|
|
tracked = False
|
|
self._add_file_to_repo(under_test_dir,
|
|
'externals/svn_branch/tmp.txt', tracked)
|
|
|
|
# run a no-op checkout: pre-checkout status should be clean,
|
|
# ignoring the untracked file.
|
|
overall, tree = self.execute_cmd_in_dir(under_test_dir,
|
|
self.checkout_args)
|
|
self._check_container_simple_svn_post_checkout(overall, tree)
|
|
|
|
# update description file to make the branch into a tag and
|
|
# trigger a modified sync status
|
|
self._generator.update_svn_branch(under_test_dir, 'svn_tag',
|
|
'tags/cesm2.0.beta07')
|
|
|
|
# checkout: pre-checkout status should be clean and modified,
|
|
# will modify working copy.
|
|
overall, tree = self.execute_cmd_in_dir(under_test_dir,
|
|
self.checkout_args)
|
|
self._check_container_simple_svn_sb_clean_st_mod(overall, tree)
|
|
|
|
# verify status is still clean and unmodified, last
|
|
# checkout modified the working dir state.
|
|
overall, tree = self.execute_cmd_in_dir(under_test_dir,
|
|
self.verbose_args)
|
|
self._check_container_simple_svn_post_checkout(overall, tree)
|
|
|
|
class TestSubrepoCheckout(BaseTestSysCheckout):
|
|
# Need to store information at setUp time for checking
|
|
# pylint: disable=too-many-instance-attributes
|
|
"""Run tests to ensure proper handling of repos with submodules.
|
|
|
|
By default, submodules in git repositories are checked out. A git
|
|
repository checked out as a submodule is treated as if it was
|
|
listed in an external with the same properties as in the source
|
|
.gitmodules file.
|
|
"""
|
|
|
|
def setUp(self):
|
|
"""Setup for all submodule checkout tests
|
|
Create a repo with two submodule repositories.
|
|
"""
|
|
|
|
# Run the basic setup
|
|
super(TestSubrepoCheckout, self).setUp()
|
|
# create test repo
|
|
# We need to do this here (rather than have a static repo) because
|
|
# git submodules do not allow for variables in .gitmodules files
|
|
self._test_repo_name = 'test_repo_with_submodules'
|
|
self._bare_branch_name = 'subrepo_branch'
|
|
self._config_branch_name = 'subrepo_config_branch'
|
|
self._container_extern_name = 'externals_container.cfg'
|
|
self._my_test_dir = os.path.join(os.environ[MANIC_TEST_TMP_REPO_ROOT],
|
|
self._test_id)
|
|
self._repo_dir = os.path.join(self._my_test_dir, self._test_repo_name)
|
|
self._checkout_dir = 'repo_with_submodules'
|
|
check_dir = self.setup_test_repo(CONTAINER_REPO_NAME,
|
|
dest_dir_in=self._repo_dir)
|
|
self.assertTrue(self._repo_dir == check_dir)
|
|
# Add the submodules
|
|
cwd = os.getcwd()
|
|
fork_repo_dir = os.path.join(self._bare_root, SIMPLE_FORK_NAME)
|
|
simple_repo_dir = os.path.join(self._bare_root, SIMPLE_REPO_NAME)
|
|
self._simple_ext_fork_name = SIMPLE_FORK_NAME.split('.')[0]
|
|
self._simple_ext_name = SIMPLE_REPO_NAME.split('.')[0]
|
|
os.chdir(self._repo_dir)
|
|
# Add a branch with a subrepo
|
|
cmd = ['git', 'branch', self._bare_branch_name, 'master']
|
|
execute_subprocess(cmd)
|
|
cmd = ['git', 'checkout', self._bare_branch_name]
|
|
execute_subprocess(cmd)
|
|
cmd = ['git', 'submodule', 'add', fork_repo_dir]
|
|
execute_subprocess(cmd)
|
|
cmd = ['git', 'commit', '-am', "'Added simple-ext-fork as a submodule'"]
|
|
execute_subprocess(cmd)
|
|
# Save the fork repo hash for comparison
|
|
os.chdir(self._simple_ext_fork_name)
|
|
self._fork_hash_check = self.get_git_hash()
|
|
os.chdir(self._repo_dir)
|
|
# Now, create a branch to test from_sbmodule
|
|
cmd = ['git', 'branch',
|
|
self._config_branch_name, self._bare_branch_name]
|
|
execute_subprocess(cmd)
|
|
cmd = ['git', 'checkout', self._config_branch_name]
|
|
execute_subprocess(cmd)
|
|
cmd = ['git', 'submodule', 'add', simple_repo_dir]
|
|
execute_subprocess(cmd)
|
|
# Checkout feature2
|
|
os.chdir(self._simple_ext_name)
|
|
cmd = ['git', 'branch', 'feature2', 'origin/feature2']
|
|
execute_subprocess(cmd)
|
|
cmd = ['git', 'checkout', 'feature2']
|
|
execute_subprocess(cmd)
|
|
# Save the fork repo hash for comparison
|
|
self._simple_hash_check = self.get_git_hash()
|
|
os.chdir(self._repo_dir)
|
|
self.create_externals_file(filename=self._container_extern_name,
|
|
dest_dir=self._repo_dir, from_submodule=True)
|
|
cmd = ['git', 'add', self._container_extern_name]
|
|
execute_subprocess(cmd)
|
|
cmd = ['git', 'commit', '-am', "'Added simple-ext as a submodule'"]
|
|
execute_subprocess(cmd)
|
|
# Reset to master
|
|
cmd = ['git', 'checkout', 'master']
|
|
execute_subprocess(cmd)
|
|
os.chdir(cwd)
|
|
|
|
@staticmethod
|
|
def get_git_hash(revision="HEAD"):
|
|
"""Return the hash for <revision>"""
|
|
cmd = ['git', 'rev-parse', revision]
|
|
git_out = execute_subprocess(cmd, output_to_caller=True)
|
|
return git_out.strip()
|
|
|
|
def create_externals_file(self, name='', filename=CFG_NAME, dest_dir=None,
|
|
branch_name=None, sub_externals=None,
|
|
from_submodule=False):
|
|
# pylint: disable=too-many-arguments
|
|
"""Create a container externals file with only simple externals.
|
|
|
|
"""
|
|
self._generator.create_config()
|
|
|
|
if dest_dir is None:
|
|
dest_dir = self._my_test_dir
|
|
|
|
if from_submodule:
|
|
self._generator.create_section(SIMPLE_FORK_NAME,
|
|
self._simple_ext_fork_name,
|
|
from_submodule=True)
|
|
self._generator.create_section(SIMPLE_REPO_NAME,
|
|
self._simple_ext_name,
|
|
branch='feature3', path='',
|
|
from_submodule=False)
|
|
else:
|
|
if branch_name is None:
|
|
branch_name = 'master'
|
|
|
|
self._generator.create_section(self._test_repo_name,
|
|
self._checkout_dir,
|
|
branch=branch_name,
|
|
path=name, externals=sub_externals,
|
|
repo_path=self._repo_dir)
|
|
|
|
self._generator.write_config(dest_dir, filename=filename)
|
|
|
|
def idempotence_check(self, checkout_dir):
|
|
"""Verify that calling checkout_externals and
|
|
checkout_externals --status does not cause errors"""
|
|
cwd = os.getcwd()
|
|
os.chdir(checkout_dir)
|
|
overall, _ = self.execute_cmd_in_dir(self._my_test_dir,
|
|
self.checkout_args)
|
|
self.assertTrue(overall == 0)
|
|
overall, _ = self.execute_cmd_in_dir(self._my_test_dir,
|
|
self.status_args)
|
|
self.assertTrue(overall == 0)
|
|
os.chdir(cwd)
|
|
|
|
def test_submodule_checkout_bare(self):
|
|
"""Verify that a git repo with submodule is properly checked out
|
|
This test if for where there is no 'externals' keyword in the
|
|
parent repo.
|
|
Correct behavior is that the submodule is checked out using
|
|
normal git submodule behavior.
|
|
"""
|
|
simple_ext_fork_tag = "(tag1)"
|
|
simple_ext_fork_status = " "
|
|
self.create_externals_file(branch_name=self._bare_branch_name)
|
|
overall, _ = self.execute_cmd_in_dir(self._my_test_dir,
|
|
self.checkout_args)
|
|
self.assertTrue(overall == 0)
|
|
cwd = os.getcwd()
|
|
checkout_dir = os.path.join(self._my_test_dir, self._checkout_dir)
|
|
fork_file = os.path.join(checkout_dir,
|
|
self._simple_ext_fork_name, "readme.txt")
|
|
self.assertTrue(os.path.exists(fork_file))
|
|
os.chdir(checkout_dir)
|
|
submods = git_submodule_status(checkout_dir)
|
|
self.assertEqual(len(submods.keys()), 1)
|
|
self.assertTrue(self._simple_ext_fork_name in submods)
|
|
submod = submods[self._simple_ext_fork_name]
|
|
self.assertTrue('hash' in submod)
|
|
self.assertEqual(submod['hash'], self._fork_hash_check)
|
|
self.assertTrue('status' in submod)
|
|
self.assertEqual(submod['status'], simple_ext_fork_status)
|
|
self.assertTrue('tag' in submod)
|
|
self.assertEqual(submod['tag'], simple_ext_fork_tag)
|
|
os.chdir(cwd)
|
|
self.idempotence_check(checkout_dir)
|
|
|
|
def test_submodule_checkout_none(self):
|
|
"""Verify that a git repo with submodule is properly checked out
|
|
This test is for when 'externals=None' is in parent repo's
|
|
externals cfg file.
|
|
Correct behavior is the submodle is not checked out.
|
|
"""
|
|
self.create_externals_file(branch_name=self._bare_branch_name,
|
|
sub_externals="none")
|
|
overall, _ = self.execute_cmd_in_dir(self._my_test_dir,
|
|
self.checkout_args)
|
|
self.assertTrue(overall == 0)
|
|
cwd = os.getcwd()
|
|
checkout_dir = os.path.join(self._my_test_dir, self._checkout_dir)
|
|
fork_file = os.path.join(checkout_dir,
|
|
self._simple_ext_fork_name, "readme.txt")
|
|
self.assertFalse(os.path.exists(fork_file))
|
|
os.chdir(cwd)
|
|
self.idempotence_check(checkout_dir)
|
|
|
|
def test_submodule_checkout_config(self): # pylint: disable=too-many-locals
|
|
"""Verify that a git repo with submodule is properly checked out
|
|
This test if for when the 'from_submodule' keyword is used in the
|
|
parent repo.
|
|
Correct behavior is that the submodule is checked out using
|
|
normal git submodule behavior.
|
|
"""
|
|
tag_check = None # Not checked out as submodule
|
|
status_check = "-" # Not checked out as submodule
|
|
self.create_externals_file(branch_name=self._config_branch_name,
|
|
sub_externals=self._container_extern_name)
|
|
overall, _ = self.execute_cmd_in_dir(self._my_test_dir,
|
|
self.checkout_args)
|
|
self.assertTrue(overall == 0)
|
|
cwd = os.getcwd()
|
|
checkout_dir = os.path.join(self._my_test_dir, self._checkout_dir)
|
|
fork_file = os.path.join(checkout_dir,
|
|
self._simple_ext_fork_name, "readme.txt")
|
|
self.assertTrue(os.path.exists(fork_file))
|
|
os.chdir(checkout_dir)
|
|
# Check submodule status
|
|
submods = git_submodule_status(checkout_dir)
|
|
self.assertEqual(len(submods.keys()), 2)
|
|
self.assertTrue(self._simple_ext_fork_name in submods)
|
|
submod = submods[self._simple_ext_fork_name]
|
|
self.assertTrue('hash' in submod)
|
|
self.assertEqual(submod['hash'], self._fork_hash_check)
|
|
self.assertTrue('status' in submod)
|
|
self.assertEqual(submod['status'], status_check)
|
|
self.assertTrue('tag' in submod)
|
|
self.assertEqual(submod['tag'], tag_check)
|
|
self.assertTrue(self._simple_ext_name in submods)
|
|
submod = submods[self._simple_ext_name]
|
|
self.assertTrue('hash' in submod)
|
|
self.assertEqual(submod['hash'], self._simple_hash_check)
|
|
self.assertTrue('status' in submod)
|
|
self.assertEqual(submod['status'], status_check)
|
|
self.assertTrue('tag' in submod)
|
|
self.assertEqual(submod['tag'], tag_check)
|
|
# Check fork repo status
|
|
os.chdir(self._simple_ext_fork_name)
|
|
self.assertEqual(self.get_git_hash(), self._fork_hash_check)
|
|
os.chdir(checkout_dir)
|
|
os.chdir(self._simple_ext_name)
|
|
hash_check = self.get_git_hash('origin/feature3')
|
|
self.assertEqual(self.get_git_hash(), hash_check)
|
|
os.chdir(cwd)
|
|
self.idempotence_check(checkout_dir)
|
|
|
|
class TestSysCheckoutErrors(BaseTestSysCheckout):
|
|
"""Run systems level tests of error conditions in checkout_externals
|
|
|
|
Error conditions - these tests are designed to trigger specific
|
|
error conditions and ensure that they are being handled as
|
|
runtime errors (and hopefully usefull error messages) instead of
|
|
the default internal message that won't mean anything to the
|
|
user, e.g. key error, called process error, etc.
|
|
|
|
These are not 'expected failures'. They are pass when a
|
|
RuntimeError is raised, fail if any other error is raised (or no
|
|
error is raised).
|
|
|
|
"""
|
|
|
|
# NOTE(bja, 2017-11) pylint complains about long method names, but
|
|
# it is hard to differentiate tests without making them more
|
|
# cryptic.
|
|
# pylint: disable=invalid-name
|
|
|
|
def test_error_unknown_protocol(self):
|
|
"""Verify that a runtime error is raised when the user specified repo
|
|
protocol is not known.
|
|
|
|
"""
|
|
# create repo
|
|
under_test_dir = self.setup_test_repo(CONTAINER_REPO_NAME)
|
|
self._generator.container_simple_required(under_test_dir)
|
|
|
|
# update the config file to point to a different remote with
|
|
# the tag instead of branch. Tag MUST NOT be in the original
|
|
# repo!
|
|
self._generator.update_protocol(under_test_dir, 'simp_branch',
|
|
'this-protocol-does-not-exist')
|
|
|
|
with self.assertRaises(RuntimeError):
|
|
self.execute_cmd_in_dir(under_test_dir, self.checkout_args)
|
|
|
|
def test_error_switch_protocol(self):
|
|
"""Verify that a runtime error is raised when the user switches
|
|
protocols, git to svn.
|
|
|
|
TODO(bja, 2017-11) This correctly results in an error, but it
|
|
isn't a helpful error message.
|
|
|
|
"""
|
|
# create repo
|
|
under_test_dir = self.setup_test_repo(CONTAINER_REPO_NAME)
|
|
self._generator.container_simple_required(under_test_dir)
|
|
|
|
# update the config file to point to a different remote with
|
|
# the tag instead of branch. Tag MUST NOT be in the original
|
|
# repo!
|
|
self._generator.update_protocol(under_test_dir, 'simp_branch', 'svn')
|
|
with self.assertRaises(RuntimeError):
|
|
self.execute_cmd_in_dir(under_test_dir, self.checkout_args)
|
|
|
|
def test_error_unknown_tag(self):
|
|
"""Verify that a runtime error is raised when the user specified tag
|
|
does not exist.
|
|
|
|
"""
|
|
# create repo
|
|
under_test_dir = self.setup_test_repo(CONTAINER_REPO_NAME)
|
|
self._generator.container_simple_required(under_test_dir)
|
|
|
|
# update the config file to point to a different remote with
|
|
# the tag instead of branch. Tag MUST NOT be in the original
|
|
# repo!
|
|
self._generator.update_tag(under_test_dir, 'simp_branch',
|
|
'this-tag-does-not-exist', SIMPLE_REPO_NAME)
|
|
|
|
with self.assertRaises(RuntimeError):
|
|
self.execute_cmd_in_dir(under_test_dir, self.checkout_args)
|
|
|
|
def test_error_overspecify_tag_branch(self):
|
|
"""Verify that a runtime error is raised when the user specified both
|
|
tag and a branch
|
|
|
|
"""
|
|
# create repo
|
|
under_test_dir = self.setup_test_repo(CONTAINER_REPO_NAME)
|
|
self._generator.container_simple_required(under_test_dir)
|
|
|
|
# update the config file to point to a different remote with
|
|
# the tag instead of branch. Tag MUST NOT be in the original
|
|
# repo!
|
|
self._generator.update_tag(under_test_dir, 'simp_branch',
|
|
'this-tag-does-not-exist', SIMPLE_REPO_NAME,
|
|
remove_branch=False)
|
|
|
|
with self.assertRaises(RuntimeError):
|
|
self.execute_cmd_in_dir(under_test_dir, self.checkout_args)
|
|
|
|
def test_error_underspecify_tag_branch(self):
|
|
"""Verify that a runtime error is raised when the user specified
|
|
neither a tag or a branch
|
|
|
|
"""
|
|
# create repo
|
|
under_test_dir = self.setup_test_repo(CONTAINER_REPO_NAME)
|
|
self._generator.container_simple_required(under_test_dir)
|
|
|
|
# update the config file to point to a different remote with
|
|
# the tag instead of branch. Tag MUST NOT be in the original
|
|
# repo!
|
|
self._generator.update_underspecify_branch_tag(under_test_dir,
|
|
'simp_branch')
|
|
|
|
with self.assertRaises(RuntimeError):
|
|
self.execute_cmd_in_dir(under_test_dir, self.checkout_args)
|
|
|
|
def test_error_missing_url(self):
|
|
"""Verify that a runtime error is raised when the user specified
|
|
neither a tag or a branch
|
|
|
|
"""
|
|
# create repo
|
|
under_test_dir = self.setup_test_repo(CONTAINER_REPO_NAME)
|
|
self._generator.container_simple_required(under_test_dir)
|
|
|
|
# update the config file to point to a different remote with
|
|
# the tag instead of branch. Tag MUST NOT be in the original
|
|
# repo!
|
|
self._generator.update_underspecify_remove_url(under_test_dir,
|
|
'simp_branch')
|
|
|
|
with self.assertRaises(RuntimeError):
|
|
self.execute_cmd_in_dir(under_test_dir, self.checkout_args)
|
|
|
|
|
|
if __name__ == '__main__':
|
|
unittest.main()
|