Upstream version 5.34.104.0
[platform/framework/web/crosswalk.git] / src / components / storage_monitor / image_capture_device_manager_unittest.mm
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 <Foundation/Foundation.h>
6 #import <ImageCaptureCore/ImageCaptureCore.h>
7
8 #include "base/file_util.h"
9 #include "base/files/file_path.h"
10 #include "base/files/scoped_temp_dir.h"
11 #include "base/mac/foundation_util.h"
12 #include "base/memory/weak_ptr.h"
13 #include "base/run_loop.h"
14 #include "components/storage_monitor/image_capture_device.h"
15 #include "components/storage_monitor/image_capture_device_manager.h"
16 #include "components/storage_monitor/test_storage_monitor.h"
17 #include "content/public/browser/browser_thread.h"
18 #include "content/public/test/test_browser_thread_bundle.h"
19 #include "testing/gtest/include/gtest/gtest.h"
20
21 #if !defined(MAC_OS_X_VERSION_10_7) || \
22     MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_7
23
24 @interface NSObject (ICCameraDeviceDelegateLionAPI)
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   base::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   base::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   base::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   [static_cast<NSObject<ICCameraDeviceDownloadDelegate>*>(downloadDelegate)
122    didDownloadFile:file
123              error:nil
124            options:returnOptions
125        contextInfo:contextInfo];
126 }
127
128 @end
129
130 @interface MockICCameraFolder : ICCameraFolder {
131  @private
132   base::scoped_nsobject<NSString> name_;
133 }
134
135 - (id)initWithName:(NSString*)name;
136
137 @end
138
139 @implementation MockICCameraFolder
140
141 - (id)initWithName:(NSString*)name {
142   if ((self = [super init])) {
143     name_.reset([name retain]);
144   }
145   return self;
146 }
147
148 - (NSString*)name {
149   return name_;
150 }
151
152 - (ICCameraFolder*)parentFolder {
153   return nil;
154 }
155
156 @end
157
158 @interface MockICCameraFile : ICCameraFile {
159  @private
160   base::scoped_nsobject<NSString> name_;
161   base::scoped_nsobject<NSDate> date_;
162   base::scoped_nsobject<MockICCameraFolder> parent_;
163 }
164
165 - (id)init:(NSString*)name;
166 - (void)setParent:(NSString*)parent;
167
168 @end
169
170 @implementation MockICCameraFile
171
172 - (id)init:(NSString*)name {
173   if ((self = [super init])) {
174     name_.reset([name retain]);
175     date_.reset([[NSDate dateWithNaturalLanguageString:@"12/12/12"] retain]);
176   }
177   return self;
178 }
179
180 - (void)setParent:(NSString*)parent {
181   parent_.reset([[MockICCameraFolder alloc] initWithName:parent]);
182 }
183
184 - (ICCameraFolder*)parentFolder {
185   return parent_.get();
186 }
187
188 - (NSString*)name {
189   return name_;
190 }
191
192 - (NSString*)UTI {
193   return base::mac::CFToNSCast(kUTTypeImage);
194 }
195
196 - (NSDate*)modificationDate {
197   return date_.get();
198 }
199
200 - (NSDate*)creationDate {
201   return date_.get();
202 }
203
204 - (off_t)fileSize {
205   return 1000;
206 }
207
208 @end
209
210 class TestCameraListener
211     : public ImageCaptureDeviceListener,
212       public base::SupportsWeakPtr<TestCameraListener> {
213  public:
214   TestCameraListener()
215       : completed_(false),
216         removed_(false),
217         last_error_(base::File::FILE_ERROR_INVALID_URL) {}
218   virtual ~TestCameraListener() {}
219
220   virtual void ItemAdded(const std::string& name,
221                          const base::File::Info& info) OVERRIDE {
222     items_.push_back(name);
223   }
224
225   virtual void NoMoreItems() OVERRIDE {
226     completed_ = true;
227   }
228
229   virtual void DownloadedFile(const std::string& name,
230                               base::File::Error error) OVERRIDE {
231     EXPECT_TRUE(content::BrowserThread::CurrentlyOn(
232         content::BrowserThread::UI));
233     downloads_.push_back(name);
234     last_error_ = error;
235   }
236
237   virtual void DeviceRemoved() OVERRIDE {
238     removed_ = true;
239   }
240
241   std::vector<std::string> items() const { return items_; }
242   std::vector<std::string> downloads() const { return downloads_; }
243   bool completed() const { return completed_; }
244   bool removed() const { return removed_; }
245   base::File::Error last_error() const { return last_error_; }
246
247  private:
248   std::vector<std::string> items_;
249   std::vector<std::string> downloads_;
250   bool completed_;
251   bool removed_;
252   base::File::Error last_error_;
253 };
254
255 class ImageCaptureDeviceManagerTest : public testing::Test {
256  public:
257   virtual void SetUp() OVERRIDE {
258     monitor_ = TestStorageMonitor::CreateAndInstall();
259   }
260
261   virtual void TearDown() OVERRIDE {
262     TestStorageMonitor::Destroy();
263   }
264
265   MockICCameraDevice* AttachDevice(ImageCaptureDeviceManager* manager) {
266     // Ownership will be passed to the device browser delegate.
267     base::scoped_nsobject<MockICCameraDevice> device(
268         [[MockICCameraDevice alloc] init]);
269     id<ICDeviceBrowserDelegate> delegate = manager->device_browser();
270     [delegate deviceBrowser:nil didAddDevice:device moreComing:NO];
271     return device.autorelease();
272   }
273
274   void DetachDevice(ImageCaptureDeviceManager* manager,
275                     ICCameraDevice* device) {
276     id<ICDeviceBrowserDelegate> delegate = manager->device_browser();
277     [delegate deviceBrowser:nil didRemoveDevice:device moreGoing:NO];
278   }
279
280  protected:
281   content::TestBrowserThreadBundle thread_bundle_;
282   TestStorageMonitor* monitor_;
283   TestCameraListener listener_;
284 };
285
286 TEST_F(ImageCaptureDeviceManagerTest, TestAttachDetach) {
287   ImageCaptureDeviceManager manager;
288   manager.SetNotifications(monitor_->receiver());
289   ICCameraDevice* device = AttachDevice(&manager);
290   std::vector<StorageInfo> devices = monitor_->GetAllAvailableStorages();
291
292   ASSERT_EQ(1U, devices.size());
293   EXPECT_EQ(std::string("ic:") + kDeviceId, devices[0].device_id());
294
295   DetachDevice(&manager, device);
296   devices = monitor_->GetAllAvailableStorages();
297   ASSERT_EQ(0U, devices.size());
298 };
299
300 TEST_F(ImageCaptureDeviceManagerTest, OpenCamera) {
301   ImageCaptureDeviceManager manager;
302   manager.SetNotifications(monitor_->receiver());
303   ICCameraDevice* device = AttachDevice(&manager);
304
305   EXPECT_FALSE(ImageCaptureDeviceManager::deviceForUUID(
306       "nonexistent"));
307
308   base::scoped_nsobject<ImageCaptureDevice> camera(
309       [ImageCaptureDeviceManager::deviceForUUID(kDeviceId) retain]);
310
311   [camera setListener:listener_.AsWeakPtr()];
312   [camera open];
313
314   base::scoped_nsobject<MockICCameraFile> picture1(
315       [[MockICCameraFile alloc] init:@"pic1"]);
316   [camera cameraDevice:nil didAddItem:picture1];
317   base::scoped_nsobject<MockICCameraFile> picture2(
318       [[MockICCameraFile alloc] init:@"pic2"]);
319   [camera cameraDevice:nil didAddItem:picture2];
320   ASSERT_EQ(2U, listener_.items().size());
321   EXPECT_EQ("pic1", listener_.items()[0]);
322   EXPECT_EQ("pic2", listener_.items()[1]);
323   EXPECT_FALSE(listener_.completed());
324
325   [camera deviceDidBecomeReadyWithCompleteContentCatalog:nil];
326
327   ASSERT_EQ(2U, listener_.items().size());
328   EXPECT_TRUE(listener_.completed());
329
330   [camera close];
331   DetachDevice(&manager, device);
332   EXPECT_FALSE(ImageCaptureDeviceManager::deviceForUUID(kDeviceId));
333 }
334
335 TEST_F(ImageCaptureDeviceManagerTest, RemoveCamera) {
336   ImageCaptureDeviceManager manager;
337   manager.SetNotifications(monitor_->receiver());
338   ICCameraDevice* device = AttachDevice(&manager);
339
340   base::scoped_nsobject<ImageCaptureDevice> camera(
341       [ImageCaptureDeviceManager::deviceForUUID(kDeviceId) retain]);
342
343   [camera setListener:listener_.AsWeakPtr()];
344   [camera open];
345
346   [camera didRemoveDevice:device];
347   EXPECT_TRUE(listener_.removed());
348 }
349
350 TEST_F(ImageCaptureDeviceManagerTest, DownloadFile) {
351   ImageCaptureDeviceManager manager;
352   manager.SetNotifications(monitor_->receiver());
353   MockICCameraDevice* device = AttachDevice(&manager);
354
355   base::scoped_nsobject<ImageCaptureDevice> camera(
356       [ImageCaptureDeviceManager::deviceForUUID(kDeviceId) retain]);
357
358   [camera setListener:listener_.AsWeakPtr()];
359   [camera open];
360
361   std::string kTestFileName("pic1");
362
363   base::scoped_nsobject<MockICCameraFile> picture1(
364       [[MockICCameraFile alloc] init:base::SysUTF8ToNSString(kTestFileName)]);
365   [device addMediaFile:picture1];
366   [camera cameraDevice:nil didAddItem:picture1];
367
368   base::ScopedTempDir temp_dir;
369   ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
370
371   EXPECT_EQ(0U, listener_.downloads().size());
372
373   // Test that a nonexistent file we ask to be downloaded will
374   // return us a not-found error.
375   base::FilePath temp_file = temp_dir.path().Append("tempfile");
376   [camera downloadFile:std::string("nonexistent") localPath:temp_file];
377   base::RunLoop().RunUntilIdle();
378   ASSERT_EQ(1U, listener_.downloads().size());
379   EXPECT_EQ("nonexistent", listener_.downloads()[0]);
380   EXPECT_EQ(base::File::FILE_ERROR_NOT_FOUND, listener_.last_error());
381
382   // Test that an existing file we ask to be downloaded will end up in
383   // the location we specify. The mock system will copy testing file
384   // contents to a separate filename, mimicking the ImageCaptureCore
385   // library behavior. Our code then renames the file onto the requested
386   // destination.
387   [camera downloadFile:kTestFileName localPath:temp_file];
388   base::RunLoop().RunUntilIdle();
389
390   ASSERT_EQ(2U, listener_.downloads().size());
391   EXPECT_EQ(kTestFileName, listener_.downloads()[1]);
392   ASSERT_EQ(base::File::FILE_OK, listener_.last_error());
393   char file_contents[5];
394   ASSERT_EQ(4, base::ReadFile(temp_file, file_contents,
395                               strlen(kTestFileContents)));
396   EXPECT_EQ(kTestFileContents,
397             std::string(file_contents, strlen(kTestFileContents)));
398
399   [camera didRemoveDevice:device];
400 }
401
402 TEST_F(ImageCaptureDeviceManagerTest, TestSubdirectories) {
403   ImageCaptureDeviceManager manager;
404   manager.SetNotifications(monitor_->receiver());
405   MockICCameraDevice* device = AttachDevice(&manager);
406
407   base::scoped_nsobject<ImageCaptureDevice> camera(
408       [ImageCaptureDeviceManager::deviceForUUID(kDeviceId) retain]);
409
410   [camera setListener:listener_.AsWeakPtr()];
411   [camera open];
412
413   std::string kTestFileName("pic1");
414   base::scoped_nsobject<MockICCameraFile> picture1(
415       [[MockICCameraFile alloc] init:base::SysUTF8ToNSString(kTestFileName)]);
416   [picture1 setParent:base::SysUTF8ToNSString("dir")];
417   [device addMediaFile:picture1];
418   [camera cameraDevice:nil didAddItem:picture1];
419
420   base::ScopedTempDir temp_dir;
421   ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
422   base::FilePath temp_file = temp_dir.path().Append("tempfile");
423
424   [camera downloadFile:("dir/" + kTestFileName) localPath:temp_file];
425   base::RunLoop().RunUntilIdle();
426
427   char file_contents[5];
428   ASSERT_EQ(4, base::ReadFile(temp_file, file_contents,
429                               strlen(kTestFileContents)));
430   EXPECT_EQ(kTestFileContents,
431             std::string(file_contents, strlen(kTestFileContents)));
432
433   [camera didRemoveDevice:device];
434 }