Disable view source for Developer Tools.
[chromium-blink-merge.git] / chrome / browser / media_galleries / mac / mtp_device_delegate_impl_mac_unittest.mm
blob7dbe1163714a217ceaec655c9235474e40629e77
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.
5 #import <Foundation/Foundation.h>
6 #import <ImageCaptureCore/ImageCaptureCore.h>
8 #include "base/file_util.h"
9 #include "base/files/scoped_temp_dir.h"
10 #include "base/mac/cocoa_protocols.h"
11 #include "base/mac/foundation_util.h"
12 #include "base/mac/scoped_nsobject.h"
13 #include "base/message_loop/message_loop.h"
14 #include "base/run_loop.h"
15 #include "base/strings/sys_string_conversions.h"
16 #include "base/synchronization/waitable_event.h"
17 #include "base/test/sequenced_worker_pool_owner.h"
18 #include "base/threading/sequenced_worker_pool.h"
19 #include "chrome/browser/media_galleries/mac/mtp_device_delegate_impl_mac.h"
20 #include "chrome/browser/storage_monitor/image_capture_device_manager.h"
21 #include "chrome/browser/storage_monitor/test_storage_monitor.h"
22 #include "content/public/browser/browser_thread.h"
23 #include "content/public/test/test_browser_thread.h"
24 #include "testing/gtest/include/gtest/gtest.h"
26 #if !defined(MAC_OS_X_VERSION_10_7) || \
27     MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_7
29 @interface NSObject (ICCameraDeviceDelegateLionAPI)
30 - (void)deviceDidBecomeReadyWithCompleteContentCatalog:(ICDevice*)device;
31 - (void)didDownloadFile:(ICCameraFile*)file
32                   error:(NSError*)error
33                 options:(NSDictionary*)options
34             contextInfo:(void*)contextInfo;
35 @end
37 #endif  // 10.6
39 namespace {
41 const char kDeviceId[] = "id";
42 const char kDevicePath[] = "/ic:id";
43 const char kTestFileContents[] = "test";
45 }  // namespace
47 @interface MockMTPICCameraDevice : ICCameraDevice {
48  @private
49   base::scoped_nsobject<NSMutableArray> allMediaFiles_;
52 - (void)addMediaFile:(ICCameraFile*)file;
54 @end
56 @implementation MockMTPICCameraDevice
58 - (NSString*)mountPoint {
59   return @"mountPoint";
62 - (NSString*)name {
63   return @"name";
66 - (NSString*)UUIDString {
67   return base::SysUTF8ToNSString(kDeviceId);
70 - (ICDeviceType)type {
71   return ICDeviceTypeCamera;
74 - (void)requestOpenSession {
77 - (void)requestCloseSession {
80 - (NSArray*)mediaFiles {
81   return allMediaFiles_;
84 - (void)addMediaFile:(ICCameraFile*)file {
85   if (!allMediaFiles_.get())
86     allMediaFiles_.reset([[NSMutableArray alloc] init]);
87   [allMediaFiles_ addObject:file];
90 - (void)requestDownloadFile:(ICCameraFile*)file
91                     options:(NSDictionary*)options
92            downloadDelegate:(id<ICCameraDeviceDownloadDelegate>)downloadDelegate
93         didDownloadSelector:(SEL)selector
94                 contextInfo:(void*)contextInfo {
95   base::FilePath saveDir(base::SysNSStringToUTF8(
96       [[options objectForKey:ICDownloadsDirectoryURL] path]));
97   std::string saveAsFilename =
98       base::SysNSStringToUTF8([options objectForKey:ICSaveAsFilename]);
99   // It appears that the ImageCapture library adds an extension to the requested
100   // filename. Do that here to require a rename.
101   saveAsFilename += ".jpg";
102   base::FilePath toBeSaved = saveDir.Append(saveAsFilename);
103   ASSERT_EQ(static_cast<int>(strlen(kTestFileContents)),
104             file_util::WriteFile(toBeSaved, kTestFileContents,
105                                  strlen(kTestFileContents)));
107   NSMutableDictionary* returnOptions =
108       [NSMutableDictionary dictionaryWithDictionary:options];
109   [returnOptions setObject:base::SysUTF8ToNSString(saveAsFilename)
110                     forKey:ICSavedFilename];
112   [static_cast<NSObject<ICCameraDeviceDownloadDelegate>*>(downloadDelegate)
113    didDownloadFile:file
114              error:nil
115            options:returnOptions
116        contextInfo:contextInfo];
119 @end
121 @interface MockMTPICCameraFile : ICCameraFile {
122  @private
123   base::scoped_nsobject<NSString> name_;
124   base::scoped_nsobject<NSDate> date_;
127 - (id)init:(NSString*)name;
129 @end
131 @implementation MockMTPICCameraFile
133 - (id)init:(NSString*)name {
134   if ((self = [super init])) {
135     name_.reset([name retain]);
136     date_.reset([[NSDate dateWithNaturalLanguageString:@"12/12/12"] retain]);
137   }
138   return self;
141 - (NSString*)name {
142   return name_.get();
145 - (NSString*)UTI {
146   return base::mac::CFToNSCast(kUTTypeImage);
149 - (NSDate*)modificationDate {
150   return date_.get();
153 - (NSDate*)creationDate {
154   return date_.get();
157 - (off_t)fileSize {
158   return 1000;
161 @end
163 class MTPDeviceDelegateImplMacTest : public testing::Test {
164  public:
165   MTPDeviceDelegateImplMacTest() : camera_(NULL), delegate_(NULL) {}
167   virtual void SetUp() OVERRIDE {
168     ui_thread_.reset(new content::TestBrowserThread(
169         content::BrowserThread::UI, &message_loop_));
170     file_thread_.reset(new content::TestBrowserThread(
171         content::BrowserThread::FILE, &message_loop_));
172     io_thread_.reset(new content::TestBrowserThread(
173         content::BrowserThread::IO));
174     ASSERT_TRUE(io_thread_->Start());
176     TestStorageMonitor* monitor = TestStorageMonitor::CreateAndInstall();
177     manager_.SetNotifications(monitor->receiver());
179     camera_ = [MockMTPICCameraDevice alloc];
180     id<ICDeviceBrowserDelegate> delegate = manager_.device_browser();
181     [delegate deviceBrowser:nil didAddDevice:camera_ moreComing:NO];
183     delegate_ = new MTPDeviceDelegateImplMac(kDeviceId, kDevicePath);
184   }
186   virtual void TearDown() OVERRIDE {
187     id<ICDeviceBrowserDelegate> delegate = manager_.device_browser();
188     [delegate deviceBrowser:nil didRemoveDevice:camera_ moreGoing:NO];
190     delegate_->CancelPendingTasksAndDeleteDelegate();
192     TestStorageMonitor::RemoveSingleton();
194     io_thread_->Stop();
195   }
197   void OnError(base::WaitableEvent* event, base::PlatformFileError error) {
198     error_ = error;
199     event->Signal();
200   }
202   void OverlappedOnError(base::WaitableEvent* event,
203                          base::PlatformFileError error) {
204     overlapped_error_ = error;
205     event->Signal();
206   }
208   void OnFileInfo(base::WaitableEvent* event,
209                   const base::PlatformFileInfo& info) {
210     error_ = base::PLATFORM_FILE_OK;
211     info_ = info;
212     event->Signal();
213   }
215   void OnReadDir(base::WaitableEvent* event,
216                  const fileapi::AsyncFileUtil::EntryList& files,
217                  bool has_more) {
218     error_ = base::PLATFORM_FILE_OK;
219     ASSERT_FALSE(has_more);
220     file_list_ = files;
221     event->Signal();
222   }
224   void OverlappedOnReadDir(base::WaitableEvent* event,
225                            const fileapi::AsyncFileUtil::EntryList& files,
226                            bool has_more) {
227     overlapped_error_ = base::PLATFORM_FILE_OK;
228     ASSERT_FALSE(has_more);
229     overlapped_file_list_ = files;
230     event->Signal();
231   }
233   void OnDownload(base::WaitableEvent* event,
234                   const base::PlatformFileInfo& file_info,
235                   const base::FilePath& local_path) {
236     error_ = base::PLATFORM_FILE_OK;
237     event->Signal();
238   }
240   base::PlatformFileError GetFileInfo(const base::FilePath& path,
241                                       base::PlatformFileInfo* info) {
242     base::WaitableEvent wait(true, false);
243     delegate_->GetFileInfo(
244       path,
245       base::Bind(&MTPDeviceDelegateImplMacTest::OnFileInfo,
246                  base::Unretained(this),
247                  &wait),
248       base::Bind(&MTPDeviceDelegateImplMacTest::OnError,
249                  base::Unretained(this),
250                  &wait));
251     base::RunLoop loop;
252     loop.RunUntilIdle();
253     EXPECT_TRUE(wait.IsSignaled());
254     *info = info_;
255     return error_;
256   }
258   base::PlatformFileError ReadDir(const base::FilePath& path) {
259     base::WaitableEvent wait(true, false);
260     delegate_->ReadDirectory(
261         path,
262         base::Bind(&MTPDeviceDelegateImplMacTest::OnReadDir,
263                    base::Unretained(this),
264                    &wait),
265         base::Bind(&MTPDeviceDelegateImplMacTest::OnError,
266                    base::Unretained(this),
267                    &wait));
268     base::RunLoop loop;
269     loop.RunUntilIdle();
270     wait.Wait();
271     return error_;
272   }
274   base::PlatformFileError DownloadFile(
275       const base::FilePath& path,
276       const base::FilePath& local_path) {
277     base::WaitableEvent wait(true, false);
278     delegate_->CreateSnapshotFile(
279         path, local_path,
280         base::Bind(&MTPDeviceDelegateImplMacTest::OnDownload,
281                    base::Unretained(this),
282                    &wait),
283         base::Bind(&MTPDeviceDelegateImplMacTest::OnError,
284                    base::Unretained(this),
285                    &wait));
286     base::RunLoop loop;
287     loop.RunUntilIdle();
288     wait.Wait();
289     return error_;
290   }
292  protected:
293   base::MessageLoopForUI message_loop_;
294   // Note: threads must be made in this order: UI > FILE > IO
295   scoped_ptr<content::TestBrowserThread> ui_thread_;
296   scoped_ptr<content::TestBrowserThread> file_thread_;
297   scoped_ptr<content::TestBrowserThread> io_thread_;
298   base::ScopedTempDir temp_dir_;
299   ImageCaptureDeviceManager manager_;
300   MockMTPICCameraDevice* camera_;
302   // This object needs special deletion inside the above |task_runner_|.
303   MTPDeviceDelegateImplMac* delegate_;
305   base::PlatformFileError error_;
306   base::PlatformFileInfo info_;
307   fileapi::AsyncFileUtil::EntryList file_list_;
309   base::PlatformFileError overlapped_error_;
310   fileapi::AsyncFileUtil::EntryList overlapped_file_list_;
312  private:
313   DISALLOW_COPY_AND_ASSIGN(MTPDeviceDelegateImplMacTest);
316 TEST_F(MTPDeviceDelegateImplMacTest, TestGetRootFileInfo) {
317   base::PlatformFileInfo info;
318   // Making a fresh delegate should have a single file entry for the synthetic
319   // root directory, with the name equal to the device id string.
320   EXPECT_EQ(base::PLATFORM_FILE_OK,
321             GetFileInfo(base::FilePath(kDevicePath), &info));
322   EXPECT_TRUE(info.is_directory);
323   EXPECT_EQ(base::PLATFORM_FILE_ERROR_NOT_FOUND,
324             GetFileInfo(base::FilePath("/nonexistent"), &info));
326   // Signal the delegate that no files are coming.
327   delegate_->NoMoreItems();
329   EXPECT_EQ(base::PLATFORM_FILE_OK, ReadDir(base::FilePath(kDevicePath)));
330   EXPECT_EQ(0U, file_list_.size());
333 TEST_F(MTPDeviceDelegateImplMacTest, TestOverlappedReadDir) {
334   base::Time time1 = base::Time::Now();
335   base::PlatformFileInfo info1;
336   info1.size = 1;
337   info1.is_directory = false;
338   info1.is_symbolic_link = false;
339   info1.last_modified = time1;
340   info1.last_accessed = time1;
341   info1.creation_time = time1;
342   delegate_->ItemAdded("name1", info1);
344   base::WaitableEvent wait(true, false);
346   delegate_->ReadDirectory(
347       base::FilePath(kDevicePath),
348       base::Bind(&MTPDeviceDelegateImplMacTest::OnReadDir,
349                  base::Unretained(this),
350                  &wait),
351       base::Bind(&MTPDeviceDelegateImplMacTest::OnError,
352                  base::Unretained(this),
353                  &wait));
355   delegate_->ReadDirectory(
356       base::FilePath(kDevicePath),
357       base::Bind(&MTPDeviceDelegateImplMacTest::OverlappedOnReadDir,
358                  base::Unretained(this),
359                  &wait),
360       base::Bind(&MTPDeviceDelegateImplMacTest::OverlappedOnError,
361                  base::Unretained(this),
362                  &wait));
365   // Signal the delegate that no files are coming.
366   delegate_->NoMoreItems();
368   base::RunLoop loop;
369   loop.RunUntilIdle();
370   wait.Wait();
372   EXPECT_EQ(base::PLATFORM_FILE_OK, error_);
373   EXPECT_EQ(1U, file_list_.size());
374   EXPECT_EQ(base::PLATFORM_FILE_OK, overlapped_error_);
375   EXPECT_EQ(1U, overlapped_file_list_.size());
378 TEST_F(MTPDeviceDelegateImplMacTest, TestGetFileInfo) {
379   base::Time time1 = base::Time::Now();
380   base::PlatformFileInfo info1;
381   info1.size = 1;
382   info1.is_directory = false;
383   info1.is_symbolic_link = false;
384   info1.last_modified = time1;
385   info1.last_accessed = time1;
386   info1.creation_time = time1;
387   delegate_->ItemAdded("name1", info1);
389   base::PlatformFileInfo info;
390   EXPECT_EQ(base::PLATFORM_FILE_OK,
391             GetFileInfo(base::FilePath("/ic:id/name1"), &info));
392   EXPECT_EQ(info1.size, info.size);
393   EXPECT_EQ(info1.is_directory, info.is_directory);
394   EXPECT_EQ(info1.last_modified, info.last_modified);
395   EXPECT_EQ(info1.last_accessed, info.last_accessed);
396   EXPECT_EQ(info1.creation_time, info.creation_time);
398   info1.size = 2;
399   delegate_->ItemAdded("name2", info1);
400   delegate_->NoMoreItems();
402   EXPECT_EQ(base::PLATFORM_FILE_OK,
403             GetFileInfo(base::FilePath("/ic:id/name2"), &info));
404   EXPECT_EQ(info1.size, info.size);
406   EXPECT_EQ(base::PLATFORM_FILE_OK, ReadDir(base::FilePath(kDevicePath)));
408   ASSERT_EQ(2U, file_list_.size());
409   EXPECT_EQ(time1, file_list_[0].last_modified_time);
410   EXPECT_FALSE(file_list_[0].is_directory);
411   EXPECT_EQ("name1", file_list_[0].name);
413   EXPECT_EQ(time1, file_list_[1].last_modified_time);
414   EXPECT_FALSE(file_list_[1].is_directory);
415   EXPECT_EQ("name2", file_list_[1].name);
418 TEST_F(MTPDeviceDelegateImplMacTest, TestDirectoriesAndSorting) {
419   base::Time time1 = base::Time::Now();
420   base::PlatformFileInfo info1;
421   info1.size = 1;
422   info1.is_directory = false;
423   info1.is_symbolic_link = false;
424   info1.last_modified = time1;
425   info1.last_accessed = time1;
426   info1.creation_time = time1;
427   delegate_->ItemAdded("name2", info1);
429   info1.is_directory = true;
430   delegate_->ItemAdded("dir2", info1);
431   delegate_->ItemAdded("dir1", info1);
433   info1.is_directory = false;
434   delegate_->ItemAdded("name1", info1);
435   delegate_->NoMoreItems();
437   EXPECT_EQ(base::PLATFORM_FILE_OK, ReadDir(base::FilePath(kDevicePath)));
439   ASSERT_EQ(4U, file_list_.size());
440   EXPECT_EQ("dir1", file_list_[0].name);
441   EXPECT_EQ("dir2", file_list_[1].name);
442   EXPECT_EQ(time1, file_list_[2].last_modified_time);
443   EXPECT_FALSE(file_list_[2].is_directory);
444   EXPECT_EQ("name1", file_list_[2].name);
446   EXPECT_EQ(time1, file_list_[3].last_modified_time);
447   EXPECT_FALSE(file_list_[3].is_directory);
448   EXPECT_EQ("name2", file_list_[3].name);
451 TEST_F(MTPDeviceDelegateImplMacTest, SubDirectories) {
452   base::Time time1 = base::Time::Now();
453   base::PlatformFileInfo info1;
454   info1.size = 0;
455   info1.is_directory = true;
456   info1.is_symbolic_link = false;
457   info1.last_modified = time1;
458   info1.last_accessed = time1;
459   info1.creation_time = time1;
460   delegate_->ItemAdded("dir1", info1);
462   info1.size = 1;
463   info1.is_directory = false;
464   info1.is_symbolic_link = false;
465   info1.last_modified = time1;
466   info1.last_accessed = time1;
467   info1.creation_time = time1;
468   delegate_->ItemAdded("dir1/name1", info1);
470   info1.is_directory = true;
471   info1.size = 0;
472   delegate_->ItemAdded("dir2", info1);
474   info1.is_directory = false;
475   info1.size = 1;
476   delegate_->ItemAdded("dir2/name2", info1);
478   info1.is_directory = true;
479   info1.size = 0;
480   delegate_->ItemAdded("dir2/subdir", info1);
482   info1.is_directory = false;
483   info1.size = 1;
484   delegate_->ItemAdded("dir2/subdir/name3", info1);
485   delegate_->ItemAdded("name4", info1);
487   delegate_->NoMoreItems();
489   EXPECT_EQ(base::PLATFORM_FILE_OK, ReadDir(base::FilePath(kDevicePath)));
490   ASSERT_EQ(3U, file_list_.size());
491   EXPECT_TRUE(file_list_[0].is_directory);
492   EXPECT_EQ("dir1", file_list_[0].name);
493   EXPECT_TRUE(file_list_[1].is_directory);
494   EXPECT_EQ("dir2", file_list_[1].name);
495   EXPECT_FALSE(file_list_[2].is_directory);
496   EXPECT_EQ("name4", file_list_[2].name);
498   EXPECT_EQ(base::PLATFORM_FILE_OK,
499             ReadDir(base::FilePath(kDevicePath).Append("dir1")));
500   ASSERT_EQ(1U, file_list_.size());
501   EXPECT_FALSE(file_list_[0].is_directory);
502   EXPECT_EQ("name1", file_list_[0].name);
504   EXPECT_EQ(base::PLATFORM_FILE_OK,
505             ReadDir(base::FilePath(kDevicePath).Append("dir2")));
506   ASSERT_EQ(2U, file_list_.size());
507   EXPECT_FALSE(file_list_[0].is_directory);
508   EXPECT_EQ("name2", file_list_[0].name);
509   EXPECT_TRUE(file_list_[1].is_directory);
510   EXPECT_EQ("subdir", file_list_[1].name);
512   EXPECT_EQ(base::PLATFORM_FILE_OK,
513             ReadDir(base::FilePath(kDevicePath)
514                     .Append("dir2").Append("subdir")));
515   ASSERT_EQ(1U, file_list_.size());
516   EXPECT_FALSE(file_list_[0].is_directory);
517   EXPECT_EQ("name3", file_list_[0].name);
519   EXPECT_EQ(base::PLATFORM_FILE_ERROR_NOT_FOUND,
520             ReadDir(base::FilePath(kDevicePath)
521                     .Append("dir2").Append("subdir").Append("subdir")));
522   EXPECT_EQ(base::PLATFORM_FILE_ERROR_NOT_FOUND,
523             ReadDir(base::FilePath(kDevicePath)
524                     .Append("dir3").Append("subdir")));
525   EXPECT_EQ(base::PLATFORM_FILE_ERROR_NOT_FOUND,
526             ReadDir(base::FilePath(kDevicePath).Append("dir3")));
529 TEST_F(MTPDeviceDelegateImplMacTest, TestDownload) {
530   ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
531   base::Time t1 = base::Time::Now();
532   base::PlatformFileInfo info;
533   info.size = 4;
534   info.is_directory = false;
535   info.is_symbolic_link = false;
536   info.last_modified = t1;
537   info.last_accessed = t1;
538   info.creation_time = t1;
539   std::string kTestFileName("filename");
540   base::scoped_nsobject<MockMTPICCameraFile> picture1(
541       [[MockMTPICCameraFile alloc]
542           init:base::SysUTF8ToNSString(kTestFileName)]);
543   [camera_ addMediaFile:picture1];
544   delegate_->ItemAdded(kTestFileName, info);
545   delegate_->NoMoreItems();
547   EXPECT_EQ(base::PLATFORM_FILE_OK, ReadDir(base::FilePath(kDevicePath)));
548   ASSERT_EQ(1U, file_list_.size());
549   ASSERT_EQ("filename", file_list_[0].name);
551   EXPECT_EQ(base::PLATFORM_FILE_ERROR_NOT_FOUND,
552             DownloadFile(base::FilePath("/ic:id/nonexist"),
553                          temp_dir_.path().Append("target")));
555   EXPECT_EQ(base::PLATFORM_FILE_OK,
556              DownloadFile(base::FilePath("/ic:id/filename"),
557                           temp_dir_.path().Append("target")));
558   std::string contents;
559   EXPECT_TRUE(base::ReadFileToString(temp_dir_.path().Append("target"),
560                                      &contents));
561   EXPECT_EQ(kTestFileContents, contents);