Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Better nullability and error handling #315

Open
wants to merge 8 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 7 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,17 @@
# OHHTTPStubs — CHANGELOG

## [8.0.0](https://github.com/AliSoftware/OHHTTPStubs/releases/tag/7.0.0)
<!-- Add new release notes above the latest release. The new version number will get added last before the new release. -->

* Fix clang static analysis warnings and use more idiomatic Objective-C error handling.
[@zeveisenberg](https://github.com/zeveisenberg)

## [8.0.0](https://github.com/AliSoftware/OHHTTPStubs/releases/tag/8.0.0)

* Update default Swift Version to 5.0
[@croig](https://github.com/CRoig)

>Notes:
> * No code changes were required (except from a little missing comma which caused a compilation error). Only xcshemes and xcodeproj were changed.
> * No code changes were required (except from a little missing comma which caused a compilation error). Only xcschemes and xcodeproj were changed.

## [7.0.0](https://github.com/AliSoftware/OHHTTPStubs/releases/tag/7.0.0)

Expand Down
4 changes: 2 additions & 2 deletions OHHTTPStubs/Sources/Mocktail/OHHTTPStubs+Mocktail.h
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ extern NSString* const MocktailErrorDomain;
* @return a stub descriptor that uniquely identifies the stub and can be later used to remove it with
* `removeStub:`.
*/
+(id<OHHTTPStubsDescriptor>)stubRequestsUsingMocktailNamed:(NSString *)fileName inBundle:(nullable NSBundle*)bundleOrNil error:(NSError **)error;
+(nullable id<OHHTTPStubsDescriptor>)stubRequestsUsingMocktailNamed:(NSString *)fileName inBundle:(nullable NSBundle*)bundleOrNil error:(NSError **)error;

/**
* Add a stub given a file URL in the format of Mocktail as defined at https://github.com/square/objc-mocktail.
Expand Down Expand Up @@ -93,7 +93,7 @@ extern NSString* const MocktailErrorDomain;
* @return an array of stub descriptor that uniquely identifies the stub and can be later used to remove it with
* `removeStub:`.
*/
+(NSArray *)stubRequestsUsingMocktailsAtPath:(NSString *)path inBundle:(nullable NSBundle*)bundleOrNil error:(NSError **)error;
+(nullable NSArray<id<OHHTTPStubsDescriptor>> *)stubRequestsUsingMocktailsAtPath:(NSString *)path inBundle:(nullable NSBundle*)bundleOrNil error:(NSError **)error;

@end

Expand Down
33 changes: 16 additions & 17 deletions OHHTTPStubs/Sources/Mocktail/OHHTTPStubs+Mocktail.m
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
@implementation OHHTTPStubs (Mocktail)


+(NSArray *)stubRequestsUsingMocktailsAtPath:(NSString *)path inBundle:(nullable NSBundle*)bundleOrNil error:(NSError **)error
+(nullable NSArray<id<OHHTTPStubsDescriptor>> *)stubRequestsUsingMocktailsAtPath:(NSString *)path inBundle:(nullable NSBundle*)bundleOrNil error:(NSError **)error
{
NSURL *dirURL = [bundleOrNil?:[NSBundle bundleForClass:self.class] URLForResource:path withExtension:nil];
if (!dirURL)
Expand All @@ -59,11 +59,10 @@ +(NSArray *)stubRequestsUsingMocktailsAtPath:(NSString *)path inBundle:(nullable
}

// Read the content of the directory
NSError *bError = nil;
NSFileManager *fileManager = [NSFileManager defaultManager];
NSArray *fileURLs = [fileManager contentsOfDirectoryAtURL:dirURL includingPropertiesForKeys:nil options:0 error:&bError];
NSArray *fileURLs = [fileManager contentsOfDirectoryAtURL:dirURL includingPropertiesForKeys:nil options:0 error:nil];

if (bError)
if (!fileURLs)
{
if (error)
{
Expand All @@ -73,15 +72,15 @@ +(NSArray *)stubRequestsUsingMocktailsAtPath:(NSString *)path inBundle:(nullable
}

//stub the Mocktail-formatted requests
NSMutableArray *descriptorArray = [[NSMutableArray alloc] initWithCapacity:fileURLs.count];
NSMutableArray<id<OHHTTPStubsDescriptor>> *descriptorArray = [[NSMutableArray alloc] initWithCapacity:fileURLs.count];
for (NSURL *fileURL in fileURLs)
{
if (![fileURL.absoluteString hasSuffix:@".tail"])
{
continue;
}
id<OHHTTPStubsDescriptor> descriptor = [[self class] stubRequestsUsingMocktail:fileURL error: &bError];
if (descriptor && !bError)
id<OHHTTPStubsDescriptor> descriptor = [[self class] stubRequestsUsingMocktail:fileURL error: nil];
if (descriptor)
{
[descriptorArray addObject:descriptor];
}
Expand All @@ -90,7 +89,7 @@ +(NSArray *)stubRequestsUsingMocktailsAtPath:(NSString *)path inBundle:(nullable
return descriptorArray;
}

+(id<OHHTTPStubsDescriptor>)stubRequestsUsingMocktailNamed:(NSString *)fileName inBundle:(nullable NSBundle*)bundleOrNil error:(NSError **)error
+(nullable id<OHHTTPStubsDescriptor>)stubRequestsUsingMocktailNamed:(NSString *)fileName inBundle:(nullable NSBundle*)bundleOrNil error:(NSError **)error
{
NSURL *responseURL = [bundleOrNil?:[NSBundle bundleForClass:self.class] URLForResource:fileName withExtension:@"tail"];

Expand All @@ -114,7 +113,7 @@ +(NSArray *)stubRequestsUsingMocktailsAtPath:(NSString *)path inBundle:(nullable
NSStringEncoding originalEncoding;
NSString *contentsOfFile = [NSString stringWithContentsOfURL:fileURL usedEncoding:&originalEncoding error:&bError];

if (!contentsOfFile || bError)
if (!contentsOfFile)
{
if (error)
{
Expand All @@ -138,9 +137,9 @@ +(NSArray *)stubRequestsUsingMocktailsAtPath:(NSString *)path inBundle:(nullable

/* Handle Mocktail format, adapted from Mocktail implementation
For more details on the file format, check out: https://github.com/square/objc-Mocktail */
NSRegularExpression *methodRegex = [NSRegularExpression regularExpressionWithPattern:lines[0] options:NSRegularExpressionCaseInsensitive error:&bError];
NSRegularExpression *methodRegex = [NSRegularExpression regularExpressionWithPattern:lines[0] options:NSRegularExpressionCaseInsensitive error:nil];

if (bError)
if (!methodRegex)
{
if (error)
{
Expand All @@ -149,9 +148,9 @@ +(NSArray *)stubRequestsUsingMocktailsAtPath:(NSString *)path inBundle:(nullable
return nil;
}

NSRegularExpression *absoluteURLRegex = [NSRegularExpression regularExpressionWithPattern:lines[1] options:NSRegularExpressionCaseInsensitive error:&bError];
NSRegularExpression *absoluteURLRegex = [NSRegularExpression regularExpressionWithPattern:lines[1] options:NSRegularExpressionCaseInsensitive error:nil];

if (bError)
if (!absoluteURLRegex)
{
if (error)
{
Expand All @@ -165,8 +164,8 @@ +(NSArray *)stubRequestsUsingMocktailsAtPath:(NSString *)path inBundle:(nullable
NSMutableDictionary *headers = @{@"Content-Type":lines[3]}.mutableCopy;

// From line 4 to '\n\n', expect HTTP response headers.
NSRegularExpression *headerPattern = [NSRegularExpression regularExpressionWithPattern:@"^([^:]+):\\s+(.*)" options:0 error:&bError];
if (bError)
NSRegularExpression *headerPattern = [NSRegularExpression regularExpressionWithPattern:@"^([^:]+):\\s+(.*)" options:0 error:nil];
if (!headerPattern)
{
if (error)
{
Expand All @@ -177,8 +176,8 @@ +(NSArray *)stubRequestsUsingMocktailsAtPath:(NSString *)path inBundle:(nullable


// Allow bare Content-Type header on line 4 before named HTTP response headers
NSRegularExpression *bareContentTypePattern = [NSRegularExpression regularExpressionWithPattern:@"^([^:]+)+$" options:0 error:&bError];
if (bError)
NSRegularExpression *bareContentTypePattern = [NSRegularExpression regularExpressionWithPattern:@"^([^:]+)+$" options:0 error:nil];
if (!bareContentTypePattern)
{
if (error)
{
Expand Down
2 changes: 1 addition & 1 deletion OHHTTPStubs/Sources/OHHTTPStubs.m
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ @interface OHHTTPStubsProtocol : NSURLProtocol @end

@interface OHHTTPStubs()
+ (instancetype)sharedInstance;
@property(atomic, copy) NSMutableArray* stubDescriptors;
@property(atomic, readonly) NSMutableArray* stubDescriptors;
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Problem with this is, it's a reference type. So that means that users can't set stubDescriptors = newValue but they can [stubDescriptors append:…] though can't they?

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh wait it's in the private category so not exposed to the consumer is it? (Not easy to review from phone 😅)

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(Forgot to reply originally) copy never works on properties of NSMutableCopying type, because the setter calls -copy, which returns an immutable copy, i.e. an NSArray in this case. So you’d set a mutable array, and the value that gets stored would be immutable. Fun edge case in Obj-C land that should probably be a compile error.

@property(atomic, assign) BOOL enabledState;
@property(atomic, copy, nullable) void (^onStubActivationBlock)(NSURLRequest*, id<OHHTTPStubsDescriptor>, OHHTTPStubsResponse*);
@property(atomic, copy, nullable) void (^onStubRedirectBlock)(NSURLRequest*, NSURLRequest*, id<OHHTTPStubsDescriptor>, OHHTTPStubsResponse*);
Expand Down
40 changes: 37 additions & 3 deletions OHHTTPStubs/UnitTests/Test Suites/MocktailTests.m
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,17 @@ - (void)testMocktailLoginSuccess
[self runLogin];
}

- (void)testMocktailFailsWithInvalidInputFile
{
NSError *error = nil;
NSBundle *bundle = [NSBundle bundleForClass:self.class];
id<OHHTTPStubsDescriptor> descriptor = [OHHTTPStubs stubRequestsUsingMocktailNamed:@"invalid file name" inBundle:bundle error:&error];
XCTAssertNil(descriptor, @"Invalid input failed to produce nil result");
XCTAssertEqualObjects(error.domain, MocktailErrorDomain);
XCTAssertEqual(error.code, OHHTTPStubsMocktailErrorPathDoesNotExist);
XCTAssertEqualObjects(error.userInfo[NSLocalizedDescriptionKey], @"File 'invalid file name' does not exist.");
}

- (void)testMocktailsAtFolder
{
NSError *error = nil;
Expand All @@ -76,6 +87,28 @@ - (void)testMocktailsAtFolder
[self runGetCards];
}

- (void)testMocktailsFailWithNonexistentFolder
{
NSError *error = nil;
NSBundle *bundle = [NSBundle bundleForClass:self.class];
NSArray *descriptors = [OHHTTPStubs stubRequestsUsingMocktailsAtPath:@"invalid folder name" inBundle:bundle error:&error];
XCTAssertNil(descriptors, @"Invalid input failed to produce nil result");
XCTAssertEqualObjects(error.domain, MocktailErrorDomain);
XCTAssertEqual(error.code, OHHTTPStubsMocktailErrorPathDoesNotExist);
XCTAssertEqualObjects(error.userInfo[NSLocalizedDescriptionKey], @"Path 'invalid folder name' does not exist.");
}

- (void)testMocktailsFailWithNonFolderFile
{
NSError *error = nil;
NSBundle *bundle = [NSBundle bundleForClass:self.class];
NSArray *descriptors = [OHHTTPStubs stubRequestsUsingMocktailsAtPath:@"login.tail" inBundle:bundle error:&error];
XCTAssertNil(descriptors, @"Invalid input failed to produce nil result");
XCTAssertEqualObjects(error.domain, MocktailErrorDomain);
XCTAssertEqual(error.code, OHHTTPStubsMocktailErrorPathIsNotFolder);
XCTAssertEqualObjects(error.userInfo[NSLocalizedDescriptionKey], @"Path 'login.tail' is not a folder.");
}

- (void)testMocktailHeaders
{
NSError *error = nil;
Expand Down Expand Up @@ -167,12 +200,13 @@ - (NSHTTPURLResponse *)runGetCards
XCTAssertNil(error, @"Error while getting cards.");

NSArray *json = nil;
if(!error && [@"application/json" isEqual:response.MIMEType])
NSError *jsonError = nil;
if([@"application/json" isEqualToString:response.MIMEType])
{
json = [NSJSONSerialization JSONObjectWithData:data options:0 error:&error];
json = [NSJSONSerialization JSONObjectWithData:data options:0 error:&jsonError];
}

XCTAssertNotNil(json, @"The response is not a json object");
XCTAssertNotNil(json, @"The response is not a json object: %@", jsonError);
XCTAssertEqual(json.count, 2, @"The response does not contain 2 cards");
XCTAssertEqualObjects([json firstObject][@"amount"], @"$25.28", @"The first card amount does not match");

Expand Down
2 changes: 1 addition & 1 deletion OHHTTPStubs/UnitTests/Test Suites/NSURLSessionTests.m
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ - (void)_test_NSURLSession:(NSURLSession*)session
{
NSError *jsonError = nil;
NSDictionary *jsonObject = [NSJSONSerialization JSONObjectWithData:data options:0 error:&jsonError];
XCTAssertNil(jsonError, @"Unexpected error deserializing JSON response");
XCTAssertNotNil(jsonObject, @"Unexpected error deserializing JSON response: %@", jsonError);
dataResponse = jsonObject;
}
[expectation fulfill];
Expand Down