Coverage for colour/utilities/tests/test_structures.py: 100%
198 statements
« prev ^ index » next coverage.py v7.11.0, created at 2025-11-15 19:01 +1300
« prev ^ index » next coverage.py v7.11.0, created at 2025-11-15 19:01 +1300
1"""Define the unit tests for the :mod:`colour.utilities.structures` module."""
3from __future__ import annotations
5import operator
6import pickle
8import numpy as np
9import pytest
11from colour.utilities import (
12 CanonicalMapping,
13 ColourUsageWarning,
14 LazyCanonicalMapping,
15 Lookup,
16 Structure,
17)
19__author__ = "Colour Developers"
20__copyright__ = "Copyright 2013 Colour Developers"
21__license__ = "BSD-3-Clause - https://opensource.org/licenses/BSD-3-Clause"
22__maintainer__ = "Colour Developers"
23__email__ = "colour-developers@colour-science.org"
24__status__ = "Production"
26__all__ = [
27 "TestStructure",
28 "TestLookup",
29 "TestCanonicalMapping",
30 "TestLazyCanonicalMapping",
31]
34class TestStructure:
35 """
36 Define :class:`colour.utilities.structures.Structure` class unit
37 tests methods.
38 """
40 def test_Structure(self) -> None:
41 """Test the :class:`colour.utilities.structures.Structure` class."""
43 structure = Structure(John="Doe", Jane="Doe")
44 assert "John" in structure
45 assert hasattr(structure, "John")
47 structure.John = "Nemo"
48 assert structure["John"] == "Nemo"
50 structure["John"] = "Vador"
51 assert structure["John"] == "Vador"
53 del structure["John"]
54 assert "John" not in structure
55 assert not hasattr(structure, "John")
57 structure.John = "Doe"
58 assert "John" in structure
59 assert hasattr(structure, "John")
61 del structure.John
62 assert "John" not in structure
63 assert not hasattr(structure, "John")
65 structure = Structure(John=None, Jane=None)
66 assert structure.John is None
67 assert structure["John"] is None
69 structure.update(John="Doe", Jane="Doe")
70 assert structure.John == "Doe"
71 assert structure["John"] == "Doe"
73 def test_pickling(self) -> None:
74 """
75 Test whether :class:`colour.utilities.structures.Structure` class
76 can be pickled.
77 """
79 structure = Structure(John="Doe", Jane="Doe")
81 data = pickle.dumps(structure)
82 data = pickle.loads(data) # noqa: S301
83 assert structure == data
85 data = pickle.dumps(structure, pickle.HIGHEST_PROTOCOL)
86 data = pickle.loads(data) # noqa: S301
87 assert structure == data
89 assert sorted(dir(data)) == ["Jane", "John"]
92class TestLookup:
93 """
94 Define :class:`colour.utilities.structures.Lookup` class unit tests
95 methods.
96 """
98 def test_required_methods(self) -> None:
99 """Test the presence of required methods."""
101 required_methods = ("keys_from_value", "first_key_from_value")
103 for method in required_methods:
104 assert method in dir(Lookup)
106 def test_keys_from_value(self) -> None:
107 """
108 Test :meth:`colour.utilities.structures.Lookup.keys_from_value`
109 method.
110 """
112 lookup = Lookup(John="Doe", Jane="Doe", Luke="Skywalker")
113 assert sorted(lookup.keys_from_value("Doe")) == ["Jane", "John"]
115 lookup = Lookup(
116 A=np.array([0, 1, 2]), B=np.array([0, 1, 2]), C=np.array([1, 2, 3])
117 )
118 assert sorted(lookup.keys_from_value(np.array([0, 1, 2]))) == ["A", "B"]
120 def test_first_key_from_value(self) -> None:
121 """
122 Test :meth:`colour.utilities.structures.\
123Lookup.first_key_from_value` method.
124 """
126 lookup = Lookup(first_name="John", last_name="Doe", gender="male")
127 assert lookup.first_key_from_value("John") == "first_name"
129 lookup = Lookup(
130 A=np.array([0, 1, 2]), B=np.array([1, 2, 3]), C=np.array([2, 3, 4])
131 )
132 assert lookup.first_key_from_value(np.array([0, 1, 2])) == "A"
134 def test_raise_exception_first_key_from_value(self) -> None:
135 """
136 Test :meth:`colour.utilities.structures.\
137Lookup.first_key_from_value` method raised exception.
138 """
140 pytest.raises(IndexError, Lookup().first_key_from_value, "John")
143class TestCanonicalMapping:
144 """
145 Define :class:`colour.utilities.structures.CanonicalMapping` class
146 unit tests methods.
147 """
149 def test_required_attributes(self) -> None:
150 """Test the presence of required attributes."""
152 required_attributes = ("data",)
154 for attribute in required_attributes:
155 assert attribute in dir(CanonicalMapping)
157 def test_required_methods(self) -> None:
158 """Test the presence of required methods."""
160 required_methods = (
161 "__init__",
162 "__repr__",
163 "__setitem__",
164 "__getitem__",
165 "__delitem__",
166 "__contains__",
167 "__iter__",
168 "__len__",
169 "__eq__",
170 "__ne__",
171 "copy",
172 "lower_keys",
173 "lower_items",
174 "slugified_keys",
175 "slugified_items",
176 "canonical_keys",
177 "canonical_items",
178 )
180 for method in required_methods:
181 assert method in dir(CanonicalMapping)
183 def test_data(self) -> None:
184 """
185 Test :meth:`colour.utilities.structures.CanonicalMapping.data`
186 property.
187 """
189 assert CanonicalMapping({"John": "Doe", "Jane": "Doe"}).data == {
190 "John": "Doe",
191 "Jane": "Doe",
192 }
194 def test__repr__(self) -> None:
195 """
196 Test :meth:`colour.utilities.structures.CanonicalMapping.__repr__`
197 method.
198 """
200 mapping = CanonicalMapping()
202 mapping["John"] = "Doe"
203 assert repr(mapping) == "CanonicalMapping({'John': 'Doe'})"
205 def test__setitem__(self) -> None:
206 """
207 Test :meth:`colour.utilities.structures.CanonicalMapping.\
208__setitem__` method.
209 """
211 mapping = CanonicalMapping()
213 mapping["John"] = "Doe"
214 assert mapping["John"] == "Doe"
215 assert mapping["john"] == "Doe"
217 def test__getitem__(self) -> None:
218 """
219 Test :meth:`colour.utilities.structures.CanonicalMapping.\
220__getitem__` method.
221 """
223 mapping = CanonicalMapping(John="Doe", Jane="Doe")
225 assert mapping["John"] == "Doe"
226 assert mapping["john"] == "Doe"
227 assert mapping["JOHN"] == "Doe"
228 assert mapping["Jane"] == "Doe"
229 assert mapping["jane"] == "Doe"
230 assert mapping["JANE"] == "Doe"
232 mapping = CanonicalMapping({1: "Foo", 2: "Bar"})
234 assert mapping[1] == "Foo"
236 mapping = CanonicalMapping({"McCamy 1992": 1, "Hernandez 1999": 2})
238 assert mapping["mccamy-1992"] == 1
239 assert mapping["hernandez-1999"] == 2
240 assert mapping["mccamy1992"] == 1
241 assert mapping["hernandez1999"] == 2
243 def test__delitem__(self) -> None:
244 """
245 Test :meth:`colour.utilities.structures.CanonicalMapping.\
246__delitem__` method.
247 """
249 mapping = CanonicalMapping(John="Doe", Jane="Doe")
251 del mapping["john"]
252 assert "John" not in mapping
254 del mapping["Jane"]
255 assert "jane" not in mapping
256 assert len(mapping) == 0
258 mapping = CanonicalMapping(John="Doe", Jane="Doe")
259 del mapping["JOHN"]
260 assert "John" not in mapping
262 del mapping["jane"]
263 assert "jane" not in mapping
264 assert len(mapping) == 0
266 mapping = CanonicalMapping({"McCamy 1992": 1, "Hernandez 1999": 2})
268 del mapping["mccamy-1992"]
269 assert "McCamy 1992" not in mapping
271 del mapping["hernandez-1999"]
272 assert "Hernandez 1999" not in mapping
274 assert len(mapping) == 0
276 mapping = CanonicalMapping({"McCamy 1992": 1, "Hernandez 1999": 2})
278 del mapping["mccamy1992"]
279 assert "McCamy 1992" not in mapping
281 del mapping["hernandez1999"]
282 assert "Hernandez 1999" not in mapping
284 assert len(mapping) == 0
286 def test__contains__(self) -> None:
287 """
288 Test :meth:`colour.utilities.structures.CanonicalMapping.\
289__contains__` method.
290 """
292 mapping = CanonicalMapping(John="Doe", Jane="Doe")
294 assert "John" in mapping
295 assert "john" in mapping
296 assert "JOHN" in mapping
297 assert "Jane" in mapping
298 assert "jane" in mapping
299 assert "JANE" in mapping
301 mapping = CanonicalMapping({"McCamy 1992": 1, "Hernandez 1999": 2})
303 assert "mccamy-1992" in mapping
304 assert "hernandez-1999" in mapping
305 assert "mccamy1992" in mapping
306 assert "hernandez1999" in mapping
308 def test__iter__(self) -> None:
309 """
310 Test :meth:`colour.utilities.structures.CanonicalMapping.__iter__`
311 method.
312 """
314 mapping = CanonicalMapping(John="Doe", Jane="Doe")
315 assert sorted(item for item in mapping) == ["Jane", "John"]
317 def test__len__(self) -> None:
318 """
319 Test :meth:`colour.utilities.structures.CanonicalMapping.__len__`
320 method.
321 """
323 assert len(CanonicalMapping()) == 0
325 assert len(CanonicalMapping(John="Doe", Jane="Doe")) == 2
327 def test__eq__(self) -> None:
328 """
329 Test :meth:`colour.utilities.structures.CanonicalMapping.__eq__`
330 method.
331 """
333 mapping1 = CanonicalMapping(John="Doe", Jane="Doe")
334 mapping2 = CanonicalMapping(John="Doe", Jane="Doe")
335 mapping3 = CanonicalMapping(john="Doe", jane="Doe")
337 assert mapping1 == mapping2
339 assert mapping2 != mapping3
341 def test_raise_exception__eq__(self) -> None:
342 """
343 Test :meth:`colour.utilities.structures.CanonicalMapping.__eq__`
344 method raised exception.
345 """
347 pytest.raises(
348 TypeError,
349 operator.eq,
350 CanonicalMapping(John="Doe", Jane="Doe"),
351 ["John", "Doe", "Jane", "Doe"],
352 )
354 def test__ne__(self) -> None:
355 """
356 Test :meth:`colour.utilities.structures.CanonicalMapping.__ne__`
357 method.
358 """
360 mapping1 = CanonicalMapping(John="Doe", Jane="Doe")
361 mapping2 = CanonicalMapping(Gi="Doe", Jane="Doe")
363 assert mapping1 != mapping2
365 def test_raise_exception__ne__(self) -> None:
366 """
367 Test :meth:`colour.utilities.structures.CanonicalMapping.__ne__`
368 method raised exception.
369 """
371 pytest.raises(
372 TypeError,
373 operator.ne,
374 CanonicalMapping(John="Doe", Jane="Doe"),
375 ["John", "Doe", "Jane", "Doe"],
376 )
378 def test_copy(self) -> None:
379 """
380 Test :meth:`colour.utilities.structures.CanonicalMapping.copy`
381 method.
382 """
384 mapping1 = CanonicalMapping(John="Doe", Jane="Doe")
385 mapping2 = mapping1.copy()
387 assert mapping1 == mapping2
389 assert id(mapping1) != id(mapping2)
391 def test_lower_keys(self) -> None:
392 """
393 Test :meth:`colour.utilities.structures.CanonicalMapping.\
394lower_keys` method.
395 """
397 mapping = CanonicalMapping(John="Doe", Jane="Doe")
399 assert sorted(item for item in mapping.lower_keys()) == ["jane", "john"]
401 mapping = CanonicalMapping(John="Doe", john="Doe")
403 pytest.warns(ColourUsageWarning, lambda: list(mapping.lower_keys()))
405 def test_lower_items(self) -> None:
406 """
407 Test :meth:`colour.utilities.structures.CanonicalMapping.\
408lower_items` method.
409 """
411 mapping = CanonicalMapping(John="Doe", Jane="Doe")
413 assert sorted(item for item in mapping.lower_items()) == [
414 ("jane", "Doe"),
415 ("john", "Doe"),
416 ]
418 def test_slugified_keys(self) -> None:
419 """
420 Test :meth:`colour.utilities.structures.CanonicalMapping.\
421slugified_keys` method.
422 """
424 mapping = CanonicalMapping({"McCamy 1992": 1, "Hernandez 1999": 2})
426 assert sorted(item for item in mapping.slugified_keys()) == [
427 "hernandez-1999",
428 "mccamy-1992",
429 ]
431 mapping = CanonicalMapping({"McCamy 1992": 1, "McCamy-1992": 2})
433 pytest.warns(ColourUsageWarning, lambda: list(mapping.slugified_keys()))
435 def test_slugified_items(self) -> None:
436 """
437 Test :meth:`colour.utilities.structures.CanonicalMapping.\
438slugified_items` method.
439 """
441 mapping = CanonicalMapping({"McCamy 1992": 1, "Hernandez 1999": 2})
442 assert sorted(item for item in mapping.slugified_items()) == [
443 ("hernandez-1999", 2),
444 ("mccamy-1992", 1),
445 ]
447 def test_canonical_keys(self) -> None:
448 """
449 Test :meth:`colour.utilities.structures.CanonicalMapping.\
450canonical_keys` method.
451 """
453 mapping = CanonicalMapping({"McCamy 1992": 1, "Hernandez 1999": 2})
455 assert sorted(item for item in mapping.canonical_keys()) == [
456 "hernandez1999",
457 "mccamy1992",
458 ]
460 mapping = CanonicalMapping({"McCamy_1992": 1, "McCamy-1992": 2})
462 pytest.warns(ColourUsageWarning, lambda: list(mapping.canonical_keys()))
464 def test_canonical_items(self) -> None:
465 """
466 Test :meth:`colour.utilities.structures.CanonicalMapping.\
467canonical_items` method.
468 """
470 mapping = CanonicalMapping({"McCamy 1992": 1, "Hernandez 1999": 2})
471 assert sorted(item for item in mapping.canonical_items()) == [
472 ("hernandez1999", 2),
473 ("mccamy1992", 1),
474 ]
477class TestLazyCanonicalMapping:
478 """
479 Define :class:`colour.utilities.structures.LazyCanonicalMapping` class
480 unit tests methods.
481 """
483 def test_required_attributes(self) -> None:
484 """Test the presence of required attributes."""
486 required_attributes = ()
488 for attribute in required_attributes: # pragma: no cover
489 assert attribute in dir(LazyCanonicalMapping)
491 def test_required_methods(self) -> None:
492 """Test the presence of required methods."""
494 required_methods = ("__getitem__",)
496 for method in required_methods:
497 assert method in dir(LazyCanonicalMapping)
499 def test__getitem__(self) -> None:
500 """
501 Test :meth:`colour.utilities.structures.LazyCanonicalMapping.\
502__getitem__` method.
503 """
505 mapping = LazyCanonicalMapping(John="Doe", Jane=lambda: "Doe")
507 assert mapping["John"] == "Doe"
508 assert mapping["john"] == "Doe"
509 assert mapping["Jane"] == "Doe"
510 assert mapping["jane"] == "Doe"