RACStreamExamples.m 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671
  1. //
  2. // RACStreamExamples.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 "RACStreamExamples.h"
  11. #import "RACStream.h"
  12. #import "RACUnit.h"
  13. #import "RACTuple.h"
  14. NSString * const RACStreamExamples = @"RACStreamExamples";
  15. NSString * const RACStreamExamplesClass = @"RACStreamExamplesClass";
  16. NSString * const RACStreamExamplesInfiniteStream = @"RACStreamExamplesInfiniteStream";
  17. NSString * const RACStreamExamplesVerifyValuesBlock = @"RACStreamExamplesVerifyValuesBlock";
  18. QuickConfigurationBegin(RACStreamExampleGroups)
  19. + (void)configure:(Configuration *)configuration {
  20. sharedExamples(RACStreamExamples, ^(QCKDSLSharedExampleContext exampleContext) {
  21. __block Class streamClass;
  22. __block void (^verifyValues)(RACStream *, NSArray *);
  23. __block RACStream *infiniteStream;
  24. __block RACStream *(^streamWithValues)(NSArray *);
  25. qck_beforeEach(^{
  26. streamClass = exampleContext()[RACStreamExamplesClass];
  27. verifyValues = exampleContext()[RACStreamExamplesVerifyValuesBlock];
  28. infiniteStream = exampleContext()[RACStreamExamplesInfiniteStream];
  29. streamWithValues = [^(NSArray *values) {
  30. RACStream *stream = [streamClass empty];
  31. for (id value in values) {
  32. stream = [stream concat:[streamClass return:value]];
  33. }
  34. return stream;
  35. } copy];
  36. });
  37. qck_it(@"should return an empty stream", ^{
  38. RACStream *stream = [streamClass empty];
  39. verifyValues(stream, @[]);
  40. });
  41. qck_it(@"should lift a value into a stream", ^{
  42. RACStream *stream = [streamClass return:RACUnit.defaultUnit];
  43. verifyValues(stream, @[ RACUnit.defaultUnit ]);
  44. });
  45. qck_describe(@"-concat:", ^{
  46. qck_it(@"should concatenate two streams", ^{
  47. RACStream *stream = [[streamClass return:@0] concat:[streamClass return:@1]];
  48. verifyValues(stream, @[ @0, @1 ]);
  49. });
  50. qck_it(@"should concatenate three streams", ^{
  51. RACStream *stream = [[[streamClass return:@0] concat:[streamClass return:@1]] concat:[streamClass return:@2]];
  52. verifyValues(stream, @[ @0, @1, @2 ]);
  53. });
  54. qck_it(@"should concatenate around an empty stream", ^{
  55. RACStream *stream = [[[streamClass return:@0] concat:[streamClass empty]] concat:[streamClass return:@2]];
  56. verifyValues(stream, @[ @0, @2 ]);
  57. });
  58. });
  59. qck_it(@"should flatten", ^{
  60. RACStream *stream = [[streamClass return:[streamClass return:RACUnit.defaultUnit]] flatten];
  61. verifyValues(stream, @[ RACUnit.defaultUnit ]);
  62. });
  63. qck_describe(@"-bind:", ^{
  64. qck_it(@"should return the result of binding a single value", ^{
  65. RACStream *stream = [[streamClass return:@0] bind:^{
  66. return ^(NSNumber *value, BOOL *stop) {
  67. NSNumber *newValue = @(value.integerValue + 1);
  68. return [streamClass return:newValue];
  69. };
  70. }];
  71. verifyValues(stream, @[ @1 ]);
  72. });
  73. qck_it(@"should concatenate the result of binding multiple values", ^{
  74. RACStream *baseStream = streamWithValues(@[ @0, @1 ]);
  75. RACStream *stream = [baseStream bind:^{
  76. return ^(NSNumber *value, BOOL *stop) {
  77. NSNumber *newValue = @(value.integerValue + 1);
  78. return [streamClass return:newValue];
  79. };
  80. }];
  81. verifyValues(stream, @[ @1, @2 ]);
  82. });
  83. qck_it(@"should concatenate with an empty result from binding a value", ^{
  84. RACStream *baseStream = streamWithValues(@[ @0, @1, @2 ]);
  85. RACStream *stream = [baseStream bind:^{
  86. return ^(NSNumber *value, BOOL *stop) {
  87. if (value.integerValue == 1) return [streamClass empty];
  88. NSNumber *newValue = @(value.integerValue + 1);
  89. return [streamClass return:newValue];
  90. };
  91. }];
  92. verifyValues(stream, @[ @1, @3 ]);
  93. });
  94. qck_it(@"should terminate immediately when returning nil", ^{
  95. RACStream *stream = [infiniteStream bind:^{
  96. return ^ id (id _, BOOL *stop) {
  97. return nil;
  98. };
  99. }];
  100. verifyValues(stream, @[]);
  101. });
  102. qck_it(@"should terminate after one value when setting 'stop'", ^{
  103. RACStream *stream = [infiniteStream bind:^{
  104. return ^ id (id value, BOOL *stop) {
  105. *stop = YES;
  106. return [streamClass return:value];
  107. };
  108. }];
  109. verifyValues(stream, @[ RACUnit.defaultUnit ]);
  110. });
  111. qck_it(@"should terminate immediately when returning nil and setting 'stop'", ^{
  112. RACStream *stream = [infiniteStream bind:^{
  113. return ^ id (id _, BOOL *stop) {
  114. *stop = YES;
  115. return nil;
  116. };
  117. }];
  118. verifyValues(stream, @[]);
  119. });
  120. qck_it(@"should be restartable even with block state", ^{
  121. NSArray *values = @[ @0, @1, @2 ];
  122. RACStream *baseStream = streamWithValues(values);
  123. RACStream *countingStream = [baseStream bind:^{
  124. __block NSUInteger counter = 0;
  125. return ^(id x, BOOL *stop) {
  126. return [streamClass return:@(counter++)];
  127. };
  128. }];
  129. verifyValues(countingStream, @[ @0, @1, @2 ]);
  130. verifyValues(countingStream, @[ @0, @1, @2 ]);
  131. });
  132. qck_it(@"should be interleavable even with block state", ^{
  133. NSArray *values = @[ @0, @1, @2 ];
  134. RACStream *baseStream = streamWithValues(values);
  135. RACStream *countingStream = [baseStream bind:^{
  136. __block NSUInteger counter = 0;
  137. return ^(id x, BOOL *stop) {
  138. return [streamClass return:@(counter++)];
  139. };
  140. }];
  141. // Just so +zip:reduce: thinks this is a unique stream.
  142. RACStream *anotherStream = [[streamClass empty] concat:countingStream];
  143. RACStream *zipped = [streamClass zip:@[ countingStream, anotherStream ] reduce:^(NSNumber *v1, NSNumber *v2) {
  144. return @(v1.integerValue + v2.integerValue);
  145. }];
  146. verifyValues(zipped, @[ @0, @2, @4 ]);
  147. });
  148. });
  149. qck_describe(@"-flattenMap:", ^{
  150. qck_it(@"should return a single mapped result", ^{
  151. RACStream *stream = [[streamClass return:@0] flattenMap:^(NSNumber *value) {
  152. NSNumber *newValue = @(value.integerValue + 1);
  153. return [streamClass return:newValue];
  154. }];
  155. verifyValues(stream, @[ @1 ]);
  156. });
  157. qck_it(@"should concatenate the results of mapping multiple values", ^{
  158. RACStream *baseStream = streamWithValues(@[ @0, @1 ]);
  159. RACStream *stream = [baseStream flattenMap:^(NSNumber *value) {
  160. NSNumber *newValue = @(value.integerValue + 1);
  161. return [streamClass return:newValue];
  162. }];
  163. verifyValues(stream, @[ @1, @2 ]);
  164. });
  165. qck_it(@"should concatenate with an empty result from mapping a value", ^{
  166. RACStream *baseStream = streamWithValues(@[ @0, @1, @2 ]);
  167. RACStream *stream = [baseStream flattenMap:^(NSNumber *value) {
  168. if (value.integerValue == 1) return [streamClass empty];
  169. NSNumber *newValue = @(value.integerValue + 1);
  170. return [streamClass return:newValue];
  171. }];
  172. verifyValues(stream, @[ @1, @3 ]);
  173. });
  174. qck_it(@"should treat nil streams like empty streams", ^{
  175. RACStream *baseStream = streamWithValues(@[ @0, @1, @2 ]);
  176. RACStream *stream = [baseStream flattenMap:^ RACStream * (NSNumber *value) {
  177. if (value.integerValue == 1) return nil;
  178. NSNumber *newValue = @(value.integerValue + 1);
  179. return [streamClass return:newValue];
  180. }];
  181. verifyValues(stream, @[ @1, @3 ]);
  182. });
  183. });
  184. qck_it(@"should map", ^{
  185. RACStream *baseStream = streamWithValues(@[ @0, @1, @2 ]);
  186. RACStream *stream = [baseStream map:^(NSNumber *value) {
  187. return @(value.integerValue + 1);
  188. }];
  189. verifyValues(stream, @[ @1, @2, @3 ]);
  190. });
  191. qck_it(@"should map and replace", ^{
  192. RACStream *baseStream = streamWithValues(@[ @0, @1, @2 ]);
  193. RACStream *stream = [baseStream mapReplace:RACUnit.defaultUnit];
  194. verifyValues(stream, @[ RACUnit.defaultUnit, RACUnit.defaultUnit, RACUnit.defaultUnit ]);
  195. });
  196. qck_it(@"should filter", ^{
  197. RACStream *baseStream = streamWithValues(@[ @0, @1, @2, @3, @4, @5, @6 ]);
  198. RACStream *stream = [baseStream filter:^ BOOL (NSNumber *value) {
  199. return value.integerValue % 2 == 0;
  200. }];
  201. verifyValues(stream, @[ @0, @2, @4, @6 ]);
  202. });
  203. qck_describe(@"-ignore:", ^{
  204. qck_it(@"should ignore a value", ^{
  205. RACStream *baseStream = streamWithValues(@[ @0, @1, @2, @3, @4, @5, @6 ]);
  206. RACStream *stream = [baseStream ignore:@1];
  207. verifyValues(stream, @[ @0, @2, @3, @4, @5, @6 ]);
  208. });
  209. qck_it(@"should ignore based on object equality", ^{
  210. RACStream *baseStream = streamWithValues(@[ @"0", @"1", @"2", @"3", @"4", @"5", @"6" ]);
  211. NSMutableString *valueToIgnore = [[NSMutableString alloc] init];
  212. [valueToIgnore appendString:@"1"];
  213. RACStream *stream = [baseStream ignore:valueToIgnore];
  214. verifyValues(stream, @[ @"0", @"2", @"3", @"4", @"5", @"6" ]);
  215. });
  216. });
  217. qck_it(@"should start with a value", ^{
  218. RACStream *stream = [[streamClass return:@1] startWith:@0];
  219. verifyValues(stream, @[ @0, @1 ]);
  220. });
  221. qck_describe(@"-skip:", ^{
  222. __block NSArray *values;
  223. __block RACStream *stream;
  224. qck_beforeEach(^{
  225. values = @[ @0, @1, @2 ];
  226. stream = streamWithValues(values);
  227. });
  228. qck_it(@"should skip any valid number of values", ^{
  229. for (NSUInteger i = 0; i < values.count; i++) {
  230. verifyValues([stream skip:i], [values subarrayWithRange:NSMakeRange(i, values.count - i)]);
  231. }
  232. });
  233. qck_it(@"should return an empty stream when skipping too many values", ^{
  234. verifyValues([stream skip:4], @[]);
  235. });
  236. });
  237. qck_describe(@"-take:", ^{
  238. qck_describe(@"with three values", ^{
  239. __block NSArray *values;
  240. __block RACStream *stream;
  241. qck_beforeEach(^{
  242. values = @[ @0, @1, @2 ];
  243. stream = streamWithValues(values);
  244. });
  245. qck_it(@"should take any valid number of values", ^{
  246. for (NSUInteger i = 0; i < values.count; i++) {
  247. verifyValues([stream take:i], [values subarrayWithRange:NSMakeRange(0, i)]);
  248. }
  249. });
  250. qck_it(@"should return the same stream when taking too many values", ^{
  251. verifyValues([stream take:4], values);
  252. });
  253. });
  254. qck_it(@"should take and terminate from an infinite stream", ^{
  255. verifyValues([infiniteStream take:0], @[]);
  256. verifyValues([infiniteStream take:1], @[ RACUnit.defaultUnit ]);
  257. verifyValues([infiniteStream take:2], @[ RACUnit.defaultUnit, RACUnit.defaultUnit ]);
  258. });
  259. qck_it(@"should take and terminate from a single-item stream", ^{
  260. NSArray *values = @[ RACUnit.defaultUnit ];
  261. RACStream *stream = streamWithValues(values);
  262. verifyValues([stream take:1], values);
  263. });
  264. });
  265. qck_describe(@"zip stream creation methods", ^{
  266. __block NSArray *valuesOne;
  267. __block RACStream *streamOne;
  268. __block RACStream *streamTwo;
  269. __block RACStream *streamThree;
  270. __block NSArray *threeStreams;
  271. __block NSArray *oneStreamTuples;
  272. __block NSArray *twoStreamTuples;
  273. __block NSArray *threeStreamTuples;
  274. qck_beforeEach(^{
  275. valuesOne = @[ @"Ada", @"Bob", @"Dea" ];
  276. NSArray *valuesTwo = @[ @"eats", @"cooks", @"jumps" ];
  277. NSArray *valuesThree = @[ @"fish", @"bear", @"rock" ];
  278. streamOne = streamWithValues(valuesOne);
  279. streamTwo = streamWithValues(valuesTwo);
  280. streamThree = streamWithValues(valuesThree);
  281. threeStreams = @[ streamOne, streamTwo, streamThree ];
  282. oneStreamTuples = @[
  283. RACTuplePack(valuesOne[0]),
  284. RACTuplePack(valuesOne[1]),
  285. RACTuplePack(valuesOne[2]),
  286. ];
  287. twoStreamTuples = @[
  288. RACTuplePack(valuesOne[0], valuesTwo[0]),
  289. RACTuplePack(valuesOne[1], valuesTwo[1]),
  290. RACTuplePack(valuesOne[2], valuesTwo[2]),
  291. ];
  292. threeStreamTuples = @[
  293. RACTuplePack(valuesOne[0], valuesTwo[0], valuesThree[0]),
  294. RACTuplePack(valuesOne[1], valuesTwo[1], valuesThree[1]),
  295. RACTuplePack(valuesOne[2], valuesTwo[2], valuesThree[2]),
  296. ];
  297. });
  298. qck_describe(@"-zipWith:", ^{
  299. qck_it(@"should make a stream of tuples", ^{
  300. RACStream *stream = [streamOne zipWith:streamTwo];
  301. verifyValues(stream, twoStreamTuples);
  302. });
  303. qck_it(@"should truncate streams", ^{
  304. RACStream *shortStream = streamWithValues(@[ @"now", @"later" ]);
  305. RACStream *stream = [streamOne zipWith:shortStream];
  306. verifyValues(stream, @[
  307. RACTuplePack(valuesOne[0], @"now"),
  308. RACTuplePack(valuesOne[1], @"later")
  309. ]);
  310. });
  311. qck_it(@"should work on infinite streams", ^{
  312. RACStream *stream = [streamOne zipWith:infiniteStream];
  313. verifyValues(stream, @[
  314. RACTuplePack(valuesOne[0], RACUnit.defaultUnit),
  315. RACTuplePack(valuesOne[1], RACUnit.defaultUnit),
  316. RACTuplePack(valuesOne[2], RACUnit.defaultUnit)
  317. ]);
  318. });
  319. qck_it(@"should handle multiples of the same stream", ^{
  320. RACStream *stream = [streamOne zipWith:streamOne];
  321. verifyValues(stream, @[
  322. RACTuplePack(valuesOne[0], valuesOne[0]),
  323. RACTuplePack(valuesOne[1], valuesOne[1]),
  324. RACTuplePack(valuesOne[2], valuesOne[2]),
  325. ]);
  326. });
  327. });
  328. qck_describe(@"+zip:reduce:", ^{
  329. qck_it(@"should reduce values", ^{
  330. RACStream *stream = [streamClass zip:threeStreams reduce:^ NSString * (id x, id y, id z) {
  331. return [NSString stringWithFormat:@"%@ %@ %@", x, y, z];
  332. }];
  333. verifyValues(stream, @[ @"Ada eats fish", @"Bob cooks bear", @"Dea jumps rock" ]);
  334. });
  335. qck_it(@"should truncate streams", ^{
  336. RACStream *shortStream = streamWithValues(@[ @"now", @"later" ]);
  337. NSArray *streams = [threeStreams arrayByAddingObject:shortStream];
  338. RACStream *stream = [streamClass zip:streams reduce:^ NSString * (id w, id x, id y, id z) {
  339. return [NSString stringWithFormat:@"%@ %@ %@ %@", w, x, y, z];
  340. }];
  341. verifyValues(stream, @[ @"Ada eats fish now", @"Bob cooks bear later" ]);
  342. });
  343. qck_it(@"should work on infinite streams", ^{
  344. NSArray *streams = [threeStreams arrayByAddingObject:infiniteStream];
  345. RACStream *stream = [streamClass zip:streams reduce:^ NSString * (id w, id x, id y, id z) {
  346. return [NSString stringWithFormat:@"%@ %@ %@", w, x, y];
  347. }];
  348. verifyValues(stream, @[ @"Ada eats fish", @"Bob cooks bear", @"Dea jumps rock" ]);
  349. });
  350. qck_it(@"should handle multiples of the same stream", ^{
  351. NSArray *streams = @[ streamOne, streamOne, streamTwo, streamThree, streamTwo, streamThree ];
  352. RACStream *stream = [streamClass zip:streams reduce:^ NSString * (id x1, id x2, id y1, id z1, id y2, id z2) {
  353. return [NSString stringWithFormat:@"%@ %@ %@ %@ %@ %@", x1, x2, y1, z1, y2, z2];
  354. }];
  355. verifyValues(stream, @[ @"Ada Ada eats fish eats fish", @"Bob Bob cooks bear cooks bear", @"Dea Dea jumps rock jumps rock" ]);
  356. });
  357. });
  358. qck_describe(@"+zip:", ^{
  359. qck_it(@"should make a stream of tuples out of single value", ^{
  360. RACStream *stream = [streamClass zip:@[ streamOne ]];
  361. verifyValues(stream, oneStreamTuples);
  362. });
  363. qck_it(@"should make a stream of tuples out of an array of streams", ^{
  364. RACStream *stream = [streamClass zip:threeStreams];
  365. verifyValues(stream, threeStreamTuples);
  366. });
  367. qck_it(@"should make an empty stream if given an empty array", ^{
  368. RACStream *stream = [streamClass zip:@[]];
  369. verifyValues(stream, @[]);
  370. });
  371. qck_it(@"should make a stream of tuples out of an enumerator of streams", ^{
  372. RACStream *stream = [streamClass zip:threeStreams.objectEnumerator];
  373. verifyValues(stream, threeStreamTuples);
  374. });
  375. qck_it(@"should make an empty stream if given an empty enumerator", ^{
  376. RACStream *stream = [streamClass zip:@[].objectEnumerator];
  377. verifyValues(stream, @[]);
  378. });
  379. });
  380. });
  381. qck_describe(@"+concat:", ^{
  382. __block NSArray *streams = nil;
  383. __block NSArray *result = nil;
  384. qck_beforeEach(^{
  385. RACStream *a = [streamClass return:@0];
  386. RACStream *b = [streamClass empty];
  387. RACStream *c = streamWithValues(@[ @1, @2, @3 ]);
  388. RACStream *d = [streamClass return:@4];
  389. RACStream *e = [streamClass return:@5];
  390. RACStream *f = [streamClass empty];
  391. RACStream *g = [streamClass empty];
  392. RACStream *h = streamWithValues(@[ @6, @7 ]);
  393. streams = @[ a, b, c, d, e, f, g, h ];
  394. result = @[ @0, @1, @2, @3, @4, @5, @6, @7 ];
  395. });
  396. qck_it(@"should concatenate an array of streams", ^{
  397. RACStream *stream = [streamClass concat:streams];
  398. verifyValues(stream, result);
  399. });
  400. qck_it(@"should concatenate an enumerator of streams", ^{
  401. RACStream *stream = [streamClass concat:streams.objectEnumerator];
  402. verifyValues(stream, result);
  403. });
  404. });
  405. qck_describe(@"scanning", ^{
  406. NSArray *values = @[ @1, @2, @3, @4 ];
  407. __block RACStream *stream;
  408. qck_beforeEach(^{
  409. stream = streamWithValues(values);
  410. });
  411. qck_it(@"should scan", ^{
  412. RACStream *scanned = [stream scanWithStart:@0 reduce:^(NSNumber *running, NSNumber *next) {
  413. return @(running.integerValue + next.integerValue);
  414. }];
  415. verifyValues(scanned, @[ @1, @3, @6, @10 ]);
  416. });
  417. qck_it(@"should scan with index", ^{
  418. RACStream *scanned = [stream scanWithStart:@0 reduceWithIndex:^(NSNumber *running, NSNumber *next, NSUInteger index) {
  419. return @(running.integerValue + next.integerValue + (NSInteger)index);
  420. }];
  421. verifyValues(scanned, @[ @1, @4, @9, @16 ]);
  422. });
  423. });
  424. qck_describe(@"taking with a predicate", ^{
  425. NSArray *values = @[ @0, @1, @2, @3, @0, @2, @4 ];
  426. __block RACStream *stream;
  427. qck_beforeEach(^{
  428. stream = streamWithValues(values);
  429. });
  430. qck_it(@"should take until a predicate is true", ^{
  431. RACStream *taken = [stream takeUntilBlock:^ BOOL (NSNumber *x) {
  432. return x.integerValue >= 3;
  433. }];
  434. verifyValues(taken, @[ @0, @1, @2 ]);
  435. });
  436. qck_it(@"should take while a predicate is true", ^{
  437. RACStream *taken = [stream takeWhileBlock:^ BOOL (NSNumber *x) {
  438. return x.integerValue <= 1;
  439. }];
  440. verifyValues(taken, @[ @0, @1 ]);
  441. });
  442. qck_it(@"should take a full stream", ^{
  443. RACStream *taken = [stream takeWhileBlock:^ BOOL (NSNumber *x) {
  444. return x.integerValue <= 10;
  445. }];
  446. verifyValues(taken, values);
  447. });
  448. qck_it(@"should return an empty stream", ^{
  449. RACStream *taken = [stream takeWhileBlock:^ BOOL (NSNumber *x) {
  450. return x.integerValue < 0;
  451. }];
  452. verifyValues(taken, @[]);
  453. });
  454. qck_it(@"should terminate an infinite stream", ^{
  455. RACStream *infiniteCounter = [infiniteStream scanWithStart:@0 reduce:^(NSNumber *running, id _) {
  456. return @(running.unsignedIntegerValue + 1);
  457. }];
  458. RACStream *taken = [infiniteCounter takeWhileBlock:^ BOOL (NSNumber *x) {
  459. return x.integerValue <= 5;
  460. }];
  461. verifyValues(taken, @[ @1, @2, @3, @4, @5 ]);
  462. });
  463. });
  464. qck_describe(@"skipping with a predicate", ^{
  465. NSArray *values = @[ @0, @1, @2, @3, @0, @2, @4 ];
  466. __block RACStream *stream;
  467. qck_beforeEach(^{
  468. stream = streamWithValues(values);
  469. });
  470. qck_it(@"should skip until a predicate is true", ^{
  471. RACStream *taken = [stream skipUntilBlock:^ BOOL (NSNumber *x) {
  472. return x.integerValue >= 3;
  473. }];
  474. verifyValues(taken, @[ @3, @0, @2, @4 ]);
  475. });
  476. qck_it(@"should skip while a predicate is true", ^{
  477. RACStream *taken = [stream skipWhileBlock:^ BOOL (NSNumber *x) {
  478. return x.integerValue <= 1;
  479. }];
  480. verifyValues(taken, @[ @2, @3, @0, @2, @4 ]);
  481. });
  482. qck_it(@"should skip a full stream", ^{
  483. RACStream *taken = [stream skipWhileBlock:^ BOOL (NSNumber *x) {
  484. return x.integerValue <= 10;
  485. }];
  486. verifyValues(taken, @[]);
  487. });
  488. qck_it(@"should finish skipping immediately", ^{
  489. RACStream *taken = [stream skipWhileBlock:^ BOOL (NSNumber *x) {
  490. return x.integerValue < 0;
  491. }];
  492. verifyValues(taken, values);
  493. });
  494. });
  495. qck_describe(@"-combinePreviousWithStart:reduce:", ^{
  496. NSArray *values = @[ @1, @2, @3 ];
  497. __block RACStream *stream;
  498. qck_beforeEach(^{
  499. stream = streamWithValues(values);
  500. });
  501. qck_it(@"should pass the previous next into the reduce block", ^{
  502. NSMutableArray *previouses = [NSMutableArray array];
  503. RACStream *mapped = [stream combinePreviousWithStart:nil reduce:^(id previous, id next) {
  504. [previouses addObject:previous ?: RACTupleNil.tupleNil];
  505. return next;
  506. }];
  507. verifyValues(mapped, @[ @1, @2, @3 ]);
  508. NSArray *expected = @[ RACTupleNil.tupleNil, @1, @2 ];
  509. expect(previouses).to(equal(expected));
  510. });
  511. qck_it(@"should send the combined value", ^{
  512. RACStream *mapped = [stream combinePreviousWithStart:@1 reduce:^(NSNumber *previous, NSNumber *next) {
  513. return [NSString stringWithFormat:@"%lu - %lu", (unsigned long)previous.unsignedIntegerValue, (unsigned long)next.unsignedIntegerValue];
  514. }];
  515. verifyValues(mapped, @[ @"1 - 1", @"1 - 2", @"2 - 3" ]);
  516. });
  517. });
  518. qck_it(@"should reduce tuples", ^{
  519. RACStream *stream = streamWithValues(@[
  520. RACTuplePack(@"foo", @"bar"),
  521. RACTuplePack(@"buzz", @"baz"),
  522. RACTuplePack(@"", @"_")
  523. ]);
  524. RACStream *reduced = [stream reduceEach:^(NSString *a, NSString *b) {
  525. return [a stringByAppendingString:b];
  526. }];
  527. verifyValues(reduced, @[ @"foobar", @"buzzbaz", @"_" ]);
  528. });
  529. });
  530. }
  531. QuickConfigurationEnd