xref: /aosp_15_r20/external/cronet/base/mac/launch_application_unittest.mm (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
1*6777b538SAndroid Build Coastguard Worker// Copyright 2023 The Chromium Authors
2*6777b538SAndroid Build Coastguard Worker// Use of this source code is governed by a BSD-style license that can be
3*6777b538SAndroid Build Coastguard Worker// found in the LICENSE file.
4*6777b538SAndroid Build Coastguard Worker
5*6777b538SAndroid Build Coastguard Worker#include "base/mac/launch_application.h"
6*6777b538SAndroid Build Coastguard Worker
7*6777b538SAndroid Build Coastguard Worker#include <sys/select.h>
8*6777b538SAndroid Build Coastguard Worker
9*6777b538SAndroid Build Coastguard Worker#include "base/apple/bridging.h"
10*6777b538SAndroid Build Coastguard Worker#include "base/apple/foundation_util.h"
11*6777b538SAndroid Build Coastguard Worker#include "base/base_paths.h"
12*6777b538SAndroid Build Coastguard Worker#include "base/files/file_path.h"
13*6777b538SAndroid Build Coastguard Worker#include "base/files/file_util.h"
14*6777b538SAndroid Build Coastguard Worker#include "base/files/scoped_temp_dir.h"
15*6777b538SAndroid Build Coastguard Worker#include "base/functional/callback_helpers.h"
16*6777b538SAndroid Build Coastguard Worker#include "base/logging.h"
17*6777b538SAndroid Build Coastguard Worker#include "base/mac/mac_util.h"
18*6777b538SAndroid Build Coastguard Worker#include "base/path_service.h"
19*6777b538SAndroid Build Coastguard Worker#include "base/process/launch.h"
20*6777b538SAndroid Build Coastguard Worker#include "base/strings/string_util.h"
21*6777b538SAndroid Build Coastguard Worker#include "base/strings/sys_string_conversions.h"
22*6777b538SAndroid Build Coastguard Worker#include "base/task/bind_post_task.h"
23*6777b538SAndroid Build Coastguard Worker#include "base/task/thread_pool.h"
24*6777b538SAndroid Build Coastguard Worker#include "base/test/bind.h"
25*6777b538SAndroid Build Coastguard Worker#include "base/test/task_environment.h"
26*6777b538SAndroid Build Coastguard Worker#include "base/test/test_future.h"
27*6777b538SAndroid Build Coastguard Worker#include "base/threading/platform_thread.h"
28*6777b538SAndroid Build Coastguard Worker#include "base/types/expected.h"
29*6777b538SAndroid Build Coastguard Worker#include "base/uuid.h"
30*6777b538SAndroid Build Coastguard Worker#include "testing/gmock/include/gmock/gmock.h"
31*6777b538SAndroid Build Coastguard Worker#include "testing/gtest/include/gtest/gtest.h"
32*6777b538SAndroid Build Coastguard Worker#import "testing/gtest_mac.h"
33*6777b538SAndroid Build Coastguard Worker
34*6777b538SAndroid Build Coastguard Workernamespace base::mac {
35*6777b538SAndroid Build Coastguard Workernamespace {
36*6777b538SAndroid Build Coastguard Worker
37*6777b538SAndroid Build Coastguard Worker// Reads XML encoded property lists from `fifo_path`, calling `callback` for
38*6777b538SAndroid Build Coastguard Worker// each succesfully parsed dictionary. Loops indefinitely until the string
39*6777b538SAndroid Build Coastguard Worker// "<!FINISHED>" is read from `fifo_path`.
40*6777b538SAndroid Build Coastguard Workervoid ReadLaunchEventsFromFifo(
41*6777b538SAndroid Build Coastguard Worker    const FilePath& fifo_path,
42*6777b538SAndroid Build Coastguard Worker    RepeatingCallback<void(NSDictionary* event)> callback) {
43*6777b538SAndroid Build Coastguard Worker  File f(fifo_path, File::FLAG_OPEN | File::FLAG_READ);
44*6777b538SAndroid Build Coastguard Worker  std::string data;
45*6777b538SAndroid Build Coastguard Worker  while (true) {
46*6777b538SAndroid Build Coastguard Worker    char buf[4096];
47*6777b538SAndroid Build Coastguard Worker    int read_count = f.ReadAtCurrentPosNoBestEffort(buf, sizeof buf);
48*6777b538SAndroid Build Coastguard Worker    if (read_count) {
49*6777b538SAndroid Build Coastguard Worker      data += std::string(buf, read_count);
50*6777b538SAndroid Build Coastguard Worker      // Assume that at any point the beginning of the data buffer is the start
51*6777b538SAndroid Build Coastguard Worker      // of a plist. Search for the first end, and parse that substring.
52*6777b538SAndroid Build Coastguard Worker      size_t end_of_plist;
53*6777b538SAndroid Build Coastguard Worker      while ((end_of_plist = data.find("</plist>")) != std::string::npos) {
54*6777b538SAndroid Build Coastguard Worker        std::string plist = data.substr(0, end_of_plist + 8);
55*6777b538SAndroid Build Coastguard Worker        data = data.substr(plist.length());
56*6777b538SAndroid Build Coastguard Worker        NSDictionary* event = apple::ObjCCastStrict<NSDictionary>(
57*6777b538SAndroid Build Coastguard Worker            SysUTF8ToNSString(TrimWhitespaceASCII(plist, TRIM_ALL))
58*6777b538SAndroid Build Coastguard Worker                .propertyList);
59*6777b538SAndroid Build Coastguard Worker        callback.Run(event);
60*6777b538SAndroid Build Coastguard Worker      }
61*6777b538SAndroid Build Coastguard Worker      // No more plists found, check if the termination marker was send.
62*6777b538SAndroid Build Coastguard Worker      if (data.find("<!FINISHED>") != std::string::npos) {
63*6777b538SAndroid Build Coastguard Worker        break;
64*6777b538SAndroid Build Coastguard Worker      }
65*6777b538SAndroid Build Coastguard Worker    } else {
66*6777b538SAndroid Build Coastguard Worker      // No data was read, wait for the file descriptor to become readable
67*6777b538SAndroid Build Coastguard Worker      // again.
68*6777b538SAndroid Build Coastguard Worker      fd_set fds;
69*6777b538SAndroid Build Coastguard Worker      FD_ZERO(&fds);
70*6777b538SAndroid Build Coastguard Worker      FD_SET(f.GetPlatformFile(), &fds);
71*6777b538SAndroid Build Coastguard Worker      select(FD_SETSIZE, &fds, nullptr, nullptr, nullptr);
72*6777b538SAndroid Build Coastguard Worker    }
73*6777b538SAndroid Build Coastguard Worker  }
74*6777b538SAndroid Build Coastguard Worker}
75*6777b538SAndroid Build Coastguard Worker
76*6777b538SAndroid Build Coastguard Worker// This test harness creates an app bundle with a random bundle identifier to
77*6777b538SAndroid Build Coastguard Worker// avoid conflicts with concurrently running other tests. The binary in this app
78*6777b538SAndroid Build Coastguard Worker// bundle writes various events to a named pipe, allowing tests here to verify
79*6777b538SAndroid Build Coastguard Worker// that correct events were received by the app.
80*6777b538SAndroid Build Coastguard Workerclass LaunchApplicationTest : public testing::Test {
81*6777b538SAndroid Build Coastguard Worker public:
82*6777b538SAndroid Build Coastguard Worker  void SetUp() override {
83*6777b538SAndroid Build Coastguard Worker    helper_bundle_id_ =
84*6777b538SAndroid Build Coastguard Worker        SysUTF8ToNSString("org.chromium.LaunchApplicationTestHelper." +
85*6777b538SAndroid Build Coastguard Worker                          Uuid::GenerateRandomV4().AsLowercaseString());
86*6777b538SAndroid Build Coastguard Worker
87*6777b538SAndroid Build Coastguard Worker    FilePath data_root;
88*6777b538SAndroid Build Coastguard Worker    ASSERT_TRUE(PathService::Get(DIR_OUT_TEST_DATA_ROOT, &data_root));
89*6777b538SAndroid Build Coastguard Worker    const FilePath helper_app_executable =
90*6777b538SAndroid Build Coastguard Worker        data_root.AppendASCII("launch_application_test_helper");
91*6777b538SAndroid Build Coastguard Worker
92*6777b538SAndroid Build Coastguard Worker    // Put helper app inside home dir, as the default temp location gets special
93*6777b538SAndroid Build Coastguard Worker    // treatment by launch services, effecting the behavior of some of these
94*6777b538SAndroid Build Coastguard Worker    // tests.
95*6777b538SAndroid Build Coastguard Worker    ASSERT_TRUE(temp_dir_.CreateUniqueTempDirUnderPath(base::GetHomeDir()));
96*6777b538SAndroid Build Coastguard Worker
97*6777b538SAndroid Build Coastguard Worker    helper_app_bundle_path_ =
98*6777b538SAndroid Build Coastguard Worker        temp_dir_.GetPath().AppendASCII("launch_application_test_helper.app");
99*6777b538SAndroid Build Coastguard Worker
100*6777b538SAndroid Build Coastguard Worker    const base::FilePath destination_contents_path =
101*6777b538SAndroid Build Coastguard Worker        helper_app_bundle_path_.AppendASCII("Contents");
102*6777b538SAndroid Build Coastguard Worker    const base::FilePath destination_executable_path =
103*6777b538SAndroid Build Coastguard Worker        destination_contents_path.AppendASCII("MacOS");
104*6777b538SAndroid Build Coastguard Worker
105*6777b538SAndroid Build Coastguard Worker    // First create the .app bundle directory structure.
106*6777b538SAndroid Build Coastguard Worker    // Use NSFileManager so that the permissions can be set appropriately. The
107*6777b538SAndroid Build Coastguard Worker    // base::CreateDirectory() routine forces mode 0700.
108*6777b538SAndroid Build Coastguard Worker    NSError* error = nil;
109*6777b538SAndroid Build Coastguard Worker    ASSERT_TRUE([NSFileManager.defaultManager
110*6777b538SAndroid Build Coastguard Worker               createDirectoryAtURL:base::apple::FilePathToNSURL(
111*6777b538SAndroid Build Coastguard Worker                                        destination_executable_path)
112*6777b538SAndroid Build Coastguard Worker        withIntermediateDirectories:YES
113*6777b538SAndroid Build Coastguard Worker                         attributes:@{
114*6777b538SAndroid Build Coastguard Worker                           NSFilePosixPermissions : @(0755)
115*6777b538SAndroid Build Coastguard Worker                         }
116*6777b538SAndroid Build Coastguard Worker                              error:&error])
117*6777b538SAndroid Build Coastguard Worker        << SysNSStringToUTF8(error.description);
118*6777b538SAndroid Build Coastguard Worker
119*6777b538SAndroid Build Coastguard Worker    // Copy the executable file.
120*6777b538SAndroid Build Coastguard Worker    helper_app_executable_path_ =
121*6777b538SAndroid Build Coastguard Worker        destination_executable_path.Append(helper_app_executable.BaseName());
122*6777b538SAndroid Build Coastguard Worker    ASSERT_TRUE(
123*6777b538SAndroid Build Coastguard Worker        base::CopyFile(helper_app_executable, helper_app_executable_path_));
124*6777b538SAndroid Build Coastguard Worker
125*6777b538SAndroid Build Coastguard Worker    // Write the PkgInfo file.
126*6777b538SAndroid Build Coastguard Worker    constexpr char kPkgInfoData[] = "APPL????";
127*6777b538SAndroid Build Coastguard Worker    ASSERT_TRUE(base::WriteFile(
128*6777b538SAndroid Build Coastguard Worker        destination_contents_path.AppendASCII("PkgInfo"), kPkgInfoData));
129*6777b538SAndroid Build Coastguard Worker
130*6777b538SAndroid Build Coastguard Worker#if defined(ADDRESS_SANITIZER)
131*6777b538SAndroid Build Coastguard Worker    const base::FilePath asan_library_path =
132*6777b538SAndroid Build Coastguard Worker        data_root.AppendASCII("libclang_rt.asan_osx_dynamic.dylib");
133*6777b538SAndroid Build Coastguard Worker    ASSERT_TRUE(base::CopyFile(
134*6777b538SAndroid Build Coastguard Worker        asan_library_path,
135*6777b538SAndroid Build Coastguard Worker        destination_executable_path.Append(asan_library_path.BaseName())));
136*6777b538SAndroid Build Coastguard Worker#endif
137*6777b538SAndroid Build Coastguard Worker
138*6777b538SAndroid Build Coastguard Worker    // Generate the Plist file
139*6777b538SAndroid Build Coastguard Worker    NSDictionary* plist = @{
140*6777b538SAndroid Build Coastguard Worker      @"CFBundleExecutable" :
141*6777b538SAndroid Build Coastguard Worker          apple::FilePathToNSString(helper_app_executable.BaseName()),
142*6777b538SAndroid Build Coastguard Worker      @"CFBundleIdentifier" : helper_bundle_id_,
143*6777b538SAndroid Build Coastguard Worker    };
144*6777b538SAndroid Build Coastguard Worker    ASSERT_TRUE([plist
145*6777b538SAndroid Build Coastguard Worker        writeToURL:apple::FilePathToNSURL(
146*6777b538SAndroid Build Coastguard Worker                       destination_contents_path.AppendASCII("Info.plist"))
147*6777b538SAndroid Build Coastguard Worker             error:nil]);
148*6777b538SAndroid Build Coastguard Worker
149*6777b538SAndroid Build Coastguard Worker    // Register the app with LaunchServices.
150*6777b538SAndroid Build Coastguard Worker    LSRegisterURL(base::apple::FilePathToCFURL(helper_app_bundle_path_).get(),
151*6777b538SAndroid Build Coastguard Worker                  true);
152*6777b538SAndroid Build Coastguard Worker
153*6777b538SAndroid Build Coastguard Worker    // Ensure app was registered with LaunchServices. Sometimes it takes a
154*6777b538SAndroid Build Coastguard Worker    // little bit of time for this to happen, and some tests might fail if the
155*6777b538SAndroid Build Coastguard Worker    // app wasn't registered yet.
156*6777b538SAndroid Build Coastguard Worker    while (true) {
157*6777b538SAndroid Build Coastguard Worker      NSArray<NSURL*>* apps = nil;
158*6777b538SAndroid Build Coastguard Worker      if (@available(macOS 12.0, *)) {
159*6777b538SAndroid Build Coastguard Worker        apps = [[NSWorkspace sharedWorkspace]
160*6777b538SAndroid Build Coastguard Worker            URLsForApplicationsWithBundleIdentifier:helper_bundle_id_];
161*6777b538SAndroid Build Coastguard Worker      } else {
162*6777b538SAndroid Build Coastguard Worker        apps =
163*6777b538SAndroid Build Coastguard Worker            apple::CFToNSOwnershipCast(LSCopyApplicationURLsForBundleIdentifier(
164*6777b538SAndroid Build Coastguard Worker                apple::NSToCFPtrCast(helper_bundle_id_), /*outError=*/nullptr));
165*6777b538SAndroid Build Coastguard Worker      }
166*6777b538SAndroid Build Coastguard Worker      if (apps.count > 0) {
167*6777b538SAndroid Build Coastguard Worker        break;
168*6777b538SAndroid Build Coastguard Worker      }
169*6777b538SAndroid Build Coastguard Worker      PlatformThread::Sleep(Milliseconds(50));
170*6777b538SAndroid Build Coastguard Worker    }
171*6777b538SAndroid Build Coastguard Worker
172*6777b538SAndroid Build Coastguard Worker    // Setup fifo to receive logs from the helper app.
173*6777b538SAndroid Build Coastguard Worker    helper_app_fifo_path_ =
174*6777b538SAndroid Build Coastguard Worker        temp_dir_.GetPath().AppendASCII("launch_application_test_helper.fifo");
175*6777b538SAndroid Build Coastguard Worker    ASSERT_EQ(0, mkfifo(helper_app_fifo_path_.value().c_str(),
176*6777b538SAndroid Build Coastguard Worker                        S_IWUSR | S_IRUSR | S_IWGRP | S_IWGRP));
177*6777b538SAndroid Build Coastguard Worker
178*6777b538SAndroid Build Coastguard Worker    // Create array to store received events in, and start listening for events.
179*6777b538SAndroid Build Coastguard Worker    launch_events_ = [[NSMutableArray alloc] init];
180*6777b538SAndroid Build Coastguard Worker    base::ThreadPool::PostTask(
181*6777b538SAndroid Build Coastguard Worker        FROM_HERE, {MayBlock()},
182*6777b538SAndroid Build Coastguard Worker        base::BindOnce(
183*6777b538SAndroid Build Coastguard Worker            &ReadLaunchEventsFromFifo, helper_app_fifo_path_,
184*6777b538SAndroid Build Coastguard Worker            BindPostTaskToCurrentDefault(BindRepeating(
185*6777b538SAndroid Build Coastguard Worker                &LaunchApplicationTest::OnLaunchEvent, Unretained(this)))));
186*6777b538SAndroid Build Coastguard Worker  }
187*6777b538SAndroid Build Coastguard Worker
188*6777b538SAndroid Build Coastguard Worker  void TearDown() override {
189*6777b538SAndroid Build Coastguard Worker    if (temp_dir_.IsValid()) {
190*6777b538SAndroid Build Coastguard Worker      // Make sure fifo reading task stops reading/waiting.
191*6777b538SAndroid Build Coastguard Worker      WriteFile(helper_app_fifo_path_, "<!FINISHED>");
192*6777b538SAndroid Build Coastguard Worker
193*6777b538SAndroid Build Coastguard Worker      // Make sure all apps that were launched by this test are terminated.
194*6777b538SAndroid Build Coastguard Worker      NSArray<NSRunningApplication*>* apps =
195*6777b538SAndroid Build Coastguard Worker          NSWorkspace.sharedWorkspace.runningApplications;
196*6777b538SAndroid Build Coastguard Worker      for (NSRunningApplication* app in apps) {
197*6777b538SAndroid Build Coastguard Worker        if (temp_dir_.GetPath().IsParent(
198*6777b538SAndroid Build Coastguard Worker                apple::NSURLToFilePath(app.bundleURL)) ||
199*6777b538SAndroid Build Coastguard Worker            [app.bundleIdentifier isEqualToString:helper_bundle_id_]) {
200*6777b538SAndroid Build Coastguard Worker          [app forceTerminate];
201*6777b538SAndroid Build Coastguard Worker        }
202*6777b538SAndroid Build Coastguard Worker      }
203*6777b538SAndroid Build Coastguard Worker
204*6777b538SAndroid Build Coastguard Worker      // And make sure the temp dir was successfully deleted.
205*6777b538SAndroid Build Coastguard Worker      EXPECT_TRUE(temp_dir_.Delete());
206*6777b538SAndroid Build Coastguard Worker    }
207*6777b538SAndroid Build Coastguard Worker  }
208*6777b538SAndroid Build Coastguard Worker
209*6777b538SAndroid Build Coastguard Worker  // Calls `LaunchApplication` with the given parameters, expecting the launch
210*6777b538SAndroid Build Coastguard Worker  // to succeed. Returns the `NSRunningApplication*` the callback passed to
211*6777b538SAndroid Build Coastguard Worker  // `LaunchApplication` was called with.
212*6777b538SAndroid Build Coastguard Worker  NSRunningApplication* LaunchApplicationSyncExpectSuccess(
213*6777b538SAndroid Build Coastguard Worker      const FilePath& app_bundle_path,
214*6777b538SAndroid Build Coastguard Worker      const CommandLineArgs& command_line_args,
215*6777b538SAndroid Build Coastguard Worker      const std::vector<std::string>& url_specs,
216*6777b538SAndroid Build Coastguard Worker      LaunchApplicationOptions options) {
217*6777b538SAndroid Build Coastguard Worker    test::TestFuture<NSRunningApplication*, NSError*> result;
218*6777b538SAndroid Build Coastguard Worker    LaunchApplication(app_bundle_path, command_line_args, url_specs, options,
219*6777b538SAndroid Build Coastguard Worker                      result.GetCallback());
220*6777b538SAndroid Build Coastguard Worker    EXPECT_FALSE(result.Get<1>());
221*6777b538SAndroid Build Coastguard Worker    EXPECT_TRUE(result.Get<0>());
222*6777b538SAndroid Build Coastguard Worker    return result.Get<0>();
223*6777b538SAndroid Build Coastguard Worker  }
224*6777b538SAndroid Build Coastguard Worker
225*6777b538SAndroid Build Coastguard Worker  // Similar to the above method, except that this version expects the launch to
226*6777b538SAndroid Build Coastguard Worker  // fail, returning the error.
227*6777b538SAndroid Build Coastguard Worker  NSError* LaunchApplicationSyncExpectError(
228*6777b538SAndroid Build Coastguard Worker      const FilePath& app_bundle_path,
229*6777b538SAndroid Build Coastguard Worker      const CommandLineArgs& command_line_args,
230*6777b538SAndroid Build Coastguard Worker      const std::vector<std::string>& url_specs,
231*6777b538SAndroid Build Coastguard Worker      LaunchApplicationOptions options) {
232*6777b538SAndroid Build Coastguard Worker    test::TestFuture<NSRunningApplication*, NSError*> result;
233*6777b538SAndroid Build Coastguard Worker    LaunchApplication(app_bundle_path, command_line_args, url_specs, options,
234*6777b538SAndroid Build Coastguard Worker                      result.GetCallback());
235*6777b538SAndroid Build Coastguard Worker    EXPECT_FALSE(result.Get<0>());
236*6777b538SAndroid Build Coastguard Worker    EXPECT_TRUE(result.Get<1>());
237*6777b538SAndroid Build Coastguard Worker    return result.Get<1>();
238*6777b538SAndroid Build Coastguard Worker  }
239*6777b538SAndroid Build Coastguard Worker
240*6777b538SAndroid Build Coastguard Worker  // Waits for the total number of received launch events to reach at least
241*6777b538SAndroid Build Coastguard Worker  // `expected_count`.
242*6777b538SAndroid Build Coastguard Worker  void WaitForLaunchEvents(unsigned expected_count) {
243*6777b538SAndroid Build Coastguard Worker    if (LaunchEventCount() >= expected_count) {
244*6777b538SAndroid Build Coastguard Worker      return;
245*6777b538SAndroid Build Coastguard Worker    }
246*6777b538SAndroid Build Coastguard Worker    base::RunLoop loop;
247*6777b538SAndroid Build Coastguard Worker    launch_event_callback_ = BindLambdaForTesting([&]() {
248*6777b538SAndroid Build Coastguard Worker      if (LaunchEventCount() >= expected_count) {
249*6777b538SAndroid Build Coastguard Worker        launch_event_callback_ = NullCallback();
250*6777b538SAndroid Build Coastguard Worker        loop.Quit();
251*6777b538SAndroid Build Coastguard Worker      }
252*6777b538SAndroid Build Coastguard Worker    });
253*6777b538SAndroid Build Coastguard Worker    loop.Run();
254*6777b538SAndroid Build Coastguard Worker  }
255*6777b538SAndroid Build Coastguard Worker
256*6777b538SAndroid Build Coastguard Worker  unsigned LaunchEventCount() { return launch_events_.count; }
257*6777b538SAndroid Build Coastguard Worker  NSString* LaunchEventName(unsigned i) {
258*6777b538SAndroid Build Coastguard Worker    if (i >= launch_events_.count) {
259*6777b538SAndroid Build Coastguard Worker      return nil;
260*6777b538SAndroid Build Coastguard Worker    }
261*6777b538SAndroid Build Coastguard Worker    return apple::ObjCCastStrict<NSString>(launch_events_[i][@"name"]);
262*6777b538SAndroid Build Coastguard Worker  }
263*6777b538SAndroid Build Coastguard Worker  NSDictionary* LaunchEventData(unsigned i) {
264*6777b538SAndroid Build Coastguard Worker    if (i >= launch_events_.count) {
265*6777b538SAndroid Build Coastguard Worker      return nil;
266*6777b538SAndroid Build Coastguard Worker    }
267*6777b538SAndroid Build Coastguard Worker    return apple::ObjCCastStrict<NSDictionary>(launch_events_[i][@"data"]);
268*6777b538SAndroid Build Coastguard Worker  }
269*6777b538SAndroid Build Coastguard Worker
270*6777b538SAndroid Build Coastguard Worker protected:
271*6777b538SAndroid Build Coastguard Worker  ScopedTempDir temp_dir_;
272*6777b538SAndroid Build Coastguard Worker
273*6777b538SAndroid Build Coastguard Worker  NSString* helper_bundle_id_;
274*6777b538SAndroid Build Coastguard Worker  FilePath helper_app_bundle_path_;
275*6777b538SAndroid Build Coastguard Worker  FilePath helper_app_executable_path_;
276*6777b538SAndroid Build Coastguard Worker  FilePath helper_app_fifo_path_;
277*6777b538SAndroid Build Coastguard Worker
278*6777b538SAndroid Build Coastguard Worker  NSMutableArray<NSDictionary*>* launch_events_;
279*6777b538SAndroid Build Coastguard Worker  RepeatingClosure launch_event_callback_;
280*6777b538SAndroid Build Coastguard Worker
281*6777b538SAndroid Build Coastguard Worker  test::TaskEnvironment task_environment_{
282*6777b538SAndroid Build Coastguard Worker      test::TaskEnvironment::MainThreadType::UI};
283*6777b538SAndroid Build Coastguard Worker
284*6777b538SAndroid Build Coastguard Worker private:
285*6777b538SAndroid Build Coastguard Worker  void OnLaunchEvent(NSDictionary* event) {
286*6777b538SAndroid Build Coastguard Worker    NSLog(@"Event: %@", event);
287*6777b538SAndroid Build Coastguard Worker    [launch_events_ addObject:event];
288*6777b538SAndroid Build Coastguard Worker    if (launch_event_callback_) {
289*6777b538SAndroid Build Coastguard Worker      launch_event_callback_.Run();
290*6777b538SAndroid Build Coastguard Worker    }
291*6777b538SAndroid Build Coastguard Worker  }
292*6777b538SAndroid Build Coastguard Worker};
293*6777b538SAndroid Build Coastguard Worker
294*6777b538SAndroid Build Coastguard WorkerTEST_F(LaunchApplicationTest, Basic) {
295*6777b538SAndroid Build Coastguard Worker  std::vector<std::string> command_line_args;
296*6777b538SAndroid Build Coastguard Worker  NSRunningApplication* app = LaunchApplicationSyncExpectSuccess(
297*6777b538SAndroid Build Coastguard Worker      helper_app_bundle_path_, command_line_args, {}, {});
298*6777b538SAndroid Build Coastguard Worker  ASSERT_TRUE(app);
299*6777b538SAndroid Build Coastguard Worker  EXPECT_NSEQ(app.bundleIdentifier, helper_bundle_id_);
300*6777b538SAndroid Build Coastguard Worker  EXPECT_EQ(apple::NSURLToFilePath(app.bundleURL), helper_app_bundle_path_);
301*6777b538SAndroid Build Coastguard Worker
302*6777b538SAndroid Build Coastguard Worker  WaitForLaunchEvents(1);
303*6777b538SAndroid Build Coastguard Worker  EXPECT_NSEQ(LaunchEventName(0), @"applicationDidFinishLaunching");
304*6777b538SAndroid Build Coastguard Worker  EXPECT_NSEQ(LaunchEventData(0)[@"activationPolicy"],
305*6777b538SAndroid Build Coastguard Worker              @(NSApplicationActivationPolicyRegular));
306*6777b538SAndroid Build Coastguard Worker  EXPECT_EQ(app.activationPolicy, NSApplicationActivationPolicyRegular);
307*6777b538SAndroid Build Coastguard Worker  EXPECT_NSEQ(LaunchEventData(0)[@"commandLine"],
308*6777b538SAndroid Build Coastguard Worker              (@[ apple::FilePathToNSString(helper_app_executable_path_) ]));
309*6777b538SAndroid Build Coastguard Worker  EXPECT_NSEQ(LaunchEventData(0)[@"processIdentifier"],
310*6777b538SAndroid Build Coastguard Worker              @(app.processIdentifier));
311*6777b538SAndroid Build Coastguard Worker}
312*6777b538SAndroid Build Coastguard Worker
313*6777b538SAndroid Build Coastguard WorkerTEST_F(LaunchApplicationTest, BundleDoesntExist) {
314*6777b538SAndroid Build Coastguard Worker  std::vector<std::string> command_line_args;
315*6777b538SAndroid Build Coastguard Worker  NSError* err = LaunchApplicationSyncExpectError(
316*6777b538SAndroid Build Coastguard Worker      temp_dir_.GetPath().AppendASCII("notexists.app"), command_line_args, {},
317*6777b538SAndroid Build Coastguard Worker      {});
318*6777b538SAndroid Build Coastguard Worker  ASSERT_TRUE(err);
319*6777b538SAndroid Build Coastguard Worker  err = LaunchApplicationSyncExpectError(
320*6777b538SAndroid Build Coastguard Worker      temp_dir_.GetPath().AppendASCII("notexists.app"), command_line_args, {},
321*6777b538SAndroid Build Coastguard Worker      {.hidden_in_background = true});
322*6777b538SAndroid Build Coastguard Worker  ASSERT_TRUE(err);
323*6777b538SAndroid Build Coastguard Worker}
324*6777b538SAndroid Build Coastguard Worker
325*6777b538SAndroid Build Coastguard WorkerTEST_F(LaunchApplicationTest, BundleCorrupt) {
326*6777b538SAndroid Build Coastguard Worker  base::DeleteFile(helper_app_executable_path_);
327*6777b538SAndroid Build Coastguard Worker  std::vector<std::string> command_line_args;
328*6777b538SAndroid Build Coastguard Worker  NSError* err = LaunchApplicationSyncExpectError(helper_app_bundle_path_,
329*6777b538SAndroid Build Coastguard Worker                                                  command_line_args, {}, {});
330*6777b538SAndroid Build Coastguard Worker  ASSERT_TRUE(err);
331*6777b538SAndroid Build Coastguard Worker  err = LaunchApplicationSyncExpectError(helper_app_bundle_path_,
332*6777b538SAndroid Build Coastguard Worker                                         command_line_args, {},
333*6777b538SAndroid Build Coastguard Worker                                         {.hidden_in_background = true});
334*6777b538SAndroid Build Coastguard Worker  ASSERT_TRUE(err);
335*6777b538SAndroid Build Coastguard Worker}
336*6777b538SAndroid Build Coastguard Worker
337*6777b538SAndroid Build Coastguard WorkerTEST_F(LaunchApplicationTest, CommandLineArgs_StringVector) {
338*6777b538SAndroid Build Coastguard Worker  std::vector<std::string> command_line_args = {"--foo", "bar", "-v"};
339*6777b538SAndroid Build Coastguard Worker  NSRunningApplication* app = LaunchApplicationSyncExpectSuccess(
340*6777b538SAndroid Build Coastguard Worker      helper_app_bundle_path_, command_line_args, {}, {});
341*6777b538SAndroid Build Coastguard Worker  ASSERT_TRUE(app);
342*6777b538SAndroid Build Coastguard Worker
343*6777b538SAndroid Build Coastguard Worker  WaitForLaunchEvents(1);
344*6777b538SAndroid Build Coastguard Worker  EXPECT_NSEQ(LaunchEventName(0), @"applicationDidFinishLaunching");
345*6777b538SAndroid Build Coastguard Worker  EXPECT_NSEQ(LaunchEventData(0)[@"commandLine"], (@[
346*6777b538SAndroid Build Coastguard Worker                apple::FilePathToNSString(helper_app_executable_path_),
347*6777b538SAndroid Build Coastguard Worker                @"--foo", @"bar", @"-v"
348*6777b538SAndroid Build Coastguard Worker              ]));
349*6777b538SAndroid Build Coastguard Worker}
350*6777b538SAndroid Build Coastguard Worker
351*6777b538SAndroid Build Coastguard WorkerTEST_F(LaunchApplicationTest, CommandLineArgs_BaseCommandLine) {
352*6777b538SAndroid Build Coastguard Worker  CommandLine command_line(CommandLine::NO_PROGRAM);
353*6777b538SAndroid Build Coastguard Worker  command_line.AppendSwitchASCII("foo", "bar");
354*6777b538SAndroid Build Coastguard Worker  command_line.AppendSwitch("v");
355*6777b538SAndroid Build Coastguard Worker  command_line.AppendSwitchPath("path", FilePath("/tmp"));
356*6777b538SAndroid Build Coastguard Worker
357*6777b538SAndroid Build Coastguard Worker  NSRunningApplication* app = LaunchApplicationSyncExpectSuccess(
358*6777b538SAndroid Build Coastguard Worker      helper_app_bundle_path_, command_line, {}, {});
359*6777b538SAndroid Build Coastguard Worker  ASSERT_TRUE(app);
360*6777b538SAndroid Build Coastguard Worker
361*6777b538SAndroid Build Coastguard Worker  WaitForLaunchEvents(1);
362*6777b538SAndroid Build Coastguard Worker  EXPECT_NSEQ(LaunchEventName(0), @"applicationDidFinishLaunching");
363*6777b538SAndroid Build Coastguard Worker  EXPECT_NSEQ(LaunchEventData(0)[@"commandLine"], (@[
364*6777b538SAndroid Build Coastguard Worker                apple::FilePathToNSString(helper_app_executable_path_),
365*6777b538SAndroid Build Coastguard Worker                @"--foo=bar", @"--v", @"--path=/tmp"
366*6777b538SAndroid Build Coastguard Worker              ]));
367*6777b538SAndroid Build Coastguard Worker}
368*6777b538SAndroid Build Coastguard Worker
369*6777b538SAndroid Build Coastguard WorkerTEST_F(LaunchApplicationTest, UrlSpecs) {
370*6777b538SAndroid Build Coastguard Worker  std::vector<std::string> command_line_args;
371*6777b538SAndroid Build Coastguard Worker  std::vector<std::string> urls = {"https://example.com",
372*6777b538SAndroid Build Coastguard Worker                                   "x-chrome-launch://1"};
373*6777b538SAndroid Build Coastguard Worker  NSRunningApplication* app = LaunchApplicationSyncExpectSuccess(
374*6777b538SAndroid Build Coastguard Worker      helper_app_bundle_path_, command_line_args, urls, {});
375*6777b538SAndroid Build Coastguard Worker  ASSERT_TRUE(app);
376*6777b538SAndroid Build Coastguard Worker  WaitForLaunchEvents(3);
377*6777b538SAndroid Build Coastguard Worker
378*6777b538SAndroid Build Coastguard Worker  EXPECT_NSEQ(LaunchEventName(0), @"openURLs");
379*6777b538SAndroid Build Coastguard Worker  EXPECT_NSEQ(LaunchEventName(1), @"applicationDidFinishLaunching");
380*6777b538SAndroid Build Coastguard Worker  EXPECT_NSEQ(LaunchEventName(2), @"openURLs");
381*6777b538SAndroid Build Coastguard Worker
382*6777b538SAndroid Build Coastguard Worker  if (MacOSMajorVersion() == 11) {
383*6777b538SAndroid Build Coastguard Worker    // macOS 11 (and only macOS 11) appears to sometimes trigger the openURLs
384*6777b538SAndroid Build Coastguard Worker    // calls in reverse order.
385*6777b538SAndroid Build Coastguard Worker    std::vector<std::string> received_urls;
386*6777b538SAndroid Build Coastguard Worker    for (NSString* url in apple::ObjCCastStrict<NSArray>(
387*6777b538SAndroid Build Coastguard Worker             LaunchEventData(0)[@"urls"])) {
388*6777b538SAndroid Build Coastguard Worker      received_urls.push_back(SysNSStringToUTF8(url));
389*6777b538SAndroid Build Coastguard Worker    }
390*6777b538SAndroid Build Coastguard Worker    EXPECT_EQ(received_urls.size(), 1u);
391*6777b538SAndroid Build Coastguard Worker    for (NSString* url in apple::ObjCCastStrict<NSArray>(
392*6777b538SAndroid Build Coastguard Worker             LaunchEventData(2)[@"urls"])) {
393*6777b538SAndroid Build Coastguard Worker      received_urls.push_back(SysNSStringToUTF8(url));
394*6777b538SAndroid Build Coastguard Worker    }
395*6777b538SAndroid Build Coastguard Worker    EXPECT_THAT(received_urls, testing::UnorderedElementsAreArray(urls));
396*6777b538SAndroid Build Coastguard Worker  } else {
397*6777b538SAndroid Build Coastguard Worker    EXPECT_NSEQ(LaunchEventData(0)[@"urls"], @[ @"https://example.com" ]);
398*6777b538SAndroid Build Coastguard Worker    EXPECT_NSEQ(LaunchEventData(2)[@"urls"], @[ @"x-chrome-launch://1" ]);
399*6777b538SAndroid Build Coastguard Worker  }
400*6777b538SAndroid Build Coastguard Worker}
401*6777b538SAndroid Build Coastguard Worker
402*6777b538SAndroid Build Coastguard WorkerTEST_F(LaunchApplicationTest, CreateNewInstance) {
403*6777b538SAndroid Build Coastguard Worker  std::vector<std::string> command_line_args;
404*6777b538SAndroid Build Coastguard Worker  NSRunningApplication* app1 = LaunchApplicationSyncExpectSuccess(
405*6777b538SAndroid Build Coastguard Worker      helper_app_bundle_path_, command_line_args, {},
406*6777b538SAndroid Build Coastguard Worker      {.create_new_instance = false});
407*6777b538SAndroid Build Coastguard Worker  WaitForLaunchEvents(1);
408*6777b538SAndroid Build Coastguard Worker  EXPECT_NSEQ(LaunchEventName(0), @"applicationDidFinishLaunching");
409*6777b538SAndroid Build Coastguard Worker  EXPECT_NSEQ(LaunchEventData(0)[@"processIdentifier"],
410*6777b538SAndroid Build Coastguard Worker              @(app1.processIdentifier));
411*6777b538SAndroid Build Coastguard Worker
412*6777b538SAndroid Build Coastguard Worker  NSRunningApplication* app2 = LaunchApplicationSyncExpectSuccess(
413*6777b538SAndroid Build Coastguard Worker      helper_app_bundle_path_, command_line_args, {"x-chrome-launch://0"},
414*6777b538SAndroid Build Coastguard Worker      {.create_new_instance = false});
415*6777b538SAndroid Build Coastguard Worker  EXPECT_NSEQ(app1, app2);
416*6777b538SAndroid Build Coastguard Worker  EXPECT_EQ(app1.processIdentifier, app2.processIdentifier);
417*6777b538SAndroid Build Coastguard Worker  WaitForLaunchEvents(2);
418*6777b538SAndroid Build Coastguard Worker  EXPECT_NSEQ(LaunchEventName(1), @"openURLs");
419*6777b538SAndroid Build Coastguard Worker  EXPECT_NSEQ(LaunchEventData(1)[@"processIdentifier"],
420*6777b538SAndroid Build Coastguard Worker              @(app2.processIdentifier));
421*6777b538SAndroid Build Coastguard Worker
422*6777b538SAndroid Build Coastguard Worker  NSRunningApplication* app3 = LaunchApplicationSyncExpectSuccess(
423*6777b538SAndroid Build Coastguard Worker      helper_app_bundle_path_, command_line_args, {"x-chrome-launch://1"},
424*6777b538SAndroid Build Coastguard Worker      {.create_new_instance = true});
425*6777b538SAndroid Build Coastguard Worker  EXPECT_NSNE(app1, app3);
426*6777b538SAndroid Build Coastguard Worker  EXPECT_NE(app1.processIdentifier, app3.processIdentifier);
427*6777b538SAndroid Build Coastguard Worker  WaitForLaunchEvents(4);
428*6777b538SAndroid Build Coastguard Worker  EXPECT_NSEQ(LaunchEventName(2), @"openURLs");
429*6777b538SAndroid Build Coastguard Worker  EXPECT_NSEQ(LaunchEventName(3), @"applicationDidFinishLaunching");
430*6777b538SAndroid Build Coastguard Worker  EXPECT_NSEQ(LaunchEventData(3)[@"processIdentifier"],
431*6777b538SAndroid Build Coastguard Worker              @(app3.processIdentifier));
432*6777b538SAndroid Build Coastguard Worker}
433*6777b538SAndroid Build Coastguard Worker
434*6777b538SAndroid Build Coastguard WorkerTEST_F(LaunchApplicationTest, HiddenInBackground) {
435*6777b538SAndroid Build Coastguard Worker  std::vector<std::string> command_line_args = {"--test", "--foo"};
436*6777b538SAndroid Build Coastguard Worker  NSRunningApplication* app = LaunchApplicationSyncExpectSuccess(
437*6777b538SAndroid Build Coastguard Worker      helper_app_bundle_path_, command_line_args, {},
438*6777b538SAndroid Build Coastguard Worker      {.hidden_in_background = true});
439*6777b538SAndroid Build Coastguard Worker  ASSERT_TRUE(app);
440*6777b538SAndroid Build Coastguard Worker  EXPECT_NSEQ(app.bundleIdentifier, helper_bundle_id_);
441*6777b538SAndroid Build Coastguard Worker  EXPECT_EQ(helper_app_bundle_path_, apple::NSURLToFilePath(app.bundleURL));
442*6777b538SAndroid Build Coastguard Worker
443*6777b538SAndroid Build Coastguard Worker  WaitForLaunchEvents(1);
444*6777b538SAndroid Build Coastguard Worker  EXPECT_NSEQ(LaunchEventName(0), @"applicationDidFinishLaunching");
445*6777b538SAndroid Build Coastguard Worker  EXPECT_NSEQ(LaunchEventData(0)[@"activationPolicy"],
446*6777b538SAndroid Build Coastguard Worker              @(NSApplicationActivationPolicyProhibited));
447*6777b538SAndroid Build Coastguard Worker  EXPECT_EQ(app.activationPolicy, NSApplicationActivationPolicyProhibited);
448*6777b538SAndroid Build Coastguard Worker  EXPECT_NSEQ(LaunchEventData(0)[@"commandLine"], (@[
449*6777b538SAndroid Build Coastguard Worker                apple::FilePathToNSString(helper_app_executable_path_),
450*6777b538SAndroid Build Coastguard Worker                @"--test", @"--foo"
451*6777b538SAndroid Build Coastguard Worker              ]));
452*6777b538SAndroid Build Coastguard Worker  EXPECT_NSEQ(LaunchEventData(0)[@"processIdentifier"],
453*6777b538SAndroid Build Coastguard Worker              @(app.processIdentifier));
454*6777b538SAndroid Build Coastguard Worker
455*6777b538SAndroid Build Coastguard Worker  NSRunningApplication* app2 = LaunchApplicationSyncExpectSuccess(
456*6777b538SAndroid Build Coastguard Worker      helper_app_bundle_path_, command_line_args, {},
457*6777b538SAndroid Build Coastguard Worker      {.create_new_instance = false, .hidden_in_background = true});
458*6777b538SAndroid Build Coastguard Worker  EXPECT_NSEQ(app, app2);
459*6777b538SAndroid Build Coastguard Worker  EXPECT_EQ(app.processIdentifier, app2.processIdentifier);
460*6777b538SAndroid Build Coastguard Worker  EXPECT_EQ(app.activationPolicy, NSApplicationActivationPolicyProhibited);
461*6777b538SAndroid Build Coastguard Worker  EXPECT_EQ(app2.activationPolicy, NSApplicationActivationPolicyProhibited);
462*6777b538SAndroid Build Coastguard Worker  // Launching without opening anything should not trigger any launch events.
463*6777b538SAndroid Build Coastguard Worker
464*6777b538SAndroid Build Coastguard Worker  // Opening a URL in a new instance, should leave both instances in the
465*6777b538SAndroid Build Coastguard Worker  // background.
466*6777b538SAndroid Build Coastguard Worker  NSRunningApplication* app3 = LaunchApplicationSyncExpectSuccess(
467*6777b538SAndroid Build Coastguard Worker      helper_app_bundle_path_, command_line_args, {"x-chrome-launch://2"},
468*6777b538SAndroid Build Coastguard Worker      {.create_new_instance = true, .hidden_in_background = true});
469*6777b538SAndroid Build Coastguard Worker  EXPECT_NSNE(app, app3);
470*6777b538SAndroid Build Coastguard Worker  EXPECT_NE(app.processIdentifier, app3.processIdentifier);
471*6777b538SAndroid Build Coastguard Worker  WaitForLaunchEvents(3);
472*6777b538SAndroid Build Coastguard Worker  EXPECT_NSEQ(LaunchEventName(1), @"openURLs");
473*6777b538SAndroid Build Coastguard Worker  EXPECT_NSEQ(LaunchEventName(2), @"applicationDidFinishLaunching");
474*6777b538SAndroid Build Coastguard Worker  EXPECT_NSEQ(LaunchEventData(2)[@"processIdentifier"],
475*6777b538SAndroid Build Coastguard Worker              @(app3.processIdentifier));
476*6777b538SAndroid Build Coastguard Worker  EXPECT_EQ(app.activationPolicy, NSApplicationActivationPolicyProhibited);
477*6777b538SAndroid Build Coastguard Worker  EXPECT_EQ(app2.activationPolicy, NSApplicationActivationPolicyProhibited);
478*6777b538SAndroid Build Coastguard Worker  EXPECT_EQ(app3.activationPolicy, NSApplicationActivationPolicyProhibited);
479*6777b538SAndroid Build Coastguard Worker}
480*6777b538SAndroid Build Coastguard Worker
481*6777b538SAndroid Build Coastguard WorkerTEST_F(LaunchApplicationTest,
482*6777b538SAndroid Build Coastguard Worker       HiddenInBackground_OpenUrlChangesActivationPolicy) {
483*6777b538SAndroid Build Coastguard Worker  std::vector<std::string> command_line_args = {"--test", "--foo"};
484*6777b538SAndroid Build Coastguard Worker  NSRunningApplication* app = LaunchApplicationSyncExpectSuccess(
485*6777b538SAndroid Build Coastguard Worker      helper_app_bundle_path_, command_line_args, {},
486*6777b538SAndroid Build Coastguard Worker      {.hidden_in_background = true});
487*6777b538SAndroid Build Coastguard Worker  ASSERT_TRUE(app);
488*6777b538SAndroid Build Coastguard Worker  EXPECT_NSEQ(app.bundleIdentifier, helper_bundle_id_);
489*6777b538SAndroid Build Coastguard Worker  EXPECT_EQ(helper_app_bundle_path_, apple::NSURLToFilePath(app.bundleURL));
490*6777b538SAndroid Build Coastguard Worker
491*6777b538SAndroid Build Coastguard Worker  WaitForLaunchEvents(1);
492*6777b538SAndroid Build Coastguard Worker  EXPECT_NSEQ(LaunchEventName(0), @"applicationDidFinishLaunching");
493*6777b538SAndroid Build Coastguard Worker  EXPECT_NSEQ(LaunchEventData(0)[@"activationPolicy"],
494*6777b538SAndroid Build Coastguard Worker              @(NSApplicationActivationPolicyProhibited));
495*6777b538SAndroid Build Coastguard Worker  EXPECT_EQ(app.activationPolicy, NSApplicationActivationPolicyProhibited);
496*6777b538SAndroid Build Coastguard Worker  EXPECT_NSEQ(LaunchEventData(0)[@"commandLine"], (@[
497*6777b538SAndroid Build Coastguard Worker                apple::FilePathToNSString(helper_app_executable_path_),
498*6777b538SAndroid Build Coastguard Worker                @"--test", @"--foo"
499*6777b538SAndroid Build Coastguard Worker              ]));
500*6777b538SAndroid Build Coastguard Worker  EXPECT_NSEQ(LaunchEventData(0)[@"processIdentifier"],
501*6777b538SAndroid Build Coastguard Worker              @(app.processIdentifier));
502*6777b538SAndroid Build Coastguard Worker
503*6777b538SAndroid Build Coastguard Worker  NSRunningApplication* app2 = LaunchApplicationSyncExpectSuccess(
504*6777b538SAndroid Build Coastguard Worker      helper_app_bundle_path_, command_line_args, {"chrome://app-launch/0"},
505*6777b538SAndroid Build Coastguard Worker      {.create_new_instance = false, .hidden_in_background = true});
506*6777b538SAndroid Build Coastguard Worker  EXPECT_NSEQ(app, app2);
507*6777b538SAndroid Build Coastguard Worker  EXPECT_EQ(app.processIdentifier, app2.processIdentifier);
508*6777b538SAndroid Build Coastguard Worker  // Unexpected to me, but opening a URL seems to always change the activation
509*6777b538SAndroid Build Coastguard Worker  // policy.
510*6777b538SAndroid Build Coastguard Worker  EXPECT_EQ(app.activationPolicy, NSApplicationActivationPolicyRegular);
511*6777b538SAndroid Build Coastguard Worker  EXPECT_EQ(app2.activationPolicy, NSApplicationActivationPolicyRegular);
512*6777b538SAndroid Build Coastguard Worker  WaitForLaunchEvents(3);
513*6777b538SAndroid Build Coastguard Worker  EXPECT_THAT(
514*6777b538SAndroid Build Coastguard Worker      std::vector<std::string>({SysNSStringToUTF8(LaunchEventName(1)),
515*6777b538SAndroid Build Coastguard Worker                                SysNSStringToUTF8(LaunchEventName(2))}),
516*6777b538SAndroid Build Coastguard Worker      testing::UnorderedElementsAre("activationPolicyChanged", "openURLs"));
517*6777b538SAndroid Build Coastguard Worker}
518*6777b538SAndroid Build Coastguard Worker
519*6777b538SAndroid Build Coastguard WorkerTEST_F(LaunchApplicationTest, HiddenInBackground_TransitionToForeground) {
520*6777b538SAndroid Build Coastguard Worker  std::vector<std::string> command_line_args;
521*6777b538SAndroid Build Coastguard Worker  NSRunningApplication* app = LaunchApplicationSyncExpectSuccess(
522*6777b538SAndroid Build Coastguard Worker      helper_app_bundle_path_, command_line_args, {"x-chrome-launch://1"},
523*6777b538SAndroid Build Coastguard Worker      {.hidden_in_background = true});
524*6777b538SAndroid Build Coastguard Worker  ASSERT_TRUE(app);
525*6777b538SAndroid Build Coastguard Worker
526*6777b538SAndroid Build Coastguard Worker  WaitForLaunchEvents(2);
527*6777b538SAndroid Build Coastguard Worker  EXPECT_NSEQ(LaunchEventName(0), @"openURLs");
528*6777b538SAndroid Build Coastguard Worker  EXPECT_NSEQ(LaunchEventName(1), @"applicationDidFinishLaunching");
529*6777b538SAndroid Build Coastguard Worker  EXPECT_NSEQ(LaunchEventData(1)[@"activationPolicy"],
530*6777b538SAndroid Build Coastguard Worker              @(NSApplicationActivationPolicyProhibited));
531*6777b538SAndroid Build Coastguard Worker  EXPECT_EQ(app.activationPolicy, NSApplicationActivationPolicyProhibited);
532*6777b538SAndroid Build Coastguard Worker  EXPECT_NSEQ(LaunchEventData(1)[@"processIdentifier"],
533*6777b538SAndroid Build Coastguard Worker              @(app.processIdentifier));
534*6777b538SAndroid Build Coastguard Worker
535*6777b538SAndroid Build Coastguard Worker  // Second launch with hidden_in_background set to false should cause the first
536*6777b538SAndroid Build Coastguard Worker  // app to switch activation policy.
537*6777b538SAndroid Build Coastguard Worker  NSRunningApplication* app2 = LaunchApplicationSyncExpectSuccess(
538*6777b538SAndroid Build Coastguard Worker      helper_app_bundle_path_, command_line_args, {},
539*6777b538SAndroid Build Coastguard Worker      {.hidden_in_background = false});
540*6777b538SAndroid Build Coastguard Worker  EXPECT_NSEQ(app, app2);
541*6777b538SAndroid Build Coastguard Worker  WaitForLaunchEvents(3);
542*6777b538SAndroid Build Coastguard Worker  EXPECT_NSEQ(LaunchEventName(2), @"activationPolicyChanged");
543*6777b538SAndroid Build Coastguard Worker  EXPECT_NSEQ(LaunchEventData(2)[@"activationPolicy"],
544*6777b538SAndroid Build Coastguard Worker              @(NSApplicationActivationPolicyRegular));
545*6777b538SAndroid Build Coastguard Worker  EXPECT_EQ(app2.activationPolicy, NSApplicationActivationPolicyRegular);
546*6777b538SAndroid Build Coastguard Worker}
547*6777b538SAndroid Build Coastguard Worker
548*6777b538SAndroid Build Coastguard WorkerTEST_F(LaunchApplicationTest, HiddenInBackground_AlreadyInForeground) {
549*6777b538SAndroid Build Coastguard Worker  std::vector<std::string> command_line_args;
550*6777b538SAndroid Build Coastguard Worker  NSRunningApplication* app = LaunchApplicationSyncExpectSuccess(
551*6777b538SAndroid Build Coastguard Worker      helper_app_bundle_path_, command_line_args, {"x-chrome-launch://1"},
552*6777b538SAndroid Build Coastguard Worker      {.hidden_in_background = false});
553*6777b538SAndroid Build Coastguard Worker  ASSERT_TRUE(app);
554*6777b538SAndroid Build Coastguard Worker
555*6777b538SAndroid Build Coastguard Worker  WaitForLaunchEvents(2);
556*6777b538SAndroid Build Coastguard Worker  EXPECT_NSEQ(LaunchEventName(0), @"openURLs");
557*6777b538SAndroid Build Coastguard Worker  EXPECT_NSEQ(LaunchEventName(1), @"applicationDidFinishLaunching");
558*6777b538SAndroid Build Coastguard Worker  EXPECT_NSEQ(LaunchEventData(1)[@"activationPolicy"],
559*6777b538SAndroid Build Coastguard Worker              @(NSApplicationActivationPolicyRegular));
560*6777b538SAndroid Build Coastguard Worker  EXPECT_EQ(app.activationPolicy, NSApplicationActivationPolicyRegular);
561*6777b538SAndroid Build Coastguard Worker  EXPECT_NSEQ(LaunchEventData(1)[@"processIdentifier"],
562*6777b538SAndroid Build Coastguard Worker              @(app.processIdentifier));
563*6777b538SAndroid Build Coastguard Worker
564*6777b538SAndroid Build Coastguard Worker  // Second (and third) launch with hidden_in_background set to true should
565*6777b538SAndroid Build Coastguard Worker  // reuse the existing app and keep it visible.
566*6777b538SAndroid Build Coastguard Worker  NSRunningApplication* app2 = LaunchApplicationSyncExpectSuccess(
567*6777b538SAndroid Build Coastguard Worker      helper_app_bundle_path_, command_line_args, {},
568*6777b538SAndroid Build Coastguard Worker      {.hidden_in_background = true});
569*6777b538SAndroid Build Coastguard Worker  EXPECT_NSEQ(app, app2);
570*6777b538SAndroid Build Coastguard Worker  EXPECT_EQ(app2.activationPolicy, NSApplicationActivationPolicyRegular);
571*6777b538SAndroid Build Coastguard Worker  NSRunningApplication* app3 = LaunchApplicationSyncExpectSuccess(
572*6777b538SAndroid Build Coastguard Worker      helper_app_bundle_path_, command_line_args, {"x-chrome-launch://23"},
573*6777b538SAndroid Build Coastguard Worker      {.hidden_in_background = true});
574*6777b538SAndroid Build Coastguard Worker  EXPECT_NSEQ(app, app3);
575*6777b538SAndroid Build Coastguard Worker  WaitForLaunchEvents(3);
576*6777b538SAndroid Build Coastguard Worker  EXPECT_NSEQ(LaunchEventName(2), @"openURLs");
577*6777b538SAndroid Build Coastguard Worker  EXPECT_NSEQ(LaunchEventData(2)[@"processIdentifier"],
578*6777b538SAndroid Build Coastguard Worker              @(app.processIdentifier));
579*6777b538SAndroid Build Coastguard Worker  EXPECT_EQ(app3.activationPolicy, NSApplicationActivationPolicyRegular);
580*6777b538SAndroid Build Coastguard Worker}
581*6777b538SAndroid Build Coastguard Worker
582*6777b538SAndroid Build Coastguard Worker}  // namespace
583*6777b538SAndroid Build Coastguard Worker}  // namespace base::mac
584