File: notification_helper_process_unittest.cc

package info (click to toggle)
chromium 139.0.7258.127-1
  • links: PTS, VCS
  • area: main
  • in suites:
  • size: 6,122,068 kB
  • sloc: cpp: 35,100,771; ansic: 7,163,530; javascript: 4,103,002; python: 1,436,920; asm: 946,517; xml: 746,709; pascal: 187,653; perl: 88,691; sh: 88,436; objc: 79,953; sql: 51,488; cs: 44,583; fortran: 24,137; makefile: 22,147; tcl: 15,277; php: 13,980; yacc: 8,984; ruby: 7,485; awk: 3,720; lisp: 3,096; lex: 1,327; ada: 727; jsp: 228; sed: 36
file content (152 lines) | stat: -rw-r--r-- 5,568 bytes parent folder | download | duplicates (7)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
// Copyright 2018 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

// This file tests launching notification_helper.exe by the OS via the registry.
//
// An advanced version of this test is
// chrome/browser/notifications/win/notification_helper_launches_chrome_unittest.cc,
// which additionally tests if chrome.exe can be successfully launched by
// notification_helper.exe via the NotificationActivator::Activate function.
//
// Different from this test being compiled into
// notification_helper_unittests.exe, the advanced test is compiled into
// unit_tests.exe. This is because the advanced test requires data dependency on
// chrome.exe which unit_tests.exe already has, and it's undesired to make
// notification_helper_unittests.exe have data dependency on chrome.exe.

#include <memory>
#include <string>

#include <wrl/client.h>

#include "base/base_paths.h"
#include "base/files/file_path.h"
#include "base/memory/ptr_util.h"
#include "base/path_service.h"
#include "base/process/process.h"
#include "base/process/process_iterator.h"
#include "base/test/test_timeouts.h"
#include "base/win/scoped_com_initializer.h"
#include "base/win/windows_types.h"
#include "chrome/install_static/install_util.h"
#include "chrome/installer/setup/install_worker.h"
#include "chrome/installer/util/install_util.h"
#include "chrome/installer/util/util_constants.h"
#include "chrome/installer/util/work_item.h"
#include "chrome/installer/util/work_item_list.h"
#include "testing/gtest/include/gtest/gtest.h"

namespace {

// Returns the process with name |name| if it is found.
base::Process FindProcess(const std::wstring& name) {
  unsigned int pid;
  {
    base::NamedProcessIterator iter(name, nullptr);
    const auto* entry = iter.NextProcessEntry();
    if (!entry)
      return base::Process();
    pid = entry->pid();
  }

  auto process = base::Process::Open(pid);
  if (!process.IsValid())
    return process;

  // Since the process could go away suddenly before we open a handle to it,
  // it's possible that a different process was just opened and assigned the
  // same PID due to aggressive PID reuse. Now that a handle is held to *some*
  // process, take another run through the snapshot to see if the process with
  // this PID has the right exe name.
  base::NamedProcessIterator iter(name, nullptr);
  while (const auto* entry = iter.NextProcessEntry()) {
    if (entry->pid() == pid)
      return process;  // PID was not reused since the PID's match.
  }
  return base::Process();  // The PID was reused.
}

}  // namespace

class NotificationHelperTest : public testing::Test {
 public:
  NotificationHelperTest(const NotificationHelperTest&) = delete;
  NotificationHelperTest& operator=(const NotificationHelperTest&) = delete;

 protected:
  NotificationHelperTest() : root_(HKEY_CURRENT_USER) {}

  void SetUp() override {
    ASSERT_NO_FATAL_FAILURE(RegisterServer());
  }

  void TearDown() override {
    ASSERT_NO_FATAL_FAILURE(UnregisterServer());
  }

 private:
  // Registers notification_helper.exe as the server.
  void RegisterServer() {
    ASSERT_TRUE(scoped_com_initializer_.Succeeded());

    // Notification_helper.exe is in the build output directory next to this
    // test executable, as the test build target has a data_deps dependency on
    // it.
    base::FilePath dir_exe;
    ASSERT_TRUE(base::PathService::Get(base::DIR_EXE, &dir_exe));
    base::FilePath notification_helper_path =
        dir_exe.Append(installer::kNotificationHelperExe);

    work_item_list_ =
        base::WrapUnique<WorkItemList>(WorkItem::CreateWorkItemList());

    installer::AddNativeNotificationWorkItems(root_, notification_helper_path,
                                              work_item_list_.get());

    ASSERT_TRUE(work_item_list_->Do());
  }

  // Unregisters the server by rolling back the work item list.
  void UnregisterServer() {
    if (work_item_list_)
      work_item_list_->Rollback();
  }

  // Predefined handle to the registry.
  const HKEY root_;

  // A list of work items on the registry.
  std::unique_ptr<WorkItemList> work_item_list_;

  base::win::ScopedCOMInitializer scoped_com_initializer_;
};

TEST_F(NotificationHelperTest, NotificationHelperServerTest) {
  // There isn't a way to directly correlate the notification_helper.exe server
  // to this test. So we need to hunt for the server.

  // Make sure there is no notification_helper process running around.
  base::Process helper = FindProcess(installer::kNotificationHelperExe);
  ASSERT_FALSE(helper.IsValid());

  Microsoft::WRL::ComPtr<IUnknown> notification_activator;
  ASSERT_HRESULT_SUCCEEDED(::CoCreateInstance(
      install_static::GetToastActivatorClsid(), nullptr, CLSCTX_LOCAL_SERVER,
      IID_PPV_ARGS(&notification_activator)));
  ASSERT_TRUE(notification_activator);

  // The server is now invoked upon the request of creating the object instance.
  // The server module now holds a reference of the instance object, the
  // notification_helper.exe process is alive waiting for that reference to be
  // released.
  helper = FindProcess(installer::kNotificationHelperExe);
  ASSERT_TRUE(helper.IsValid());

  // Release the instance object. Now that the last (and the only) instance
  // object of the module is released, the event living in the server
  // process is signaled, which allows the server process to exit.
  notification_activator.Reset();
  ASSERT_TRUE(
      helper.WaitForExitWithTimeout(TestTimeouts::action_timeout(), nullptr));
}