1 // Copyright 2022 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "base/win/dark_mode_support.h"
6
7 #include <windows.h>
8
9 #include "base/native_library.h"
10 #include "base/scoped_native_library.h"
11 #include "base/test/gtest_util.h"
12 #include "testing/gtest/include/gtest/gtest.h"
13
14 namespace base::win {
15
16 namespace {
17
18 constexpr wchar_t kWndClassName[] = L"DarkModeWClass";
19
20 class DarkModeSupportTest : public testing::Test {
21 protected:
DarkModeSupportTest()22 DarkModeSupportTest() {
23 ux_theme_lib_ = base::ScopedNativeLibrary(
24 base::LoadSystemLibrary(L"uxtheme.dll", &error_));
25 }
26 ~DarkModeSupportTest() override = default;
27
SetUp()28 void SetUp() override {
29 testing::Test::SetUp();
30 // Do this here since the assert cannot live in the constructor.
31 ASSERT_FALSE(error_.code)
32 << "Failed to load uxtheme.dll with error " << error_.ToString();
33 }
34
TearDown()35 void TearDown() override {
36 // Return the process to the pre-allowed dark mode state.
37 AllowDarkModeForApp(false);
38 testing::Test::TearDown();
39 }
40
41 private:
42 base::NativeLibraryLoadError error_;
43 base::ScopedNativeLibrary ux_theme_lib_;
44 };
45
46 class ScopedWindowClass {
47 public:
ScopedWindowClass(const WNDCLASSEX & wnd_class)48 explicit ScopedWindowClass(const WNDCLASSEX& wnd_class)
49 : instance_(wnd_class.hInstance),
50 wnd_class_name_(wnd_class.lpszClassName) {
51 DoRegisterClass(wnd_class);
52 }
~ScopedWindowClass()53 ~ScopedWindowClass() { ::UnregisterClass(wnd_class_name_, instance_); }
54
55 private:
DoRegisterClass(const WNDCLASSEX & wnd_class)56 void DoRegisterClass(const WNDCLASSEX& wnd_class) {
57 ASSERT_TRUE(::RegisterClassEx(&wnd_class));
58 }
59 const HMODULE instance_;
60 const wchar_t* wnd_class_name_;
61 };
62
63 } // namespace
64
TEST_F(DarkModeSupportTest,TestIsDarkModeAvailable)65 TEST_F(DarkModeSupportTest, TestIsDarkModeAvailable) {
66 // The return value is irrelevant here. This only tests that this call can be
67 // make without crashing or otherwise behaving badly.
68 IsDarkModeAvailable();
69 }
70
TEST_F(DarkModeSupportTest,TestAllowDarkModeForApp)71 TEST_F(DarkModeSupportTest, TestAllowDarkModeForApp) {
72 // This call should always succeed and not crash or behave badly.
73 AllowDarkModeForApp(true);
74 }
75
TEST_F(DarkModeSupportTest,TestAllowDarkModeForWindowNoCrash)76 TEST_F(DarkModeSupportTest, TestAllowDarkModeForWindowNoCrash) {
77 HINSTANCE instance = ::GetModuleHandle(nullptr);
78 WNDCLASSEX wnd_class = {
79 .cbSize = sizeof(wnd_class),
80 .style = CS_HREDRAW | CS_VREDRAW,
81 .lpfnWndProc = ::DefWindowProc,
82 .hInstance = instance,
83 .hIcon = ::LoadIcon(NULL, IDI_APPLICATION),
84 .hCursor = ::LoadCursor(NULL, IDC_ARROW),
85 .hbrBackground = static_cast<HBRUSH>(::GetStockObject(WHITE_BRUSH)),
86 .lpszClassName = kWndClassName,
87 };
88 ScopedWindowClass scoped_window_class = ScopedWindowClass(wnd_class);
89
90 HWND hwnd =
91 ::CreateWindow(kWndClassName, L"DarkModeTest", WS_OVERLAPPEDWINDOW,
92 CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
93 nullptr, nullptr, instance, nullptr);
94 ASSERT_TRUE(hwnd) << "::CreateWindow failed with error " << ::GetLastError();
95 // The "dark mode" calls below shouldn't crash. This doesn't test whether or
96 // not they actually work, rather only that they don't crash or otherwise
97 // behave badly.
98 AllowDarkModeForApp(true);
99 AllowDarkModeForWindow(hwnd, true);
100 ::DestroyWindow(hwnd);
101 }
102
103 } // namespace base::win
104