Coverage for colour/utilities/tests/test_array.py: 100%

645 statements  

« prev     ^ index     » next       coverage.py v7.11.0, created at 2025-11-16 23:01 +1300

1"""Define the unit tests for the :mod:`colour.utilities.array` module.""" 

2 

3from __future__ import annotations 

4 

5import typing 

6import unittest 

7from copy import deepcopy 

8from dataclasses import dataclass, field, fields 

9from functools import partial 

10 

11import numpy as np 

12import pytest 

13 

14from colour.constants import ( 

15 DTYPE_COMPLEX_DEFAULT, 

16 DTYPE_FLOAT_DEFAULT, 

17 DTYPE_INT_DEFAULT, 

18 TOLERANCE_ABSOLUTE_TESTS, 

19) 

20 

21if typing.TYPE_CHECKING: 

22 from colour.hints import ( 

23 Annotated, 

24 Any, 

25 ArrayLike, 

26 Domain1, 

27 Domain10, 

28 Domain100, 

29 Domain100_100_360, 

30 Domain360, 

31 DType, 

32 NDArray, 

33 NDArrayFloat, 

34 Range1, 

35 Range10, 

36 Range100, 

37 Range100_100_360, 

38 Range360, 

39 Type, 

40 ) 

41else: 

42 # Import Annotated at runtime for test helper function signatures 

43 # get_domain_range_scale_metadata() needs to access Annotated.__metadata__ 

44 from colour.hints import ( # noqa: TC001 

45 Annotated, 

46 Any, 

47 ArrayLike, 

48 Domain1, 

49 Domain10, 

50 Domain100, 

51 Domain360, 

52 Domain100_100_360, 

53 NDArrayFloat, 

54 Range1, 

55 Range10, 

56 Range100, 

57 Range360, 

58 Range100_100_360, 

59 ) 

60 

61from colour.utilities import ( 

62 MixinDataclassArithmetic, 

63 MixinDataclassArray, 

64 MixinDataclassFields, 

65 MixinDataclassIterable, 

66 as_array, 

67 as_complex_array, 

68 as_float, 

69 as_float_array, 

70 as_float_scalar, 

71 as_int, 

72 as_int_array, 

73 as_int_scalar, 

74 centroid, 

75 closest, 

76 closest_indexes, 

77 domain_range_scale, 

78 fill_nan, 

79 format_array_as_row, 

80 from_range_1, 

81 from_range_10, 

82 from_range_100, 

83 from_range_degrees, 

84 from_range_int, 

85 full, 

86 get_domain_range_scale, 

87 get_domain_range_scale_metadata, 

88 has_only_nan, 

89 in_array, 

90 index_along_last_axis, 

91 interval, 

92 is_ndarray_copy_enabled, 

93 is_networkx_installed, 

94 is_scipy_installed, 

95 is_uniform, 

96 ndarray_copy, 

97 ndarray_copy_enable, 

98 ndarray_write, 

99 ones, 

100 orient, 

101 row_as_diagonal, 

102 set_default_float_dtype, 

103 set_default_int_dtype, 

104 set_domain_range_scale, 

105 set_ndarray_copy_enable, 

106 to_domain_1, 

107 to_domain_10, 

108 to_domain_100, 

109 to_domain_degrees, 

110 to_domain_int, 

111 tsplit, 

112 tstack, 

113 zeros, 

114) 

115 

116__author__ = "Colour Developers" 

117__copyright__ = "Copyright 2013 Colour Developers" 

118__license__ = "BSD-3-Clause - https://opensource.org/licenses/BSD-3-Clause" 

119__maintainer__ = "Colour Developers" 

120__email__ = "colour-developers@colour-science.org" 

121__status__ = "Production" 

122 

123__all__ = [ 

124 "TestMixinDataclassFields", 

125 "TestMixinDataclassIterable", 

126 "TestMixinDataclassArray", 

127 "TestMixinDataclassArithmetic", 

128 "TestAsArray", 

129 "TestAsInt", 

130 "TestAsFloat", 

131 "TestAsIntArray", 

132 "TestAsFloatArray", 

133 "TestAsComplexArray", 

134 "TestAsIntScalar", 

135 "TestAsFloatScalar", 

136 "TestSetDefaultIntegerDtype", 

137 "TestSetDefaultFloatDtype", 

138 "TestGetDomainRangeScale", 

139 "TestSetDomainRangeScale", 

140 "TestDomainRangeScale", 

141 "TestGetDomainRangeScaleMetadata", 

142 "TestToDomain1", 

143 "TestToDomain10", 

144 "TestToDomain100", 

145 "TestToDomainDegrees", 

146 "TestToDomainInt", 

147 "TestFromRange1", 

148 "TestFromRange10", 

149 "TestFromRange100", 

150 "TestFromRangeDegrees", 

151 "TestFromRangeInt", 

152 "TestIsNdarrayCopyEnabled", 

153 "TestSetNdarrayCopyEnabled", 

154 "TestNdarrayCopyEnable", 

155 "TestNdarrayCopy", 

156 "TestClosestIndexes", 

157 "TestClosest", 

158 "TestInterval", 

159 "TestIsUniform", 

160 "TestInArray", 

161 "TestTstack", 

162 "TestTsplit", 

163 "TestRowAsDiagonal", 

164 "TestOrient", 

165 "TestCentroid", 

166 "TestFillNan", 

167 "TestHasNanOnly", 

168 "TestNdarrayWrite", 

169 "TestZeros", 

170 "TestOnes", 

171 "TestFull", 

172 "TestIndexAlongLastAxis", 

173] 

174 

175 

176class TestMixinDataclassFields(unittest.TestCase): 

177 """ 

178 Define :class:`colour.utilities.array.MixinDataclassFields` class unit 

179 tests methods. 

180 """ 

181 

182 def setUp(self) -> None: 

183 """Initialise the common tests attributes.""" 

184 

185 @dataclass 

186 class Data(MixinDataclassFields): 

187 a: str 

188 b: str 

189 c: str 

190 

191 self._data: Data = Data(a="Foo", b="Bar", c="Baz") 

192 

193 def test_required_attributes(self) -> None: 

194 """Test the presence of required attributes.""" 

195 

196 required_attributes = ("fields",) 

197 

198 for method in required_attributes: 

199 assert method in dir(MixinDataclassFields) 

200 

201 def test_fields(self) -> None: 

202 """ 

203 Test :meth:`colour.utilities.array.MixinDataclassIterable._fields` 

204 method. 

205 """ 

206 

207 assert self._data.fields == fields(self._data) 

208 

209 

210class TestMixinDataclassIterable(unittest.TestCase): 

211 """ 

212 Define :class:`colour.utilities.array.MixinDataclassIterable` class unit 

213 tests methods. 

214 """ 

215 

216 def setUp(self) -> None: 

217 """Initialise the common tests attributes.""" 

218 

219 @dataclass 

220 class Data(MixinDataclassIterable): 

221 a: str 

222 b: str 

223 c: str 

224 

225 self._data: Data = Data(a="Foo", b="Bar", c="Baz") 

226 

227 def test_required_attributes(self) -> None: 

228 """Test the presence of required attributes.""" 

229 

230 required_attributes = ( 

231 "keys", 

232 "values", 

233 "items", 

234 ) 

235 

236 for method in required_attributes: 

237 assert method in dir(MixinDataclassIterable) 

238 

239 def test_required_methods(self) -> None: 

240 """Test the presence of required methods.""" 

241 

242 required_methods = ("__iter__",) 

243 

244 for method in required_methods: 

245 assert method in dir(MixinDataclassIterable) 

246 

247 def test__iter__(self) -> None: 

248 """ 

249 Test :meth:`colour.utilities.array.MixinDataclassIterable.__iter__` 

250 method. 

251 """ 

252 

253 assert {key: value for key, value in self._data} == ( 

254 {"a": "Foo", "b": "Bar", "c": "Baz"} 

255 ) 

256 

257 def test_keys(self) -> None: 

258 """ 

259 Test :meth:`colour.utilities.array.MixinDataclassIterable.keys` 

260 method. 

261 """ 

262 

263 assert tuple(self._data.keys) == ("a", "b", "c") 

264 

265 def test_values(self) -> None: 

266 """ 

267 Test :meth:`colour.utilities.array.MixinDataclassIterable.values` 

268 method. 

269 """ 

270 

271 assert tuple(self._data.values) == ("Foo", "Bar", "Baz") 

272 

273 def test_items(self) -> None: 

274 """ 

275 Test :meth:`colour.utilities.array.MixinDataclassIterable.items` 

276 method. 

277 """ 

278 

279 assert tuple(self._data.items) == (("a", "Foo"), ("b", "Bar"), ("c", "Baz")) 

280 

281 

282class TestMixinDataclassArray(unittest.TestCase): 

283 """ 

284 Define :class:`colour.utilities.array.MixinDataclassArray` class unit 

285 tests methods. 

286 """ 

287 

288 def setUp(self) -> None: 

289 """Initialise the common tests attributes.""" 

290 

291 @dataclass 

292 class Data(MixinDataclassArray): 

293 a: float | list | tuple | np.ndarray | None = field( 

294 default_factory=lambda: None 

295 ) 

296 

297 b: float | list | tuple | np.ndarray | None = field( 

298 default_factory=lambda: None 

299 ) 

300 

301 c: float | list | tuple | np.ndarray | None = field( 

302 default_factory=lambda: None 

303 ) 

304 

305 self._data: Data = Data( 

306 b=np.array([0.1, 0.2, 0.3]), c=np.array([0.4, 0.5, 0.6]) 

307 ) 

308 self._array: NDArray = np.array( 

309 [ 

310 [np.nan, 0.1, 0.4], 

311 [np.nan, 0.2, 0.5], 

312 [np.nan, 0.3, 0.6], 

313 ] 

314 ) 

315 

316 def test_required_methods(self) -> None: 

317 """Test the presence of required methods.""" 

318 

319 required_methods = ("__array__",) 

320 

321 for method in required_methods: 

322 assert method in dir(MixinDataclassArray) 

323 

324 def test__array__(self) -> None: 

325 """ 

326 Test :meth:`colour.utilities.array.MixinDataclassArray.__array__` 

327 method. 

328 """ 

329 

330 np.testing.assert_array_equal(self._data, self._array) 

331 

332 assert np.array(self._data, dtype=DTYPE_INT_DEFAULT).dtype == DTYPE_INT_DEFAULT 

333 

334 

335class TestMixinDataclassArithmetic(unittest.TestCase): 

336 """ 

337 Define :class:`colour.utilities.array.MixinDataclassArithmetic` class unit 

338 tests methods. 

339 """ 

340 

341 def setUp(self) -> None: 

342 """Initialise the common tests attributes.""" 

343 

344 @dataclass 

345 class Data(MixinDataclassArithmetic): 

346 a: float | list | tuple | np.ndarray | None = field( 

347 default_factory=lambda: None 

348 ) 

349 

350 b: float | list | tuple | np.ndarray | None = field( 

351 default_factory=lambda: None 

352 ) 

353 

354 c: float | list | tuple | np.ndarray | None = field( 

355 default_factory=lambda: None 

356 ) 

357 

358 self._factory: Type[Data] = Data 

359 self._data: Data = Data( 

360 b=np.array([0.1, 0.2, 0.3]), c=np.array([0.4, 0.5, 0.6]) 

361 ) 

362 self._array: NDArray = np.array( 

363 [ 

364 [np.nan, 0.1, 0.4], 

365 [np.nan, 0.2, 0.5], 

366 [np.nan, 0.3, 0.6], 

367 ] 

368 ) 

369 

370 def test_required_methods(self) -> None: 

371 """Test the presence of required methods.""" 

372 

373 required_methods = ( 

374 "__iadd__", 

375 "__add__", 

376 "__isub__", 

377 "__sub__", 

378 "__imul__", 

379 "__mul__", 

380 "__idiv__", 

381 "__div__", 

382 "__ipow__", 

383 "__pow__", 

384 "arithmetical_operation", 

385 ) 

386 

387 for method in required_methods: 

388 assert method in dir(MixinDataclassArithmetic) 

389 

390 def test_arithmetical_operation(self) -> None: 

391 """ 

392 Test :meth:`colour.utilities.array.MixinDataclassArithmetic.\ 

393arithmetical_operation` method. 

394 """ 

395 

396 np.testing.assert_allclose( 

397 self._data.arithmetical_operation(10, "+", False), 

398 self._array + 10, 

399 atol=TOLERANCE_ABSOLUTE_TESTS, 

400 ) 

401 

402 np.testing.assert_allclose( 

403 self._data.arithmetical_operation(10, "-", False), 

404 self._array - 10, 

405 atol=TOLERANCE_ABSOLUTE_TESTS, 

406 ) 

407 

408 np.testing.assert_allclose( 

409 self._data.arithmetical_operation(10, "*", False), 

410 self._array * 10, 

411 atol=TOLERANCE_ABSOLUTE_TESTS, 

412 ) 

413 

414 np.testing.assert_allclose( 

415 self._data.arithmetical_operation(10, "/", False), 

416 self._array / 10, 

417 atol=TOLERANCE_ABSOLUTE_TESTS, 

418 ) 

419 

420 np.testing.assert_allclose( 

421 self._data.arithmetical_operation(10, "**", False), 

422 self._array**10, 

423 atol=TOLERANCE_ABSOLUTE_TESTS, 

424 ) 

425 

426 np.testing.assert_allclose( 

427 self._data + 10, 

428 self._array + 10, 

429 atol=TOLERANCE_ABSOLUTE_TESTS, 

430 ) 

431 

432 np.testing.assert_allclose( 

433 self._data - 10, 

434 self._array - 10, 

435 atol=TOLERANCE_ABSOLUTE_TESTS, 

436 ) 

437 

438 np.testing.assert_allclose( 

439 self._data * 10, 

440 self._array * 10, 

441 atol=TOLERANCE_ABSOLUTE_TESTS, 

442 ) 

443 

444 np.testing.assert_allclose( 

445 self._data / 10, 

446 self._array / 10, 

447 atol=TOLERANCE_ABSOLUTE_TESTS, 

448 ) 

449 

450 np.testing.assert_allclose( 

451 self._data**10, 

452 self._array**10, 

453 atol=TOLERANCE_ABSOLUTE_TESTS, 

454 ) 

455 

456 data = deepcopy(self._data) 

457 

458 np.testing.assert_allclose( 

459 data.arithmetical_operation(10, "+", True), 

460 self._array + 10, 

461 atol=TOLERANCE_ABSOLUTE_TESTS, 

462 ) 

463 

464 np.testing.assert_allclose( 

465 data.arithmetical_operation(10, "-", True), 

466 self._array, 

467 atol=TOLERANCE_ABSOLUTE_TESTS, 

468 ) 

469 

470 np.testing.assert_allclose( 

471 data.arithmetical_operation(10, "*", True), 

472 self._array * 10, 

473 atol=TOLERANCE_ABSOLUTE_TESTS, 

474 ) 

475 

476 np.testing.assert_allclose( 

477 data.arithmetical_operation(10, "/", True), 

478 self._array, 

479 atol=TOLERANCE_ABSOLUTE_TESTS, 

480 ) 

481 

482 np.testing.assert_allclose( 

483 data.arithmetical_operation(10, "**", True), 

484 self._array**10, 

485 atol=TOLERANCE_ABSOLUTE_TESTS, 

486 ) 

487 

488 data = deepcopy(self._data) 

489 

490 np.testing.assert_allclose( 

491 data.arithmetical_operation(self._array, "+", False), 

492 data + self._array, 

493 atol=TOLERANCE_ABSOLUTE_TESTS, 

494 ) 

495 

496 np.testing.assert_allclose( 

497 data.arithmetical_operation(data, "+", False), 

498 data + data, 

499 atol=TOLERANCE_ABSOLUTE_TESTS, 

500 ) 

501 

502 data = self._factory(1, 2, 3) 

503 

504 data += 1 

505 assert data.a == 2 

506 

507 data -= 1 

508 assert data.a == 1 

509 

510 data *= 2 

511 assert data.a == 2 

512 

513 data /= 2 

514 assert data.a == 1 

515 

516 data **= 0.5 

517 assert data.a == 1 

518 

519 

520class TestAsArray(unittest.TestCase): 

521 """ 

522 Define :func:`colour.utilities.array.as_array` definition unit tests 

523 methods. 

524 """ 

525 

526 def test_as_array(self) -> None: 

527 """Test :func:`colour.utilities.array.as_array` definition.""" 

528 

529 np.testing.assert_equal(as_array([1, 2, 3]), np.array([1, 2, 3])) 

530 

531 assert as_array([1, 2, 3], DTYPE_FLOAT_DEFAULT).dtype == DTYPE_FLOAT_DEFAULT 

532 

533 assert as_array([1, 2, 3], DTYPE_INT_DEFAULT).dtype == DTYPE_INT_DEFAULT 

534 

535 np.testing.assert_equal( 

536 as_array(dict(zip("abc", [1, 2, 3], strict=True)).values()), 

537 np.array([1, 2, 3]), 

538 ) 

539 

540 

541class TestAsInt(unittest.TestCase): 

542 """ 

543 Define :func:`colour.utilities.array.as_int` definition unit tests 

544 methods. 

545 """ 

546 

547 def test_as_int(self) -> None: 

548 """Test :func:`colour.utilities.array.as_int` definition.""" 

549 

550 assert as_int(1) == 1 

551 

552 assert as_int(np.array([1])).ndim == 1 

553 

554 assert as_int(np.array([[1]])).ndim == 2 

555 

556 np.testing.assert_array_equal( 

557 as_int(np.array([1.0, 2.0, 3.0])), np.array([1, 2, 3]) 

558 ) 

559 

560 assert as_int(np.array([1.0, 2.0, 3.0])).dtype == DTYPE_INT_DEFAULT 

561 

562 assert isinstance(as_int(1), DTYPE_INT_DEFAULT) 

563 

564 

565class TestAsFloat(unittest.TestCase): 

566 """ 

567 Define :func:`colour.utilities.array.as_float` definition unit tests 

568 methods. 

569 """ 

570 

571 def test_as_float(self) -> None: 

572 """Test :func:`colour.utilities.array.as_float` definition.""" 

573 

574 assert as_float(1) == 1.0 

575 

576 assert as_float(np.array([1])).ndim == 1 

577 

578 assert as_float(np.array([[1]])).ndim == 2 

579 

580 np.testing.assert_allclose( 

581 as_float(np.array([1, 2, 3])), 

582 np.array([1.0, 2.0, 3.0]), 

583 atol=TOLERANCE_ABSOLUTE_TESTS, 

584 ) 

585 

586 assert as_float(np.array([1, 2, 3])).dtype == DTYPE_FLOAT_DEFAULT 

587 

588 assert isinstance(as_float(1), DTYPE_FLOAT_DEFAULT) 

589 

590 

591class TestAsIntArray(unittest.TestCase): 

592 """ 

593 Define :func:`colour.utilities.array.as_int_array` definition unit tests 

594 methods. 

595 """ 

596 

597 def test_as_int_array(self) -> None: 

598 """Test :func:`colour.utilities.array.as_int_array` definition.""" 

599 

600 np.testing.assert_equal(as_int_array([1.0, 2.0, 3.0]), np.array([1, 2, 3])) 

601 

602 assert as_int_array([1, 2, 3]).dtype == DTYPE_INT_DEFAULT 

603 

604 

605class TestAsFloatArray(unittest.TestCase): 

606 """ 

607 Define :func:`colour.utilities.array.as_float_array` definition unit tests 

608 methods. 

609 """ 

610 

611 def test_as_float_array(self) -> None: 

612 """Test :func:`colour.utilities.array.as_float_array` definition.""" 

613 

614 np.testing.assert_equal(as_float_array([1, 2, 3]), np.array([1, 2, 3])) 

615 

616 assert as_float_array([1, 2, 3]).dtype == DTYPE_FLOAT_DEFAULT 

617 

618 

619class TestAsComplexArray(unittest.TestCase): 

620 """ 

621 Define :func:`colour.utilities.array.as_complex_array` definition unit tests 

622 methods. 

623 """ 

624 

625 def test_as_complex_array(self) -> None: 

626 """Test :func:`colour.utilities.array.as_complex_array` definition.""" 

627 

628 np.testing.assert_equal( 

629 as_complex_array([1, 2, 3]), np.array([1 + 0j, 2 + 0j, 3 + 0j]) 

630 ) 

631 

632 np.testing.assert_equal( 

633 as_complex_array([1 + 2j, 3 + 4j]), np.array([1 + 2j, 3 + 4j]) 

634 ) 

635 

636 assert as_complex_array([1, 2, 3]).dtype == DTYPE_COMPLEX_DEFAULT 

637 

638 assert as_complex_array([1, 2, 3], np.complex64).dtype == np.complex64 

639 

640 

641class TestAsIntScalar(unittest.TestCase): 

642 """ 

643 Define :func:`colour.utilities.array.as_int_scalar` definition unit tests 

644 methods. 

645 """ 

646 

647 def test_as_int_scalar(self) -> None: 

648 """Test :func:`colour.utilities.array.as_int_scalar` definition.""" 

649 

650 assert as_int_scalar(1.0) == 1 

651 

652 assert as_int_scalar(1.0).dtype == DTYPE_INT_DEFAULT # pyright: ignore 

653 

654 

655class TestAsFloatScalar(unittest.TestCase): 

656 """ 

657 Define :func:`colour.utilities.array.as_float_scalar` definition unit 

658 tests methods. 

659 """ 

660 

661 def test_as_float_scalar(self) -> None: 

662 """Test :func:`colour.utilities.array.as_float_scalar` definition.""" 

663 

664 assert as_float_scalar(1) == 1.0 

665 

666 assert as_float_scalar(1).dtype == DTYPE_FLOAT_DEFAULT # pyright: ignore 

667 

668 

669class TestSetDefaultIntegerDtype(unittest.TestCase): 

670 """ 

671 Define :func:`colour.utilities.array.set_default_int_dtype` definition unit 

672 tests methods. 

673 """ 

674 

675 def test_set_default_int_dtype(self) -> None: 

676 """ 

677 Test :func:`colour.utilities.array.set_default_int_dtype` definition. 

678 """ 

679 

680 assert as_int_array(np.ones(3)).dtype == np.int64 

681 

682 set_default_int_dtype(np.int32) 

683 

684 assert as_int_array(np.ones(3)).dtype == np.int32 

685 

686 set_default_int_dtype(np.int64) 

687 

688 assert as_int_array(np.ones(3)).dtype == np.int64 

689 

690 def tearDown(self) -> None: 

691 """After tests actions.""" 

692 

693 set_default_int_dtype(np.int64) 

694 

695 

696class TestSetDefaultFloatDtype(unittest.TestCase): 

697 """ 

698 Define :func:`colour.utilities.array.set_default_float_dtype` definition unit 

699 tests methods. 

700 """ 

701 

702 def test_set_default_float_dtype(self) -> None: 

703 """ 

704 Test :func:`colour.utilities.array.set_default_float_dtype` 

705 definition. 

706 """ 

707 

708 try: 

709 assert as_float_array(np.ones(3)).dtype == np.float64 

710 

711 set_default_float_dtype(np.float16) 

712 

713 assert as_float_array(np.ones(3)).dtype == np.float16 

714 

715 set_default_float_dtype(np.float64) 

716 

717 assert as_float_array(np.ones(3)).dtype == np.float64 

718 finally: 

719 set_default_float_dtype(np.float64) 

720 

721 def test_set_default_float_dtype_enforcement(self) -> None: 

722 """ 

723 Test whether :func:`colour.utilities.array.set_default_float_dtype` 

724 effect is applied through most of *Colour* public API. 

725 """ 

726 

727 if not is_scipy_installed(): # pragma: no cover 

728 return 

729 

730 if not is_networkx_installed(): # pragma: no cover 

731 return 

732 

733 from colour.appearance import ( # noqa: PLC0415 

734 CAM_Specification_CAM16, 

735 CAM_Specification_CIECAM02, 

736 CAM_Specification_CIECAM16, 

737 CAM_Specification_Hellwig2022, 

738 CAM_Specification_Kim2009, 

739 CAM_Specification_sCAM, 

740 CAM_Specification_ZCAM, 

741 ) 

742 from colour.graph.conversion import ( # noqa: PLC0415 

743 CONVERSION_SPECIFICATIONS_DATA, 

744 convert, 

745 ) 

746 

747 try: 

748 dtype = np.float32 

749 set_default_float_dtype(dtype) 

750 

751 for source, target, _callable in CONVERSION_SPECIFICATIONS_DATA: 

752 if target in ("Hexadecimal", "Munsell Colour"): 

753 continue 

754 

755 # Spectral distributions are instantiated with float64 data and 

756 # spectral up-sampling optimization fails. 

757 if ( 

758 "Spectral Distribution" in (source, target) # noqa: PLR1714 

759 or target == "Complementary Wavelength" 

760 or target == "Dominant Wavelength" 

761 ): 

762 continue 

763 

764 a = np.array([(0.25, 0.5, 0.25), (0.25, 0.5, 0.25)]) 

765 

766 if source == "CAM16": 

767 a = CAM_Specification_CAM16(J=0.25, M=0.5, h=0.25) 

768 

769 if source == "CIECAM02": 

770 a = CAM_Specification_CIECAM02(J=0.25, M=0.5, h=0.25) 

771 

772 if source == "CIECAM16": 

773 a = CAM_Specification_CIECAM16(J=0.25, M=0.5, h=0.25) 

774 

775 if source == "Hellwig 2022": 

776 a = CAM_Specification_Hellwig2022(J=0.25, M=0.5, h=0.25) 

777 

778 if source == "Kim 2009": 

779 a = CAM_Specification_Kim2009(J=0.25, M=0.5, h=0.25) 

780 

781 if source == "sCAM": 

782 a = CAM_Specification_sCAM(J=0.25, M=0.5, h=0.25) 

783 

784 if source == "ZCAM": 

785 a = CAM_Specification_ZCAM(J=0.25, M=0.5, h=0.25) 

786 

787 if source == "CMYK": 

788 a = np.array([(0.25, 0.5, 0.25, 0.5), (0.25, 0.5, 0.25, 0.5)]) 

789 

790 if source == "Hexadecimal": 

791 a = np.array(["#FFFFFF", "#FFFFFF"]) 

792 

793 if source == "CSS Color 3": 

794 a = "aliceblue" 

795 

796 if source == "Munsell Colour": 

797 a = ["4.2YR 8.1/5.3", "4.2YR 8.1/5.3"] 

798 

799 if source == "Wavelength": 

800 a = 555 

801 

802 if ( 

803 source.startswith("CCT") # noqa: PIE810 

804 or source.endswith(" xy") 

805 or source.endswith(" uv") 

806 ): 

807 a = np.array([(0.25, 0.5), (0.25, 0.5)]) 

808 

809 def dtype_getter(x: NDArray) -> DType: 

810 """Dtype getter callable.""" 

811 

812 for specification in ( 

813 "ATD95", 

814 "CIECAM02", 

815 "CAM16", 

816 "Hellwig 2022", 

817 "Hunt", 

818 "Kim 2009", 

819 "LLAB", 

820 "Nayatani95", 

821 "RLAB", 

822 "sCAM", 

823 "ZCAM", 

824 ): 

825 if target.endswith(specification): # noqa: B023 

826 return getattr(x, fields(x)[0].name).dtype # pyright: ignore 

827 

828 return x.dtype # pyright: ignore 

829 

830 assert dtype_getter(convert(a, source, target)) == dtype 

831 finally: 

832 set_default_float_dtype(np.float64) 

833 

834 

835class TestGetDomainRangeScale(unittest.TestCase): 

836 """ 

837 Define :func:`colour.utilities.common.get_domain_range_scale` definition 

838 unit tests methods. 

839 """ 

840 

841 def test_get_domain_range_scale(self) -> None: 

842 """ 

843 Test :func:`colour.utilities.common.get_domain_range_scale` 

844 definition. 

845 """ 

846 

847 with domain_range_scale("Reference"): 

848 assert get_domain_range_scale() == "reference" 

849 

850 with domain_range_scale("1"): 

851 assert get_domain_range_scale() == "1" 

852 

853 with domain_range_scale("100"): 

854 assert get_domain_range_scale() == "100" 

855 

856 

857class TestSetDomainRangeScale(unittest.TestCase): 

858 """ 

859 Define :func:`colour.utilities.common.set_domain_range_scale` definition 

860 unit tests methods. 

861 """ 

862 

863 def test_set_domain_range_scale(self) -> None: 

864 """ 

865 Test :func:`colour.utilities.common.set_domain_range_scale` 

866 definition. 

867 """ 

868 

869 with domain_range_scale("Reference"): 

870 set_domain_range_scale("1") 

871 assert get_domain_range_scale() == "1" 

872 

873 with domain_range_scale("Reference"): 

874 set_domain_range_scale("100") 

875 assert get_domain_range_scale() == "100" 

876 

877 with domain_range_scale("1"): 

878 set_domain_range_scale("Reference") 

879 assert get_domain_range_scale() == "reference" 

880 

881 with pytest.raises(ValueError): 

882 set_domain_range_scale("Invalid") 

883 

884 

885class TestDomainRangeScale(unittest.TestCase): 

886 """ 

887 Define :func:`colour.utilities.common.domain_range_scale` definition 

888 unit tests methods. 

889 """ 

890 

891 def test_domain_range_scale(self) -> None: 

892 """ 

893 Test :func:`colour.utilities.common.domain_range_scale` 

894 definition. 

895 """ 

896 

897 assert get_domain_range_scale() == "reference" 

898 

899 with domain_range_scale("Reference"): 

900 assert get_domain_range_scale() == "reference" 

901 

902 assert get_domain_range_scale() == "reference" 

903 

904 with domain_range_scale("1"): 

905 assert get_domain_range_scale() == "1" 

906 

907 assert get_domain_range_scale() == "reference" 

908 

909 with domain_range_scale("100"): 

910 assert get_domain_range_scale() == "100" 

911 

912 assert get_domain_range_scale() == "reference" 

913 

914 def fn_a(a: ArrayLike) -> NDArrayFloat: 

915 """Change the domain-range scale for unit testing.""" 

916 

917 b = to_domain_10(a) 

918 

919 b *= 2 

920 

921 return from_range_100(b) 

922 

923 with domain_range_scale("Reference"): 

924 with domain_range_scale("1"): 

925 with domain_range_scale("100"): 

926 with domain_range_scale("Ignore"): 

927 assert get_domain_range_scale() == "ignore" 

928 assert fn_a(4) == 8 

929 

930 assert get_domain_range_scale() == "100" 

931 assert fn_a(40) == 8 

932 

933 assert get_domain_range_scale() == "1" 

934 assert fn_a(0.4) == 0.08 

935 

936 assert get_domain_range_scale() == "reference" 

937 assert fn_a(4) == 8 

938 

939 assert get_domain_range_scale() == "reference" 

940 

941 @domain_range_scale("1") 

942 def fn_b(a: ArrayLike) -> NDArrayFloat: 

943 """Change the domain-range scale for unit testing.""" 

944 

945 b = to_domain_10(a) 

946 

947 b *= 2 

948 

949 return from_range_100(b) 

950 

951 assert fn_b(10) == 2.0 

952 

953 

954class TestGetDomainRangeScaleMetadata(unittest.TestCase): 

955 """ 

956 Define :func:`colour.utilities.array.get_domain_range_scale_metadata` 

957 definition unit tests methods. 

958 """ 

959 

960 def test_get_domain_range_scale_metadata(self) -> None: 

961 """ 

962 Test :func:`colour.utilities.array.get_domain_range_scale_metadata` 

963 definition. 

964 """ 

965 

966 # Pattern 1: Uniform parameter scaling 

967 def function_a( 

968 XYZ: Annotated[ArrayLike, 1], 

969 illuminant: ArrayLike = None, # type: ignore 

970 ) -> Annotated[NDArrayFloat, 100]: # type: ignore 

971 """Test uniform parameter scaling.""" 

972 

973 metadata = get_domain_range_scale_metadata(function_a) 

974 assert metadata["domain"] == {"XYZ": 1} 

975 assert metadata["range"] == 100 

976 

977 # Pattern 2: Per-parameter scaling (only some params scaled) 

978 def function_b( 

979 uv: ArrayLike, 

980 illuminant: ArrayLike = None, # type: ignore 

981 L: Annotated[ArrayLike, 100] = 100, 

982 ) -> Annotated[NDArrayFloat, 100]: # type: ignore 

983 """Test per-parameter scaling.""" 

984 

985 metadata = get_domain_range_scale_metadata(function_b) 

986 assert metadata["domain"] == {"L": 100} 

987 assert metadata["range"] == 100 

988 

989 # Pattern 3: Per-component tuple scaling (CAM models) 

990 def function_c( 

991 XYZ: Annotated[ArrayLike, 100], 

992 ) -> Annotated[tuple, (100, 100, 360, 100, 100, 100, 400)]: # type: ignore 

993 """Test tuple return scaling.""" 

994 

995 metadata = get_domain_range_scale_metadata(function_c) 

996 assert metadata["domain"] == {"XYZ": 100} 

997 assert metadata["range"] == (100, 100, 360, 100, 100, 100, 400) 

998 

999 # Multiple domain parameters 

1000 def function_d( 

1001 XYZ: Annotated[ArrayLike, 100], 

1002 XYZ_w: Annotated[ArrayLike, 100], 

1003 illuminant: ArrayLike = None, # type: ignore 

1004 ) -> Annotated[NDArrayFloat, 100]: # type: ignore 

1005 """Test multiple domain parameters.""" 

1006 

1007 metadata = get_domain_range_scale_metadata(function_d) 

1008 assert metadata["domain"] == {"XYZ": 100, "XYZ_w": 100} 

1009 assert metadata["range"] == 100 

1010 

1011 # No annotations (backward compatibility) 

1012 def function_e(XYZ: Any, illuminant: Any = None) -> None: 

1013 """Test backward compatibility.""" 

1014 

1015 metadata = get_domain_range_scale_metadata(function_e) 

1016 assert metadata["domain"] == {} 

1017 assert metadata["range"] is None 

1018 

1019 # Only domain scaling, no range 

1020 def function_f( 

1021 XYZ: Annotated[ArrayLike, 1], 

1022 ) -> NDArrayFloat: # type: ignore 

1023 """Test domain-only scaling.""" 

1024 

1025 metadata = get_domain_range_scale_metadata(function_f) 

1026 assert metadata["domain"] == {"XYZ": 1} 

1027 assert metadata["range"] is None 

1028 

1029 # Only range scaling, no domain 

1030 def function_g( 

1031 XYZ: ArrayLike, 

1032 ) -> Annotated[NDArrayFloat, 100]: # type: ignore 

1033 """Test range-only scaling.""" 

1034 

1035 metadata = get_domain_range_scale_metadata(function_g) 

1036 assert metadata["domain"] == {} 

1037 assert metadata["range"] == 100 

1038 

1039 # Type aliases: Domain1/Range1 

1040 def function_h(XYZ: Domain1, XYZ_w: Domain1 = 1) -> Range1: # type: ignore 

1041 """Test Domain1/Range1 type aliases.""" 

1042 

1043 metadata = get_domain_range_scale_metadata(function_h) 

1044 assert metadata["domain"] == {"XYZ": 1, "XYZ_w": 1} 

1045 

1046 # Union with Annotated types 

1047 def function_i( 

1048 value: Annotated[int, 100] | Annotated[float, 200], 

1049 ) -> NDArrayFloat: # type: ignore 

1050 """Test Union with Annotated members.""" 

1051 

1052 metadata = get_domain_range_scale_metadata(function_i) 

1053 assert metadata["domain"] == {"value": 100} 

1054 assert metadata["range"] is None 

1055 

1056 # Type aliases: Domain100/Range100 

1057 def function_j(Y: Domain100, Y_n: Domain100 = 100) -> Range100: # type: ignore 

1058 """Test Domain100/Range100 type aliases.""" 

1059 

1060 metadata = get_domain_range_scale_metadata(function_j) 

1061 assert metadata["domain"] == {"Y": 100, "Y_n": 100} 

1062 assert metadata["range"] == 100 

1063 

1064 # Type aliases: Domain10/Range10 

1065 def function_k(L: Domain10) -> Range10: # type: ignore 

1066 """Test Domain10/Range10 type aliases.""" 

1067 

1068 metadata = get_domain_range_scale_metadata(function_k) 

1069 assert metadata["domain"] == {"L": 10} 

1070 assert metadata["range"] == 10 

1071 

1072 # Type aliases: Domain360/Range360 

1073 def function_l(hue: Domain360) -> Range360: # type: ignore 

1074 """Test Domain360/Range360 type aliases.""" 

1075 

1076 metadata = get_domain_range_scale_metadata(function_l) 

1077 assert metadata["domain"] == {"hue": 360} 

1078 assert metadata["range"] == 360 

1079 

1080 # Type aliases: Domain100_100_360/Range100_100_360 

1081 def function_m(Lab: Domain100_100_360) -> Range100_100_360: # type: ignore 

1082 """Test Domain100_100_360/Range100_100_360 type aliases.""" 

1083 

1084 metadata = get_domain_range_scale_metadata(function_m) 

1085 assert metadata["domain"] == {"Lab": (100, 100, 360)} 

1086 assert metadata["range"] == (100, 100, 360) 

1087 

1088 # Mixed: type aliases and explicit Annotated 

1089 def function_n( 

1090 XYZ: Domain1, L: Domain100, custom: Annotated[ArrayLike, 50] 

1091 ) -> Range100: # type: ignore 

1092 """Test mixed type aliases and Annotated.""" 

1093 

1094 metadata = get_domain_range_scale_metadata(function_n) 

1095 assert metadata["domain"] == {"XYZ": 1, "L": 100, "custom": 50} 

1096 assert metadata["range"] == 100 

1097 

1098 # functools.partial with type aliases 

1099 def function_o( 

1100 XYZ: Domain1, 

1101 colourspace: str, 

1102 illuminant: ArrayLike | None = None, 

1103 ) -> Range1: # type: ignore 

1104 """Test function for partial wrapping.""" 

1105 

1106 partial_func = partial(function_o, colourspace="sRGB") 

1107 metadata = get_domain_range_scale_metadata(partial_func) 

1108 assert metadata["domain"] == {"XYZ": 1} 

1109 assert metadata["range"] == 1 

1110 

1111 # functools.partial with explicit Annotated 

1112 def function_p( 

1113 Lab: Annotated[ArrayLike, 100], 

1114 illuminant: ArrayLike | None = None, 

1115 method: str = "CIE 1976", 

1116 ) -> Annotated[NDArrayFloat, 100]: # type: ignore 

1117 """Test function for partial wrapping with Annotated.""" 

1118 

1119 partial_func2 = partial(function_p, method="CIE 2000") 

1120 metadata = get_domain_range_scale_metadata(partial_func2) 

1121 assert metadata["domain"] == {"Lab": 100} 

1122 assert metadata["range"] == 100 

1123 

1124 # Test string annotation with unevaluable scale (triggers exception handler) 

1125 # This simulates what happens with `from __future__ import annotations` 

1126 # when the annotation contains an undefined variable 

1127 def function_q(x: Any) -> Any: 

1128 """Test function with mock string annotation.""" 

1129 

1130 # Manually set __annotations__ to simulate string annotation with undefined var 

1131 function_q.__annotations__ = { 

1132 "x": "Annotated[float, undefined_variable]", 

1133 "return": "Annotated[float, another_undefined]", 

1134 } 

1135 

1136 metadata = get_domain_range_scale_metadata(function_q) 

1137 # The eval will fail, so it falls back to the string itself 

1138 assert metadata["domain"] == {"x": "undefined_variable"} 

1139 assert metadata["range"] == "another_undefined" 

1140 

1141 

1142class TestToDomain1(unittest.TestCase): 

1143 """ 

1144 Define :func:`colour.utilities.common.to_domain_1` definition unit 

1145 tests methods. 

1146 """ 

1147 

1148 def test_to_domain_1(self) -> None: 

1149 """Test :func:`colour.utilities.common.to_domain_1` definition.""" 

1150 

1151 with domain_range_scale("Reference"): 

1152 assert to_domain_1(1) == 1 

1153 

1154 with domain_range_scale("1"): 

1155 assert to_domain_1(1) == 1 

1156 

1157 with domain_range_scale("100"): 

1158 assert to_domain_1(1) == 0.01 

1159 

1160 with domain_range_scale("100"): 

1161 assert to_domain_1(1, np.pi) == 1 / np.pi 

1162 

1163 with domain_range_scale("100"): 

1164 assert to_domain_1(1, dtype=np.float16).dtype == np.float16 

1165 

1166 

1167class TestToDomain10(unittest.TestCase): 

1168 """ 

1169 Define :func:`colour.utilities.common.to_domain_10` definition unit 

1170 tests methods. 

1171 """ 

1172 

1173 def test_to_domain_10(self) -> None: 

1174 """Test :func:`colour.utilities.common.to_domain_10` definition.""" 

1175 

1176 with domain_range_scale("Reference"): 

1177 assert to_domain_10(1) == 1 

1178 

1179 with domain_range_scale("1"): 

1180 assert to_domain_10(1) == 10 

1181 

1182 with domain_range_scale("100"): 

1183 assert to_domain_10(1) == 0.1 

1184 

1185 with domain_range_scale("100"): 

1186 assert to_domain_10(1, np.pi) == 1 / np.pi 

1187 

1188 with domain_range_scale("100"): 

1189 assert to_domain_10(1, dtype=np.float16).dtype == np.float16 

1190 

1191 

1192class TestToDomain100(unittest.TestCase): 

1193 """ 

1194 Define :func:`colour.utilities.common.to_domain_100` definition unit 

1195 tests methods. 

1196 """ 

1197 

1198 def test_to_domain_100(self) -> None: 

1199 """Test :func:`colour.utilities.common.to_domain_100` definition.""" 

1200 

1201 with domain_range_scale("Reference"): 

1202 assert to_domain_100(1) == 1 

1203 

1204 with domain_range_scale("1"): 

1205 assert to_domain_100(1) == 100 

1206 

1207 with domain_range_scale("100"): 

1208 assert to_domain_100(1) == 1 

1209 

1210 with domain_range_scale("1"): 

1211 assert to_domain_100(1, np.pi) == np.pi 

1212 

1213 with domain_range_scale("100"): 

1214 assert to_domain_100(1, dtype=np.float16).dtype == np.float16 

1215 

1216 

1217class TestToDomainDegrees(unittest.TestCase): 

1218 """ 

1219 Define :func:`colour.utilities.common.to_domain_degrees` definition unit 

1220 tests methods. 

1221 """ 

1222 

1223 def test_to_domain_degrees(self) -> None: 

1224 """Test :func:`colour.utilities.common.to_domain_degrees` definition.""" 

1225 

1226 with domain_range_scale("Reference"): 

1227 assert to_domain_degrees(1) == 1 

1228 

1229 with domain_range_scale("1"): 

1230 assert to_domain_degrees(1) == 360 

1231 

1232 with domain_range_scale("100"): 

1233 assert to_domain_degrees(1) == 3.6 

1234 

1235 with domain_range_scale("100"): 

1236 assert to_domain_degrees(1, np.pi) == np.pi / 100 

1237 

1238 with domain_range_scale("100"): 

1239 assert to_domain_degrees(1, dtype=np.float16).dtype == np.float16 

1240 

1241 

1242class TestToDomainInt(unittest.TestCase): 

1243 """ 

1244 Define :func:`colour.utilities.common.to_domain_int` definition unit 

1245 tests methods. 

1246 """ 

1247 

1248 def test_to_domain_int(self) -> None: 

1249 """Test :func:`colour.utilities.common.to_domain_int` definition.""" 

1250 

1251 with domain_range_scale("Reference"): 

1252 assert to_domain_int(1) == 1 

1253 

1254 with domain_range_scale("1"): 

1255 assert to_domain_int(1) == 255 

1256 

1257 with domain_range_scale("100"): 

1258 assert to_domain_int(1) == 2.55 

1259 

1260 with domain_range_scale("100"): 

1261 assert to_domain_int(1, 10) == 10.23 

1262 

1263 with domain_range_scale("100"): 

1264 assert to_domain_int(1, dtype=np.float16).dtype == np.float16 

1265 

1266 

1267class TestFromRange1(unittest.TestCase): 

1268 """ 

1269 Define :func:`colour.utilities.common.from_range_1` definition unit 

1270 tests methods. 

1271 """ 

1272 

1273 def test_from_range_1(self) -> None: 

1274 """Test :func:`colour.utilities.common.from_range_1` definition.""" 

1275 

1276 with domain_range_scale("Reference"): 

1277 assert from_range_1(1) == 1 

1278 

1279 with domain_range_scale("1"): 

1280 assert from_range_1(1) == 1 

1281 

1282 with domain_range_scale("100"): 

1283 assert from_range_1(1) == 100 

1284 

1285 with domain_range_scale("100"): 

1286 assert from_range_1(1, np.pi) == 1 * np.pi 

1287 

1288 

1289class TestFromRange10(unittest.TestCase): 

1290 """ 

1291 Define :func:`colour.utilities.common.from_range_10` definition unit 

1292 tests methods. 

1293 """ 

1294 

1295 def test_from_range_10(self) -> None: 

1296 """Test :func:`colour.utilities.common.from_range_10` definition.""" 

1297 

1298 with domain_range_scale("Reference"): 

1299 assert from_range_10(1) == 1 

1300 

1301 with domain_range_scale("1"): 

1302 assert from_range_10(1) == 0.1 

1303 

1304 with domain_range_scale("100"): 

1305 assert from_range_10(1) == 10 

1306 

1307 with domain_range_scale("100"): 

1308 assert from_range_10(1, np.pi) == 1 * np.pi 

1309 

1310 

1311class TestFromRange100(unittest.TestCase): 

1312 """ 

1313 Define :func:`colour.utilities.common.from_range_100` definition unit 

1314 tests methods. 

1315 """ 

1316 

1317 def test_from_range_100(self) -> None: 

1318 """Test :func:`colour.utilities.common.from_range_100` definition.""" 

1319 

1320 with domain_range_scale("Reference"): 

1321 assert from_range_100(1) == 1 

1322 

1323 with domain_range_scale("1"): 

1324 assert from_range_100(1) == 0.01 

1325 

1326 with domain_range_scale("100"): 

1327 assert from_range_100(1) == 1 

1328 

1329 with domain_range_scale("1"): 

1330 assert from_range_100(1, np.pi) == 1 / np.pi 

1331 

1332 

1333class TestFromRangeDegrees(unittest.TestCase): 

1334 """ 

1335 Define :func:`colour.utilities.common.from_range_degrees` definition unit 

1336 tests methods. 

1337 """ 

1338 

1339 def test_from_range_degrees(self) -> None: 

1340 """Test :func:`colour.utilities.common.from_range_degrees` definition.""" 

1341 

1342 with domain_range_scale("Reference"): 

1343 assert from_range_degrees(1) == 1 

1344 

1345 with domain_range_scale("1"): 

1346 assert from_range_degrees(1) == 1 / 360 

1347 

1348 with domain_range_scale("100"): 

1349 assert from_range_degrees(1) == 1 / 3.6 

1350 

1351 with domain_range_scale("100"): 

1352 assert from_range_degrees(1, np.pi) == 1 / (np.pi / 100) 

1353 

1354 

1355class TestFromRangeInt(unittest.TestCase): 

1356 """ 

1357 Define :func:`colour.utilities.common.from_range_int` definition unit 

1358 tests methods. 

1359 """ 

1360 

1361 def test_from_range_int(self) -> None: 

1362 """Test :func:`colour.utilities.common.from_range_int` definition.""" 

1363 

1364 with domain_range_scale("Reference"): 

1365 assert from_range_int(1) == 1 

1366 

1367 with domain_range_scale("1"): 

1368 assert from_range_int(1) == 1 / 255 

1369 

1370 with domain_range_scale("100"): 

1371 assert from_range_int(1) == 1 / 2.55 

1372 

1373 with domain_range_scale("100"): 

1374 assert from_range_int(1, 10) == 1 / (1023 / 100) 

1375 

1376 with domain_range_scale("100"): 

1377 assert from_range_int(1, dtype=np.float16).dtype == np.float16 

1378 

1379 

1380class TestIsNdarrayCopyEnabled(unittest.TestCase): 

1381 """ 

1382 Define :func:`colour.utilities.array.is_ndarray_copy_enabled` definition 

1383 unit tests methods. 

1384 """ 

1385 

1386 def test_is_ndarray_copy_enabled(self) -> None: 

1387 """ 

1388 Test :func:`colour.utilities.array.is_ndarray_copy_enabled` definition. 

1389 """ 

1390 

1391 with ndarray_copy_enable(True): 

1392 assert is_ndarray_copy_enabled() 

1393 

1394 with ndarray_copy_enable(False): 

1395 assert not is_ndarray_copy_enabled() 

1396 

1397 

1398class TestSetNdarrayCopyEnabled(unittest.TestCase): 

1399 """ 

1400 Define :func:`colour.utilities.array.set_ndarray_copy_enable` definition 

1401 unit tests methods. 

1402 """ 

1403 

1404 def test_set_ndarray_copy_enable(self) -> None: 

1405 """ 

1406 Test :func:`colour.utilities.array.set_ndarray_copy_enable` definition. 

1407 """ 

1408 

1409 with ndarray_copy_enable(is_ndarray_copy_enabled()): 

1410 set_ndarray_copy_enable(True) 

1411 assert is_ndarray_copy_enabled() 

1412 

1413 with ndarray_copy_enable(is_ndarray_copy_enabled()): 

1414 set_ndarray_copy_enable(False) 

1415 assert not is_ndarray_copy_enabled() 

1416 

1417 

1418class TestNdarrayCopyEnable(unittest.TestCase): 

1419 """ 

1420 Define :func:`colour.utilities.array.ndarray_copy_enable` definition unit 

1421 tests methods. 

1422 """ 

1423 

1424 def test_ndarray_copy_enable(self) -> None: 

1425 """ 

1426 Test :func:`colour.utilities.array.ndarray_copy_enable` definition. 

1427 """ 

1428 

1429 with ndarray_copy_enable(True): 

1430 assert is_ndarray_copy_enabled() 

1431 

1432 with ndarray_copy_enable(False): 

1433 assert not is_ndarray_copy_enabled() 

1434 

1435 @ndarray_copy_enable(True) 

1436 def fn_a() -> None: 

1437 """:func:`ndarray_copy_enable` unit tests :func:`fn_a` definition.""" 

1438 

1439 assert is_ndarray_copy_enabled() 

1440 

1441 fn_a() 

1442 

1443 @ndarray_copy_enable(False) 

1444 def fn_b() -> None: 

1445 """:func:`ndarray_copy_enable` unit tests :func:`fn_b` definition.""" 

1446 

1447 assert not is_ndarray_copy_enabled() 

1448 

1449 fn_b() 

1450 

1451 

1452class TestNdarrayCopy(unittest.TestCase): 

1453 """ 

1454 Define :func:`colour.utilities.array.ndarray_copy` definition unit 

1455 tests methods. 

1456 """ 

1457 

1458 def test_ndarray_copy(self) -> None: 

1459 """Test :func:`colour.utilities.array.ndarray_copy` definition.""" 

1460 

1461 a = np.linspace(0, 1, 10) 

1462 with ndarray_copy_enable(True): 

1463 assert id(ndarray_copy(a)) != id(a) 

1464 

1465 with ndarray_copy_enable(False): 

1466 assert id(ndarray_copy(a)) == id(a) 

1467 

1468 

1469class TestClosestIndexes(unittest.TestCase): 

1470 """ 

1471 Define :func:`colour.utilities.array.closest_indexes` definition unit 

1472 tests methods. 

1473 """ 

1474 

1475 def test_closest_indexes(self) -> None: 

1476 """Test :func:`colour.utilities.array.closest_indexes` definition.""" 

1477 

1478 a = np.array( 

1479 [ 

1480 24.31357115, 

1481 63.62396289, 

1482 55.71528816, 

1483 62.70988028, 

1484 46.84480573, 

1485 25.40026416, 

1486 ] 

1487 ) 

1488 

1489 assert closest_indexes(a, 63.05) == 3 

1490 

1491 assert closest_indexes(a, 51.15) == 4 

1492 

1493 assert closest_indexes(a, 24.90) == 5 

1494 

1495 np.testing.assert_array_equal( 

1496 closest_indexes(a, np.array([63.05, 51.15, 24.90])), 

1497 np.array([3, 4, 5]), 

1498 ) 

1499 

1500 

1501class TestClosest(unittest.TestCase): 

1502 """ 

1503 Define :func:`colour.utilities.array.closest` definition unit tests 

1504 methods. 

1505 """ 

1506 

1507 def test_closest(self) -> None: 

1508 """Test :func:`colour.utilities.array.closest` definition.""" 

1509 

1510 a = np.array( 

1511 [ 

1512 24.31357115, 

1513 63.62396289, 

1514 55.71528816, 

1515 62.70988028, 

1516 46.84480573, 

1517 25.40026416, 

1518 ] 

1519 ) 

1520 

1521 assert closest(a, 63.05) == 62.70988028 

1522 

1523 assert closest(a, 51.15) == 46.84480573 

1524 

1525 assert closest(a, 24.90) == 25.40026416 

1526 

1527 np.testing.assert_allclose( 

1528 closest(a, np.array([63.05, 51.15, 24.90])), 

1529 np.array([62.70988028, 46.84480573, 25.40026416]), 

1530 atol=TOLERANCE_ABSOLUTE_TESTS, 

1531 ) 

1532 

1533 

1534class TestInterval(unittest.TestCase): 

1535 """ 

1536 Define :func:`colour.utilities.array.interval` definition unit tests 

1537 methods. 

1538 """ 

1539 

1540 def test_interval(self) -> None: 

1541 """Test :func:`colour.utilities.array.interval` definition.""" 

1542 

1543 np.testing.assert_array_equal(interval(range(0, 10, 2)), np.array([2])) 

1544 

1545 np.testing.assert_array_equal( 

1546 interval(range(0, 10, 2), False), np.array([2, 2, 2, 2]) 

1547 ) 

1548 

1549 np.testing.assert_allclose( 

1550 interval([1, 2, 3, 4, 6, 6.5]), 

1551 np.array([0.5, 1.0, 2.0]), 

1552 atol=TOLERANCE_ABSOLUTE_TESTS, 

1553 ) 

1554 

1555 np.testing.assert_allclose( 

1556 interval([1, 2, 3, 4, 6, 6.5], False), 

1557 np.array([1.0, 1.0, 1.0, 2.0, 0.5]), 

1558 atol=TOLERANCE_ABSOLUTE_TESTS, 

1559 ) 

1560 

1561 

1562class TestIsUniform(unittest.TestCase): 

1563 """ 

1564 Define :func:`colour.utilities.array.is_uniform` definition unit tests 

1565 methods. 

1566 """ 

1567 

1568 def test_is_uniform(self) -> None: 

1569 """Test :func:`colour.utilities.array.is_uniform` definition.""" 

1570 

1571 assert is_uniform(range(0, 10, 2)) 

1572 

1573 assert not is_uniform([1, 2, 3, 4, 6]) 

1574 

1575 

1576class TestInArray(unittest.TestCase): 

1577 """ 

1578 Define :func:`colour.utilities.array.in_array` definition unit tests 

1579 methods. 

1580 """ 

1581 

1582 def test_in_array(self) -> None: 

1583 """Test :func:`colour.utilities.array.in_array` definition.""" 

1584 

1585 assert np.array_equal( 

1586 in_array(np.array([0.50, 0.60]), np.linspace(0, 10, 101)), 

1587 np.array([True, True]), 

1588 ) 

1589 

1590 assert not np.array_equal( 

1591 in_array(np.array([0.50, 0.61]), np.linspace(0, 10, 101)), 

1592 np.array([True, True]), 

1593 ) 

1594 

1595 assert np.array_equal( 

1596 in_array(np.array([[0.50], [0.60]]), np.linspace(0, 10, 101)), 

1597 np.array([[True], [True]]), 

1598 ) 

1599 

1600 def test_n_dimensional_in_array(self) -> None: 

1601 """ 

1602 Test :func:`colour.utilities.array.in_array` definition n-dimensional 

1603 support. 

1604 """ 

1605 

1606 np.testing.assert_array_equal( 

1607 in_array(np.array([0.50, 0.60]), np.linspace(0, 10, 101)).shape, 

1608 np.array([2]), 

1609 ) 

1610 

1611 np.testing.assert_array_equal( 

1612 in_array(np.array([[0.50, 0.60]]), np.linspace(0, 10, 101)).shape, 

1613 np.array([1, 2]), 

1614 ) 

1615 

1616 np.testing.assert_array_equal( 

1617 in_array(np.array([[0.50], [0.60]]), np.linspace(0, 10, 101)).shape, 

1618 np.array([2, 1]), 

1619 ) 

1620 

1621 

1622class TestTstack(unittest.TestCase): 

1623 """ 

1624 Define :func:`colour.utilities.array.tstack` definition unit tests 

1625 methods. 

1626 """ 

1627 

1628 def test_tstack(self) -> None: 

1629 """Test :func:`colour.utilities.array.tstack` definition.""" 

1630 

1631 a = 0 

1632 np.testing.assert_array_equal(tstack([a, a, a]), np.array([0, 0, 0])) 

1633 

1634 a = np.arange(0, 6) 

1635 np.testing.assert_array_equal( 

1636 tstack([a, a, a]), 

1637 np.array( 

1638 [ 

1639 [0, 0, 0], 

1640 [1, 1, 1], 

1641 [2, 2, 2], 

1642 [3, 3, 3], 

1643 [4, 4, 4], 

1644 [5, 5, 5], 

1645 ] 

1646 ), 

1647 ) 

1648 

1649 a = np.reshape(a, (1, 6)) 

1650 np.testing.assert_array_equal( 

1651 tstack([a, a, a]), 

1652 np.array( 

1653 [ 

1654 [ 

1655 [0, 0, 0], 

1656 [1, 1, 1], 

1657 [2, 2, 2], 

1658 [3, 3, 3], 

1659 [4, 4, 4], 

1660 [5, 5, 5], 

1661 ] 

1662 ] 

1663 ), 

1664 ) 

1665 

1666 a = np.reshape(a, (1, 2, 3)) 

1667 np.testing.assert_array_equal( 

1668 tstack([a, a, a]), 

1669 np.array( 

1670 [ 

1671 [ 

1672 [[0, 0, 0], [1, 1, 1], [2, 2, 2]], 

1673 [[3, 3, 3], [4, 4, 4], [5, 5, 5]], 

1674 ] 

1675 ] 

1676 ), 

1677 ) 

1678 

1679 # Ensuring that contiguity is maintained. 

1680 a = np.array([0, 1, 2], dtype=DTYPE_FLOAT_DEFAULT) 

1681 b = tstack([a, a, a]) 

1682 assert b.flags.contiguous 

1683 

1684 # Ensuring that independence is maintained. 

1685 a *= 2 

1686 np.testing.assert_array_equal( 

1687 b, 

1688 np.array( 

1689 [ 

1690 [0, 0, 0], 

1691 [1, 1, 1], 

1692 [2, 2, 2], 

1693 ], 

1694 ), 

1695 ) 

1696 

1697 a = np.array([0, 1, 2], dtype=DTYPE_FLOAT_DEFAULT) 

1698 b = tstack([a, a, a]) 

1699 

1700 b[1] *= 2 

1701 np.testing.assert_array_equal( 

1702 a, 

1703 np.array([0, 1, 2]), 

1704 ) 

1705 

1706 

1707class TestTsplit(unittest.TestCase): 

1708 """ 

1709 Define :func:`colour.utilities.array.tsplit` definition unit tests 

1710 methods. 

1711 """ 

1712 

1713 def test_tsplit(self) -> None: 

1714 """Test :func:`colour.utilities.array.tsplit` definition.""" 

1715 

1716 a = np.array([0, 0, 0]) 

1717 np.testing.assert_array_equal(tsplit(a), np.array([0, 0, 0])) 

1718 a = np.array( 

1719 [ 

1720 [0, 0, 0], 

1721 [1, 1, 1], 

1722 [2, 2, 2], 

1723 [3, 3, 3], 

1724 [4, 4, 4], 

1725 [5, 5, 5], 

1726 ] 

1727 ) 

1728 np.testing.assert_array_equal( 

1729 tsplit(a), 

1730 np.array( 

1731 [ 

1732 [0, 1, 2, 3, 4, 5], 

1733 [0, 1, 2, 3, 4, 5], 

1734 [0, 1, 2, 3, 4, 5], 

1735 ] 

1736 ), 

1737 ) 

1738 

1739 a = np.array( 

1740 [ 

1741 [ 

1742 [0, 0, 0], 

1743 [1, 1, 1], 

1744 [2, 2, 2], 

1745 [3, 3, 3], 

1746 [4, 4, 4], 

1747 [5, 5, 5], 

1748 ], 

1749 ] 

1750 ) 

1751 np.testing.assert_array_equal( 

1752 tsplit(a), 

1753 np.array( 

1754 [ 

1755 [[0, 1, 2, 3, 4, 5]], 

1756 [[0, 1, 2, 3, 4, 5]], 

1757 [[0, 1, 2, 3, 4, 5]], 

1758 ] 

1759 ), 

1760 ) 

1761 

1762 a = np.array( 

1763 [ 

1764 [ 

1765 [[0, 0, 0], [1, 1, 1], [2, 2, 2]], 

1766 [[3, 3, 3], [4, 4, 4], [5, 5, 5]], 

1767 ] 

1768 ] 

1769 ) 

1770 np.testing.assert_array_equal( 

1771 tsplit(a), 

1772 np.array( 

1773 [ 

1774 [[[0, 1, 2], [3, 4, 5]]], 

1775 [[[0, 1, 2], [3, 4, 5]]], 

1776 [[[0, 1, 2], [3, 4, 5]]], 

1777 ] 

1778 ), 

1779 ) 

1780 

1781 # Ensuring that contiguity is maintained. 

1782 a = np.array( 

1783 [ 

1784 [0, 0, 0], 

1785 [1, 1, 1], 

1786 [2, 2, 2], 

1787 ], 

1788 dtype=DTYPE_FLOAT_DEFAULT, 

1789 ) 

1790 b = tsplit(a) 

1791 assert b.flags.contiguous 

1792 

1793 # Ensuring that independence is maintained. 

1794 a *= 2 

1795 np.testing.assert_array_equal( 

1796 b, 

1797 np.array( 

1798 [ 

1799 [0, 1, 2], 

1800 [0, 1, 2], 

1801 [0, 1, 2], 

1802 ] 

1803 ), 

1804 ) 

1805 

1806 a = np.array( 

1807 [ 

1808 [0, 0, 0], 

1809 [1, 1, 1], 

1810 [2, 2, 2], 

1811 ], 

1812 dtype=DTYPE_FLOAT_DEFAULT, 

1813 ) 

1814 b = tsplit(a) 

1815 

1816 b[1] *= 2 

1817 np.testing.assert_array_equal( 

1818 a, 

1819 np.array( 

1820 [ 

1821 [0, 0, 0], 

1822 [1, 1, 1], 

1823 [2, 2, 2], 

1824 ] 

1825 ), 

1826 ) 

1827 

1828 

1829class TestRowAsDiagonal(unittest.TestCase): 

1830 """ 

1831 Define :func:`colour.utilities.array.row_as_diagonal` definition unit 

1832 tests methods. 

1833 """ 

1834 

1835 def test_row_as_diagonal(self) -> None: 

1836 """Test :func:`colour.utilities.array.row_as_diagonal` definition.""" 

1837 

1838 np.testing.assert_allclose( 

1839 row_as_diagonal( 

1840 np.array( 

1841 [ 

1842 [0.25891593, 0.07299478, 0.36586996], 

1843 [0.30851087, 0.37131459, 0.16274825], 

1844 [0.71061831, 0.67718718, 0.09562581], 

1845 [0.71588836, 0.76772047, 0.15476079], 

1846 [0.92985142, 0.22263399, 0.88027331], 

1847 ] 

1848 ) 

1849 ), 

1850 np.array( 

1851 [ 

1852 [ 

1853 [0.25891593, 0.00000000, 0.00000000], 

1854 [0.00000000, 0.07299478, 0.00000000], 

1855 [0.00000000, 0.00000000, 0.36586996], 

1856 ], 

1857 [ 

1858 [0.30851087, 0.00000000, 0.00000000], 

1859 [0.00000000, 0.37131459, 0.00000000], 

1860 [0.00000000, 0.00000000, 0.16274825], 

1861 ], 

1862 [ 

1863 [0.71061831, 0.00000000, 0.00000000], 

1864 [0.00000000, 0.67718718, 0.00000000], 

1865 [0.00000000, 0.00000000, 0.09562581], 

1866 ], 

1867 [ 

1868 [0.71588836, 0.00000000, 0.00000000], 

1869 [0.00000000, 0.76772047, 0.00000000], 

1870 [0.00000000, 0.00000000, 0.15476079], 

1871 ], 

1872 [ 

1873 [0.92985142, 0.00000000, 0.00000000], 

1874 [0.00000000, 0.22263399, 0.00000000], 

1875 [0.00000000, 0.00000000, 0.88027331], 

1876 ], 

1877 ] 

1878 ), 

1879 atol=TOLERANCE_ABSOLUTE_TESTS, 

1880 ) 

1881 

1882 

1883class TestOrient(unittest.TestCase): 

1884 """ 

1885 Define :func:`colour.utilities.array.orient` definition unit tests 

1886 methods. 

1887 """ 

1888 

1889 def test_orient(self) -> None: 

1890 """Test :func:`colour.utilities.array.orient` definition.""" 

1891 

1892 a = np.tile(np.arange(5), (5, 1)) 

1893 

1894 np.testing.assert_array_equal( 

1895 orient(a, "Flip"), 

1896 np.array( 

1897 [ 

1898 [4, 3, 2, 1, 0], 

1899 [4, 3, 2, 1, 0], 

1900 [4, 3, 2, 1, 0], 

1901 [4, 3, 2, 1, 0], 

1902 [4, 3, 2, 1, 0], 

1903 ] 

1904 ), 

1905 ) 

1906 

1907 np.testing.assert_array_equal( 

1908 orient(a, "Flop"), 

1909 np.array( 

1910 [ 

1911 [0, 1, 2, 3, 4], 

1912 [0, 1, 2, 3, 4], 

1913 [0, 1, 2, 3, 4], 

1914 [0, 1, 2, 3, 4], 

1915 [0, 1, 2, 3, 4], 

1916 ] 

1917 ), 

1918 ) 

1919 

1920 np.testing.assert_array_equal( 

1921 orient(a, "90 CW"), 

1922 np.array( 

1923 [ 

1924 [0, 0, 0, 0, 0], 

1925 [1, 1, 1, 1, 1], 

1926 [2, 2, 2, 2, 2], 

1927 [3, 3, 3, 3, 3], 

1928 [4, 4, 4, 4, 4], 

1929 ] 

1930 ), 

1931 ) 

1932 

1933 np.testing.assert_array_equal( 

1934 orient(a, "90 CCW"), 

1935 np.array( 

1936 [ 

1937 [4, 4, 4, 4, 4], 

1938 [3, 3, 3, 3, 3], 

1939 [2, 2, 2, 2, 2], 

1940 [1, 1, 1, 1, 1], 

1941 [0, 0, 0, 0, 0], 

1942 ] 

1943 ), 

1944 ) 

1945 

1946 np.testing.assert_array_equal( 

1947 orient(a, "180"), 

1948 np.array( 

1949 [ 

1950 [4, 3, 2, 1, 0], 

1951 [4, 3, 2, 1, 0], 

1952 [4, 3, 2, 1, 0], 

1953 [4, 3, 2, 1, 0], 

1954 [4, 3, 2, 1, 0], 

1955 ] 

1956 ), 

1957 ) 

1958 

1959 np.testing.assert_array_equal(orient(a), a) 

1960 

1961 

1962class TestCentroid(unittest.TestCase): 

1963 """ 

1964 Define :func:`colour.utilities.array.centroid` definition unit tests 

1965 methods. 

1966 """ 

1967 

1968 def test_centroid(self) -> None: 

1969 """Test :func:`colour.utilities.array.centroid` definition.""" 

1970 

1971 a = np.arange(5) 

1972 np.testing.assert_array_equal(centroid(a), np.array([3])) 

1973 

1974 a = np.tile(a, (5, 1)) 

1975 np.testing.assert_array_equal(centroid(a), np.array([2, 3])) 

1976 

1977 a = np.tile(np.linspace(0, 1, 10), (10, 1)) 

1978 np.testing.assert_array_equal(centroid(a), np.array([4, 6])) 

1979 

1980 a = tstack([a, a, a]) 

1981 np.testing.assert_array_equal(centroid(a), np.array([4, 6, 1])) 

1982 

1983 

1984class TestFillNan(unittest.TestCase): 

1985 """ 

1986 Define :func:`colour.utilities.array.fill_nan` definition unit tests 

1987 methods. 

1988 """ 

1989 

1990 def test_fill_nan(self) -> None: 

1991 """Test :func:`colour.utilities.array.fill_nan` definition.""" 

1992 

1993 a = np.array([0.1, 0.2, np.nan, 0.4, 0.5]) 

1994 np.testing.assert_allclose( 

1995 fill_nan(a), 

1996 np.array([0.1, 0.2, 0.3, 0.4, 0.5]), 

1997 atol=TOLERANCE_ABSOLUTE_TESTS, 

1998 ) 

1999 

2000 np.testing.assert_allclose( 

2001 fill_nan(a, method="Constant", default=8.0), 

2002 np.array([0.1, 0.2, 8.0, 0.4, 0.5]), 

2003 atol=TOLERANCE_ABSOLUTE_TESTS, 

2004 ) 

2005 

2006 

2007class TestHasNanOnly(unittest.TestCase): 

2008 """ 

2009 Define :func:`colour.utilities.array.has_only_nan` definition unit tests 

2010 methods. 

2011 """ 

2012 

2013 def test_has_only_nan(self) -> None: 

2014 """Test :func:`colour.utilities.array.has_only_nan` definition.""" 

2015 

2016 assert has_only_nan(None) # pyright: ignore 

2017 

2018 assert has_only_nan([None, None]) # pyright: ignore 

2019 

2020 assert not has_only_nan([True, None]) # pyright: ignore 

2021 

2022 assert not has_only_nan([0.1, np.nan, 0.3]) 

2023 

2024 

2025class TestNdarrayWrite(unittest.TestCase): 

2026 """ 

2027 Define :func:`colour.utilities.array.ndarray_write` definition unit tests 

2028 methods. 

2029 """ 

2030 

2031 def test_ndarray_write(self) -> None: 

2032 """Test :func:`colour.utilities.array.ndarray_write` definition.""" 

2033 

2034 a = np.linspace(0, 1, 10) 

2035 a.setflags(write=False) 

2036 

2037 with pytest.raises(ValueError): 

2038 a += 1 

2039 

2040 with ndarray_write(a): 

2041 a += 1 

2042 

2043 

2044class TestZeros(unittest.TestCase): 

2045 """ 

2046 Define :func:`colour.utilities.array.zeros` definition unit tests 

2047 methods. 

2048 """ 

2049 

2050 def test_zeros(self) -> None: 

2051 """Test :func:`colour.utilities.array.zeros` definition.""" 

2052 

2053 np.testing.assert_equal(zeros(3), np.zeros(3)) 

2054 

2055 

2056class TestOnes(unittest.TestCase): 

2057 """ 

2058 Define :func:`colour.utilities.array.ones` definition unit tests 

2059 methods. 

2060 """ 

2061 

2062 def test_ones(self) -> None: 

2063 """Test :func:`colour.utilities.array.ones` definition.""" 

2064 

2065 np.testing.assert_equal(ones(3), np.ones(3)) 

2066 

2067 

2068class TestFull(unittest.TestCase): 

2069 """ 

2070 Define :func:`colour.utilities.array.full` definition unit tests 

2071 methods. 

2072 """ 

2073 

2074 def test_full(self) -> None: 

2075 """Test :func:`colour.utilities.array.full` definition.""" 

2076 

2077 np.testing.assert_equal(full(3, 0.5), np.full(3, 0.5)) 

2078 

2079 

2080class TestIndexAlongLastAxis(unittest.TestCase): 

2081 """ 

2082 Define :func:`colour.utilities.array.index_along_last_axis` definition 

2083 unit tests methods. 

2084 """ 

2085 

2086 def test_index_along_last_axis(self) -> None: 

2087 """Test :func:`colour.utilities.array.index_along_last_axis` definition.""" 

2088 a = np.array( 

2089 [ 

2090 [ 

2091 [ 

2092 [0.51090627, 0.86191718, 0.8687926], 

2093 [0.82738158, 0.80587656, 0.28285687], 

2094 ], 

2095 [ 

2096 [0.84085977, 0.03851814, 0.06057988], 

2097 [0.94659267, 0.79308353, 0.30870888], 

2098 ], 

2099 ], 

2100 [ 

2101 [ 

2102 [0.50758436, 0.24066455, 0.20199051], 

2103 [0.4507304, 0.84189245, 0.81160878], 

2104 ], 

2105 [ 

2106 [0.75421871, 0.88187494, 0.01612045], 

2107 [0.38777511, 0.58905552, 0.32970469], 

2108 ], 

2109 ], 

2110 [ 

2111 [ 

2112 [0.99285824, 0.738076, 0.0716432], 

2113 [0.35847844, 0.0367514, 0.18586322], 

2114 ], 

2115 [ 

2116 [0.72674561, 0.0822759, 0.9771182], 

2117 [0.90644279, 0.09689787, 0.93483977], 

2118 ], 

2119 ], 

2120 ] 

2121 ) 

2122 

2123 indexes = np.array([[[0, 1], [0, 1]], [[2, 1], [2, 1]], [[2, 1], [2, 0]]]) 

2124 

2125 np.testing.assert_equal( 

2126 index_along_last_axis(a, indexes), 

2127 np.array( 

2128 [ 

2129 [[0.51090627, 0.80587656], [0.84085977, 0.79308353]], 

2130 [[0.20199051, 0.84189245], [0.01612045, 0.58905552]], 

2131 [[0.0716432, 0.0367514], [0.9771182, 0.90644279]], 

2132 ] 

2133 ), 

2134 ) 

2135 

2136 def test_compare_with_argmin_argmax(self) -> None: 

2137 """ 

2138 Test :func:`colour.utilities.array.index_along_last_axis` definition 

2139 by comparison with :func:`argmin` and :func:`argmax`. 

2140 """ 

2141 

2142 a = np.random.random((2, 3, 4, 5, 6, 7)) 

2143 

2144 np.testing.assert_equal( 

2145 index_along_last_axis(a, np.argmin(a, axis=-1)), np.min(a, axis=-1) 

2146 ) 

2147 

2148 np.testing.assert_equal( 

2149 index_along_last_axis(a, np.argmax(a, axis=-1)), np.max(a, axis=-1) 

2150 ) 

2151 

2152 def test_exceptions(self) -> None: 

2153 """ 

2154 Test :func:`colour.utilities.array.index_along_last_axis` definition 

2155 handling of invalid inputs. 

2156 """ 

2157 

2158 a = as_float_array([[11, 12], [21, 22]]) 

2159 

2160 # Bad shape 

2161 with pytest.raises(ValueError): 

2162 indexes = np.array([0]) 

2163 index_along_last_axis(a, indexes) 

2164 

2165 # Indexes out of range 

2166 with pytest.raises(IndexError): 

2167 indexes = np.array([123, 456]) 

2168 index_along_last_axis(a, indexes) 

2169 

2170 # Non-int indexes 

2171 with pytest.raises(IndexError): 

2172 indexes = np.array([0.0, 0.0]) 

2173 index_along_last_axis(a, indexes) 

2174 

2175 

2176class TestFormatArrayAsRow(unittest.TestCase): 

2177 """ 

2178 Define :func:`colour.utilities.array.format_array_as_row` definition unit 

2179 tests methods. 

2180 """ 

2181 

2182 def test_format_array_as_row(self) -> None: 

2183 """Test :func:`colour.utilities.array.format_array_as_row` definition.""" 

2184 

2185 assert format_array_as_row([1.25, 2.5, 3.75]) == "1.2500000 2.5000000 3.7500000" 

2186 

2187 assert format_array_as_row([1.25, 2.5, 3.75], 3) == "1.250 2.500 3.750" 

2188 

2189 assert format_array_as_row([1.25, 2.5, 3.75], 3, ", ") == "1.250, 2.500, 3.750"