PageRenderTime 150ms CodeModel.GetById 17ms app.highlight 126ms RepoModel.GetById 2ms app.codeStats 0ms

/core/externals/update-engine/Core/KSDownloadActionTest.m

http://macfuse.googlecode.com/
Objective C | 730 lines | 499 code | 131 blank | 100 comment | 20 complexity | 455270f784124200312a422061bd3eae MD5 | raw file
  1// Copyright 2008 Google Inc.
  2//
  3// Licensed under the Apache License, Version 2.0 (the "License");
  4// you may not use this file except in compliance with the License.
  5// You may obtain a copy of the License at
  6//
  7//     http://www.apache.org/licenses/LICENSE-2.0
  8//
  9// Unless required by applicable law or agreed to in writing, software
 10// distributed under the License is distributed on an "AS IS" BASIS,
 11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 12// See the License for the specific language governing permissions and
 13// limitations under the License.
 14
 15#import <SenTestingKit/SenTestingKit.h>
 16#import "GTMBase64.h"
 17#import "KSActionPipe.h"
 18#import "KSActionProcessor.h"
 19#import "KSDownloadAction.h"
 20#import "NSData+Hash.h"
 21#import <unistd.h>
 22#import <sys/utsname.h>
 23#import <sys/stat.h>
 24
 25
 26// Let us poke into the action's fiddly bits without compiler complaint.
 27@interface KSDownloadAction (TestingFriends)
 28- (NSString *)ksurlDirectoryName;
 29- (NSString *)ksurlValidatedDirectory;
 30- (NSString *)ksurlPath;
 31- (void)markProgress:(float)progress;
 32+ (NSString *)downloadDirectoryIdentifier;
 33+ (NSString *)defaultDownloadDirectory;
 34+ (NSString *)writableTempNameForPath:(NSString *)path inDomain:(int)domain;
 35+ (NSString *)cacheSubfolderName;
 36@end
 37
 38
 39// Action class where we can control the return value from
 40// +cacheSubfolderName, to make sure that +writableTempNameForPath
 41// does the right thing, without the test clobbering existing
 42// directories.
 43@interface KSDownloadDirectoryAction : KSDownloadAction
 44+ (void)setCacheSubfolderName:(NSString *)subfolderName;
 45+ (void)setDownloadDirectoryIdentifier:(NSString *)identifier;
 46@end
 47
 48@implementation KSDownloadDirectoryAction
 49
 50static NSString *g_subfolderName;
 51static NSString *g_directoryIdentifier;
 52
 53+ (void)setCacheSubfolderName:(NSString *)name {
 54  [g_subfolderName autorelease];
 55  g_subfolderName = [name copy];
 56}
 57
 58+ (NSString *)cacheSubfolderName {
 59  return g_subfolderName ? g_subfolderName : [super cacheSubfolderName];
 60}
 61
 62+ (void)setDownloadDirectoryIdentifier:(NSString *)identifier {
 63  [g_directoryIdentifier autorelease];
 64  g_directoryIdentifier = [identifier copy];
 65}
 66
 67+ (NSString *)downloadDirectoryIdentifier {
 68  return g_directoryIdentifier ?
 69    g_directoryIdentifier : [super downloadDirectoryIdentifier];
 70}
 71
 72@end
 73
 74
 75@interface KSDownloadActionTest : SenTestCase {
 76 @private
 77  NSString *tempName_;
 78}
 79@end
 80
 81
 82// ----------------------------------------------------------------
 83// Implement KSDownloadActionDelegateMethods.  Keep track of progress.
 84@interface KSDownloadProgressCounter : NSObject {
 85  NSMutableArray *progressArray_;
 86}
 87+ (id)counter;
 88- (NSArray *)progressArray;
 89@end
 90
 91@implementation KSDownloadProgressCounter
 92
 93+ (id)counter {
 94  return [[[self alloc] init] autorelease];
 95}
 96
 97- (id)init {
 98  if ((self = [super init])) {
 99    progressArray_ = [[NSMutableArray array] retain];
100  }
101  return self;
102}
103
104- (void)dealloc {
105  [progressArray_ release];
106  [super dealloc];
107}
108
109- (NSArray *)progressArray {
110  return progressArray_;
111}
112
113- (void)processor:(KSActionProcessor *)processor
114    runningAction:(KSAction *)action
115         progress:(float)progress {
116  [progressArray_ addObject:[NSNumber numberWithFloat:progress]];
117}
118
119@end
120
121// ----------------------------------------------------------------
122// Just like a KSDownloadAction but lets us specify the directory
123// where ksurl will be created.
124@interface KSDownloadActionWithDirectory : KSDownloadAction {
125  NSString *directory_;
126}
127- (id)initWithURL:(NSURL *)url
128             size:(unsigned long long)size
129             hash:(NSString *)hash
130             path:(NSString *)path
131      ksdirectory:(NSString *)directory;
132- (NSString *)ksurlDirectoryName;
133@end
134
135@implementation KSDownloadActionWithDirectory
136
137- (id)initWithURL:(NSURL *)url
138             size:(unsigned long long)size
139             hash:(NSString *)hash
140             path:(NSString *)path
141      ksdirectory:(NSString *)directory {
142  if ((self = [super initWithURL:url size:size hash:hash path:path])) {
143    directory_ = [directory copy];
144  }
145  return self;
146}
147
148- (void)dealloc {
149  [directory_ release];
150  [super dealloc];
151}
152
153- (NSString *)ksurlDirectoryName {
154  return directory_;
155}
156
157@end
158
159// ----------------------------------------------------------------
160
161//
162// In this file we use file:// URLs for testing so that we don't have any
163// network dependencies for this unit test.
164//
165
166@implementation KSDownloadActionTest
167
168- (void)setUp {
169  tempName_ = [[NSString alloc] initWithFormat:
170               @"/tmp/KSDownloadActionUnitTest-%x", geteuid()];
171  [[NSFileManager defaultManager] removeFileAtPath:tempName_ handler:nil];
172}
173
174- (void)tearDown {
175  [[NSFileManager defaultManager] removeFileAtPath:tempName_ handler:nil];
176}
177
178- (void)loopUntilDone:(KSAction *)action seconds:(int)seconds {
179  int count = seconds * 5;
180  while ([action isRunning] && (count > 0)) {
181    NSDate *quick = [NSDate dateWithTimeIntervalSinceNow:0.2];
182    [[NSRunLoop currentRunLoop] runUntilDate:quick];
183    count--;
184  }
185  STAssertFalse([action isRunning], nil);
186}
187
188- (void)loopUntilDone:(KSAction *)action {
189  [self loopUntilDone:action seconds:2];
190}
191
192- (void)testCreation {
193  KSDownloadAction *download = nil;
194
195  download = [[KSDownloadAction alloc] init];
196  STAssertNil(download, nil);
197
198  download = [[KSDownloadAction alloc] initWithURL:nil
199                                              size:nil
200                                              hash:nil
201                                              path:nil];
202  STAssertNil(download, nil);
203
204  NSURL *url = [NSURL URLWithString:@"http://www.google.com"];
205  download = [[KSDownloadAction alloc] initWithURL:url
206                                              size:1
207                                              hash:@"not nil"
208                                              path:@"/tmp/Google"];
209  STAssertNotNil(download, nil);
210  STAssertTrue([[download description] length] > 1, nil);
211  [download release];
212
213  // Make sure this convenience method works and that it gives us a nice
214  // default download directory (since we didn't specify one).
215  download = [KSDownloadAction actionWithURL:url
216                                        size:1
217                                        hash:@"not nil"
218                                        name:@"Google"];
219  STAssertNotNil(download, nil);
220
221  NSString *cachePath = [@"~/Library/Caches/com.google.UpdateEngine.Framework."
222                         stringByExpandingTildeInPath];
223  // We do a range check instead of a prefix check because home directories that
224  // are symlinked to other weird places (e.g., /Volumes/Users) may not resolve
225  // correctly.
226  NSRange range = [[download path] rangeOfString:cachePath];
227  STAssertTrue(range.location != NSNotFound, nil);
228}
229
230- (void)testAccessors {
231  NSURL *url = [NSURL URLWithString:@"http://www.google.com"];
232  KSDownloadAction *download = nil;
233  download = [[KSDownloadAction alloc] initWithURL:url
234                                              size:1
235                                              hash:@"not nil"
236                                              path:@"/tmp/Google"];
237  [download autorelease];
238  STAssertNotNil(download, nil);
239
240  STAssertEqualObjects(url, [download url], nil);
241  STAssertEqualObjects(@"not nil", [download hash], nil);
242  STAssertEqualObjects(@"/tmp/Google", [download path], nil);
243  STAssertEquals(1ULL, [download size], nil);
244
245  // The download path should be nil until a successful download
246  STAssertNil([[download outPipe] contents], nil);
247}
248
249- (void)testDownloadWithBadHash {
250  // To avoid network issues screwing up the tests, we'll use file: URLs
251  NSURL *url = [NSURL URLWithString:@"file:///etc/passwd"];
252
253  KSDownloadAction *download = [[[KSDownloadAction alloc] initWithURL:url
254                                                                 size:1
255                                                                 hash:@"bad hash value"
256                                                                 path:tempName_] autorelease];
257  STAssertNotNil(download, nil);
258  STAssertNil([[download outPipe] contents], nil);
259
260  // Create an action processor and process the download
261  KSActionProcessor *ap = [[[KSActionProcessor alloc] init] autorelease];
262  STAssertNotNil(ap, nil);
263
264  [ap enqueueAction:download];
265  [ap startProcessing];  // Starts action
266
267  [self loopUntilDone:download];
268
269  // We didn't provide a valid hash value, so the path should still be nil.
270  STAssertTrue([download isRunning] == NO, nil);  // make sure we're done
271  STAssertNil([[download outPipe] contents], nil);
272}
273
274// Return a good file URL download with good hash, size
275- (KSDownloadAction *)goodDownloadActionWithFile:(NSString *)file {
276  NSURL *url = [NSURL fileURLWithPath:file];
277  NSData *data = [NSData dataWithContentsOfFile:file];
278  NSData *dhash = [data SHA1Hash];
279  NSString *hash = [GTMBase64 stringByEncodingData:dhash];
280
281  unsigned long long realSize =
282  [[[NSFileManager defaultManager] fileAttributesAtPath:file
283                                           traverseLink:NO] fileSize];
284
285  KSDownloadAction *download = [[[KSDownloadAction alloc] initWithURL:url
286                                                                 size:realSize
287                                                                 hash:hash
288                                                                 path:tempName_] autorelease];
289  STAssertNotNil(download, nil);
290  STAssertNil([[download outPipe] contents], nil);
291  return download;
292}
293
294- (void)testDownloadWithGoodHash {
295  // To avoid network issues screwing up the tests, we'll use file: URLs
296  KSDownloadAction *download = [self goodDownloadActionWithFile:@"/etc/passwd"];
297
298  // Create an action processor and process the download
299  KSActionProcessor *ap = [[[KSActionProcessor alloc] init] autorelease];
300  STAssertNotNil(ap, nil);
301
302  [ap enqueueAction:download];
303  [ap startProcessing];  // Starts action
304
305  [self loopUntilDone:download];
306
307  STAssertTrue([download isRunning] == NO, nil);  // make sure we're done
308  STAssertNotNil([[download outPipe] contents], nil);
309}
310
311- (void)testDownloadWithBadURL {
312  // To avoid network issues screwing up the tests, we'll use file: URLs
313  NSURL *url = [NSURL URLWithString:@"file:///path/to/fake/file"];
314
315  KSDownloadAction *download = [[[KSDownloadAction alloc] initWithURL:url
316                                                                 size:1
317                                                                 hash:@"bad hash value"
318                                                                 path:tempName_] autorelease];
319  STAssertNotNil(download, nil);
320  STAssertNil([[download outPipe] contents], nil);
321
322  // Create an action processor and process the download
323  KSActionProcessor *ap = [[[KSActionProcessor alloc] init] autorelease];
324  STAssertNotNil(ap, nil);
325
326  [ap enqueueAction:download];
327  [ap startProcessing];  // Starts action
328
329  [self loopUntilDone:download];
330
331  // We didn't provide a valid hash value, so the path should still be nil.
332  STAssertTrue([download isRunning] == NO, nil);  // make sure we're done
333  STAssertNil([[download outPipe] contents], nil);
334}
335
336- (void)testShortCircuitDownload {
337
338  //
339  // 1. Predownload the file so that it will be cached and the short circuit
340  //    will work below.
341  //
342
343  // To avoid network issues screwing up the tests, we'll use file: URLs
344  NSURL *url = [NSURL URLWithString:@"file:///etc/passwd"];
345  NSData *data = [NSData dataWithContentsOfFile:@"/etc/passwd"];
346  NSData *dhash = [data SHA1Hash];
347  NSString *hash = [GTMBase64 stringByEncodingData:dhash];
348  unsigned long long realSize =
349    [[[NSFileManager defaultManager] fileAttributesAtPath:@"/etc/passwd"
350                                             traverseLink:NO] fileSize];
351  STAssertTrue(realSize > 0, nil);
352
353  // download file must not already exist so that we can ensure that we're not
354  // short circuited on the first run.
355  [[NSFileManager defaultManager] removeFileAtPath:tempName_
356                                           handler:nil];
357
358  KSDownloadAction *download = [[[KSDownloadAction alloc] initWithURL:url
359                                                                 size:realSize
360                                                                 hash:hash
361                                                                 path:tempName_] autorelease];
362  STAssertNotNil(download, nil);
363  STAssertNil([[download outPipe] contents], nil);
364
365  // Create an action processor and process the download
366  KSActionProcessor *ap = [[[KSActionProcessor alloc] init] autorelease];
367  STAssertNotNil(ap, nil);
368
369  [ap enqueueAction:download];
370  [ap startProcessing];  // Starts action
371  STAssertNil([[download outPipe] contents], nil);
372
373  [self loopUntilDone:download];
374
375  STAssertTrue([download isRunning] == NO, nil);  // make sure we're done
376  STAssertNotNil([[download outPipe] contents], nil);
377  STAssertTrue([[[download outPipe] contents] isEqual:tempName_], nil);
378
379  //
380  // 2. Now that we know the file is already downloaded, let's make sure our
381  //    short circuit download works by starting the download but not spinning
382  //    the runloop.
383  //
384
385  download = [[[KSDownloadAction alloc] initWithURL:url
386                                               size:realSize
387                                               hash:hash
388                                               path:tempName_] autorelease];
389  STAssertNotNil(download, nil);
390  STAssertNil([[download outPipe] contents], nil);
391
392  // Create an action processor and process the download
393  ap = [[[KSActionProcessor alloc] init] autorelease];
394  STAssertNotNil(ap, nil);
395
396  [ap enqueueAction:download];
397  [ap startProcessing];  // Starts action
398
399  // Short-circuit download should be done already (w/o a runloop spin)
400
401  STAssertTrue([download isRunning] == NO, nil);  // make sure we're done
402  STAssertNotNil([[download outPipe] contents], nil);
403  STAssertTrue([[[download outPipe] contents] isEqual:tempName_], nil);
404}
405
406// Return a basic KSDownloadAction for whatever.
407- (KSDownloadAction *)basicDownload {
408  NSURL *url = [NSURL URLWithString:@"file:///etc/passwd"];
409  KSDownloadAction *download = [[[KSDownloadAction alloc] initWithURL:url
410                                                                 size:1
411                                                                 hash:@"bad hash value"
412                                                                 path:tempName_] autorelease];
413  STAssertNotNil(download, nil);
414  return download;
415}
416
417- (void)testKsurlName {
418  KSDownloadAction *download = [self basicDownload];
419  NSString *dirname = [download ksurlDirectoryName];
420
421  // Sanity check path
422  STAssertTrue([dirname length] > 1, nil);
423  NSString *topDirectory = [[dirname pathComponents] objectAtIndex:0];
424  BOOL isDir = NO;
425  STAssertTrue([[NSFileManager defaultManager] fileExistsAtPath:topDirectory
426                                                    isDirectory:&isDir], nil);
427  STAssertTrue(isDir, nil);
428}
429
430// Return a basic KSDownloadActionWithDirectory for whatever.
431- (KSDownloadAction *)basicDownloadWithDirectory:(NSString *)dir {
432  NSURL *url = [NSURL URLWithString:@"file:///etc/passwd"];
433  KSDownloadAction *download = [[[KSDownloadActionWithDirectory alloc]
434                                  initWithURL:url
435                                         size:1
436                                         hash:@"bad hash value"
437                                         path:tempName_
438                                  ksdirectory:dir] autorelease];
439  STAssertNotNil(download, nil);
440  return download;
441}
442
443// Helper to confirm the parent directory of ksurl is valid.
444- (void)confirmValidatedDirectory:(KSDownloadAction *)download {
445  NSNumber *properPermission = [NSNumber numberWithUnsignedLong:0755];
446  NSDictionary *attr = nil;
447
448  STAssertNotNil(download, nil);
449  NSString *dir = [[download ksurlPath] stringByDeletingLastPathComponent];
450  STAssertNotNil(dir, nil);
451
452  attr = [[NSFileManager defaultManager] fileAttributesAtPath:dir
453                                                 traverseLink:NO];
454  STAssertTrue([[attr objectForKey:NSFileOwnerAccountID]
455                   isEqual:[NSNumber numberWithUnsignedLong:(long)geteuid()]],
456               nil);
457  STAssertTrue([[attr objectForKey:NSFilePosixPermissions]
458                   isEqual:properPermission], nil);
459}
460
461- (void)testKsurlValidatedDirectory {
462  NSString *tempdir = [NSString stringWithFormat:@"/tmp/ksda-%d", geteuid()];
463  KSDownloadAction *download = nil;
464
465  // test 1: doesn't exist yet.
466  [[NSFileManager defaultManager] removeFileAtPath:tempdir handler:nil];
467  download = [self basicDownloadWithDirectory:tempdir];
468  [self confirmValidatedDirectory:download];
469
470  // test 2: already exists, correct permission
471  download = [self basicDownloadWithDirectory:tempdir];
472  [self confirmValidatedDirectory:download];
473
474  // test 3: already exists, wrong permission
475  [[NSFileManager defaultManager] removeFileAtPath:tempdir handler:nil];
476  NSNumber *wrongPermission = [NSNumber numberWithUnsignedLong:0777];
477  NSDictionary *attr = [NSDictionary dictionaryWithObject:wrongPermission
478                                                   forKey:NSFilePosixPermissions];
479  [[NSFileManager defaultManager] createDirectoryAtPath:tempdir
480                                             attributes:attr];
481  download = [self basicDownloadWithDirectory:tempdir];
482  [self confirmValidatedDirectory:download];
483
484  // prevent Alex from getting mad
485  [[NSFileManager defaultManager] removeFileAtPath:tempdir handler:nil];
486}
487
488- (void)testKsurlPath {
489  KSDownloadAction *download = [self basicDownload];
490  NSString *ksurl = [download ksurlPath];
491
492  STAssertTrue([[NSFileManager defaultManager] isExecutableFileAtPath:ksurl], nil);
493  NSDictionary *attr = [[NSFileManager defaultManager] fileAttributesAtPath:ksurl
494                                                               traverseLink:NO];
495  STAssertNotNil(ksurl, nil);
496  NSNumber *uidNumber = [attr objectForKey:NSFileOwnerAccountID];
497  STAssertNotNil(uidNumber, nil);
498  STAssertTrue([uidNumber isEqual:[NSNumber numberWithUnsignedLong:(long)geteuid()]], nil);
499
500  NSNumber *properPermission = [NSNumber numberWithUnsignedLong:0755];
501  NSNumber *currentPermission = [attr objectForKey:NSFilePosixPermissions];
502  STAssertTrue([properPermission isEqual:currentPermission], nil);
503}
504
505- (void)testBasicProgress {
506  KSDownloadAction *download = [self basicDownload];
507  KSDownloadProgressCounter *progressCounter = [KSDownloadProgressCounter counter];
508
509  // We need to put the download action on a processor because the progress
510  // is relayed via the processor
511  KSActionProcessor *processor = [[[KSActionProcessor alloc ]
512                                   initWithDelegate:progressCounter] autorelease];
513  [processor enqueueAction:download];
514  STAssertEqualsWithAccuracy([processor progress], 0.0f, 0.01, nil);
515
516  STAssertTrue([[progressCounter progressArray] count] == 0, nil);
517  [download markProgress:0.0];
518  [download markProgress:0.7];
519  [download markProgress:1.0];
520  STAssertTrue([[progressCounter progressArray] count] == 3, nil);
521
522  STAssertEqualsWithAccuracy([processor progress], 1.0f, 0.01, nil);
523}
524
525- (void)testProgress {
526  // To avoid network issues screwing up the tests, we'll use file: URLs.
527  // Find a file we know we can read without issue.  Some continuous build
528  // systems throw errors when trying to read from system files.
529  NSBundle *me = [NSBundle bundleForClass:[self class]];
530  NSString *file = [me executablePath];
531  KSDownloadAction *download = [self goodDownloadActionWithFile:file];
532
533  // keep track of status
534  KSDownloadProgressCounter *progressCounter = [KSDownloadProgressCounter counter];
535
536  STAssertTrue([[progressCounter progressArray] count] == 0, nil);
537
538  // Create an action processor and process the download
539  KSActionProcessor *ap = [[[KSActionProcessor alloc]
540                            initWithDelegate:progressCounter] autorelease];
541  STAssertNotNil(ap, nil);
542
543  [ap enqueueAction:download];
544  [ap startProcessing];  // Starts action
545
546  [self loopUntilDone:download seconds:10];
547  STAssertTrue([download isRunning] == NO, nil);  // make sure we're done
548
549  // Want more than nothing...
550  STAssertTrue([[progressCounter progressArray] count] >= 1, nil);
551
552  // confirm we got some good lookin' status (10.5 only)
553  // No incremental progress on 10.4 for file:// URLs :-(
554  // 10.4.11: relase is 8.11; 10.5.3: release is 9.3
555  struct utsname name;
556  if ((uname(&name) == 0) &&
557      (name.release[0] != '8')) {
558    STAssertTrue([[progressCounter progressArray] count] > 8, nil);
559  }
560
561  NSNumber *num;
562  NSEnumerator *penum = [[progressCounter progressArray] objectEnumerator];
563  float last = 0.0;
564  while ((num = [penum nextObject])) {
565    STAssertTrue([num floatValue] >= last, nil);
566    last = [num floatValue];
567  }
568  STAssertTrue(last <= 1.0, nil);
569}
570
571- (BOOL)verifyDownloadDirectoryPermissions:(NSString *)path {
572  NSFileManager *fm = [NSFileManager defaultManager];
573  mode_t filePerms =
574    [[fm fileAttributesAtPath:path traverseLink:YES] filePosixPermissions];
575  BOOL success = YES;
576  if (!(filePerms & S_IRWXU)) success = NO;  // RWX by owner.
577  if ((filePerms & (S_IWGRP | S_IWOTH))) success = NO;  // Others can't write.
578  return success;
579}
580
581- (void)testDefaultDownloadDirectoryPermissions {
582  NSFileManager *fm = [NSFileManager defaultManager];
583  NSString *path = [KSDownloadAction defaultDownloadDirectory];
584  STAssertNotNil(path, nil);
585
586  NSString *ddd;
587
588  // Make sure default download directory permissions are sane.
589  [KSDownloadDirectoryAction setDownloadDirectoryIdentifier:@"hoff"];
590  ddd = [KSDownloadDirectoryAction defaultDownloadDirectory];
591  STAssertTrue([self verifyDownloadDirectoryPermissions:ddd], nil);
592
593  // Change the perms to no access, and make sure they get fixed.
594  int result = chmod([ddd fileSystemRepresentation], 0000);
595  STAssertEquals(result, 0, nil);
596  ddd = [KSDownloadDirectoryAction defaultDownloadDirectory];
597  STAssertTrue([self verifyDownloadDirectoryPermissions:ddd], nil);
598
599  // Change the perms to be maximally promiscuous.
600  result = chmod([ddd fileSystemRepresentation], 0777);
601  STAssertEquals(result, 0, nil);
602  ddd = [KSDownloadDirectoryAction defaultDownloadDirectory];
603  STAssertTrue([self verifyDownloadDirectoryPermissions:ddd], nil);
604
605  // Remove the directory, and bash the parent directory's permissions to
606  // no access.
607  NSString *parentPath = [ddd stringByDeletingLastPathComponent];
608  STAssertTrue([fm removeFileAtPath:ddd handler:nil], nil);
609  result = chmod([parentPath fileSystemRepresentation], 0000);
610  ddd = [KSDownloadDirectoryAction defaultDownloadDirectory];
611  STAssertTrue([self verifyDownloadDirectoryPermissions:parentPath], nil);
612  STAssertTrue([self verifyDownloadDirectoryPermissions:ddd], nil);
613
614  // Remove the directory, and bash the parent directory's permissions to
615  // be maximally promiscuous.
616  STAssertTrue([fm removeFileAtPath:ddd handler:nil], nil);
617  result = chmod([parentPath fileSystemRepresentation], 0777);
618  ddd = [KSDownloadDirectoryAction defaultDownloadDirectory];
619  STAssertTrue([self verifyDownloadDirectoryPermissions:parentPath], nil);
620  STAssertTrue([self verifyDownloadDirectoryPermissions:ddd], nil);
621
622  // Cleanup.  Remove the download directory, and its parent.
623  STAssertTrue([fm removeFileAtPath:ddd handler:nil], nil);
624  STAssertTrue([fm removeFileAtPath:parentPath handler:nil], nil);
625}
626
627- (void)testTempName {
628  NSFileManager *fm = [NSFileManager defaultManager];
629  BOOL isDir, exists;
630
631  // First start off with kUserDomain (~/Library/Caches). To resolve symlinks
632  // and tilde, we use stringByResolvingSymlinksInPath but "UpdateEngine-Temp"
633  // may not yet exist.
634  NSString *cachePath =
635      [[@"~/Library/Caches/" stringByResolvingSymlinksInPath]
636          stringByAppendingPathComponent:@"UpdateEngine-Temp"];
637  NSString *basePath = [NSString stringWithFormat:@"%@/%@", cachePath, @"hoff"];
638  NSString *path = [KSDownloadAction writableTempNameForPath:@"hoff"
639                                                    inDomain:kUserDomain];
640  // Make sure the leading part of the generated path is correct.  The tail
641  // part is a UUID which we can't predict.
642  STAssertTrue([path hasPrefix:basePath], nil);
643  // Make sure cachePath exists.
644  exists = [fm fileExistsAtPath:cachePath isDirectory:&isDir];
645  STAssertTrue(exists, nil);
646  STAssertTrue(isDir, nil);
647
648  // Make sure a new directory in ~/Library/Caches is created. Again, resolve
649  // the portion we know must exist first.
650  cachePath = [[@"~/Library/Caches/" stringByResolvingSymlinksInPath]
651                  stringByAppendingPathComponent:@"HoffenGrouse-Temp"];
652  basePath = [NSString stringWithFormat:@"%@/%@", cachePath, @"hoff"];
653  [KSDownloadDirectoryAction setCacheSubfolderName:@"HoffenGrouse-Temp"];
654  path = [KSDownloadDirectoryAction writableTempNameForPath:@"hoff"
655                                                   inDomain:kUserDomain];
656  STAssertTrue([path hasPrefix:basePath], nil);
657  exists = [fm fileExistsAtPath:cachePath isDirectory:&isDir];
658  STAssertTrue(exists, nil);
659  STAssertTrue(isDir, nil);
660
661  // Make sure permissions are sane
662  struct stat statbuf;
663  int result;
664  mode_t newMode;
665  result = stat([cachePath fileSystemRepresentation], &statbuf);
666  STAssertEquals(result, 0, nil);
667  STAssertEquals(statbuf.st_mode & 0700, 0700, nil);
668
669  // Make sure permissions get fixed to owner-writable if changed.
670  newMode = statbuf.st_mode & ~S_IWUSR;
671  result = chmod([cachePath fileSystemRepresentation], newMode);
672  STAssertEquals(result, 0, nil);
673  result = stat([cachePath fileSystemRepresentation], &statbuf);
674  STAssertEquals(result, 0, nil);
675  STAssertEquals(statbuf.st_mode & 0700, 0500, nil);
676  path = [KSDownloadDirectoryAction writableTempNameForPath:@"hoff"
677                                                   inDomain:kUserDomain];
678  result = stat([cachePath fileSystemRepresentation], &statbuf);
679  STAssertEquals(result, 0, nil);
680  STAssertEquals(statbuf.st_mode & 0700, 0700, nil);
681
682  [fm removeFileAtPath:cachePath handler:nil];
683
684  // Now look at kLocalDomain (/Library/Caches)
685  cachePath = @"/Library/Caches/UpdateEngine-Temp";
686  basePath = [NSString stringWithFormat:@"%@/%@", cachePath, @"grouse"];
687  path = [KSDownloadAction writableTempNameForPath:@"grouse"
688                                          inDomain:kLocalDomain];
689  STAssertTrue([path hasPrefix:basePath], nil);
690  // Make sure cachePath exists.
691  exists = [fm fileExistsAtPath:cachePath isDirectory:&isDir];
692  STAssertTrue(exists, nil);
693  STAssertTrue(isDir, nil);
694
695  // Make sure a new directory is created.
696  cachePath = [NSString stringWithFormat:@"/Library/Caches/GrousenHoff-Temp"];
697  basePath = [NSString stringWithFormat:@"%@/%@", cachePath, @"hoff"];
698  [KSDownloadDirectoryAction setCacheSubfolderName:@"GrousenHoff-Temp"];
699  path = [KSDownloadDirectoryAction writableTempNameForPath:@"hoff"
700                                                   inDomain:kLocalDomain];
701  STAssertTrue([path hasPrefix:basePath], nil);
702  exists = [fm fileExistsAtPath:cachePath isDirectory:&isDir];
703  STAssertTrue(exists, nil);
704  STAssertTrue(isDir, nil);
705
706  // Make sure cachePath permissions are sane.
707  result = stat([cachePath fileSystemRepresentation], &statbuf);
708  STAssertEquals(result, 0, nil);
709  STAssertEquals(statbuf.st_mode & 0007, 0007, nil);
710
711  // Make sure permissions get fixed to world-writable if changed.
712  newMode = statbuf.st_mode & ~S_IWOTH;
713  result = chmod([cachePath fileSystemRepresentation], newMode);
714  STAssertEquals(result, 0, nil);
715  result = stat([cachePath fileSystemRepresentation], &statbuf);
716  STAssertEquals(result, 0, nil);
717  STAssertEquals(statbuf.st_mode & 0007, 0005, nil);
718
719  path = [KSDownloadDirectoryAction writableTempNameForPath:@"hoff"
720                                                   inDomain:kLocalDomain];
721  result = stat([cachePath fileSystemRepresentation], &statbuf);
722  STAssertEquals(result, 0, nil);
723  STAssertEquals(statbuf.st_mode & 0007, 0007, nil);
724
725  [KSDownloadDirectoryAction setCacheSubfolderName:nil];
726
727  [fm removeFileAtPath:cachePath handler:nil];
728}
729
730@end