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