@@ -1080,5 +1080,255 @@ TEST_F(ListFamilyTest, ContendExpire) {
1080
1080
}
1081
1081
}
1082
1082
1083
+ TEST_F (ListFamilyTest, LMPopInvalidSyntax) {
1084
+ // Not enough arguments
1085
+ auto resp = Run ({" lmpop" , " 1" , " a" });
1086
+ EXPECT_THAT (resp, ErrArg (" wrong number of arguments" ));
1087
+
1088
+ // Zero keys
1089
+ resp = Run ({" lmpop" , " 0" , " LEFT" , " COUNT" , " 1" });
1090
+ EXPECT_THAT (resp, ErrArg (" syntax error" ));
1091
+
1092
+ // Number of keys is not uint
1093
+ resp = Run ({" lmpop" , " aa" , " a" , " LEFT" });
1094
+ EXPECT_THAT (resp, ErrArg (" value is not an integer or out of range" ));
1095
+
1096
+ // Missing LEFT/RIGHT
1097
+ resp = Run ({" lmpop" , " 1" , " a" , " COUNT" , " 1" });
1098
+ EXPECT_THAT (resp, ErrArg (" syntax error" ));
1099
+
1100
+ // Wrong number of keys
1101
+ resp = Run ({" lmpop" , " 1" , " a" , " b" , " LEFT" });
1102
+ EXPECT_THAT (resp, ErrArg (" syntax error" ));
1103
+
1104
+ // COUNT without number
1105
+ resp = Run ({" lmpop" , " 1" , " a" , " LEFT" , " COUNT" });
1106
+ EXPECT_THAT (resp, ErrArg (" syntax error" ));
1107
+
1108
+ // COUNT is not uint
1109
+ resp = Run ({" lmpop" , " 1" , " a" , " LEFT" , " COUNT" , " boo" });
1110
+ EXPECT_THAT (resp, ErrArg (" value is not an integer or out of range" ));
1111
+
1112
+ // Too many arguments
1113
+ resp = Run ({" lmpop" , " 1" , " c" , " LEFT" , " COUNT" , " 2" , " foo" });
1114
+ EXPECT_THAT (resp, ErrArg (" syntax error" ));
1115
+ }
1116
+
1117
+ TEST_F (ListFamilyTest, LMPop) {
1118
+ // All lists are empty
1119
+ auto resp = Run ({" lmpop" , " 1" , " e" , " LEFT" });
1120
+ EXPECT_THAT (resp, ArgType (RespExpr::NIL));
1121
+
1122
+ // LEFT operation
1123
+ resp = Run ({" lpush" , " a" , " a1" , " a2" });
1124
+ EXPECT_THAT (resp, IntArg (2 ));
1125
+
1126
+ resp = Run ({" lmpop" , " 1" , " a" , " LEFT" });
1127
+ EXPECT_THAT (resp, RespArray (ElementsAre (" a" , RespArray (ElementsAre (" a2" )))));
1128
+
1129
+ // RIGHT operation
1130
+ resp = Run ({" lpush" , " b" , " b1" , " b2" });
1131
+ EXPECT_THAT (resp, IntArg (2 ));
1132
+
1133
+ resp = Run ({" lmpop" , " 1" , " b" , " RIGHT" });
1134
+ EXPECT_THAT (resp, RespArray (ElementsAre (" b" , RespArray (ElementsAre (" b1" )))));
1135
+
1136
+ // COUNT > 1
1137
+ resp = Run ({" lpush" , " c" , " c1" , " c2" });
1138
+ EXPECT_THAT (resp, IntArg (2 ));
1139
+
1140
+ resp = Run ({" lmpop" , " 1" , " c" , " RIGHT" , " COUNT" , " 2" });
1141
+ EXPECT_THAT (resp, RespArray (ElementsAre (" c" , RespArray (ElementsAre (" c1" , " c2" )))));
1142
+
1143
+ resp = Run ({" llen" , " c" });
1144
+ EXPECT_THAT (resp, IntArg (0 ));
1145
+
1146
+ // COUNT > number of elements in list
1147
+ resp = Run ({" lpush" , " d" , " d1" , " d2" });
1148
+ EXPECT_THAT (resp, IntArg (2 ));
1149
+
1150
+ resp = Run ({" lmpop" , " 1" , " d" , " RIGHT" , " COUNT" , " 3" });
1151
+ EXPECT_THAT (resp, RespArray (ElementsAre (" d" , RespArray (ElementsAre (" d1" , " d2" )))));
1152
+
1153
+ resp = Run ({" llen" , " d" });
1154
+ EXPECT_THAT (resp, IntArg (0 ));
1155
+
1156
+ // First non-empty list is not the first list
1157
+ resp = Run ({" lpush" , " x" , " x1" });
1158
+ EXPECT_THAT (resp, IntArg (1 ));
1159
+
1160
+ resp = Run ({" lpush" , " y" , " y1" });
1161
+ EXPECT_THAT (resp, IntArg (1 ));
1162
+
1163
+ resp = Run ({" lmpop" , " 3" , " empty" , " x" , " y" , " RIGHT" });
1164
+ EXPECT_THAT (resp, RespArray (ElementsAre (" x" , RespArray (ElementsAre (" x1" )))));
1165
+
1166
+ resp = Run ({" llen" , " x" });
1167
+ EXPECT_THAT (resp, IntArg (0 ));
1168
+ }
1169
+
1170
+ TEST_F (ListFamilyTest, LMPopMultipleElements) {
1171
+ // Test removing multiple elements from left end
1172
+ Run ({" rpush" , " list1" , " a" , " b" , " c" , " d" , " e" });
1173
+ auto resp = Run ({" lmpop" , " 1" , " list1" , " LEFT" , " COUNT" , " 3" });
1174
+ EXPECT_THAT (resp, RespArray (ElementsAre (" list1" , RespArray (ElementsAre (" a" , " b" , " c" )))));
1175
+
1176
+ resp = Run ({" lrange" , " list1" , " 0" , " -1" });
1177
+ EXPECT_THAT (resp.GetVec (), ElementsAre (" d" , " e" ));
1178
+
1179
+ // Test removing multiple elements from right end
1180
+ Run ({" rpush" , " list2" , " v" , " w" , " x" , " y" , " z" });
1181
+ resp = Run ({" lmpop" , " 1" , " list2" , " RIGHT" , " COUNT" , " 2" });
1182
+ EXPECT_THAT (resp, RespArray (ElementsAre (" list2" , RespArray (ElementsAre (" z" , " y" )))));
1183
+
1184
+ resp = Run ({" lrange" , " list2" , " 0" , " -1" });
1185
+ EXPECT_THAT (resp.GetVec (), ElementsAre (" v" , " w" , " x" ));
1186
+ }
1187
+
1188
+ TEST_F (ListFamilyTest, LMPopMultipleLists) {
1189
+ // Test finding first non-empty list
1190
+ Run ({" rpush" , " list1" , " a" , " b" });
1191
+ Run ({" rpush" , " list2" , " c" , " d" });
1192
+ Run ({" rpush" , " list3" , " e" , " f" });
1193
+
1194
+ // Pop from first non-empty list
1195
+ auto resp = Run ({" lmpop" , " 3" , " list1" , " list2" , " list3" , " LEFT" });
1196
+ EXPECT_THAT (resp, RespArray (ElementsAre (" list1" , RespArray (ElementsAre (" a" )))));
1197
+
1198
+ // Pop from second list after first becomes empty
1199
+ Run ({" lmpop" , " 1" , " list1" , " LEFT" }); // Empty list1
1200
+ resp = Run ({" lmpop" , " 3" , " list1" , " list2" , " list3" , " RIGHT" , " COUNT" , " 2" });
1201
+ EXPECT_THAT (resp, RespArray (ElementsAre (" list2" , RespArray (ElementsAre (" d" , " c" )))));
1202
+
1203
+ // Verify third list remains untouched
1204
+ resp = Run ({" lrange" , " list3" , " 0" , " -1" });
1205
+ EXPECT_THAT (resp.GetVec (), ElementsAre (" e" , " f" ));
1206
+ }
1207
+
1208
+ TEST_F (ListFamilyTest, LMPopEdgeCases) {
1209
+ // Test with empty list
1210
+ Run ({" rpush" , " empty_list" , " a" });
1211
+ Run ({" lpop" , " empty_list" });
1212
+ auto resp = Run ({" lmpop" , " 1" , " empty_list" , " LEFT" });
1213
+ EXPECT_THAT (resp, ArgType (RespExpr::NIL));
1214
+
1215
+ // Test with non-existent list
1216
+ resp = Run ({" lmpop" , " 1" , " nonexistent" , " LEFT" });
1217
+ EXPECT_THAT (resp, ArgType (RespExpr::NIL));
1218
+
1219
+ // Test with wrong type key
1220
+ Run ({" set" , " string_key" , " value" });
1221
+ resp = Run ({" lmpop" , " 1" , " string_key" , " LEFT" });
1222
+ EXPECT_THAT (resp, ErrArg (" WRONGTYPE Operation against a key holding the wrong kind of value" ));
1223
+
1224
+ // Test without COUNT parameter - should return 1 element by default
1225
+ Run ({" rpush" , " list" , " a" , " b" });
1226
+ resp = Run ({" lmpop" , " 1" , " list" , " LEFT" });
1227
+ EXPECT_THAT (resp,
1228
+ RespArray (ElementsAre (
1229
+ " list" , RespArray (ElementsAre (" a" ))))); // Should return 1 element by default
1230
+
1231
+ // Test with COUNT = 0 - should return error
1232
+ resp = Run ({" lmpop" , " 1" , " list" , " LEFT" , " COUNT" , " 0" });
1233
+ EXPECT_THAT (resp, RespArray (ElementsAre (" list" , RespArray (ElementsAre ()))));
1234
+
1235
+ // Test with negative COUNT - should return error
1236
+ resp = Run ({" lmpop" , " 1" , " list" , " LEFT" , " COUNT" , " -1" });
1237
+ EXPECT_THAT (resp, RespArray (ElementsAre (" list" , RespArray (ElementsAre (" b" )))));
1238
+ }
1239
+
1240
+ TEST_F (ListFamilyTest, LMPopDocExample) {
1241
+ // Try to pop from non-existing lists
1242
+ auto resp = Run ({" LMPOP" , " 2" , " non1" , " non2" , " LEFT" , " COUNT" , " 10" });
1243
+ EXPECT_THAT (resp, ArgType (RespExpr::NIL));
1244
+
1245
+ // Create first list and test basic pop
1246
+ resp = Run ({" LPUSH" , " mylist" , " one" , " two" , " three" , " four" , " five" });
1247
+ EXPECT_THAT (resp, IntArg (5 ));
1248
+
1249
+ resp = Run ({" LMPOP" , " 1" , " mylist" , " LEFT" });
1250
+ EXPECT_THAT (resp, RespArray (ElementsAre (" mylist" , RespArray (ElementsAre (" five" )))));
1251
+
1252
+ resp = Run ({" LRANGE" , " mylist" , " 0" , " -1" });
1253
+ EXPECT_THAT (resp.GetVec (), ElementsAre (" four" , " three" , " two" , " one" ));
1254
+
1255
+ // Test RIGHT pop with COUNT
1256
+ resp = Run ({" LMPOP" , " 1" , " mylist" , " RIGHT" , " COUNT" , " 10" });
1257
+ EXPECT_THAT (resp, RespArray (ElementsAre (" mylist" ,
1258
+ RespArray (ElementsAre (" one" , " two" , " three" , " four" )))));
1259
+
1260
+ // Create two lists and test multi-key pop
1261
+ resp = Run ({" LPUSH" , " mylist" , " one" , " two" , " three" , " four" , " five" });
1262
+ EXPECT_THAT (resp, IntArg (5 ));
1263
+
1264
+ resp = Run ({" LPUSH" , " mylist2" , " a" , " b" , " c" , " d" , " e" });
1265
+ EXPECT_THAT (resp, IntArg (5 ));
1266
+
1267
+ resp = Run ({" LMPOP" , " 2" , " mylist" , " mylist2" , " RIGHT" , " COUNT" , " 3" });
1268
+ EXPECT_THAT (resp,
1269
+ RespArray (ElementsAre (" mylist" , RespArray (ElementsAre (" one" , " two" , " three" )))));
1270
+
1271
+ resp = Run ({" LRANGE" , " mylist" , " 0" , " -1" });
1272
+ EXPECT_THAT (resp.GetVec (), ElementsAre (" five" , " four" ));
1273
+
1274
+ resp = Run ({" LMPOP" , " 2" , " mylist" , " mylist2" , " RIGHT" , " COUNT" , " 5" });
1275
+ EXPECT_THAT (resp, RespArray (ElementsAre (" mylist" , RespArray (ElementsAre (" four" , " five" )))));
1276
+
1277
+ resp = Run ({" LMPOP" , " 2" , " mylist" , " mylist2" , " RIGHT" , " COUNT" , " 10" });
1278
+ EXPECT_THAT (resp,
1279
+ RespArray (ElementsAre (" mylist2" , RespArray (ElementsAre (" a" , " b" , " c" , " d" , " e" )))));
1280
+
1281
+ // Verify both lists are now empty
1282
+ resp = Run ({" EXISTS" , " mylist" , " mylist2" });
1283
+ EXPECT_THAT (resp, IntArg (0 ));
1284
+ }
1285
+
1286
+ TEST_F (ListFamilyTest, LMPopWrongType) {
1287
+ // Setup: create a list and a hash
1288
+ Run ({" lpush" , " l1" , " e1" });
1289
+ Run ({" hset" , " foo" , " k1" , " v1" });
1290
+
1291
+ // Test: first key is wrong type
1292
+ auto resp = Run ({" lmpop" , " 2" , " foo" , " l1" , " left" });
1293
+ EXPECT_THAT (resp, ErrArg (" WRONGTYPE Operation against a key holding the wrong kind of value" ));
1294
+
1295
+ // Test: second key is wrong type but first doesn't exist
1296
+ resp = Run ({" lmpop" , " 2" , " nonexistent" , " foo" , " left" });
1297
+ EXPECT_THAT (resp, ErrArg (" WRONGTYPE Operation against a key holding the wrong kind of value" ));
1298
+
1299
+ // Test: second key is wrong type but first is a valid list
1300
+ resp = Run ({" lmpop" , " 2" , " l1" , " foo" , " left" });
1301
+ EXPECT_THAT (resp, RespArray (ElementsAre (" l1" , RespArray (ElementsAre (" e1" )))));
1302
+ }
1303
+
1304
+ // Reproduce a flow that trigerred a wrong DCHECK in the transaction flow.
1305
+ TEST_F (ListFamilyTest, AwakeMulti) {
1306
+ auto f1 = pp_->at (1 )->LaunchFiber (Launch::dispatch, [&] {
1307
+ for (unsigned i = 0 ; i < 100 ; ++i) {
1308
+ Run (" CONSUMER" , {" blmove" , " src" , " dest" , " LEFT" , " LEFT" , " 0" });
1309
+ };
1310
+ });
1311
+ auto f2 = pp_->at (1 )->LaunchFiber ([&] {
1312
+ for (unsigned i = 0 ; i < 100 ; ++i) {
1313
+ Run (" PROD" , {" lpush" , " src" , " a" });
1314
+ ThisFiber::SleepFor (50us);
1315
+ };
1316
+ });
1317
+
1318
+ auto f3 = pp_->at (2 )->LaunchFiber ([&] {
1319
+ for (unsigned i = 0 ; i < 100 ; ++i) {
1320
+ Run ({" multi" });
1321
+ for (unsigned j = 0 ; j < 8 ; ++j) {
1322
+ Run ({" get" , StrCat (" key" , j)});
1323
+ };
1324
+ Run ({" exec" });
1325
+ };
1326
+ });
1327
+
1328
+ f1.Join ();
1329
+ f2.Join ();
1330
+ f3.Join ();
1331
+ }
1332
+
1083
1333
#pragma GCC diagnostic pop
1084
1334
} // namespace dfly
0 commit comments