//  Boost operations_test.cpp  ---------------------------------------------------------//

//  Copyright Beman Dawes 2002, 2009.

//  Distributed under the Boost Software License, Version 1.0.
//  See http://www.boost.org/LICENSE_1_0.txt

//  Library home page: http://www.boost.org/libs/filesystem

#include <boost/config/warning_disable.hpp>

//  See deprecated_test for tests of deprecated features
#ifndef BOOST_FILESYSTEM_NO_DEPRECATED
#  define BOOST_FILESYSTEM_NO_DEPRECATED
#endif
#ifndef BOOST_SYSTEM_NO_DEPRECATED
#  define BOOST_SYSTEM_NO_DEPRECATED
#endif

#include <boost/filesystem/operations.hpp>
#include <boost/filesystem/directory.hpp>
#include <boost/filesystem/exception.hpp>
#include <boost/filesystem/file_status.hpp>

#include <boost/config.hpp>
# if defined( BOOST_NO_STD_WSTRING )
#   error Configuration not supported: Boost.Filesystem V3 and later requires std::wstring support
# endif

#include <boost/cerrno.hpp>
#include <boost/system/error_code.hpp>
#include <boost/core/lightweight_test.hpp>
#include <boost/detail/lightweight_main.hpp>

namespace fs = boost::filesystem;
using boost::system::error_code;
using boost::system::system_category;
using boost::system::system_error;

#include <fstream>
#include <iostream>

using std::cout;
using std::endl;

#include <string>
#include <vector>
#include <algorithm>
#include <cstring> // for strncmp, etc.
#include <ctime>
#include <cstdlib> // for system(), getenv(), etc.

#ifdef BOOST_WINDOWS_API
# include <windows.h>

inline std::wstring convert(const char* c)
{
   std::string s(c);

   return std::wstring(s.begin(), s.end());
}

//  Note: these three setenv* functions are not general solutions for the missing
//  setenv* problem on VC++. See Microsoft's _putenv for that need, and ticker #7018
//  for discussion and rationale for returning void for this test program, which needs
//  to work for both the MSVC Runtime and the Windows Runtime (which does not support
//  _putenv).

inline void setenv_(const char* name, const fs::path::value_type* val, int)
{
  SetEnvironmentVariableW(convert(name).c_str(), val);
}

inline void setenv_(const char* name, const char* val, int)
{
  SetEnvironmentVariableW(convert(name).c_str(), convert(val).c_str());
}

inline void unsetenv_(const char* name)
{
  SetEnvironmentVariableW(convert(name).c_str(), 0);
}

#else

#include <unistd.h>  // sleep
#include <stdlib.h>  // allow unqualifed calls to env funcs on SunOS

inline void setenv_(const char* name, const char* val, int ovw)
{
  setenv(name, val, ovw);
}

inline void unsetenv_(const char* name)
{
  unsetenv(name);
}

#endif

//  on Windows, except for standard libaries known to have wchar_t overloads for
//  file stream I/O, use path::string() to get a narrow character c_str()
#if defined(BOOST_WINDOWS_API) \
  && (!defined(_CPPLIB_VER) || _CPPLIB_VER < 405)  // not Dinkumware || no wide overloads
# define BOOST_FILESYSTEM_C_STR string().c_str()  // use narrow, since wide not available
#else  // use the native c_str, which will be narrow on POSIX, wide on Windows
# define BOOST_FILESYSTEM_C_STR c_str()
#endif

#define CHECK_EXCEPTION(Functor,Expect) throws_fs_error(Functor,Expect,__LINE__)

namespace
{
  typedef int errno_t;
  std::string platform(BOOST_PLATFORM);
  bool report_throws = false;
  bool cleanup = true;
  bool skip_long_windows_tests = false;

  fs::directory_iterator end_itr;
  fs::path dir;
  fs::path d1;
  fs::path d2;
  fs::path f0;
  fs::path f1;
  fs::path d1f1;

  bool create_symlink_ok(true);

  fs::path ng(" no-way, Jose");

  unsigned short language_id;  // 0 except for Windows

  const fs::path temp_dir(fs::unique_path("op-test-%%%%-%%%%"));

  void create_file(const fs::path & ph, const std::string & contents = std::string())
  {
    std::ofstream f(ph.BOOST_FILESYSTEM_C_STR);
    if (!f)
      throw fs::filesystem_error("operations_test create_file",
      ph, error_code(errno, system_category()));
    if (!contents.empty()) f << contents;
  }

  void verify_file(const fs::path & ph, const std::string & expected)
  {
    std::ifstream f(ph.BOOST_FILESYSTEM_C_STR);
    if (!f)
      throw fs::filesystem_error("operations_test verify_file",
        ph, error_code(errno, system_category()));
    std::string contents;
    f >> contents;
    if (contents != expected)
      throw fs::filesystem_error("operations_test verify_file contents \""
        + contents  + "\" != \"" + expected + "\"", ph, error_code());
  }

  template< typename F >
  bool throws_fs_error(F func, errno_t en, int line)
  {
    try { func(); }

    catch (const fs::filesystem_error & ex)
    {
      if (report_throws)
      {
        // use the what() convenience function to display exceptions
        cout << "\n" << ex.what() << "\n";
      }
      if (en == 0
        || en == ex.code().default_error_condition().value()) return true;
      cout
        << "\nWarning: line " << line
        << " exception reports default_error_condition().value() "
        << ex.code().default_error_condition().value()
        << ", should be " << en
        << "\n value() is " << ex.code().value()
        << endl;
      return true;
    }
    return false;
  }

  struct poison_category_impl: public boost::system::error_category
  {
    char const * name() const BOOST_NOEXCEPT { return "poison"; }
    std::string message( int ) const { return "poison_category::message"; }
  };

  boost::system::error_category& poison_category()
  {
    static poison_category_impl instance;
    return instance;
  }

  // compile-only two argument "do-the-right-thing" tests
  //   verifies that all overload combinations compile without error
  void do_the_right_thing_tests(bool call_ = false)
  {
    if (call_)
    {
      fs::path p;
      std::string s;
      const char* a = 0;
      fs::copy_file(p, p);
      fs::copy_file(s, p);
      fs::copy_file(a, p);
      fs::copy_file(p, s);
      fs::copy_file(p, a);
      fs::copy_file(s, s);
      fs::copy_file(a, s);
      fs::copy_file(s, a);
      fs::copy_file(a, a);
    }
  }

  void bad_file_size()
  {
    fs::file_size(" No way, Jose");
  }

  void bad_directory_size()
  {
    fs::file_size(fs::current_path());
  }

  fs::path bad_create_directory_path;
  void bad_create_directory()
  {
    fs::create_directory(bad_create_directory_path);
  }

  void bad_equivalent()
  {
    fs::equivalent("no-such-path", "another-not-present-path");
  }

  fs::path bad_remove_dir;
  void bad_remove()
  {
    fs::remove(bad_remove_dir);
  }

  class renamer
  {
  public:
    renamer(const fs::path & p1, const fs::path & p2)
      : from(p1), to(p2) {}
    void operator()()
    {
      fs::rename(from, to);
    }
  private:
    fs::path from;
    fs::path to;
  };

  //------------------------------ debugging aids --------------------------------------//

  //std::ostream& operator<<(std::ostream& os, const fs::file_status& s)
  //{
  //  if (s.type() == fs::status_error)        { os << "status_error"; }
  //  else if (s.type() == fs::file_not_found) { os << "file_not_found"; }
  //  else if (s.type() == fs::regular_file)   { os << "regular_file"; }
  //  else if (s.type() == fs::directory_file) { os << "directory_file"; }
  //  else if (s.type() == fs::symlink_file)   { os << "symlink_file"; }
  //  else if (s.type() == fs::block_file)     { os << "block_file"; }
  //  else if (s.type() == fs::character_file) { os << "character_file"; }
  //  else if (s.type() == fs::fifo_file)      { os << "fifo_file"; }
  //  else if (s.type() == fs::socket_file)    { os << "socket_file"; }
  //  else if (s.type() == fs::reparse_file)   { os << "reparse_file"; }
  //  else if (s.type() == fs::type_unknown)   { os << "type_unknown"; }
  //  else                                     { os << "_detail_directory_symlink"; }
  //  return os;
  //}

  //void dump_tree(const fs::path & root)
  //{
  //  cout << "dumping tree rooted at " << root << endl;
  //  for (fs::recursive_directory_iterator it (root, fs::directory_options::follow_directory_symlink);
  //       it != fs::recursive_directory_iterator();
  //       ++it)
  //  {
  //    for (int i = 0; i <= it.level(); ++i)
  //      cout << "  ";

  //    cout << it->path();
  //    if (fs::is_symlink(it->path()))
  //    {
  //      cout << " [symlink]" << endl;
  //    }
  //    else
  //      cout << endl;
  //  }
  //}

  //  exception_tests()  ---------------------------------------------------------------//

#if defined(BOOST_GCC) && BOOST_GCC >= 80000
#pragma GCC diagnostic push
// catching polymorphic type "X" by value - that's the intention of the test
#pragma GCC diagnostic ignored "-Wcatch-value"
#endif

  void exception_tests()
  {
    cout << "exception_tests..." << endl;
    bool exception_thrown;

    //  catch runtime_error by value

    cout << "  catch runtime_error by value" << endl;
    exception_thrown = false;
    try
    {
      fs::create_directory("no-such-dir/foo/bar");
    }
    catch (std::runtime_error x)
    {
      exception_thrown = true;
      if (report_throws) cout << x.what() << endl;
      if (platform == "Windows" && language_id == 0x0409) // English (United States)
        // the stdcxx standard library apparently appends additional info
        // to what(), so check only the initial portion:
        BOOST_TEST(std::strncmp(x.what(),
          "boost::filesystem::create_directory",
          sizeof("boost::filesystem::create_directory")-1) == 0);
    }
    BOOST_TEST(exception_thrown);

    //  catch system_error by value

    cout << "  catch system_error by value" << endl;
    exception_thrown = false;
    try
    {
      fs::create_directory("no-such-dir/foo/bar");
    }
    catch (system_error x)
    {
      exception_thrown = true;
      if (report_throws) cout << x.what() << endl;
      if (platform == "Windows" && language_id == 0x0409) // English (United States)
        BOOST_TEST(std::strcmp(x.what(),
          "boost::filesystem::create_directory: The system cannot find the path specified") == 0);
    }
    BOOST_TEST(exception_thrown);

    //  catch filesystem_error by value

    cout << "  catch filesystem_error by value" << endl;
    exception_thrown = false;
    try
    {
      fs::create_directory("no-such-dir/foo/bar");
    }
    catch (fs::filesystem_error x)
    {
      exception_thrown = true;
      if (report_throws) cout << x.what() << endl;
      if (platform == "Windows" && language_id == 0x0409) // English (United States)
      {
        bool ok (std::strcmp(x.what(),
          "boost::filesystem::create_directory: The system cannot find the path specified: \"no-such-dir/foo/bar\"") == 0);
        BOOST_TEST(ok);
        if (!ok)
        {
          cout << "what returns \"" << x.what() << "\"" << endl;
        }
      }
    }
    BOOST_TEST(exception_thrown);

    //  catch filesystem_error by const reference

    cout << "  catch filesystem_error by const reference" << endl;
    exception_thrown = false;
    try
    {
      fs::create_directory("no-such-dir/foo/bar");
    }
    catch (const fs::filesystem_error& x)
    {
      exception_thrown = true;
      if (report_throws) cout << x.what() << endl;
      if (platform == "Windows" && language_id == 0x0409) // English (United States)
      {
        bool ok (std::strcmp(x.what(),
          "boost::filesystem::create_directory: The system cannot find the path specified: \"no-such-dir/foo/bar\"") == 0);
        BOOST_TEST(ok);
        if (!ok)
        {
          cout << "what returns \"" << x.what() << "\"" << endl;
        }
      }
    }
    BOOST_TEST(exception_thrown);

    // the bound functions should throw, so CHECK_EXCEPTION() should return true

    BOOST_TEST(CHECK_EXCEPTION(bad_file_size, ENOENT));

    if (platform == "Windows")
      BOOST_TEST(CHECK_EXCEPTION(bad_directory_size, ENOENT));
    else
      BOOST_TEST(CHECK_EXCEPTION(bad_directory_size, 0));

    // test path::exception members
    try
    {
      fs::file_size(ng); // will throw
    }
    catch (fs::filesystem_error& ex)
    {
      BOOST_TEST(ex.path1().string() == " no-way, Jose");
    }

    cout << "  exception_tests complete" << endl;
  }

#if defined(BOOST_GCC) && BOOST_GCC >= 80000
#pragma GCC diagnostic pop
#endif

  // create a directory tree that can be used by subsequent tests  ---------------------//
  //
  //    dir
  //      d1
  //        d1f1       // an empty file
  //      f0           // an empty file
  //      f1           // a file containing "file-f1"

  void create_tree()
  {
    cout << "creating test directories and files in " << dir << endl;

    // create directory d1
    BOOST_TEST(!fs::create_directory(dir));
    BOOST_TEST(!fs::is_symlink(dir));
    BOOST_TEST(!fs::is_symlink("nosuchfileordirectory"));
    d1 = dir / "d1";
    BOOST_TEST(fs::create_directory(d1));
    BOOST_TEST(fs::exists(d1));
    BOOST_TEST(fs::is_directory(d1));
    BOOST_TEST(fs::is_empty(d1));

    // create an empty file named "d1f1"
    d1f1 = d1 / "d1f1";
    create_file(d1f1, "");
    BOOST_TEST(fs::exists(d1f1));
    BOOST_TEST(!fs::is_directory(d1f1));
    BOOST_TEST(fs::is_regular_file(d1f1));
    BOOST_TEST(fs::is_empty(d1f1));
    BOOST_TEST(fs::file_size(d1f1) == 0);
    BOOST_TEST(fs::hard_link_count(d1f1) == 1);

    // create an empty file named "f0"
    f0 = dir / "f0";
    create_file(f0, "");
    BOOST_TEST(fs::exists(f0));
    BOOST_TEST(!fs::is_directory(f0));
    BOOST_TEST(fs::is_regular_file(f0));
    BOOST_TEST(fs::is_empty(f0));
    BOOST_TEST(fs::file_size(f0) == 0);
    BOOST_TEST(fs::hard_link_count(f0) == 1);

    // create a file named "f1"
    f1 = dir / "f1";
    create_file(f1, "file-f1");
    BOOST_TEST(fs::exists(f1));
    BOOST_TEST(!fs::is_directory(f1));
    BOOST_TEST(fs::is_regular_file(f1));
    BOOST_TEST(fs::file_size(f1) == 7);
    verify_file(f1, "file-f1");
  }

  //  directory_iterator_tests  --------------------------------------------------------//

  void directory_iterator_tests()
  {
    cout << "directory_iterator_tests..." << endl;

    bool dir_itr_exception(false);
    try { fs::directory_iterator it(""); }
    catch (const fs::filesystem_error &) { dir_itr_exception = true; }
    BOOST_TEST(dir_itr_exception);

    error_code ec;

    BOOST_TEST(!ec);
    fs::directory_iterator it("", ec);
    BOOST_TEST(ec);

    dir_itr_exception = false;
    try { fs::directory_iterator itx("nosuchdirectory"); }
    catch (const fs::filesystem_error &) { dir_itr_exception = true; }
    BOOST_TEST(dir_itr_exception);

    ec.clear();
    fs::directory_iterator it2x("nosuchdirectory", ec);
    BOOST_TEST(ec);

    dir_itr_exception = false;
    try
    {
      error_code ecx;
      fs::directory_iterator itx("nosuchdirectory", ecx);
      BOOST_TEST(ecx);
      BOOST_TEST(ecx == boost::system::errc::no_such_file_or_directory);
    }
    catch (const fs::filesystem_error &) { dir_itr_exception = true; }
    BOOST_TEST(!dir_itr_exception);

    // create a second directory named d2
    d2 = dir / "d2";
    fs::create_directory(d2);
    BOOST_TEST(fs::exists(d2));
    BOOST_TEST(fs::is_directory(d2));

    // test the basic operation of directory_iterators, and test that
    // stepping one iterator doesn't affect a different iterator.
    {
      typedef std::vector<fs::directory_entry> vec_type;
      vec_type vec;

      fs::directory_iterator it1(dir);
      BOOST_TEST(it1 != fs::directory_iterator());
      BOOST_TEST(fs::exists(it1->status()));
      vec.push_back(*it1);
      BOOST_TEST(*it1 == vec[0]);

      fs::directory_iterator it2(dir);
      BOOST_TEST(it2 != fs::directory_iterator());
      BOOST_TEST(*it1 == *it2);

      ++it1;
      BOOST_TEST(it1 != fs::directory_iterator());
      BOOST_TEST(fs::exists(it1->status()));
      BOOST_TEST(it1 != it2);
      BOOST_TEST(*it1 != vec[0]);
      BOOST_TEST(*it2 == vec[0]);
      vec.push_back(*it1);

      ++it1;
      BOOST_TEST(it1 != fs::directory_iterator());
      BOOST_TEST(fs::exists(it1->status()));
      BOOST_TEST(it1 != it2);
      BOOST_TEST(*it2 == vec[0]);
      vec.push_back(*it1);

      ++it1;
      BOOST_TEST(it1 != fs::directory_iterator());
      BOOST_TEST(fs::exists(it1->status()));
      BOOST_TEST(it1 != it2);
      BOOST_TEST(*it2 == vec[0]);
      vec.push_back(*it1);

      ++it1;
      BOOST_TEST(it1 == fs::directory_iterator());

      BOOST_TEST(*it2 == vec[0]);
      ec.clear();
      it2.increment(ec);
      BOOST_TEST(!ec);
      BOOST_TEST(it2 != fs::directory_iterator());
      BOOST_TEST(it1 == fs::directory_iterator());
      BOOST_TEST(*it2 == vec[1]);
      ++it2;
      BOOST_TEST(*it2 == vec[2]);
      BOOST_TEST(it1 == fs::directory_iterator());
      ++it2;
      BOOST_TEST(*it2 == vec[3]);
      ++it2;
      BOOST_TEST(it1 == fs::directory_iterator());
      BOOST_TEST(it2 == fs::directory_iterator());

      // sort vec and check that the right directory entries were found
      std::sort(vec.begin(), vec.end());

      BOOST_TEST_EQ(vec[0].path().filename().string(), std::string("d1"));
      BOOST_TEST_EQ(vec[1].path().filename().string(), std::string("d2"));
      BOOST_TEST_EQ(vec[2].path().filename().string(), std::string("f0"));
      BOOST_TEST_EQ(vec[3].path().filename().string(), std::string("f1"));
    }

    { // *i++ must meet the standard's InputIterator requirements
      fs::directory_iterator dir_itr(dir);
      BOOST_TEST(dir_itr != fs::directory_iterator());
      fs::path p = dir_itr->path();
      BOOST_TEST((*dir_itr++).path() == p);
      BOOST_TEST(dir_itr != fs::directory_iterator());
      BOOST_TEST(dir_itr->path() != p);

      // test case reported in comment to SourceForge bug tracker [937606]
      // augmented to test single pass semantics of a copied iterator [#12578]
      fs::directory_iterator itx(dir);
      fs::directory_iterator itx2(itx);
      BOOST_TEST(itx == itx2);
      const fs::path p1 = (*itx++).path();
      BOOST_TEST(itx == itx2);
      BOOST_TEST(itx != fs::directory_iterator());
      const fs::path p2 = (*itx++).path();
      BOOST_TEST(itx == itx2);
      BOOST_TEST(p1 != p2);
      ++itx;
      BOOST_TEST(itx == itx2);
      ++itx;
      BOOST_TEST(itx == itx2);
      BOOST_TEST(itx == fs::directory_iterator());
      BOOST_TEST(itx2 == fs::directory_iterator());
    }

    //  Windows has a tricky special case when just the root-name is given,
    //  causing the rest of the path to default to the current directory.
    //  Reported as S/F bug [ 1259176 ]
    if (platform == "Windows")
    {
      fs::path root_name_path(fs::current_path().root_name());
      fs::directory_iterator itx(root_name_path);
      BOOST_TEST(itx != fs::directory_iterator());
//      BOOST_TEST(fs::exists((*itx).path()));
      BOOST_TEST(fs::exists(itx->path()));
      BOOST_TEST(itx->path().parent_path() == root_name_path);
      bool found(false);
      do
      {
        if (itx->path().filename() == temp_dir.filename())
          found = true;
      } while (++itx != fs::directory_iterator());
      BOOST_TEST(found);
    }

    // there was an inital bug in directory_iterator that caused premature
    // close of an OS handle. This block will detect regression.
    {
      fs::directory_iterator di;
      {
        di = fs::directory_iterator(dir);
      }
      BOOST_TEST(++di != fs::directory_iterator());
    }

    cout << "  directory_iterator_tests complete" << endl;
  }

  //  recursive_directory_iterator_tests  ----------------------------------------------//

  int walk_tree(bool recursive)
  {
    //cout << "    walk_tree" << endl;
    error_code ec;
    int d1f1_count = 0;
    for (fs::recursive_directory_iterator it (dir,
      recursive ? (fs::directory_options::follow_directory_symlink | fs::directory_options::skip_dangling_symlinks) : fs::directory_options::none);
         it != fs::recursive_directory_iterator();
         it.increment(ec))
    {
      //cout << "      " << it->path() << " : " << ec << endl;
      if (it->path().filename() == "d1f1")
        ++d1f1_count;
    }
    //cout << "      last error : " << ec << endl;
    return d1f1_count;
  }

  void recursive_directory_iterator_tests()
  {
    cout << "recursive_directory_iterator_tests..." << endl;
    BOOST_TEST_EQ(walk_tree(false), 1);
    if (create_symlink_ok)
      BOOST_TEST(walk_tree(true) > 1);

    //  test iterator increment with error_code argument
    cout << "  with error_code argument" << endl;
    boost::system::error_code ec;
    int d1f1_count = 0;
    fs::recursive_directory_iterator it(dir, fs::directory_options::none);
    fs::recursive_directory_iterator it2(it);  // test single pass shallow copy semantics
    for (;
         it != fs::recursive_directory_iterator();
         it.increment(ec))
    {
      if (it->path().filename() == "d1f1")
        ++d1f1_count;
      BOOST_TEST(it == it2);  // verify single pass shallow copy semantics
    }
    BOOST_TEST(!ec);
    BOOST_TEST_EQ(d1f1_count, 1);
    BOOST_TEST(it == it2);  // verify single pass shallow copy semantics

    cout << "  recursive_directory_iterator_tests complete" << endl;
  }

  //  iterator_status_tests  -----------------------------------------------------------//

  void iterator_status_tests()
  {
    cout << "iterator_status_tests..." << endl;

    error_code ec;
    // harmless if these fail:
    fs::create_symlink(dir/"f0", dir/"f0_symlink", ec);
    fs::create_symlink(dir/"no such file", dir/"dangling_symlink", ec);
    fs::create_directory_symlink(dir/"d1", dir/"d1_symlink", ec);
    fs::create_directory_symlink(dir/"no such directory",
      dir/"dangling_directory_symlink", ec);

    for (fs::directory_iterator it(dir);
          it != fs::directory_iterator(); ++it)
    {
      BOOST_TEST(fs::status(it->path()).type() == it->status().type());
      BOOST_TEST(fs::symlink_status(it->path()).type() == it->symlink_status().type());
      if (it->path().filename() == "d1")
      {
        BOOST_TEST(fs::is_directory(it->status()));
        BOOST_TEST(fs::is_directory(it->symlink_status()));
      }
      else if (it->path().filename() == "d2")
      {
        BOOST_TEST(fs::is_directory(it->status()));
        BOOST_TEST(fs::is_directory(it->symlink_status()));
      }
      else if (it->path().filename() == "f0")
      {
        BOOST_TEST(fs::is_regular_file(it->status()));
        BOOST_TEST(fs::is_regular_file(it->symlink_status()));
      }
      else if (it->path().filename() == "f1")
      {
        BOOST_TEST(fs::is_regular_file(it->status()));
        BOOST_TEST(fs::is_regular_file(it->symlink_status()));
      }
      else if (it->path().filename() == "f0_symlink")
      {
        BOOST_TEST(fs::is_regular_file(it->status()));
        BOOST_TEST(fs::is_symlink(it->symlink_status()));
        BOOST_TEST(fs::is_symlink(*it));
      }
      else if (it->path().filename() == "dangling_symlink")
      {
        BOOST_TEST(it->status().type() == fs::file_not_found);
        BOOST_TEST(fs::is_symlink(it->symlink_status()));
      }
      else if (it->path().filename() == "d1_symlink")
      {
        BOOST_TEST(fs::is_directory(it->status()));
        BOOST_TEST(fs::is_symlink(it->symlink_status()));
      }
      else if (it->path().filename() == "dangling_directory_symlink")
      {
        BOOST_TEST(it->status().type() == fs::file_not_found);
        BOOST_TEST(fs::is_symlink(it->symlink_status()));
      }
      //else
      //  cout << "    Note: unexpected directory entry " << it->path().filename() << endl;
    }
  }

  //  recursive_iterator_status_tests  -------------------------------------------------//

  void recursive_iterator_status_tests()
  {
    cout << "recursive_iterator_status_tests..." << endl;
    for (fs::recursive_directory_iterator it (dir);
         it != fs::recursive_directory_iterator();
         ++it)
    {
      BOOST_TEST(fs::status(it->path()).type() == it->status().type());
      BOOST_TEST(fs::symlink_status(it->path()).type() == it->symlink_status().type());
    }
  }

  //  create_hard_link_tests  ----------------------------------------------------------//

  void create_hard_link_tests()
  {
    cout << "create_hard_link_tests..." << endl;

    fs::path from_ph(dir / "f3");
    fs::path f1x(dir / "f1");

    BOOST_TEST(!fs::exists(from_ph));
    BOOST_TEST(fs::exists(f1x));
    bool create_hard_link_ok(true);
    try { fs::create_hard_link(f1x, from_ph); }
    catch (const fs::filesystem_error & ex)
    {
      create_hard_link_ok = false;
      cout
        << "     *** For information only ***\n"
           "     create_hard_link() attempt failed\n"
           "     filesystem_error.what() reports: " << ex.what() << "\n"
           "     create_hard_link() may not be supported on this file system\n";
    }

    if (create_hard_link_ok)
    {
      cout
        << "     *** For information only ***\n"
           "     create_hard_link() succeeded\n";
      BOOST_TEST(fs::exists(from_ph));
      BOOST_TEST(fs::exists(f1x));
      BOOST_TEST(fs::equivalent(from_ph, f1x));
      BOOST_TEST(fs::hard_link_count(from_ph) == 2);
      BOOST_TEST(fs::hard_link_count(f1x) == 2);
    }

    //  Although tests may be running on a FAT or other file system that does
    //  not support hard links, that is unusual enough that it is considered
    //  a test failure.
    BOOST_TEST(create_hard_link_ok);

    error_code ec;
    fs::create_hard_link(fs::path("doesnotexist"),
      fs::path("shouldnotwork"), ec);
    BOOST_TEST(ec);
  }

  //  create_symlink_tests  ------------------------------------------------------------//

  void create_symlink_tests()
  {
    cout << "create_symlink_tests..." << endl;

    fs::path from_ph(dir / "f4");
    fs::path f1x(dir / "f1");
    BOOST_TEST(!fs::exists(from_ph));
    BOOST_TEST(fs::exists(f1x));
    try { fs::create_symlink(f1x, from_ph); }
    catch (const fs::filesystem_error & ex)
    {
      create_symlink_ok = false;
      cout
        << "     *** For information only ***\n"
           "     create_symlink() attempt failed\n"
           "     filesystem_error.what() reports: " << ex.what() << "\n"
           "     create_symlink() may not be supported on this operating system or file system\n";
    }

    if (create_symlink_ok)
    {
      cout
        << "     *** For information only ***\n"
           "     create_symlink() succeeded\n";
      BOOST_TEST(fs::exists(from_ph));
      BOOST_TEST(fs::is_symlink(from_ph));
      BOOST_TEST(fs::exists(f1x));
      BOOST_TEST(fs::equivalent(from_ph, f1x));
      BOOST_TEST(fs::read_symlink(from_ph) == f1x);

      fs::file_status stat = fs::symlink_status(from_ph);
      BOOST_TEST(fs::exists(stat));
      BOOST_TEST(!fs::is_directory(stat));
      BOOST_TEST(!fs::is_regular_file(stat));
      BOOST_TEST(!fs::is_other(stat));
      BOOST_TEST(fs::is_symlink(stat));

      stat = fs::status(from_ph);
      BOOST_TEST(fs::exists(stat));
      BOOST_TEST(!fs::is_directory(stat));
      BOOST_TEST(fs::is_regular_file(stat));
      BOOST_TEST(!fs::is_other(stat));
      BOOST_TEST(!fs::is_symlink(stat));

      // since create_symlink worked, copy_symlink should also work
      fs::path symlink2_ph(dir / "symlink2");
      fs::copy_symlink(from_ph, symlink2_ph);
      stat = fs::symlink_status(symlink2_ph);
      BOOST_TEST(fs::is_symlink(stat));
      BOOST_TEST(fs::exists(stat));
      BOOST_TEST(!fs::is_directory(stat));
      BOOST_TEST(!fs::is_regular_file(stat));
      BOOST_TEST(!fs::is_other(stat));
    }

    error_code ec = error_code();
    fs::create_symlink("doesnotexist", "", ec);
    BOOST_TEST(ec);
  }

  //  permissions_tests  ---------------------------------------------------------------//

  void permissions_tests()
  {
    cout << "permissions_tests..." << endl;

    fs::path p(dir / "permissions.txt");
    create_file(p);

    if (platform == "POSIX")
    {
      cout << "  fs::status(p).permissions() " << std::oct << fs::status(p).permissions()
        << std::dec << endl;
      BOOST_TEST((fs::status(p).permissions() & 0600) == 0600);  // 0644, 0664 sometimes returned

      fs::permissions(p, fs::owner_all);
      BOOST_TEST(fs::status(p).permissions() == fs::owner_all);

      fs::permissions(p, fs::add_perms | fs::group_all);
      BOOST_TEST(fs::status(p).permissions() == (fs::owner_all | fs::group_all));

      fs::permissions(p, fs::remove_perms | fs::group_all);
      BOOST_TEST(fs::status(p).permissions() == fs::owner_all);

      // some POSIX platforms cache permissions during directory iteration, some don't
      // so test that iteration finds the correct permissions
      for (fs::directory_iterator itr(dir); itr != fs::directory_iterator(); ++itr)
        if (itr->path().filename() == fs::path("permissions.txt"))
          BOOST_TEST(itr->status().permissions() == fs::owner_all);

      if (create_symlink_ok)  // only if symlinks supported
      {
        BOOST_TEST(fs::status(p).permissions() == fs::owner_all);
        fs::path p2(dir / "permissions-symlink.txt");
        fs::create_symlink(p, p2);
        cout << std::oct;
        cout << "   status(p).permissions() "  << fs::status(p).permissions() << endl;
        cout << "  status(p2).permissions() "  << fs::status(p).permissions() << endl;
        fs::permissions(p2, fs::add_perms | fs::others_read);
        cout << "   status(p).permissions(): " << fs::status(p).permissions() << endl;
        cout << "  status(p2).permissions(): " << fs::status(p2).permissions() << endl;
        cout << std::dec;
      }

    }
    else // Windows
    {
      BOOST_TEST(fs::status(p).permissions() == 0666);
      fs::permissions(p, fs::remove_perms | fs::group_write);
      BOOST_TEST(fs::status(p).permissions() == 0444);
      fs::permissions(p, fs::add_perms | fs::group_write);
      BOOST_TEST(fs::status(p).permissions() == 0666);
    }
  }

  //  rename_tests  --------------------------------------------------------------------//

  void rename_tests()
  {
    cout << "rename_tests..." << endl;

    fs::path f1x(dir / "f1");
    BOOST_TEST(fs::exists(f1x));

    // error: rename a non-existent old file
    BOOST_TEST(!fs::exists(d1 / "f99"));
    BOOST_TEST(!fs::exists(d1 / "f98"));
    renamer n1a(d1 / "f99", d1 / "f98");
    BOOST_TEST(CHECK_EXCEPTION(n1a, ENOENT));
    renamer n1b(fs::path(""), d1 / "f98");
    BOOST_TEST(CHECK_EXCEPTION(n1b, ENOENT));

    // error: rename an existing file to ""
    renamer n2(f1x, "");
    BOOST_TEST(CHECK_EXCEPTION(n2, ENOENT));

    // rename an existing file to an existent file
    create_file(dir / "ff1", "ff1");
    create_file(dir / "ff2", "ff2");
    fs::rename(dir / "ff2", dir / "ff1");
    BOOST_TEST(fs::exists(dir / "ff1"));
    verify_file(dir / "ff1", "ff2");
    BOOST_TEST(!fs::exists(dir / "ff2"));

    // rename an existing file to itself
    BOOST_TEST(fs::exists(dir / "f1"));
    fs::rename(dir / "f1", dir / "f1");
    BOOST_TEST(fs::exists(dir / "f1"));

    // error: rename an existing directory to an existing non-empty directory
    BOOST_TEST(fs::exists(dir / "f1"));
    BOOST_TEST(fs::exists(d1 / "f2"));
    // several POSIX implementations (cygwin, openBSD) report ENOENT instead of EEXIST,
    // so we don't verify error type on the following test.
    renamer n3b(dir, d1);
    BOOST_TEST(CHECK_EXCEPTION(n3b, 0));

    //  error: move existing file to a nonexistent parent directory
    BOOST_TEST(!fs::is_directory(dir / "f1"));
    BOOST_TEST(!fs::exists(dir / "d3/f3"));
    renamer n4a(dir / "f1", dir / "d3/f3");
    BOOST_TEST(CHECK_EXCEPTION(n4a, ENOENT));

    // rename existing file in same directory
    BOOST_TEST(fs::exists(d1 / "f2"));
    BOOST_TEST(!fs::exists(d1 / "f50"));
    fs::rename(d1 / "f2", d1 / "f50");
    BOOST_TEST(!fs::exists(d1 / "f2"));
    BOOST_TEST(fs::exists(d1 / "f50"));
    fs::rename(d1 / "f50", d1 / "f2");
    BOOST_TEST(fs::exists(d1 / "f2"));
    BOOST_TEST(!fs::exists(d1 / "f50"));

    // move and rename an existing file to a different directory
    fs::rename(d1 / "f2", d2 / "f3");
    BOOST_TEST(!fs::exists(d1 / "f2"));
    BOOST_TEST(!fs::exists(d2 / "f2"));
    BOOST_TEST(fs::exists(d2 / "f3"));
    BOOST_TEST(!fs::is_directory(d2 / "f3"));
    verify_file(d2 / "f3", "file-f1");
    fs::rename(d2 / "f3", d1 / "f2");
    BOOST_TEST(fs::exists(d1 / "f2"));

    // error: move existing directory to nonexistent parent directory
    BOOST_TEST(fs::exists(d1));
    BOOST_TEST(!fs::exists(dir / "d3/d5"));
    BOOST_TEST(!fs::exists(dir / "d3"));
    renamer n5a(d1, dir / "d3/d5");
    BOOST_TEST(CHECK_EXCEPTION(n5a, ENOENT));

    // rename existing directory
    fs::path d3(dir / "d3");
    BOOST_TEST(fs::exists(d1));
    BOOST_TEST(fs::exists(d1 / "f2"));
    BOOST_TEST(!fs::exists(d3));
    fs::rename(d1, d3);
    BOOST_TEST(!fs::exists(d1));
    BOOST_TEST(fs::exists(d3));
    BOOST_TEST(fs::is_directory(d3));
    BOOST_TEST(!fs::exists(d1 / "f2"));
    BOOST_TEST(fs::exists(d3 / "f2"));
    fs::rename(d3, d1);
    BOOST_TEST(fs::exists(d1));
    BOOST_TEST(fs::exists(d1 / "f2"));
    BOOST_TEST(!fs::exists(d3));

    // rename and move d1 to d2 / "d20"
    BOOST_TEST(fs::exists(d1));
    BOOST_TEST(!fs::exists(d2 / "d20"));
    BOOST_TEST(fs::exists(d1 / "f2"));
    fs::rename(d1, d2 / "d20");
    BOOST_TEST(!fs::exists(d1));
    BOOST_TEST(fs::exists(d2 / "d20"));
    BOOST_TEST(fs::exists(d2 / "d20" / "f2"));
    fs::rename(d2 / "d20", d1);
    BOOST_TEST(fs::exists(d1));
    BOOST_TEST(!fs::exists(d2 / "d20"));
    BOOST_TEST(fs::exists(d1 / "f2"));
  }

  //  predicate_and_status_tests  ------------------------------------------------------//

  void predicate_and_status_tests()
  {
    cout << "predicate_and_status_tests..." << endl;

    BOOST_TEST(!fs::exists(ng));
    BOOST_TEST(!fs::is_directory(ng));
    BOOST_TEST(!fs::is_regular_file(ng));
    BOOST_TEST(!fs::is_symlink(ng));
    fs::file_status stat(fs::status(ng));
    BOOST_TEST(fs::type_present(stat));
    BOOST_TEST(fs::permissions_present(stat));
    BOOST_TEST(fs::status_known(stat));
    BOOST_TEST(!fs::exists(stat));
    BOOST_TEST(!fs::is_directory(stat));
    BOOST_TEST(!fs::is_regular_file(stat));
    BOOST_TEST(!fs::is_other(stat));
    BOOST_TEST(!fs::is_symlink(stat));
    stat = fs::status("");
    BOOST_TEST(fs::type_present(stat));
    BOOST_TEST(fs::permissions_present(stat));
    BOOST_TEST(fs::status_known(stat));
    BOOST_TEST(!fs::exists(stat));
    BOOST_TEST(!fs::is_directory(stat));
    BOOST_TEST(!fs::is_regular_file(stat));
    BOOST_TEST(!fs::is_other(stat));
    BOOST_TEST(!fs::is_symlink(stat));
  }

  //  create_directory_tests  ----------------------------------------------------------//

  void create_directory_tests()
  {
    cout << "create_directory_tests..." << endl;

    error_code ec;
    BOOST_TEST(!fs::create_directory("", ec));
    BOOST_TEST(ec);

#ifdef BOOST_WINDOWS_API
    ec.clear();
    BOOST_TEST(!fs::create_directory(" ", ec));  // OK on Linux
    BOOST_TEST(ec);
#endif

    ec.clear();
    BOOST_TEST(!fs::create_directory("/", ec));
    BOOST_TEST(!ec);
    BOOST_TEST(fs::is_directory("/"));  // this is a post-condition

    ec.clear();
    BOOST_TEST(!fs::create_directory(".", ec));
    BOOST_TEST(!ec);

    ec.clear();
    BOOST_TEST(!fs::create_directory("..", ec));
    BOOST_TEST(!ec);

    // create a directory, then check it for consistency
    //   take extra care to report problems, since if this fails
    //   many subsequent tests will fail
    try
    {
      fs::create_directory(dir);
    }

    catch (const fs::filesystem_error & x)
    {
      cout << x.what() << "\n\n"
         "***** Creating directory " << dir << " failed.   *****\n"
         "***** This is a serious error that will prevent further tests    *****\n"
         "***** from returning useful results. Further testing is aborted. *****\n\n";
      std::exit(1);
    }

    catch (...)
    {
      cout << "\n\n"
         "***** Creating directory " << dir << " failed.   *****\n"
         "***** This is a serious error that will prevent further tests    *****\n"
         "***** from returning useful results. Further testing is aborted. *****\n\n";
      std::exit(1);
    }

    BOOST_TEST(fs::exists(dir));
    BOOST_TEST(fs::is_empty(dir));
    BOOST_TEST(fs::is_directory(dir));
    BOOST_TEST(!fs::is_regular_file(dir));
    BOOST_TEST(!fs::is_other(dir));
    BOOST_TEST(!fs::is_symlink(dir));
    fs::file_status stat = fs::status(dir);
    BOOST_TEST(fs::exists(stat));
    BOOST_TEST(fs::is_directory(stat));
    BOOST_TEST(!fs::is_regular_file(stat));
    BOOST_TEST(!fs::is_other(stat));
    BOOST_TEST(!fs::is_symlink(stat));

    cout << "  create_directory_tests complete" << endl;
  }

  //  current_directory_tests  ---------------------------------------------------------//

  void current_directory_tests()
  {
    cout << "current_directory_tests..." << endl;

    // set the current directory, then check it for consistency
    fs::path original_dir = fs::current_path();
    BOOST_TEST(dir != original_dir);
    fs::current_path(dir);
    BOOST_TEST(fs::current_path() == dir);
    BOOST_TEST(fs::current_path() != original_dir);
    fs::current_path(original_dir);
    BOOST_TEST(fs::current_path() == original_dir);
    BOOST_TEST(fs::current_path() != dir);

    // make sure the overloads work
    fs::current_path(dir.c_str());
    BOOST_TEST(fs::current_path() == dir);
    BOOST_TEST(fs::current_path() != original_dir);
    fs::current_path(original_dir.string());
    BOOST_TEST(fs::current_path() == original_dir);
    BOOST_TEST(fs::current_path() != dir);
  }

  //  create_directories_tests  --------------------------------------------------------//

  void create_directories_tests()
  {
    cout << "create_directories_tests..." << endl;

    error_code ec;
    BOOST_TEST(!fs::create_directories("", ec));
    BOOST_TEST(ec);

#ifdef BOOST_WINDOWS_API
    // Windows only test, since " " is OK on Linux as a directory name
    ec.clear();
    BOOST_TEST(!fs::create_directories(" ", ec));
    BOOST_TEST(ec);
#endif

    ec.clear();
    BOOST_TEST(!fs::create_directories("/", ec));
    BOOST_TEST(!ec);

    ec.clear();
    BOOST_TEST(!fs::create_directories(".", ec));
    BOOST_TEST(ec);

    ec.clear();
    BOOST_TEST(!fs::create_directories("..", ec));
    BOOST_TEST(ec);

#ifdef BOOST_POSIX_API
    ec.clear();
    BOOST_TEST(!fs::create_directories("/foo", ec));  // may be OK on Windows
                                                      //  but unlikely to be OK on POSIX
    BOOST_TEST(ec);
#endif

    fs::path p = dir / "level1/." / "level2/./.." / "level3/";
    // trailing "/.", "/./..", and "/" in the above elements test ticket #7258 and
    // related issues

    cout << "    p is " << p << endl;
    BOOST_TEST(!fs::exists(p));
    BOOST_TEST(fs::create_directories(p));
    BOOST_TEST(fs::exists(p));
    BOOST_TEST(fs::is_directory(p));

    if (fs::exists("/permissions_test"))
    {
      BOOST_TEST(!fs::create_directories("/permissions_test", ec));
      BOOST_TEST(!fs::create_directories("/permissions_test/another_directory", ec));
      BOOST_TEST(ec);
    }
  }

  //  resize_file_tests  ---------------------------------------------------------------//

  void resize_file_tests()
  {
    cout << "resize_file_tests..." << endl;

    fs::path p(dir / "resize_file_test.txt");

    fs::remove(p);
    create_file(p, "1234567890");

    BOOST_TEST(fs::exists(p));
    BOOST_TEST_EQ(fs::file_size(p), 10U);
    fs::resize_file(p, 5);
    BOOST_TEST(fs::exists(p));
    BOOST_TEST_EQ(fs::file_size(p), 5U);
    fs::resize_file(p, 15);
    BOOST_TEST(fs::exists(p));
    BOOST_TEST_EQ(fs::file_size(p), 15U);

    error_code ec;
    fs::resize_file("no such file", 15, ec);
    BOOST_TEST(ec);
  }

  //  status_of_nonexistent_tests  -----------------------------------------------------//

  void status_of_nonexistent_tests()
  {
    cout << "status_of_nonexistent_tests..." << endl;
    fs::path p ("nosuch");
    BOOST_TEST(!fs::exists(p));
    BOOST_TEST(!fs::is_regular_file(p));
    BOOST_TEST(!fs::is_directory(p));
    BOOST_TEST(!fs::is_symlink(p));
    BOOST_TEST(!fs::is_other(p));

    fs::file_status s = fs::status(p);
    BOOST_TEST(!fs::exists(s));
    BOOST_TEST_EQ(s.type(), fs::file_not_found);
    BOOST_TEST(fs::type_present(s));
    BOOST_TEST(!fs::is_regular_file(s));
    BOOST_TEST(!fs::is_directory(s));
    BOOST_TEST(!fs::is_symlink(s));
    BOOST_TEST(!fs::is_other(s));

    // ticket #12574 was just user confusion, but are the tests are worth keeping
    error_code ec;
    BOOST_TEST(!fs::is_directory(dir / "no-such-directory", ec));
    BOOST_TEST(ec);
    //cout << "error_code value: " << ec.value() << endl;
    ec.clear();
    BOOST_TEST(!fs::is_directory(dir / "no-such-directory" / "bar", ec));
    BOOST_TEST(ec);
    //cout << "error_code value: " << ec.value() << endl;
  }

  //  status_error_reporting_tests  ----------------------------------------------------//

  void status_error_reporting_tests()
  {
    cout << "status_error_reporting_tests..." << endl;

    error_code ec;

    // test status, ec, for existing file
    ec.assign(-1,poison_category());
    BOOST_TEST(ec.value() == -1);
    BOOST_TEST(&ec.category() == &poison_category());
    fs::file_status s = fs::status(".",ec);
    BOOST_TEST(ec.value() == 0);
    BOOST_TEST(ec.category() == system_category());
    BOOST_TEST(fs::exists(s));
    BOOST_TEST(fs::is_directory(s));

    // test status, ec, for non-existing file
    fs::path p ("nosuch");
    ec.assign(-1,poison_category());
    s = fs::status(p,ec);
    BOOST_TEST(ec.value() != 0);
    BOOST_TEST(ec.category() == system_category());

    BOOST_TEST(!fs::exists(s));
    BOOST_TEST_EQ(s.type(), fs::file_not_found);
    BOOST_TEST(fs::type_present(s));
    BOOST_TEST(!fs::is_regular_file(s));
    BOOST_TEST(!fs::is_directory(s));
    BOOST_TEST(!fs::is_symlink(s));
    BOOST_TEST(!fs::is_other(s));

    // test queries, ec, for existing file
    ec.assign(-1,poison_category());
    BOOST_TEST(fs::exists(".", ec));
    BOOST_TEST(ec.value() == 0);
    BOOST_TEST(ec.category() == system_category());
    ec.assign(-1,poison_category());
    BOOST_TEST(!fs::is_regular_file(".", ec));
    BOOST_TEST(ec.value() == 0);
    BOOST_TEST(ec.category() == system_category());
    ec.assign(-1,poison_category());
    BOOST_TEST(fs::is_directory(".", ec));
    BOOST_TEST(ec.value() == 0);
    BOOST_TEST(ec.category() == system_category());

    // test queries, ec, for non-existing file
    ec.assign(-1,poison_category());
    BOOST_TEST(!fs::exists(p, ec));
    BOOST_TEST(ec.value() != 0);
    BOOST_TEST(ec.category() == system_category());
    ec.assign(-1,poison_category());
    BOOST_TEST(!fs::is_regular_file(p, ec));
    BOOST_TEST(ec.value() != 0);
    BOOST_TEST(ec.category() == system_category());
    ec.assign(-1,poison_category());
    BOOST_TEST(!fs::is_directory(p, ec));
    BOOST_TEST(ec.value() != 0);
    BOOST_TEST(ec.category() == system_category());
  }

  //  remove_tests  --------------------------------------------------------------------//

  void remove_tests(const fs::path& dirx)
  {
    cout << "remove_tests..." << endl;

    // remove() file
    fs::path f1x = dirx / "shortlife";
    BOOST_TEST(!fs::exists(f1x));
    create_file(f1x, "");
    BOOST_TEST(fs::exists(f1x));
    BOOST_TEST(!fs::is_directory(f1x));
    BOOST_TEST(fs::remove(f1x));
    BOOST_TEST(!fs::exists(f1x));
    BOOST_TEST(!fs::remove("no-such-file"));
    BOOST_TEST(!fs::remove("no-such-directory/no-such-file"));

    // remove() directory
    fs::path d1x = dirx / "shortlife_dir";
    BOOST_TEST(!fs::exists(d1x));
    fs::create_directory(d1x);
    BOOST_TEST(fs::exists(d1x));
    BOOST_TEST(fs::is_directory(d1x));
    BOOST_TEST(fs::is_empty(d1x));
    bad_remove_dir = dirx;
    BOOST_TEST(CHECK_EXCEPTION(bad_remove, ENOTEMPTY));
    BOOST_TEST(fs::remove(d1x));
    BOOST_TEST(!fs::exists(d1x));
  }

  //  remove_symlink_tests  ------------------------------------------------------------//

  void remove_symlink_tests()
  {
    cout << "remove_symlink_tests..." << endl;

    // remove() dangling symbolic link
    fs::path link("dangling_link");
    fs::remove(link);  // remove any residue from past tests
    BOOST_TEST(!fs::is_symlink(link));
    BOOST_TEST(!fs::exists(link));
    fs::create_symlink("nowhere", link);
    BOOST_TEST(!fs::exists(link));
    BOOST_TEST(fs::is_symlink(link));
    BOOST_TEST(fs::remove(link));
    BOOST_TEST(!fs::is_symlink(link));

    // remove() self-refering symbolic link
    link = "link_to_self";
    fs::remove(link);  // remove any residue from past tests
    BOOST_TEST(!fs::is_symlink(link));
    BOOST_TEST(!fs::exists(link));
    fs::create_symlink(link, link);
    BOOST_TEST(fs::remove(link));
    BOOST_TEST(!fs::exists(link));
    BOOST_TEST(!fs::is_symlink(link));

    // remove() cyclic symbolic link
    link = "link_to_a";
    fs::path link2("link_to_b");
    fs::remove(link);   // remove any residue from past tests
    fs::remove(link2);  // remove any residue from past tests
    BOOST_TEST(!fs::is_symlink(link));
    BOOST_TEST(!fs::exists(link));
    fs::create_symlink(link, link2);
    fs::create_symlink(link2, link);
    BOOST_TEST(fs::remove(link));
    BOOST_TEST(fs::remove(link2));
    BOOST_TEST(!fs::exists(link));
    BOOST_TEST(!fs::exists(link2));
    BOOST_TEST(!fs::is_symlink(link));

    // remove() symbolic link to file
    fs::path f1x = "link_target";
    fs::remove(f1x);  // remove any residue from past tests
    BOOST_TEST(!fs::exists(f1x));
    create_file(f1x, "");
    BOOST_TEST(fs::exists(f1x));
    BOOST_TEST(!fs::is_directory(f1x));
    BOOST_TEST(fs::is_regular_file(f1x));
    link = "non_dangling_link";
    fs::create_symlink(f1x, link);
    BOOST_TEST(fs::exists(link));
    BOOST_TEST(!fs::is_directory(link));
    BOOST_TEST(fs::is_regular_file(link));
    BOOST_TEST(fs::is_symlink(link));
    BOOST_TEST(fs::remove(link));
    BOOST_TEST(fs::exists(f1x));
    BOOST_TEST(!fs::exists(link));
    BOOST_TEST(!fs::is_symlink(link));
    BOOST_TEST(fs::remove(f1x));
    BOOST_TEST(!fs::exists(f1x));
  }

  //  absolute_tests  -----------------------------------------------------------------//

  void absolute_tests()
  {
    cout << "absolute_tests..." << endl;

    BOOST_TEST_EQ(fs::absolute(""), fs::current_path() );
    BOOST_TEST_EQ(fs::absolute("", ""), fs::current_path() );
    BOOST_TEST_EQ(fs::absolute(fs::current_path() / "foo/bar"), fs::current_path() / "foo/bar");
    BOOST_TEST_EQ(fs::absolute("foo"), fs::current_path() / "foo");
    BOOST_TEST_EQ(fs::absolute("foo", fs::current_path()), fs::current_path() / "foo");
    BOOST_TEST_EQ(fs::absolute("bar", "foo"), fs::current_path() / "foo" / "bar");
    BOOST_TEST_EQ(fs::absolute("/foo"), fs::current_path().root_path().string() + "foo");

#  ifdef BOOST_WINDOWS_API
    BOOST_TEST_EQ(fs::absolute("a:foo", "b:/bar"), "a:/bar/foo");
#  endif

    // these tests were moved from elsewhere, so may duplicate some of the above tests

    // p.empty()
      BOOST_TEST_EQ(fs::absolute(fs::path(), "//foo/bar"), "//foo/bar");
      if (platform == "Windows")
      {
        BOOST_TEST_EQ(fs::absolute(fs::path(), "a:/bar"), "a:/bar");
      }

    // p.has_root_name()
      //   p.has_root_directory()
        BOOST_TEST_EQ(fs::absolute(fs::path("//foo/bar"), "//uvw/xyz"), "//foo/bar");
        if (platform == "Windows")
        {
          BOOST_TEST_EQ(fs::absolute(fs::path("a:/bar"), "b:/xyz"), "a:/bar");
        }
      //   !p.has_root_directory()
        BOOST_TEST_EQ(fs::absolute(fs::path("//net"), "//xyz/"), "//net/");
        BOOST_TEST_EQ(fs::absolute(fs::path("//net"), "//xyz/abc"), "//net/abc");
        BOOST_TEST_EQ(fs::absolute(fs::path("//net"), "//xyz/abc/def"), "//net/abc/def");
        if (platform == "Windows")
        {
          BOOST_TEST_EQ(fs::absolute(fs::path("a:"), "b:/"), "a:/");
          BOOST_TEST_EQ(fs::absolute(fs::path("a:"),"b:/abc"), "a:/abc");
          BOOST_TEST_EQ(fs::absolute(fs::path("a:"),"b:/abc/def"), "a:/abc/def");
          BOOST_TEST_EQ(fs::absolute(fs::path("a:foo"), "b:/"), "a:/foo");
          BOOST_TEST_EQ(fs::absolute(fs::path("a:foo"), "b:/abc"), "a:/abc/foo");
          BOOST_TEST_EQ(fs::absolute(fs::path("a:foo"), "b:/abc/def"), "a:/abc/def/foo");
          BOOST_TEST_EQ(fs::absolute(fs::path("a:foo/bar"), "b:/"), "a:/foo/bar");
          BOOST_TEST_EQ(fs::absolute(fs::path("a:foo/bar"), "b:/abc"), "a:/abc/foo/bar");
          BOOST_TEST_EQ(fs::absolute(fs::path("a:foo/bar"), "b:/abc/def"), "a:/abc/def/foo/bar");
        }
    // !p.has_root_name()
      //   p.has_root_directory()
        BOOST_TEST_EQ(fs::absolute(fs::path("/"), "//xyz/"), "//xyz/");
        BOOST_TEST_EQ(fs::absolute(fs::path("/"), "//xyz/abc"), "//xyz/");
        BOOST_TEST_EQ(fs::absolute(fs::path("/foo"), "//xyz/"), "//xyz/foo");
        BOOST_TEST_EQ(fs::absolute(fs::path("/foo"), "//xyz/abc"), "//xyz/foo");
      //   !p.has_root_directory()
        BOOST_TEST_EQ(fs::absolute(fs::path("foo"), "//xyz/abc"), "//xyz/abc/foo");
        BOOST_TEST_EQ(fs::absolute(fs::path("foo/bar"), "//xyz/abc"), "//xyz/abc/foo/bar");
        BOOST_TEST_EQ(fs::absolute(fs::path("."), "//xyz/abc"), "//xyz/abc/.");
        BOOST_TEST_EQ(fs::absolute(fs::path(".."), "//xyz/abc"), "//xyz/abc/..");
        BOOST_TEST_EQ(fs::absolute(fs::path("./foo"), "//xyz/abc"), "//xyz/abc/./foo");
        BOOST_TEST_EQ(fs::absolute(fs::path("../foo"), "//xyz/abc"), "//xyz/abc/../foo");
        if (platform == "POSIX")
        {
          BOOST_TEST_EQ(fs::absolute(fs::path("foo"), "/abc"), "/abc/foo");
          BOOST_TEST_EQ(fs::absolute(fs::path("foo/bar"), "/abc"), "/abc/foo/bar");
          BOOST_TEST_EQ(fs::absolute(fs::path("."), "/abc"), "/abc/.");
          BOOST_TEST_EQ(fs::absolute(fs::path(".."), "/abc"), "/abc/..");
          BOOST_TEST_EQ(fs::absolute(fs::path("./foo"), "/abc"), "/abc/./foo");
          BOOST_TEST_EQ(fs::absolute(fs::path("../foo"), "/abc"), "/abc/../foo");
        }

  }

  //  canonical_basic_tests  -----------------------------------------------------------//

  void canonical_basic_tests()
  {
    cout << "canonical_basic_tests..." << endl;

    // error handling
    error_code ec;
    ec.clear();
    fs::canonical("no-such-file", ec);
    BOOST_TEST(ec);
    ec.clear();
    fs::canonical("no-such-file", "x", ec);
    BOOST_TEST(ec);
    bool ok(false);
    try { fs::canonical("no-such-file"); }
    catch (const fs::filesystem_error&) { ok = true; }
    BOOST_TEST(ok);

    // non-symlink tests; also see canonical_symlink_tests()
    BOOST_TEST_EQ(fs::canonical(""), fs::current_path());
    BOOST_TEST_EQ(fs::canonical("", fs::current_path()), fs::current_path());
    BOOST_TEST_EQ(fs::canonical("", ""), fs::current_path());
    BOOST_TEST_EQ(fs::canonical(fs::current_path()), fs::current_path());
    BOOST_TEST_EQ(fs::canonical(fs::current_path(), ""), fs::current_path());
    BOOST_TEST_EQ(fs::canonical(fs::current_path(), "no-such-file"), fs::current_path());

    BOOST_TEST_EQ(fs::canonical("."), fs::current_path());
    BOOST_TEST_EQ(fs::canonical(".."), fs::current_path().parent_path());
    BOOST_TEST_EQ(fs::canonical("/"), fs::current_path().root_path());

    fs::path relative_dir(dir.filename());
    BOOST_TEST_EQ(fs::canonical(dir), dir);
    BOOST_TEST_EQ(fs::canonical(relative_dir), dir);
    BOOST_TEST_EQ(fs::canonical(dir / "f0"), dir / "f0");
    BOOST_TEST_EQ(fs::canonical(relative_dir / "f0"), dir / "f0");
    BOOST_TEST_EQ(fs::canonical(relative_dir / "./f0"), dir / "f0");
    BOOST_TEST_EQ(fs::canonical(relative_dir / "d1/../f0"), dir / "f0");

    // treat parent of root as itself on both POSIX and Windows
    fs::path init(fs::initial_path());
    fs::path root(init.root_path());
    fs::path::const_iterator it(init.begin());
    fs::path first;   // relative first non-root directory
#  ifdef BOOST_WINDOWS_API
    if (!init.empty())
      ++it;
#  endif
    if (++it != init.end())
      first = *it;
    fs::path expected(root/first);

    cout << "  init: " << init << endl;
    cout << "  root: " << root << endl;
    cout << "  first: " << first << endl;
    cout << "  expected: " << expected << endl;

    //  ticket 10187 tests
    BOOST_TEST_EQ(fs::canonical(root / "../.." / first), expected);
    BOOST_TEST_EQ(fs::canonical(fs::path("../..") / first, root), expected);
    BOOST_TEST_EQ(fs::canonical(fs::path("/../..") / first, fs::current_path().root_name()), expected);

    //  ticket 9683 test
    BOOST_TEST_EQ(fs::canonical(root / first / "../../../../.."), root);
  }

  //  canonical_symlink_tests  -----------------------------------------------------------//

  void canonical_symlink_tests()
  {
    cout << "canonical_symlink_tests..." << endl;

    fs::path relative_dir(dir.filename());
    BOOST_TEST_EQ(fs::canonical(dir / "sym-d1/f2"), d1 / "f2");
    BOOST_TEST_EQ(fs::canonical(relative_dir / "sym-d1/f2"), d1 / "f2");
  }

 //  copy_file_tests  ------------------------------------------------------------------//

  void copy_file_tests(const fs::path& f1x, const fs::path& d1x)
  {
    cout << "copy_file_tests..." << endl;

    BOOST_TEST(fs::exists(f1x));
    fs::remove(d1x / "f2");  // remove possible residue from prior testing
    BOOST_TEST(fs::exists(d1x));
    BOOST_TEST(!fs::exists(d1x / "f2"));
    cout << " copy " << f1x << " to " << d1x / "f2" << endl;
    bool file_copied = fs::copy_file(f1x, d1x / "f2");
    cout << " copy complete" << endl;
    BOOST_TEST(file_copied);
    BOOST_TEST(fs::exists(f1x));
    BOOST_TEST(fs::exists(d1x / "f2"));
    BOOST_TEST(!fs::is_directory(d1x / "f2"));
    verify_file(d1x / "f2", "file-f1");

    bool copy_ex_ok = false;
    file_copied = false;
    try { file_copied = fs::copy_file(f1x, d1x / "f2"); }
    catch (const fs::filesystem_error &) { copy_ex_ok = true; }
    BOOST_TEST(copy_ex_ok);
    BOOST_TEST(!file_copied);

    file_copied = false;
    copy_ex_ok = false;
    try { file_copied = fs::copy_file(f1x, d1x / "f2", fs::copy_options::none); }
    catch (const fs::filesystem_error &) { copy_ex_ok = true; }
    BOOST_TEST(copy_ex_ok);
    BOOST_TEST(!file_copied);

    fs::remove(d1x / "f2");
    create_file(d1x / "f2", "1234567890");
    BOOST_TEST_EQ(fs::file_size(d1x / "f2"), 10U);
    file_copied = false;
    copy_ex_ok = true;
    try { file_copied = fs::copy_file(f1x, d1x / "f2", fs::copy_options::skip_existing); }
    catch (const fs::filesystem_error &) { copy_ex_ok = false; }
    BOOST_TEST(copy_ex_ok);
    BOOST_TEST(!file_copied);
    BOOST_TEST_EQ(fs::file_size(d1x / "f2"), 10U);
    verify_file(d1x / "f2", "1234567890");

    file_copied = false;
    copy_ex_ok = true;
    try { file_copied = fs::copy_file(f1x, d1x / "f2-non-existing", fs::copy_options::skip_existing); }
    catch (const fs::filesystem_error &) { copy_ex_ok = false; }
    BOOST_TEST(copy_ex_ok);
    BOOST_TEST(file_copied);
    BOOST_TEST_EQ(fs::file_size(d1x / "f2-non-existing"), 7U);
    verify_file(d1x / "f2-non-existing", "file-f1");
    fs::remove(d1x / "f2-non-existing");

    file_copied = false;
    copy_ex_ok = true;
    try { file_copied = fs::copy_file(f1x, d1x / "f2", fs::copy_options::update_existing); }
    catch (const fs::filesystem_error &) { copy_ex_ok = false; }
    BOOST_TEST(copy_ex_ok);
    BOOST_TEST(!file_copied);
    BOOST_TEST_EQ(fs::file_size(d1x / "f2"), 10U);
    verify_file(d1x / "f2", "1234567890");

    // Sleep for a while so that the last modify time is more recent for new files
#if defined(BOOST_POSIX_API)
    sleep(2);
#else
    Sleep(2000);
#endif

    create_file(d1x / "f2-more-recent", "x");
    BOOST_TEST_EQ(fs::file_size(d1x / "f2-more-recent"), 1U);
    file_copied = false;
    copy_ex_ok = true;
    try { file_copied = fs::copy_file(d1x / "f2-more-recent", d1x / "f2", fs::copy_options::update_existing); }
    catch (const fs::filesystem_error &) { copy_ex_ok = false; }
    BOOST_TEST(copy_ex_ok);
    BOOST_TEST(file_copied);
    BOOST_TEST_EQ(fs::file_size(d1x / "f2"), 1U);
    verify_file(d1x / "f2", "x");
    fs::remove(d1x / "f2-more-recent");

    fs::remove(d1x / "f2");
    create_file(d1x / "f2", "1234567890");
    BOOST_TEST_EQ(fs::file_size(d1x / "f2"), 10U);
    file_copied = false;
    copy_ex_ok = true;
    try { file_copied = fs::copy_file(f1x, d1x / "f2", fs::copy_options::overwrite_existing); }
    catch (const fs::filesystem_error &) { copy_ex_ok = false; }
    BOOST_TEST(copy_ex_ok);
    BOOST_TEST(file_copied);
    BOOST_TEST_EQ(fs::file_size(d1x / "f2"), 7U);
    verify_file(d1x / "f2", "file-f1");
  }

 //  symlink_status_tests  -------------------------------------------------------------//

  void symlink_status_tests()
  {
    cout << "symlink_status_tests..." << endl;

    boost::system::error_code ec;

    fs::path dangling_sym(dir / "dangling-sym");
    fs::path dangling_directory_sym(dir / "dangling-directory-sym");
    fs::path sym_d1(dir / "sym-d1");
    fs::path symsym_d1(dir / "symsym-d1");
    fs::path sym_f1(dir / "sym-f1");
    fs::path symsym_f1(dir / "symsym-f1");
    fs::create_symlink("does not exist", dangling_sym);
    fs::create_directory_symlink("does not exist", dangling_directory_sym);
    fs::create_directory_symlink(d1, sym_d1);
    fs::create_directory_symlink(sym_d1, symsym_d1);
    fs::create_symlink(f1, sym_f1);
    fs::create_symlink(sym_f1, symsym_f1);

    //  verify all cases detected as symlinks
    BOOST_TEST_EQ(fs::symlink_status(dangling_sym, ec).type(), fs::symlink_file);
    BOOST_TEST_EQ(fs::symlink_status(dangling_directory_sym, ec).type(), fs::symlink_file);
    BOOST_TEST_EQ(fs::symlink_status(sym_d1, ec).type(), fs::symlink_file);
    BOOST_TEST_EQ(fs::symlink_status(symsym_d1, ec).type(), fs::symlink_file);
    BOOST_TEST_EQ(fs::symlink_status(sym_f1, ec).type(), fs::symlink_file);
    BOOST_TEST_EQ(fs::symlink_status(symsym_f1, ec).type(), fs::symlink_file);

    //  verify all cases resolve to the (possibly recursive) symlink target
    BOOST_TEST_EQ(fs::status(dangling_sym, ec).type(), fs::file_not_found);
    BOOST_TEST_EQ(fs::status(dangling_directory_sym, ec).type(), fs::file_not_found);

    BOOST_TEST_EQ(fs::status(sym_d1, ec).type(), fs::directory_file);
    BOOST_TEST_EQ(fs::status(sym_d1 / "d1f1", ec).type(), fs::regular_file);
    BOOST_TEST_EQ(fs::status(symsym_d1, ec).type(), fs::directory_file);
    BOOST_TEST_EQ(fs::status(symsym_d1 / "d1f1", ec).type(), fs::regular_file);
    BOOST_TEST_EQ(fs::status(sym_f1, ec).type(), fs::regular_file);
    BOOST_TEST_EQ(fs::status(symsym_f1, ec).type(), fs::regular_file);

#ifdef BOOST_WINDOWS_API

    //  On Windows, telling if a filesystem entry is a symlink (or junction which is
    //  treated as a symlink), rather than some other kind of reparse point, requires some
    //  baroque code. See ticket #4663, filesystem objects falsely identified as symlinks.
    //  This test checks two directory entries created by Windows itself to verify
    //  is_symlink() works correctly. Try "dir /A %HOMEPATH%\.." from the command line to
    //  verify this test is valid on your version of Windows. It only works on Vista and
    //  later.

    fs::path users(getenv("HOMEDRIVE"));
    BOOST_TEST(!users.empty());
    users /= "\\Users";
    BOOST_TEST(fs::exists(users));
    BOOST_TEST(fs::exists(users/"All Users"));
    BOOST_TEST(fs::exists(users/"Default User"));
    BOOST_TEST(fs::is_symlink(users/"All Users"));      // dir /A reports <SYMLINKD>
    BOOST_TEST(fs::is_symlink(users/"Default User"));   // dir /A reports <JUNCTION>

#endif
  }

 //  copy_symlink_tests  ---------------------------------------------------------------//

  void copy_symlink_tests(const fs::path& f1x, const fs::path& d1x)
  {
    cout << "copy_symlink_tests..." << endl;

    BOOST_TEST(fs::exists(f1x));
    BOOST_TEST(fs::exists(d1x));
    fs::path sym1(d1x / "symlink1");
    fs::remove(sym1);  // remove possible residue from prior testing
    fs::create_symlink(f1x, sym1);
    BOOST_TEST(fs::exists(sym1));
    BOOST_TEST(fs::is_symlink(sym1));
    fs::path sym2(d1x / "symlink2");
    fs::copy_symlink(sym1, sym2);
    BOOST_TEST(fs::exists(sym2));
    BOOST_TEST(fs::is_symlink(sym2));
    //fs::path sym3(d1x / "symlink3");
    //fs::copy(sym1, sym3);
    //BOOST_TEST(fs::exists(sym3));
    //BOOST_TEST(fs::is_symlink(sym3));

    bool copy_ex_ok = false;
    try { fs::copy_symlink("no-such-file", "new-symlink1"); }
    catch (const fs::filesystem_error &) { copy_ex_ok = true; }
    BOOST_TEST(copy_ex_ok);

    copy_ex_ok = false;
    try { fs::copy_symlink(f1x, "new-symlink2"); } // should fail; f1x not symlink
    catch (const fs::filesystem_error &) { copy_ex_ok = true; }
    BOOST_TEST(copy_ex_ok);
  }

  //  write_time_tests  ----------------------------------------------------------------//

  void write_time_tests(const fs::path& dirx)
  {
    cout << "write_time_tests..." << endl;

    fs::path f1x = dirx / "foobar2";
    create_file(f1x, "foobar2");
    BOOST_TEST(fs::exists(f1x));
    BOOST_TEST(!fs::is_directory(f1x));
    BOOST_TEST(fs::is_regular_file(f1x));
    BOOST_TEST(fs::file_size(f1x) == 7);
    verify_file(f1x, "foobar2");

    // Some file system report last write time as local (FAT), while
    // others (NTFS) report it as UTC. The C standard does not specify
    // if time_t is local or UTC.

    std::time_t ft = fs::last_write_time(f1x);
    cout << "\n  UTC last_write_time() for a file just created is "
      << std::asctime(std::gmtime(&ft)) << endl;

    std::tm * tmp = std::localtime(&ft);
    cout << "\n  Year is " << tmp->tm_year << endl;
    --tmp->tm_year;
    cout << "  Change year to " << tmp->tm_year << endl;
    fs::last_write_time(f1x, std::mktime(tmp));
    std::time_t ft2 = fs::last_write_time(f1x);
    cout << "  last_write_time() for the file is now "
      << std::asctime(std::gmtime(&ft2)) << endl;
    BOOST_TEST(ft != fs::last_write_time(f1x));

    cout << "\n  Reset to current time" << endl;
    fs::last_write_time(f1x, ft);
    double time_diff = std::difftime(ft, fs::last_write_time(f1x));
    cout
      << "  original last_write_time() - current last_write_time() is "
      << time_diff << " seconds" << endl;
    BOOST_TEST(time_diff >= -60.0 && time_diff <= 60.0);
  }

  //  platform_specific_tests  ---------------------------------------------------------//

  void platform_specific_tests()
  {
    // Windows only tests
    if (platform == "Windows")
    {
      cout << "Windows specific tests..." << endl;
      if (!skip_long_windows_tests)
      {
        cout << "  (may take several seconds)"<< endl;

        BOOST_TEST(!fs::exists(fs::path("//share-not")));
        BOOST_TEST(!fs::exists(fs::path("//share-not/")));
        BOOST_TEST(!fs::exists(fs::path("//share-not/foo")));
      }
      cout << endl;

      BOOST_TEST(!fs::exists("tools/jam/src/:sys:stat.h")); // !exists() if ERROR_INVALID_NAME
      BOOST_TEST(!fs::exists(":sys:stat.h")); // !exists() if ERROR_INVALID_PARAMETER
      BOOST_TEST(dir.string().size() > 1
        && dir.string()[1] == ':'); // verify path includes drive

      BOOST_TEST(fs::system_complete("").empty());
      BOOST_TEST(fs::system_complete("/") == fs::initial_path().root_path());
      BOOST_TEST(fs::system_complete("foo")
        == fs::initial_path() / "foo");

      fs::path p1(fs::system_complete("/foo"));
      BOOST_TEST_EQ(p1.string().size(), 6U);  // this failed during v3 development due to bug
      std::string s1(p1.string() );
      std::string s2(fs::initial_path().root_path().string()+"foo");
      BOOST_TEST_EQ(s1, s2);

      BOOST_TEST(fs::system_complete(fs::path(fs::initial_path().root_name()))
        == fs::initial_path());
      BOOST_TEST(fs::system_complete(fs::path(fs::initial_path().root_name().string()
        + "foo")).string() == fs::initial_path() / "foo");
      BOOST_TEST(fs::system_complete(fs::path("c:/")).generic_string()
        == "c:/");
      BOOST_TEST(fs::system_complete(fs::path("c:/foo")).generic_string()
        ==  "c:/foo");
      BOOST_TEST(fs::system_complete(fs::path("//share")).generic_string()
        ==  "//share");

#if defined(BOOST_FILESYSTEM_HAS_MKLINK)
      // Issue 9016 asked that NTFS directory junctions be recognized as directories.
      // That is equivalent to recognizing them as symlinks, and then the normal symlink
      // mechanism takes care of recognizing them as directories.
      //
      // Directory junctions are very similar to symlinks, but have some performance
      // and other advantages over symlinks. They can be created from the command line
      // with "mklink /j junction-name target-path".

      {
        cout << "  directory junction tests..." << endl;
        BOOST_TEST(fs::exists(dir));
        BOOST_TEST(fs::exists(dir / "d1/d1f1"));
        fs::path junc(dir / "junc");
        if (fs::exists(junc))
          fs::remove(junc);
        fs::path new_junc(dir / "new-junc");
        if (fs::exists(new_junc))
          fs::remove(new_junc);

        //cout << "    dir is " << dir << endl;
        //cout << "    junc is " << junc << endl;
        //cout << "    new_junc is " << new_junc << endl;
        //cout << "    current_path() is " << fs::current_path() << endl;

        fs::path cur_path(fs::current_path());
        fs::current_path(dir);
        //cout << "    current_path() is " << fs::current_path() << endl;
        BOOST_TEST(std::system("mklink /j junc d1") == 0);
        //std::system("dir");
        fs::current_path(cur_path);
        //cout << "    current_path() is " << fs::current_path() << endl;

        BOOST_TEST(fs::exists(junc));
        BOOST_TEST(fs::is_symlink(junc));
        BOOST_TEST(fs::is_directory(junc));
        BOOST_TEST(!fs::is_regular_file(junc));
        BOOST_TEST(fs::exists(junc / "d1f1"));
        BOOST_TEST(fs::is_regular_file(junc / "d1f1"));

        int count = 0;
        for (fs::directory_iterator itr(junc); itr != fs::directory_iterator(); ++itr)
        {
          //cout << itr->path() << endl;
          ++count;
        }
        cout << "    iteration count is " << count << endl;
        BOOST_TEST(count > 0);

        fs::rename(junc, new_junc);
        BOOST_TEST(!fs::exists(junc));
        BOOST_TEST(fs::exists(new_junc));
        BOOST_TEST(fs::is_symlink(new_junc));
        BOOST_TEST(fs::is_directory(new_junc));
        BOOST_TEST(!fs::is_regular_file(new_junc));
        BOOST_TEST(fs::exists(new_junc / "d1f1"));
        BOOST_TEST(fs::is_regular_file(new_junc / "d1f1"));

        fs::remove(new_junc);
        BOOST_TEST(!fs::exists(new_junc / "d1f1"));
        BOOST_TEST(!fs::exists(new_junc));
        BOOST_TEST(fs::exists(dir));
        BOOST_TEST(fs::exists(dir / "d1/d1f1"));
      }
#endif // defined(BOOST_FILESYSTEM_HAS_MKLINK)
    } // Windows

    else if (platform == "POSIX")
    {
      cout << "POSIX specific tests..." << endl;
      BOOST_TEST(fs::system_complete("").empty());
      BOOST_TEST(fs::initial_path().root_path().string() == "/");
      BOOST_TEST(fs::system_complete("/").string() == "/");
      BOOST_TEST(fs::system_complete("foo").string()
        == fs::initial_path().string()+"/foo");
      BOOST_TEST(fs::system_complete("/foo").string()
        == fs::initial_path().root_path().string()+"foo");
    } // POSIX
  }

  //  initial_tests  -------------------------------------------------------------------//

  void initial_tests()
  {
    cout << "initial_tests..." << endl;

    cout << "  current_path().string() is\n  \""
              << fs::initial_path().string()
              << "\"\n\n";
    BOOST_TEST(fs::initial_path() == fs::current_path());
    BOOST_TEST(fs::initial_path().is_absolute());
    BOOST_TEST(fs::current_path().is_absolute());
    BOOST_TEST(fs::initial_path().string()
      == fs::current_path().string());
  }

  //  space_tests  ---------------------------------------------------------------------//

  void space_tests()
  {
    cout << "space_tests..." << endl;

    // make some reasonable assuptions for testing purposes
    fs::space_info spi(fs::space(dir));
    BOOST_TEST(spi.capacity > 1000000);
    BOOST_TEST(spi.free > 1000);
    BOOST_TEST(spi.capacity > spi.free);
    BOOST_TEST(spi.free >= spi.available);

    // it is convenient to display space, but older VC++ versions choke
#   if !defined(BOOST_MSVC) || _MSC_VER >= 1300  // 1300 == VC++ 7.0
      cout << "   capacity = " << spi.capacity << '\n';
      cout << "       free = " << spi.free << '\n';
      cout << "  available = " << spi.available << '\n';
#   endif

    // Test that we can specify path to file
    fs::path file = dir / "file";
    create_file(file);

    fs::space_info spi_file(fs::space(file));
    BOOST_TEST_EQ(spi_file.capacity, spi.capacity);

    fs::remove(file);
  }

  //  equivalent_tests  ----------------------------------------------------------------//

  void equivalent_tests(const fs::path& f1x)
  {
    cout << "equivalent_tests..." << endl;

    BOOST_TEST(CHECK_EXCEPTION(bad_equivalent, ENOENT));
    BOOST_TEST(fs::equivalent(f1x, dir / "f1"));
    BOOST_TEST(fs::equivalent(dir, d1 / ".."));
    BOOST_TEST(!fs::equivalent(f1x, dir));
    BOOST_TEST(!fs::equivalent(dir, f1x));
    BOOST_TEST(!fs::equivalent(d1, d2));
    BOOST_TEST(!fs::equivalent(dir, ng));
    BOOST_TEST(!fs::equivalent(ng, dir));
    BOOST_TEST(!fs::equivalent(f1x, ng));
    BOOST_TEST(!fs::equivalent(ng, f1x));
  }

  //  temp_directory_path_tests  -------------------------------------------------------//
  //    contributed by Jeff Flinn

  struct guarded_env_var
  {
    struct previous_value
    {
      std::string m_name;
      std::string m_string;
      bool        m_empty;

      previous_value(const char* name)
      : m_name(name)
      , m_empty (true)
      {
        if(const char* value = getenv(name))
        {
          m_string.assign(value);
          m_empty = false;
        }
        else
        {
          m_empty = true;
        }
      }
      ~previous_value()
      {
        m_empty? unsetenv_(m_name.c_str())
               : setenv_(m_name.c_str(), m_string.c_str(), 1);
      }
    };

    previous_value m_previous_value;

    guarded_env_var(const char* name, const char* value)
    : m_previous_value(name)
    {
//      std::cout << name << " old value is \"" << getenv(name) << "\"" << std::endl;
      value ? setenv_(name, value, 1) : unsetenv_(name);
//      std::cout << name << " new value is \"" << getenv(name) << "\"" << std::endl;
    }
  };

  void temp_directory_path_tests()
  {
    {
      cout << "temp_directory_path_tests..." << endl;
      cout << " temp_directory_path() is " << fs::temp_directory_path() << endl;

#if defined(BOOST_WINDOWS_API)

//**************************************************************************************//
//   Bug in GCC 4.9 getenv() when !defined(__GXX_EXPERIMENTAL_CXX0X__) makes these
//   tests meaningless, so skip them
//**************************************************************************************//

#if defined(__CYGWIN__) && !defined(__GXX_EXPERIMENTAL_CXX0X__) && __GNUC__ == 4
      cout << "Bug in GCC 4.9 getenv() when !defined(__GXX_EXPERIMENTAL_CXX0X__) makes these"
        "tests meaningless, so skip them" << endl;
      return;
#endif
      // Test ticket #5300, temp_directory_path failure on Windows with path length > 130.
      // (This test failed prior to the fix being applied.)
      {
        const wchar_t long_name[] =
          L"12345678901234567890123456789012345678901234567890"
          L"12345678901234567890123456789012345678901234567890"
          L"12345678901234567890123456789012345678901234567890#"   // total 151 chars
          ;
        fs::path p (temp_dir);
        p /= long_name;
        fs::create_directory(p);

        guarded_env_var tmp_guard("TMP", p.string().c_str());
        error_code ec;
        fs::path tmp_path = fs::temp_directory_path(ec);
        BOOST_TEST(!ec);
        BOOST_TEST_EQ(p, tmp_path);
        fs::remove(p);
      }

      // Test ticket #10388, null character at end of filesystem::temp_directory_path path
      {
        guarded_env_var tmp_guard("TMP", fs::initial_path().string().c_str());

        error_code ec;
        fs::path tmp_path = fs::temp_directory_path(ec);
        BOOST_TEST_EQ(tmp_path, fs::initial_path());
      }

#endif
      BOOST_TEST(!fs::temp_directory_path().empty());
      BOOST_TEST(exists(fs::temp_directory_path()));
      fs::path ph = fs::temp_directory_path() / fs::unique_path("temp_directory_path_test_%%%%_%%%%.txt");
      {
          if(exists(ph)) remove(ph);
          std::ofstream f(ph.BOOST_FILESYSTEM_C_STR);
          f << "passed";
      }
      BOOST_TEST(exists(ph));
      {
          std::ifstream f(ph.BOOST_FILESYSTEM_C_STR);
          std::string   s;
          f >> s;
          BOOST_TEST(s == "passed");
      }
      remove(ph);
      BOOST_TEST(!exists(ph));
    }

    fs::path test_temp_dir = temp_dir;

#if defined(BOOST_POSIX_API)
    {
      struct guarded_tmp_vars
      {
        guarded_env_var m_tmpdir ;
        guarded_env_var m_tmp    ;
        guarded_env_var m_temp   ;
        guarded_env_var m_tempdir;

        guarded_tmp_vars
        ( const fs::path::value_type* tmpdir
        , const fs::path::value_type* tmp
        , const fs::path::value_type* temp
        , const fs::path::value_type* tempdir
        )
        : m_tmpdir ("TMPDIR" , tmpdir )
        , m_tmp    ("TMP"    , tmp    )
        , m_temp   ("TEMP"   , temp   )
        , m_tempdir("TEMPDIR", tempdir)
        {}
      };

      {
        guarded_tmp_vars vars(test_temp_dir.c_str(), 0, 0, 0);
        fs::path ph = fs::temp_directory_path();
        BOOST_TEST(equivalent(test_temp_dir, ph));
      }
      {
        guarded_tmp_vars vars(0, test_temp_dir.c_str(), 0, 0);
        fs::path ph = fs::temp_directory_path();
        BOOST_TEST(equivalent(test_temp_dir, ph));
      }
      {
        guarded_tmp_vars vars(0, 0, test_temp_dir.c_str(), 0);
        fs::path ph = fs::temp_directory_path();
        BOOST_TEST(equivalent(test_temp_dir, ph));
      }
      {
        guarded_tmp_vars vars(0, 0, 0, test_temp_dir.c_str());
        fs::path ph = fs::temp_directory_path();
        BOOST_TEST(equivalent(test_temp_dir, ph));
      }
    }
#endif

#if defined(BOOST_WINDOWS_API)

    struct guarded_tmp_vars
    {
      guarded_env_var m_tmp        ;
      guarded_env_var m_temp       ;
      guarded_env_var m_localappdata;
      guarded_env_var m_userprofile;

      guarded_tmp_vars
      ( const char* tmp
      , const char* temp
      , const char* localappdata
      , const char* userprofile
      )
      : m_tmp          ("TMP"           , tmp         )
      , m_temp         ("TEMP"          , temp        )
      , m_localappdata ("LOCALAPPDATA"  , localappdata)
      , m_userprofile  ("USERPROFILE"   , userprofile )
      {}
    };

    // test the GetWindowsDirectoryW()/Temp fallback
    {
      guarded_tmp_vars vars(0, 0, 0, 0);
      error_code ec;
      fs::path ph = fs::temp_directory_path(ec);
      BOOST_TEST(!ec);
      cout << "Fallback test, temp_directory_path() returned " << ph << endl;
    }

    {
      guarded_tmp_vars vars(test_temp_dir.string().c_str(), 0, 0, 0);
      fs::path ph = fs::temp_directory_path();
      BOOST_TEST(equivalent(test_temp_dir, ph));
    }
    {
      guarded_tmp_vars vars(0, test_temp_dir.string().c_str(), 0, 0);
      fs::path ph = fs::temp_directory_path();
      BOOST_TEST(equivalent(test_temp_dir, ph));
    }

    fs::create_directory(test_temp_dir / L"Temp");
    {
      guarded_tmp_vars vars(0, 0, test_temp_dir.string().c_str(), 0);
      fs::path ph = fs::temp_directory_path();
      BOOST_TEST(equivalent(test_temp_dir/L"Temp", ph));
      cout << "temp_directory_path() returned " << ph << endl;
    }
    {
      guarded_tmp_vars vars(0, 0, 0, test_temp_dir.string().c_str());
      fs::path ph = fs::temp_directory_path();
      BOOST_TEST(equivalent(test_temp_dir/L"Temp", ph));
      cout << "temp_directory_path() returned " << ph << endl;
    }
#endif
  }

  //  weakly_canonical_tests  ----------------------------------------------------------//

  void weakly_canonical_tests()
  {
    cout << "weakly_canonical_tests..." << endl;
    cout << "  dir is " << dir << endl;

    BOOST_TEST_EQ(fs::weakly_canonical("no-such/foo/bar"), "no-such/foo/bar");
    BOOST_TEST_EQ(fs::weakly_canonical("no-such/foo/../bar"), "no-such/bar");
    BOOST_TEST_EQ(fs::weakly_canonical(dir), dir);
    BOOST_TEST_EQ(fs::weakly_canonical(dir/"no-such/foo/bar"), dir/"no-such/foo/bar");
    BOOST_TEST_EQ(fs::weakly_canonical(dir/"no-such/foo/../bar"), dir/"no-such/bar");
    BOOST_TEST_EQ(fs::weakly_canonical(dir/"../no-such/foo/../bar"),
      dir.parent_path()/"no-such/bar");
    BOOST_TEST_EQ(fs::weakly_canonical("c:/no-such/foo/bar"), "c:/no-such/foo/bar");

    fs::create_directory_symlink(dir / "d1", dir / "sld1");
    BOOST_TEST_EQ(fs::weakly_canonical(dir / "sld1/foo/bar"), dir / "d1/foo/bar");

    BOOST_TEST_EQ(relative(dir / "sld1/foo/bar/baz", dir / "d1/foo"), "bar/baz");
  }

  //  _tests  --------------------------------------------------------------------------//

  //void _tests()
  //{
  //  cout << "_tests..." << endl;
  //}

} // unnamed namespace

  //------------------------------------------------------------------------------------//
  //                                                                                    //
  //                                    main                                            //
  //                                                                                    //
  //------------------------------------------------------------------------------------//

int cpp_main(int argc, char* argv[])
{
// document state of critical macros
#ifdef BOOST_POSIX_API
  cout << "BOOST_POSIX_API is defined\n";
#endif
#ifdef BOOST_WINDOWS_API
  cout << "BOOST_WINDOWS_API is defined\n";
#endif

  for (; argc > 1; --argc, ++argv)
  {
    if (*argv[1]=='-' && *(argv[1]+1)=='t')
      report_throws = true;
    else if (*argv[1]=='-' && *(argv[1]+1)=='x')
      cleanup = false;
    else if (*argv[1]=='-' && *(argv[1]+1)=='w')
      skip_long_windows_tests = true;
  }

  // The choice of platform to test is made at runtime rather than compile-time
  // so that compile errors for all platforms will be detected even though
  // only the current platform is runtime tested.
# if defined(BOOST_POSIX_API)
    platform = "POSIX";
# elif defined(BOOST_WINDOWS_API)
    platform = "Windows";
    language_id = ::GetUserDefaultUILanguage();
# else
#   error neither BOOST_POSIX_API nor BOOST_WINDOWS_API is defined. See boost/system/api_config.hpp
# endif
  cout << "API is " << platform << endl;
  cout << "initial_path() is " << fs::initial_path() << endl;
  fs::path ip = fs::initial_path();
  do_the_right_thing_tests(); // compile-only tests, but call anyhow to suppress warnings

  for (fs::path::const_iterator it = ip.begin(); it != ip.end(); ++it)
  {
    if (it != ip.begin())
      cout << ", ";
    cout << *it;
  }
  cout << endl;

  dir = fs::initial_path() / temp_dir;

  if (fs::exists(dir))
  {
    cout << "remove residue from prior failed tests..." << endl;
    fs::remove_all(dir);
  }
  BOOST_TEST(!fs::exists(dir));

  // several functions give unreasonable results if uintmax_t isn't 64-bits
  cout << "sizeof(boost::uintmax_t) = " << sizeof(boost::uintmax_t) << '\n';
  BOOST_TEST(sizeof(boost::uintmax_t) >= 8);

  initial_tests();
  predicate_and_status_tests();
  exception_tests();
  create_directory_tests();
  current_directory_tests();
  space_tests();

  // create a directory tree that can be used by subsequent tests
  //
  //    dir
  //      d1
  //        d1f1       // an empty file
  //      f0           // an empty file
  //      f1           // a file containing "file f1"
  //
  create_tree();

  status_of_nonexistent_tests();
  status_error_reporting_tests();
  directory_iterator_tests();
  create_directories_tests();  // must run AFTER directory_iterator_tests

  bad_create_directory_path = f1;
  BOOST_TEST(CHECK_EXCEPTION(bad_create_directory, EEXIST));
  fs::file_status stat = fs::status(f1);
  BOOST_TEST(fs::status_known(stat));
  BOOST_TEST(fs::exists(stat));
  BOOST_TEST(!fs::is_directory(stat));
  BOOST_TEST(fs::is_regular_file(stat));
  BOOST_TEST(!fs::is_other(stat));
  BOOST_TEST(!fs::is_symlink(stat));

  equivalent_tests(f1);
  create_hard_link_tests();
  create_symlink_tests();
  resize_file_tests();
  absolute_tests();
  canonical_basic_tests();
  permissions_tests();
  copy_file_tests(f1, d1);
  if (create_symlink_ok)  // only if symlinks supported
  {
    symlink_status_tests();
    copy_symlink_tests(f1, d1);
    canonical_symlink_tests();
    weakly_canonical_tests();
  }
  iterator_status_tests();  // lots of cases by now, so a good time to test
//  dump_tree(dir);
  recursive_directory_iterator_tests();
  recursive_iterator_status_tests();  // lots of cases by now, so a good time to test
  rename_tests();
  remove_tests(dir);
  if (create_symlink_ok)  // only if symlinks supported
    remove_symlink_tests();
  write_time_tests(dir);
  temp_directory_path_tests();

  platform_specific_tests();  // do these last since they take a lot of time on Windows,
                              // and that's a pain during manual testing

  cout << "testing complete" << endl;

  // post-test cleanup
  if (cleanup)
  {
    cout << "post-test removal of " << dir << endl;
    BOOST_TEST(fs::remove_all(dir) != 0);
    // above was added just to simplify testing, but it ended up detecting
    // a bug (failure to close an internal search handle).
    cout << "post-test removal complete" << endl;
//    BOOST_TEST(!fs::exists(dir));  // nice test, but doesn't play well with TortoiseGit cache
  }

  cout << "returning from main()" << endl;
  return ::boost::report_errors();
} // main