Skip to content

Commit 5c19fa3

Browse files
committed
Add tests for mixin referrals across mixin files
Document how add_mixins replaces a whole mixin when a later mixin file redefines an existing name, and what that means for the 'mixin' referral key: the referral is dropped, changed or added together with the rest of the mixin, an unrelated addition leaves it untouched, and a later file can supply a mixin that an earlier one already references. Assisted-by: Claude Opus (Antigravity) for test case generation and architecture-level testing
1 parent df953e1 commit 5c19fa3

1 file changed

Lines changed: 135 additions & 0 deletions

File tree

test/test_add_mixins_referral.py

Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
# Copyright 2026 Innocent Pious
2+
# Licensed under the Apache License, Version 2.0
3+
4+
"""Tests for how ``add_mixins`` merges the 'mixin' referral key.
5+
6+
When a second mixin file (e.g. from another mixin repository/location)
7+
defines a mixin whose name already exists, ``add_mixins`` replaces the whole
8+
mixin dict rather than merging individual keys. These tests document how that
9+
whole-mixin overwrite affects the 'mixin' referral key introduced for mixins
10+
referencing other mixins.
11+
"""
12+
13+
from collections import defaultdict
14+
15+
from colcon_mixin.mixin import add_mixins
16+
from colcon_mixin.mixin.order import compute_application_order
17+
18+
19+
def _write_mixin(tmp_path, name, content):
20+
path = tmp_path / name
21+
path.write_text(content)
22+
return path
23+
24+
25+
def _add(tmp_path, name, content):
26+
"""Add a mixin file to a fresh collection and return it."""
27+
mixins_by_verb = defaultdict(dict)
28+
add_mixins(_write_mixin(tmp_path, name, content), mixins_by_verb)
29+
return mixins_by_verb
30+
31+
32+
def test_referral_dropped_when_overwritten_without_mixin_key(tmp_path):
33+
# repo 1 defines 'child' referencing 'base'
34+
mixins_by_verb = _add(tmp_path, 'repo1.mixin', """\
35+
build:
36+
base:
37+
cmake-args: ['FOO=BASE']
38+
child:
39+
mixin: ['base']
40+
cmake-args: ['FOO=CHILD1']
41+
""")
42+
# repo 2 redefines 'child' WITHOUT the referral
43+
add_mixins(_write_mixin(tmp_path, 'repo2.mixin', """\
44+
build:
45+
child:
46+
cmake-args: ['FOO=CHILD2']
47+
"""), mixins_by_verb)
48+
49+
child = mixins_by_verb[('build',)]['child']
50+
# the whole mixin was replaced, so the referral is gone
51+
assert 'mixin' not in child
52+
assert child['cmake-args'] == ['FOO=CHILD2']
53+
# and the application order no longer pulls in 'base'
54+
assert compute_application_order(
55+
mixins_by_verb[('build',)], 'child') == ['child']
56+
57+
58+
def test_referral_changed_when_overwritten_with_different_targets(tmp_path):
59+
# repo 1: child -> [base_a]
60+
mixins_by_verb = _add(tmp_path, 'repo1.mixin', """\
61+
build:
62+
base_a: {cmake-args: ['FOO=A']}
63+
base_b: {cmake-args: ['FOO=B']}
64+
child:
65+
mixin: ['base_a']
66+
""")
67+
# repo 2 redefines child -> [base_b]
68+
add_mixins(_write_mixin(tmp_path, 'repo2.mixin', """\
69+
build:
70+
child:
71+
mixin: ['base_b']
72+
"""), mixins_by_verb)
73+
74+
assert mixins_by_verb[('build',)]['child']['mixin'] == ['base_b']
75+
assert compute_application_order(
76+
mixins_by_verb[('build',)], 'child') == ['base_b', 'child']
77+
78+
79+
def test_referral_added_when_overwriting_plain_mixin(tmp_path):
80+
# repo 1: child has no referral
81+
mixins_by_verb = _add(tmp_path, 'repo1.mixin', """\
82+
build:
83+
base: {cmake-args: ['FOO=BASE']}
84+
child: {cmake-args: ['FOO=CHILD1']}
85+
""")
86+
# repo 2 redefines child to reference base
87+
add_mixins(_write_mixin(tmp_path, 'repo2.mixin', """\
88+
build:
89+
child:
90+
mixin: ['base']
91+
cmake-args: ['FOO=CHILD2']
92+
"""), mixins_by_verb)
93+
94+
assert mixins_by_verb[('build',)]['child']['mixin'] == ['base']
95+
assert compute_application_order(
96+
mixins_by_verb[('build',)], 'child') == ['base', 'child']
97+
98+
99+
def test_referral_survives_when_second_repo_adds_unrelated_mixin(tmp_path):
100+
# repo 1 defines child -> [base]
101+
mixins_by_verb = _add(tmp_path, 'repo1.mixin', """\
102+
build:
103+
base: {cmake-args: ['FOO=BASE']}
104+
child:
105+
mixin: ['base']
106+
cmake-args: ['FOO=CHILD1']
107+
""")
108+
# repo 2 only adds a new, unrelated mixin
109+
add_mixins(_write_mixin(tmp_path, 'repo2.mixin', """\
110+
build:
111+
extra: {cmake-args: ['FOO=EXTRA']}
112+
"""), mixins_by_verb)
113+
114+
# the existing referral is untouched
115+
assert mixins_by_verb[('build',)]['child']['mixin'] == ['base']
116+
assert compute_application_order(
117+
mixins_by_verb[('build',)], 'child') == ['base', 'child']
118+
119+
120+
def test_second_repo_can_supply_the_referenced_mixin(tmp_path):
121+
# repo 1 references a 'base' that does not exist yet
122+
mixins_by_verb = _add(tmp_path, 'repo1.mixin', """\
123+
build:
124+
child:
125+
mixin: ['base']
126+
cmake-args: ['FOO=CHILD1']
127+
""")
128+
# repo 2 provides the referenced mixin
129+
add_mixins(_write_mixin(tmp_path, 'repo2.mixin', """\
130+
build:
131+
base: {cmake-args: ['FOO=BASE']}
132+
"""), mixins_by_verb)
133+
134+
assert compute_application_order(
135+
mixins_by_verb[('build',)], 'child') == ['base', 'child']

0 commit comments

Comments
 (0)