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