NaCl: Update revision in DEPS, r12770 -> r12773
[chromium-blink-merge.git] / chrome / browser / media_galleries / mac / mtp_device_delegate_impl_mac_unittest.mm
blob89b55f95123d929f2f9fead0c71a37716f852182
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/file.h"
10 #include "base/files/scoped_temp_dir.h"
11 #include "base/mac/cocoa_protocols.h"
12 #include "base/mac/foundation_util.h"
13 #include "base/mac/scoped_nsobject.h"
14 #include "base/message_loop/message_loop.h"
15 #include "base/run_loop.h"
16 #include "base/strings/sys_string_conversions.h"
17 #include "base/synchronization/waitable_event.h"
18 #include "base/test/sequenced_worker_pool_owner.h"
19 #include "base/threading/sequenced_worker_pool.h"
20 #include "chrome/browser/media_galleries/mac/mtp_device_delegate_impl_mac.h"
21 #include "chrome/browser/storage_monitor/image_capture_device_manager.h"
22 #include "chrome/browser/storage_monitor/test_storage_monitor.h"
23 #include "content/public/browser/browser_thread.h"
24 #include "content/public/test/test_browser_thread.h"
25 #include "testing/gtest/include/gtest/gtest.h"
27 #if !defined(MAC_OS_X_VERSION_10_7) || \
28     MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_7
30 @interface NSObject (ICCameraDeviceDelegateLionAPI)
31 - (void)deviceDidBecomeReadyWithCompleteContentCatalog:(ICDevice*)device;
32 - (void)didDownloadFile:(ICCameraFile*)file
33                   error:(NSError*)error
34                 options:(NSDictionary*)options
35             contextInfo:(void*)contextInfo;
36 @end
38 #endif  // 10.6
40 namespace {
42 const char kDeviceId[] = "id";
43 const char kDevicePath[] = "/ic:id";
44 const char kTestFileContents[] = "test";
46 }  // namespace
48 @interface MockMTPICCameraDevice : ICCameraDevice {
49  @private
50   base::scoped_nsobject<NSMutableArray> allMediaFiles_;
53 - (void)addMediaFile:(ICCameraFile*)file;
55 @end
57 @implementation MockMTPICCameraDevice
59 - (NSString*)mountPoint {
60   return @"mountPoint";
63 - (NSString*)name {
64   return @"name";
67 - (NSString*)UUIDString {
68   return base::SysUTF8ToNSString(kDeviceId);
71 - (ICDeviceType)type {
72   return ICDeviceTypeCamera;
75 - (void)requestOpenSession {
78 - (void)requestCloseSession {
81 - (NSArray*)mediaFiles {
82   return allMediaFiles_;
85 - (void)addMediaFile:(ICCameraFile*)file {
86   if (!allMediaFiles_.get())
87     allMediaFiles_.reset([[NSMutableArray alloc] init]);
88   [allMediaFiles_ addObject:file];
91 - (void)requestDownloadFile:(ICCameraFile*)file
92                     options:(NSDictionary*)options
93            downloadDelegate:(id<ICCameraDeviceDownloadDelegate>)downloadDelegate
94         didDownloadSelector:(SEL)selector
95                 contextInfo:(void*)contextInfo {
96   base::FilePath saveDir(base::SysNSStringToUTF8(
97       [[options objectForKey:ICDownloadsDirectoryURL] path]));
98   std::string saveAsFilename =
99       base::SysNSStringToUTF8([options objectForKey:ICSaveAsFilename]);
100   // It appears that the ImageCapture library adds an extension to the requested
101   // filename. Do that here to require a rename.
102   saveAsFilename += ".jpg";
103   base::FilePath toBeSaved = saveDir.Append(saveAsFilename);
104   ASSERT_EQ(static_cast<int>(strlen(kTestFileContents)),
105             file_util::WriteFile(toBeSaved, kTestFileContents,
106                                  strlen(kTestFileContents)));
108   NSMutableDictionary* returnOptions =
109       [NSMutableDictionary dictionaryWithDictionary:options];
110   [returnOptions setObject:base::SysUTF8ToNSString(saveAsFilename)
111                     forKey:ICSavedFilename];
113   [static_cast<NSObject<ICCameraDeviceDownloadDelegate>*>(downloadDelegate)
114    didDownloadFile:file
115              error:nil
116            options:returnOptions
117        contextInfo:contextInfo];
120 @end
122 @interface MockMTPICCameraFile : ICCameraFile {
123  @private
124   base::scoped_nsobject<NSString> name_;
125   base::scoped_nsobject<NSDate> date_;
128 - (id)init:(NSString*)name;
130 @end
132 @implementation MockMTPICCameraFile
134 - (id)init:(NSString*)name {
135   if ((self = [super init])) {
136     name_.reset([name retain]);
137     date_.reset([[NSDate dateWithNaturalLanguageString:@"12/12/12"] retain]);
138   }
139   return self;
142 - (NSString*)name {
143   return name_.get();
146 - (NSString*)UTI {
147   return base::mac::CFToNSCast(kUTTypeImage);
150 - (NSDate*)modificationDate {
151   return date_.get();
154 - (NSDate*)creationDate {
155   return date_.get();
158 - (off_t)fileSize {
159   return 1000;
162 @end
164 class MTPDeviceDelegateImplMacTest : public testing::Test {
165  public:
166   MTPDeviceDelegateImplMacTest() : camera_(NULL), delegate_(NULL) {}
168   virtual void SetUp() OVERRIDE {
169     ui_thread_.reset(new content::TestBrowserThread(
170         content::BrowserThread::UI, &message_loop_));
171     file_thread_.reset(new content::TestBrowserThread(
172         content::BrowserThread::FILE, &message_loop_));
173     io_thread_.reset(new content::TestBrowserThread(
174         content::BrowserThread::IO));
175     ASSERT_TRUE(io_thread_->Start());
177     TestStorageMonitor* monitor = TestStorageMonitor::CreateAndInstall();
178     manager_.SetNotifications(monitor->receiver());
180     camera_ = [MockMTPICCameraDevice alloc];
181     id<ICDeviceBrowserDelegate> delegate = manager_.device_browser();
182     [delegate deviceBrowser:nil didAddDevice:camera_ moreComing:NO];
184     delegate_ = new MTPDeviceDelegateImplMac(kDeviceId, kDevicePath);
185   }
187   virtual void TearDown() OVERRIDE {
188     id<ICDeviceBrowserDelegate> delegate = manager_.device_browser();
189     [delegate deviceBrowser:nil didRemoveDevice:camera_ moreGoing:NO];
191     delegate_->CancelPendingTasksAndDeleteDelegate();
193     TestStorageMonitor::RemoveSingleton();
195     io_thread_->Stop();
196   }
198   void OnError(base::WaitableEvent* event, base::File::Error error) {
199     error_ = error;
200     event->Signal();
201   }
203   void OverlappedOnError(base::WaitableEvent* event,
204                          base::File::Error error) {
205     overlapped_error_ = error;
206     event->Signal();
207   }
209   void OnFileInfo(base::WaitableEvent* event,
210                   const base::File::Info& info) {
211     error_ = base::File::FILE_OK;
212     info_ = info;
213     event->Signal();
214   }
216   void OnReadDir(base::WaitableEvent* event,
217                  const fileapi::AsyncFileUtil::EntryList& files,
218                  bool has_more) {
219     error_ = base::File::FILE_OK;
220     ASSERT_FALSE(has_more);
221     file_list_ = files;
222     event->Signal();
223   }
225   void OverlappedOnReadDir(base::WaitableEvent* event,
226                            const fileapi::AsyncFileUtil::EntryList& files,
227                            bool has_more) {
228     overlapped_error_ = base::File::FILE_OK;
229     ASSERT_FALSE(has_more);
230     overlapped_file_list_ = files;
231     event->Signal();
232   }
234   void OnDownload(base::WaitableEvent* event,
235                   const base::File::Info& file_info,
236                   const base::FilePath& local_path) {
237     error_ = base::File::FILE_OK;
238     event->Signal();
239   }
241   base::File::Error GetFileInfo(const base::FilePath& path,
242                                 base::File::Info* info) {
243     base::WaitableEvent wait(true, false);
244     delegate_->GetFileInfo(
245       path,
246       base::Bind(&MTPDeviceDelegateImplMacTest::OnFileInfo,
247                  base::Unretained(this),
248                  &wait),
249       base::Bind(&MTPDeviceDelegateImplMacTest::OnError,
250                  base::Unretained(this),
251                  &wait));
252     base::RunLoop loop;
253     loop.RunUntilIdle();
254     EXPECT_TRUE(wait.IsSignaled());
255     *info = info_;
256     return error_;
257   }
259   base::File::Error ReadDir(const base::FilePath& path) {
260     base::WaitableEvent wait(true, false);
261     delegate_->ReadDirectory(
262         path,
263         base::Bind(&MTPDeviceDelegateImplMacTest::OnReadDir,
264                    base::Unretained(this),
265                    &wait),
266         base::Bind(&MTPDeviceDelegateImplMacTest::OnError,
267                    base::Unretained(this),
268                    &wait));
269     base::RunLoop loop;
270     loop.RunUntilIdle();
271     wait.Wait();
272     return error_;
273   }
275   base::File::Error DownloadFile(
276       const base::FilePath& path,
277       const base::FilePath& local_path) {
278     base::WaitableEvent wait(true, false);
279     delegate_->CreateSnapshotFile(
280         path, local_path,
281         base::Bind(&MTPDeviceDelegateImplMacTest::OnDownload,
282                    base::Unretained(this),
283                    &wait),
284         base::Bind(&MTPDeviceDelegateImplMacTest::OnError,
285                    base::Unretained(this),
286                    &wait));
287     base::RunLoop loop;
288     loop.RunUntilIdle();
289     wait.Wait();
290     return error_;
291   }
293  protected:
294   base::MessageLoopForUI message_loop_;
295   // Note: threads must be made in this order: UI > FILE > IO
296   scoped_ptr<content::TestBrowserThread> ui_thread_;
297   scoped_ptr<content::TestBrowserThread> file_thread_;
298   scoped_ptr<content::TestBrowserThread> io_thread_;
299   base::ScopedTempDir temp_dir_;
300   ImageCaptureDeviceManager manager_;
301   MockMTPICCameraDevice* camera_;
303   // This object needs special deletion inside the above |task_runner_|.
304   MTPDeviceDelegateImplMac* delegate_;
306   base::File::Error error_;
307   base::File::Info info_;
308   fileapi::AsyncFileUtil::EntryList file_list_;
310   base::File::Error overlapped_error_;
311   fileapi::AsyncFileUtil::EntryList overlapped_file_list_;
313  private:
314   DISALLOW_COPY_AND_ASSIGN(MTPDeviceDelegateImplMacTest);
317 TEST_F(MTPDeviceDelegateImplMacTest, TestGetRootFileInfo) {
318   base::File::Info info;
319   // Making a fresh delegate should have a single file entry for the synthetic
320   // root directory, with the name equal to the device id string.
321   EXPECT_EQ(base::File::FILE_OK,
322             GetFileInfo(base::FilePath(kDevicePath), &info));
323   EXPECT_TRUE(info.is_directory);
324   EXPECT_EQ(base::File::FILE_ERROR_NOT_FOUND,
325             GetFileInfo(base::FilePath("/nonexistent"), &info));
327   // Signal the delegate that no files are coming.
328   delegate_->NoMoreItems();
330   EXPECT_EQ(base::File::FILE_OK, ReadDir(base::FilePath(kDevicePath)));
331   EXPECT_EQ(0U, file_list_.size());
334 TEST_F(MTPDeviceDelegateImplMacTest, TestOverlappedReadDir) {
335   base::Time time1 = base::Time::Now();
336   base::File::Info info1;
337   info1.size = 1;
338   info1.is_directory = false;
339   info1.is_symbolic_link = false;
340   info1.last_modified = time1;
341   info1.last_accessed = time1;
342   info1.creation_time = time1;
343   delegate_->ItemAdded("name1", info1);
345   base::WaitableEvent wait(true, false);
347   delegate_->ReadDirectory(
348       base::FilePath(kDevicePath),
349       base::Bind(&MTPDeviceDelegateImplMacTest::OnReadDir,
350                  base::Unretained(this),
351                  &wait),
352       base::Bind(&MTPDeviceDelegateImplMacTest::OnError,
353                  base::Unretained(this),
354                  &wait));
356   delegate_->ReadDirectory(
357       base::FilePath(kDevicePath),
358       base::Bind(&MTPDeviceDelegateImplMacTest::OverlappedOnReadDir,
359                  base::Unretained(this),
360                  &wait),
361       base::Bind(&MTPDeviceDelegateImplMacTest::OverlappedOnError,
362                  base::Unretained(this),
363                  &wait));
366   // Signal the delegate that no files are coming.
367   delegate_->NoMoreItems();
369   base::RunLoop loop;
370   loop.RunUntilIdle();
371   wait.Wait();
373   EXPECT_EQ(base::File::FILE_OK, error_);
374   EXPECT_EQ(1U, file_list_.size());
375   EXPECT_EQ(base::File::FILE_OK, overlapped_error_);
376   EXPECT_EQ(1U, overlapped_file_list_.size());
379 TEST_F(MTPDeviceDelegateImplMacTest, TestGetFileInfo) {
380   base::Time time1 = base::Time::Now();
381   base::File::Info info1;
382   info1.size = 1;
383   info1.is_directory = false;
384   info1.is_symbolic_link = false;
385   info1.last_modified = time1;
386   info1.last_accessed = time1;
387   info1.creation_time = time1;
388   delegate_->ItemAdded("name1", info1);
390   base::File::Info info;
391   EXPECT_EQ(base::File::FILE_OK,
392             GetFileInfo(base::FilePath("/ic:id/name1"), &info));
393   EXPECT_EQ(info1.size, info.size);
394   EXPECT_EQ(info1.is_directory, info.is_directory);
395   EXPECT_EQ(info1.last_modified, info.last_modified);
396   EXPECT_EQ(info1.last_accessed, info.last_accessed);
397   EXPECT_EQ(info1.creation_time, info.creation_time);
399   info1.size = 2;
400   delegate_->ItemAdded("name2", info1);
401   delegate_->NoMoreItems();
403   EXPECT_EQ(base::File::FILE_OK,
404             GetFileInfo(base::FilePath("/ic:id/name2"), &info));
405   EXPECT_EQ(info1.size, info.size);
407   EXPECT_EQ(base::File::FILE_OK, ReadDir(base::FilePath(kDevicePath)));
409   ASSERT_EQ(2U, file_list_.size());
410   EXPECT_EQ(time1, file_list_[0].last_modified_time);
411   EXPECT_FALSE(file_list_[0].is_directory);
412   EXPECT_EQ("name1", file_list_[0].name);
414   EXPECT_EQ(time1, file_list_[1].last_modified_time);
415   EXPECT_FALSE(file_list_[1].is_directory);
416   EXPECT_EQ("name2", file_list_[1].name);
419 TEST_F(MTPDeviceDelegateImplMacTest, TestDirectoriesAndSorting) {
420   base::Time time1 = base::Time::Now();
421   base::File::Info info1;
422   info1.size = 1;
423   info1.is_directory = false;
424   info1.is_symbolic_link = false;
425   info1.last_modified = time1;
426   info1.last_accessed = time1;
427   info1.creation_time = time1;
428   delegate_->ItemAdded("name2", info1);
430   info1.is_directory = true;
431   delegate_->ItemAdded("dir2", info1);
432   delegate_->ItemAdded("dir1", info1);
434   info1.is_directory = false;
435   delegate_->ItemAdded("name1", info1);
436   delegate_->NoMoreItems();
438   EXPECT_EQ(base::File::FILE_OK, ReadDir(base::FilePath(kDevicePath)));
440   ASSERT_EQ(4U, file_list_.size());
441   EXPECT_EQ("dir1", file_list_[0].name);
442   EXPECT_EQ("dir2", file_list_[1].name);
443   EXPECT_EQ(time1, file_list_[2].last_modified_time);
444   EXPECT_FALSE(file_list_[2].is_directory);
445   EXPECT_EQ("name1", file_list_[2].name);
447   EXPECT_EQ(time1, file_list_[3].last_modified_time);
448   EXPECT_FALSE(file_list_[3].is_directory);
449   EXPECT_EQ("name2", file_list_[3].name);
452 TEST_F(MTPDeviceDelegateImplMacTest, SubDirectories) {
453   base::Time time1 = base::Time::Now();
454   base::File::Info info1;
455   info1.size = 0;
456   info1.is_directory = true;
457   info1.is_symbolic_link = false;
458   info1.last_modified = time1;
459   info1.last_accessed = time1;
460   info1.creation_time = time1;
461   delegate_->ItemAdded("dir1", info1);
463   info1.size = 1;
464   info1.is_directory = false;
465   info1.is_symbolic_link = false;
466   info1.last_modified = time1;
467   info1.last_accessed = time1;
468   info1.creation_time = time1;
469   delegate_->ItemAdded("dir1/name1", info1);
471   info1.is_directory = true;
472   info1.size = 0;
473   delegate_->ItemAdded("dir2", info1);
475   info1.is_directory = false;
476   info1.size = 1;
477   delegate_->ItemAdded("dir2/name2", info1);
479   info1.is_directory = true;
480   info1.size = 0;
481   delegate_->ItemAdded("dir2/subdir", info1);
483   info1.is_directory = false;
484   info1.size = 1;
485   delegate_->ItemAdded("dir2/subdir/name3", info1);
486   delegate_->ItemAdded("name4", info1);
488   delegate_->NoMoreItems();
490   EXPECT_EQ(base::File::FILE_OK, ReadDir(base::FilePath(kDevicePath)));
491   ASSERT_EQ(3U, file_list_.size());
492   EXPECT_TRUE(file_list_[0].is_directory);
493   EXPECT_EQ("dir1", file_list_[0].name);
494   EXPECT_TRUE(file_list_[1].is_directory);
495   EXPECT_EQ("dir2", file_list_[1].name);
496   EXPECT_FALSE(file_list_[2].is_directory);
497   EXPECT_EQ("name4", file_list_[2].name);
499   EXPECT_EQ(base::File::FILE_OK,
500             ReadDir(base::FilePath(kDevicePath).Append("dir1")));
501   ASSERT_EQ(1U, file_list_.size());
502   EXPECT_FALSE(file_list_[0].is_directory);
503   EXPECT_EQ("name1", file_list_[0].name);
505   EXPECT_EQ(base::File::FILE_OK,
506             ReadDir(base::FilePath(kDevicePath).Append("dir2")));
507   ASSERT_EQ(2U, file_list_.size());
508   EXPECT_FALSE(file_list_[0].is_directory);
509   EXPECT_EQ("name2", file_list_[0].name);
510   EXPECT_TRUE(file_list_[1].is_directory);
511   EXPECT_EQ("subdir", file_list_[1].name);
513   EXPECT_EQ(base::File::FILE_OK,
514             ReadDir(base::FilePath(kDevicePath)
515                     .Append("dir2").Append("subdir")));
516   ASSERT_EQ(1U, file_list_.size());
517   EXPECT_FALSE(file_list_[0].is_directory);
518   EXPECT_EQ("name3", file_list_[0].name);
520   EXPECT_EQ(base::File::FILE_ERROR_NOT_FOUND,
521             ReadDir(base::FilePath(kDevicePath)
522                     .Append("dir2").Append("subdir").Append("subdir")));
523   EXPECT_EQ(base::File::FILE_ERROR_NOT_FOUND,
524             ReadDir(base::FilePath(kDevicePath)
525                     .Append("dir3").Append("subdir")));
526   EXPECT_EQ(base::File::FILE_ERROR_NOT_FOUND,
527             ReadDir(base::FilePath(kDevicePath).Append("dir3")));
530 TEST_F(MTPDeviceDelegateImplMacTest, TestDownload) {
531   ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
532   base::Time t1 = base::Time::Now();
533   base::File::Info info;
534   info.size = 4;
535   info.is_directory = false;
536   info.is_symbolic_link = false;
537   info.last_modified = t1;
538   info.last_accessed = t1;
539   info.creation_time = t1;
540   std::string kTestFileName("filename");
541   base::scoped_nsobject<MockMTPICCameraFile> picture1(
542       [[MockMTPICCameraFile alloc]
543           init:base::SysUTF8ToNSString(kTestFileName)]);
544   [camera_ addMediaFile:picture1];
545   delegate_->ItemAdded(kTestFileName, info);
546   delegate_->NoMoreItems();
548   EXPECT_EQ(base::File::FILE_OK, ReadDir(base::FilePath(kDevicePath)));
549   ASSERT_EQ(1U, file_list_.size());
550   ASSERT_EQ("filename", file_list_[0].name);
552   EXPECT_EQ(base::File::FILE_ERROR_NOT_FOUND,
553             DownloadFile(base::FilePath("/ic:id/nonexist"),
554                          temp_dir_.path().Append("target")));
556   EXPECT_EQ(base::File::FILE_OK,
557             DownloadFile(base::FilePath("/ic:id/filename"),
558                          temp_dir_.path().Append("target")));
559   std::string contents;
560   EXPECT_TRUE(base::ReadFileToString(temp_dir_.path().Append("target"),
561                                      &contents));
562   EXPECT_EQ(kTestFileContents, contents);