| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197 |
- #!/usr/bin/env python
- # Licensed to Cloudera, Inc. under one
- # or more contributor license agreements. See the NOTICE file
- # distributed with this work for additional information
- # regarding copyright ownership. Cloudera, Inc. licenses this file
- # to you under the Apache License, Version 2.0 (the
- # "License"); you may not use this file except in compliance
- # with the License. You may obtain a copy of the License at
- #
- # http://www.apache.org/licenses/LICENSE-2.0
- #
- # Unless required by applicable law or agreed to in writing, software
- # distributed under the License is distributed on an "AS IS" BASIS,
- # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- # See the License for the specific language governing permissions and
- # limitations under the License.
- import os
- import platform
- import shutil
- import sys
- import tempfile
- from unittest import mock
- import pytest
- # Workaround for import error: add current directory to Python path
- # The package-style import (desktop.core.generate_requirements) fails,
- # so we modify the path to enable direct module import
- sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
- from generate_requirements import RequirementsGenerator
- class TestRequirementsGenerator:
- """Test cases for the RequirementsGenerator class."""
- def setup_method(self):
- """
- Setup test environment with temporary directory and mock requirement files.
- Stores original system values to restore after test.
- """
- # Store original system values to restore later
- self.original_arch = platform.machine
- self.original_version_info = sys.version_info
- self.temp_dir = tempfile.mkdtemp()
- # Create a temporary ext-py3 directory mimicking the structure used by RequirementsGenerator
- self.ext_py3_dir = os.path.join(self.temp_dir, "ext-py3")
- os.makedirs(self.ext_py3_dir)
- # Create empty placeholder files that simulate local requirement packages
- for req in ["boto-2.49.0", "django-axes-5.13.0", "django-babel", "pysaml2-7.3.1", "python-sasl-0.3.1"]:
- with open(os.path.join(self.ext_py3_dir, req), "w") as f:
- f.write("")
- def teardown_method(self):
- """Teardown after each test method."""
- shutil.rmtree(self.temp_dir)
- platform.machine = self.original_arch
- sys.version_info = self.original_version_info
- @mock.patch("generate_requirements.this_dir")
- @mock.patch("generate_requirements.platform.machine")
- @mock.patch("generate_requirements.sys.version_info")
- def test_init_sets_properties(self, mock_version_info, mock_machine, mock_this_dir):
- """Test that __init__ sets the class properties correctly."""
- mock_machine.return_value = "x86_64"
- mock_version_info.major = 3
- mock_version_info.minor = 9
- mock_this_dir.return_value = self.temp_dir
- generator = RequirementsGenerator()
- assert generator.arch == "x86_64"
- assert generator.python_version_string == "3.9"
- assert isinstance(generator.requirements, list)
- assert isinstance(generator.local_requirements, list)
- assert "x86_64" in generator.arch_requirements_map
- assert "aarch64" in generator.arch_requirements_map
- @mock.patch("generate_requirements.this_dir", create=True)
- @mock.patch("generate_requirements.shutil.copytree")
- def test_copy_local_requirements(self, mock_copytree, mock_this_dir):
- """Test that copy_local_requirements copies files and returns correct paths."""
- # Need to make this_dir return the string value directly, not a MagicMock
- mock_this_dir.__str__.return_value = self.temp_dir
- python_version_string = "3.9"
- with mock.patch.object(RequirementsGenerator, "__init__", return_value=None):
- generator = RequirementsGenerator()
- generator.local_requirements = ["boto-2.49.0", "django-axes-5.13.0"]
- result = generator.copy_local_requirements(python_version_string)
- mock_copytree.assert_called_once_with(f"{self.temp_dir}/ext-py3", f"{self.temp_dir}/{python_version_string}")
- assert len(result) == 2
- assert f"file://{self.temp_dir}/{python_version_string}/boto-2.49.0" in result
- assert f"file://{self.temp_dir}/{python_version_string}/django-axes-5.13.0" in result
- @mock.patch("generate_requirements.this_dir")
- @mock.patch("builtins.open", new_callable=mock.mock_open)
- def test_generate_requirements_x86_64(self, mock_open, mock_this_dir):
- """Test generating requirements for x86_64 architecture."""
- # Need to make this_dir return the string value directly, not a MagicMock
- mock_this_dir.__str__.return_value = self.temp_dir
- with mock.patch.object(RequirementsGenerator, "__init__", return_value=None):
- generator = RequirementsGenerator()
- generator.arch = "x86_64"
- generator.python_version_string = "3.9"
- generator.requirements = ["Django==4.1.13"]
- generator.local_requirements = []
- generator.arch_requirements_map = {
- "x86_64": {"default": ["cryptography==42.0.8"], "3.9": ["Markdown==3.8", "numpy==1.24.4", "setuptools==80.9.0"]}
- }
- # Mock copy_local_requirements for test isolation
- generator.copy_local_requirements = mock.MagicMock(return_value=[])
- generator.generate_requirements()
- # Verify file creation with correct path
- mock_open.assert_called_once_with(f"{self.temp_dir}/requirements-x86_64-3.9.txt", "w")
- # Verify correct requirements were written
- expected_requirements = "\n".join(["Django==4.1.13", "Markdown==3.8", "numpy==1.24.4", "setuptools==80.9.0"])
- mock_open().write.assert_called_once_with(expected_requirements)
- @mock.patch("generate_requirements.this_dir")
- def test_generate_requirements_unsupported_arch(self, mock_this_dir):
- """Test that generate_requirements raises an error for unsupported architectures."""
- # Need to make this_dir return the string value directly, not a MagicMock
- mock_this_dir.__str__.return_value = self.temp_dir
- with mock.patch.object(RequirementsGenerator, "__init__", return_value=None):
- generator = RequirementsGenerator()
- generator.arch = "unsupported_arch"
- generator.python_version_string = "3.9"
- generator.requirements = []
- generator.arch_requirements_map = {"x86_64": {"default": []}, "aarch64": {"default": []}}
- with pytest.raises(ValueError) as exc_info:
- generator.generate_requirements()
- assert "Unsupported architecture: unsupported_arch" in str(exc_info.value)
- @mock.patch("generate_requirements.this_dir")
- def test_get_file_name(self, mock_this_dir):
- """Test that get_file_name returns the correct file path."""
- # Need to make this_dir return the string value directly, not a MagicMock
- mock_this_dir.__str__.return_value = self.temp_dir
- with mock.patch.object(RequirementsGenerator, "__init__", return_value=None):
- generator = RequirementsGenerator()
- generator.arch = "x86_64"
- generator.python_version_string = "3.9"
- result = generator.get_file_name()
- assert result == f"{self.temp_dir}/requirements-x86_64-3.9.txt"
- @mock.patch("generate_requirements.this_dir")
- @mock.patch("builtins.open", new_callable=mock.mock_open)
- def test_generate_requirements_with_local_requirements(self, mock_open, mock_this_dir):
- """Test generating requirements including local requirements."""
- # Need to make this_dir return the string value directly, not a MagicMock
- mock_this_dir.__str__.return_value = self.temp_dir
- with mock.patch.object(RequirementsGenerator, "__init__", return_value=None):
- generator = RequirementsGenerator()
- generator.arch = "x86_64"
- generator.python_version_string = "3.9"
- generator.requirements = []
- generator.local_requirements = ["boto-2.49.0", "django-axes-5.13.0"]
- generator.arch_requirements_map = {"x86_64": {"default": [], "3.9": ["Markdown==3.8", "setuptools==80.9.0"]}}
- # Mock copy_local_requirements to return file paths
- local_reqs = [f"file://{self.temp_dir}/3.9/boto-2.49.0", f"file://{self.temp_dir}/3.9/django-axes-5.13.0"]
- generator.copy_local_requirements = mock.MagicMock(return_value=local_reqs)
- generator.generate_requirements()
- mock_open.assert_called_once_with(f"{self.temp_dir}/requirements-x86_64-3.9.txt", "w")
- expected_requirements = "\n".join(
- ["Markdown==3.8", "setuptools==80.9.0", f"file://{self.temp_dir}/3.9/boto-2.49.0", f"file://{self.temp_dir}/3.9/django-axes-5.13.0"]
- )
- mock_open().write.assert_called_once_with(expected_requirements)
|