@@ -879,6 +879,130 @@ async def test_bitop_string_operands(self, r: redis.Redis):
879
879
assert int (binascii .hexlify (await r .get ("res2" )), 16 ) == 0x0102FFFF
880
880
assert int (binascii .hexlify (await r .get ("res3" )), 16 ) == 0x000000FF
881
881
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
+
882
1006
@pytest .mark .onlynoncluster
883
1007
@skip_if_server_version_lt ("2.8.7" )
884
1008
async def test_bitpos (self , r : redis .Redis ):
0 commit comments