Coverage for utilities/tests/test_structures.py: 100%

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

2

3 from __future__ import annotations

4

5 import operator

6 import pickle

7

8 import numpy as np

9 import pytest

10

11 from colour.utilities import (

12 CanonicalMapping,

13 ColourUsageWarning,

14 LazyCanonicalMapping,

15 Lookup,

16 Structure,

17)

18

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"

25

26 __all__ = [

27 "TestStructure",

28 "TestLookup",

29 "TestCanonicalMapping",

30 "TestLazyCanonicalMapping",

31]

32

33

34 class TestStructure:

35 """

36 Define :class:`colour.utilities.structures.Structure` class unit

37 tests methods.

38 """

39

40 def test_Structure(self) -> None:

41 """Test the :class:`colour.utilities.structures.Structure` class."""

42

43 structure = Structure(John="Doe", Jane="Doe")

44 assert "John" in structure

45 assert hasattr(structure, "John")

46

47 structure.John = "Nemo"

48 assert structure["John"] == "Nemo"

49

50 structure["John"] = "Vador"

51 assert structure["John"] == "Vador"

52

53 del structure["John"]

54 assert "John" not in structure

55 assert not hasattr(structure, "John")

56

57 structure.John = "Doe"

58 assert "John" in structure

59 assert hasattr(structure, "John")

60

61 del structure.John

62 assert "John" not in structure

63 assert not hasattr(structure, "John")

64

65 structure = Structure(John=None, Jane=None)

66 assert structure.John is None

67 assert structure["John"] is None

68

69 structure.update(John="Doe", Jane="Doe")

70 assert structure.John == "Doe"

71 assert structure["John"] == "Doe"

72

73 def test_pickling(self) -> None:

74 """

75 Test whether :class:`colour.utilities.structures.Structure` class

76 can be pickled.

77 """

78

79 structure = Structure(John="Doe", Jane="Doe")

80

81 data = pickle.dumps(structure)

82 data = pickle.loads(data) # noqa: S301

83 assert structure == data

84

85 data = pickle.dumps(structure, pickle.HIGHEST_PROTOCOL)

86 data = pickle.loads(data) # noqa: S301

87 assert structure == data

88

89 assert sorted(dir(data)) == ["Jane", "John"]

90

91

92 class TestLookup:

93 """

94 Define :class:`colour.utilities.structures.Lookup` class unit tests

95 methods.

96 """

97

98 def test_required_methods(self) -> None:

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

100

101 required_methods = ("keys_from_value", "first_key_from_value")

102

103 for method in required_methods:

104 assert method in dir(Lookup)

105

106 def test_keys_from_value(self) -> None:

107 """

108 Test :meth:`colour.utilities.structures.Lookup.keys_from_value`

109 method.

110 """

111

112 lookup = Lookup(John="Doe", Jane="Doe", Luke="Skywalker")

113 assert sorted(lookup.keys_from_value("Doe")) == ["Jane", "John"]

114

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"]

119

120 def test_first_key_from_value(self) -> None:

121 """

122 Test :meth:`colour.utilities.structures.\

123 Lookup.first_key_from_value` method.

124 """

125

126 lookup = Lookup(first_name="John", last_name="Doe", gender="male")

127 assert lookup.first_key_from_value("John") == "first_name"

128

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"

133

134 def test_raise_exception_first_key_from_value(self) -> None:

135 """

136 Test :meth:`colour.utilities.structures.\

137 Lookup.first_key_from_value` method raised exception.

138 """

139

140 pytest.raises(IndexError, Lookup().first_key_from_value, "John")

141

142

143 class TestCanonicalMapping:

144 """

145 Define :class:`colour.utilities.structures.CanonicalMapping` class

146 unit tests methods.

147 """

148

149 def test_required_attributes(self) -> None:

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

151

152 required_attributes = ("data",)

153

154 for attribute in required_attributes:

155 assert attribute in dir(CanonicalMapping)

156

157 def test_required_methods(self) -> None:

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

159

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 )

179

180 for method in required_methods:

181 assert method in dir(CanonicalMapping)

182

183 def test_data(self) -> None:

184 """

185 Test :meth:`colour.utilities.structures.CanonicalMapping.data`

186 property.

187 """

188

189 assert CanonicalMapping({"John": "Doe", "Jane": "Doe"}).data == {

190 "John": "Doe",

191 "Jane": "Doe",

192 }

193

194 def test__repr__(self) -> None:

195 """

196 Test :meth:`colour.utilities.structures.CanonicalMapping.__repr__`

197 method.

198 """

199

200 mapping = CanonicalMapping()

201

202 mapping["John"] = "Doe"

203 assert repr(mapping) == "CanonicalMapping({'John': 'Doe'})"

204

205 def test__setitem__(self) -> None:

206 """

207 Test :meth:`colour.utilities.structures.CanonicalMapping.\

208 __setitem__` method.

209 """

210

211 mapping = CanonicalMapping()

212

213 mapping["John"] = "Doe"

214 assert mapping["John"] == "Doe"

215 assert mapping["john"] == "Doe"

216

217 def test__getitem__(self) -> None:

218 """

219 Test :meth:`colour.utilities.structures.CanonicalMapping.\

220 __getitem__` method.

221 """

222

223 mapping = CanonicalMapping(John="Doe", Jane="Doe")

224

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"

231

232 mapping = CanonicalMapping({1: "Foo", 2: "Bar"})

233

234 assert mapping[1] == "Foo"

235

236 mapping = CanonicalMapping({"McCamy 1992": 1, "Hernandez 1999": 2})

237

238 assert mapping["mccamy-1992"] == 1

239 assert mapping["hernandez-1999"] == 2

240 assert mapping["mccamy1992"] == 1

241 assert mapping["hernandez1999"] == 2

242

243 def test__delitem__(self) -> None:

244 """

245 Test :meth:`colour.utilities.structures.CanonicalMapping.\

246 __delitem__` method.

247 """

248

249 mapping = CanonicalMapping(John="Doe", Jane="Doe")

250

251 del mapping["john"]

252 assert "John" not in mapping

253

254 del mapping["Jane"]

255 assert "jane" not in mapping

256 assert len(mapping) == 0

257

258 mapping = CanonicalMapping(John="Doe", Jane="Doe")

259 del mapping["JOHN"]

260 assert "John" not in mapping

261

262 del mapping["jane"]

263 assert "jane" not in mapping

264 assert len(mapping) == 0

265

266 mapping = CanonicalMapping({"McCamy 1992": 1, "Hernandez 1999": 2})

267

268 del mapping["mccamy-1992"]

269 assert "McCamy 1992" not in mapping

270

271 del mapping["hernandez-1999"]

272 assert "Hernandez 1999" not in mapping

273

274 assert len(mapping) == 0

275

276 mapping = CanonicalMapping({"McCamy 1992": 1, "Hernandez 1999": 2})

277

278 del mapping["mccamy1992"]

279 assert "McCamy 1992" not in mapping

280

281 del mapping["hernandez1999"]

282 assert "Hernandez 1999" not in mapping

283

284 assert len(mapping) == 0

285

286 def test__contains__(self) -> None:

287 """

288 Test :meth:`colour.utilities.structures.CanonicalMapping.\

289 __contains__` method.

290 """

291

292 mapping = CanonicalMapping(John="Doe", Jane="Doe")

293

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

300

301 mapping = CanonicalMapping({"McCamy 1992": 1, "Hernandez 1999": 2})

302

303 assert "mccamy-1992" in mapping

304 assert "hernandez-1999" in mapping

305 assert "mccamy1992" in mapping

306 assert "hernandez1999" in mapping

307

308 def test__iter__(self) -> None:

309 """

310 Test :meth:`colour.utilities.structures.CanonicalMapping.__iter__`

311 method.

312 """

313

314 mapping = CanonicalMapping(John="Doe", Jane="Doe")

315 assert sorted(item for item in mapping) == ["Jane", "John"]

316

317 def test__len__(self) -> None:

318 """

319 Test :meth:`colour.utilities.structures.CanonicalMapping.__len__`

320 method.

321 """

322

323 assert len(CanonicalMapping()) == 0

324

325 assert len(CanonicalMapping(John="Doe", Jane="Doe")) == 2

326

327 def test__eq__(self) -> None:

328 """

329 Test :meth:`colour.utilities.structures.CanonicalMapping.__eq__`

330 method.

331 """

332

333 mapping1 = CanonicalMapping(John="Doe", Jane="Doe")

334 mapping2 = CanonicalMapping(John="Doe", Jane="Doe")

335 mapping3 = CanonicalMapping(john="Doe", jane="Doe")

336

337 assert mapping1 == mapping2

338

339 assert mapping2 != mapping3

340

341 def test_raise_exception__eq__(self) -> None:

342 """

343 Test :meth:`colour.utilities.structures.CanonicalMapping.__eq__`

344 method raised exception.

345 """

346

347 pytest.raises(

348 TypeError,

349 operator.eq,

350 CanonicalMapping(John="Doe", Jane="Doe"),

351 ["John", "Doe", "Jane", "Doe"],

352 )

353

354 def test__ne__(self) -> None:

355 """

356 Test :meth:`colour.utilities.structures.CanonicalMapping.__ne__`

357 method.

358 """

359

360 mapping1 = CanonicalMapping(John="Doe", Jane="Doe")

361 mapping2 = CanonicalMapping(Gi="Doe", Jane="Doe")

362

363 assert mapping1 != mapping2

364

365 def test_raise_exception__ne__(self) -> None:

366 """

367 Test :meth:`colour.utilities.structures.CanonicalMapping.__ne__`

368 method raised exception.

369 """

370

371 pytest.raises(

372 TypeError,

373 operator.ne,

374 CanonicalMapping(John="Doe", Jane="Doe"),

375 ["John", "Doe", "Jane", "Doe"],

376 )

377

378 def test_copy(self) -> None:

379 """

380 Test :meth:`colour.utilities.structures.CanonicalMapping.copy`

381 method.

382 """

383

384 mapping1 = CanonicalMapping(John="Doe", Jane="Doe")

385 mapping2 = mapping1.copy()

386

387 assert mapping1 == mapping2

388

389 assert id(mapping1) != id(mapping2)

390

391 def test_lower_keys(self) -> None:

392 """

393 Test :meth:`colour.utilities.structures.CanonicalMapping.\

394 lower_keys` method.

395 """

396

397 mapping = CanonicalMapping(John="Doe", Jane="Doe")

398

399 assert sorted(item for item in mapping.lower_keys()) == ["jane", "john"]

400

401 mapping = CanonicalMapping(John="Doe", john="Doe")

402

403 pytest.warns(ColourUsageWarning, lambda: list(mapping.lower_keys()))

404

405 def test_lower_items(self) -> None:

406 """

407 Test :meth:`colour.utilities.structures.CanonicalMapping.\

408 lower_items` method.

409 """

410

411 mapping = CanonicalMapping(John="Doe", Jane="Doe")

412

413 assert sorted(item for item in mapping.lower_items()) == [

414 ("jane", "Doe"),

415 ("john", "Doe"),

416 ]

417

418 def test_slugified_keys(self) -> None:

419 """

420 Test :meth:`colour.utilities.structures.CanonicalMapping.\

421 slugified_keys` method.

422 """

423

424 mapping = CanonicalMapping({"McCamy 1992": 1, "Hernandez 1999": 2})

425

426 assert sorted(item for item in mapping.slugified_keys()) == [

427 "hernandez-1999",

428 "mccamy-1992",

429 ]

430

431 mapping = CanonicalMapping({"McCamy 1992": 1, "McCamy-1992": 2})

432

433 pytest.warns(ColourUsageWarning, lambda: list(mapping.slugified_keys()))

434

435 def test_slugified_items(self) -> None:

436 """

437 Test :meth:`colour.utilities.structures.CanonicalMapping.\

438 slugified_items` method.

439 """

440

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 ]

446

447 def test_canonical_keys(self) -> None:

448 """

449 Test :meth:`colour.utilities.structures.CanonicalMapping.\

450 canonical_keys` method.

451 """

452

453 mapping = CanonicalMapping({"McCamy 1992": 1, "Hernandez 1999": 2})

454

455 assert sorted(item for item in mapping.canonical_keys()) == [

456 "hernandez1999",

457 "mccamy1992",

458 ]

459

460 mapping = CanonicalMapping({"McCamy_1992": 1, "McCamy-1992": 2})

461

462 pytest.warns(ColourUsageWarning, lambda: list(mapping.canonical_keys()))

463

464 def test_canonical_items(self) -> None:

465 """

466 Test :meth:`colour.utilities.structures.CanonicalMapping.\

467 canonical_items` method.

468 """

469

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 ]

475

476

477 class TestLazyCanonicalMapping:

478 """

479 Define :class:`colour.utilities.structures.LazyCanonicalMapping` class

480 unit tests methods.

481 """

482

483 def test_required_attributes(self) -> None:

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

485

486 required_attributes = ()

487

488 for attribute in required_attributes: # pragma: no cover

489 assert attribute in dir(LazyCanonicalMapping)

490

491 def test_required_methods(self) -> None:

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

493

494 required_methods = ("__getitem__",)

495

496 for method in required_methods:

497 assert method in dir(LazyCanonicalMapping)

498

499 def test__getitem__(self) -> None:

500 """

501 Test :meth:`colour.utilities.structures.LazyCanonicalMapping.\

502 __getitem__` method.

503 """

504

505 mapping = LazyCanonicalMapping(John="Doe", Jane=lambda: "Doe")

506

507 assert mapping["John"] == "Doe"

508 assert mapping["john"] == "Doe"

509 assert mapping["Jane"] == "Doe"

510 assert mapping["jane"] == "Doe"