Skip to content

Commit

Permalink
Merge pull request #256 from inkling/jeff/241_deprecate_SLWaitUntilTrue
Browse files Browse the repository at this point in the history
Deprecate `SLWaitUntilTrue` and `SLWaitUntilTrueRetryDelay`. (resolves #241)
  • Loading branch information
aegolden committed Aug 29, 2014
2 parents 92a053c + f7c1d97 commit b96ce92
Show file tree
Hide file tree
Showing 5 changed files with 113 additions and 30 deletions.
43 changes: 34 additions & 9 deletions Sources/Classes/SLTestAssertions.h
Expand Up @@ -13,10 +13,18 @@
/// Thrown if a test assertion fails.
extern NSString *const SLTestAssertionFailedException;

/// The interval for which `SLAssertTrueWithTimeout` and `SLWaitUntilTrue`
/// The interval for which `SLAssertTrueWithTimeout` and `SLIsTrueWithTimeout`
/// wait before re-evaluating their conditions.
extern const NSTimeInterval SLWaitUntilTrueRetryDelay;
extern const NSTimeInterval SLIsTrueRetryDelay;

// Log the deprecation warning asynchronously in case the constant was referenced
// from the main thread (possible).
#define SLWaitUntilTrueRetryDelay ({\
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{\
[[SLLogger sharedLogger] logWarning:@"As of v1.2, `SLWaitUntilTrueRetryDelay` is deprecated: use `SLIsTrueRetryDelay` instead. `SLWaitUntilTrueRetryDelay` will be removed for v2.0."];\
});\
SLIsTrueRetryDelay;\
})

#pragma mark - Test Assertions

Expand Down Expand Up @@ -113,7 +121,7 @@ NSString *__reason = [NSString stringWithFormat:@"\"%@\" should be true.%@", \
#define SLAssertTrueWithTimeout(expression, timeout, failureDescription, ...) do {\
[SLTest recordLastKnownFile:__FILE__ line:__LINE__]; \
\
if (!SLWaitUntilTrue(expression, timeout)) { \
if (!SLIsTrueWithTimeout(expression, timeout)) { \
NSString *reason = [NSString stringWithFormat:@"\"%@\" did not become true within %g seconds.%@", \
@(#expression), (NSTimeInterval)timeout, SLComposeString(@" ", failureDescription, ##__VA_ARGS__)]; \
@throw [NSException exceptionWithName:SLTestAssertionFailedException reason:reason userInfo:nil]; \
Expand All @@ -127,18 +135,18 @@ NSString *reason = [NSString stringWithFormat:@"\"%@\" did not become true withi
The macro re-evaluates the condition at small intervals.
The great advantage to using `SLWaitUntilTrue` instead of `-wait:` is that `SLWaitUntilTrue`
The great advantage to using `SLIsTrueWithTimeout` instead of `-wait:` is that `SLIsTrueWithTimeout`
need not wait for the entirety of the specified timeout if the condition becomes true
before the timeout elapses. This can lead to faster tests, and makes it feasible
to allow even longer timeouts when using `SLWaitUntilTrue` than when using
to allow even longer timeouts when using `SLIsTrueWithTimeout` than when using
`-wait:`.
The difference between `SLWaitUntilTrue` and `SLAssertTrueWithTimeout` is that `SLWaitUntilTrue`
The difference between `SLIsTrueWithTimeout` and `SLAssertTrueWithTimeout` is that `SLIsTrueWithTimeout`
may be used to wait upon a condition which might, with equal validity, evaluate to true _or_ false.
For example:
// wait for a confirmation message that may or may not appear, and dismiss it
BOOL messageDisplayed = SLWaitUntilTrue([UIAElement(messageDismissButton) isValidAndVisible], 10.0);
BOOL messageDisplayed = SLIsTrueWithTimeout([UIAElement(messageDismissButton) isValidAndVisible], 10.0);
if (messageDisplayed) {
[UIAElement(messageDismissButton) tap];
}
Expand All @@ -147,15 +155,32 @@ NSString *reason = [NSString stringWithFormat:@"\"%@\" did not become true withi
@param timeout The interval for which to wait.
@return Whether or not the expression evaluated to true before the timeout was reached.
*/
#define SLWaitUntilTrue(expression, timeout) ({\
#define SLIsTrueWithTimeout(expression, timeout) ({\
NSDate *_startDate = [NSDate date];\
BOOL _expressionTrue = NO;\
while (!(_expressionTrue = (expression)) && ([[NSDate date] timeIntervalSinceDate:_startDate] < timeout)) {\
[NSThread sleepForTimeInterval:SLWaitUntilTrueRetryDelay];\
[NSThread sleepForTimeInterval:SLIsTrueRetryDelay];\
}\
_expressionTrue;\
})

/**
Suspends test execution until the specified expression becomes true or the
specified timeout is reached, and then returns the value of the specified
expression at the moment of returning.
@warning As of v1.2, `SLWaitUntilTrue` is deprecated: use `SLIsTrueWithTimeout`
instead. `SLWaitUntilTrue` will be removed for v2.0.
@param expression A boolean expression on whose truth the test should wait.
@param timeout The interval for which to wait.
@return Whether or not the expression evaluated to true before the timeout was reached.
*/
#define SLWaitUntilTrue(expression, timeout) ({\
[[SLLogger sharedLogger] logWarning:@"As of v1.2, `SLWaitUntilTrue` is deprecated: use `SLIsTrueWithTimeout` instead. `SLWaitUntilTrue` will be removed for v2.0."];\
SLIsTrueWithTimeout(expression, timeout);\
})

/**
Fails the test case if the specified expression is true.
Expand Down
2 changes: 1 addition & 1 deletion Sources/Classes/SLTestAssertions.m
Expand Up @@ -9,4 +9,4 @@
#import "SLTestAssertions.h"

NSString *const SLTestAssertionFailedException = @"SLTestCaseAssertionFailedException";
const NSTimeInterval SLWaitUntilTrueRetryDelay = 0.25;
const NSTimeInterval SLIsTrueRetryDelay = 0.25;
92 changes: 72 additions & 20 deletions Unit Tests/SLTestTests.m
Expand Up @@ -1395,7 +1395,7 @@ - (void)testSLAssertTrueWithTimeoutDoesNotThrowAndReturnsImmediatelyWhenConditio

NSTimeInterval endTimeInterval = [NSDate timeIntervalSinceReferenceDate];
// note that `SLAssertTrueWithTimeout` should not wait at all here, thus the variability
// is not `SLWaitUntilTrueRetryDelay` like the cases below
// is not `SLIsTrueRetryDelay` like the cases below
NSTimeInterval waitTimeInterval = endTimeInterval - startTimeInterval;
STAssertTrue(waitTimeInterval < .01, @"Test should not have waited for an appreciable interval.");
}] testOne];
Expand Down Expand Up @@ -1425,7 +1425,7 @@ - (void)testSLAssertTrueWithTimeoutDoesNotThrowAndReturnsImmediatelyAfterConditi
NSTimeInterval endTimeInterval = [NSDate timeIntervalSinceReferenceDate];
// check that the test waited for about the amount of time for the condition to evaluate to true
NSTimeInterval waitTimeInterval = endTimeInterval - startTimeInterval;
STAssertTrue(waitTimeInterval - truthTimeout < SLWaitUntilTrueRetryDelay,
STAssertTrue(waitTimeInterval - truthTimeout < SLIsTrueRetryDelay,
@"Test should have only waited for about the amount of time necessary for the condition to become true.");
}] testThree];

Expand All @@ -1449,17 +1449,17 @@ - (void)testSLAssertTrueWithTimeoutThrowsIfConditionIsStillFalseAtEndOfTimeout {

NSTimeInterval endTimeInterval = [NSDate timeIntervalSinceReferenceDate];
NSTimeInterval waitTimeInterval = endTimeInterval - startTimeInterval;
STAssertTrue(waitTimeInterval - timeout < SLWaitUntilTrueRetryDelay,
STAssertTrue(waitTimeInterval - timeout < SLIsTrueRetryDelay,
@"Test should have waited for the specified timeout.");
}] testTwo];

SLRunTestsAndWaitUntilFinished([NSSet setWithObject:testClass], nil);
STAssertNoThrow([testMock verify], @"Test case did not execute as expected.");
}

#pragma mark -SLWaitUntilTrue
#pragma mark -SLIsTrueWithTimeout

- (void)testSLWaitUntilTrueDoesNotThrowAndReturnsYESImmediatelyWhenConditionIsTrueUponWait {
- (void)testSLIsTrueWithTimeoutDoesNotThrowAndReturnsYESImmediatelyWhenConditionIsTrueUponWait {
Class testClass = [TestWithSomeTestCases class];
id testMock = [OCMockObject partialMockForClass:testClass];

Expand All @@ -1468,16 +1468,16 @@ - (void)testSLWaitUntilTrueDoesNotThrowAndReturnsYESImmediatelyWhenConditionIsTr
SLTest *test = [invocation target];
NSTimeInterval startTimeInterval = [NSDate timeIntervalSinceReferenceDate];

BOOL slWaitUntilTrueReturnValue;
STAssertNoThrow(slWaitUntilTrueReturnValue = [test SLWaitUntilTrue:^BOOL{
BOOL slIsTrueWithTimeoutReturnValue;
STAssertNoThrow(slIsTrueWithTimeoutReturnValue = [test SLIsTrue:^BOOL{
return YES;
} withTimeout:1.5], @"Assertion should not have failed.");

STAssertTrue(slWaitUntilTrueReturnValue, @"SLWaitUntilTrue should have returned YES");
STAssertTrue(slIsTrueWithTimeoutReturnValue, @"`SLIsTrueWithTimeout` should have returned YES");

NSTimeInterval endTimeInterval = [NSDate timeIntervalSinceReferenceDate];
// note that `SLWaitUntilTrue` should not wait at all here, thus the variability
// is not `SLWaitUntilTrueRetryDelay` like the cases below
// note that `SLIsTrueWithTimeout` should not wait at all here, thus the variability
// is not `SLIsTrueRetryDelay` like the cases below
NSTimeInterval waitTimeInterval = endTimeInterval - startTimeInterval;
STAssertTrue(waitTimeInterval < .01, @"Test should not have waited for an appreciable interval.");
}] testOne];
Expand All @@ -1486,7 +1486,7 @@ - (void)testSLWaitUntilTrueDoesNotThrowAndReturnsYESImmediatelyWhenConditionIsTr
STAssertNoThrow([testMock verify], @"Test case did not execute as expected.");
}

- (void)testSLWaitUntilTrueDoesNotThrowAndReturnsYESImmediatelyAfterConditionBecomesTrue {
- (void)testSLIsTrueWithTimeoutDoesNotThrowAndReturnsYESImmediatelyAfterConditionBecomesTrue {
Class testClass = [TestWithSomeTestCases class];
id testMock = [OCMockObject partialMockForClass:testClass];

Expand All @@ -1498,27 +1498,27 @@ - (void)testSLWaitUntilTrueDoesNotThrowAndReturnsYESImmediatelyAfterConditionBec

NSTimeInterval waitTimeout = 1.5;
NSTimeInterval truthTimeout = 1.0;
BOOL slWaitUntilTrueReturnValue;
STAssertNoThrow(slWaitUntilTrueReturnValue = [test SLWaitUntilTrue:^BOOL{
BOOL slIsTrueWithTimeoutReturnValue;
STAssertNoThrow(slIsTrueWithTimeoutReturnValue = [test SLIsTrue:^BOOL{
NSTimeInterval endTimeInterval = [NSDate timeIntervalSinceReferenceDate];
NSTimeInterval waitInterval = endTimeInterval - startTimeInterval;
return (waitInterval >= truthTimeout);
} withTimeout:waitTimeout], @"Assertion should not have failed.");

STAssertTrue(slWaitUntilTrueReturnValue, @"SLWaitUntilTrue should have returned YES");
STAssertTrue(slIsTrueWithTimeoutReturnValue, @"`SLIsTrueWithTimeout` should have returned YES");

NSTimeInterval endTimeInterval = [NSDate timeIntervalSinceReferenceDate];
// check that the test waited for about the amount of time for the condition to evaluate to true
NSTimeInterval waitTimeInterval = endTimeInterval - startTimeInterval;
STAssertTrue(waitTimeInterval - truthTimeout < SLWaitUntilTrueRetryDelay,
STAssertTrue(waitTimeInterval - truthTimeout < SLIsTrueRetryDelay,
@"Test should have only waited for about the amount of time necessary for the condition to become true.");
}] testThree];

SLRunTestsAndWaitUntilFinished([NSSet setWithObject:testClass], nil);
STAssertNoThrow([testMock verify], @"Test case did not execute as expected.");
}

- (void)testSLWaitUntilTrueDoesNotThrowIfConditionIsStillFalseAtEndOfTimeout {
- (void)testSLIsTrueWithTimeoutDoesNotThrowIfConditionIsStillFalseAtEndOfTimeout {
Class testClass = [TestWithSomeTestCases class];
id testMock = [OCMockObject partialMockForClass:testClass];

Expand All @@ -1528,23 +1528,75 @@ - (void)testSLWaitUntilTrueDoesNotThrowIfConditionIsStillFalseAtEndOfTimeout {
NSTimeInterval startTimeInterval = [NSDate timeIntervalSinceReferenceDate];

NSTimeInterval timeout = 1.5;
BOOL slWaitUntilTrueReturnValue;
STAssertNoThrow(slWaitUntilTrueReturnValue = [test SLWaitUntilTrue:^BOOL{
BOOL slIsTrueWithTimeoutReturnValue;
STAssertNoThrow(slIsTrueWithTimeoutReturnValue = [test SLIsTrue:^BOOL{
return NO;
} withTimeout:timeout], @"Assertion should have failed.");

STAssertFalse(slWaitUntilTrueReturnValue, @"SLWaitUntilTrue should have returned YES");
STAssertFalse(slIsTrueWithTimeoutReturnValue, @"`SLIsTrueWithTimeout` should have returned YES");

NSTimeInterval endTimeInterval = [NSDate timeIntervalSinceReferenceDate];
NSTimeInterval waitTimeInterval = endTimeInterval - startTimeInterval;
STAssertTrue(waitTimeInterval - timeout < SLWaitUntilTrueRetryDelay,
STAssertTrue(waitTimeInterval - timeout < SLIsTrueRetryDelay,
@"Test should have waited for the specified timeout.");
}] testTwo];

SLRunTestsAndWaitUntilFinished([NSSet setWithObject:testClass], nil);
STAssertNoThrow([testMock verify], @"Test case did not execute as expected.");
}

- (void)testSLWaitUntilTrueIsDeprecated {
[[_loggerMock expect] logWarning:@"As of v1.2, `SLWaitUntilTrue` is deprecated: use `SLIsTrueWithTimeout` instead. `SLWaitUntilTrue` will be removed for v2.0."];

// To test that `SLWaitUntilTrue` works until it's removed, replicate one of the
// `SLIsTrueWithTimeout` test cases from above (`testSLIsTrueWithTimeoutDoesNotThrowAndReturnsYESImmediatelyAfterConditionBecomesTrue`).
// This should be sufficient considering that `SLWaitUntilTrue` is now a wrapper
// around `SLIsTrueWithTimeout` anyway.

Class testClass = [TestWithSomeTestCases class];
id testMock = [OCMockObject partialMockForClass:testClass];

// have "testThree" wait on a condition that evaluates to false initially,
// then to true partway through the timeout
[[[testMock expect] andDo:^(NSInvocation *invocation) {
SLTest *test = [invocation target];
NSTimeInterval startTimeInterval = [NSDate timeIntervalSinceReferenceDate];

NSTimeInterval waitTimeout = 1.5;
NSTimeInterval truthTimeout = 1.0;
BOOL slWaitUntilTrueReturnValue;
STAssertNoThrow(slWaitUntilTrueReturnValue = [test SLWaitUntilTrue:^BOOL{
NSTimeInterval endTimeInterval = [NSDate timeIntervalSinceReferenceDate];
NSTimeInterval waitInterval = endTimeInterval - startTimeInterval;
return (waitInterval >= truthTimeout);
} withTimeout:waitTimeout], @"Assertion should not have failed.");

STAssertTrue(slWaitUntilTrueReturnValue, @"`SLWaitUntilTrue` should have returned YES");

NSTimeInterval endTimeInterval = [NSDate timeIntervalSinceReferenceDate];
// check that the test waited for about the amount of time for the condition to evaluate to true
NSTimeInterval waitTimeInterval = endTimeInterval - startTimeInterval;
STAssertTrue(waitTimeInterval - truthTimeout < SLIsTrueRetryDelay,
@"Test should have only waited for about the amount of time necessary for the condition to become true.");
}] testThree];

SLRunTestsAndWaitUntilFinished([NSSet setWithObject:testClass], nil);
STAssertNoThrow([testMock verify], @"Test case did not execute as expected.");

STAssertNoThrow([_loggerMock verify], @"Use of `SLWaitUntilTrue` should have caused a warning to be logged.");
}

- (void)testSLWaitUntilTrueRetryDelayIsDeprecated {
[[_loggerMock expect] logWarning:@"As of v1.2, `SLWaitUntilTrueRetryDelay` is deprecated: use `SLIsTrueRetryDelay` instead. `SLWaitUntilTrueRetryDelay` will be removed for v2.0."];

NSTimeInterval retryDelay = SLWaitUntilTrueRetryDelay;
// spin the run loop to allow the warning to be logged asynchronously
[[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.1]];

STAssertNoThrow([_loggerMock verify], @"Use of `SLWaitUntilTrueRetryDelay` should have caused a warning to be logged.");
STAssertTrue(retryDelay == SLIsTrueRetryDelay, @"`SLWaitUntilTrueRetryDelay` should work until it is removed.");
}

#pragma mark -SLAssertThrows

- (void)testSLAssertThrowsThrowsIffDoesNotThrowException {
Expand Down
1 change: 1 addition & 0 deletions Unit Tests/TestUtilities.h
Expand Up @@ -52,6 +52,7 @@ extern void SLRunTestsUsingSeedAndWaitUntilFinished(NSSet *tests, unsigned int s
- (void)slAssertFalse:(BOOL (^)(void))condition;
- (void)slAssertTrueWithUnsignedInteger:(NSUInteger (^)(void))expression;
- (void)SLAssertTrueWithTimeout:(BOOL (^)(void))condition withTimeout:(NSTimeInterval)timeout;
- (BOOL)SLIsTrue:(BOOL (^)(void))condition withTimeout:(NSTimeInterval)timeout;
- (BOOL)SLWaitUntilTrue:(BOOL (^)(void))condition withTimeout:(NSTimeInterval)timeout;

- (void)slAssertThrows:(void (^)(void))expression;
Expand Down
5 changes: 5 additions & 0 deletions Unit Tests/TestUtilities.m
Expand Up @@ -73,6 +73,11 @@ - (void)SLAssertTrueWithTimeout:(BOOL (^)(void))condition withTimeout:(NSTimeInt
SLAssertTrueWithTimeout(condition(), timeout, nil);
}

- (BOOL)SLIsTrue:(BOOL (^)(void))condition withTimeout:(NSTimeInterval)timeout {
NSParameterAssert(condition);
return SLIsTrueWithTimeout(condition(), timeout);
}

- (BOOL)SLWaitUntilTrue:(BOOL (^)(void))condition withTimeout:(NSTimeInterval)timeout {
NSParameterAssert(condition);
return SLWaitUntilTrue(condition(), timeout);
Expand Down

0 comments on commit b96ce92

Please sign in to comment.