Repository: carbon-language/carbon-lang
File: common/latch_test.cpp (C++)

Code before:
  Latch::Handle handle = latch.Init();
  // Dropping a copy doesn't satisfy the latch.
  Latch::Handle handle_copy = handle;
  EXPECT_FALSE(std::move(handle_copy).Drop());
  // Can create more copies even after we start dropping.
  Latch::Handle handle_copy2 = handle;
  EXPECT_FALSE(std::move(handle_copy2).Drop());
  // Now drop the last handle.
  EXPECT_TRUE(std::move(handle).Drop());
}

// Tests that the on-zero callback is called.
TEST(LatchTest, OnZeroCallback) {
  Latch latch;
  bool called = false;
  Latch::Handle handle = latch.Init([&] { called = true; });
  Latch::Handle handle2 = handle;
  Latch::Handle handle3 = handle;

  EXPECT_FALSE(called);
  ASSERT_FALSE(std::move(handle).Drop());
  EXPECT_FALSE(called);
  ASSERT_FALSE(std::move(handle2).Drop());
  EXPECT_FALSE(called);
  ASSERT_TRUE(std::move(handle3).Drop());
  EXPECT_TRUE(called);
}

// Tests moving a handle.
TEST(LatchTest, MoveHandle) {
  Latch latch;
  bool called = false;
  Latch::Handle handle = latch.Init([&] { called = true; });
  Latch::Handle handle2 = std::move(handle);

  // Check that dropping the new handle satisfies the latch.
  EXPECT_FALSE(called);
  std::move(handle2).Drop();
  EXPECT_TRUE(called);
}

// Test that creating and destroying a handle without dropping works.
TEST(LatchTest, Destructor) {
  Latch latch;
  bool called = false;
  Latch::Handle handle = latch.Init([&] { called = true; });
  {
    // NOLINTNEXTLINE(performance-unnecessary-copy-initialization)
    Latch::Handle handle2 = handle;
    EXPECT_FALSE(called);
  }

Review comment (bug):
```suggestion
  EXPECT_FALSE(std::move(handle).Drop());
  EXPECT_FALSE(called);
  EXPECT_FALSE(std::move(handle2).Drop());
  EXPECT_FALSE(called);
  EXPECT_FALSE(std::move(handle3).Drop());
  EXPECT_TRUE(called);
```
The general practice AIUI is to use ASSERT conditions to protect against crashes but otherwise allow the test to proceed as later failures can provide additional useful information.

Code after:
  Latch::Handle handle = latch.Init();
  // Dropping a copy doesn't satisfy the latch.
  Latch::Handle handle_copy = handle;
  EXPECT_FALSE(std::move(handle_copy).Drop());
  // Can create more copies even after we start dropping.
  Latch::Handle handle_copy2 = handle;
  EXPECT_FALSE(std::move(handle_copy2).Drop());
  // Now drop the last handle.
  EXPECT_TRUE(std::move(handle).Drop());
}

// Tests that the on-zero callback is called.
TEST(LatchTest, OnZeroCallback) {
  Latch latch;
  bool called = false;
  Latch::Handle handle = latch.Init([&] { called = true; });
  Latch::Handle handle2 = handle;
  Latch::Handle handle3 = handle;

  EXPECT_FALSE(called);
  EXPECT_FALSE(std::move(handle).Drop());
  EXPECT_FALSE(called);
  EXPECT_FALSE(std::move(handle2).Drop());
  EXPECT_FALSE(called);
  EXPECT_TRUE(std::move(handle3).Drop());
  EXPECT_TRUE(called);
}

// Tests moving a handle.
TEST(LatchTest, MoveHandle) {
  Latch latch;
  bool called = false;
  Latch::Handle handle = latch.Init([&] { called = true; });
  Latch::Handle handle2 = std::move(handle);

  // Check that dropping the new handle satisfies the latch.
  EXPECT_FALSE(called);
  EXPECT_TRUE(std::move(handle2).Drop());
  EXPECT_TRUE(called);
}

// Test that creating and destroying a handle without dropping works.
TEST(LatchTest, Destructor) {
  Latch latch;
  bool called = false;
  Latch::Handle handle = latch.Init([&] { called = true; });
  {
    // NOLINTNEXTLINE(performance-unnecessary-copy-initialization)
    Latch::Handle handle2 = handle;
    EXPECT_FALSE(called);
  }