On Thu, 29 Jan 2026 at 02:09, Adam Wood <[email protected]> wrote: > > Adds tests for std::filesystem::copy_symlink. > > Tested on x86_64-pc-linux-gnu. > > libstdc++-v3/Changelog: > > PR libstdc++/122217 > * testsuite/27_io/filesystem/operations/copy_symlink/1.cc: New test. > * testsuite/27_io/filesystem/operations/copy_symlink/2.cc: New test. > * testsuite/27_io/filesystem/operations/copy_symlink/3.cc: New test. > * testsuite/27_io/filesystem/operations/copy_symlink/4.cc: New test. > --- > .../filesystem/operations/copy_symlink/1.cc | 63 +++++++++++++++++++ > .../filesystem/operations/copy_symlink/2.cc | 43 +++++++++++++ > .../filesystem/operations/copy_symlink/3.cc | 47 ++++++++++++++ > .../filesystem/operations/copy_symlink/4.cc | 44 +++++++++++++ > 4 files changed, 197 insertions(+) > create mode 100644 > libstdc++-v3/testsuite/27_io/filesystem/operations/copy_symlink/1.cc > create mode 100644 > libstdc++-v3/testsuite/27_io/filesystem/operations/copy_symlink/2.cc > create mode 100644 > libstdc++-v3/testsuite/27_io/filesystem/operations/copy_symlink/3.cc > create mode 100644 > libstdc++-v3/testsuite/27_io/filesystem/operations/copy_symlink/4.cc > > diff --git > a/libstdc++-v3/testsuite/27_io/filesystem/operations/copy_symlink/1.cc > b/libstdc++-v3/testsuite/27_io/filesystem/operations/copy_symlink/1.cc > new file mode 100644 > index 00000000000..e04b2672e6d > --- /dev/null > +++ b/libstdc++-v3/testsuite/27_io/filesystem/operations/copy_symlink/1.cc > @@ -0,0 +1,63 @@ > +// { dg-do run { target c++17 } } > +// { dg-require-filesystem-ts "" } > + > +#include <filesystem> > +#include <testsuite_hooks.h> > +#include <testsuite_fs.h> > + > +// Test successful copies > + > +namespace fs = std::filesystem; > + > +void > +test_successful_copy(const fs::path& p, > + bool (*is_some_file_type)(const fs::path&))
Using a function pointer to the is_directory function isn't guaranteed to work, due to: https://eel.is/c++draft/namespace.std#6.sentence-2 But it's OK for the testsuite. I've pushed this to trunk - thanks! > +{ > + const std::error_code bad_ec = > make_error_code(std::errc::invalid_argument); > + std::error_code ec; > + > + auto to1 = read_symlink(p); > + > + auto p2 = __gnu_test::nonexistent_path(); > + ec = bad_ec; > + copy_symlink(p, p2, ec); > + VERIFY( !ec ); > + VERIFY( exists(symlink_status(p2)) ); > + VERIFY( is_symlink(p2) ); > + VERIFY( is_some_file_type(p2) ); > + auto to2 = read_symlink(p2); > + VERIFY( to1 == to2 ); > + > + // Copy again without ec > + remove(p2); > + copy_symlink(p, p2); > + VERIFY( exists(symlink_status(p2)) ); > + VERIFY( is_symlink(p2) ); > + VERIFY( is_some_file_type(p2) ); > + to2 = read_symlink(p2); > + VERIFY( to1 == to2 ); > + > + remove(p); > + remove(p2); > +} > + > +void > +test01() > +{ > + __gnu_test::scoped_file f; > + auto p = __gnu_test::nonexistent_path(); > + create_symlink(f.path, p); > + test_successful_copy(p, fs::is_regular_file); > + > + auto dir = __gnu_test::nonexistent_path(); > + create_directory(dir); > + create_directory_symlink(dir, p); > + test_successful_copy(p, fs::is_directory); > + remove_all(dir); > +} > + > +int > +main() > +{ > + test01(); > +} > diff --git > a/libstdc++-v3/testsuite/27_io/filesystem/operations/copy_symlink/2.cc > b/libstdc++-v3/testsuite/27_io/filesystem/operations/copy_symlink/2.cc > new file mode 100644 > index 00000000000..27caf4a1d4a > --- /dev/null > +++ b/libstdc++-v3/testsuite/27_io/filesystem/operations/copy_symlink/2.cc > @@ -0,0 +1,43 @@ > +// { dg-do run { target c++17 } } > +// { dg-require-filesystem-ts "" } > + > +#include <filesystem> > +#include <testsuite_hooks.h> > +#include <testsuite_fs.h> > + > +// Test copying into an existing file > + > +void > +test01() > +{ > + std::error_code ec, ec2; > + __gnu_test::scoped_file f, f2; > + > + auto p = __gnu_test::nonexistent_path(); > + create_symlink(f.path, p); > + > + copy_symlink(p, f2.path, ec); > + VERIFY( ec ); > + VERIFY( !is_symlink(f2.path) ); > + > + try > + { > + copy_symlink(p, f2.path); > + } > + catch (const std::filesystem::filesystem_error& ex) > + { > + ec2 = ex.code(); > + VERIFY( ex.path1() == p ); > + VERIFY( ex.path2() == f2.path ); > + } > + VERIFY( ec2 == ec ); > + VERIFY( !is_symlink(f2.path) ); > + > + remove(p); > +} > + > +int > +main() > +{ > + test01(); > +} > diff --git > a/libstdc++-v3/testsuite/27_io/filesystem/operations/copy_symlink/3.cc > b/libstdc++-v3/testsuite/27_io/filesystem/operations/copy_symlink/3.cc > new file mode 100644 > index 00000000000..13e9dad20fc > --- /dev/null > +++ b/libstdc++-v3/testsuite/27_io/filesystem/operations/copy_symlink/3.cc > @@ -0,0 +1,47 @@ > +// { dg-do run { target c++17 } } > +// { dg-require-filesystem-ts "" } > + > +#include <filesystem> > +#include <testsuite_hooks.h> > +#include <testsuite_fs.h> > + > +// Test copying from a non-symlink > + > +namespace fs = std::filesystem; > + > +void > +test_copy_from_non_symlink(const fs::path& from) > +{ > + std::error_code ec, ec2; > + auto p = __gnu_test::nonexistent_path(); > + copy_symlink(from, p, ec); > + VERIFY( ec ); > + VERIFY( !is_symlink(p) ); > + > + try > + { > + copy_symlink(from, p); > + } > + catch (const fs::filesystem_error& ex) > + { > + ec2 = ex.code(); > + VERIFY( ex.path1() == from ); > + VERIFY( ex.path2() == p ); > + } > + VERIFY( ec2 == ec ); > + VERIFY( !is_symlink(p) ); > +} > + > +void > +test01() > +{ > + __gnu_test::scoped_file f; > + test_copy_from_non_symlink(f.path); > + test_copy_from_non_symlink(__gnu_test::nonexistent_path()); > +} > + > +int > +main() > +{ > + test01(); > +} > diff --git > a/libstdc++-v3/testsuite/27_io/filesystem/operations/copy_symlink/4.cc > b/libstdc++-v3/testsuite/27_io/filesystem/operations/copy_symlink/4.cc > new file mode 100644 > index 00000000000..2e4f300194b > --- /dev/null > +++ b/libstdc++-v3/testsuite/27_io/filesystem/operations/copy_symlink/4.cc > @@ -0,0 +1,44 @@ > +// { dg-do run { target c++17 } } > +// { dg-require-filesystem-ts "" } > + > +#include <filesystem> > +#include <testsuite_hooks.h> > +#include <testsuite_fs.h> > + > +// Test copying into an empty path > + > +namespace fs = std::filesystem; > + > +void > +test01() > +{ > + std::error_code ec, ec2; > + __gnu_test::scoped_file f, f2; > + > + auto p = __gnu_test::nonexistent_path(); > + create_symlink(f.path, p); > + > + fs::path empty; > + copy_symlink(p, empty, ec); > + VERIFY( ec ); > + > + try > + { > + copy_symlink(p, empty); > + } > + catch (const std::filesystem::filesystem_error& ex) > + { > + ec2 = ex.code(); > + VERIFY( ex.path1() == p ); > + VERIFY( ex.path2() == empty ); > + } > + VERIFY( ec2 == ec ); > + > + remove(p); > +} > + > +int > +main() > +{ > + test01(); > +} > -- > 2.52.0 >
