@@ -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 ):
0 commit comments