text: #include <memory>
  #include <cstdlib>
  #include <gtest/gtest.h>
  #include <uvw.hpp>
  
  
  template<typename T>
  struct tag { using type = T; };
  
  
  TEST(Util, UnscopedFlags) {
      enum class UnscopedEnum { FOO = 1, BAR = 2, BAZ = 4, QUUX = 8 };
  
      uvw::Flags<UnscopedEnum> flags{};
  
      ASSERT_NO_THROW((flags = uvw::Flags<UnscopedEnum>::from<UnscopedEnum::FOO, UnscopedEnum::BAR>()));
      ASSERT_NO_THROW((flags = uvw::Flags<UnscopedEnum>{UnscopedEnum::BAZ}));
      ASSERT_NO_THROW((flags = uvw::Flags<UnscopedEnum>{static_cast<uvw::Flags<UnscopedEnum>::Type>(UnscopedEnum::QUUX)}));
  
      ASSERT_NO_THROW((flags = uvw::Flags<UnscopedEnum>{std::move(flags)}));
      ASSERT_NO_THROW((flags = uvw::Flags<UnscopedEnum>{flags}));
  
      flags = uvw::Flags<UnscopedEnum>::from<UnscopedEnum::FOO, UnscopedEnum::QUUX>();
  
      ASSERT_TRUE(static_cast<bool>(flags));
      ASSERT_EQ(static_cast<uvw::Flags<UnscopedEnum>::Type>(flags), 9);
  
      ASSERT_TRUE(flags & uvw::Flags<UnscopedEnum>::from<UnscopedEnum::FOO>());
      ASSERT_FALSE(flags & UnscopedEnum::BAR);
      ASSERT_FALSE(flags & uvw::Flags<UnscopedEnum>::from<UnscopedEnum::BAZ>());
      ASSERT_TRUE(flags & UnscopedEnum::QUUX);
  
      ASSERT_NO_THROW(flags = flags | UnscopedEnum::BAR);
      ASSERT_NO_THROW(flags = flags | uvw::Flags<UnscopedEnum>::from<UnscopedEnum::BAZ>());
  
      ASSERT_TRUE(flags & UnscopedEnum::FOO);
      ASSERT_TRUE(flags & uvw::Flags<UnscopedEnum>::from<UnscopedEnum::BAR>());
      ASSERT_TRUE(flags & UnscopedEnum::BAZ);
      ASSERT_TRUE(flags & uvw::Flags<UnscopedEnum>::from<UnscopedEnum::QUUX>());
  }
  
  
  TEST(Util, ScopedFlags) {
      enum class ScopedEnum { FOO = 1, BAR = 2, BAZ = 4, QUUX = 8 };
  
      uvw::Flags<ScopedEnum> flags{};
  
      ASSERT_NO_THROW((flags = uvw::Flags<ScopedEnum>::from<ScopedEnum::FOO, ScopedEnum::BAR>()));
      ASSERT_NO_THROW((flags = uvw::Flags<ScopedEnum>{ScopedEnum::BAZ}));
      ASSERT_NO_THROW((flags = uvw::Flags<ScopedEnum>{static_cast<uvw::Flags<ScopedEnum>::Type>(ScopedEnum::QUUX)}));
  
      ASSERT_NO_THROW((flags = uvw::Flags<ScopedEnum>{std::move(flags)}));
      ASSERT_NO_THROW((flags = uvw::Flags<ScopedEnum>{flags}));
  
      flags = uvw::Flags<ScopedEnum>::from<ScopedEnum::FOO, ScopedEnum::QUUX>();
  
      ASSERT_TRUE(static_cast<bool>(flags));
      ASSERT_EQ(static_cast<uvw::Flags<ScopedEnum>::Type>(flags), 9);
  
      ASSERT_TRUE(flags & uvw::Flags<ScopedEnum>::from<ScopedEnum::FOO>());
      ASSERT_FALSE(flags & ScopedEnum::BAR);
      ASSERT_FALSE(flags & uvw::Flags<ScopedEnum>::from<ScopedEnum::BAZ>());
      ASSERT_TRUE(flags & ScopedEnum::QUUX);
  
      ASSERT_NO_THROW(flags = flags | ScopedEnum::BAR);
      ASSERT_NO_THROW(flags = flags | uvw::Flags<ScopedEnum>::from<ScopedEnum::BAZ>());
  
      ASSERT_TRUE(flags & ScopedEnum::FOO);
      ASSERT_TRUE(flags & uvw::Flags<ScopedEnum>::from<ScopedEnum::BAR>());
      ASSERT_TRUE(flags & ScopedEnum::BAZ);
      ASSERT_TRUE(flags & uvw::Flags<ScopedEnum>::from<ScopedEnum::QUUX>());
  }
  
  
  TEST(Util, Utilities) {
      ASSERT_EQ(uvw::PidType{}, uvw::PidType{});
  
      ASSERT_NE(uvw::Utilities::OS::pid(), uvw::PidType{});
      ASSERT_NE(uvw::Utilities::OS::parent(), uvw::PidType{});
      ASSERT_FALSE(uvw::Utilities::OS::homedir().empty());
      ASSERT_FALSE(uvw::Utilities::OS::tmpdir().empty());
      ASSERT_NE(uvw::Utilities::OS::hostname(), "");
  
      ASSERT_TRUE(uvw::Utilities::OS::env("UVW_TEST_UTIL_UTILITIES", "TRUE"));
      ASSERT_TRUE(uvw::Utilities::OS::env("UVW_TEST_UTIL_UTILITIES") == "TRUE");
      ASSERT_TRUE(uvw::Utilities::OS::env("UVW_TEST_UTIL_UTILITIES", ""));
      ASSERT_FALSE(uvw::Utilities::OS::env("UVW_TEST_UTIL_UTILITIES") == "TRUE");
  
      auto passwd = uvw::Utilities::OS::passwd();
  
      ASSERT_TRUE(static_cast<bool>(passwd));
      ASSERT_FALSE(passwd.username().empty());
      ASSERT_FALSE(passwd.homedir().empty());
      ASSERT_NO_THROW(passwd.uid());
      ASSERT_NO_THROW(passwd.gid());
  
  #ifndef _MSC_VER
      // libuv returns a null string for the shell on Windows
      ASSERT_FALSE(passwd.shell().empty());
  #endif
  
      ASSERT_EQ(uvw::Utilities::guessHandle(uvw::FileHandle{-1}), uvw::HandleType::UNKNOWN);
      ASSERT_NE(uvw::Utilities::guessHandle(uvw::StdIN), uvw::HandleType::UNKNOWN);
  
      auto guessHandle = [](auto tag, auto type) {
          auto loop = uvw::Loop::getDefault();
          auto handle = loop->resource<typename decltype(tag)::type>();
          ASSERT_EQ(uvw::Utilities::guessHandle(handle->category()), type);
          handle->close();
          loop->run();
      };
  
      guessHandle(tag<uvw::AsyncHandle>{}, uvw::HandleType::ASYNC);
      guessHandle(tag<uvw::CheckHandle>{}, uvw::HandleType::CHECK);
      guessHandle(tag<uvw::FsEventHandle>{}, uvw::HandleType::FS_EVENT);
      guessHandle(tag<uvw::FsPollHandle>{}, uvw::HandleType::FS_POLL);
      guessHandle(tag<uvw::IdleHandle>{}, uvw::HandleType::IDLE);
      guessHandle(tag<uvw::PipeHandle>{}, uvw::HandleType::PIPE);
      guessHandle(tag<uvw::PrepareHandle>{}, uvw::HandleType::PREPARE);
      guessHandle(tag<uvw::TCPHandle>{}, uvw::HandleType::TCP);
      guessHandle(tag<uvw::TimerHandle>{}, uvw::HandleType::TIMER);
      guessHandle(tag<uvw::UDPHandle>{}, uvw::HandleType::UDP);
      guessHandle(tag<uvw::SignalHandle>{}, uvw::HandleType::SIGNAL);
  
      auto cpuInfo = uvw::Utilities::cpuInfo();
  
      ASSERT_NE(cpuInfo.size(), decltype(cpuInfo.size()){0});
      ASSERT_FALSE(cpuInfo[0].model.empty());
      ASSERT_NE(cpuInfo[0].speed, decltype(cpuInfo[0].speed){0});
  
      auto interfaceAddresses = uvw::Utilities::interfaceAddresses();
  
      ASSERT_NE(interfaceAddresses.size(), decltype(interfaceAddresses.size()){0});
      ASSERT_FALSE(interfaceAddresses[0].name.empty());
      ASSERT_FALSE(interfaceAddresses[0].address.ip.empty());
      ASSERT_FALSE(interfaceAddresses[0].netmask.ip.empty());
  
      ASSERT_NO_THROW(uvw::Utilities::indexToName(0));
      ASSERT_NO_THROW(uvw::Utilities::indexToIid(0));
  
      ASSERT_TRUE(uvw::Utilities::replaceAllocator(
          [](size_t size) { return malloc(size); },
          [](void *ptr, size_t size) { return realloc(ptr, size); },
          [](size_t num, size_t size) { return calloc(num, size); },
          [](void *ptr) { return free(ptr); }
      ));
  
      ASSERT_NO_THROW(uvw::Utilities::loadAverage());
      ASSERT_NE(uvw::Utilities::totalMemory(), decltype(uvw::Utilities::totalMemory()){0});
      ASSERT_NE(uvw::Utilities::uptime(), decltype(uvw::Utilities::uptime()){0});
      ASSERT_NO_THROW(uvw::Utilities::rusage());
      ASSERT_NE(uvw::Utilities::hrtime(), decltype(uvw::Utilities::hrtime()){0});
      ASSERT_FALSE(uvw::Utilities::path().empty());
      ASSERT_FALSE(uvw::Utilities::cwd().empty());
      ASSERT_TRUE(uvw::Utilities::chdir(uvw::Utilities::cwd()));
  
      std::unique_ptr<char[], void(*)(void *)> fake{new char[1], [](void *ptr) { delete[] static_cast<char *>(ptr); }};
      char *argv = fake.get();
      argv[0] = '\0';
  
      ASSERT_NE(uvw::Utilities::setupArgs(1, &argv), nullptr);
      ASSERT_NE(uvw::Utilities::processTitle(), std::string{});
      ASSERT_TRUE(uvw::Utilities::processTitle(uvw::Utilities::processTitle()));
  }
  