Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
6ac2142
Added checkout implementation for zip and tar clients.
leander-dsouza Aug 27, 2025
f88346c
Added checkout implementation and integration tests for the bzr client.
leander-dsouza Aug 27, 2025
50c408d
Added checkout implementation and integration tests for Git client.
leander-dsouza Aug 27, 2025
19b4b73
Added checkout implementation and integration tests for svn client.
leander-dsouza Aug 27, 2025
66a8dec
Added checkout implementation and integration tests for hg client.
leander-dsouza Aug 27, 2025
06e2606
Added export_repository implementation for zip and tar clients.
leander-dsouza Aug 27, 2025
6b2b1e6
Added export_repository implementation for bzr client.
leander-dsouza Aug 27, 2025
a2cfbd6
Ignore bzr tests if system is Windows.
leander-dsouza Aug 27, 2025
672cc16
Added export_repository method to SvnClient and corresponding tests.
leander-dsouza Aug 27, 2025
43e7574
Added export_repository support and corresponding tests for hg client.
leander-dsouza Aug 27, 2025
c3e7b87
Added export_repository support and corresponding tests for git client.
leander-dsouza Aug 27, 2025
c76c4e6
Added breezy to the CI for testing across linux and macOS.
leander-dsouza Aug 27, 2025
3739eba
Add helper function to match vcstools API.
leander-dsouza Sep 16, 2025
d2c0a77
Added get_path method to mimic vcstools' API.
leander-dsouza Sep 16, 2025
09dd985
Skip breezy tests in favor of codecov image.
leander-dsouza Oct 1, 2025
8330c80
Simplify git client checkout_repository method.
leander-dsouza Oct 6, 2025
5b27149
Standardised all export repository methods across clients.
leander-dsouza Oct 6, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 11 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -41,12 +41,22 @@ jobs:
brew install subversion mercurial
if: matrix.os == 'macos-latest'

- name: Install dependencies (macOS)
run: |
brew install subversion mercurial breezy
if: matrix.os == 'macos-latest'

- name: Install dependencies (Ubuntu)
run: |
sudo apt-get update
sudo apt-get install -y --no-install-recommends subversion mercurial
sudo apt-get install -y --no-install-recommends subversion mercurial brz
if: startsWith(matrix.os, 'ubuntu')

- name: Install breezy pip (Ubuntu 24.04)
run: |
python -m pip install --upgrade breezy # Adds breezy to the PYTHONPATH
if: startsWith(matrix.os, 'ubuntu-24.04')

- name: Test with pytest
run: |
pip install --upgrade .[test]
Expand Down
130 changes: 130 additions & 0 deletions test/test_bzr.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
"""Integration tests for BzrClient class."""

import os
import tarfile
import tempfile
import unittest
from shutil import which

from vcs2l.clients.bzr import BzrClient
from vcs2l.util import rmtree

bzr = which('bzr')


@unittest.skipIf(not bzr, '`bzr` was not found')
class TestCheckout(unittest.TestCase):
"""Simple integration tests for BzrClient checkout functionality."""

def setUp(self):
"""Set up test fixtures."""
self.test_dir = tempfile.mkdtemp()
self.repo_link = 'https://github.com/octocat/Hello-World.git'
self.repo_path = os.path.join(self.test_dir, 'test_repo')

def tearDown(self):
"""Clean up after tests."""
if os.path.exists(self.test_dir):
rmtree(self.test_dir)

def test_checkout_repository(self):
"""Test checkout with a valid repository URL."""
client = BzrClient(self.repo_path)
result = client.checkout(self.repo_link)

self.assertTrue(result)
self.assertTrue(os.path.exists(self.repo_path))

def test_checkout_invalid_url(self):
"""Test checkout with invalid URL raises ValueError."""
client = BzrClient(self.repo_path)

with self.assertRaises(ValueError):
client.checkout(None)

with self.assertRaises(ValueError):
client.checkout('')

def test_checkout_existing_directory_fails(self):
"""Test checkout fails when directory already exists with content."""
# Create the target directory and put a file in it
os.makedirs(self.repo_path, exist_ok=True)
test_file = os.path.join(self.repo_path, 'existing_file.txt')
with open(test_file, 'w', encoding='utf-8') as f:
f.write('This file already exists')

client = BzrClient(self.repo_path)

with self.assertRaises(RuntimeError) as context:
client.checkout(self.repo_link)

# Verify the error message mentions the path
self.assertIn(self.repo_path, str(context.exception))
self.assertIn('Target path exists and is not empty', str(context.exception))


@unittest.skipIf(not bzr, '`bzr` was not found')
class TestExportRepository(unittest.TestCase):
"""Integration tests for BzrClient export_repository functionality."""

def setUp(self):
self.test_dir = tempfile.mkdtemp()
self.repo_path = os.path.join(self.test_dir, 'test_repo')
self.export_path = os.path.join(self.test_dir, 'export_test')
self.client = BzrClient(self.repo_path)
# Use a git repository instead of launchpad for future-proofing
self.repo_url = 'https://github.com/octocat/Hello-World.git'

def tearDown(self):
if os.path.exists(self.test_dir):
rmtree(self.test_dir)

def test_export_repository(self):
"""Test export repository."""
self.client.checkout(self.repo_url)

# Change to the repository directory for export
original_cwd = os.getcwd()
try:
os.chdir(self.repo_path)

# Test export with a specific revision
result = self.client.export_repository(None, self.export_path)
self.assertTrue(result, 'Export should return True on success')

archive_path = self.export_path + '.tar.gz'
self.assertTrue(
os.path.exists(archive_path), 'Archive file should be created'
)

# Verify the archive is a valid tar.gz file
with tarfile.open(archive_path, 'r:gz') as tar:
members = tar.getnames()
self.assertGreater(len(members), 0, 'Archive should contain files')

finally:
os.chdir(original_cwd)

def test_export_repository_git_version_unsupported(self):
"""Test export repository with unsupported version for git repo."""
self.client.checkout(self.repo_url)

# Change to the repository directory for export
original_cwd = os.getcwd()
try:
os.chdir(self.repo_path)

# Test export with invalid version
result = self.client.export_repository('999999999', self.export_path)
self.assertFalse(result, 'Version is not supported for git repositories.')

archive_path = self.export_path + '.tar.gz'
self.assertFalse(
os.path.exists(archive_path), 'Archive should not be created on failure'
)
finally:
os.chdir(original_cwd)


if __name__ == '__main__':
unittest.main()
145 changes: 145 additions & 0 deletions test/test_git.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
"""Unit tests for GitClient checkout functionality"""

import os
import tarfile
import tempfile
import unittest

from vcs2l.clients.git import GitClient
from vcs2l.util import rmtree


class TestCheckout(unittest.TestCase):
"""Test cases for GitClient checkout method"""

def setUp(self):
# Create a temporary directory for testing
self.test_dir = tempfile.mkdtemp()
self.repo_path = os.path.join(self.test_dir, 'test_repo')

def tearDown(self):
if os.path.exists(self.test_dir):
rmtree(self.test_dir)

def test_checkout_specific_branch(self):
"""Test checking out a specific branch"""
client = GitClient(self.repo_path)

url = 'https://github.com/octocat/Hello-World.git'
success = client.checkout(url, version='test', shallow=True)

self.assertTrue(success, 'Checkout should succeed')
self.assertTrue(os.path.exists(self.repo_path), 'Repo directory should exist')
self.assertTrue(
os.path.isdir(os.path.join(self.repo_path, '.git')),
'Should be a git repository',
)

def test_checkout_nonexistent_repo(self):
"""Test checking out a non-existent repository should fail"""
client = GitClient(self.repo_path)

url = 'https://github.com/this/repo/does/not/exist.git'
success = client.checkout(url, verbose=True)

self.assertFalse(success, 'Checkout should fail for non-existent repo')

def test_checkout_to_existing_directory(self):
"""Test checking out to a non-empty directory should fail"""
# Create a non-empty directory
os.makedirs(self.repo_path)
with open(
os.path.join(self.repo_path, 'existing_file.txt'), 'w', encoding='utf-8'
) as f:
f.write('This file already exists')

client = GitClient(self.repo_path)
url = 'https://github.com/octocat/Hello-World.git'
success = client.checkout(url)

self.assertFalse(success, 'Checkout should fail for non-empty directory')


class TestExportRepository(unittest.TestCase):
"""Test cases for GitClient export_repository method"""

def setUp(self):
# Create a temporary directory for testing
self.test_dir = tempfile.mkdtemp()
self.repo_path = os.path.join(self.test_dir, 'test_repo')
self.export_dir = os.path.join(self.test_dir, 'exports')
os.makedirs(self.export_dir, exist_ok=True)

self.test_repo_url = 'https://github.com/octocat/Hello-World.git'
self.client = GitClient(self.repo_path)

def tearDown(self):
if os.path.exists(self.test_dir):
rmtree(self.test_dir)

def test_export_specific_branch(self):
"""Test exporting the repository at a specific branch"""
success = self.client.checkout(self.test_repo_url, version='test')

if not success:
self.fail('Failed to clone test repository')

basepath = os.path.join(self.export_dir, 'repo_export')
filepath = self.client.export_repository('test', basepath)

self.assertTrue(os.path.exists(filepath), 'Export file should exist')
self.assertGreater(
os.path.getsize(filepath), 0, 'Export file should not be empty'
)
# Verify it's a valid tar.gz file
try:
if filepath and isinstance(filepath, str):
with tarfile.open(filepath, 'r:gz') as tar:
members = tar.getnames()
self.assertIn('README', members, 'Should contain README file')
else:
self.fail('Exported file path is invalid')
except tarfile.ReadError:
self.fail('Exported file should be a valid tar.gz archive')

def test_export_with_local_changes_uses_temp_dir(self):
"""Test that export uses temp directory when there are local changes"""
# First clone the repository
success = self.client.checkout(self.test_repo_url)
if not success:
self.fail('Failed to clone test repository')

# Create a local change
test_file = os.path.join(self.repo_path, 'test_change.txt')
with open(test_file, 'w', encoding='utf-8') as f:
f.write('test change')

basepath = os.path.join(self.export_dir, 'local_changes_test')
filepath = self.client.export_repository(None, basepath)

self.assertIsNotNone(filepath, 'Export should succeed even with local changes')
self.assertTrue(os.path.exists(filepath), 'Export file should exist')
self.assertGreater(
os.path.getsize(filepath), 0, 'Export file should not be empty'
)

# Clean up
os.remove(test_file)

def test_export_invalid_branch(self):
"""Test exporting a non-existent branch should fail gracefully"""
success = self.client.checkout(self.test_repo_url)

if not success:
self.fail('Failed to clone test repository')

basepath = os.path.join(self.export_dir, 'nonexistent_export')
result = self.client.export_repository('nonexistent-branch-12345', basepath)

self.assertEqual(
result, False, 'Export should return False for non-existent ref'
)


if __name__ == '__main__':
unittest.main()
Loading
Loading