def test_skip(self): """Do not run fix if the Rez package does not define a Python package / module.""" root = os.path.join(tempfile.mkdtemp(), "test_folder") os.makedirs(root) self.delete_item_later(root) package = package_common.make_package( "project_a", root, package_common.make_source_package) repository, packages, remote_root = package_common.make_fake_repository( [package], root) self.delete_item_later(repository.working_dir) self.delete_item_later(remote_root) package = packages[0] expected = ( set(), [], [ worker.Skip( package, os.path.join(repository.working_dir, "project_a"), "Rez Package does not define Python packages / modules.", ) ], ) with rez_configuration.patch_packages_path([repository.working_dir]): self._test(expected)
def test_no_change(self, _create_pull_request): """Successfully run a command but have that command do nothing. In this case, no PR should be submitted (because nothing was changed). """ root = os.path.join(tempfile.mkdtemp(), "test_folder") os.makedirs(root) self.delete_item_later(root) packages = [ package_common.make_package( "project_a", root, package_common.make_source_python_package) ] repository, packages, remote_root = package_common.make_fake_repository( packages, root) self.delete_item_later(repository.working_dir) self.delete_item_later(remote_root) paths = [repository.working_dir] with rez_configuration.patch_packages_path(paths): unfixed, invalids, skips = self._test_unhandled(paths=paths) self.assertEqual(["project_a"], [package.name for package, _ in unfixed]) self.assertEqual([], invalids) self.assertEqual([], skips)
def test_no_remote(self): """Check that a fix will not run if the package's repository has no git remote.""" root = os.path.join(tempfile.mkdtemp(), "test_folder") os.makedirs(root) self.delete_item_later(root) package = package_common.make_package("project_a", root, self._make_source_with_no_remote) invalids = [ exceptions.NoRepositoryRemote( package, os.path.join(root, "project_a"), "No remote origin could be found for", ) ] paths = [inspection.get_packages_path_from_package(package)] with rez_configuration.patch_packages_path(paths): unfixed, invalids, skips = self._test_unhandled() self.assertEqual(set(), unfixed) self.assertEqual([], skips) invalid = next(iter(invalids)) self.assertTrue( str(invalid).startswith( "Generic error: No remote origin could be found for"))
def test_source_variant(self, run_command, has_documentation): """Create a source (non-built) Rez package that has 1+ variants and run a command on it. Args: run_command (:class:`mock.MagicMock`): A replacement for the function that would normally run as part of the commands that run on a Rez package. If this function gets run, we know that this test passes. """ has_documentation.side_effect = [False, True] packages = self._setup_test( run_command, package_common.make_source_variant_python_package) roots = list( set( inspection.get_packages_path_from_package(package) for package in packages)) directory = tempfile.mkdtemp(suffix="_some_build_location") self.delete_item_later(directory) build_package = package_common.make_build_python_package( textwrap.dedent("""\ name = "project_b" version = "2.0.0" revision = { "push_url": "fake_git_repo", } """), "project_b", "2.0.0", directory, ) build_package = finder.get_nearest_rez_package( os.path.join(build_package)) build_root = inspection.get_packages_path_from_package(build_package) paths = roots + [build_root] with rez_configuration.patch_packages_path(paths): self._test( ( set(), [], [ # Important note: "project_b" doesn't actually already # have documentation. It's a result of `side_effect`, above. # worker.Skip( build_package, finder.get_package_root(build_package), "Python package already has documentation.", ) ], ), paths, ) self.assertEqual(1, run_command.call_count)
def test_released_dependency_missing(self, run_command): """Fail to resolve a package because "project_a" could not be found. Args: run_command (:class:`mock.MagicMock`): A replacement for the function that would normally run as part of the commands that run on a Rez package. If this function gets run, we know that this test passes. """ run_command.return_value = "" root = os.path.join(tempfile.mkdtemp(), "test_folder") os.makedirs(root) self.delete_item_later(root) packages = [ package_common.make_package( "project_a", root, package_common.make_source_python_package), package_common.make_package( "project_b", root, package_common.make_source_python_package, dependencies={"project_a-1+<2"}, ), ] repository, packages, remote_root = package_common.make_fake_repository( packages, root) self.delete_item_later(repository.working_dir) self.delete_item_later(remote_root) package = packages[1] package_root = finder.get_package_root(package) with rez_configuration.patch_packages_path([repository.working_dir]): self._test( ( set(), [ exceptions.InvalidPackage( package, package_root, "Package could not be found: project_a-1+<2", ) ], [], ), [repository.working_dir], ) self.assertEqual(1, run_command.call_count)
def _test(request, directory, build_directory=""): """Test a CLI command for `rez_test_env` and get its resolved packages back. This function is a bit intense so let's break it down. The goal of this function is to - Get resolved Rez packages - Make a test environment that will work regardless of the user's actual Rez environment. - Be as quiet (not-spammy) as possible Args: request (str): User-provided test which would be sent directly to the CLI. directory (str): The folder on-disk where the fake Rez package(s) for the test live. build_directory (str, optional): If provided, this path is added to resolves. Default: "". Returns: set[str]: The resolved Rez packages/versions. """ packages_path = [directory] if build_directory: packages_path += [build_directory] with _override_context_command(): with rez_configuration.patch_packages_path(packages_path): try: with wurlitzer.pipes() as ( stdout, stderr, ): # wurlitzer will capture stdout cli.main(shlex.split(request)) except SystemExit as error: # Since `rez_test_env` literally runs the `rez-env` CLI # as part of its work, it always raises a `SystemExit` # exception. So we catch that and only re-raise if there # was actually an error. # if error.code != 0: raise data = set(stdout.read().strip().split()) stdout.close() stderr.close() return data
def test_source_no_variant(self, run_command): """Create a source (non-built) Rez package and run a command on it. Args: run_command (:class:`mock.MagicMock`): A replacement for the function that would normally run as part of the commands that run on a Rez package. If this function gets run, we know that this test passes. """ packages = self._setup_test(run_command, package_common.make_source_python_package) path_root = inspection.get_packages_path_from_package(packages[0]) paths = [path_root] with rez_configuration.patch_packages_path(paths): self._test((set(), [], []), [path_root]) self.assertEqual(1, run_command.call_count)
def test_no_repository(self): """Check that a fix will not run if the package has no destination repository.""" def _make_package_with_no_repository(text, name, _, root): return package_common.make_source_python_package( text, name, None, root) root = os.path.join(tempfile.mkdtemp(), "test_folder") os.makedirs(root) self.delete_item_later(root) package = package_common.make_package( "project_a", root, _make_package_with_no_repository) invalids = [ exceptions.NoGitRepository(package, os.path.join(root, "project_a"), "is not in a Git repository.") ] expected = (set(), invalids, []) with rez_configuration.patch_packages_path([root]): self._test(expected, [root])
def test_one(self, run_command): """Run command on a single Rez package. Unlike :meth:`Fix.test_two` which has special logic for dealing with source vs release packages, this test will just run on a single source Rez package. In other words, this test is intentionally simpler than :meth:`Fix.test_two`. Args: run_command (:class:`mock.MagicMock`): A replacement for the function that would normally run as part of the commands that run on a Rez package. If this function gets run, we know that this test passes. """ run_command.return_value = "" root = os.path.join(tempfile.mkdtemp(), "test_folder") os.makedirs(root) self.delete_item_later(root) packages = [ package_common.make_package( "project_a", root, package_common.make_source_python_package) ] repository, packages, remote_root = package_common.make_fake_repository( packages, root) self.delete_item_later(repository.working_dir) self.delete_item_later(remote_root) paths = [repository.working_dir] with rez_configuration.patch_packages_path(paths): self._test((set(), [], []), paths) self.assertEqual(1, run_command.call_count)
def test_versioned_source(self, run_command): """Make sure a Rez source package directory which resembles an installed Rez package works. Basically if the source Rez package has a parent version folder BUT the version folder isn't an exact match to what is written in the package.py file then we need to handle that scenario correctly. """ def _make_version_package(path, text): directory = os.path.dirname(path) if not os.path.isdir(directory): os.makedirs(directory) with open(path, "w") as handler: handler.write(text) os.makedirs(os.path.join(directory, "python")) open(os.path.join(directory, "python", "some_module.py"), "a").close() with open(os.path.join(directory, "rezbuild.py"), "w") as handler: handler.write( textwrap.dedent("""\ import os import shutil def main(source, install): shutil.copytree( os.path.join(source, "python"), os.path.join(install, "python"), ) main( os.environ["REZ_BUILD_SOURCE_PATH"], os.environ["REZ_BUILD_INSTALL_PATH"], ) """)) return finder.get_nearest_rez_package(directory) run_command.return_value = "" root = os.path.join(tempfile.mkdtemp(), "test_versioned_source") packages = [ _make_version_package( os.path.join(root, "rez_package_a", "1.2.0", "package.py"), textwrap.dedent("""\ name = "rez_package_a" version = "1.2.0" build_command = "python {root}/rezbuild.py" def commands(): import os env.PYTHONPATH.append(os.path.join("{root}", "python")) """), ), _make_version_package( os.path.join(root, "rez_package_a", "1.3.0", "package.py"), textwrap.dedent("""\ name = "rez_package_a" version = "1.3.0" build_command = "python {root}/rezbuild.py" def commands(): import os env.PYTHONPATH.append(os.path.join("{root}", "python")) """), ), _make_version_package( os.path.join(root, "rez_package_b", "2", "package.py"), textwrap.dedent("""\ name = "rez_package_b" version = "2.5.0" build_command = "python {root}/rezbuild.py" def commands(): import os env.PYTHONPATH.append(os.path.join("{root}", "python")) """), ), ] repository, packages, remote_root = package_common.make_fake_repository( packages, root) self.delete_item_later(repository.working_dir) self.delete_item_later(remote_root) release_path = _release_packages(packages) self.delete_item_later(release_path) paths = [release_path] with rez_configuration.patch_packages_path(paths): self._test((set(), [], []), paths) self.assertEqual(2, run_command.call_count)
def _test_release(self, run_command, builder): """Build a Rez package, release it, and then test it. This is a convenience function to keep unittests a bit more concise. Args: run_command (:class:`mock.MagicMock`): A replacement for the function that would normally run as part of the commands that run on a Rez package. If this function gets run, we know that this test passes. builder (callable[str, str, str, str] -> str): The function that is used to generate the "contents" of the Rez package. This function is only responsible for creating the package.py/rezbuild.py files, it doesn't create a Python package, for example. This parameter is responsible for creating the rest of the files of the package. """ packages = self._setup_test(run_command, builder, variants=[["project_b-1"]]) directory = tempfile.mkdtemp(suffix="_some_build_location") self.delete_item_later(directory) build_package = package_common.make_build_python_package( textwrap.dedent("""\ name = "project_b" version = "2.0.0" revision = { "push_url": "fake_git_repo", } """), "project_b", "2.0.0", directory, ) build_package = finder.get_nearest_rez_package( os.path.join(build_package)) build_root = inspection.get_packages_path_from_package(build_package) build_paths = [build_root] release_path = _release_packages( packages, search_paths=build_paths + config.packages_path, # pylint: disable=no-member ) self.delete_item_later(release_path) packages = [ packages_.get_developer_package( os.path.join(release_path, package.name, str(package.version))) for package in packages ] paths = build_paths + [release_path] with rez_configuration.patch_packages_path(paths): self._test( ( set(), [], [ worker.Skip( build_package, finder.get_package_root(build_package), "Python package already has documentation.", ) ], ), paths, ) self.assertEqual(1, run_command.call_count)
def test_empty(self): """Check that zero packages does not error.""" with rez_configuration.patch_packages_path([]): self._test((set(), [], []), [])
def test_multiple(self, run_command): """Run command on Rez packages with more than one repository. Args: run_command (:class:`mock.MagicMock`): A replacement for the function that would normally run as part of the commands that run on a Rez package. If this function gets run, we know that this test passes. """ run_command.return_value = "" root = os.path.join(tempfile.mkdtemp(), "test_folder") os.makedirs(root) self.delete_item_later(root) repository_a, packages_a, remote_root = package_common.make_fake_repository( [ package_common.make_package( "project_a", root, package_common.make_source_python_package), package_common.make_package( "project_b", root, package_common.make_source_python_package, dependencies={"project_a-1+<2"}, ), ], root, ) self.delete_item_later(repository_a.working_dir) self.delete_item_later(remote_root) release_path_a = _release_packages(packages_a) self.delete_item_later(release_path_a) repository_b, packages_b, remote_root = package_common.make_fake_repository( [ package_common.make_package( "project_c", root, package_common.make_source_python_package), package_common.make_package( "project_d", root, package_common.make_source_python_package, dependencies={"project_a-1+<2"}, ), package_common.make_package( "project_e", root, package_common.make_source_python_package), ], root, ) self.delete_item_later(repository_b.working_dir) self.delete_item_later(remote_root) release_path_b = _release_packages(packages_b, search_paths=[release_path_a]) self.delete_item_later(release_path_b) self._test((set(), [], []), [release_path_a]) self.assertEqual(2, run_command.call_count) with rez_configuration.patch_packages_path( [release_path_b, release_path_a]): self._test((set(), [], []), [release_path_b, release_path_a]) self.assertEqual(7, run_command.call_count)