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