bitmask/tests/test_bitmask.py

360 lines
10 KiB
Python
Raw Normal View History

2022-07-31 20:33:06 -04:00
# Copyright 2022 dogeystamp <dogeystamp@disroot.org>
# See LICENSE file for more details.
from bitmask import Bitmask
from enum import IntFlag
import pytest
2022-08-02 18:11:51 -04:00
2022-07-31 20:33:06 -04:00
class Desc(IntFlag):
SMALL = 1
ROUND = 1 << 1
FUNKY = 1 << 2
SONAR = 1 << 4
2022-08-02 18:11:51 -04:00
class Colors(IntFlag):
TEAL = 1
PINK = 1 << 1
BLUE = 1 << 2
2022-08-02 18:11:51 -04:00
2022-07-31 20:33:06 -04:00
def test_eq():
"""Test equality checks."""
# Equality
assert Bitmask() == Bitmask()
assert Bitmask(Desc.SMALL) == Bitmask(Desc.SMALL)
2022-08-03 14:58:25 -04:00
assert Bitmask(Desc.SMALL, Desc.ROUND) == Bitmask(Desc.SMALL, Desc.ROUND)
assert Bitmask(Desc.ROUND, Desc.SMALL) == Bitmask(Desc.SMALL, Desc.ROUND)
2022-07-31 20:33:06 -04:00
# Inequality
assert Bitmask(Desc.SMALL) != Bitmask(Desc.SMALL, Desc.ROUND)
assert Bitmask(Desc.SMALL) != Bitmask(Desc.ROUND)
assert Bitmask(Desc.SMALL) != Bitmask()
assert Bitmask(Desc.SMALL) != Desc.SMALL
assert Bitmask(Desc.SMALL) != Desc.ROUND
assert Bitmask() != Desc.ROUND
# Wrong types
assert Bitmask() != "Hello World!"
assert Bitmask() != 0
2022-08-03 14:58:25 -04:00
assert Bitmask(Desc.SMALL) != Bitmask(Colors.TEAL)
2022-07-31 20:33:06 -04:00
2022-08-02 18:11:51 -04:00
def test_all_flags():
"""Test the Bitmask.AllFlags and Bitmask.defined attributes."""
mask = Bitmask()
assert not mask.defined
mask.AllFlags = Desc
assert mask.defined
with pytest.raises(TypeError, match="conflicting Enum types"):
mask += Colors.TEAL
mask = Bitmask()
mask.add(Desc.SMALL)
assert mask.AllFlags == Desc
mask = Bitmask()
with pytest.raises(TypeError, match="flags aren't Enum values"):
mask.AllFlags = 1
mask = Bitmask(AllFlags=Desc)
assert mask.defined
with pytest.raises(TypeError, match="conflicting Enum types"):
mask += Colors.TEAL
def test_format_types():
"""Test error formatting."""
mask = Bitmask()
assert mask._Bitmask__format_types() == "Bitmask"
mask += Desc.SMALL
assert mask._Bitmask__format_types() == "Bitmask or Desc"
2022-07-31 20:33:06 -04:00
def test_repr():
"""Ensure evaluating __repr__ creates an identical object."""
mask = Bitmask(Desc.ROUND, Desc.FUNKY)
2022-07-31 20:33:06 -04:00
assert eval(repr(mask)) == mask
empty_mask = Bitmask()
2022-07-31 20:33:06 -04:00
assert eval(repr(empty_mask)) == empty_mask
2022-08-02 18:11:51 -04:00
2022-07-31 20:33:06 -04:00
def test_add():
"""Test Bitmask.add() method."""
mask = Bitmask(Desc.SMALL, Desc.FUNKY)
2022-07-31 20:33:06 -04:00
mask.add(Desc.ROUND)
assert mask == Bitmask(Desc.SMALL, Desc.FUNKY, Desc.ROUND)
2022-07-31 20:33:06 -04:00
mask = Bitmask()
2022-07-31 20:33:06 -04:00
mask.add(Desc.ROUND)
assert mask == Bitmask(Desc.ROUND)
2022-07-31 20:33:06 -04:00
mask.add(Desc.ROUND)
assert mask == Bitmask(Desc.ROUND)
2022-07-31 20:33:06 -04:00
2022-08-03 14:58:25 -04:00
with pytest.raises(TypeError, match=".* Bitmask .* Desc .* Bitmask.*"):
mask.add(1)
2022-08-02 18:11:51 -04:00
with pytest.raises(TypeError, match=r".* Bitmask .* Desc .* Bitmask.*"):
mask += 1
2022-08-02 18:11:51 -04:00
2022-07-31 20:33:06 -04:00
def test_add_operator():
"""Test + operator."""
# Individual flags
mask = Bitmask(Desc.SMALL, Desc.FUNKY)
assert mask + Desc.ROUND == Bitmask(Desc.SMALL, Desc.FUNKY, Desc.ROUND)
assert Desc.ROUND + mask == Bitmask(Desc.SMALL, Desc.FUNKY, Desc.ROUND)
assert Desc.SMALL + mask == Bitmask(Desc.SMALL, Desc.FUNKY)
assert Bitmask() + Desc.ROUND == Bitmask(Desc.ROUND)
2022-07-31 20:33:06 -04:00
# Union of bitmasks
assert Bitmask(Desc.SMALL) + Bitmask(Desc.FUNKY, Desc.ROUND) == Bitmask(
Desc.SMALL, Desc.FUNKY, Desc.ROUND
2022-08-02 18:11:51 -04:00
)
assert Bitmask(Desc.FUNKY, Desc.ROUND) + Bitmask(Desc.SMALL) == Bitmask(
Desc.SMALL, Desc.FUNKY, Desc.ROUND
2022-08-02 18:11:51 -04:00
)
assert Bitmask() + Bitmask() == Bitmask()
2022-07-31 20:33:06 -04:00
2022-08-02 18:11:51 -04:00
2022-07-31 20:33:06 -04:00
def test_add_inline():
"""Test += operator."""
# Individual flags
mask = Bitmask()
2022-07-31 20:33:06 -04:00
mask += Desc.FUNKY
assert mask == Bitmask(Desc.FUNKY)
2022-07-31 20:33:06 -04:00
mask += Desc.ROUND
assert mask == Bitmask(Desc.FUNKY, Desc.ROUND)
2022-07-31 20:33:06 -04:00
# Bitmasks
mask = Bitmask()
mask += Bitmask(Desc.ROUND, Desc.FUNKY)
assert mask == Bitmask(Desc.FUNKY, Desc.ROUND)
2022-07-31 20:33:06 -04:00
2022-08-02 18:11:51 -04:00
2022-07-31 20:33:06 -04:00
def test_or_operator():
"""Test | operator."""
# Individual flags
mask = Bitmask(Desc.SMALL, Desc.FUNKY)
assert mask | Desc.ROUND == Bitmask(Desc.SMALL, Desc.FUNKY, Desc.ROUND)
assert Desc.ROUND | mask == Bitmask(Desc.SMALL, Desc.FUNKY, Desc.ROUND)
assert Desc.SMALL | mask == Bitmask(Desc.SMALL, Desc.FUNKY)
assert Bitmask() | Desc.ROUND == Bitmask(Desc.ROUND)
2022-07-31 20:33:06 -04:00
# Union of bitmasks
assert Bitmask(Desc.SMALL) | Bitmask(Desc.FUNKY, Desc.ROUND) == Bitmask(
Desc.SMALL, Desc.FUNKY, Desc.ROUND
2022-08-02 18:11:51 -04:00
)
assert Bitmask(Desc.FUNKY, Desc.ROUND) | Bitmask(Desc.SMALL) == Bitmask(
Desc.SMALL, Desc.FUNKY, Desc.ROUND
2022-08-02 18:11:51 -04:00
)
assert Bitmask() | Bitmask() == Bitmask()
2022-07-31 20:33:06 -04:00
2022-08-02 18:11:51 -04:00
2022-07-31 20:33:06 -04:00
def test_or_inline():
"""Test |= operator."""
# Individual flags
mask = Bitmask()
2022-07-31 20:33:06 -04:00
mask |= Desc.FUNKY
assert mask == Bitmask(Desc.FUNKY)
2022-07-31 20:33:06 -04:00
mask |= Desc.ROUND
assert mask == Bitmask(Desc.FUNKY, Desc.ROUND)
2022-07-31 20:33:06 -04:00
# Bitmasks
mask = Bitmask()
mask |= Bitmask(Desc.ROUND, Desc.FUNKY)
assert mask == Bitmask(Desc.FUNKY, Desc.ROUND)
2022-07-31 20:33:06 -04:00
2022-08-02 18:11:51 -04:00
2022-07-31 20:33:06 -04:00
def test_and_operator():
"""Test & operator."""
# Individual flags
mask = Bitmask(Desc.SMALL, Desc.FUNKY)
assert mask & Desc.SMALL == Bitmask(Desc.SMALL)
assert Desc.SMALL & mask == Bitmask(Desc.SMALL)
assert Desc.ROUND & mask == Bitmask()
assert Bitmask() & Desc.ROUND == Bitmask()
2022-07-31 20:33:06 -04:00
# AND of bitmasks
2022-08-03 15:23:28 -04:00
assert Bitmask(Desc.FUNKY, Desc.SONAR) & Bitmask(Desc.FUNKY, Desc.ROUND) == Bitmask(
Desc.FUNKY
)
assert Bitmask(Desc.FUNKY, Desc.ROUND) & Bitmask(Desc.SMALL) == Bitmask()
assert Bitmask() & Bitmask() == Bitmask()
2022-07-31 20:33:06 -04:00
2022-08-02 18:11:51 -04:00
2022-07-31 20:33:06 -04:00
def test_and_inline():
"""Test &= operator."""
# Individual flags
mask = Bitmask(Desc.FUNKY, Desc.SMALL)
2022-07-31 20:33:06 -04:00
mask &= Desc.FUNKY
assert mask == Bitmask(Desc.FUNKY)
2022-07-31 20:33:06 -04:00
mask &= Desc.ROUND
assert mask == Bitmask()
2022-07-31 20:33:06 -04:00
# Bitmasks
mask = Bitmask(Desc.ROUND, Desc.FUNKY)
mask &= Bitmask(Desc.SMALL)
assert mask == Bitmask()
2022-08-02 18:11:51 -04:00
2022-07-31 20:33:06 -04:00
def test_remove():
"""Test the `Bitmask.remove()` method."""
mask = Bitmask(Desc.SMALL, Desc.FUNKY)
2022-07-31 20:33:06 -04:00
mask.remove(Desc.SMALL)
assert mask == Bitmask(Desc.FUNKY)
2022-07-31 20:33:06 -04:00
with pytest.raises(KeyError):
mask.remove(Desc.SMALL)
empty_mask = Bitmask()
2022-07-31 20:33:06 -04:00
with pytest.raises(KeyError):
empty_mask.remove(Desc.SMALL)
2022-08-02 18:11:51 -04:00
2022-07-31 20:33:06 -04:00
def test_discard():
"""Test the `Bitmask.discard()` method."""
mask = Bitmask(Desc.SMALL, Desc.FUNKY)
2022-07-31 20:33:06 -04:00
mask.discard(Desc.SMALL)
assert mask == Bitmask(Desc.FUNKY)
2022-07-31 20:33:06 -04:00
mask.discard(Desc.SMALL)
assert mask == Bitmask(Desc.FUNKY)
2022-07-31 20:33:06 -04:00
empty_mask = Bitmask()
2022-07-31 20:33:06 -04:00
empty_mask.discard(Desc.SMALL)
assert empty_mask == Bitmask()
2022-08-03 14:58:25 -04:00
with pytest.raises(TypeError, match=".* Bitmask or Desc from Bitmask"):
empty_mask.discard(Bitmask(Desc.SMALL))
2022-07-31 20:33:06 -04:00
2022-08-02 18:11:51 -04:00
2022-07-31 20:33:06 -04:00
def test_subtract():
"""Test - operator."""
# Individual flag
2022-08-03 14:58:25 -04:00
assert Bitmask(Desc.SMALL, Desc.FUNKY) - Desc.SMALL == Bitmask(Desc.FUNKY)
assert Bitmask(Desc.FUNKY, Desc.SMALL) - Desc.SMALL == Bitmask(Desc.FUNKY)
2022-07-31 20:33:06 -04:00
# Two bitmasks
assert Bitmask(Desc.SMALL, Desc.FUNKY, Desc.ROUND) - Bitmask(
Desc.SMALL, Desc.ROUND
) == Bitmask(Desc.FUNKY)
assert (
2022-08-03 15:23:28 -04:00
Bitmask(Desc.FUNKY, Desc.SMALL) - Bitmask(Desc.SMALL, Desc.FUNKY) == Bitmask()
)
2022-08-02 18:11:51 -04:00
2022-07-31 20:33:06 -04:00
def test_subtract_inline():
"""Test -= operator."""
mask = Bitmask(Desc.SMALL, Desc.FUNKY)
2022-07-31 20:33:06 -04:00
mask -= Desc.SMALL
assert mask == Bitmask(Desc.FUNKY)
2022-07-31 20:33:06 -04:00
2022-08-02 18:11:51 -04:00
2022-07-31 20:33:06 -04:00
def test_xor_operator():
"""Test ^ operator."""
# Individual flags
mask = Bitmask(Desc.SMALL, Desc.FUNKY)
assert mask ^ Desc.ROUND == Bitmask(Desc.SMALL, Desc.FUNKY, Desc.ROUND)
assert Desc.ROUND ^ mask == Bitmask(Desc.SMALL, Desc.FUNKY, Desc.ROUND)
assert Desc.SMALL ^ mask == Bitmask(Desc.FUNKY)
assert Bitmask() ^ Desc.ROUND == Bitmask(Desc.ROUND)
2022-07-31 20:33:06 -04:00
# XOR bitmasks
assert Bitmask(Desc.SMALL) ^ Bitmask(Desc.FUNKY, Desc.ROUND) == Bitmask(
Desc.SMALL, Desc.FUNKY, Desc.ROUND
2022-08-02 18:11:51 -04:00
)
assert (
2022-08-03 15:23:28 -04:00
Bitmask(Desc.FUNKY, Desc.ROUND) ^ Bitmask(Desc.FUNKY, Desc.ROUND) == Bitmask()
)
assert Bitmask() ^ Bitmask() == Bitmask()
2022-07-31 20:33:06 -04:00
2022-08-02 18:11:51 -04:00
2022-07-31 20:33:06 -04:00
def test_xor_inline():
"""Test ^= operator."""
# Individual flags
mask = Bitmask()
2022-07-31 20:33:06 -04:00
mask ^= Desc.FUNKY
assert mask == Bitmask(Desc.FUNKY)
2022-07-31 20:33:06 -04:00
mask ^= Desc.ROUND
assert mask == Bitmask(Desc.FUNKY, Desc.ROUND)
2022-07-31 20:33:06 -04:00
mask ^= Desc.ROUND
assert mask == Bitmask(Desc.FUNKY)
2022-07-31 20:33:06 -04:00
# Bitmasks
mask = Bitmask(Desc.ROUND)
mask ^= Bitmask(Desc.ROUND, Desc.FUNKY)
assert mask == Bitmask(Desc.FUNKY)
2022-07-31 20:33:06 -04:00
2022-08-02 18:11:51 -04:00
2022-07-31 20:33:06 -04:00
def test_value():
"""Ensure Bitmask.value lines up with the state."""
mask = Bitmask(Desc.SMALL, Desc.FUNKY)
2022-07-31 20:33:06 -04:00
assert mask.value == 5
mask.add(Desc.ROUND)
2022-08-02 18:11:51 -04:00
assert mask.value == 7
2022-07-31 20:33:06 -04:00
assert Bitmask().value == 0
2022-07-31 20:33:06 -04:00
# Setting values directly
mask.value = 0
assert mask == Bitmask()
2022-07-31 20:33:06 -04:00
mask.value = 1
assert mask == Bitmask(Desc.SMALL)
with pytest.raises(TypeError, match="value must be an integer"):
2022-07-31 20:33:06 -04:00
mask.value = 1j
with pytest.raises(TypeError, match="value must be an integer"):
2022-07-31 20:33:06 -04:00
mask.value = 2.5
2022-08-02 18:11:51 -04:00
2022-07-31 20:33:06 -04:00
def test_contains():
"""Test `flag in mask` check."""
mask = Bitmask(Desc.SMALL, Desc.FUNKY)
empty_mask = Bitmask()
2022-07-31 20:33:06 -04:00
# Individual flags
assert Desc.FUNKY in mask
assert Desc.SMALL in mask
assert Desc.ROUND not in mask
assert Desc.ROUND not in empty_mask
# Bitmasks
assert mask in mask
assert Bitmask(Desc.SMALL, Desc.FUNKY) in mask
assert Bitmask(Desc.SMALL, Desc.FUNKY, Desc.ROUND) not in mask
assert Bitmask(Desc.FUNKY) in mask
with pytest.raises(TypeError, match="item must be Bitmask or Desc"):
2022-08-02 18:11:51 -04:00
assert 1 in mask
2022-07-31 20:33:06 -04:00
def test_iter():
"""Test iteration."""
assert [i for i in Bitmask(Desc.SMALL, Desc.FUNKY)] == [
2022-08-02 18:11:51 -04:00
Desc.SMALL,
Desc.FUNKY,
]
2022-07-31 20:33:06 -04:00
def test_str():
"""Test string conversion."""
mask = Bitmask(Desc.SMALL, Desc.FUNKY)
2022-07-31 20:33:06 -04:00
assert str(mask) == "SMALL|FUNKY"
mask.add(Desc.ROUND)
assert str(mask) == "SMALL|ROUND|FUNKY"
assert str(Bitmask(Desc.ROUND)) == "ROUND"
assert str(Bitmask()) == "0"
2022-07-31 20:33:06 -04:00
2022-08-02 18:11:51 -04:00
2022-07-31 20:33:06 -04:00
def test_int():
"""Test int conversion."""
mask = Bitmask(Desc.SMALL, Desc.FUNKY)
2022-07-31 20:33:06 -04:00
assert int(mask) == mask.value
mask.value = 4
assert int(mask) == mask.value
2022-08-02 18:11:51 -04:00
2022-07-31 20:33:06 -04:00
def test_hex():
"""Test hexadecimal conversion."""
assert hex(Bitmask(Desc.SMALL)) == "0x1"
assert hex(Bitmask()) == "0x0"
assert hex(Bitmask(Desc.SONAR)) == "0x10"