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"
11 #include "mozilla/Atomics.h"
12 #include "mozilla/gtest/MozAssertions.h"
13 #include "mozilla/Monitor.h"
14 #include "nsNamedPipeService.h"
17 #define PIPE_NAME L"\\\\.\\pipe\\TestNPS"
18 #define TEST_STR "Hello World"
20 using namespace mozilla
;
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.
28 explicit Event(const char* aName
) : mMonitor(aName
) {}
33 MonitorAutoLock
lock(mMonitor
);
34 MOZ_RELEASE_ASSERT(!mSignaled
);
39 MonitorAutoLock
lock(mMonitor
);
47 Monitor mMonitor MOZ_UNANNOTATED
;
48 bool mSignaled
= false;
51 class nsNamedPipeDataObserver final
: public nsINamedPipeDataObserver
{
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
; }
64 ~nsNamedPipeDataObserver() = default;
67 OVERLAPPED mOverlapped
;
68 Atomic
<uint32_t> mBytesTransferred
;
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
) {
81 if (!ReadFile(mPipe
, aBuffer
, aSize
, &bytesRead
, &mOverlapped
)) {
82 switch (GetLastError()) {
83 case ERROR_IO_PENDING
: {
86 if (!GetOverlappedResult(mPipe
, &mOverlapped
, &bytesRead
, FALSE
)) {
87 ADD_FAILURE() << "GetOverlappedResult failed";
90 if (mBytesTransferred
!= bytesRead
) {
91 ADD_FAILURE() << "GetOverlappedResult mismatch";
97 ADD_FAILURE() << "ReadFile error " << GetLastError();
103 if (mBytesTransferred
!= bytesRead
) {
104 ADD_FAILURE() << "GetOverlappedResult mismatch";
109 mBytesTransferred
= 0;
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
: {
120 if (!GetOverlappedResult(mPipe
, &mOverlapped
, &bytesWritten
, FALSE
)) {
121 ADD_FAILURE() << "GetOverlappedResult failed";
124 if (mBytesTransferred
!= bytesWritten
) {
125 ADD_FAILURE() << "GetOverlappedResult mismatch";
131 ADD_FAILURE() << "WriteFile error " << GetLastError();
137 if (mBytesTransferred
!= bytesWritten
) {
138 ADD_FAILURE() << "GetOverlappedResult mismatch";
143 mBytesTransferred
= 0;
148 nsNamedPipeDataObserver::OnDataAvailable(uint32_t aBytesTransferred
,
150 if (aOverlapped
!= &mOverlapped
) {
151 ADD_FAILURE() << "invalid overlapped object";
152 return NS_ERROR_FAILURE
;
155 DWORD bytesTransferred
= 0;
157 GetOverlappedResult(mPipe
, reinterpret_cast<LPOVERLAPPED
>(aOverlapped
),
158 &bytesTransferred
, FALSE
);
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
;
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
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();
196 return ConnectToNewClient(*aPipe
, aOverlapped
);
199 BOOL
ConnectToNewClient(HANDLE aPipe
, LPOVERLAPPED aOverlapped
) {
200 if (ConnectNamedPipe(aPipe
, aOverlapped
)) {
202 << "Unexpected, overlapped ConnectNamedPipe() always returns 0.";
206 switch (GetLastError()) {
207 case ERROR_IO_PENDING
:
210 case ERROR_PIPE_CONNECTED
:
211 if (SetEvent(aOverlapped
->hEvent
)) break;
215 ADD_FAILURE() << "ConnectNamedPipe failed " << GetLastError();
222 static nsresult
CreateNamedPipe(LPHANDLE aServer
, LPHANDLE aClient
) {
223 OVERLAPPED overlapped
;
224 overlapped
.hEvent
= CreateEvent(NULL
, TRUE
, TRUE
, NULL
);
227 ret
= CreateAndConnectInstance(&overlapped
, aServer
);
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
);
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
))),
274 char buffer
[sizeof(TEST_STR
)];
275 ASSERT_EQ(std::size_t(readObserver
->Read(buffer
, sizeof(buffer
))),
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
));