text: // Copyright (c) 2011 The Chromium Authors. All rights reserved.
  // Use of this source code is governed by a BSD-style license that can be
  // found in the LICENSE file.
  
  #include <windows.h>
  #include <winternl.h>
  
  #include "base/base_switches.h"
  #include "base/command_line.h"
  #include "base/files/file_path.h"
  #include "base/scoped_native_library.h"
  #include "base/test/multiprocess_test.h"
  #include "base/test/test_timeouts.h"
  #include "base/win/scoped_handle.h"
  
  #include "testing/gtest/include/gtest/gtest.h"
  #include "testing/multiprocess_func_list.h"
  
  namespace base {
  namespace win {
  
  namespace testing {
  extern "C" bool __declspec(dllexport) RunTest();
  }  // namespace testing
  
  TEST(ScopedHandleTest, ScopedHandle) {
    // Any illegal error code will do. We just need to test that it is preserved
    // by ScopedHandle to avoid bug 528394.
    const DWORD magic_error = 0x12345678;
  
    HANDLE handle = ::CreateMutex(nullptr, false, nullptr);
    // Call SetLastError after creating the handle.
    ::SetLastError(magic_error);
    base::win::ScopedHandle handle_holder(handle);
    EXPECT_EQ(magic_error, ::GetLastError());
  
    // Create a new handle and then set LastError again.
    handle = ::CreateMutex(nullptr, false, nullptr);
    ::SetLastError(magic_error);
    handle_holder.Set(handle);
    EXPECT_EQ(magic_error, ::GetLastError());
  
    // Create a new handle and then set LastError again.
    handle = ::CreateMutex(nullptr, false, nullptr);
    base::win::ScopedHandle handle_source(handle);
    ::SetLastError(magic_error);
    handle_holder = std::move(handle_source);
    EXPECT_EQ(magic_error, ::GetLastError());
  }
  
  TEST(ScopedHandleTest, ActiveVerifierTrackedHasBeenClosed) {
    HANDLE handle = ::CreateMutex(nullptr, false, nullptr);
    ASSERT_NE(HANDLE(nullptr), handle);
    typedef NTSTATUS(WINAPI * NtCloseFunc)(HANDLE);
    NtCloseFunc ntclose = reinterpret_cast<NtCloseFunc>(
        GetProcAddress(GetModuleHandle(L"ntdll.dll"), "NtClose"));
    ASSERT_NE(nullptr, ntclose);
  
    ASSERT_DEATH({
      base::win::ScopedHandle handle_holder(handle);
      ntclose(handle);
      // Destructing a ScopedHandle with an illegally closed handle should fail.
    }, "");
  }
  
  TEST(ScopedHandleTest, ActiveVerifierDoubleTracking) {
    HANDLE handle = ::CreateMutex(nullptr, false, nullptr);
    ASSERT_NE(HANDLE(nullptr), handle);
  
    base::win::ScopedHandle handle_holder(handle);
  
    ASSERT_DEATH({
      base::win::ScopedHandle handle_holder2(handle);
    }, "");
  }
  
  TEST(ScopedHandleTest, ActiveVerifierWrongOwner) {
    HANDLE handle = ::CreateMutex(nullptr, false, nullptr);
    ASSERT_NE(HANDLE(nullptr), handle);
  
    base::win::ScopedHandle handle_holder(handle);
    ASSERT_DEATH({
      base::win::ScopedHandle handle_holder2;
      handle_holder2.handle_ = handle;
    }, "");
    ASSERT_TRUE(handle_holder.IsValid());
    handle_holder.Close();
  }
  
  TEST(ScopedHandleTest, ActiveVerifierUntrackedHandle) {
    HANDLE handle = ::CreateMutex(nullptr, false, nullptr);
    ASSERT_NE(HANDLE(nullptr), handle);
  
    ASSERT_DEATH({
      base::win::ScopedHandle handle_holder;
      handle_holder.handle_ = handle;
    }, "");
  
    ASSERT_TRUE(::CloseHandle(handle));
  }
  
  TEST(ScopedHandleTest, MultiProcess) {
    // Initializing ICU in the child process causes a scoped handle to be created
    // before the test gets a chance to test the race condition, so disable ICU
    // for the child process here.
    CommandLine command_line(base::GetMultiProcessTestChildBaseCommandLine());
    command_line.AppendSwitch(switches::kTestDoNotInitializeIcu);
  
    base::Process test_child_process = base::SpawnMultiProcessTestChild(
        "ActiveVerifierChildProcess", command_line, LaunchOptions());
  
    int rv = -1;
    ASSERT_TRUE(test_child_process.WaitForExitWithTimeout(
        TestTimeouts::action_timeout(), &rv));
    EXPECT_EQ(0, rv);
  }
  
  MULTIPROCESS_TEST_MAIN(ActiveVerifierChildProcess) {
    ScopedNativeLibrary module(FilePath(L"scoped_handle_test_dll.dll"));
  
    if (!module.is_valid())
      return 1;
    auto run_test_function = reinterpret_cast<decltype(&testing::RunTest)>(
        module.GetFunctionPointer("RunTest"));
    if (!run_test_function)
      return 1;
    if (!run_test_function())
      return 1;
  
    return 0;
  }
  
  }  // namespace win
  }  // namespace base
  