agent_win_unittest.cc (15387B)
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 <latch> 6 #include <memory> 7 #include <thread> 8 9 #include "agent/src/agent_win.h" 10 #include "agent/src/event_win.h" 11 #include "browser/src/client_win.h" 12 #include "gtest/gtest.h" 13 14 namespace content_analysis { 15 namespace sdk { 16 namespace testing { 17 18 // A handler that counts the number of times the callback methods are invoked. 19 // Also remembers the last BrowserInfo structure passed to it from any of the 20 // callbacks. 21 struct TestHandler : public AgentEventHandler { 22 void OnBrowserConnected(const BrowserInfo& info) override { 23 last_info_ = info; 24 ++connect_count_; 25 } 26 void OnBrowserDisconnected(const BrowserInfo& info) override { 27 last_info_ = info; 28 ++disconnect_count_; 29 } 30 void OnAnalysisRequested( 31 std::unique_ptr<ContentAnalysisEvent> event) override { 32 ++request_count_; 33 ResultCode ret = event->Send(); 34 ASSERT_EQ(ResultCode::OK, ret); 35 } 36 void OnResponseAcknowledged( 37 const ContentAnalysisAcknowledgement& ack) override { 38 ++ack_count_; 39 } 40 void OnCancelRequests( 41 const ContentAnalysisCancelRequests& cancel) override { 42 ++cancel_count_; 43 } 44 45 int connect_count_ = 0; 46 int disconnect_count_ = 0; 47 int request_count_ = 0; 48 int ack_count_ = 0; 49 int cancel_count_ = 0; 50 BrowserInfo last_info_; 51 }; 52 53 // A test handler that closes its event before sending the response. 54 struct CloseEventTestHandler : public TestHandler { 55 void OnAnalysisRequested( 56 std::unique_ptr<ContentAnalysisEvent> event) override { 57 ++request_count_; 58 59 // Closing the event before sending should generate an error. 60 ResultCode ret = event->Close(); 61 ASSERT_EQ(ResultCode::OK, ret); 62 63 ret = event->Send(); 64 ASSERT_NE(ResultCode::OK, ret); 65 } 66 }; 67 68 // A test handler that attempts to send two responses for a given request. 69 struct DoubleSendTestHandler : public TestHandler { 70 void OnAnalysisRequested( 71 std::unique_ptr<ContentAnalysisEvent> event) override { 72 ++request_count_; 73 74 ResultCode ret = event->Send(); 75 ASSERT_EQ(ResultCode::OK, ret); 76 77 // Trying to send again fails. 78 ret = event->Send(); 79 ASSERT_NE(ResultCode::OK, ret); 80 } 81 }; 82 83 // A test handler that signals a latch after a client connects. 84 // Can only be used with one client. 85 struct SignalClientConnectedTestHandler : public TestHandler { 86 void OnBrowserConnected(const BrowserInfo& info) override { 87 TestHandler::OnBrowserConnected(info); 88 wait_for_client.count_down(); 89 } 90 91 std::latch wait_for_client{ 1 }; 92 }; 93 94 // A test handler that signals a latch after a request is processed. 95 // Can only be used with one request. 96 struct SignalClientRequestedTestHandler : public TestHandler { 97 void OnAnalysisRequested( 98 std::unique_ptr<ContentAnalysisEvent> event) override { 99 TestHandler::OnAnalysisRequested(std::move(event)); 100 wait_for_request.count_down(); 101 } 102 103 std::latch wait_for_request{ 1 }; 104 }; 105 106 std::unique_ptr<AgentWin> CreateAgent( 107 Agent::Config config, 108 TestHandler** handler_ptr, 109 ResultCode expected_rc=ResultCode::OK) { 110 ResultCode rc; 111 auto handler = std::make_unique<TestHandler>(); 112 *handler_ptr = handler.get(); 113 auto agent = std::make_unique<AgentWin>( 114 std::move(config), std::move(handler), &rc); 115 EXPECT_EQ(expected_rc, rc); 116 return agent; 117 } 118 119 std::unique_ptr<ClientWin> CreateClient( 120 Client::Config config) { 121 int rc; 122 auto client = std::make_unique<ClientWin>(std::move(config), &rc); 123 return rc == 0 ? std::move(client) : nullptr; 124 } 125 126 ContentAnalysisRequest BuildRequest(std::string content=std::string()) { 127 ContentAnalysisRequest request; 128 request.set_request_token("req-token"); 129 *request.add_tags() = "dlp"; 130 request.set_text_content(content); // Moved. 131 return request; 132 } 133 134 TEST(AgentTest, Create) { 135 const Agent::Config config{"test", false}; 136 TestHandler* handler_ptr; 137 auto agent = CreateAgent(config, &handler_ptr); 138 ASSERT_TRUE(agent); 139 ASSERT_TRUE(handler_ptr); 140 141 ASSERT_EQ(config.name, agent->GetConfig().name); 142 ASSERT_EQ(config.user_specific, agent->GetConfig().user_specific); 143 } 144 145 TEST(AgentTest, Create_InvalidPipename) { 146 // TODO(rogerta): The win32 docs say that a backslash is an invalid 147 // character for a pipename, but it seemed to work correctly in testing. 148 // Using an empty name was the easiest way to generate an invalid pipe 149 // name. 150 const Agent::Config config{"", false}; 151 TestHandler* handler_ptr; 152 auto agent = CreateAgent(config, &handler_ptr, 153 ResultCode::ERR_INVALID_CHANNEL_NAME); 154 ASSERT_TRUE(agent); 155 156 ASSERT_EQ(ResultCode::ERR_AGENT_NOT_INITIALIZED, 157 agent->HandleOneEventForTesting()); 158 } 159 160 // Can't create two agents with the same name. 161 TEST(AgentTest, Create_SecondFails) { 162 const Agent::Config config{ "test", false }; 163 TestHandler* handler_ptr1; 164 auto agent1 = CreateAgent(config, &handler_ptr1); 165 ASSERT_TRUE(agent1); 166 167 TestHandler* handler_ptr2; 168 auto agent2 = CreateAgent(config, &handler_ptr2, 169 ResultCode::ERR_AGENT_ALREADY_EXISTS); 170 ASSERT_TRUE(agent2); 171 172 ASSERT_EQ(ResultCode::ERR_AGENT_NOT_INITIALIZED, 173 agent2->HandleOneEventForTesting()); 174 } 175 176 TEST(AgentTest, Stop) { 177 TestHandler* handler_ptr; 178 auto agent = CreateAgent({ "test", false }, &handler_ptr); 179 ASSERT_TRUE(agent); 180 181 // Create a separate thread to stop the agent. 182 std::thread thread([&agent]() { 183 agent->Stop(); 184 }); 185 186 agent->HandleEvents(); 187 thread.join(); 188 } 189 190 TEST(AgentTest, ConnectAndStop) { 191 ResultCode rc; 192 auto handler = std::make_unique<SignalClientConnectedTestHandler>(); 193 auto* handler_ptr = handler.get(); 194 auto agent = std::make_unique<AgentWin>( 195 Agent::Config{"test", false}, std::move(handler), &rc); 196 ASSERT_TRUE(agent); 197 ASSERT_EQ(ResultCode::OK, rc); 198 199 // Client thread waits until latch reaches zero. 200 std::latch stop_client{ 1 }; 201 202 // Create a thread to handle the client. Since the client is sync, it can't 203 // run in the same thread as the agent. 204 std::thread client_thread([&stop_client]() { 205 auto client = CreateClient({ "test", false }); 206 ASSERT_TRUE(client); 207 stop_client.wait(); 208 }); 209 210 // A thread that stops the agent after one client connects. 211 std::thread stop_agent([&handler_ptr, &agent]() { 212 handler_ptr->wait_for_client.wait(); 213 agent->Stop(); 214 }); 215 216 agent->HandleEvents(); 217 218 stop_client.count_down(); 219 client_thread.join(); 220 stop_agent.join(); 221 } 222 223 TEST(AgentTest, Connect_UserSpecific) { 224 ResultCode rc; 225 auto handler = std::make_unique<SignalClientConnectedTestHandler>(); 226 auto* handler_ptr = handler.get(); 227 auto agent = std::make_unique<AgentWin>( 228 Agent::Config{ "test", true }, std::move(handler), &rc); 229 ASSERT_TRUE(agent); 230 ASSERT_EQ(ResultCode::OK, rc); 231 232 // Create a thread to handle the client. Since the client is sync, it can't 233 // run in the same thread as the agent. 234 std::thread client_thread([]() { 235 // If the user_specific does not match the agent, the client should not 236 // connect. 237 auto client = CreateClient({ "test", false }); 238 ASSERT_FALSE(client); 239 240 auto client2 = CreateClient({ "test", true }); 241 ASSERT_TRUE(client2); 242 }); 243 244 // A thread that stops the agent after one client connects. 245 std::thread stop_agent([&handler_ptr, &agent]() { 246 handler_ptr->wait_for_client.wait(); 247 agent->Stop(); 248 }); 249 250 agent->HandleEvents(); 251 252 client_thread.join(); 253 stop_agent.join(); 254 } 255 256 TEST(AgentTest, ConnectRequestAndStop) { 257 ResultCode rc; 258 auto handler = std::make_unique<SignalClientRequestedTestHandler>(); 259 auto* handler_ptr = handler.get(); 260 auto agent = std::make_unique<AgentWin>( 261 Agent::Config{"test", false}, std::move(handler), &rc); 262 ASSERT_TRUE(agent); 263 ASSERT_EQ(ResultCode::OK, rc); 264 265 // Create a thread to handle the client. Since the client is sync, it can't 266 // run in the same thread as the agent. 267 std::thread client_thread([]() { 268 auto client = CreateClient({ "test", false }); 269 ASSERT_TRUE(client); 270 271 ContentAnalysisRequest request = BuildRequest("test"); 272 ContentAnalysisResponse response; 273 client->Send(request, &response); 274 }); 275 276 // A thread that stops the agent after one client connects. 277 std::thread stop_agent([&handler_ptr, &agent]() { 278 handler_ptr->wait_for_request.wait(); 279 agent->Stop(); 280 }); 281 282 agent->HandleEvents(); 283 284 client_thread.join(); 285 stop_agent.join(); 286 } 287 288 TEST(AgentTest, ConnectAndClose) { 289 const Agent::Config aconfig{ "test", false }; 290 const Client::Config cconfig{ "test", false }; 291 292 // Create an agent and client that connects to it. 293 TestHandler* handler_ptr; 294 auto agent = CreateAgent(aconfig, &handler_ptr); 295 ASSERT_TRUE(agent); 296 auto client = CreateClient(cconfig); 297 ASSERT_TRUE(client); 298 ASSERT_EQ(cconfig.name, client->GetConfig().name); 299 ASSERT_EQ(cconfig.user_specific, client->GetConfig().user_specific); 300 301 agent->HandleOneEventForTesting(); 302 ASSERT_EQ(1, handler_ptr->connect_count_); 303 ASSERT_EQ(0, handler_ptr->disconnect_count_); 304 ASSERT_EQ(0, handler_ptr->cancel_count_); 305 ASSERT_EQ(GetCurrentProcessId(), handler_ptr->last_info_.pid); 306 307 // Close the client and make sure a disconnect is received. 308 client.reset(); 309 agent->HandleOneEventForTesting(); 310 ASSERT_EQ(1, handler_ptr->connect_count_); 311 ASSERT_EQ(1, handler_ptr->disconnect_count_); 312 ASSERT_EQ(0, handler_ptr->cancel_count_); 313 ASSERT_EQ(GetCurrentProcessId(), handler_ptr->last_info_.pid); 314 } 315 316 TEST(AgentTest, Request) { 317 TestHandler* handler_ptr; 318 auto agent = CreateAgent({"test", false}, &handler_ptr); 319 ASSERT_TRUE(agent); 320 321 bool done = false; 322 323 // Create a thread to handle the client. Since the client is sync, it can't 324 // run in the same thread as the agent. 325 std::thread client_thread([&done]() { 326 auto client = CreateClient({"test", false}); 327 ASSERT_TRUE(client); 328 329 // Send a request and wait for a response. 330 ContentAnalysisRequest request = BuildRequest(); 331 ContentAnalysisResponse response; 332 int ret = client->Send(request, &response); 333 ASSERT_EQ(0, ret); 334 ASSERT_EQ(request.request_token(), response.request_token()); 335 336 done = true; 337 }); 338 339 while (!done) { 340 agent->HandleOneEventForTesting(); 341 } 342 ASSERT_EQ(1, handler_ptr->request_count_); 343 ASSERT_EQ(0, handler_ptr->ack_count_); 344 ASSERT_EQ(0, handler_ptr->cancel_count_); 345 346 client_thread.join(); 347 } 348 349 TEST(AgentTest, Request_Large) { 350 TestHandler* handler_ptr; 351 auto agent = CreateAgent({"test", false}, &handler_ptr); 352 ASSERT_TRUE(agent); 353 354 bool done = false; 355 356 // Create a thread to handle the client. Since the client is sync, it can't 357 // run in the same thread as the agent. 358 std::thread client_thread([&done]() { 359 auto client = CreateClient({"test", false}); 360 ASSERT_TRUE(client); 361 362 // Send a request and wait for a response. Create a large string, which 363 // means larger than the initial mesasge buffer size specified when 364 // creating the pipes (4096 bytes). 365 ContentAnalysisRequest request = BuildRequest(std::string(5000, 'a')); 366 ContentAnalysisResponse response; 367 int ret = client->Send(request, &response); 368 ASSERT_EQ(0, ret); 369 ASSERT_EQ(request.request_token(), response.request_token()); 370 371 done = true; 372 }); 373 374 while (!done) { 375 agent->HandleOneEventForTesting(); 376 } 377 ASSERT_EQ(1, handler_ptr->request_count_); 378 ASSERT_EQ(0, handler_ptr->ack_count_); 379 ASSERT_EQ(0, handler_ptr->cancel_count_); 380 381 client_thread.join(); 382 } 383 384 TEST(AgentTest, Request_DoubleSend) { 385 ResultCode rc; 386 auto handler = std::make_unique<DoubleSendTestHandler>(); 387 DoubleSendTestHandler* handler_ptr = handler.get(); 388 auto agent = std::make_unique<AgentWin>( 389 Agent::Config{"test", false}, std::move(handler), &rc); 390 ASSERT_TRUE(agent); 391 ASSERT_EQ(ResultCode::OK, rc); 392 393 bool done = false; 394 395 // Create a thread to handle the client. Since the client is sync, it can't 396 // run in the same thread as the agent. 397 std::thread client_thread([&done]() { 398 auto client = CreateClient({ "test", false }); 399 ASSERT_TRUE(client); 400 401 // Send a request and wait for a response. 402 ContentAnalysisRequest request = BuildRequest(); 403 ContentAnalysisResponse response; 404 int ret = client->Send(request, &response); 405 ASSERT_EQ(0, ret); 406 ASSERT_EQ(request.request_token(), response.request_token()); 407 408 done = true; 409 }); 410 411 while (!done) { 412 agent->HandleOneEventForTesting(); 413 } 414 ASSERT_EQ(1, handler_ptr->request_count_); 415 ASSERT_EQ(0, handler_ptr->ack_count_); 416 ASSERT_EQ(0, handler_ptr->cancel_count_); 417 418 client_thread.join(); 419 } 420 421 TEST(AgentTest, Request_CloseEvent) { 422 ResultCode rc; 423 auto handler = std::make_unique<CloseEventTestHandler>(); 424 CloseEventTestHandler* handler_ptr = handler.get(); 425 auto agent = std::make_unique<AgentWin>( 426 Agent::Config{"test", false}, std::move(handler), &rc); 427 ASSERT_TRUE(agent); 428 ASSERT_EQ(ResultCode::OK, rc); 429 430 bool done = false; 431 432 // Create a thread to handle the client. Since the client is sync, it can't 433 // run in the same thread as the agent. 434 std::thread client_thread([&done]() { 435 auto client = CreateClient({"test", false}); 436 ASSERT_TRUE(client); 437 438 // Send a request and wait for a response. 439 ContentAnalysisRequest request = BuildRequest(); 440 ContentAnalysisResponse response; 441 int ret = client->Send(request, &response); 442 ASSERT_EQ(0, ret); 443 ASSERT_EQ(request.request_token(), response.request_token()); 444 445 done = true; 446 }); 447 448 while (!done) { 449 agent->HandleOneEventForTesting(); 450 } 451 ASSERT_EQ(1, handler_ptr->request_count_); 452 453 client_thread.join(); 454 } 455 456 TEST(AgentTest, Ack) { 457 TestHandler* handler_ptr; 458 auto agent = CreateAgent({ "test", false }, &handler_ptr); 459 ASSERT_TRUE(agent); 460 461 bool done = false; 462 463 // Create a thread to handle the client. Since the client is sync, it can't 464 // run in the same thread as the agent. 465 std::thread client_thread([&done]() { 466 auto client = CreateClient({"test", false}); 467 ASSERT_TRUE(client); 468 469 // Send a request and wait for a response. 470 ContentAnalysisRequest request = BuildRequest(); 471 ContentAnalysisResponse response; 472 int ret = client->Send(request, &response); 473 ASSERT_EQ(0, ret); 474 475 ContentAnalysisAcknowledgement ack; 476 ack.set_request_token(request.request_token()); 477 ret = client->Acknowledge(ack); 478 ASSERT_EQ(0, ret); 479 480 done = true; 481 }); 482 483 while (!done) { 484 agent->HandleOneEventForTesting(); 485 } 486 ASSERT_EQ(1, handler_ptr->request_count_); 487 ASSERT_EQ(1, handler_ptr->ack_count_); 488 ASSERT_EQ(0, handler_ptr->cancel_count_); 489 490 client_thread.join(); 491 } 492 493 TEST(AgentTest, Cancel) { 494 TestHandler* handler_ptr; 495 auto agent = CreateAgent({ "test", false }, &handler_ptr); 496 ASSERT_TRUE(agent); 497 498 // Create a thread to handle the client. Since the client is sync, it can't 499 // run in the same thread as the agent. 500 std::thread client_thread([]() { 501 auto client = CreateClient({"test", false}); 502 ASSERT_TRUE(client); 503 504 ContentAnalysisCancelRequests cancel; 505 cancel.set_user_action_id("1234567890"); 506 int ret = client->CancelRequests(cancel); 507 ASSERT_EQ(0, ret); 508 }); 509 510 while (handler_ptr->cancel_count_ == 0) { 511 agent->HandleOneEventForTesting(); 512 } 513 ASSERT_EQ(0, handler_ptr->request_count_); 514 ASSERT_EQ(0, handler_ptr->ack_count_); 515 516 client_thread.join(); 517 } 518 519 } // namespace testing 520 } // namespace sdk 521 } // namespace content_analysis