Skip to content

Commit e35e559

Browse files
committed
Add support for new BITOP operations: DIFF, DIFF1, ANDOR, ONE
1 parent 513c8d0 commit e35e559

File tree

2 files changed

+221
-0
lines changed

2 files changed

+221
-0
lines changed

tests/test_asyncio/test_commands.py

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -879,6 +879,130 @@ async def test_bitop_string_operands(self, r: redis.Redis):
879879
assert int(binascii.hexlify(await r.get("res2")), 16) == 0x0102FFFF
880880
assert int(binascii.hexlify(await r.get("res3")), 16) == 0x000000FF
881881

882+
@pytest.mark.onlynoncluster
883+
@skip_if_server_version_lt("8.2.0")
884+
async def test_bitop_diff(self, r: redis.Redis):
885+
"""Test BITOP DIFF operation"""
886+
# Set up test data: a=0b11110000, b=0b11000000, c=0b10000000
887+
await r.set("a", b"\xf0") # 11110000
888+
await r.set("b", b"\xc0") # 11000000
889+
await r.set("c", b"\x80") # 10000000
890+
891+
# DIFF: a AND NOT(b OR c) = 11110000 AND NOT(11000000) = 11110000 AND 00111111 = 00110000
892+
result = await r.bitop("DIFF", "result", "a", "b", "c")
893+
assert result == 1 # Length of result
894+
assert await r.get("result") == b"\x30" # 00110000
895+
896+
# Test with non-existent keys
897+
await r.bitop("DIFF", "result2", "a", "nonexistent")
898+
assert await r.get("result2") == b"\xf0" # Should be same as 'a'
899+
900+
@pytest.mark.onlynoncluster
901+
@skip_if_server_version_lt("8.2.0")
902+
async def test_bitop_diff1(self, r: redis.Redis):
903+
"""Test BITOP DIFF1 operation"""
904+
# Set up test data: a=0b11110000, b=0b11000000, c=0b10000000
905+
await r.set("a", b"\xf0") # 11110000
906+
await r.set("b", b"\xc0") # 11000000
907+
await r.set("c", b"\x80") # 10000000
908+
909+
# DIFF1: NOT(a) AND (b OR c) = NOT(11110000) AND (11000000) = 00001111 AND 11000000 = 00000000
910+
result = await r.bitop("DIFF1", "result", "a", "b", "c")
911+
assert result == 1 # Length of result
912+
assert await r.get("result") == b"\x00" # 00000000
913+
914+
# Test with different data where result is non-zero
915+
await r.set("d", b"\x0f") # 00001111
916+
await r.set("e", b"\x03") # 00000011
917+
# DIFF1: NOT(d) AND e = NOT(00001111) AND 00000011 = 11110000 AND 00000011 = 00000000
918+
await r.bitop("DIFF1", "result2", "d", "e")
919+
assert await r.get("result2") == b"\x00"
920+
921+
@pytest.mark.onlynoncluster
922+
@skip_if_server_version_lt("8.2.0")
923+
async def test_bitop_andor(self, r: redis.Redis):
924+
"""Test BITOP ANDOR operation"""
925+
# Set up test data: a=0b11110000, b=0b11000000, c=0b10000000
926+
await r.set("a", b"\xf0") # 11110000
927+
await r.set("b", b"\xc0") # 11000000
928+
await r.set("c", b"\x80") # 10000000
929+
930+
# ANDOR: a AND (b OR c) = 11110000 AND (11000000) = 11110000 AND 11000000 = 11000000
931+
result = await r.bitop("ANDOR", "result", "a", "b", "c")
932+
assert result == 1 # Length of result
933+
assert await r.get("result") == b"\xc0" # 11000000
934+
935+
# Test with non-overlapping bits
936+
await r.set("x", b"\xf0") # 11110000
937+
await r.set("y", b"\x0f") # 00001111
938+
await r.bitop("ANDOR", "result2", "x", "y")
939+
assert await r.get("result2") == b"\x00" # No overlap
940+
941+
@pytest.mark.onlynoncluster
942+
@skip_if_server_version_lt("8.2.0")
943+
async def test_bitop_one(self, r: redis.Redis):
944+
"""Test BITOP ONE operation"""
945+
# Set up test data: a=0b11110000, b=0b11000000, c=0b10000000
946+
await r.set("a", b"\xf0") # 11110000
947+
await r.set("b", b"\xc0") # 11000000
948+
await r.set("c", b"\x80") # 10000000
949+
950+
# ONE: bits set in exactly one key
951+
# Position analysis:
952+
# Bit 7: a=1, b=1, c=1 -> count=3 -> not included
953+
# Bit 6: a=1, b=1, c=0 -> count=2 -> not included
954+
# Bit 5: a=1, b=0, c=0 -> count=1 -> included
955+
# Bit 4: a=1, b=0, c=0 -> count=1 -> included
956+
# Expected result: 00110000 = 0x30
957+
result = await r.bitop("ONE", "result", "a", "b", "c")
958+
assert result == 1 # Length of result
959+
assert await r.get("result") == b"\x30" # 00110000
960+
961+
# Test with two keys (should be equivalent to XOR)
962+
await r.set("x", b"\xf0") # 11110000
963+
await r.set("y", b"\x0f") # 00001111
964+
await r.bitop("ONE", "result2", "x", "y")
965+
assert await r.get("result2") == b"\xff" # 11111111 (XOR result)
966+
967+
@pytest.mark.onlynoncluster
968+
@skip_if_server_version_lt("8.2.0")
969+
async def test_bitop_new_operations_with_empty_keys(self, r: redis.Redis):
970+
"""Test new BITOP operations with empty/non-existent keys"""
971+
await r.set("a", b"\xff") # 11111111
972+
973+
# Test with empty destination
974+
await r.bitop("DIFF", "empty_result", "nonexistent", "a")
975+
assert await r.get("empty_result") is None
976+
977+
await r.bitop("DIFF1", "empty_result2", "a", "nonexistent")
978+
assert await r.get("empty_result2") is None
979+
980+
await r.bitop("ANDOR", "empty_result3", "a", "nonexistent")
981+
assert await r.get("empty_result3") is None
982+
983+
await r.bitop("ONE", "empty_result4", "nonexistent")
984+
assert await r.get("empty_result4") is None
985+
986+
@pytest.mark.onlynoncluster
987+
@skip_if_server_version_lt("8.2.0")
988+
async def test_bitop_new_operations_return_values(self, r: redis.Redis):
989+
"""Test that new BITOP operations return correct length values"""
990+
await r.set("a", b"\xff\x00\xff") # 3 bytes
991+
await r.set("b", b"\x00\xff") # 2 bytes
992+
993+
# All operations should return the length of the result string
994+
result1 = await r.bitop("DIFF", "result1", "a", "b")
995+
assert result1 == 3 # Length of longest input
996+
997+
result2 = await r.bitop("DIFF1", "result2", "a", "b")
998+
assert result2 == 3
999+
1000+
result3 = await r.bitop("ANDOR", "result3", "a", "b")
1001+
assert result3 == 3
1002+
1003+
result4 = await r.bitop("ONE", "result4", "a", "b")
1004+
assert result4 == 3
1005+
8821006
@pytest.mark.onlynoncluster
8831007
@skip_if_server_version_lt("2.8.7")
8841008
async def test_bitpos(self, r: redis.Redis):

tests/test_commands.py

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1313,6 +1313,103 @@ def test_bitop_string_operands(self, r):
13131313
assert int(binascii.hexlify(r["res2"]), 16) == 0x0102FFFF
13141314
assert int(binascii.hexlify(r["res3"]), 16) == 0x000000FF
13151315

1316+
@pytest.mark.onlynoncluster
1317+
@skip_if_server_version_lt("8.2.0")
1318+
def test_bitop_diff(self, r):
1319+
r["a"] = b"\xf0"
1320+
r["b"] = b"\xc0"
1321+
r["c"] = b"\x80"
1322+
1323+
result = r.bitop("DIFF", "result", "a", "b", "c")
1324+
assert result == 1
1325+
assert r["result"] == b"\x30"
1326+
1327+
r.bitop("DIFF", "result2", "a", "nonexistent")
1328+
assert r["result2"] == b"\xf0"
1329+
1330+
@pytest.mark.onlynoncluster
1331+
@skip_if_server_version_lt("8.2.0")
1332+
def test_bitop_diff1(self, r):
1333+
r["a"] = b"\xf0"
1334+
r["b"] = b"\xc0"
1335+
r["c"] = b"\x80"
1336+
1337+
result = r.bitop("DIFF1", "result", "a", "b", "c")
1338+
assert result == 1
1339+
assert r["result"] == b"\x00"
1340+
1341+
r["d"] = b"\x0f"
1342+
r["e"] = b"\x03"
1343+
r.bitop("DIFF1", "result2", "d", "e")
1344+
assert r["result2"] == b"\x00"
1345+
1346+
@pytest.mark.onlynoncluster
1347+
@skip_if_server_version_lt("8.2.0")
1348+
def test_bitop_andor(self, r):
1349+
r["a"] = b"\xf0"
1350+
r["b"] = b"\xc0"
1351+
r["c"] = b"\x80"
1352+
1353+
result = r.bitop("ANDOR", "result", "a", "b", "c")
1354+
assert result == 1
1355+
assert r["result"] == b"\xc0"
1356+
1357+
r["x"] = b"\xf0"
1358+
r["y"] = b"\x0f"
1359+
r.bitop("ANDOR", "result2", "x", "y")
1360+
assert r["result2"] == b"\x00"
1361+
1362+
@pytest.mark.onlynoncluster
1363+
@skip_if_server_version_lt("8.2.0")
1364+
def test_bitop_one(self, r):
1365+
r["a"] = b"\xf0"
1366+
r["b"] = b"\xc0"
1367+
r["c"] = b"\x80"
1368+
1369+
result = r.bitop("ONE", "result", "a", "b", "c")
1370+
assert result == 1
1371+
assert r["result"] == b"\x30"
1372+
1373+
r["x"] = b"\xf0"
1374+
r["y"] = b"\x0f"
1375+
r.bitop("ONE", "result2", "x", "y")
1376+
assert r["result2"] == b"\xff"
1377+
1378+
@pytest.mark.onlynoncluster
1379+
@skip_if_server_version_lt("8.2.0")
1380+
def test_bitop_new_operations_with_empty_keys(self, r):
1381+
r["a"] = b"\xff"
1382+
1383+
r.bitop("DIFF", "empty_result", "nonexistent", "a")
1384+
assert r.get("empty_result") is None
1385+
1386+
r.bitop("DIFF1", "empty_result2", "a", "nonexistent")
1387+
assert r.get("empty_result2") is None
1388+
1389+
r.bitop("ANDOR", "empty_result3", "a", "nonexistent")
1390+
assert r.get("empty_result3") is None
1391+
1392+
r.bitop("ONE", "empty_result4", "nonexistent")
1393+
assert r.get("empty_result4") is None
1394+
1395+
@pytest.mark.onlynoncluster
1396+
@skip_if_server_version_lt("8.2.0")
1397+
def test_bitop_new_operations_return_values(self, r):
1398+
r["a"] = b"\xff\x00\xff"
1399+
r["b"] = b"\x00\xff"
1400+
1401+
result1 = r.bitop("DIFF", "result1", "a", "b")
1402+
assert result1 == 3
1403+
1404+
result2 = r.bitop("DIFF1", "result2", "a", "b")
1405+
assert result2 == 3
1406+
1407+
result3 = r.bitop("ANDOR", "result3", "a", "b")
1408+
assert result3 == 3
1409+
1410+
result4 = r.bitop("ONE", "result4", "a", "b")
1411+
assert result4 == 3
1412+
13161413
@pytest.mark.onlynoncluster
13171414
@skip_if_server_version_lt("2.8.7")
13181415
def test_bitpos(self, r):

0 commit comments

Comments
 (0)