tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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