Skip to content

Commit e70b8e3

Browse files
Added basic tests for colcon mixin subverbs.
Signed-off-by: Leander Stephen D'Souza <leanderdsouza1234@gmail.com>
1 parent 83be597 commit e70b8e3

3 files changed

Lines changed: 192 additions & 0 deletions

File tree

test/expected_mixins/demo.txt

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
build:
2+
- coverage-gcc
3+
cmake-args: ["-DCMAKE_C_FLAGS='--coverage'", "-DCMAKE_CXX_FLAGS='--coverage'"]
4+
- coverage-pytest
5+
ament-cmake-args: ['-DAMENT_CMAKE_PYTEST_WITH_COVERAGE=ON']
6+
- debug
7+
cmake-args: ['-DCMAKE_BUILD_TYPE=Debug']
8+
- min-size-rel
9+
cmake-args: ['-DCMAKE_BUILD_TYPE=MinSizeRel']
10+
- rel-with-deb-info
11+
cmake-args: ['-DCMAKE_BUILD_TYPE=RelWithDebInfo']
12+
- release
13+
cmake-args: ['-DCMAKE_BUILD_TYPE=Release']
14+
test:
15+
- coverage-pytest
16+
pytest-args: ['--cov-report=term']
17+
pytest-with-coverage: True

test/spell_check.words

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,18 @@
11
apache
22
argcomplete
33
argparse
4+
autouse
45
basenames
56
basepath
67
blocklist
8+
capsys
79
colcon
810
completers
911
defaultdict
12+
desouza
1013
iterdir
14+
joinpath
15+
leander
1116
linter
1217
mixins
1318
nargs
@@ -17,15 +22,18 @@ plugin
1722
prepending
1823
pydocstyle
1924
pytest
25+
readouterr
2026
rtype
2127
scspell
2228
setuptools
2329
stacklevel
30+
stephen
2431
subparser
2532
subparsers
2633
subverb
2734
subverbs
2835
thomas
36+
unittest
2937
urllib
3038
urlopen
3139
urls

test/test_subverb.py

Lines changed: 167 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,167 @@
1+
# Copyright 2025 Leander Stephen Desouza
2+
# Licensed under the Apache License, Version 2.0
3+
4+
from argparse import ArgumentTypeError
5+
from pathlib import Path
6+
from unittest.mock import Mock
7+
8+
from colcon_core.command import CommandContext
9+
from colcon_core.location import set_default_config_path
10+
import pytest
11+
12+
set_default_config_path(path=Path('/tmp/colcon_mixin_test'))
13+
14+
# Import colcon-mixin modules after config path is set
15+
# flake8: noqa: E402
16+
from colcon_mixin.mixin.repository import set_repositories
17+
from colcon_mixin.subverb.add import (_non_empty_string_without_pathsep,
18+
_url_string, AddMixinSubverb)
19+
from colcon_mixin.subverb.remove import RemoveMixinSubverb
20+
from colcon_mixin.subverb.show import ShowMixinSubverb
21+
from colcon_mixin.subverb.update import UpdateMixinSubverb
22+
23+
24+
MIXIN_IDENTIFIER = 'demo'
25+
TEST_URL = Path(__file__).parent.joinpath('index.yaml').absolute().as_uri()
26+
27+
28+
@pytest.fixture(autouse=True)
29+
def mixin_cleanup():
30+
"""Cleanup mixin repositories before and after each test."""
31+
set_repositories({})
32+
yield
33+
set_repositories({})
34+
35+
36+
def test_add_success():
37+
"""Test successfully adding a mixin repository."""
38+
extension = AddMixinSubverb()
39+
context = CommandContext(command_name='colcon', args=Mock())
40+
41+
context.args.name = MIXIN_IDENTIFIER
42+
context.args.url = TEST_URL
43+
44+
result = extension.main(context=context)
45+
assert result is None
46+
47+
48+
def test_add_repository_duplicate_name():
49+
"""Test adding a repository with a name that already exists."""
50+
extension = AddMixinSubverb()
51+
52+
context1 = CommandContext(command_name='colcon', args=Mock())
53+
context1.args.name = MIXIN_IDENTIFIER
54+
context1.args.url = TEST_URL
55+
result1 = extension.main(context=context1)
56+
assert result1 is None
57+
58+
# Second add (duplicate)
59+
context2 = CommandContext(command_name='colcon', args=Mock())
60+
context2.args.name = MIXIN_IDENTIFIER
61+
context2.args.url = TEST_URL
62+
result2 = extension.main(context=context2)
63+
assert result2 == (
64+
f"A repository with the name '{MIXIN_IDENTIFIER}' already exists"
65+
)
66+
67+
68+
def test_add_repository_invalid_url():
69+
"""Test adding a repository with an invalid URL format."""
70+
with pytest.raises(ArgumentTypeError, match="must contain '://'"):
71+
_url_string('invalid-url')
72+
73+
74+
def test_add_repository_invalid_name():
75+
"""Test adding a repository with an invalid name."""
76+
# Forward slash test
77+
with pytest.raises(ArgumentTypeError, match="must not contain '/'"):
78+
_non_empty_string_without_pathsep(f'{MIXIN_IDENTIFIER}/invalid')
79+
80+
# Backslash test
81+
with pytest.raises(ArgumentTypeError, match="must not contain '\\\\'"):
82+
_non_empty_string_without_pathsep(f'{MIXIN_IDENTIFIER}\\invalid')
83+
84+
85+
def test_update_mixin(capsys):
86+
"""Test updating a mixin repository."""
87+
add_extension = AddMixinSubverb()
88+
add_context = CommandContext(command_name='colcon', args=Mock())
89+
add_context.args.name = MIXIN_IDENTIFIER
90+
add_context.args.url = TEST_URL
91+
add_extension.main(context=add_context)
92+
93+
update_extension = UpdateMixinSubverb()
94+
update_context = CommandContext(command_name='colcon', args=Mock())
95+
update_context.args.name = MIXIN_IDENTIFIER
96+
97+
result = update_extension.main(context=update_context)
98+
captured = capsys.readouterr()
99+
100+
assert result == 0
101+
assert f'fetching {MIXIN_IDENTIFIER}:' in captured.out
102+
assert 'build-type.mixin' in captured.out
103+
assert 'coverage.mixin' in captured.out
104+
105+
106+
def test_show_mixins(capsys):
107+
"""Test showing available mixins against expected files."""
108+
add_extension = AddMixinSubverb()
109+
add_context = CommandContext(command_name='colcon', args=Mock())
110+
add_context.args.name = MIXIN_IDENTIFIER
111+
add_context.args.url = TEST_URL
112+
add_extension.main(context=add_context)
113+
114+
update_extension = UpdateMixinSubverb()
115+
update_context = CommandContext(command_name='colcon', args=Mock())
116+
update_context.args.name = MIXIN_IDENTIFIER
117+
update_extension.main(context=update_context)
118+
119+
# Clear previous output from add/update
120+
capsys.readouterr()
121+
122+
show_extension = ShowMixinSubverb()
123+
show_extension.add_arguments(parser=Mock()) # Initialize mixins_by_verb
124+
show_context = CommandContext(command_name='colcon', args=Mock())
125+
126+
# Show all verbs and all mixins
127+
show_context.args.verb = None
128+
show_context.args.mixin_name = None
129+
130+
result = show_extension.main(context=show_context)
131+
captured = capsys.readouterr()
132+
133+
assert result is None
134+
135+
expected_demo_file = Path(__file__).parent.joinpath('expected_mixins', 'demo.txt')
136+
with expected_demo_file.open('r', encoding='utf-8') as f:
137+
expected_content = f.read().strip()
138+
139+
assert captured.out.strip() == expected_content
140+
141+
142+
def test_remove_repository_success():
143+
"""Test successfully removing a mixin repository."""
144+
add_extension = AddMixinSubverb()
145+
add_context = CommandContext(command_name='colcon', args=Mock())
146+
add_context.args.name = MIXIN_IDENTIFIER
147+
add_context.args.url = TEST_URL
148+
add_extension.main(context=add_context)
149+
150+
remove_extension = RemoveMixinSubverb()
151+
remove_context = CommandContext(command_name='colcon', args=Mock())
152+
remove_context.args.name = MIXIN_IDENTIFIER
153+
154+
result = remove_extension.main(context=remove_context)
155+
assert result is None
156+
157+
158+
def test_remove_repository_not_exists():
159+
"""Test removing a repository that doesn't exist."""
160+
extension = RemoveMixinSubverb()
161+
context = CommandContext(command_name='colcon', args=Mock())
162+
context.args.name = MIXIN_IDENTIFIER
163+
164+
result = extension.main(context=context)
165+
assert result == (
166+
f"A repository with the name '{MIXIN_IDENTIFIER}' doesn't exist"
167+
)

0 commit comments

Comments
 (0)