Bug 1857386 [wpt PR 42383] - Update wpt metadata, a=testonly
[gecko.git] / netwerk / test / gtest / TestNamedPipeService.cpp
blobb91a17a93e1ab1adc4c7a5ef36cc9618c99c9873
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #include "TestCommon.h"
7 #include "gtest/gtest.h"
9 #include <windows.h>
11 #include "mozilla/Atomics.h"
12 #include "mozilla/gtest/MozAssertions.h"
13 #include "mozilla/Monitor.h"
14 #include "nsNamedPipeService.h"
15 #include "nsNetCID.h"
17 #define PIPE_NAME L"\\\\.\\pipe\\TestNPS"
18 #define TEST_STR "Hello World"
20 using namespace mozilla;
22 /**
23 * Unlike a monitor, an event allows a thread to wait on another thread
24 * completing an action without regard to ordering of the wait and the notify.
26 class Event {
27 public:
28 explicit Event(const char* aName) : mMonitor(aName) {}
30 ~Event() = default;
32 void Set() {
33 MonitorAutoLock lock(mMonitor);
34 MOZ_ASSERT(!mSignaled);
35 mSignaled = true;
36 mMonitor.Notify();
38 void Wait() {
39 MonitorAutoLock lock(mMonitor);
40 while (!mSignaled) {
41 lock.Wait();
43 mSignaled = false;
46 private:
47 Monitor mMonitor MOZ_UNANNOTATED;
48 bool mSignaled = false;
51 class nsNamedPipeDataObserver final : public nsINamedPipeDataObserver {
52 public:
53 NS_DECL_THREADSAFE_ISUPPORTS
54 NS_DECL_NSINAMEDPIPEDATAOBSERVER
56 explicit nsNamedPipeDataObserver(HANDLE aPipe);
58 int Read(void* aBuffer, uint32_t aSize);
59 int Write(const void* aBuffer, uint32_t aSize);
61 uint32_t Transferred() const { return mBytesTransferred; }
63 private:
64 ~nsNamedPipeDataObserver() = default;
66 HANDLE mPipe;
67 OVERLAPPED mOverlapped;
68 Atomic<uint32_t> mBytesTransferred;
69 Event mEvent;
72 NS_IMPL_ISUPPORTS(nsNamedPipeDataObserver, nsINamedPipeDataObserver)
74 nsNamedPipeDataObserver::nsNamedPipeDataObserver(HANDLE aPipe)
75 : mPipe(aPipe), mOverlapped(), mBytesTransferred(0), mEvent("named-pipe") {
76 mOverlapped.hEvent = CreateEventA(nullptr, TRUE, TRUE, "named-pipe");
79 int nsNamedPipeDataObserver::Read(void* aBuffer, uint32_t aSize) {
80 DWORD bytesRead = 0;
81 if (!ReadFile(mPipe, aBuffer, aSize, &bytesRead, &mOverlapped)) {
82 switch (GetLastError()) {
83 case ERROR_IO_PENDING: {
84 mEvent.Wait();
86 if (!GetOverlappedResult(mPipe, &mOverlapped, &bytesRead, FALSE)) {
87 ADD_FAILURE() << "GetOverlappedResult failed";
88 return -1;
90 if (mBytesTransferred != bytesRead) {
91 ADD_FAILURE() << "GetOverlappedResult mismatch";
92 return -1;
95 break;
96 default:
97 ADD_FAILURE() << "ReadFile error " << GetLastError();
98 return -1;
100 } else {
101 mEvent.Wait();
103 if (mBytesTransferred != bytesRead) {
104 ADD_FAILURE() << "GetOverlappedResult mismatch";
105 return -1;
109 mBytesTransferred = 0;
110 return bytesRead;
113 int nsNamedPipeDataObserver::Write(const void* aBuffer, uint32_t aSize) {
114 DWORD bytesWritten = 0;
115 if (!WriteFile(mPipe, aBuffer, aSize, &bytesWritten, &mOverlapped)) {
116 switch (GetLastError()) {
117 case ERROR_IO_PENDING: {
118 mEvent.Wait();
120 if (!GetOverlappedResult(mPipe, &mOverlapped, &bytesWritten, FALSE)) {
121 ADD_FAILURE() << "GetOverlappedResult failed";
122 return -1;
124 if (mBytesTransferred != bytesWritten) {
125 ADD_FAILURE() << "GetOverlappedResult mismatch";
126 return -1;
129 break;
130 default:
131 ADD_FAILURE() << "WriteFile error " << GetLastError();
132 return -1;
134 } else {
135 mEvent.Wait();
137 if (mBytesTransferred != bytesWritten) {
138 ADD_FAILURE() << "GetOverlappedResult mismatch";
139 return -1;
143 mBytesTransferred = 0;
144 return bytesWritten;
147 NS_IMETHODIMP
148 nsNamedPipeDataObserver::OnDataAvailable(uint32_t aBytesTransferred,
149 void* aOverlapped) {
150 if (aOverlapped != &mOverlapped) {
151 ADD_FAILURE() << "invalid overlapped object";
152 return NS_ERROR_FAILURE;
155 DWORD bytesTransferred = 0;
156 BOOL ret =
157 GetOverlappedResult(mPipe, reinterpret_cast<LPOVERLAPPED>(aOverlapped),
158 &bytesTransferred, FALSE);
160 if (!ret) {
161 ADD_FAILURE() << "GetOverlappedResult failed";
162 return NS_ERROR_FAILURE;
165 if (bytesTransferred != aBytesTransferred) {
166 ADD_FAILURE() << "GetOverlappedResult mismatch";
167 return NS_ERROR_FAILURE;
170 mBytesTransferred += aBytesTransferred;
171 mEvent.Set();
173 return NS_OK;
176 NS_IMETHODIMP
177 nsNamedPipeDataObserver::OnError(uint32_t aError, void* aOverlapped) {
178 return NS_ERROR_NOT_IMPLEMENTED;
181 BOOL CreateAndConnectInstance(LPOVERLAPPED aOverlapped, LPHANDLE aPipe);
182 BOOL ConnectToNewClient(HANDLE aPipe, LPOVERLAPPED aOverlapped);
184 BOOL CreateAndConnectInstance(LPOVERLAPPED aOverlapped, LPHANDLE aPipe) {
185 // FIXME: adjust parameters
186 *aPipe =
187 CreateNamedPipeW(PIPE_NAME, PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED,
188 PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT, 1,
189 65536, 65536, 3000, NULL);
191 if (*aPipe == INVALID_HANDLE_VALUE) {
192 ADD_FAILURE() << "CreateNamedPipe failed " << GetLastError();
193 return FALSE;
196 return ConnectToNewClient(*aPipe, aOverlapped);
199 BOOL ConnectToNewClient(HANDLE aPipe, LPOVERLAPPED aOverlapped) {
200 if (ConnectNamedPipe(aPipe, aOverlapped)) {
201 ADD_FAILURE()
202 << "Unexpected, overlapped ConnectNamedPipe() always returns 0.";
203 return FALSE;
206 switch (GetLastError()) {
207 case ERROR_IO_PENDING:
208 return TRUE;
210 case ERROR_PIPE_CONNECTED:
211 if (SetEvent(aOverlapped->hEvent)) break;
213 [[fallthrough]];
214 default: // error
215 ADD_FAILURE() << "ConnectNamedPipe failed " << GetLastError();
216 break;
219 return FALSE;
222 static nsresult CreateNamedPipe(LPHANDLE aServer, LPHANDLE aClient) {
223 OVERLAPPED overlapped;
224 overlapped.hEvent = CreateEvent(NULL, TRUE, TRUE, NULL);
225 BOOL ret;
227 ret = CreateAndConnectInstance(&overlapped, aServer);
228 if (!ret) {
229 ADD_FAILURE() << "pipe server should be pending";
230 return NS_ERROR_FAILURE;
233 *aClient = CreateFileW(PIPE_NAME, GENERIC_READ | GENERIC_WRITE,
234 FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr,
235 OPEN_EXISTING, FILE_FLAG_OVERLAPPED, nullptr);
237 if (*aClient == INVALID_HANDLE_VALUE) {
238 ADD_FAILURE() << "Unable to create pipe client";
239 CloseHandle(*aServer);
240 return NS_ERROR_FAILURE;
243 DWORD pipeMode = PIPE_READMODE_MESSAGE;
244 if (!SetNamedPipeHandleState(*aClient, &pipeMode, nullptr, nullptr)) {
245 ADD_FAILURE() << "SetNamedPipeHandleState error " << GetLastError();
246 CloseHandle(*aServer);
247 CloseHandle(*aClient);
248 return NS_ERROR_FAILURE;
251 WaitForSingleObjectEx(overlapped.hEvent, INFINITE, TRUE);
253 return NS_OK;
256 TEST(TestNamedPipeService, Test)
258 nsCOMPtr<nsINamedPipeService> svc = net::NamedPipeService::GetOrCreate();
260 HANDLE readPipe, writePipe;
261 nsresult rv = CreateNamedPipe(&readPipe, &writePipe);
262 ASSERT_NS_SUCCEEDED(rv);
264 RefPtr<nsNamedPipeDataObserver> readObserver =
265 new nsNamedPipeDataObserver(readPipe);
266 RefPtr<nsNamedPipeDataObserver> writeObserver =
267 new nsNamedPipeDataObserver(writePipe);
269 ASSERT_NS_SUCCEEDED(svc->AddDataObserver(readPipe, readObserver));
270 ASSERT_NS_SUCCEEDED(svc->AddDataObserver(writePipe, writeObserver));
271 ASSERT_EQ(std::size_t(writeObserver->Write(TEST_STR, sizeof(TEST_STR))),
272 sizeof(TEST_STR));
274 char buffer[sizeof(TEST_STR)];
275 ASSERT_EQ(std::size_t(readObserver->Read(buffer, sizeof(buffer))),
276 sizeof(TEST_STR));
277 ASSERT_STREQ(buffer, TEST_STR) << "I/O mismatch";
279 ASSERT_NS_SUCCEEDED(svc->RemoveDataObserver(readPipe, readObserver));
280 ASSERT_NS_SUCCEEDED(svc->RemoveDataObserver(writePipe, writeObserver));