Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(177)

Side by Side Diff: apps/app_shim/app_shim_interactive_uitest_mac.mm

Issue 316493002: [Mac] Add interactive App Shim test. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Address comments Created 6 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #import <Cocoa/Cocoa.h>
6 #include <vector>
7
8 #include "apps/app_shim/app_shim_host_manager_mac.h"
9 #include "apps/app_shim/extension_app_shim_handler_mac.h"
10 #include "apps/switches.h"
11 #include "apps/ui/native_app_window.h"
12 #include "base/auto_reset.h"
13 #include "base/callback.h"
14 #include "base/files/file_path_watcher.h"
15 #include "base/mac/foundation_util.h"
16 #include "base/mac/launch_services_util.h"
17 #include "base/mac/scoped_nsobject.h"
18 #include "base/path_service.h"
19 #include "base/process/launch.h"
20 #include "base/strings/sys_string_conversions.h"
21 #include "base/test/test_timeouts.h"
22 #include "chrome/browser/apps/app_browsertest_util.h"
23 #include "chrome/browser/browser_process.h"
24 #include "chrome/browser/extensions/extension_test_message_listener.h"
25 #include "chrome/browser/profiles/profile.h"
26 #include "chrome/browser/web_applications/web_app_mac.h"
27 #include "chrome/common/chrome_paths.h"
28 #include "chrome/common/chrome_switches.h"
29 #include "chrome/common/mac/app_mode_common.h"
30 #include "content/public/test/test_utils.h"
31 #include "extensions/browser/extension_registry.h"
32 #import "ui/events/test/cocoa_test_event_utils.h"
33
34 namespace {
35
36 // General end-to-end test for app shims.
37 class AppShimInteractiveTest : public extensions::PlatformAppBrowserTest {
38 protected:
39 AppShimInteractiveTest()
40 : auto_reset_(&g_app_shims_allow_update_and_launch_in_tests, true) {}
41
42 // Temporarily enable app shims.
43 base::AutoReset<bool> auto_reset_;
44
45 DISALLOW_COPY_AND_ASSIGN(AppShimInteractiveTest);
46 };
47
48 // Watches for changes to a file. This is designed to be used from the the UI
49 // thread.
50 class WindowedFilePathWatcher
51 : public base::RefCountedThreadSafe<WindowedFilePathWatcher> {
52 public:
53 WindowedFilePathWatcher(const base::FilePath& path)
54 : observed_(false) {
55 content::BrowserThread::PostTask(
56 content::BrowserThread::FILE,
tapted 2014/06/04 09:36:03 nit: indent 2 more spaces
jackhou1 2014/06/05 02:57:11 Done.
57 FROM_HERE,
58 base::Bind(&WindowedFilePathWatcher::Watch, this, path));
59 }
60
61 void Wait() {
62 if (observed_)
63 return;
64
65 run_loop_.reset(new base::RunLoop);
66 run_loop_->Run();
67 }
68
69 protected:
70 friend class base::RefCountedThreadSafe<WindowedFilePathWatcher>;
71 virtual ~WindowedFilePathWatcher() {}
72
73 void Watch(const base::FilePath& path) {
74 watcher_.Watch(
75 path, false, base::Bind(&WindowedFilePathWatcher::Observe, this));
76 }
77
78 void Observe(const base::FilePath& path, bool error) {
79 observed_ = true;
80 content::BrowserThread::PostTask(
81 content::BrowserThread::UI,
tapted 2014/06/04 09:36:03 nit: indent
jackhou1 2014/06/05 02:57:11 Done.
82 FROM_HERE,
83 base::Bind(&WindowedFilePathWatcher::StopRunLoop, this));
84 }
85
86 void StopRunLoop() {
87 if (run_loop_.get())
88 run_loop_->Quit();
89 }
90
91 base::FilePathWatcher watcher_;
92 bool observed_;
93 scoped_ptr<base::RunLoop> run_loop_;
94
95 DISALLOW_COPY_AND_ASSIGN(WindowedFilePathWatcher);
96 };
97
98 NSString* GetBundleID(const base::FilePath& shim_path) {
99 base::FilePath plist_path = shim_path.Append("Contents").Append("Info.plist");
100 NSMutableDictionary* plist = [NSMutableDictionary
101 dictionaryWithContentsOfFile:base::mac::FilePathToNSString(plist_path)];
102 return [plist objectForKey:base::mac::CFToNSCast(kCFBundleIdentifierKey)];
103 }
104
105 bool HasAppShimHost(Profile* profile, const std::string& app_id) {
106 return g_browser_process->platform_part()->app_shim_host_manager()->
107 extension_app_shim_handler()->FindHost(profile, app_id);
108 }
109
110 } // namespace
111
112 // Watches for NSNotifications from the shared workspace.
113 @interface WindowedNSNotificationObserver : NSObject {
114 @private
115 BOOL notificationReceived_;
116 scoped_ptr<base::RunLoop> runLoop_;
117 }
118
119 -(id) initForNotification:(NSString*)name;
tapted 2014/06/04 09:36:03 nit: spacing on all these should be like - (id)in
jackhou1 2014/06/05 02:57:11 Done.
120 -(void) observe:(NSNotification*)notification;
121 -(void) wait;
122 @end
123
124 @implementation WindowedNSNotificationObserver
125
126 -(id) initForNotification:(NSString*)name {
127 if (self = [super init]) {
128 [[[NSWorkspace sharedWorkspace] notificationCenter]
129 addObserver:self
tapted 2014/06/04 09:36:03 nit: min 4 spaces indent
jackhou1 2014/06/05 02:57:11 Done.
130 selector:@selector(observe:)
131 name:name
132 object:nil];
133 }
134 return self;
135 }
136
137 -(void) observe:(NSNotification*)notification {
138 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
139
140 [[[NSWorkspace sharedWorkspace] notificationCenter] removeObserver:self];
141 notificationReceived_ = YES;
tapted 2014/06/04 09:36:03 I think this will quit once any application is sta
jackhou1 2014/06/05 02:57:11 Done.
142 if (runLoop_.get())
143 runLoop_->Quit();
144 }
145
146 -(void) wait {
147 if (notificationReceived_)
148 return;
149
150 runLoop_.reset(new base::RunLoop);
151 runLoop_->Run();
152 }
153
154 @end
155
156 namespace apps {
157
158 // Test that launching the shim for an app starts the app, and vice versa.
159 // These two cases are combined because the time to run the test is dominated
160 // by loading the extension and creating the shim.
161 IN_PROC_BROWSER_TEST_F(AppShimInteractiveTest, Launch) {
162 // Create the internal app shim.
163 const extensions::Extension* app = InstallPlatformApp("minimal");
164
165 // Use a WebAppShortcutCreator to get the path.
166 web_app::WebAppShortcutCreator shortcut_creator(
167 web_app::GetWebAppDataDirectory(profile()->GetPath(), app->id(), GURL()),
168 web_app::ShortcutInfoForExtensionAndProfile(app, profile()),
169 extensions::FileHandlersInfo());
170 base::FilePath shim_path = shortcut_creator.GetInternalShortcutPath();
171 EXPECT_FALSE(base::PathExists(shim_path));
172
173 // Simulate updating the app. FilePathWatcher is used to wait for file
174 // operations on the shim to be finished before attempting to launch it.
175 scoped_refptr<WindowedFilePathWatcher> file_watcher =
176 new WindowedFilePathWatcher(shim_path);
tapted 2014/06/04 09:36:03 I think this is watching the .app root directory?
jackhou1 2014/06/05 02:57:11 It's guaranteed but in a non-obvious way. Added a
tapted 2014/06/05 04:08:01 Ah - cool makes sense. FilePathWatcher's documenta
177 web_app::UpdateAllShortcuts(base::string16(), profile(), app);
178 file_watcher->Wait();
179 NSString* bundle_id = GetBundleID(shim_path);
180
181 // Case 1: Launch the shim, it should start the app.
182 {
183 ExtensionTestMessageListener launched_listener("Launched", false);
184 CommandLine shim_cmdline(CommandLine::NO_PROGRAM);
185 shim_cmdline.AppendSwitch(app_mode::kLaunchedForTest);
186 ProcessSerialNumber shim_psn;
187 ASSERT_TRUE(base::mac::OpenApplicationWithPath(
188 shim_path, shim_cmdline, kLSLaunchDefaults, &shim_psn));
189 ASSERT_TRUE(launched_listener.WaitUntilSatisfied());
190
191 EXPECT_TRUE(HasAppShimHost(profile(), app->id()));
192
193 // If the window is closed, the shim should quit.
194 pid_t shim_pid;
195 EXPECT_EQ(noErr, GetProcessPID(&shim_psn, &shim_pid));
196 GetFirstAppWindow()->GetBaseWindow()->Close();
197 ASSERT_TRUE(base::WaitForSingleProcess(
198 shim_pid, TestTimeouts::action_timeout()));
199
200 EXPECT_FALSE(HasAppShimHost(profile(), app->id()));
201 }
202
203 // Case 2: Launch the app, it should start the shim.
204 {
205 base::scoped_nsobject<WindowedNSNotificationObserver> observer;
206 observer.reset([[WindowedNSNotificationObserver alloc]
207 initForNotification:NSWorkspaceDidLaunchApplicationNotification]);
208 LaunchPlatformApp(app);
209 [observer wait];
210
211 // Quitting the shim eventually closes the app.
212 NSArray* running_shim = [NSRunningApplication
tapted 2014/06/04 09:36:03 git cl format might format this line differently t
jackhou1 2014/06/05 02:57:11 Ran git cl format. It's ok with this, but it puts
213 runningApplicationsWithBundleIdentifier:bundle_id];
214 ASSERT_EQ(1u, [running_shim count]);
215
216 observer.reset([[WindowedNSNotificationObserver alloc]
217 initForNotification:NSWorkspaceDidTerminateApplicationNotification]);
218 [base::mac::ObjCCastStrict<NSRunningApplication>(
219 [running_shim objectAtIndex:0]) terminate];
220 [observer wait];
tapted 2014/06/04 09:36:03 I *think* this is not racy, but it might need a co
jackhou1 2014/06/05 02:57:11 I think this is only fired when the shim actually
tapted 2014/06/05 04:08:01 Yep - I just meant it's subtle that [NSRunnignAppl
jackhou1 2014/06/05 05:11:01 Done.
221
222 EXPECT_FALSE(GetFirstAppWindow());
223 EXPECT_FALSE(HasAppShimHost(profile(), app->id()));
tapted 2014/06/04 09:36:03 add a matching EXPECT_TRUE(HasAppShimHost../window
jackhou1 2014/06/05 02:57:11 Looks like I need to wait for the shim to connect
224 }
225 }
226
227 } // namespace apps
OLDNEW
« no previous file with comments | « no previous file | apps/app_shim/chrome_main_app_mode_mac.mm » ('j') | chrome/browser/web_applications/web_app_mac.h » ('J')

Powered by Google App Engine
This is Rietveld 408576698