/core/externals/update-engine/Common/KSActionProcessorTest.m

http://macfuse.googlecode.com/ · Objective C · 697 lines · 474 code · 140 blank · 83 comment · 29 complexity · acb6728fd436318853ea1635172e5fed 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 "KSActionProcessor.h"
  16. #import "KSAction.h"
  17. #import "GTMLogger.h"
  18. @interface KSActionProcessorTest : SenTestCase
  19. @end
  20. // Unit test notes
  21. // ---------------
  22. // We're testing the KSActionProcessor here. We test it by creating two
  23. // KSAction subclasses: one that performs an asynchronous action and one that
  24. // performs an action synchronously. Each of these classes will create more
  25. // actions that will be added to the KSActionProcessor's queue while the action
  26. // is running.
  27. //
  28. // The unit tests will make sure that all the actions get executed in the right
  29. // order, and that all the delegate callbacks happen correctly.
  30. // Simple do-nothing action, for testing.
  31. @interface NOPAction : KSAction
  32. @end
  33. @implementation NOPAction
  34. - (void)performAction {
  35. [[self processor] finishedProcessing:self successfully:YES];
  36. }
  37. @end // NOPAction
  38. // A sample KSAction subclass that runs an asynchronous action. This action will
  39. // also add other TestAsyncAction instances to the action queue (up to 10).
  40. @interface TestAsyncAction : KSAction {
  41. int num_;
  42. }
  43. @end
  44. @implementation TestAsyncAction
  45. - (id)initWithNum:(int)num {
  46. if ((self = [super init])) {
  47. num_ = num;
  48. }
  49. return self;
  50. }
  51. - (void)performAction {
  52. GTMLoggerInfo(@"num = %d, processor = %@", num_, [self processor]);
  53. [NSTimer scheduledTimerWithTimeInterval:0.1
  54. target:self
  55. selector:@selector(fire:)
  56. userInfo:nil
  57. repeats:NO];
  58. }
  59. - (void)fire:(NSTimer *)timer {
  60. GTMLoggerInfo(@"num = %d", num_);
  61. if (num_ < 10) {
  62. TestAsyncAction *newAction = [[[TestAsyncAction alloc] initWithNum:++num_] autorelease];
  63. [[self processor] enqueueAction:newAction];
  64. }
  65. [[self processor] finishedProcessing:self successfully:YES];
  66. }
  67. @end // TestAsyncAction
  68. // Test action for sending progress callbacks
  69. @interface ProgressAction : KSAction {
  70. int totalCalls_;
  71. int callsMade_;
  72. NSTimeInterval interval_;
  73. float progress_;
  74. NSTimer *timer_;
  75. }
  76. - (id)initWithCalls:(int)interval;
  77. @end
  78. @implementation ProgressAction
  79. - (id)initWithCalls:(int)calls {
  80. if ((self = [super init])) {
  81. totalCalls_ = calls;
  82. interval_ = (float)1/calls;
  83. }
  84. return self;
  85. }
  86. - (void)performAction {
  87. GTMLoggerInfo(@"interval_ = %f, processor = %@", interval_, [self processor]);
  88. timer_ = [[NSTimer scheduledTimerWithTimeInterval:interval_
  89. target:self
  90. selector:@selector(fire:)
  91. userInfo:nil
  92. repeats:YES] retain];
  93. }
  94. - (void)fire:(NSTimer *)timer {
  95. ++callsMade_;
  96. progress_ += interval_;
  97. GTMLoggerInfo(@"TEST: progress_ = %f, interval = %f", progress_, interval_);
  98. [[self processor] runningAction:self progress:progress_];
  99. if (callsMade_ >= totalCalls_) {
  100. [timer_ invalidate];
  101. [timer_ release];
  102. timer_ = nil;
  103. [[self processor] finishedProcessing:self successfully:YES];
  104. }
  105. }
  106. @end // ProgressAction
  107. // A sample KSAction that runs an action synchronously. This action will also
  108. // add other KSActions to the action queue (up to 10). The type of action added
  109. // is configurable through the toggle parameter. If |toggle| is YES, then the
  110. // actions added will alternate between TestAction and TestAsyncAction.
  111. @interface TestAction : KSAction {
  112. int num_;
  113. BOOL toggle_;
  114. }
  115. @end
  116. @implementation TestAction
  117. - (id)initWithNum:(int)num toggle:(BOOL)toggle {
  118. if ((self = [super init])) {
  119. num_ = num;
  120. toggle_ = toggle;
  121. }
  122. return self;
  123. }
  124. - (void)performAction {
  125. GTMLoggerInfo(@"num = %d, processor = %@", num_, [self processor]);
  126. if (num_ < 10) {
  127. TestAction *newAction = nil;
  128. if (toggle_ && (num_ % 2) == 0)
  129. newAction = [[[TestAsyncAction alloc] initWithNum:++num_] autorelease];
  130. else
  131. newAction = [[[TestAction alloc] initWithNum:++num_ toggle:NO] autorelease];
  132. [[self processor] enqueueAction:newAction];
  133. }
  134. [[self processor] finishedProcessing:self successfully:YES];
  135. }
  136. @end // TestAction
  137. // This is a KSActionProcessor delegate. It simply records the number of times
  138. // a delegate method is called, and the order in which they're called. This
  139. // is used to verify that delegate methods happend correctly.
  140. @interface MethodCounter : NSObject {
  141. NSMutableDictionary *methodCalls_;
  142. NSMutableArray *callOrder_;
  143. }
  144. @end
  145. @implementation MethodCounter
  146. + (id)counter {
  147. return [[[self alloc] init] autorelease];
  148. }
  149. - (id)init {
  150. if ((self = [super init])) {
  151. methodCalls_ = [[NSMutableDictionary alloc] init];
  152. callOrder_ = [[NSMutableArray alloc] init];
  153. }
  154. return self;
  155. }
  156. - (void)dealloc {
  157. [methodCalls_ release];
  158. [callOrder_ release];
  159. [super dealloc];
  160. }
  161. - (NSDictionary *)methodCalls {
  162. return methodCalls_;
  163. }
  164. - (NSArray *)callOrder {
  165. return callOrder_;
  166. }
  167. // Sent when processing is started.
  168. - (void)processingStarted:(KSActionProcessor *)processor {
  169. NSString *key = NSStringFromSelector(_cmd);
  170. NSNumber *count = [methodCalls_ objectForKey:key];
  171. [methodCalls_ setObject:[NSNumber numberWithInt:(1 + [count intValue])]
  172. forKey:key];
  173. [callOrder_ addObject:key];
  174. GTMLoggerInfo(@"%@", processor);
  175. }
  176. // Sent when the action queue is empty.
  177. - (void)processingDone:(KSActionProcessor *)processor {
  178. NSString *key = NSStringFromSelector(_cmd);
  179. NSNumber *count = [methodCalls_ objectForKey:key];
  180. [methodCalls_ setObject:[NSNumber numberWithInt:(1 + [count intValue])]
  181. forKey:key];
  182. [callOrder_ addObject:key];
  183. GTMLoggerInfo(@"%@", processor);
  184. }
  185. // Sent when processing is stopped by via the -stopProcessing message. This
  186. // does not imply that the action queue is empty.
  187. - (void)processingStopped:(KSActionProcessor *)processor {
  188. NSString *key = NSStringFromSelector(_cmd);
  189. NSNumber *count = [methodCalls_ objectForKey:key];
  190. [methodCalls_ setObject:[NSNumber numberWithInt:(1 + [count intValue])]
  191. forKey:key];
  192. [callOrder_ addObject:key];
  193. GTMLoggerInfo(@"%@", processor);
  194. }
  195. // Called after an action has been enqueued by the KSActionProcessor.
  196. - (void)processor:(KSActionProcessor *)processor
  197. enqueuedAction:(KSAction *)action {
  198. NSString *key = NSStringFromSelector(_cmd);
  199. NSNumber *count = [methodCalls_ objectForKey:key];
  200. [methodCalls_ setObject:[NSNumber numberWithInt:(1 + [count intValue])]
  201. forKey:key];
  202. [callOrder_ addObject:key];
  203. GTMLoggerInfo(@"%@ %@", processor, action);
  204. }
  205. // Called right before the KSActionProcessor starts the KSAction by sending it
  206. // the -performAction: message.
  207. - (void)processor:(KSActionProcessor *)processor
  208. startingAction:(KSAction *)action {
  209. NSString *key = NSStringFromSelector(_cmd);
  210. NSNumber *count = [methodCalls_ objectForKey:key];
  211. [methodCalls_ setObject:[NSNumber numberWithInt:(1 + [count intValue])]
  212. forKey:key];
  213. [callOrder_ addObject:key];
  214. GTMLoggerInfo(@"%@ %@", processor, action);
  215. }
  216. // Called once the KSAction informs the KSActionProcessor that the action has
  217. // finished.
  218. - (void)processor:(KSActionProcessor *)processor
  219. finishedAction:(KSAction *)action
  220. successfully:(BOOL)wasOK {
  221. NSString *key = NSStringFromSelector(_cmd);
  222. NSNumber *count = [methodCalls_ objectForKey:key];
  223. [methodCalls_ setObject:[NSNumber numberWithInt:(1 + [count intValue])]
  224. forKey:key];
  225. [callOrder_ addObject:key];
  226. GTMLoggerInfo(@"%@ %@", processor, action);
  227. }
  228. @end // MethodCounter
  229. @interface ProgressRecorder : NSObject {
  230. NSMutableArray *calls_;
  231. }
  232. - (NSArray *)calls;
  233. @end
  234. @implementation ProgressRecorder
  235. - (id)init {
  236. if ((self = [super init])) {
  237. calls_ = [[NSMutableArray alloc] init];
  238. }
  239. return self;
  240. }
  241. - (void)dealloc {
  242. [calls_ release];
  243. [super dealloc];
  244. }
  245. - (NSArray *)calls {
  246. return calls_;
  247. }
  248. - (void)processor:(KSActionProcessor *)processor
  249. runningAction:(KSAction *)action
  250. progress:(float)progress {
  251. NSString *call = [NSString stringWithFormat:@"action:%.02f,processor:%.02f",
  252. progress, [processor progress]];
  253. [calls_ addObject:call];
  254. }
  255. @end
  256. @interface KSActionProcessor (InternalPrivateMethods)
  257. - (void)updateProgressWithFraction:(float)fraction;
  258. @end
  259. //
  260. // Begin unit test code
  261. //
  262. @implementation KSActionProcessorTest
  263. - (void)testBasic {
  264. KSActionProcessor *ap = nil;
  265. ap = [[[KSActionProcessor alloc] initWithDelegate:nil] autorelease];
  266. STAssertNotNil(ap, nil);
  267. ap = [[[KSActionProcessor alloc] initWithDelegate:@"blah"] autorelease];
  268. STAssertNotNil(ap, nil);
  269. ap = [[[KSActionProcessor alloc] init] autorelease];
  270. STAssertNotNil(ap, nil);
  271. STAssertTrue([[ap actions] count] == 0, nil);
  272. [ap enqueueAction:nil];
  273. STAssertTrue([[ap actions] count] == 0, nil);
  274. [ap enqueueAction:[[[KSAction alloc] init] autorelease]];
  275. [ap enqueueAction:[[[KSAction alloc] init] autorelease]];
  276. [ap enqueueAction:[[[KSAction alloc] init] autorelease]];
  277. STAssertTrue([[ap actions] count] == 3, nil);
  278. STAssertTrue([ap actionsCompleted] == 0, nil);
  279. STAssertNil([ap delegate], nil);
  280. [ap setDelegate:@"blah"];
  281. STAssertNotNil([ap delegate], nil);
  282. STAssertTrue([[ap description] length] > 1, nil);
  283. }
  284. - (void)verifyMethodCounter:(MethodCounter *)counter {
  285. //
  286. // Make sure each delegate method was called the correct number of times
  287. //
  288. NSDictionary *calls = [counter methodCalls];
  289. STAssertNotNil(calls, nil);
  290. // There are 7 delegate methods total, but MethodCounter only counts 6 of
  291. // them. The one that's not counted is processor:runningAction:progress:,
  292. // because that method can be called a number of times
  293. STAssertTrue([calls count] == 6, nil);
  294. STAssertEqualObjects([calls objectForKey:@"processingStarted:"],
  295. [NSNumber numberWithInt:1], nil);
  296. STAssertEqualObjects([calls objectForKey:@"processingDone:"],
  297. [NSNumber numberWithInt:1], nil);
  298. STAssertEqualObjects([calls objectForKey:@"processingStopped:"],
  299. [NSNumber numberWithInt:1], nil);
  300. STAssertEqualObjects([calls objectForKey:@"processor:enqueuedAction:"],
  301. [NSNumber numberWithInt:10], nil);
  302. STAssertEqualObjects([calls objectForKey:@"processor:startingAction:"],
  303. [NSNumber numberWithInt:10], nil);
  304. STAssertEqualObjects([calls objectForKey:@"processor:finishedAction:successfully:"],
  305. [NSNumber numberWithInt:10], nil);
  306. //
  307. // Make sure the methods were called in the correct order.
  308. //
  309. NSArray *order = [counter callOrder];
  310. STAssertNotNil(order, nil);
  311. STAssertTrue([order count] == 33, nil); // 33 calls total
  312. NSArray *correctOrder = [NSArray arrayWithObjects:
  313. @"processor:enqueuedAction:",
  314. @"processingStarted:",
  315. @"processor:startingAction:",
  316. @"processor:enqueuedAction:",
  317. @"processor:finishedAction:successfully:",
  318. @"processor:startingAction:",
  319. @"processor:enqueuedAction:",
  320. @"processor:finishedAction:successfully:",
  321. @"processor:startingAction:",
  322. @"processor:enqueuedAction:",
  323. @"processor:finishedAction:successfully:",
  324. @"processor:startingAction:",
  325. @"processor:enqueuedAction:",
  326. @"processor:finishedAction:successfully:",
  327. @"processor:startingAction:",
  328. @"processor:enqueuedAction:",
  329. @"processor:finishedAction:successfully:",
  330. @"processor:startingAction:",
  331. @"processor:enqueuedAction:",
  332. @"processor:finishedAction:successfully:",
  333. @"processor:startingAction:",
  334. @"processor:enqueuedAction:",
  335. @"processor:finishedAction:successfully:",
  336. @"processor:startingAction:",
  337. @"processor:enqueuedAction:",
  338. @"processor:finishedAction:successfully:",
  339. @"processor:startingAction:",
  340. @"processor:enqueuedAction:",
  341. @"processor:finishedAction:successfully:",
  342. @"processor:startingAction:",
  343. @"processor:finishedAction:successfully:",
  344. @"processingDone:",
  345. @"processingStopped:",
  346. nil];
  347. STAssertTrue([order count] == [correctOrder count], nil);
  348. STAssertEqualObjects(order, correctOrder, nil);
  349. // Useful for debugging
  350. // GTMLoggerInfo(@"methodCalls = %@", calls);
  351. // GTMLoggerInfo(@"callOrder = %@", [counter callOrder]);
  352. }
  353. // The guts of a sync test but allow the caller to specify the number
  354. // of times [ap startProcessing] is called.
  355. - (void)commonTestSynchronousWithStarts:(int)starts {
  356. // Create an action processor delegate that counts the delegate calls
  357. MethodCounter *counter = [MethodCounter counter];
  358. STAssertNotNil(counter, nil);
  359. KSActionProcessor *ap = [[[KSActionProcessor alloc] initWithDelegate:counter] autorelease];
  360. STAssertNotNil(ap, nil);
  361. // Create some simple action to start the ball rolling. This action will
  362. // create other actions that will be appended to the action processor queue.
  363. // A total of 10 actions should be used overall (in this test).
  364. KSAction *action = [[[TestAction alloc] initWithNum:1 toggle:NO] autorelease];
  365. STAssertNotNil(action, nil);
  366. STAssertTrue([ap actionsCompleted] == 0, nil);
  367. // Add our one action to kick things off, then start processing the queue
  368. [ap enqueueAction:action];
  369. // INTERNAL KNOWLEDGE:
  370. // startProcessing: calls processHead: which asserts if the
  371. // currentAction_ isn't nil. The currentAction_ becomes non-nil as
  372. // a result of a valid call to processHead:.
  373. // Thus, if >1 of these calls don't throw an assert, we're fine.
  374. for (int i = 0; i < starts; i++)
  375. [ap startProcessing];
  376. // Since this is the synchronous test, all actions will be done by this point,
  377. // so we can verify that all the delegate methods worked correctly.
  378. // Only call verifyMethodCounter on the "normal" case, since the
  379. // trap mechanism doesn't understand excessive starts.
  380. if (starts == 1)
  381. [self verifyMethodCounter:counter];
  382. STAssertTrue([ap actionsCompleted] > 1, nil);
  383. }
  384. - (void)testSynchronous {
  385. [self commonTestSynchronousWithStarts:1];
  386. }
  387. - (void)testExcessiveStartProcessing {
  388. [self commonTestSynchronousWithStarts:8];
  389. }
  390. - (void)testAsynchronous {
  391. MethodCounter *counter = [MethodCounter counter];
  392. STAssertNotNil(counter, nil);
  393. KSActionProcessor *ap = [[[KSActionProcessor alloc] initWithDelegate:counter] autorelease];
  394. STAssertNotNil(ap, nil);
  395. // Create an action that does its stuff asynchronously.
  396. KSAction *action = [[[TestAsyncAction alloc] initWithNum:1] autorelease];
  397. STAssertNotNil(action, nil);
  398. // Add our one action to kick things off, then start processing the queue
  399. [ap enqueueAction:action];
  400. [ap startProcessing];
  401. // Since we're testing asynchronous actions, we need to spin the runloop for
  402. // a bit to make sure all of our actions complete. Each action uses a 0.1
  403. // second timer, so spinning for 2 seconds should be more than enough time
  404. // for everything to complete.
  405. NSDate *quick = [NSDate dateWithTimeIntervalSinceNow:2];
  406. [[NSRunLoop currentRunLoop] runUntilDate:quick];
  407. [self verifyMethodCounter:counter];
  408. }
  409. - (void)testBoth {
  410. MethodCounter *counter = [MethodCounter counter];
  411. STAssertNotNil(counter, nil);
  412. KSActionProcessor *ap = [[[KSActionProcessor alloc] initWithDelegate:counter] autorelease];
  413. STAssertNotNil(ap, nil);
  414. // Create an action that will create sync and async actions in the same Q
  415. KSAction *action = [[[TestAction alloc] initWithNum:1 toggle:YES] autorelease];
  416. STAssertNotNil(action, nil);
  417. // Add our one action to kick things off, then start processing the queue
  418. [ap enqueueAction:action];
  419. STAssertTrue([ap actionsCompleted] == 0, nil);
  420. [ap startProcessing];
  421. NSDate *quick = [NSDate dateWithTimeIntervalSinceNow:1];
  422. [[NSRunLoop currentRunLoop] runUntilDate:quick];
  423. [self verifyMethodCounter:counter];
  424. STAssertTrue([ap actionsCompleted] > 3, nil);
  425. }
  426. - (void)testUpdateProgress {
  427. KSActionProcessor *ap = [[[KSActionProcessor alloc] initWithDelegate:nil] autorelease];
  428. KSAction *nop = [[[NOPAction alloc] init] autorelease];
  429. [ap enqueueAction:nop];
  430. STAssertNotNil(ap, nil);
  431. STAssertEqualsWithAccuracy([ap progress], 0.0f, 0.01, nil);
  432. [ap updateProgressWithFraction:0.1];
  433. STAssertEqualsWithAccuracy([ap progress], 0.1f, 0.01, nil);
  434. [ap updateProgressWithFraction:0.2];
  435. STAssertEqualsWithAccuracy([ap progress], 0.2f, 0.01, nil);
  436. [ap updateProgressWithFraction:0.3];
  437. STAssertEqualsWithAccuracy([ap progress], 0.3f, 0.01, nil);
  438. [ap updateProgressWithFraction:1.0];
  439. STAssertEqualsWithAccuracy([ap progress], 1.0f, 0.01, nil);
  440. [ap updateProgressWithFraction:0.0];
  441. STAssertEqualsWithAccuracy([ap progress], 0.0f, 0.01, nil);
  442. }
  443. - (void)testProgressSingleAction {
  444. ProgressRecorder *recorder = [[[ProgressRecorder alloc] init] autorelease];
  445. STAssertNotNil(recorder, nil);
  446. KSActionProcessor *ap = [[[KSActionProcessor alloc] initWithDelegate:recorder] autorelease];
  447. STAssertNotNil(ap, nil);
  448. STAssertEqualsWithAccuracy([ap progress], 0.0f, 0.01, nil);
  449. KSAction *action = [[[ProgressAction alloc] initWithCalls:10] autorelease];
  450. STAssertNotNil(action, nil);
  451. [ap enqueueAction:action];
  452. STAssertTrue([ap actionsCompleted] == 0, nil);
  453. STAssertEqualsWithAccuracy([ap progress], 0.0f, 0.01, nil);
  454. [ap startProcessing];
  455. NSDate *quick = [NSDate dateWithTimeIntervalSinceNow:1];
  456. [[NSRunLoop currentRunLoop] runUntilDate:quick];
  457. STAssertFalse([ap isProcessing], nil);
  458. STAssertEquals([ap actionsCompleted], 1, nil);
  459. STAssertEqualsWithAccuracy([ap progress], 1.0f, 0.01, nil);
  460. NSArray *progressCalls = [recorder calls];
  461. STAssertTrue([progressCalls count] == 10, nil);
  462. NSArray *expectedCalls = [NSArray arrayWithObjects:
  463. @"action:0.10,processor:0.10",
  464. @"action:0.20,processor:0.20",
  465. @"action:0.30,processor:0.30",
  466. @"action:0.40,processor:0.40",
  467. @"action:0.50,processor:0.50",
  468. @"action:0.60,processor:0.60",
  469. @"action:0.70,processor:0.70",
  470. @"action:0.80,processor:0.80",
  471. @"action:0.90,processor:0.90",
  472. @"action:1.00,processor:1.00",
  473. nil];
  474. STAssertEqualObjects(progressCalls, expectedCalls, nil);
  475. }
  476. - (void)testProgressMultipleActions {
  477. ProgressRecorder *recorder = [[[ProgressRecorder alloc] init] autorelease];
  478. STAssertNotNil(recorder, nil);
  479. KSActionProcessor *ap = [[[KSActionProcessor alloc] initWithDelegate:recorder] autorelease];
  480. STAssertNotNil(ap, nil);
  481. KSAction *action1 = [[[ProgressAction alloc] initWithCalls:10] autorelease];
  482. STAssertNotNil(action1, nil);
  483. KSAction *action2 = [[[ProgressAction alloc] initWithCalls:10] autorelease];
  484. STAssertNotNil(action2, nil);
  485. [ap enqueueAction:action1];
  486. [ap enqueueAction:action2];
  487. STAssertTrue([ap actionsCompleted] == 0, nil);
  488. STAssertEqualsWithAccuracy([ap progress], 0.0f, 0.01, nil);
  489. [ap startProcessing];
  490. NSDate *quick = [NSDate dateWithTimeIntervalSinceNow:2.2];
  491. [[NSRunLoop currentRunLoop] runUntilDate:quick];
  492. STAssertFalse([ap isProcessing], nil);
  493. STAssertEquals([ap actionsCompleted], 2, nil);
  494. STAssertEqualsWithAccuracy([ap progress], 1.0f, 0.01, nil);
  495. NSArray *progressCalls = [recorder calls];
  496. STAssertTrue([progressCalls count] == 20, nil);
  497. NSArray *expectedCalls = [NSArray arrayWithObjects:
  498. @"action:0.10,processor:0.05",
  499. @"action:0.20,processor:0.10",
  500. @"action:0.30,processor:0.15",
  501. @"action:0.40,processor:0.20",
  502. @"action:0.50,processor:0.25",
  503. @"action:0.60,processor:0.30",
  504. @"action:0.70,processor:0.35",
  505. @"action:0.80,processor:0.40",
  506. @"action:0.90,processor:0.45",
  507. @"action:1.00,processor:0.50",
  508. @"action:0.10,processor:0.55",
  509. @"action:0.20,processor:0.60",
  510. @"action:0.30,processor:0.65",
  511. @"action:0.40,processor:0.70",
  512. @"action:0.50,processor:0.75",
  513. @"action:0.60,processor:0.80",
  514. @"action:0.70,processor:0.85",
  515. @"action:0.80,processor:0.90",
  516. @"action:0.90,processor:0.95",
  517. @"action:1.00,processor:1.00",
  518. nil];
  519. STAssertEqualObjects(progressCalls, expectedCalls, nil);
  520. }
  521. - (void)testProgressMixedActions {
  522. ProgressRecorder *recorder = [[[ProgressRecorder alloc] init] autorelease];
  523. STAssertNotNil(recorder, nil);
  524. KSActionProcessor *ap = [[[KSActionProcessor alloc] initWithDelegate:recorder] autorelease];
  525. STAssertNotNil(ap, nil);
  526. KSAction *action1 = [[[ProgressAction alloc] initWithCalls:10] autorelease];
  527. STAssertNotNil(action1, nil);
  528. KSAction *action2 = [[[NOPAction alloc] init] autorelease];
  529. STAssertNotNil(action2, nil);
  530. [ap enqueueAction:action1];
  531. [ap enqueueAction:action2];
  532. STAssertTrue([ap actionsCompleted] == 0, nil);
  533. STAssertEqualsWithAccuracy([ap progress], 0.0f, 0.01, nil);
  534. [ap startProcessing];
  535. NSDate *quick = [NSDate dateWithTimeIntervalSinceNow:2.2];
  536. [[NSRunLoop currentRunLoop] runUntilDate:quick];
  537. STAssertFalse([ap isProcessing], nil);
  538. STAssertEquals([ap actionsCompleted], 2, nil);
  539. // Verify that the progress now is 1.0
  540. STAssertEqualsWithAccuracy([ap progress], 1.0f, 0.01, nil);
  541. NSArray *progressCalls = [recorder calls];
  542. STAssertTrue([progressCalls count] == 10, nil);
  543. NSArray *expectedCalls = [NSArray arrayWithObjects:
  544. @"action:0.10,processor:0.05",
  545. @"action:0.20,processor:0.10",
  546. @"action:0.30,processor:0.15",
  547. @"action:0.40,processor:0.20",
  548. @"action:0.50,processor:0.25",
  549. @"action:0.60,processor:0.30",
  550. @"action:0.70,processor:0.35",
  551. @"action:0.80,processor:0.40",
  552. @"action:0.90,processor:0.45",
  553. @"action:1.00,processor:0.50",
  554. nil];
  555. STAssertEqualObjects(progressCalls, expectedCalls, nil);
  556. }
  557. @end