RACSequenceSpec.m 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444
  1. //
  2. // RACSequenceSpec.m
  3. // ReactiveCocoa
  4. //
  5. // Created by Justin Spahr-Summers on 2012-11-01.
  6. // Copyright (c) 2012 GitHub, Inc. All rights reserved.
  7. //
  8. #import <Quick/Quick.h>
  9. #import <Nimble/Nimble.h>
  10. #import "RACSequenceExamples.h"
  11. #import "RACStreamExamples.h"
  12. #import "NSArray+RACSequenceAdditions.h"
  13. #import "NSObject+RACDeallocating.h"
  14. #import "NSObject+RACPropertySubscribing.h"
  15. #import "RACCompoundDisposable.h"
  16. #import "RACDisposable.h"
  17. #import "RACSequence.h"
  18. #import "RACUnit.h"
  19. QuickSpecBegin(RACSequenceSpec)
  20. qck_describe(@"RACStream", ^{
  21. id verifyValues = ^(RACSequence *sequence, NSArray *expectedValues) {
  22. NSMutableArray *collectedValues = [NSMutableArray array];
  23. while (sequence.head != nil) {
  24. [collectedValues addObject:sequence.head];
  25. sequence = sequence.tail;
  26. }
  27. expect(collectedValues).to(equal(expectedValues));
  28. };
  29. __block RACSequence *infiniteSequence = [RACSequence sequenceWithHeadBlock:^{
  30. return RACUnit.defaultUnit;
  31. } tailBlock:^{
  32. return infiniteSequence;
  33. }];
  34. qck_itBehavesLike(RACStreamExamples, ^{
  35. return @{
  36. RACStreamExamplesClass: RACSequence.class,
  37. RACStreamExamplesVerifyValuesBlock: verifyValues,
  38. RACStreamExamplesInfiniteStream: infiniteSequence
  39. };
  40. });
  41. });
  42. qck_describe(@"+sequenceWithHeadBlock:tailBlock:", ^{
  43. __block RACSequence *sequence;
  44. __block BOOL headInvoked;
  45. __block BOOL tailInvoked;
  46. qck_beforeEach(^{
  47. headInvoked = NO;
  48. tailInvoked = NO;
  49. sequence = [RACSequence sequenceWithHeadBlock:^{
  50. headInvoked = YES;
  51. return @0;
  52. } tailBlock:^{
  53. tailInvoked = YES;
  54. return [RACSequence return:@1];
  55. }];
  56. expect(sequence).notTo(beNil());
  57. });
  58. qck_it(@"should use the values from the head and tail blocks", ^{
  59. expect(sequence.head).to(equal(@0));
  60. expect(sequence.tail.head).to(equal(@1));
  61. expect(sequence.tail.tail).to(beNil());
  62. });
  63. qck_it(@"should lazily invoke head and tail blocks", ^{
  64. expect(@(headInvoked)).to(beFalsy());
  65. expect(@(tailInvoked)).to(beFalsy());
  66. expect(sequence.head).to(equal(@0));
  67. expect(@(headInvoked)).to(beTruthy());
  68. expect(@(tailInvoked)).to(beFalsy());
  69. expect(sequence.tail).notTo(beNil());
  70. expect(@(tailInvoked)).to(beTruthy());
  71. });
  72. qck_context(@"behaves like a sequence", ^{
  73. qck_itBehavesLike(RACSequenceExamples, ^{
  74. return @{
  75. RACSequenceExampleSequence: sequence,
  76. RACSequenceExampleExpectedValues: @[ @0, @1 ]
  77. };
  78. });
  79. });
  80. });
  81. qck_describe(@"empty sequences", ^{
  82. qck_itBehavesLike(RACSequenceExamples, ^{
  83. return @{
  84. RACSequenceExampleSequence: [RACSequence empty],
  85. RACSequenceExampleExpectedValues: @[]
  86. };
  87. });
  88. });
  89. qck_describe(@"non-empty sequences", ^{
  90. qck_itBehavesLike(RACSequenceExamples, ^{
  91. return @{
  92. RACSequenceExampleSequence: [[[RACSequence return:@0] concat:[RACSequence return:@1]] concat:[RACSequence return:@2]],
  93. RACSequenceExampleExpectedValues: @[ @0, @1, @2 ]
  94. };
  95. });
  96. });
  97. qck_describe(@"eager sequences", ^{
  98. __block RACSequence *lazySequence;
  99. __block BOOL headInvoked;
  100. __block BOOL tailInvoked;
  101. NSArray *values = @[ @0, @1 ];
  102. qck_beforeEach(^{
  103. headInvoked = NO;
  104. tailInvoked = NO;
  105. lazySequence = [RACSequence sequenceWithHeadBlock:^{
  106. headInvoked = YES;
  107. return @0;
  108. } tailBlock:^{
  109. tailInvoked = YES;
  110. return [RACSequence return:@1];
  111. }];
  112. expect(lazySequence).notTo(beNil());
  113. });
  114. qck_itBehavesLike(RACSequenceExamples, ^{
  115. return @{
  116. RACSequenceExampleSequence: lazySequence.eagerSequence,
  117. RACSequenceExampleExpectedValues: values
  118. };
  119. });
  120. qck_it(@"should evaluate all values immediately", ^{
  121. RACSequence *eagerSequence = lazySequence.eagerSequence;
  122. expect(@(headInvoked)).to(beTruthy());
  123. expect(@(tailInvoked)).to(beTruthy());
  124. expect(eagerSequence.array).to(equal(values));
  125. });
  126. });
  127. qck_describe(@"-take:", ^{
  128. qck_it(@"should complete take: without needing the head of the second item in the sequence", ^{
  129. __block NSUInteger valuesTaken = 0;
  130. __block RACSequence *sequence = [RACSequence sequenceWithHeadBlock:^{
  131. ++valuesTaken;
  132. return RACUnit.defaultUnit;
  133. } tailBlock:^{
  134. return sequence;
  135. }];
  136. NSArray *values = [sequence take:1].array;
  137. expect(values).to(equal(@[ RACUnit.defaultUnit ]));
  138. expect(@(valuesTaken)).to(equal(@1));
  139. });
  140. });
  141. qck_describe(@"-bind:", ^{
  142. qck_it(@"should only evaluate head when the resulting sequence is evaluated", ^{
  143. __block BOOL headInvoked = NO;
  144. RACSequence *original = [RACSequence sequenceWithHeadBlock:^{
  145. headInvoked = YES;
  146. return RACUnit.defaultUnit;
  147. } tailBlock:^ id {
  148. return nil;
  149. }];
  150. RACSequence *bound = [original bind:^{
  151. return ^(id value, BOOL *stop) {
  152. return [RACSequence return:value];
  153. };
  154. }];
  155. expect(bound).notTo(beNil());
  156. expect(@(headInvoked)).to(beFalsy());
  157. expect(bound.head).to(equal(RACUnit.defaultUnit));
  158. expect(@(headInvoked)).to(beTruthy());
  159. });
  160. });
  161. qck_describe(@"-objectEnumerator", ^{
  162. qck_it(@"should only evaluate head as it's enumerated", ^{
  163. __block BOOL firstHeadInvoked = NO;
  164. __block BOOL secondHeadInvoked = NO;
  165. __block BOOL thirdHeadInvoked = NO;
  166. RACSequence *sequence = [RACSequence sequenceWithHeadBlock:^id{
  167. firstHeadInvoked = YES;
  168. return @1;
  169. } tailBlock:^RACSequence *{
  170. return [RACSequence sequenceWithHeadBlock:^id{
  171. secondHeadInvoked = YES;
  172. return @2;
  173. } tailBlock:^RACSequence *{
  174. return [RACSequence sequenceWithHeadBlock:^id{
  175. thirdHeadInvoked = YES;
  176. return @3;
  177. } tailBlock:^RACSequence *{
  178. return RACSequence.empty;
  179. }];
  180. }];
  181. }];
  182. NSEnumerator *enumerator = sequence.objectEnumerator;
  183. expect(@(firstHeadInvoked)).to(beFalsy());
  184. expect(@(secondHeadInvoked)).to(beFalsy());
  185. expect(@(thirdHeadInvoked)).to(beFalsy());
  186. expect([enumerator nextObject]).to(equal(@1));
  187. expect(@(firstHeadInvoked)).to(beTruthy());
  188. expect(@(secondHeadInvoked)).to(beFalsy());
  189. expect(@(thirdHeadInvoked)).to(beFalsy());
  190. expect([enumerator nextObject]).to(equal(@2));
  191. expect(@(secondHeadInvoked)).to(beTruthy());
  192. expect(@(thirdHeadInvoked)).to(beFalsy());
  193. expect([enumerator nextObject]).to(equal(@3));
  194. expect(@(thirdHeadInvoked)).to(beTruthy());
  195. expect([enumerator nextObject]).to(beNil());
  196. });
  197. qck_it(@"should let the sequence dealloc as it's enumerated", ^{
  198. __block BOOL firstSequenceDeallocd = NO;
  199. __block BOOL secondSequenceDeallocd = NO;
  200. __block BOOL thirdSequenceDeallocd = NO;
  201. NSEnumerator *enumerator = nil;
  202. @autoreleasepool {
  203. RACSequence *thirdSequence __attribute__((objc_precise_lifetime)) = [RACSequence sequenceWithHeadBlock:^id{
  204. return @3;
  205. } tailBlock:^RACSequence *{
  206. return RACSequence.empty;
  207. }];
  208. [thirdSequence.rac_deallocDisposable addDisposable:[RACDisposable disposableWithBlock:^{
  209. thirdSequenceDeallocd = YES;
  210. }]];
  211. RACSequence *secondSequence __attribute__((objc_precise_lifetime)) = [RACSequence sequenceWithHeadBlock:^id{
  212. return @2;
  213. } tailBlock:^RACSequence *{
  214. return thirdSequence;
  215. }];
  216. [secondSequence.rac_deallocDisposable addDisposable:[RACDisposable disposableWithBlock:^{
  217. secondSequenceDeallocd = YES;
  218. }]];
  219. RACSequence *firstSequence __attribute__((objc_precise_lifetime)) = [RACSequence sequenceWithHeadBlock:^id{
  220. return @1;
  221. } tailBlock:^RACSequence *{
  222. return secondSequence;
  223. }];
  224. [firstSequence.rac_deallocDisposable addDisposable:[RACDisposable disposableWithBlock:^{
  225. firstSequenceDeallocd = YES;
  226. }]];
  227. enumerator = firstSequence.objectEnumerator;
  228. }
  229. @autoreleasepool {
  230. expect([enumerator nextObject]).to(equal(@1));
  231. }
  232. @autoreleasepool {
  233. expect([enumerator nextObject]).to(equal(@2));
  234. }
  235. expect(@(firstSequenceDeallocd)).toEventually(beTruthy());
  236. @autoreleasepool {
  237. expect([enumerator nextObject]).to(equal(@3));
  238. }
  239. expect(@(secondSequenceDeallocd)).toEventually(beTruthy());
  240. @autoreleasepool {
  241. expect([enumerator nextObject]).to(beNil());
  242. }
  243. expect(@(thirdSequenceDeallocd)).toEventually(beTruthy());
  244. });
  245. });
  246. qck_it(@"shouldn't overflow the stack when deallocated on a background queue", ^{
  247. NSUInteger length = 10000;
  248. NSMutableArray *values = [NSMutableArray arrayWithCapacity:length];
  249. for (NSUInteger i = 0; i < length; ++i) {
  250. [values addObject:@(i)];
  251. }
  252. __block BOOL finished = NO;
  253. dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
  254. @autoreleasepool {
  255. (void)[[values.rac_sequence map:^(id value) {
  256. return value;
  257. }] array];
  258. }
  259. finished = YES;
  260. });
  261. expect(@(finished)).toEventually(beTruthy());
  262. });
  263. qck_describe(@"-foldLeftWithStart:reduce:", ^{
  264. qck_it(@"should reduce with start first", ^{
  265. RACSequence *sequence = [[[RACSequence return:@0] concat:[RACSequence return:@1]] concat:[RACSequence return:@2]];
  266. NSNumber *result = [sequence foldLeftWithStart:@3 reduce:^(NSNumber *first, NSNumber *rest) {
  267. return first;
  268. }];
  269. expect(result).to(equal(@3));
  270. });
  271. qck_it(@"should be left associative", ^{
  272. RACSequence *sequence = [[[RACSequence return:@1] concat:[RACSequence return:@2]] concat:[RACSequence return:@3]];
  273. NSNumber *result = [sequence foldLeftWithStart:@0 reduce:^(NSNumber *first, NSNumber *rest) {
  274. int difference = first.intValue - rest.intValue;
  275. return @(difference);
  276. }];
  277. expect(result).to(equal(@-6));
  278. });
  279. });
  280. qck_describe(@"-foldRightWithStart:reduce:", ^{
  281. qck_it(@"should be lazy", ^{
  282. __block BOOL headInvoked = NO;
  283. __block BOOL tailInvoked = NO;
  284. RACSequence *sequence = [RACSequence sequenceWithHeadBlock:^{
  285. headInvoked = YES;
  286. return @0;
  287. } tailBlock:^{
  288. tailInvoked = YES;
  289. return [RACSequence return:@1];
  290. }];
  291. NSNumber *result = [sequence foldRightWithStart:@2 reduce:^(NSNumber *first, RACSequence *rest) {
  292. return first;
  293. }];
  294. expect(result).to(equal(@0));
  295. expect(@(headInvoked)).to(beTruthy());
  296. expect(@(tailInvoked)).to(beFalsy());
  297. });
  298. qck_it(@"should reduce with start last", ^{
  299. RACSequence *sequence = [[[RACSequence return:@0] concat:[RACSequence return:@1]] concat:[RACSequence return:@2]];
  300. NSNumber *result = [sequence foldRightWithStart:@3 reduce:^(NSNumber *first, RACSequence *rest) {
  301. return rest.head;
  302. }];
  303. expect(result).to(equal(@3));
  304. });
  305. qck_it(@"should be right associative", ^{
  306. RACSequence *sequence = [[[RACSequence return:@1] concat:[RACSequence return:@2]] concat:[RACSequence return:@3]];
  307. NSNumber *result = [sequence foldRightWithStart:@0 reduce:^(NSNumber *first, RACSequence *rest) {
  308. int difference = first.intValue - [rest.head intValue];
  309. return @(difference);
  310. }];
  311. expect(result).to(equal(@2));
  312. });
  313. });
  314. qck_describe(@"-any", ^{
  315. __block RACSequence *sequence;
  316. qck_beforeEach(^{
  317. sequence = [[[RACSequence return:@0] concat:[RACSequence return:@1]] concat:[RACSequence return:@2]];
  318. });
  319. qck_it(@"should return true when at least one exists", ^{
  320. BOOL result = [sequence any:^ BOOL (NSNumber *value) {
  321. return value.integerValue > 0;
  322. }];
  323. expect(@(result)).to(beTruthy());
  324. });
  325. qck_it(@"should return false when no such thing exists", ^{
  326. BOOL result = [sequence any:^ BOOL (NSNumber *value) {
  327. return value.integerValue == 3;
  328. }];
  329. expect(@(result)).to(beFalsy());
  330. });
  331. });
  332. qck_describe(@"-all", ^{
  333. __block RACSequence *sequence;
  334. qck_beforeEach(^{
  335. sequence = [[[RACSequence return:@0] concat:[RACSequence return:@1]] concat:[RACSequence return:@2]];
  336. });
  337. qck_it(@"should return true when all values pass", ^{
  338. BOOL result = [sequence all:^ BOOL (NSNumber *value) {
  339. return value.integerValue >= 0;
  340. }];
  341. expect(@(result)).to(beTruthy());
  342. });
  343. qck_it(@"should return false when at least one value fails", ^{
  344. BOOL result = [sequence all:^ BOOL (NSNumber *value) {
  345. return value.integerValue < 2;
  346. }];
  347. expect(@(result)).to(beFalsy());
  348. });
  349. });
  350. qck_describe(@"-objectPassingTest:", ^{
  351. __block RACSequence *sequence;
  352. qck_beforeEach(^{
  353. sequence = [[[RACSequence return:@0] concat:[RACSequence return:@1]] concat:[RACSequence return:@2]];
  354. });
  355. qck_it(@"should return leftmost object that passes the test", ^{
  356. NSNumber *result = [sequence objectPassingTest:^ BOOL (NSNumber *value) {
  357. return value.intValue > 0;
  358. }];
  359. expect(result).to(equal(@1));
  360. });
  361. qck_it(@"should return nil if no objects pass the test", ^{
  362. NSNumber *result = [sequence objectPassingTest:^ BOOL (NSNumber *value) {
  363. return value.intValue < 0;
  364. }];
  365. expect(result).to(beNil());
  366. });
  367. });
  368. QuickSpecEnd