TEST(AtomicIntrusiveLinkedList, Stress) {
  constexpr size_t kNumThreads = 32;
  constexpr size_t kNumElements = 100000;

  std::vector<TestIntrusiveObject> elements;
  for (size_t i = 0; i < kNumThreads * kNumElements; ++i) {
    elements.emplace_back(i);
  }

  TestIntrusiveObject::List list;

  std::vector<std::thread> threads;
  for (size_t threadId = 0; threadId < kNumThreads; ++threadId) {
    threads.emplace_back(
        [threadId, kNumThreads, kNumElements, &list, &elements]() {
          for (size_t id = 0; id < kNumElements; ++id) {
            list.insertHead(&elements[threadId + kNumThreads * id]);
          }
        });
  }

  std::vector<size_t> ids;
  TestIntrusiveObject* prev{nullptr};

  while (ids.size() < kNumThreads * kNumElements) {
    list.sweep([&](TestIntrusiveObject* current) {
      ids.push_back(current->id());

      if (prev && prev->id() % kNumThreads == current->id() % kNumThreads) {
        EXPECT_EQ(prev->id() + kNumThreads, current->id());
      }

      prev = current;
    });
  }

  std::sort(ids.begin(), ids.end());

  for (size_t i = 0; i < kNumThreads * kNumElements; ++i) {
    EXPECT_EQ(i, ids[i]);
  }

  for (auto& thread : threads) {
    thread.join();
  }
}
TEST(AtomicIntrusiveLinkedList, ReverseSweep) {
  TestIntrusiveObject a(1), b(2), c(3);
  TestIntrusiveObject::List list;
  list.insertHead(&a);
  list.insertHead(&b);
  list.insertHead(&c);
  size_t next_expected_id = 3;
  list.reverseSweep([&](TestIntrusiveObject* obj) {
    auto const expected = next_expected_id--;
    EXPECT_EQ(expected, obj->id());
  });
  EXPECT_TRUE(list.empty());
  // Test that we can still insert
  list.insertHead(&a);
  EXPECT_FALSE(list.empty());
  list.reverseSweep([](TestIntrusiveObject*) {});
}
TEST(AtomicIntrusiveLinkedList, Basic) {
  TestIntrusiveObject a(1), b(2), c(3);

  TestIntrusiveObject::List list;

  EXPECT_TRUE(list.empty());

  {
    EXPECT_TRUE(list.insertHead(&a));
    EXPECT_FALSE(list.insertHead(&b));

    EXPECT_FALSE(list.empty());

    size_t id = 0;
    list.sweep([&](TestIntrusiveObject* obj) mutable {
      ++id;
      EXPECT_EQ(id, obj->id());
    });

    EXPECT_TRUE(list.empty());
  }

  // Try re-inserting the same item (b) and a new item (c)
  {
    EXPECT_TRUE(list.insertHead(&b));
    EXPECT_FALSE(list.insertHead(&c));

    EXPECT_FALSE(list.empty());

    size_t id = 1;
    list.sweep([&](TestIntrusiveObject* obj) mutable {
      ++id;
      EXPECT_EQ(id, obj->id());
    });

    EXPECT_TRUE(list.empty());
  }

  TestIntrusiveObject::List movedList = std::move(list);
}