RACSerialDisposableSpec.m 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163
  1. //
  2. // RACSerialDisposableSpec.m
  3. // ReactiveCocoa
  4. //
  5. // Created by Justin Spahr-Summers on 2013-07-22.
  6. // Copyright (c) 2013 GitHub, Inc. All rights reserved.
  7. //
  8. #import <Quick/Quick.h>
  9. #import <Nimble/Nimble.h>
  10. #import "RACSerialDisposable.h"
  11. QuickSpecBegin(RACSerialDisposableSpec)
  12. qck_it(@"should initialize with -init", ^{
  13. RACSerialDisposable *serial = [[RACSerialDisposable alloc] init];
  14. expect(serial).notTo(beNil());
  15. expect(serial.disposable).to(beNil());
  16. });
  17. qck_it(@"should initialize an inner disposable with -initWithBlock:", ^{
  18. __block BOOL disposed = NO;
  19. RACSerialDisposable *serial = [RACSerialDisposable disposableWithBlock:^{
  20. disposed = YES;
  21. }];
  22. expect(serial).notTo(beNil());
  23. expect(serial.disposable).notTo(beNil());
  24. [serial.disposable dispose];
  25. expect(@(serial.disposed)).to(beFalsy());
  26. expect(@(disposed)).to(beTruthy());
  27. });
  28. qck_it(@"should initialize with a disposable", ^{
  29. RACDisposable *inner = [[RACDisposable alloc] init];
  30. RACSerialDisposable *serial = [RACSerialDisposable serialDisposableWithDisposable:inner];
  31. expect(serial).notTo(beNil());
  32. expect(serial.disposable).to(equal(inner));
  33. });
  34. qck_it(@"should dispose of the inner disposable", ^{
  35. __block BOOL disposed = NO;
  36. RACDisposable *inner = [RACDisposable disposableWithBlock:^{
  37. disposed = YES;
  38. }];
  39. RACSerialDisposable *serial = [RACSerialDisposable serialDisposableWithDisposable:inner];
  40. expect(@(serial.disposed)).to(beFalsy());
  41. expect(@(disposed)).to(beFalsy());
  42. [serial dispose];
  43. expect(@(serial.disposed)).to(beTruthy());
  44. expect(serial.disposable).to(beNil());
  45. expect(@(disposed)).to(beTruthy());
  46. });
  47. qck_it(@"should dispose of a new inner disposable if it's already been disposed", ^{
  48. __block BOOL disposed = NO;
  49. RACDisposable *inner = [RACDisposable disposableWithBlock:^{
  50. disposed = YES;
  51. }];
  52. RACSerialDisposable *serial = [[RACSerialDisposable alloc] init];
  53. expect(@(serial.disposed)).to(beFalsy());
  54. [serial dispose];
  55. expect(@(serial.disposed)).to(beTruthy());
  56. expect(@(disposed)).to(beFalsy());
  57. serial.disposable = inner;
  58. expect(@(disposed)).to(beTruthy());
  59. expect(serial.disposable).to(beNil());
  60. });
  61. qck_it(@"should allow the inner disposable to be set to nil", ^{
  62. __block BOOL disposed = NO;
  63. RACDisposable *inner = [RACDisposable disposableWithBlock:^{
  64. disposed = YES;
  65. }];
  66. RACSerialDisposable *serial = [RACSerialDisposable serialDisposableWithDisposable:inner];
  67. expect(@(disposed)).to(beFalsy());
  68. serial.disposable = nil;
  69. expect(serial.disposable).to(beNil());
  70. serial.disposable = inner;
  71. expect(serial.disposable).to(equal(inner));
  72. [serial dispose];
  73. expect(@(disposed)).to(beTruthy());
  74. expect(serial.disposable).to(beNil());
  75. });
  76. qck_it(@"should swap inner disposables", ^{
  77. __block BOOL firstDisposed = NO;
  78. RACDisposable *first = [RACDisposable disposableWithBlock:^{
  79. firstDisposed = YES;
  80. }];
  81. __block BOOL secondDisposed = NO;
  82. RACDisposable *second = [RACDisposable disposableWithBlock:^{
  83. secondDisposed = YES;
  84. }];
  85. RACSerialDisposable *serial = [RACSerialDisposable serialDisposableWithDisposable:first];
  86. expect([serial swapInDisposable:second]).to(equal(first));
  87. expect(@(serial.disposed)).to(beFalsy());
  88. expect(@(firstDisposed)).to(beFalsy());
  89. expect(@(secondDisposed)).to(beFalsy());
  90. [serial dispose];
  91. expect(@(serial.disposed)).to(beTruthy());
  92. expect(serial.disposable).to(beNil());
  93. expect(@(firstDisposed)).to(beFalsy());
  94. expect(@(secondDisposed)).to(beTruthy());
  95. });
  96. qck_it(@"should release the inner disposable upon deallocation", ^{
  97. __weak RACDisposable *weakInnerDisposable;
  98. __weak RACSerialDisposable *weakSerialDisposable;
  99. @autoreleasepool {
  100. RACDisposable *innerDisposable __attribute__((objc_precise_lifetime)) = [[RACDisposable alloc] init];
  101. weakInnerDisposable = innerDisposable;
  102. RACSerialDisposable *serialDisposable __attribute__((objc_precise_lifetime)) = [[RACSerialDisposable alloc] init];
  103. serialDisposable.disposable = innerDisposable;
  104. weakSerialDisposable = serialDisposable;
  105. }
  106. expect(weakSerialDisposable).to(beNil());
  107. expect(weakInnerDisposable).to(beNil());
  108. });
  109. qck_it(@"should not crash when racing between swapInDisposable and disposable", ^{
  110. __block BOOL stop = NO;
  111. dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (long long)(0.1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
  112. stop = YES;
  113. });
  114. RACSerialDisposable *serialDisposable = [[RACSerialDisposable alloc] init];
  115. dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
  116. while (!stop) {
  117. [serialDisposable swapInDisposable:[RACDisposable disposableWithBlock:^{}]];
  118. }
  119. });
  120. dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
  121. while (!stop) {
  122. (void)[serialDisposable disposable];
  123. }
  124. });
  125. expect(@(stop)).toEventually(beTruthy());
  126. });
  127. QuickSpecEnd