OLD | NEW |
---|---|
(Empty) | |
1 // Copyright (c) 2012 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 | |
6 #import <Foundation/Foundation.h> | |
7 #import <ImageCaptureCore/ImageCaptureCore.h> | |
8 | |
9 #include "base/file_path.h" | |
10 #include "base/file_util.h" | |
11 #include "base/files/scoped_temp_dir.h" | |
12 #include "base/mac/foundation_util.h" | |
13 #include "base/memory/weak_ptr.h" | |
14 #include "base/message_loop.h" | |
15 #include "base/system_monitor/system_monitor.h" | |
16 #include "chrome/browser/system_monitor/image_capture_device.h" | |
17 #include "chrome/browser/system_monitor/image_capture_device_manager.h" | |
18 #include "content/public/test/test_browser_thread.h" | |
19 #include "testing/gtest/include/gtest/gtest.h" | |
20 | |
21 #if !defined(MAC_OS_X_VERSION_10_6) || \ | |
22 MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_6 | |
23 | |
24 @interface ICCameraDeviceDelegate (SnowLeopardAPI) | |
25 - (void)deviceDidBecomeReadyWithCompleteContentCatalog:(ICDevice*)device; | |
26 - (void)didDownloadFile:(ICCameraFile*)file | |
27 error:(NSError*)error | |
28 options:(NSDictionary*)options | |
29 contextInfo:(void*)contextInfo; | |
30 @end | |
31 | |
32 @interface NSScreen (LionAPI) | |
33 - (CGFloat)backingScaleFactor; | |
sail
2013/01/10 18:34:31
don't need this and the one below
Greg Billock
2013/01/10 21:42:15
Oops! Removed.
On 2013/01/10 18:34:31, sail wrote
| |
34 @end | |
35 | |
36 @interface NSWindow (LionAPI) | |
37 - (CGFloat)backingScaleFactor; | |
38 @end | |
39 | |
40 #endif // 10.6 | |
41 | |
42 namespace { | |
43 | |
44 const char kDeviceId[] = "id"; | |
45 const char kTestFileContents[] = "test"; | |
46 | |
47 } // namespace | |
48 | |
49 // Private ICCameraDevice method needed to properly initialize the object. | |
50 @interface NSObject (PrivateAPIICCameraDevice) | |
51 - (id)initWithDictionary:(id)properties; | |
52 @end | |
53 | |
54 @interface MockICCameraDevice : ICCameraDevice { | |
55 @private | |
56 scoped_nsobject<NSMutableArray> allMediaFiles_; | |
57 } | |
58 | |
59 - (void)addMediaFile:(ICCameraFile*)file; | |
60 | |
61 @end | |
62 | |
63 @implementation MockICCameraDevice | |
64 | |
65 - (id)init { | |
66 if ((self = [super initWithDictionary:[NSDictionary dictionary]])) { | |
67 } | |
68 return self; | |
69 } | |
70 | |
71 - (NSString*)mountPoint { | |
72 return @"mountPoint"; | |
73 } | |
74 | |
75 - (NSString*)name { | |
76 return @"name"; | |
77 } | |
78 | |
79 - (NSString*)UUIDString { | |
80 return base::SysUTF8ToNSString(kDeviceId); | |
81 } | |
82 | |
83 - (ICDeviceType)type { | |
84 return ICDeviceTypeCamera; | |
85 } | |
86 | |
87 - (void)requestOpenSession { | |
88 } | |
89 | |
90 - (void)requestCloseSession { | |
91 } | |
92 | |
93 - (NSArray*)mediaFiles { | |
94 return allMediaFiles_; | |
95 } | |
96 | |
97 - (void)addMediaFile:(ICCameraFile*)file { | |
98 if (!allMediaFiles_.get()) | |
99 allMediaFiles_.reset([[NSMutableArray alloc] init]); | |
100 [allMediaFiles_ addObject:file]; | |
101 } | |
102 | |
103 // This method does approximately what the internal ImageCapture platform | |
104 // library is observed to do: take the download save-as filename and mangle | |
105 // it to attach an extension, then return that new filename to the caller | |
106 // in the options. | |
107 - (void)requestDownloadFile:(ICCameraFile*)file | |
108 options:(NSDictionary*)options | |
109 downloadDelegate:(id<ICCameraDeviceDownloadDelegate>)downloadDelegate | |
110 didDownloadSelector:(SEL)selector | |
111 contextInfo:(void*)contextInfo { | |
112 FilePath saveDir(base::SysNSStringToUTF8( | |
113 [[options objectForKey:ICDownloadsDirectoryURL] path])); | |
114 std::string saveAsFilename = | |
115 base::SysNSStringToUTF8([options objectForKey:ICSaveAsFilename]); | |
116 // It appears that the ImageCapture library adds an extension to the requested | |
117 // filename. Do that here to require a rename. | |
118 saveAsFilename += ".jpg"; | |
119 FilePath toBeSaved = saveDir.Append(saveAsFilename); | |
120 ASSERT_EQ(static_cast<int>(strlen(kTestFileContents)), | |
121 file_util::WriteFile(toBeSaved, kTestFileContents, | |
122 strlen(kTestFileContents))); | |
123 | |
124 NSMutableDictionary* returnOptions = | |
125 [NSMutableDictionary dictionaryWithDictionary:options]; | |
126 [returnOptions setObject:base::SysUTF8ToNSString(saveAsFilename) | |
127 forKey:ICSavedFilename]; | |
128 | |
129 [downloadDelegate didDownloadFile:file | |
130 error:nil | |
131 options:returnOptions | |
132 contextInfo:contextInfo]; | |
133 } | |
134 | |
135 @end | |
136 | |
137 @interface MockICCameraFile : ICCameraFile { | |
138 @private | |
139 scoped_nsobject<NSString> name_; | |
140 scoped_nsobject<NSDate> date_; | |
141 } | |
142 | |
143 - (id)init:(NSString*)name; | |
144 | |
145 @end | |
146 | |
147 @implementation MockICCameraFile | |
148 | |
149 - (id)init:(NSString*)name { | |
150 if ((self = [super init])) { | |
151 name_.reset([name retain]); | |
152 date_.reset([[NSDate dateWithNaturalLanguageString:@"12/12/12"] retain]); | |
153 } | |
154 return self; | |
155 } | |
156 | |
157 - (NSString*)name { | |
158 return name_.get(); | |
159 } | |
160 | |
161 - (NSString*)UTI { | |
162 return base::mac::CFToNSCast(kUTTypeImage); | |
163 } | |
164 | |
165 - (NSDate*)modificationDate { | |
166 return date_.get(); | |
167 } | |
168 | |
169 - (NSDate*)creationDate { | |
170 return date_.get(); | |
171 } | |
172 | |
173 - (off_t)fileSize { | |
174 return 1000; | |
175 } | |
176 | |
177 @end | |
178 | |
179 class TestCameraListener | |
180 : public ImageCaptureDeviceListener, | |
181 public base::SupportsWeakPtr<TestCameraListener> { | |
182 public: | |
183 TestCameraListener() | |
184 : completed_(false), | |
185 removed_(false), | |
186 last_error_(base::PLATFORM_FILE_ERROR_INVALID_URL) {} | |
187 virtual ~TestCameraListener() {} | |
188 | |
189 virtual void ItemAdded(const std::string& name, | |
190 const base::PlatformFileInfo& info) OVERRIDE { | |
191 items_.push_back(name); | |
192 } | |
193 | |
194 virtual void NoMoreItems() OVERRIDE { | |
195 completed_ = true; | |
196 } | |
197 | |
198 virtual void DownloadedFile(const std::string& name, | |
199 base::PlatformFileError error) OVERRIDE { | |
200 EXPECT_TRUE(content::BrowserThread::CurrentlyOn( | |
201 content::BrowserThread::UI)); | |
202 downloads_.push_back(name); | |
203 last_error_ = error; | |
204 } | |
205 | |
206 virtual void DeviceRemoved() OVERRIDE { | |
207 removed_ = true; | |
208 } | |
209 | |
210 std::vector<std::string> items() const { return items_; } | |
211 std::vector<std::string> downloads() const { return downloads_; } | |
212 bool completed() const { return completed_; } | |
213 bool removed() const { return removed_; } | |
214 base::PlatformFileError last_error() const { return last_error_; } | |
215 | |
216 private: | |
217 std::vector<std::string> items_; | |
218 std::vector<std::string> downloads_; | |
219 bool completed_; | |
220 bool removed_; | |
221 base::PlatformFileError last_error_; | |
222 }; | |
223 | |
224 class ImageCaptureDeviceManagerTest : public testing::Test { | |
225 public: | |
226 virtual void SetUp() OVERRIDE { | |
227 base::SystemMonitor::AllocateSystemIOPorts(); | |
228 system_monitor_.reset(new base::SystemMonitor()); | |
229 ui_thread_.reset(new content::TestBrowserThread( | |
230 content::BrowserThread::UI, &message_loop_)); | |
231 } | |
232 | |
233 MockICCameraDevice* AttachDevice( | |
234 chrome::ImageCaptureDeviceManager* manager) { | |
235 // Ownership will be passed to the device browser delegate. | |
236 scoped_nsobject<MockICCameraDevice> device( | |
237 [[MockICCameraDevice alloc] init]); | |
238 id<ICDeviceBrowserDelegate> delegate = manager->device_browser(); | |
239 [delegate deviceBrowser:nil didAddDevice:device moreComing:NO]; | |
240 return device.autorelease(); | |
241 } | |
242 | |
243 void DetachDevice(chrome::ImageCaptureDeviceManager* manager, | |
244 ICCameraDevice* device) { | |
245 id<ICDeviceBrowserDelegate> delegate = manager->device_browser(); | |
246 [delegate deviceBrowser:nil didRemoveDevice:device moreGoing:NO]; | |
247 } | |
248 | |
249 protected: | |
250 MessageLoopForUI message_loop_; | |
251 scoped_ptr<content::TestBrowserThread> ui_thread_; | |
252 scoped_ptr<base::SystemMonitor> system_monitor_; | |
253 TestCameraListener listener_; | |
254 }; | |
255 | |
256 TEST_F(ImageCaptureDeviceManagerTest, TestAttachDetach) { | |
257 chrome::ImageCaptureDeviceManager manager; | |
258 ICCameraDevice* device = AttachDevice(&manager); | |
259 std::vector<base::SystemMonitor::RemovableStorageInfo> devices = | |
260 system_monitor_->GetAttachedRemovableStorage(); | |
261 | |
262 ASSERT_EQ(1U, devices.size()); | |
263 EXPECT_EQ(std::string("ic:") + kDeviceId, devices[0].device_id); | |
264 | |
265 DetachDevice(&manager, device); | |
266 devices = system_monitor_->GetAttachedRemovableStorage(); | |
267 ASSERT_EQ(0U, devices.size()); | |
268 }; | |
269 | |
270 TEST_F(ImageCaptureDeviceManagerTest, OpenCamera) { | |
271 chrome::ImageCaptureDeviceManager manager; | |
272 ICCameraDevice* device = AttachDevice(&manager); | |
273 | |
274 EXPECT_FALSE(chrome::ImageCaptureDeviceManager::deviceForUUID( | |
275 "nonexistent")); | |
276 | |
277 scoped_nsobject<ImageCaptureDevice> camera( | |
278 [chrome::ImageCaptureDeviceManager::deviceForUUID(kDeviceId) | |
279 retain]); | |
280 | |
281 [camera setListener:listener_.AsWeakPtr()]; | |
282 [camera open]; | |
283 | |
284 scoped_nsobject<MockICCameraFile> picture1( | |
285 [[MockICCameraFile alloc] init:@"pic1"]); | |
286 [camera cameraDevice:nil didAddItem:picture1]; | |
287 scoped_nsobject<MockICCameraFile> picture2( | |
288 [[MockICCameraFile alloc] init:@"pic2"]); | |
289 [camera cameraDevice:nil didAddItem:picture2]; | |
290 ASSERT_EQ(2U, listener_.items().size()); | |
291 EXPECT_EQ("pic1", listener_.items()[0]); | |
292 EXPECT_EQ("pic2", listener_.items()[1]); | |
293 EXPECT_FALSE(listener_.completed()); | |
294 | |
295 [camera deviceDidBecomeReadyWithCompleteContentCatalog:nil]; | |
296 ASSERT_EQ(2U, listener_.items().size()); | |
297 EXPECT_TRUE(listener_.completed()); | |
298 | |
299 [camera close]; | |
300 DetachDevice(&manager, device); | |
301 EXPECT_FALSE(chrome::ImageCaptureDeviceManager::deviceForUUID( | |
302 kDeviceId)); | |
303 } | |
304 | |
305 TEST_F(ImageCaptureDeviceManagerTest, RemoveCamera) { | |
306 chrome::ImageCaptureDeviceManager manager; | |
307 ICCameraDevice* device = AttachDevice(&manager); | |
308 | |
309 scoped_nsobject<ImageCaptureDevice> camera( | |
310 [chrome::ImageCaptureDeviceManager::deviceForUUID(kDeviceId) | |
311 retain]); | |
312 | |
313 [camera setListener:listener_.AsWeakPtr()]; | |
314 [camera open]; | |
315 | |
316 [camera didRemoveDevice:device]; | |
317 EXPECT_TRUE(listener_.removed()); | |
318 } | |
319 | |
320 TEST_F(ImageCaptureDeviceManagerTest, DownloadFile) { | |
321 scoped_ptr<content::TestBrowserThread> file_thread_( | |
322 new content::TestBrowserThread( | |
323 content::BrowserThread::FILE, &message_loop_)); | |
324 | |
325 chrome::ImageCaptureDeviceManager manager; | |
326 MockICCameraDevice* device = AttachDevice(&manager); | |
327 | |
328 scoped_nsobject<ImageCaptureDevice> camera( | |
329 [chrome::ImageCaptureDeviceManager::deviceForUUID(kDeviceId) | |
330 retain]); | |
331 | |
332 [camera setListener:listener_.AsWeakPtr()]; | |
333 [camera open]; | |
334 | |
335 std::string kTestFileName("pic1"); | |
336 | |
337 scoped_nsobject<MockICCameraFile> picture1( | |
338 [[MockICCameraFile alloc] | |
339 init:base::SysUTF8ToNSString(kTestFileName)]); | |
340 [device addMediaFile:picture1]; | |
341 [camera cameraDevice:nil didAddItem:picture1]; | |
342 | |
343 base::ScopedTempDir temp_dir; | |
344 ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); | |
345 | |
346 EXPECT_EQ(0U, listener_.downloads().size()); | |
347 | |
348 // Test that a nonexistent file we ask to be downloaded will | |
349 // return us a not-found error. | |
350 FilePath temp_file = temp_dir.path().Append("tempfile"); | |
351 [camera downloadFile:std::string("nonexistent") localPath:temp_file]; | |
352 message_loop_.RunUntilIdle(); | |
353 ASSERT_EQ(1U, listener_.downloads().size()); | |
354 EXPECT_EQ("nonexistent", listener_.downloads()[0]); | |
355 EXPECT_EQ(base::PLATFORM_FILE_ERROR_NOT_FOUND, listener_.last_error()); | |
356 | |
357 // Test that an existing file we ask to be downloaded will end up in | |
358 // the location we specify. The mock system will copy testing file | |
359 // contents to a separate filename, mimicking the ImageCaptureCore | |
360 // library behavior. Our code then renames the file onto the requested | |
361 // destination. | |
362 [camera downloadFile:kTestFileName localPath:temp_file]; | |
363 message_loop_.RunUntilIdle(); | |
364 | |
365 ASSERT_EQ(2U, listener_.downloads().size()); | |
366 EXPECT_EQ(kTestFileName, listener_.downloads()[1]); | |
367 ASSERT_EQ(base::PLATFORM_FILE_OK, listener_.last_error()); | |
368 char file_contents[5]; | |
369 ASSERT_EQ(4, file_util::ReadFile(temp_file, file_contents, | |
370 strlen(kTestFileContents))); | |
371 EXPECT_EQ(kTestFileContents, | |
372 std::string(file_contents, strlen(kTestFileContents))); | |
373 | |
374 [camera didRemoveDevice:device]; | |
375 } | |
OLD | NEW |