@@ -214,6 +214,163 @@ TEST(ArcReplacerTest, DISABLED_SampleTest2) {
214214 ASSERT_EQ (1 , arc_replacer.Evict ());
215215}
216216
217+ TEST (ArcReplacerTest, DISABLED_RemoveBehaviorTest) {
218+ // Scenario 1: Remove from empty replacer — should be a no-op
219+ {
220+ ArcReplacer arc_replacer (5 );
221+ arc_replacer.Remove (0 );
222+ ASSERT_EQ (0 , arc_replacer.Size ());
223+ }
224+
225+ // Scenario 2: Remove a non-existent frame_id — should be a no-op
226+ {
227+ ArcReplacer arc_replacer (5 );
228+ arc_replacer.RecordAccess (0 , 10 );
229+ arc_replacer.SetEvictable (0 , true );
230+ ASSERT_EQ (1 , arc_replacer.Size ());
231+ arc_replacer.Remove (99 ); // never inserted
232+ ASSERT_EQ (1 , arc_replacer.Size ());
233+ }
234+
235+ // Scenario 3: Remove an evictable frame from MRU
236+ {
237+ ArcReplacer arc_replacer (5 );
238+ arc_replacer.RecordAccess (0 , 10 );
239+ arc_replacer.SetEvictable (0 , true );
240+ arc_replacer.RecordAccess (1 , 11 );
241+ arc_replacer.SetEvictable (1 , true );
242+ arc_replacer.RecordAccess (2 , 12 );
243+ arc_replacer.SetEvictable (2 , true );
244+ // State: [][(10,f0),(11,f1),(12,f2)]![][] p=0
245+ ASSERT_EQ (3 , arc_replacer.Size ());
246+
247+ arc_replacer.Remove (1 ); // remove middle frame from MRU
248+ ASSERT_EQ (2 , arc_replacer.Size ());
249+
250+ // Evict remaining: should be f0 then f2 (oldest to newest in MRU)
251+ ASSERT_EQ (0 , arc_replacer.Evict ());
252+ ASSERT_EQ (2 , arc_replacer.Evict ());
253+ ASSERT_FALSE (arc_replacer.Evict ().has_value ());
254+ }
255+
256+ // Scenario 4: Remove an evictable frame from MFU
257+ {
258+ ArcReplacer arc_replacer (4 );
259+ arc_replacer.RecordAccess (0 , 20 );
260+ arc_replacer.SetEvictable (0 , true );
261+ arc_replacer.RecordAccess (1 , 21 );
262+ arc_replacer.SetEvictable (1 , true );
263+ // Move frame 0 to MFU by accessing again
264+ arc_replacer.RecordAccess (0 , 20 );
265+ // State: [][(21,f1)]![(20,f0)][] p=0
266+ ASSERT_EQ (2 , arc_replacer.Size ());
267+
268+ arc_replacer.Remove (0 ); // remove from MFU
269+ ASSERT_EQ (1 , arc_replacer.Size ());
270+
271+ // Only frame 1 remains
272+ ASSERT_EQ (1 , arc_replacer.Evict ());
273+ ASSERT_FALSE (arc_replacer.Evict ().has_value ());
274+ }
275+
276+ // Scenario 5: Remove does NOT create ghost entries
277+ // If Evict were used instead of Remove, page_id 30 would go into a ghost list.
278+ // On re-access, it would be a ghost hit → placed in MFU and target size adjusted.
279+ // With Remove, no ghost is created → re-access goes to MRU as a fresh entry.
280+ {
281+ ArcReplacer arc_replacer (3 );
282+ arc_replacer.RecordAccess (0 , 30 );
283+ arc_replacer.SetEvictable (0 , true );
284+ arc_replacer.RecordAccess (1 , 31 );
285+ arc_replacer.SetEvictable (1 , true );
286+ arc_replacer.RecordAccess (2 , 32 );
287+ arc_replacer.SetEvictable (2 , true );
288+ // State: [][(30,f0),(31,f1),(32,f2)]![][] p=0
289+
290+ // Remove frame 0 (page 30) — no ghost created
291+ arc_replacer.Remove (0 );
292+ ASSERT_EQ (2 , arc_replacer.Size ());
293+ // State: [][(31,f1),(32,f2)]![][] p=0
294+
295+ // Re-add frame 0 with the same page_id 30.
296+ // Since no ghost exists, this is Case IV (miss all lists) → goes to MRU.
297+ arc_replacer.RecordAccess (0 , 30 );
298+ arc_replacer.SetEvictable (0 , true );
299+ // State: [][(31,f1),(32,f2),(30,f0)]![][] p=0
300+
301+ // Evict order from MRU (target=0, all in MRU): f1, f2, f0
302+ ASSERT_EQ (1 , arc_replacer.Evict ());
303+ ASSERT_EQ (2 , arc_replacer.Evict ());
304+ ASSERT_EQ (0 , arc_replacer.Evict ());
305+ }
306+
307+ // Scenario 6: Double remove (same frame_id twice)
308+ {
309+ ArcReplacer arc_replacer (3 );
310+ arc_replacer.RecordAccess (0 , 40 );
311+ arc_replacer.SetEvictable (0 , true );
312+ ASSERT_EQ (1 , arc_replacer.Size ());
313+
314+ arc_replacer.Remove (0 );
315+ ASSERT_EQ (0 , arc_replacer.Size ());
316+
317+ // Second remove is a no-op (frame no longer in alive_map_)
318+ arc_replacer.Remove (0 );
319+ ASSERT_EQ (0 , arc_replacer.Size ());
320+ }
321+
322+ // Scenario 7: Remove then re-insert same frame with a different page_id
323+ {
324+ ArcReplacer arc_replacer (3 );
325+ arc_replacer.RecordAccess (0 , 50 );
326+ arc_replacer.SetEvictable (0 , true );
327+
328+ arc_replacer.Remove (0 );
329+ ASSERT_EQ (0 , arc_replacer.Size ());
330+
331+ // Re-use frame 0 with a new page
332+ arc_replacer.RecordAccess (0 , 51 );
333+ arc_replacer.SetEvictable (0 , true );
334+ ASSERT_EQ (1 , arc_replacer.Size ());
335+ ASSERT_EQ (0 , arc_replacer.Evict ());
336+ }
337+
338+ // Scenario 8: Remove middle frame preserves eviction order of remaining frames
339+ {
340+ ArcReplacer arc_replacer (5 );
341+ for (int i = 0 ; i < 5 ; i++) {
342+ arc_replacer.RecordAccess (i, 60 + i);
343+ arc_replacer.SetEvictable (i, true );
344+ }
345+ // MRU: [(60,f0),(61,f1),(62,f2),(63,f3),(64,f4)]
346+ arc_replacer.Remove (2 );
347+ ASSERT_EQ (4 , arc_replacer.Size ());
348+
349+ // Evict order: f0, f1, f3, f4 (frame 2 skipped)
350+ ASSERT_EQ (0 , arc_replacer.Evict ());
351+ ASSERT_EQ (1 , arc_replacer.Evict ());
352+ ASSERT_EQ (3 , arc_replacer.Evict ());
353+ ASSERT_EQ (4 , arc_replacer.Evict ());
354+ ASSERT_FALSE (arc_replacer.Evict ().has_value ());
355+ }
356+
357+ // Scenario 9: Remove all frames one by one
358+ {
359+ ArcReplacer arc_replacer (5 );
360+ for (int i = 0 ; i < 5 ; i++) {
361+ arc_replacer.RecordAccess (i, 80 + i);
362+ arc_replacer.SetEvictable (i, true );
363+ }
364+ ASSERT_EQ (5 , arc_replacer.Size ());
365+
366+ for (int i = 0 ; i < 5 ; i++) {
367+ arc_replacer.Remove (i);
368+ }
369+ ASSERT_EQ (0 , arc_replacer.Size ());
370+ ASSERT_FALSE (arc_replacer.Evict ().has_value ());
371+ }
372+ }
373+
217374// Feel free to write more tests!
218375
219376} // namespace bustub
0 commit comments