1 /*
2  * PROJECT:     ReactOS API tests
3  * LICENSE:     GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+)
4  * PURPOSE:     Test for CUserNotification
5  * COPYRIGHT:   Copyright 2018 Hermes Belusca-Maito
6  */
7 
8 #include "shelltest.h"
9 
10 #define NDEBUG
11 #include <debug.h>
12 
13 #define ok_hr(status, expected)     ok_hex(status, expected)
14 
15 #define HRESULT_CANCELLED   HRESULT_FROM_WIN32(ERROR_CANCELLED)
16 
17 
18 /* An implementation of the IQueryContinue interface */
19 class CQueryContinue : public IQueryContinue
20 {
21 private:
22     HRESULT m_hContinue;
23 
24 public:
25     CQueryContinue(HRESULT hContinue = S_OK) : m_hContinue(hContinue) {}
26     ~CQueryContinue() {}
27 
28     CQueryContinue& operator=(const CQueryContinue& qc)
29     {
30         if (this != &qc)
31             m_hContinue = qc.m_hContinue;
32         return *this;
33     }
34 
35     CQueryContinue& operator=(HRESULT hContinue)
36     {
37         m_hContinue = hContinue;
38         return *this;
39     }
40 
41     operator HRESULT()
42     {
43         return m_hContinue;
44     }
45 
46 public:
47     // IUnknown
48     virtual ULONG STDMETHODCALLTYPE AddRef()
49     {
50         return 1;
51     }
52 
53     virtual ULONG STDMETHODCALLTYPE Release()
54     {
55         return 0;
56     }
57 
58     virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void **ppvObject)
59     {
60         return S_OK;
61     }
62 
63     // IQueryContinue
64     virtual HRESULT STDMETHODCALLTYPE QueryContinue(void)
65     {
66         // TRACE("IQueryContinue::QueryContinue() returning 0x%lx\n", m_hContinue);
67         return m_hContinue;
68     }
69 };
70 
71 
72 static void
73 TestNotification(void)
74 {
75     HRESULT hr;
76     CComPtr<IUserNotification> pUserNotif;
77     CQueryContinue queryContinue(S_OK);
78 
79     // hr = pUserNotif.CoCreateInstance(CLSID_UserNotification);
80     hr = ::CoCreateInstance(CLSID_UserNotification, NULL, CLSCTX_ALL,
81                             /*IID_PPV_ARG(IUserNotification, &pUserNotif)*/
82                             IID_IUserNotification, (void**)&pUserNotif);
83     ok(hr == S_OK, "CoCreateInstance, hr = 0x%lx\n", hr);
84     if (FAILED(hr))
85     {
86         skip("Could not instantiate IUserNotification\n");
87         return;
88     }
89 
90     /* Set an invalid icon for the notification icon */
91     hr = pUserNotif->SetIconInfo((HICON)UlongToHandle(0xdeadbeef), L"Tooltip text");
92     ok_hr(hr, S_OK);
93 
94 #if 0
95     /* Seting an invalid string would crash the application */
96     hr = pUserNotif->SetIconInfo(NULL, (LPCWSTR)0xdeadbeef);
97     ok_hr(hr, S_OK);
98 #endif
99 
100     /* Set a default icon for the notification icon */
101     hr = pUserNotif->SetIconInfo(NULL, L"Tooltip text");
102     ok_hr(hr, S_OK);
103 
104     /*
105      * Since just displaying a notification icon without balloon hangs (expected),
106      * for making this test automatable we instead just test balloon functionality
107      * where timeouts can be programmed.
108      */
109 
110     /* Set up a balloon associated to the notification icon */
111     hr = pUserNotif->SetBalloonInfo(L"Balloon title", L"Balloon text", NIIF_ERROR);
112     ok_hr(hr, S_OK);
113 
114     /*
115      * Try to display twice the balloon if the user cancels it.
116      * Without setting balloon retry, we would wait for a very long time...
117      */
118     hr = pUserNotif->SetBalloonRetry(2000, 1000, 2);
119     ok_hr(hr, S_OK);
120 
121     /* Display the balloon and also the tooltip if one points on the icon */
122     hr = pUserNotif->Show(NULL, 0);
123     ok_hr(hr, HRESULT_CANCELLED);
124 
125     /*
126      * Setting icon information *after* having enabled balloon info,
127      * allows to automatically set the notification icon according
128      * to the dwInfoFlags passed to SetBalloonInfo() and by giving
129      * NULL to the hIcon parameter of SetIconInfo().
130      */
131     hr = pUserNotif->SetIconInfo(NULL, NULL);
132     ok_hr(hr, S_OK);
133 
134     /* Display the balloon and also the tooltip if one points on the icon */
135     hr = pUserNotif->Show(NULL, 0);
136     ok_hr(hr, HRESULT_CANCELLED);
137 
138     /*
139      * This line shows the balloon, but without title nor icon in it.
140      * Note that the balloon icon is not displayed when not setting any title.
141      */
142     hr = pUserNotif->SetBalloonInfo(NULL, L"Balloon text", NIIF_WARNING);
143     ok_hr(hr, S_OK);
144 
145     hr = pUserNotif->Show(NULL, 0);
146     ok_hr(hr, HRESULT_CANCELLED);
147 
148 
149     /* Test support of the IQueryContinue interface */
150 
151     hr = pUserNotif->SetBalloonInfo(L"Balloon title", L"Balloon text", NIIF_WARNING);
152     ok_hr(hr, S_OK);
153 
154     hr = pUserNotif->Show(&queryContinue, 2000); /* Timeout of 2 seconds */
155     ok_hr(hr, HRESULT_CANCELLED);
156 
157 #if 0 // Commented because this test (the Show() call) is unreliable.
158     /* Try to hide the balloon by setting an empty string (can use either NULL or L"") */
159     hr = pUserNotif->SetBalloonInfo(L"Balloon title", NULL, NIIF_WARNING);
160     ok_hr(hr, S_OK);
161 
162     hr = pUserNotif->Show(&queryContinue, 2000); /* Timeout of 2 seconds */
163     ok_hr(hr, HRESULT_CANCELLED);
164 #endif
165 
166     hr = pUserNotif->SetBalloonInfo(L"Balloon title", L"Balloon text", NIIF_WARNING);
167     ok_hr(hr, S_OK);
168 
169     queryContinue = S_FALSE;
170     hr = pUserNotif->Show(&queryContinue, 2000); /* Timeout of 2 seconds */
171     ok_hr(hr, S_FALSE);
172 }
173 
174 DWORD
175 CALLBACK
176 TestThread(LPVOID lpParam)
177 {
178     /* Initialize COM */
179     CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
180 
181     /* Start the test */
182     TestNotification();
183 
184     /* Cleanup and return */
185     CoUninitialize();
186     return 0;
187 }
188 
189 START_TEST(CUserNotification)
190 {
191     HANDLE hThread;
192     DWORD dwWait;
193 
194     /* We create a test thread, because the notification tests can hang */
195     hThread = CreateThread(NULL, 0, TestThread, NULL, 0, NULL);
196     ok(hThread != NULL, "CreateThread failed with error 0x%lu\n", GetLastError());
197     if (!hThread)
198     {
199         skip("Could not create the CUserNotification test thread!\n");
200         return;
201     }
202 
203     /* Wait a maximum of 60 seconds for the thread to finish (the timeout tests take some time) */
204     dwWait = WaitForSingleObject(hThread, 60 * 1000);
205     ok(dwWait == WAIT_OBJECT_0, "WaitForSingleObject returned %lu, expected WAIT_OBJECT_0\n", dwWait);
206 
207     /* Cleanup and return */
208     CloseHandle(hThread);
209 }
210