[libc-commits] [libc] [libc] Use scope_exit to clean up resources in tests (PR #196120)

Pavel Labath via libc-commits libc-commits at lists.llvm.org
Wed May 6 10:13:50 PDT 2026


https://github.com/labath created https://github.com/llvm/llvm-project/pull/196120

Simplify socket and FILE unit tests by using the `scope_exit` helper to automatically clean up resources (sockets and FILE pointers), preventing leaks on test failures.

Assisted by Gemini.

>From 5904923e1edd07913f5a5b63a02a67c21602d1cd Mon Sep 17 00:00:00 2001
From: Pavel Labath <pavel at labath.sk>
Date: Wed, 6 May 2026 09:59:54 +0000
Subject: [PATCH] [libc] Use scope_exit to clean up resources in tests

Simplify socket and FILE unit tests by using the `scope_exit` helper to
automatically clean up resources (sockets and FILE pointers), preventing leaks
on test failures.
---
 libc/test/src/stdio/CMakeLists.txt            |   5 +
 libc/test/src/stdio/fgetc_test.cpp            |  48 ++++---
 libc/test/src/stdio/fgetc_unlocked_test.cpp   |  53 ++++----
 libc/test/src/stdio/fgets_test.cpp            | 122 ++++++++++--------
 libc/test/src/stdio/fopen_test.cpp            |  47 ++++---
 libc/test/src/stdio/rewind_test.cpp           |  85 ++++++------
 libc/test/src/sys/socket/linux/CMakeLists.txt |   1 +
 .../src/sys/socket/linux/shutdown_test.cpp    |  23 ++--
 8 files changed, 216 insertions(+), 168 deletions(-)

diff --git a/libc/test/src/stdio/CMakeLists.txt b/libc/test/src/stdio/CMakeLists.txt
index f5eb5c81f90e1..9737dcba7f904 100644
--- a/libc/test/src/stdio/CMakeLists.txt
+++ b/libc/test/src/stdio/CMakeLists.txt
@@ -31,6 +31,7 @@ add_libc_test(
     rewind_test.cpp
   DEPENDS
     libc.include.stdio
+    libc.src.__support.CPP.scope
     libc.src.stdio.fclose
     libc.src.stdio.ferror
     libc.src.stdio.fopen
@@ -437,6 +438,7 @@ add_libc_test(
   SRCS
     fopen_test.cpp
   DEPENDS
+    libc.src.__support.CPP.scope
     libc.src.stdio.fread
     libc.src.stdio.fwrite
     libc.src.stdio.fclose
@@ -519,6 +521,7 @@ add_libc_test(
     fgetc_test.cpp
   DEPENDS
     libc.include.stdio
+    libc.src.__support.CPP.scope
     libc.src.errno.errno
     libc.src.stdio.fclose
     libc.src.stdio.feof
@@ -539,6 +542,7 @@ add_libc_test(
     fgetc_unlocked_test.cpp
   DEPENDS
     libc.include.stdio
+    libc.src.__support.CPP.scope
     libc.src.errno.errno
     libc.src.stdio.fclose
     libc.src.stdio.ferror
@@ -563,6 +567,7 @@ add_libc_test(
     fgets_test.cpp
   DEPENDS
     libc.include.stdio
+    libc.src.__support.CPP.scope
     libc.src.errno.errno
     libc.src.stdio.fclose
     libc.src.stdio.feof
diff --git a/libc/test/src/stdio/fgetc_test.cpp b/libc/test/src/stdio/fgetc_test.cpp
index 1d242a0475aba..f880ba499d3ab 100644
--- a/libc/test/src/stdio/fgetc_test.cpp
+++ b/libc/test/src/stdio/fgetc_test.cpp
@@ -6,6 +6,7 @@
 //
 //===----------------------------------------------------------------------===//
 
+#include "src/__support/CPP/scope.h"
 #include "src/stdio/clearerr.h"
 #include "src/stdio/fclose.h"
 #include "src/stdio/feof.h"
@@ -21,37 +22,44 @@
 #include "hdr/stdio_macros.h"
 
 using namespace LIBC_NAMESPACE::testing::ErrnoSetterMatcher;
+using LIBC_NAMESPACE::cpp::scope_exit;
 
 class LlvmLibcGetcTest : public LIBC_NAMESPACE::testing::ErrnoCheckingTest {
 public:
   using GetcFunc = int(FILE *);
   void test_with_func(GetcFunc *func, const char *filename) {
-    ::FILE *file = LIBC_NAMESPACE::fopen(filename, "w");
-    ASSERT_FALSE(file == nullptr);
     constexpr char CONTENT[] = "123456789";
     constexpr size_t WRITE_SIZE = sizeof(CONTENT) - 1;
-    ASSERT_THAT(LIBC_NAMESPACE::fwrite(CONTENT, 1, WRITE_SIZE, file),
-                Succeeds(WRITE_SIZE));
-    // This is a write-only file so reads should fail.
-    ASSERT_THAT(func(file), Fails(EBADF, EOF));
-    // This is an error and not a real EOF.
-    ASSERT_EQ(LIBC_NAMESPACE::feof(file), 0);
-    ASSERT_NE(LIBC_NAMESPACE::ferror(file), 0);
 
-    ASSERT_THAT(LIBC_NAMESPACE::fclose(file), Succeeds());
+    {
+      ::FILE *file = LIBC_NAMESPACE::fopen(filename, "w");
+      ASSERT_FALSE(file == nullptr);
+      scope_exit close_file(
+          [&] { ASSERT_THAT(LIBC_NAMESPACE::fclose(file), Succeeds()); });
 
-    file = LIBC_NAMESPACE::fopen(filename, "r");
-    ASSERT_FALSE(file == nullptr);
-
-    for (size_t i = 0; i < WRITE_SIZE; ++i) {
-      ASSERT_THAT(func(file), Succeeds(int('1' + i)));
+      ASSERT_THAT(LIBC_NAMESPACE::fwrite(CONTENT, 1, WRITE_SIZE, file),
+                  Succeeds(WRITE_SIZE));
+      // This is a write-only file so reads should fail.
+      ASSERT_THAT(func(file), Fails(EBADF, EOF));
+      // This is an error and not a real EOF.
+      ASSERT_EQ(LIBC_NAMESPACE::feof(file), 0);
+      ASSERT_NE(LIBC_NAMESPACE::ferror(file), 0);
     }
-    // Reading more should return EOF but not set error.
-    ASSERT_THAT(func(file), Succeeds(EOF));
-    ASSERT_NE(LIBC_NAMESPACE::feof(file), 0);
-    ASSERT_EQ(LIBC_NAMESPACE::ferror(file), 0);
 
-    ASSERT_THAT(LIBC_NAMESPACE::fclose(file), Succeeds());
+    {
+      ::FILE *file = LIBC_NAMESPACE::fopen(filename, "r");
+      ASSERT_FALSE(file == nullptr);
+      scope_exit close_file(
+          [&] { ASSERT_THAT(LIBC_NAMESPACE::fclose(file), Succeeds()); });
+
+      for (size_t i = 0; i < WRITE_SIZE; ++i) {
+        ASSERT_THAT(func(file), Succeeds(int('1' + i)));
+      }
+      // Reading more should return EOF but not set error.
+      ASSERT_THAT(func(file), Succeeds(EOF));
+      ASSERT_NE(LIBC_NAMESPACE::feof(file), 0);
+      ASSERT_EQ(LIBC_NAMESPACE::ferror(file), 0);
+    }
   }
 };
 
diff --git a/libc/test/src/stdio/fgetc_unlocked_test.cpp b/libc/test/src/stdio/fgetc_unlocked_test.cpp
index 16d79211f5e8f..e3f936f48af68 100644
--- a/libc/test/src/stdio/fgetc_unlocked_test.cpp
+++ b/libc/test/src/stdio/fgetc_unlocked_test.cpp
@@ -6,6 +6,7 @@
 //
 //===----------------------------------------------------------------------===//
 
+#include "src/__support/CPP/scope.h"
 #include "src/stdio/fclose.h"
 #include "src/stdio/feof.h"
 #include "src/stdio/feof_unlocked.h"
@@ -24,39 +25,47 @@
 #include "hdr/stdio_macros.h"
 
 using namespace LIBC_NAMESPACE::testing::ErrnoSetterMatcher;
+using LIBC_NAMESPACE::cpp::scope_exit;
 
 class LlvmLibcGetcTest : public LIBC_NAMESPACE::testing::ErrnoCheckingTest {
 public:
   using GetcFunc = int(FILE *);
   void test_with_func(GetcFunc *func, const char *filename) {
-    ::FILE *file = LIBC_NAMESPACE::fopen(filename, "w");
-    ASSERT_FALSE(file == nullptr);
     constexpr char CONTENT[] = "123456789";
     constexpr size_t WRITE_SIZE = sizeof(CONTENT) - 1;
-    ASSERT_THAT(LIBC_NAMESPACE::fwrite(CONTENT, 1, WRITE_SIZE, file),
-                Succeeds(WRITE_SIZE));
-    // This is a write-only file so reads should fail.
-    ASSERT_THAT(func(file), Fails(EBADF, EOF));
-    // This is an error and not a real EOF.
-    ASSERT_EQ(LIBC_NAMESPACE::feof(file), 0);
-    ASSERT_NE(LIBC_NAMESPACE::ferror(file), 0);
 
-    ASSERT_THAT(LIBC_NAMESPACE::fclose(file), Succeeds());
+    {
+      ::FILE *file = LIBC_NAMESPACE::fopen(filename, "w");
+      ASSERT_FALSE(file == nullptr);
+      scope_exit close_file(
+          [&] { ASSERT_THAT(LIBC_NAMESPACE::fclose(file), Succeeds()); });
 
-    file = LIBC_NAMESPACE::fopen(filename, "r");
-    ASSERT_FALSE(file == nullptr);
-
-    LIBC_NAMESPACE::flockfile(file);
-    for (size_t i = 0; i < WRITE_SIZE; ++i) {
-      ASSERT_THAT(func(file), Succeeds(int('1' + i)));
+      ASSERT_THAT(LIBC_NAMESPACE::fwrite(CONTENT, 1, WRITE_SIZE, file),
+                  Succeeds(WRITE_SIZE));
+      // This is a write-only file so reads should fail.
+      ASSERT_THAT(func(file), Fails(EBADF, EOF));
+      // This is an error and not a real EOF.
+      ASSERT_EQ(LIBC_NAMESPACE::feof(file), 0);
+      ASSERT_NE(LIBC_NAMESPACE::ferror(file), 0);
     }
-    // Reading more should return EOF but not set error.
-    ASSERT_THAT(func(file), Succeeds(EOF));
-    ASSERT_NE(LIBC_NAMESPACE::feof_unlocked(file), 0);
-    ASSERT_EQ(LIBC_NAMESPACE::ferror_unlocked(file), 0);
 
-    LIBC_NAMESPACE::funlockfile(file);
-    ASSERT_THAT(LIBC_NAMESPACE::fclose(file), Succeeds());
+    {
+      ::FILE *file = LIBC_NAMESPACE::fopen(filename, "r");
+      ASSERT_FALSE(file == nullptr);
+      scope_exit close_file(
+          [&] { ASSERT_THAT(LIBC_NAMESPACE::fclose(file), Succeeds()); });
+
+      LIBC_NAMESPACE::flockfile(file);
+      scope_exit unlock_file([&] { LIBC_NAMESPACE::funlockfile(file); });
+
+      for (size_t i = 0; i < WRITE_SIZE; ++i) {
+        ASSERT_THAT(func(file), Succeeds(int('1' + i)));
+      }
+      // Reading more should return EOF but not set error.
+      ASSERT_THAT(func(file), Succeeds(EOF));
+      ASSERT_NE(LIBC_NAMESPACE::feof_unlocked(file), 0);
+      ASSERT_EQ(LIBC_NAMESPACE::ferror_unlocked(file), 0);
+    }
   }
 };
 
diff --git a/libc/test/src/stdio/fgets_test.cpp b/libc/test/src/stdio/fgets_test.cpp
index 14f054eee1339..41b0a9291e29c 100644
--- a/libc/test/src/stdio/fgets_test.cpp
+++ b/libc/test/src/stdio/fgets_test.cpp
@@ -6,6 +6,7 @@
 //
 //===----------------------------------------------------------------------===//
 
+#include "src/__support/CPP/scope.h"
 #include "src/stdio/fclose.h"
 #include "src/stdio/feof.h"
 #include "src/stdio/ferror.h"
@@ -18,78 +19,85 @@
 
 using LlvmLibcFgetsTest = LIBC_NAMESPACE::testing::ErrnoCheckingTest;
 using namespace LIBC_NAMESPACE::testing::ErrnoSetterMatcher;
+using LIBC_NAMESPACE::cpp::scope_exit;
 
 TEST_F(LlvmLibcFgetsTest, WriteAndReadCharacters) {
   constexpr char FILENAME[] = APPEND_LIBC_TEST("testdata/fgets.test");
-  ::FILE *file = LIBC_NAMESPACE::fopen(FILENAME, "w");
-  ASSERT_FALSE(file == nullptr);
-  constexpr char CONTENT[] = "123456789\n"
-                             "1234567\n"
-                             "123456\n"
-                             "1";
-  constexpr size_t WRITE_SIZE = sizeof(CONTENT) - 1;
-
   char buff[8];
   char *output;
 
-  ASSERT_THAT(LIBC_NAMESPACE::fwrite(CONTENT, 1, WRITE_SIZE, file),
-              Succeeds(WRITE_SIZE));
-  // This is a write-only file so reads should fail.
-  ASSERT_THAT(LIBC_NAMESPACE::fgets(buff, 8, file),
-              Fails(EBADF, static_cast<char *>(nullptr)));
-  // This is an error and not a real EOF.
-  ASSERT_EQ(LIBC_NAMESPACE::feof(file), 0);
-  ASSERT_NE(LIBC_NAMESPACE::ferror(file), 0);
-
-  ASSERT_THAT(LIBC_NAMESPACE::fclose(file), Succeeds());
+  {
+    ::FILE *file = LIBC_NAMESPACE::fopen(FILENAME, "w");
+    ASSERT_FALSE(file == nullptr);
+    scope_exit close_file(
+        [&] { ASSERT_THAT(LIBC_NAMESPACE::fclose(file), Succeeds()); });
+
+    constexpr char CONTENT[] = "123456789\n"
+                               "1234567\n"
+                               "123456\n"
+                               "1";
+    constexpr size_t WRITE_SIZE = sizeof(CONTENT) - 1;
+
+    ASSERT_THAT(LIBC_NAMESPACE::fwrite(CONTENT, 1, WRITE_SIZE, file),
+                Succeeds(WRITE_SIZE));
+    // This is a write-only file so reads should fail.
+    ASSERT_THAT(LIBC_NAMESPACE::fgets(buff, 8, file),
+                Fails(EBADF, static_cast<char *>(nullptr)));
+    // This is an error and not a real EOF.
+    ASSERT_EQ(LIBC_NAMESPACE::feof(file), 0);
+    ASSERT_NE(LIBC_NAMESPACE::ferror(file), 0);
+  }
 
-  file = LIBC_NAMESPACE::fopen(FILENAME, "r");
-  ASSERT_FALSE(file == nullptr);
+  {
+    ::FILE *file = LIBC_NAMESPACE::fopen(FILENAME, "r");
+    ASSERT_FALSE(file == nullptr);
+    scope_exit close_file(
+        [&] { ASSERT_THAT(LIBC_NAMESPACE::fclose(file), Succeeds()); });
 
-  // The GPU build relies on the host C library, so this check may be different.
+    // The GPU build relies on the host C library, so this check may be
+    // different.
 #ifndef LIBC_TARGET_ARCH_IS_GPU
-  // If we request just 1 byte, it should return just a null byte and not
-  // advance the read head. This is implementation defined.
-  output = LIBC_NAMESPACE::fgets(buff, 1, file);
-  ASSERT_TRUE(output == buff);
-  ASSERT_EQ(buff[0], '\0');
-  ASSERT_ERRNO_SUCCESS();
-
-  // If we request less than 1 byte, it should do nothing and return nullptr.
-  // This is also implementation defined.
-  output = LIBC_NAMESPACE::fgets(buff, 0, file);
-  ASSERT_TRUE(output == nullptr);
-  ASSERT_ERRNO_SUCCESS();
+    // If we request just 1 byte, it should return just a null byte and not
+    // advance the read head. This is implementation defined.
+    output = LIBC_NAMESPACE::fgets(buff, 1, file);
+    ASSERT_TRUE(output == buff);
+    ASSERT_EQ(buff[0], '\0');
+    ASSERT_ERRNO_SUCCESS();
+
+    // If we request less than 1 byte, it should do nothing and return nullptr.
+    // This is also implementation defined.
+    output = LIBC_NAMESPACE::fgets(buff, 0, file);
+    ASSERT_TRUE(output == nullptr);
+    ASSERT_ERRNO_SUCCESS();
 #endif
 
-  const char *output_arr[] = {
-      "1234567", "89\n", "1234567", "\n", "123456\n", "1",
-  };
-
-  constexpr size_t ARR_SIZE = sizeof(output_arr) / sizeof(char *);
+    const char *output_arr[] = {
+        "1234567", "89\n", "1234567", "\n", "123456\n", "1",
+    };
 
-  for (size_t i = 0; i < ARR_SIZE; ++i) {
-    output = LIBC_NAMESPACE::fgets(buff, 8, file);
+    constexpr size_t ARR_SIZE = sizeof(output_arr) / sizeof(char *);
 
-    // This pointer comparison is intentional, fgets should return a pointer to
-    // buff when it succeeds.
-    ASSERT_TRUE(output == buff);
-    ASSERT_EQ(LIBC_NAMESPACE::ferror(file), 0);
+    for (size_t i = 0; i < ARR_SIZE; ++i) {
+      output = LIBC_NAMESPACE::fgets(buff, 8, file);
 
-    EXPECT_STREQ(buff, output_arr[i]);
-  }
+      // This pointer comparison is intentional, fgets should return a pointer
+      // to buff when it succeeds.
+      ASSERT_TRUE(output == buff);
+      ASSERT_EQ(LIBC_NAMESPACE::ferror(file), 0);
 
-  // This should have hit the end of the file, but that isn't an error unless it
-  // fails to read anything.
-  ASSERT_NE(LIBC_NAMESPACE::feof(file), 0);
-  ASSERT_EQ(LIBC_NAMESPACE::ferror(file), 0);
-  ASSERT_ERRNO_SUCCESS();
+      EXPECT_STREQ(buff, output_arr[i]);
+    }
 
-  // Reading more should be an EOF, but not an error.
-  output = LIBC_NAMESPACE::fgets(buff, 8, file);
-  ASSERT_TRUE(output == nullptr);
-  ASSERT_NE(LIBC_NAMESPACE::feof(file), 0);
-  ASSERT_ERRNO_SUCCESS();
+    // This should have hit the end of the file, but that isn't an error unless
+    // it fails to read anything.
+    ASSERT_NE(LIBC_NAMESPACE::feof(file), 0);
+    ASSERT_EQ(LIBC_NAMESPACE::ferror(file), 0);
+    ASSERT_ERRNO_SUCCESS();
 
-  ASSERT_THAT(LIBC_NAMESPACE::fclose(file), Succeeds());
+    // Reading more should be an EOF, but not an error.
+    output = LIBC_NAMESPACE::fgets(buff, 8, file);
+    ASSERT_TRUE(output == nullptr);
+    ASSERT_NE(LIBC_NAMESPACE::feof(file), 0);
+    ASSERT_ERRNO_SUCCESS();
+  }
 }
diff --git a/libc/test/src/stdio/fopen_test.cpp b/libc/test/src/stdio/fopen_test.cpp
index 60852ddf09388..b3b24253d1438 100644
--- a/libc/test/src/stdio/fopen_test.cpp
+++ b/libc/test/src/stdio/fopen_test.cpp
@@ -6,36 +6,41 @@
 //
 //===----------------------------------------------------------------------===//
 
+#include "src/__support/CPP/scope.h"
 #include "src/__support/File/file.h"
 #include "src/stdio/fclose.h"
 #include "src/stdio/fopen.h"
-#include "src/stdio/fwrite.h"
 #include "src/stdio/fread.h"
+#include "src/stdio/fwrite.h"
 
 #include "test/UnitTest/Test.h"
 
+using LIBC_NAMESPACE::cpp::scope_exit;
+
 TEST(LlvmLibcFOpenTest, PrintToFile) {
   size_t result;
 
-  FILE *file =
-      LIBC_NAMESPACE::fopen(APPEND_LIBC_TEST("testdata/test.txt"), "w");
-  ASSERT_FALSE(file == nullptr);
-
   static constexpr char STRING[] = "A simple string written to a file\n";
-  result = LIBC_NAMESPACE::fwrite(STRING, 1, sizeof(STRING) - 1, file);
-  EXPECT_GE(result, size_t(0));
-
-  ASSERT_EQ(0, LIBC_NAMESPACE::fclose(file));
-
-  FILE *new_file =
-      LIBC_NAMESPACE::fopen(APPEND_LIBC_TEST("testdata/test.txt"), "r");
-  ASSERT_FALSE(new_file == nullptr);
-
-  static char data[64] = {0};
-  ASSERT_EQ(LIBC_NAMESPACE::fread(data, 1, sizeof(STRING) - 1, new_file),
-            sizeof(STRING) - 1);
-  data[sizeof(STRING) - 1] = '\0';
-  ASSERT_STREQ(data, STRING);
-
-  ASSERT_EQ(0, LIBC_NAMESPACE::fclose(new_file));
+  {
+    FILE *file =
+        LIBC_NAMESPACE::fopen(APPEND_LIBC_TEST("testdata/test.txt"), "w");
+    ASSERT_FALSE(file == nullptr);
+    scope_exit close_file([&] { ASSERT_EQ(0, LIBC_NAMESPACE::fclose(file)); });
+
+    result = LIBC_NAMESPACE::fwrite(STRING, 1, sizeof(STRING) - 1, file);
+    EXPECT_GE(result, size_t(0));
+  }
+
+  {
+    FILE *file =
+        LIBC_NAMESPACE::fopen(APPEND_LIBC_TEST("testdata/test.txt"), "r");
+    ASSERT_FALSE(file == nullptr);
+    scope_exit close_file([&] { ASSERT_EQ(0, LIBC_NAMESPACE::fclose(file)); });
+
+    static char data[64] = {0};
+    ASSERT_EQ(LIBC_NAMESPACE::fread(data, 1, sizeof(STRING) - 1, file),
+              sizeof(STRING) - 1);
+    data[sizeof(STRING) - 1] = '\0';
+    ASSERT_STREQ(data, STRING);
+  }
 }
diff --git a/libc/test/src/stdio/rewind_test.cpp b/libc/test/src/stdio/rewind_test.cpp
index 41dcbbdb8dd80..a86b7bfa9ea33 100644
--- a/libc/test/src/stdio/rewind_test.cpp
+++ b/libc/test/src/stdio/rewind_test.cpp
@@ -6,6 +6,7 @@
 //
 //===----------------------------------------------------------------------===//
 
+#include "src/__support/CPP/scope.h"
 #include "src/stdio/fclose.h"
 #include "src/stdio/ferror.h"
 #include "src/stdio/fopen.h"
@@ -17,60 +18,66 @@
 #include "test/UnitTest/Test.h"
 
 using LlvmLibcRewindTest = LIBC_NAMESPACE::testing::ErrnoCheckingTest;
+using LIBC_NAMESPACE::cpp::scope_exit;
 using LIBC_NAMESPACE::testing::ErrnoSetterMatcher::Succeeds;
 
 TEST_F(LlvmLibcRewindTest, WriteRewindRead) {
   constexpr char FILENAME[] = APPEND_LIBC_TEST("testdata/rewind.test");
   auto FILEPATH = libc_make_test_file_path(FILENAME);
-  ::FILE *file = LIBC_NAMESPACE::fopen(FILEPATH, "w");
-  ASSERT_FALSE(file == nullptr);
-
   constexpr char FIRST_DATA[] = "123456789";
-  ASSERT_THAT(LIBC_NAMESPACE::fwrite(FIRST_DATA, 1, sizeof(FIRST_DATA), file),
-              Succeeds(sizeof(FIRST_DATA)));
 
-  // File state is "123456789"
+  {
+    ::FILE *file = LIBC_NAMESPACE::fopen(FILEPATH, "w");
+    ASSERT_FALSE(file == nullptr);
+    scope_exit close_file([&] { ASSERT_EQ(0, LIBC_NAMESPACE::fclose(file)); });
+
+    ASSERT_THAT(LIBC_NAMESPACE::fwrite(FIRST_DATA, 1, sizeof(FIRST_DATA), file),
+                Succeeds(sizeof(FIRST_DATA)));
 
-  LIBC_NAMESPACE::rewind(file);
+    // File state is "123456789"
 
-  // Cursor is back to the start
+    LIBC_NAMESPACE::rewind(file);
 
-  constexpr char SECOND_DATA[] = "abc";
-  ASSERT_THAT(
-      LIBC_NAMESPACE::fwrite(SECOND_DATA, 1, sizeof(SECOND_DATA) - 1, file),
-      Succeeds(sizeof(SECOND_DATA) - 1));
+    // Cursor is back to the start
 
-  // File state is "abc456789"
+    constexpr char SECOND_DATA[] = "abc";
+    ASSERT_THAT(
+        LIBC_NAMESPACE::fwrite(SECOND_DATA, 1, sizeof(SECOND_DATA) - 1, file),
+        Succeeds(sizeof(SECOND_DATA) - 1));
 
-  // attempt to read from write-only file causing error state
-  char read_data[sizeof(FIRST_DATA)];
-  ASSERT_EQ(LIBC_NAMESPACE::fread(read_data, 1, sizeof(read_data), file),
-            size_t(0));
-  ASSERT_ERRNO_FAILURE();
-  ASSERT_NE(LIBC_NAMESPACE::ferror(file), 0);
+    // File state is "abc456789"
 
-  // rewind to start and check that that clears the error.
-  LIBC_NAMESPACE::rewind(file);
-  ASSERT_EQ(LIBC_NAMESPACE::ferror(file), 0);
+    // attempt to read from write-only file causing error state
+    char read_data[sizeof(FIRST_DATA)];
+    ASSERT_EQ(LIBC_NAMESPACE::fread(read_data, 1, sizeof(read_data), file),
+              size_t(0));
+    ASSERT_ERRNO_FAILURE();
+    ASSERT_NE(LIBC_NAMESPACE::ferror(file), 0);
 
-  // Close out the file and reopen in read mode.
-  LIBC_NAMESPACE::fclose(file);
-  file = LIBC_NAMESPACE::fopen(FILEPATH, "r");
-  ASSERT_FALSE(file == nullptr);
+    // rewind to start and check that that clears the error.
+    LIBC_NAMESPACE::rewind(file);
+    ASSERT_EQ(LIBC_NAMESPACE::ferror(file), 0);
+  }
 
-  // Read the file to check that it was written correctly.
-  ASSERT_THAT(LIBC_NAMESPACE::fread(read_data, 1, 3, file),
-              Succeeds(size_t(3)));
-  read_data[3] = '\0';
-  ASSERT_STREQ(read_data, "abc");
-  ASSERT_EQ(LIBC_NAMESPACE::ferror(file), 0);
+  {
+    // Reopen the file in read mode.
+    ::FILE *file = LIBC_NAMESPACE::fopen(FILEPATH, "r");
+    ASSERT_FALSE(file == nullptr);
+    scope_exit close_file([&] { ASSERT_EQ(0, LIBC_NAMESPACE::fclose(file)); });
 
-  // check that rewind also works on read files.
-  LIBC_NAMESPACE::rewind(file);
-  ASSERT_THAT(LIBC_NAMESPACE::fread(read_data, 1, sizeof(read_data), file),
-              Succeeds(sizeof(FIRST_DATA)));
-  read_data[sizeof(FIRST_DATA) - 1] = '\0';
-  ASSERT_STREQ(read_data, "abc456789");
+    char read_data[sizeof(FIRST_DATA)];
+    // Read the file to check that it was written correctly.
+    ASSERT_THAT(LIBC_NAMESPACE::fread(read_data, 1, 3, file),
+                Succeeds(size_t(3)));
+    read_data[3] = '\0';
+    ASSERT_STREQ(read_data, "abc");
+    ASSERT_EQ(LIBC_NAMESPACE::ferror(file), 0);
 
-  LIBC_NAMESPACE::fclose(file);
+    // check that rewind also works on read files.
+    LIBC_NAMESPACE::rewind(file);
+    ASSERT_THAT(LIBC_NAMESPACE::fread(read_data, 1, sizeof(read_data), file),
+                Succeeds(sizeof(FIRST_DATA)));
+    read_data[sizeof(FIRST_DATA) - 1] = '\0';
+    ASSERT_STREQ(read_data, "abc456789");
+  }
 }
diff --git a/libc/test/src/sys/socket/linux/CMakeLists.txt b/libc/test/src/sys/socket/linux/CMakeLists.txt
index e29140ee06b24..ae6bd9162dca8 100644
--- a/libc/test/src/sys/socket/linux/CMakeLists.txt
+++ b/libc/test/src/sys/socket/linux/CMakeLists.txt
@@ -208,6 +208,7 @@ add_libc_unittest(
     libc.include.sys_socket
     libc.hdr.sys_socket_macros
     libc.hdr.types.ssize_t
+    libc.src.__support.CPP.scope
     libc.src.errno.errno
     libc.src.sys.socket.shutdown
     libc.src.sys.socket.socketpair
diff --git a/libc/test/src/sys/socket/linux/shutdown_test.cpp b/libc/test/src/sys/socket/linux/shutdown_test.cpp
index e32ae625277b9..f351d85b3dbf3 100644
--- a/libc/test/src/sys/socket/linux/shutdown_test.cpp
+++ b/libc/test/src/sys/socket/linux/shutdown_test.cpp
@@ -8,6 +8,7 @@
 
 #include "hdr/sys_socket_macros.h"
 #include "hdr/types/ssize_t.h"
+#include "src/__support/CPP/scope.h"
 #include "src/sys/socket/shutdown.h"
 #include "src/sys/socket/socketpair.h"
 #include "src/unistd/close.h"
@@ -20,11 +21,16 @@
 using LIBC_NAMESPACE::testing::ErrnoSetterMatcher::Fails;
 using LIBC_NAMESPACE::testing::ErrnoSetterMatcher::Succeeds;
 using LlvmLibcShutdownTest = LIBC_NAMESPACE::testing::ErrnoCheckingTest;
+using LIBC_NAMESPACE::cpp::scope_exit;
 
 TEST_F(LlvmLibcShutdownTest, ShutWrProducesEOF) {
   int sv[2];
   ASSERT_THAT(LIBC_NAMESPACE::socketpair(AF_UNIX, SOCK_STREAM, 0, sv),
               Succeeds(0));
+  scope_exit close_sv([&] {
+    ASSERT_THAT(LIBC_NAMESPACE::close(sv[0]), Succeeds(0));
+    ASSERT_THAT(LIBC_NAMESPACE::close(sv[1]), Succeeds(0));
+  });
 
   // Shut down write on sv[0].
   ASSERT_THAT(LIBC_NAMESPACE::shutdown(sv[0], SHUT_WR), Succeeds(0));
@@ -33,15 +39,16 @@ TEST_F(LlvmLibcShutdownTest, ShutWrProducesEOF) {
   char read_buf[10];
   ASSERT_THAT(LIBC_NAMESPACE::read(sv[1], read_buf, sizeof(read_buf)),
               Succeeds<ssize_t>(0));
-
-  ASSERT_THAT(LIBC_NAMESPACE::close(sv[0]), Succeeds(0));
-  ASSERT_THAT(LIBC_NAMESPACE::close(sv[1]), Succeeds(0));
 }
 
 TEST_F(LlvmLibcShutdownTest, ShutRdPreventsReading) {
   int sv[2];
   ASSERT_THAT(LIBC_NAMESPACE::socketpair(AF_UNIX, SOCK_STREAM, 0, sv),
               Succeeds(0));
+  scope_exit close_sv([&] {
+    ASSERT_THAT(LIBC_NAMESPACE::close(sv[0]), Succeeds(0));
+    ASSERT_THAT(LIBC_NAMESPACE::close(sv[1]), Succeeds(0));
+  });
 
   // Shut down read on sv[0].
   ASSERT_THAT(LIBC_NAMESPACE::shutdown(sv[0], SHUT_RD), Succeeds(0));
@@ -50,15 +57,16 @@ TEST_F(LlvmLibcShutdownTest, ShutRdPreventsReading) {
   char read_buf[10];
   ASSERT_THAT(LIBC_NAMESPACE::read(sv[0], read_buf, sizeof(read_buf)),
               Succeeds<ssize_t>(0));
-
-  ASSERT_THAT(LIBC_NAMESPACE::close(sv[0]), Succeeds(0));
-  ASSERT_THAT(LIBC_NAMESPACE::close(sv[1]), Succeeds(0));
 }
 
 TEST_F(LlvmLibcShutdownTest, ShutRdWrDoesBoth) {
   int sv[2];
   ASSERT_THAT(LIBC_NAMESPACE::socketpair(AF_UNIX, SOCK_STREAM, 0, sv),
               Succeeds(0));
+  scope_exit close_sv([&] {
+    ASSERT_THAT(LIBC_NAMESPACE::close(sv[0]), Succeeds(0));
+    ASSERT_THAT(LIBC_NAMESPACE::close(sv[1]), Succeeds(0));
+  });
 
   // Shut down read and write on sv[0].
   ASSERT_THAT(LIBC_NAMESPACE::shutdown(sv[0], SHUT_RDWR), Succeeds(0));
@@ -69,9 +77,6 @@ TEST_F(LlvmLibcShutdownTest, ShutRdWrDoesBoth) {
               Succeeds<ssize_t>(0));
   ASSERT_THAT(LIBC_NAMESPACE::read(sv[1], read_buf, sizeof(read_buf)),
               Succeeds<ssize_t>(0));
-
-  ASSERT_THAT(LIBC_NAMESPACE::close(sv[0]), Succeeds(0));
-  ASSERT_THAT(LIBC_NAMESPACE::close(sv[1]), Succeeds(0));
 }
 
 TEST_F(LlvmLibcShutdownTest, FailsOnInvalidSocket) {



More information about the libc-commits mailing list