- Release 0.1.0
This commit is contained in:
parent
a3c2db98d5
commit
84ece5bd23
@ -1,11 +1,11 @@
|
||||
source 'https://github.com/CocoaPods/Specs.git'
|
||||
use_frameworks!
|
||||
|
||||
target 'STTweetTextView_Example', :exclusive => true do
|
||||
target 'STTweetTextView_Example' do
|
||||
pod "STTweetTextView", :path => "../"
|
||||
end
|
||||
|
||||
target 'STTweetTextView_Tests', :exclusive => true do
|
||||
target 'STTweetTextView_Tests' do
|
||||
pod "STTweetTextView", :path => "../"
|
||||
|
||||
pod 'Specta'
|
||||
|
||||
34
Example/Podfile.lock
Normal file
34
Example/Podfile.lock
Normal file
@ -0,0 +1,34 @@
|
||||
PODS:
|
||||
- Expecta (1.0.5)
|
||||
- Expecta+Snapshots (2.0.0):
|
||||
- Expecta (~> 1.0)
|
||||
- FBSnapshotTestCase/Core (~> 2.0.3)
|
||||
- FBSnapshotTestCase (2.0.7):
|
||||
- FBSnapshotTestCase/SwiftSupport (= 2.0.7)
|
||||
- FBSnapshotTestCase/Core (2.0.7)
|
||||
- FBSnapshotTestCase/SwiftSupport (2.0.7):
|
||||
- FBSnapshotTestCase/Core
|
||||
- Specta (1.0.5)
|
||||
- STTweetTextView (0.1.0)
|
||||
|
||||
DEPENDENCIES:
|
||||
- Expecta
|
||||
- Expecta+Snapshots
|
||||
- FBSnapshotTestCase
|
||||
- Specta
|
||||
- STTweetTextView (from `../`)
|
||||
|
||||
EXTERNAL SOURCES:
|
||||
STTweetTextView:
|
||||
:path: "../"
|
||||
|
||||
SPEC CHECKSUMS:
|
||||
Expecta: e1c022fcd33910b6be89c291d2775b3fe27a89fe
|
||||
Expecta+Snapshots: 29b38dd695bc72a0ed2bea833937d78df41943ba
|
||||
FBSnapshotTestCase: 7e85180d0d141a0cf472352edda7e80d7eaeb547
|
||||
Specta: ac94d110b865115fe60ff2c6d7281053c6f8e8a2
|
||||
STTweetTextView: f49d385c25baf6786a777ad4de74fa7fc5c6237d
|
||||
|
||||
PODFILE CHECKSUM: 5ab403b6f2a425a6954d815443a891014dc298db
|
||||
|
||||
COCOAPODS: 1.0.0.beta.2
|
||||
22
Example/Pods/Expecta+Snapshots/EXPMatchers+FBSnapshotTest.h
generated
Normal file
22
Example/Pods/Expecta+Snapshots/EXPMatchers+FBSnapshotTest.h
generated
Normal file
@ -0,0 +1,22 @@
|
||||
//
|
||||
// EXPMatchers+FBSnapshotTest.h
|
||||
// Artsy
|
||||
//
|
||||
// Created by Daniel Doubrovkine on 1/14/14.
|
||||
// Copyright (c) 2014 Artsy Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Expecta/Expecta.h>
|
||||
#import "ExpectaObject+FBSnapshotTest.h"
|
||||
|
||||
@interface EXPExpectFBSnapshotTest : NSObject
|
||||
@end
|
||||
|
||||
/// Set the default folder for image tests to run in
|
||||
extern void setGlobalReferenceImageDir(char *reference);
|
||||
|
||||
EXPMatcherInterface(haveValidSnapshot, (void));
|
||||
EXPMatcherInterface(recordSnapshot, (void));
|
||||
|
||||
EXPMatcherInterface(haveValidSnapshotNamed, (NSString *snapshot));
|
||||
EXPMatcherInterface(recordSnapshotNamed, (NSString *snapshot));
|
||||
319
Example/Pods/Expecta+Snapshots/EXPMatchers+FBSnapshotTest.m
generated
Normal file
319
Example/Pods/Expecta+Snapshots/EXPMatchers+FBSnapshotTest.m
generated
Normal file
@ -0,0 +1,319 @@
|
||||
//
|
||||
// EXPMatchers+FBSnapshotTest.h
|
||||
// Artsy
|
||||
//
|
||||
// Created by Daniel Doubrovkine on 1/14/14.
|
||||
// Copyright (c) 2014 Artsy Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#import "EXPMatchers+FBSnapshotTest.h"
|
||||
#import <Expecta/EXPMatcherHelpers.h>
|
||||
#import <FBSnapshotTestCase/FBSnapshotTestController.h>
|
||||
|
||||
@interface EXPExpectFBSnapshotTest()
|
||||
@property (nonatomic, strong) NSString *referenceImagesDirectory;
|
||||
@end
|
||||
|
||||
@implementation EXPExpectFBSnapshotTest
|
||||
|
||||
+ (id)instance
|
||||
{
|
||||
static EXPExpectFBSnapshotTest *instance = nil;
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
instance = [[self alloc] init];
|
||||
});
|
||||
return instance;
|
||||
}
|
||||
|
||||
+ (BOOL)compareSnapshotOfViewOrLayer:(id)viewOrLayer snapshot:(NSString *)snapshot testCase:(id)testCase record:(BOOL)record referenceDirectory:(NSString *)referenceDirectory error:(NSError **)error
|
||||
|
||||
{
|
||||
FBSnapshotTestController *snapshotController = [[FBSnapshotTestController alloc] initWithTestClass:[testCase class]];
|
||||
snapshotController.recordMode = record;
|
||||
snapshotController.referenceImagesDirectory = referenceDirectory;
|
||||
snapshotController.usesDrawViewHierarchyInRect = [Expecta usesDrawViewHierarchyInRect];
|
||||
|
||||
if (! snapshotController.referenceImagesDirectory) {
|
||||
[NSException raise:@"Missing value for referenceImagesDirectory" format:@"Call [[EXPExpectFBSnapshotTest instance] setReferenceImagesDirectory"];
|
||||
}
|
||||
|
||||
return [snapshotController compareSnapshotOfViewOrLayer:viewOrLayer
|
||||
selector:NSSelectorFromString(snapshot)
|
||||
identifier:nil
|
||||
tolerance:0
|
||||
error:error];
|
||||
}
|
||||
|
||||
+ (NSString *)combinedError:(NSString *)message test:(NSString *)test error:(NSError *)error
|
||||
{
|
||||
NSAssert(message, @"missing message");
|
||||
NSAssert(test, @"missing test name");
|
||||
|
||||
NSMutableArray *ary = [NSMutableArray array];
|
||||
|
||||
[ary addObject:[NSString stringWithFormat:@"%@ %@", message, test]];
|
||||
|
||||
for(NSString *key in error.userInfo.keyEnumerator) {
|
||||
[ary addObject:[NSString stringWithFormat:@" %@: %@", key, [error.userInfo valueForKey:key]]];
|
||||
}
|
||||
|
||||
return [ary componentsJoinedByString:@"\n"];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
void setGlobalReferenceImageDir(char *reference) {
|
||||
NSString *referenceImagesDirectory = [NSString stringWithFormat:@"%s", reference];
|
||||
[[EXPExpectFBSnapshotTest instance] setReferenceImagesDirectory:referenceImagesDirectory];
|
||||
};
|
||||
|
||||
@interface EXPExpect(ReferenceDirExtension)
|
||||
- (NSString *)_getDefaultReferenceDirectory;
|
||||
@end
|
||||
|
||||
@implementation EXPExpect(ReferenceDirExtension)
|
||||
|
||||
- (NSString *)_getDefaultReferenceDirectory
|
||||
{
|
||||
NSString *globalReference = [[EXPExpectFBSnapshotTest instance] referenceImagesDirectory];
|
||||
if (globalReference) {
|
||||
return globalReference;
|
||||
}
|
||||
|
||||
// Search the test file's path to find the first folder with the substring "tests"
|
||||
// then append "/ReferenceImages" and use that
|
||||
|
||||
NSString *testFileName = [NSString stringWithCString:self.fileName encoding:NSUTF8StringEncoding];
|
||||
NSArray *pathComponents = [testFileName pathComponents];
|
||||
|
||||
for (NSString *folder in pathComponents) {
|
||||
if ([folder.lowercaseString rangeOfString:@"tests"].location != NSNotFound) {
|
||||
|
||||
NSArray *folderPathComponents = [pathComponents subarrayWithRange:NSMakeRange(0, [pathComponents indexOfObject:folder] + 1)];
|
||||
return [NSString stringWithFormat:@"%@/ReferenceImages", [folderPathComponents componentsJoinedByString:@"/"]];
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
[NSException raise:@"Could not infer reference image folder" format:@"You should provide a reference dir using setGlobalReferenceImageDir(FB_REFERENCE_IMAGE_DIR);"];
|
||||
return nil;
|
||||
}
|
||||
@end
|
||||
|
||||
|
||||
|
||||
// If you're bringing in Speca via CocoaPods
|
||||
// use the test path to get the test's image file URL
|
||||
|
||||
#if __has_include(<Specta/Specta.h>)
|
||||
#import <Specta/Specta.h>
|
||||
#import <Specta/SpectaUtility.h>
|
||||
#import <Specta/SPTExample.h>
|
||||
|
||||
NSString *sanitizedTestPath();
|
||||
|
||||
NSString *sanitizedTestPath(){
|
||||
id compiledExample = [[NSThread currentThread] threadDictionary][@"SPTCurrentSpec"]; // SPTSpec
|
||||
NSString *name;
|
||||
if ([compiledExample respondsToSelector:@selector(name)]) {
|
||||
// Specta 0.3 syntax
|
||||
name = [compiledExample performSelector:@selector(name)];
|
||||
} else if ([compiledExample respondsToSelector:@selector(fileName)]) {
|
||||
// Specta 0.2 syntax
|
||||
name = [compiledExample performSelector:@selector(fileName)];
|
||||
}
|
||||
name = [[[[name componentsSeparatedByString:@" test_"] lastObject] stringByReplacingOccurrencesOfString:@"__" withString:@"_"] stringByReplacingOccurrencesOfString:@"]" withString:@""];
|
||||
return name;
|
||||
}
|
||||
|
||||
EXPMatcherImplementationBegin(haveValidSnapshot, (void)){
|
||||
__block NSError *error = nil;
|
||||
|
||||
match(^BOOL{
|
||||
NSString *referenceImageDir = [self _getDefaultReferenceDirectory];
|
||||
NSString *name = sanitizedTestPath();
|
||||
if ([actual isKindOfClass:UIViewController.class]) {
|
||||
[actual beginAppearanceTransition:YES animated:NO];
|
||||
[actual endAppearanceTransition];
|
||||
|
||||
actual = [actual view];
|
||||
}
|
||||
|
||||
return [EXPExpectFBSnapshotTest compareSnapshotOfViewOrLayer:actual snapshot:name testCase:[self testCase] record:NO referenceDirectory:referenceImageDir error:&error];
|
||||
});
|
||||
|
||||
failureMessageForTo(^NSString *{
|
||||
return [EXPExpectFBSnapshotTest combinedError:@"expected a matching snapshot in" test:sanitizedTestPath() error:error];
|
||||
});
|
||||
|
||||
failureMessageForNotTo(^NSString *{
|
||||
return [EXPExpectFBSnapshotTest combinedError:@"expected to not have a matching snapshot in" test:sanitizedTestPath() error:error];
|
||||
});
|
||||
}
|
||||
EXPMatcherImplementationEnd
|
||||
|
||||
EXPMatcherImplementationBegin(recordSnapshot, (void)) {
|
||||
__block NSError *error = nil;
|
||||
|
||||
BOOL actualIsViewLayerOrViewController = ([actual isKindOfClass:UIView.class] || [actual isKindOfClass:CALayer.class] || [actual isKindOfClass:UIViewController.class]);
|
||||
|
||||
prerequisite(^BOOL{
|
||||
return actualIsViewLayerOrViewController;
|
||||
});
|
||||
|
||||
match(^BOOL{
|
||||
NSString *referenceImageDir = [self _getDefaultReferenceDirectory];
|
||||
|
||||
// For view controllers do the viewWill/viewDid dance, then pass view through
|
||||
if ([actual isKindOfClass:UIViewController.class]) {
|
||||
|
||||
[actual beginAppearanceTransition:YES animated:NO];
|
||||
[actual endAppearanceTransition];
|
||||
actual = [actual view];
|
||||
}
|
||||
|
||||
[EXPExpectFBSnapshotTest compareSnapshotOfViewOrLayer:actual snapshot:sanitizedTestPath() testCase:[self testCase] record:YES referenceDirectory:referenceImageDir error:&error];
|
||||
return NO;
|
||||
});
|
||||
|
||||
failureMessageForTo(^NSString *{
|
||||
if (!actualIsViewLayerOrViewController) {
|
||||
return [EXPExpectFBSnapshotTest combinedError:@"Expected a View, Layer or View Controller." test:sanitizedTestPath() error:nil];
|
||||
}
|
||||
if (error) {
|
||||
return [EXPExpectFBSnapshotTest combinedError:@"expected to record a snapshot in" test:sanitizedTestPath() error:error];
|
||||
} else {
|
||||
return [NSString stringWithFormat:@"snapshot %@ successfully recorded, replace recordSnapshot with a check", sanitizedTestPath()];
|
||||
}
|
||||
});
|
||||
|
||||
failureMessageForNotTo(^NSString *{
|
||||
if (error) {
|
||||
return [EXPExpectFBSnapshotTest combinedError:@"expected to record a snapshot in" test:sanitizedTestPath() error:error];
|
||||
} else {
|
||||
return [NSString stringWithFormat:@"snapshot %@ successfully recorded, replace recordSnapshot with a check", sanitizedTestPath()];
|
||||
}
|
||||
});
|
||||
}
|
||||
EXPMatcherImplementationEnd
|
||||
|
||||
#else
|
||||
|
||||
// If you don't have Speca stub the functions
|
||||
|
||||
EXPMatcherImplementationBegin(haveValidSnapshot, (void)){
|
||||
|
||||
prerequisite(^BOOL{
|
||||
return NO;
|
||||
});
|
||||
|
||||
failureMessageForTo(^NSString *{
|
||||
return @"you need Specta installed via CocoaPods to use haveValidSnapshot, use haveValidSnapshotNamed instead";
|
||||
});
|
||||
|
||||
failureMessageForNotTo(^NSString *{
|
||||
return @"you need Specta installed via CocoaPods to use haveValidSnapshot, use haveValidSnapshotNamed instead";
|
||||
});
|
||||
}
|
||||
EXPMatcherImplementationEnd
|
||||
|
||||
|
||||
EXPMatcherImplementationBegin(recordSnapshot, (void)) {
|
||||
|
||||
prerequisite(^BOOL{
|
||||
return NO;
|
||||
});
|
||||
|
||||
failureMessageForTo(^NSString *{
|
||||
return @"you need Specta installed via CocoaPods to use recordSnapshot, use recordSnapshotNamed instead";
|
||||
});
|
||||
|
||||
failureMessageForNotTo(^NSString *{
|
||||
return @"you need Specta installed via CocoaPods to use recordSnapshot, use recordSnapshotNamed instead";
|
||||
});
|
||||
}
|
||||
EXPMatcherImplementationEnd
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
EXPMatcherImplementationBegin(haveValidSnapshotNamed, (NSString *snapshot)){
|
||||
BOOL snapshotIsNil = (snapshot == nil);
|
||||
__block NSError *error = nil;
|
||||
|
||||
prerequisite(^BOOL{
|
||||
return !(snapshotIsNil);
|
||||
});
|
||||
|
||||
match(^BOOL{
|
||||
NSString *referenceImageDir = [self _getDefaultReferenceDirectory];
|
||||
if ([actual isKindOfClass:UIViewController.class]) {
|
||||
[actual beginAppearanceTransition:YES animated:NO];
|
||||
[actual endAppearanceTransition];
|
||||
|
||||
actual = [actual view];
|
||||
}
|
||||
return [EXPExpectFBSnapshotTest compareSnapshotOfViewOrLayer:actual snapshot:snapshot testCase:[self testCase] record:NO referenceDirectory:referenceImageDir error:&error];
|
||||
});
|
||||
|
||||
failureMessageForTo(^NSString *{
|
||||
return [EXPExpectFBSnapshotTest combinedError:@"expected a matching snapshot named" test:snapshot error:error];
|
||||
|
||||
});
|
||||
|
||||
failureMessageForNotTo(^NSString *{
|
||||
return [EXPExpectFBSnapshotTest combinedError:@"expected not to have a matching snapshot named" test:snapshot error:error];
|
||||
});
|
||||
}
|
||||
EXPMatcherImplementationEnd
|
||||
|
||||
EXPMatcherImplementationBegin(recordSnapshotNamed, (NSString *snapshot)) {
|
||||
BOOL snapshotExists = (snapshot != nil);
|
||||
BOOL actualIsViewLayerOrViewController = ([actual isKindOfClass:UIView.class] || [actual isKindOfClass:CALayer.class] || [actual isKindOfClass:UIViewController.class]);
|
||||
__block NSError *error = nil;
|
||||
id actualRef = actual;
|
||||
|
||||
prerequisite(^BOOL{
|
||||
return actualRef && snapshotExists && actualIsViewLayerOrViewController;
|
||||
});
|
||||
|
||||
match(^BOOL{
|
||||
NSString *referenceImageDir = [self _getDefaultReferenceDirectory];
|
||||
|
||||
// For view controllers do the viewWill/viewDid dance, then pass view through
|
||||
if ([actual isKindOfClass:UIViewController.class]) {
|
||||
[actual beginAppearanceTransition:YES animated:NO];
|
||||
[actual endAppearanceTransition];
|
||||
actual = [actual view];
|
||||
}
|
||||
|
||||
[EXPExpectFBSnapshotTest compareSnapshotOfViewOrLayer:actual snapshot:snapshot testCase:[self testCase] record:YES referenceDirectory:referenceImageDir error:&error];
|
||||
return NO;
|
||||
});
|
||||
|
||||
failureMessageForTo(^NSString *{
|
||||
if (!actualIsViewLayerOrViewController) {
|
||||
return [EXPExpectFBSnapshotTest combinedError:@"Expected a View, Layer or View Controller." test:snapshot error:nil];
|
||||
}
|
||||
if (error) {
|
||||
return [EXPExpectFBSnapshotTest combinedError:@"expected to record a matching snapshot named" test:snapshot error:error];
|
||||
} else {
|
||||
return [NSString stringWithFormat:@"snapshot %@ successfully recorded, replace recordSnapshot with a check", snapshot];
|
||||
}
|
||||
});
|
||||
|
||||
failureMessageForNotTo(^NSString *{
|
||||
if (!actualIsViewLayerOrViewController) {
|
||||
return [EXPExpectFBSnapshotTest combinedError:@"Expected a View, Layer or View Controller." test:snapshot error:nil];
|
||||
}
|
||||
if (error) {
|
||||
return [EXPExpectFBSnapshotTest combinedError:@"expected to record a matching snapshot named" test:snapshot error:error];
|
||||
} else {
|
||||
return [NSString stringWithFormat:@"snapshot %@ successfully recorded, replace recordSnapshot with a check", snapshot];
|
||||
}
|
||||
});
|
||||
}
|
||||
EXPMatcherImplementationEnd
|
||||
17
Example/Pods/Expecta+Snapshots/ExpectaObject+FBSnapshotTest.h
generated
Normal file
17
Example/Pods/Expecta+Snapshots/ExpectaObject+FBSnapshotTest.h
generated
Normal file
@ -0,0 +1,17 @@
|
||||
//
|
||||
// ExpectaObject+FBSnapshotTest.h
|
||||
// Expecta+Snapshots
|
||||
//
|
||||
// Created by John Boiles on 8/3/15.
|
||||
// Copyright (c) 2015 Expecta+Snapshots All rights reserved.
|
||||
//
|
||||
|
||||
#import <Expecta/ExpectaObject.h>
|
||||
|
||||
@interface Expecta (FBSnapshotTest)
|
||||
|
||||
+ (void)setUsesDrawViewHierarchyInRect:(BOOL)usesDrawViewHierarchyInRect;
|
||||
|
||||
+ (BOOL)usesDrawViewHierarchyInRect;
|
||||
|
||||
@end
|
||||
25
Example/Pods/Expecta+Snapshots/ExpectaObject+FBSnapshotTest.m
generated
Normal file
25
Example/Pods/Expecta+Snapshots/ExpectaObject+FBSnapshotTest.m
generated
Normal file
@ -0,0 +1,25 @@
|
||||
//
|
||||
// ExpectaObject+FBSnapshotTest.m
|
||||
// Expecta+Snapshots
|
||||
//
|
||||
// Created by John Boiles on 8/3/15.
|
||||
// Copyright (c) 2015 Expecta+Snapshots All rights reserved.
|
||||
//
|
||||
|
||||
#import "ExpectaObject+FBSnapshotTest.h"
|
||||
#import <objc/runtime.h>
|
||||
|
||||
static NSString const *kUsesDrawViewHierarchyInRectKey = @"ExpectaObject+FBSnapshotTest.usesDrawViewHierarchyInRect";
|
||||
|
||||
@implementation Expecta (FBSnapshotTest)
|
||||
|
||||
+ (void)setUsesDrawViewHierarchyInRect:(BOOL)usesDrawViewHierarchyInRect {
|
||||
objc_setAssociatedObject(self, (__bridge const void *)(kUsesDrawViewHierarchyInRectKey), @(usesDrawViewHierarchyInRect), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
|
||||
}
|
||||
|
||||
+ (BOOL)usesDrawViewHierarchyInRect {
|
||||
NSNumber *usesDrawViewHierarchyInRect = objc_getAssociatedObject(self, (__bridge const void *)(kUsesDrawViewHierarchyInRectKey));
|
||||
return usesDrawViewHierarchyInRect.boolValue;
|
||||
}
|
||||
|
||||
@end
|
||||
22
Example/Pods/Expecta+Snapshots/LICENSE.md
generated
Normal file
22
Example/Pods/Expecta+Snapshots/LICENSE.md
generated
Normal file
@ -0,0 +1,22 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2014 Daniel Doubrovkine, Artsy Inc.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
87
Example/Pods/Expecta+Snapshots/README.md
generated
Normal file
87
Example/Pods/Expecta+Snapshots/README.md
generated
Normal file
@ -0,0 +1,87 @@
|
||||
Expecta Matchers for FBSnapshotTestCase
|
||||
=======================================
|
||||
|
||||
[Expecta](https://github.com/specta/expecta) matchers for [ios-snapshot-test-case](https://github.com/facebook/ios-snapshot-test-case).
|
||||
|
||||
[](https://travis-ci.org/dblock/ios-snapshot-test-case-expecta)
|
||||
|
||||
### Usage
|
||||
|
||||
Add `Expecta+Snapshots` to your Podfile, the latest `FBSnapshotTestCase` will come in as a dependency.
|
||||
|
||||
``` ruby
|
||||
pod 'Expecta+Snapshots'
|
||||
```
|
||||
|
||||
### App setup
|
||||
|
||||
Use `expect(view).to.recordSnapshotNamed(@"unique snapshot name")` to record a snapshot and `expect(view).to.haveValidSnapshotNamed(@"unique snapshot name")` to check it.
|
||||
|
||||
If you project was compiled with Specta included, you have two extra methods that use the spec hierarchy to generate the snapshot name for you: `recordSnapshot()` and `haveValidSnapshot()`. You should only call these once per `it()` block.
|
||||
|
||||
If you need the `usesDrawViewHierarchyInRect` property in order to correctly render UIVisualEffect, UIAppearance and Size Classes, call `[Expecta setUsesDrawViewHierarchyInRect:NO];` inside `beforeAll`.
|
||||
|
||||
``` Objective-C
|
||||
#define EXP_SHORTHAND
|
||||
#include <Specta/Specta.h>
|
||||
#include <Expecta/Expecta.h>
|
||||
#include <Expecta+Snapshots/EXPMatchers+FBSnapshotTest.h>
|
||||
#include "FBExampleView.h"
|
||||
|
||||
SpecBegin(FBExampleView)
|
||||
|
||||
describe(@"manual matching", ^{
|
||||
|
||||
it(@"matches view", ^{
|
||||
FBExampleView *view = [[FBExampleView alloc] initWithFrame:CGRectMake(0, 0, 64, 64)];
|
||||
expect(view).to.recordSnapshotNamed(@"FBExampleView");
|
||||
expect(view).to.haveValidSnapshotNamed(@"FBExampleView");
|
||||
});
|
||||
|
||||
it(@"doesn't match a view", ^{
|
||||
FBExampleView *view = [[FBExampleView alloc] initWithFrame:CGRectMake(0, 0, 64, 64)];
|
||||
expect(view).toNot.haveValidSnapshotNamed(@"FBExampleViewDoesNotExist");
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe(@"test name derived matching", ^{
|
||||
|
||||
it(@"matches view", ^{
|
||||
FBExampleView *view = [[FBExampleView alloc] initWithFrame:CGRectMake(0, 0, 64, 64)];
|
||||
expect(view).to.recordSnapshot();
|
||||
expect(view).to.haveValidSnapshot();
|
||||
});
|
||||
|
||||
it(@"doesn't match a view", ^{
|
||||
FBExampleView *view = [[FBExampleView alloc] initWithFrame:CGRectMake(0, 0, 64, 64)];
|
||||
expect(view).toNot.haveValidSnapshot();
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
SpecEnd
|
||||
```
|
||||
|
||||
### Sane defaults
|
||||
|
||||
`EXPMatchers+FBSnapshotTest` will automatically figure out the tests folder, and [add a reference image](https://github.com/dblock/ios-snapshot-test-case-expecta/blob/master/EXPMatchers%2BFBSnapshotTest.m#L84-L85) directory, if you'd like to override this, you should include a `beforeAll` block setting the `setGlobalReferenceImageDir` in each file containing tests.
|
||||
|
||||
```
|
||||
beforeAll(^{
|
||||
setGlobalReferenceImageDir(FB_REFERENCE_IMAGE_DIR);
|
||||
});
|
||||
```
|
||||
|
||||
|
||||
### Example
|
||||
|
||||
A complete project can be found in [FBSnapshotTestCaseDemo](FBSnapshotTestCaseDemo).
|
||||
|
||||
Notably, take a look at [FBSnapshotTestCaseDemoSpecs.m](FBSnapshotTestCaseDemo/FBSnapshotTestCaseDemoTests/FBSnapshotTestCaseDemoSpecs.m) for a complete example, which is an expanded Specta version version of [FBSnapshotTestCaseDemoTests.m](https://github.com/facebook/ios-snapshot-test-case/blob/master/FBSnapshotTestCaseDemo/FBSnapshotTestCaseDemoTests/FBSnapshotTestCaseDemoTests.m).
|
||||
|
||||
Finally you can consult the tests for [ARTiledImageView](https://github.com/dblock/ARTiledImageView/tree/master/IntegrationTests) or [NAMapKit](https://github.com/neilang/NAMapKit/tree/master/Demo/DemoTests).
|
||||
|
||||
### License
|
||||
|
||||
MIT, see [LICENSE](LICENSE.md)
|
||||
25
Example/Pods/Expecta/Expecta/EXPBlockDefinedMatcher.h
generated
Normal file
25
Example/Pods/Expecta/Expecta/EXPBlockDefinedMatcher.h
generated
Normal file
@ -0,0 +1,25 @@
|
||||
//
|
||||
// EXPRuntimeMatcher.h
|
||||
// Expecta
|
||||
//
|
||||
// Created by Luke Redpath on 26/03/2012.
|
||||
// Copyright (c) 2012 Peter Jihoon Kim. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import "EXPMatcher.h"
|
||||
#import "EXPDefines.h"
|
||||
|
||||
@interface EXPBlockDefinedMatcher : NSObject <EXPMatcher> {
|
||||
EXPBoolBlock prerequisiteBlock;
|
||||
EXPBoolBlock matchBlock;
|
||||
EXPStringBlock failureMessageForToBlock;
|
||||
EXPStringBlock failureMessageForNotToBlock;
|
||||
}
|
||||
|
||||
@property(nonatomic, copy) EXPBoolBlock prerequisiteBlock;
|
||||
@property(nonatomic, copy) EXPBoolBlock matchBlock;
|
||||
@property(nonatomic, copy) EXPStringBlock failureMessageForToBlock;
|
||||
@property(nonatomic, copy) EXPStringBlock failureMessageForNotToBlock;
|
||||
|
||||
@end
|
||||
60
Example/Pods/Expecta/Expecta/EXPBlockDefinedMatcher.m
generated
Normal file
60
Example/Pods/Expecta/Expecta/EXPBlockDefinedMatcher.m
generated
Normal file
@ -0,0 +1,60 @@
|
||||
//
|
||||
// EXPRuntimeMatcher.m
|
||||
// Expecta
|
||||
//
|
||||
// Created by Luke Redpath on 26/03/2012.
|
||||
// Copyright (c) 2012 Peter Jihoon Kim. All rights reserved.
|
||||
//
|
||||
|
||||
#import "EXPBlockDefinedMatcher.h"
|
||||
|
||||
@implementation EXPBlockDefinedMatcher
|
||||
|
||||
- (void)dealloc
|
||||
{
|
||||
self.prerequisiteBlock = nil;
|
||||
self.matchBlock = nil;
|
||||
self.failureMessageForToBlock = nil;
|
||||
self.failureMessageForNotToBlock = nil;
|
||||
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
@synthesize prerequisiteBlock;
|
||||
@synthesize matchBlock;
|
||||
@synthesize failureMessageForToBlock;
|
||||
@synthesize failureMessageForNotToBlock;
|
||||
|
||||
- (BOOL)meetsPrerequesiteFor:(id)actual
|
||||
{
|
||||
if (self.prerequisiteBlock) {
|
||||
return self.prerequisiteBlock();
|
||||
}
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (BOOL)matches:(id)actual
|
||||
{
|
||||
if (self.matchBlock) {
|
||||
return self.matchBlock();
|
||||
}
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (NSString *)failureMessageForTo:(id)actual
|
||||
{
|
||||
if (self.failureMessageForToBlock) {
|
||||
return self.failureMessageForToBlock();
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (NSString *)failureMessageForNotTo:(id)actual
|
||||
{
|
||||
if (self.failureMessageForNotToBlock) {
|
||||
return self.failureMessageForNotToBlock();
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
@end
|
||||
17
Example/Pods/Expecta/Expecta/EXPDefines.h
generated
Normal file
17
Example/Pods/Expecta/Expecta/EXPDefines.h
generated
Normal file
@ -0,0 +1,17 @@
|
||||
//
|
||||
// EXPDefines.h
|
||||
// Expecta
|
||||
//
|
||||
// Created by Luke Redpath on 26/03/2012.
|
||||
// Copyright (c) 2012 Peter Jihoon Kim. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef Expecta_EXPDefines_h
|
||||
#define Expecta_EXPDefines_h
|
||||
|
||||
typedef void (^EXPBasicBlock)();
|
||||
typedef id (^EXPIdBlock)();
|
||||
typedef BOOL (^EXPBoolBlock)();
|
||||
typedef NSString *(^EXPStringBlock)();
|
||||
|
||||
#endif
|
||||
13
Example/Pods/Expecta/Expecta/EXPDoubleTuple.h
generated
Normal file
13
Example/Pods/Expecta/Expecta/EXPDoubleTuple.h
generated
Normal file
@ -0,0 +1,13 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@interface EXPDoubleTuple : NSObject {
|
||||
double *_values;
|
||||
size_t _size;
|
||||
}
|
||||
|
||||
@property (nonatomic, assign) double *values;
|
||||
@property (nonatomic, assign) size_t size;
|
||||
|
||||
- (instancetype)initWithDoubleValues:(double *)values size:(size_t)size NS_DESIGNATED_INITIALIZER;
|
||||
|
||||
@end
|
||||
45
Example/Pods/Expecta/Expecta/EXPDoubleTuple.m
generated
Normal file
45
Example/Pods/Expecta/Expecta/EXPDoubleTuple.m
generated
Normal file
@ -0,0 +1,45 @@
|
||||
#import "EXPDoubleTuple.h"
|
||||
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wobjc-designated-initializers"
|
||||
@implementation EXPDoubleTuple
|
||||
#pragma clang diagnostic pop
|
||||
|
||||
@synthesize values = _values, size = _size;
|
||||
|
||||
- (instancetype)initWithDoubleValues:(double *)values size:(size_t)size {
|
||||
if ((self = [super init])) {
|
||||
self.values = malloc(sizeof(double) * size);
|
||||
memcpy(self.values, values, sizeof(double) * size);
|
||||
self.size = size;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc {
|
||||
free(self.values);
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
- (BOOL)isEqual:(id)object {
|
||||
if (![object isKindOfClass:[EXPDoubleTuple class]]) return NO;
|
||||
EXPDoubleTuple *other = (EXPDoubleTuple *)object;
|
||||
if (self.size == other.size) {
|
||||
for (int i = 0; i < self.size; ++i) {
|
||||
if (self.values[i] != other.values[i]) return NO;
|
||||
}
|
||||
return YES;
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (NSString *)description {
|
||||
if (self.size == 2) {
|
||||
return [NSString stringWithFormat:@"Double tuple: {%f, %f}", self.values[0], self.values[1]];
|
||||
} else if (self.size == 4) {
|
||||
return [NSString stringWithFormat:@"Double tuple: {%f, %f, %f, %f}", self.values[0], self.values[1], self.values[2], self.values[3]];
|
||||
}
|
||||
return [NSString stringWithFormat:@"Double tuple of unexpected size %zd, sadly", self.size];
|
||||
}
|
||||
|
||||
@end
|
||||
45
Example/Pods/Expecta/Expecta/EXPExpect.h
generated
Normal file
45
Example/Pods/Expecta/Expecta/EXPExpect.h
generated
Normal file
@ -0,0 +1,45 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
#import "EXPMatcher.h"
|
||||
#import "EXPDefines.h"
|
||||
|
||||
@interface EXPExpect : NSObject {
|
||||
EXPIdBlock _actualBlock;
|
||||
id _testCase;
|
||||
int _lineNumber;
|
||||
char *_fileName;
|
||||
BOOL _negative;
|
||||
BOOL _asynchronous;
|
||||
NSTimeInterval _timeout;
|
||||
}
|
||||
|
||||
@property(nonatomic, copy) EXPIdBlock actualBlock;
|
||||
@property(nonatomic, readonly) id actual;
|
||||
@property(nonatomic, assign) id testCase;
|
||||
@property(nonatomic) int lineNumber;
|
||||
@property(nonatomic) const char *fileName;
|
||||
@property(nonatomic) BOOL negative;
|
||||
@property(nonatomic) BOOL asynchronous;
|
||||
@property(nonatomic) NSTimeInterval timeout;
|
||||
|
||||
@property(nonatomic, readonly) EXPExpect *to;
|
||||
@property(nonatomic, readonly) EXPExpect *toNot;
|
||||
@property(nonatomic, readonly) EXPExpect *notTo;
|
||||
@property(nonatomic, readonly) EXPExpect *will;
|
||||
@property(nonatomic, readonly) EXPExpect *willNot;
|
||||
@property(nonatomic, readonly) EXPExpect *(^after)(NSTimeInterval timeInterval);
|
||||
|
||||
- (instancetype)initWithActualBlock:(id)actualBlock testCase:(id)testCase lineNumber:(int)lineNumber fileName:(const char *)fileName NS_DESIGNATED_INITIALIZER;
|
||||
+ (EXPExpect *)expectWithActualBlock:(id)actualBlock testCase:(id)testCase lineNumber:(int)lineNumber fileName:(const char *)fileName;
|
||||
|
||||
- (void)applyMatcher:(id<EXPMatcher>)matcher;
|
||||
- (void)applyMatcher:(id<EXPMatcher>)matcher to:(NSObject **)actual;
|
||||
|
||||
@end
|
||||
|
||||
@interface EXPDynamicPredicateMatcher : NSObject <EXPMatcher> {
|
||||
EXPExpect *_expectation;
|
||||
SEL _selector;
|
||||
}
|
||||
- (instancetype)initWithExpectation:(EXPExpect *)expectation selector:(SEL)selector NS_DESIGNATED_INITIALIZER;
|
||||
@property (nonatomic, readonly, copy) void (^dispatch)(void);
|
||||
@end
|
||||
221
Example/Pods/Expecta/Expecta/EXPExpect.m
generated
Normal file
221
Example/Pods/Expecta/Expecta/EXPExpect.m
generated
Normal file
@ -0,0 +1,221 @@
|
||||
#import "EXPExpect.h"
|
||||
#import "NSObject+Expecta.h"
|
||||
#import "Expecta.h"
|
||||
#import "EXPUnsupportedObject.h"
|
||||
#import "EXPMatcher.h"
|
||||
#import "EXPBlockDefinedMatcher.h"
|
||||
#import <libkern/OSAtomic.h>
|
||||
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wobjc-designated-initializers"
|
||||
@implementation EXPExpect
|
||||
#pragma clang diagnostic pop
|
||||
|
||||
@dynamic
|
||||
actual,
|
||||
to,
|
||||
toNot,
|
||||
notTo,
|
||||
will,
|
||||
willNot,
|
||||
after;
|
||||
|
||||
@synthesize
|
||||
actualBlock=_actualBlock,
|
||||
testCase=_testCase,
|
||||
negative=_negative,
|
||||
asynchronous=_asynchronous,
|
||||
timeout=_timeout,
|
||||
lineNumber=_lineNumber,
|
||||
fileName=_fileName;
|
||||
|
||||
- (instancetype)initWithActualBlock:(id)actualBlock testCase:(id)testCase lineNumber:(int)lineNumber fileName:(const char *)fileName {
|
||||
self = [super init];
|
||||
if(self) {
|
||||
self.actualBlock = actualBlock;
|
||||
self.testCase = testCase;
|
||||
self.negative = NO;
|
||||
self.asynchronous = NO;
|
||||
self.timeout = [Expecta asynchronousTestTimeout];
|
||||
self.lineNumber = lineNumber;
|
||||
self.fileName = fileName;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc
|
||||
{
|
||||
[_actualBlock release];
|
||||
_actualBlock = nil;
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
+ (EXPExpect *)expectWithActualBlock:(id)actualBlock testCase:(id)testCase lineNumber:(int)lineNumber fileName:(const char *)fileName {
|
||||
return [[[EXPExpect alloc] initWithActualBlock:actualBlock testCase:(id)testCase lineNumber:lineNumber fileName:fileName] autorelease];
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
|
||||
- (EXPExpect *)to {
|
||||
return self;
|
||||
}
|
||||
|
||||
- (EXPExpect *)toNot {
|
||||
self.negative = !self.negative;
|
||||
return self;
|
||||
}
|
||||
|
||||
- (EXPExpect *)notTo {
|
||||
return [self toNot];
|
||||
}
|
||||
|
||||
- (EXPExpect *)will {
|
||||
self.asynchronous = YES;
|
||||
return self;
|
||||
}
|
||||
|
||||
- (EXPExpect *)willNot {
|
||||
return self.will.toNot;
|
||||
}
|
||||
|
||||
- (EXPExpect *(^)(NSTimeInterval))after
|
||||
{
|
||||
EXPExpect * (^block)(NSTimeInterval) = [^EXPExpect *(NSTimeInterval timeout) {
|
||||
self.asynchronous = YES;
|
||||
self.timeout = timeout;
|
||||
return self;
|
||||
} copy];
|
||||
|
||||
return [block autorelease];
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
|
||||
- (id)actual {
|
||||
if(self.actualBlock) {
|
||||
return self.actualBlock();
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (void)applyMatcher:(id<EXPMatcher>)matcher
|
||||
{
|
||||
id actual = [self actual];
|
||||
[self applyMatcher:matcher to:&actual];
|
||||
}
|
||||
|
||||
- (void)applyMatcher:(id<EXPMatcher>)matcher to:(NSObject **)actual {
|
||||
if([*actual isKindOfClass:[EXPUnsupportedObject class]]) {
|
||||
EXPFail(self.testCase, self.lineNumber, self.fileName,
|
||||
[NSString stringWithFormat:@"expecting a %@ is not supported", ((EXPUnsupportedObject *)*actual).type]);
|
||||
} else {
|
||||
BOOL failed = NO;
|
||||
if([matcher respondsToSelector:@selector(meetsPrerequesiteFor:)] &&
|
||||
![matcher meetsPrerequesiteFor:*actual]) {
|
||||
failed = YES;
|
||||
} else {
|
||||
BOOL matchResult = NO;
|
||||
if(self.asynchronous) {
|
||||
NSTimeInterval timeOut = self.timeout;
|
||||
NSDate *expiryDate = [NSDate dateWithTimeIntervalSinceNow:timeOut];
|
||||
while(1) {
|
||||
matchResult = [matcher matches:*actual];
|
||||
failed = self.negative ? matchResult : !matchResult;
|
||||
if(!failed || ([(NSDate *)[NSDate date] compare:expiryDate] == NSOrderedDescending)) {
|
||||
break;
|
||||
}
|
||||
[[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.01]];
|
||||
OSMemoryBarrier();
|
||||
*actual = self.actual;
|
||||
}
|
||||
} else {
|
||||
matchResult = [matcher matches:*actual];
|
||||
}
|
||||
failed = self.negative ? matchResult : !matchResult;
|
||||
}
|
||||
if(failed) {
|
||||
NSString *message = nil;
|
||||
|
||||
if(self.negative) {
|
||||
if ([matcher respondsToSelector:@selector(failureMessageForNotTo:)]) {
|
||||
message = [matcher failureMessageForNotTo:*actual];
|
||||
}
|
||||
} else {
|
||||
if ([matcher respondsToSelector:@selector(failureMessageForTo:)]) {
|
||||
message = [matcher failureMessageForTo:*actual];
|
||||
}
|
||||
}
|
||||
if (message == nil) {
|
||||
message = @"Match Failed.";
|
||||
}
|
||||
|
||||
EXPFail(self.testCase, self.lineNumber, self.fileName, message);
|
||||
}
|
||||
}
|
||||
self.negative = NO;
|
||||
}
|
||||
|
||||
#pragma mark - Dynamic predicate dispatch
|
||||
|
||||
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector
|
||||
{
|
||||
if ([self.actual respondsToSelector:aSelector]) {
|
||||
return [self.actual methodSignatureForSelector:aSelector];
|
||||
}
|
||||
return [super methodSignatureForSelector:aSelector];
|
||||
}
|
||||
|
||||
- (void)forwardInvocation:(NSInvocation *)anInvocation
|
||||
{
|
||||
if ([self.actual respondsToSelector:anInvocation.selector]) {
|
||||
EXPDynamicPredicateMatcher *matcher = [[EXPDynamicPredicateMatcher alloc] initWithExpectation:self selector:anInvocation.selector];
|
||||
[anInvocation setSelector:@selector(dispatch)];
|
||||
[anInvocation invokeWithTarget:matcher];
|
||||
[matcher release];
|
||||
}
|
||||
else {
|
||||
[super forwardInvocation:anInvocation];
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wobjc-designated-initializers"
|
||||
@implementation EXPDynamicPredicateMatcher
|
||||
#pragma clang diagnostic pop
|
||||
|
||||
- (instancetype)initWithExpectation:(EXPExpect *)expectation selector:(SEL)selector
|
||||
{
|
||||
if ((self = [super init])) {
|
||||
_expectation = expectation;
|
||||
_selector = selector;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (BOOL)matches:(id)actual
|
||||
{
|
||||
return (BOOL)[actual performSelector:_selector];
|
||||
}
|
||||
|
||||
- (NSString *)failureMessageForTo:(id)actual
|
||||
{
|
||||
return [NSString stringWithFormat:@"expected %@ to be true", NSStringFromSelector(_selector)];
|
||||
}
|
||||
|
||||
- (NSString *)failureMessageForNotTo:(id)actual
|
||||
{
|
||||
return [NSString stringWithFormat:@"expected %@ to be false", NSStringFromSelector(_selector)];
|
||||
}
|
||||
|
||||
- (void (^)(void))dispatch
|
||||
{
|
||||
__block id blockExpectation = _expectation;
|
||||
|
||||
return [[^{
|
||||
[blockExpectation applyMatcher:self];
|
||||
} copy] autorelease];
|
||||
}
|
||||
|
||||
@end
|
||||
13
Example/Pods/Expecta/Expecta/EXPFloatTuple.h
generated
Normal file
13
Example/Pods/Expecta/Expecta/EXPFloatTuple.h
generated
Normal file
@ -0,0 +1,13 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@interface EXPFloatTuple : NSObject {
|
||||
float *_values;
|
||||
size_t _size;
|
||||
}
|
||||
|
||||
@property (nonatomic, assign) float *values;
|
||||
@property (nonatomic, assign) size_t size;
|
||||
|
||||
- (instancetype)initWithFloatValues:(float *)values size:(size_t)size NS_DESIGNATED_INITIALIZER;
|
||||
|
||||
@end
|
||||
55
Example/Pods/Expecta/Expecta/EXPFloatTuple.m
generated
Normal file
55
Example/Pods/Expecta/Expecta/EXPFloatTuple.m
generated
Normal file
@ -0,0 +1,55 @@
|
||||
#import "EXPFloatTuple.h"
|
||||
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wobjc-designated-initializers"
|
||||
@implementation EXPFloatTuple
|
||||
#pragma clang diagnostic pop
|
||||
|
||||
@synthesize values = _values, size = _size;
|
||||
|
||||
- (instancetype)initWithFloatValues:(float *)values size:(size_t)size {
|
||||
if ((self = [super init])) {
|
||||
self.values = malloc(sizeof(float) * size);
|
||||
memcpy(self.values, values, sizeof(float) * size);
|
||||
self.size = size;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc {
|
||||
free(self.values);
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
- (BOOL)isEqual:(id)object {
|
||||
if (![object isKindOfClass:[EXPFloatTuple class]]) return NO;
|
||||
EXPFloatTuple *other = (EXPFloatTuple *)object;
|
||||
if (self.size == other.size) {
|
||||
for (int i = 0; i < self.size; ++i) {
|
||||
if (self.values[i] != other.values[i]) return NO;
|
||||
}
|
||||
return YES;
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (NSUInteger)hash
|
||||
{
|
||||
NSUInteger prime = 31;
|
||||
NSUInteger hash = 0;
|
||||
for (int i=0; i<self.size; i++) {
|
||||
hash = prime * hash + (NSUInteger)self.values[i];
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
|
||||
- (NSString *)description {
|
||||
if (self.size == 2) {
|
||||
return [NSString stringWithFormat:@"Float tuple: {%f, %f}", self.values[0], self.values[1]];
|
||||
} else if (self.size == 4) {
|
||||
return [NSString stringWithFormat:@"Float tuple: {%f, %f, %f, %f}", self.values[0], self.values[1], self.values[2], self.values[3]];
|
||||
}
|
||||
return [NSString stringWithFormat:@"Float tuple of unexpected size %zd, sadly", self.size];
|
||||
}
|
||||
|
||||
@end
|
||||
20
Example/Pods/Expecta/Expecta/EXPMatcher.h
generated
Normal file
20
Example/Pods/Expecta/Expecta/EXPMatcher.h
generated
Normal file
@ -0,0 +1,20 @@
|
||||
//
|
||||
// EXPMatcher.h
|
||||
// Expecta
|
||||
//
|
||||
// Created by Luke Redpath on 26/03/2012.
|
||||
// Copyright (c) 2012 Peter Jihoon Kim. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@protocol EXPMatcher <NSObject>
|
||||
|
||||
- (BOOL)matches:(id)actual;
|
||||
|
||||
@optional
|
||||
- (BOOL)meetsPrerequesiteFor:(id)actual;
|
||||
- (NSString *)failureMessageForTo:(id)actual;
|
||||
- (NSString *)failureMessageForNotTo:(id)actual;
|
||||
|
||||
@end
|
||||
11
Example/Pods/Expecta/Expecta/EXPUnsupportedObject.h
generated
Normal file
11
Example/Pods/Expecta/Expecta/EXPUnsupportedObject.h
generated
Normal file
@ -0,0 +1,11 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@interface EXPUnsupportedObject : NSObject {
|
||||
NSString *_type;
|
||||
}
|
||||
|
||||
@property (nonatomic, retain) NSString *type;
|
||||
|
||||
- (instancetype)initWithType:(NSString *)type NS_DESIGNATED_INITIALIZER;
|
||||
|
||||
@end
|
||||
23
Example/Pods/Expecta/Expecta/EXPUnsupportedObject.m
generated
Normal file
23
Example/Pods/Expecta/Expecta/EXPUnsupportedObject.m
generated
Normal file
@ -0,0 +1,23 @@
|
||||
#import "EXPUnsupportedObject.h"
|
||||
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wobjc-designated-initializers"
|
||||
@implementation EXPUnsupportedObject
|
||||
#pragma clang diagnostic pop
|
||||
|
||||
@synthesize type=_type;
|
||||
|
||||
- (instancetype)initWithType:(NSString *)type {
|
||||
self = [super init];
|
||||
if(self) {
|
||||
self.type = type;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc {
|
||||
self.type = nil;
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
@end
|
||||
15
Example/Pods/Expecta/Expecta/Expecta.h
generated
Normal file
15
Example/Pods/Expecta/Expecta/Expecta.h
generated
Normal file
@ -0,0 +1,15 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
//! Project version number for Expecta.
|
||||
FOUNDATION_EXPORT double ExpectaVersionNumber;
|
||||
|
||||
//! Project version string for Expecta.
|
||||
FOUNDATION_EXPORT const unsigned char ExpectaVersionString[];
|
||||
|
||||
#import <Expecta/ExpectaObject.h>
|
||||
#import <Expecta/ExpectaSupport.h>
|
||||
#import <Expecta/EXPMatchers.h>
|
||||
|
||||
// Enable shorthand by default
|
||||
#define expect(...) EXP_expect((__VA_ARGS__))
|
||||
#define failure(...) EXP_failure((__VA_ARGS__))
|
||||
18
Example/Pods/Expecta/Expecta/ExpectaObject.h
generated
Normal file
18
Example/Pods/Expecta/Expecta/ExpectaObject.h
generated
Normal file
@ -0,0 +1,18 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#define EXPObjectify(value) _EXPObjectify(@encode(__typeof__((value))), (value))
|
||||
#define EXP_expect(actual) _EXP_expect(self, __LINE__, __FILE__, ^id{ __typeof__((actual)) strongActual = (actual); return EXPObjectify(strongActual); })
|
||||
#define EXPMatcherInterface(matcherName, matcherArguments) _EXPMatcherInterface(matcherName, matcherArguments)
|
||||
#define EXPMatcherImplementationBegin(matcherName, matcherArguments) _EXPMatcherImplementationBegin(matcherName, matcherArguments)
|
||||
#define EXPMatcherImplementationEnd _EXPMatcherImplementationEnd
|
||||
#define EXPMatcherAliasImplementation(newMatcherName, oldMatcherName, matcherArguments) _EXPMatcherAliasImplementation(newMatcherName, oldMatcherName, matcherArguments)
|
||||
|
||||
#define EXP_failure(message) EXPFail(self, __LINE__, __FILE__, message)
|
||||
|
||||
|
||||
@interface Expecta : NSObject
|
||||
|
||||
+ (NSTimeInterval)asynchronousTestTimeout;
|
||||
+ (void)setAsynchronousTestTimeout:(NSTimeInterval)timeout;
|
||||
|
||||
@end
|
||||
15
Example/Pods/Expecta/Expecta/ExpectaObject.m
generated
Normal file
15
Example/Pods/Expecta/Expecta/ExpectaObject.m
generated
Normal file
@ -0,0 +1,15 @@
|
||||
#import "ExpectaObject.h"
|
||||
|
||||
@implementation Expecta
|
||||
|
||||
static NSTimeInterval _asynchronousTestTimeout = 1.0;
|
||||
|
||||
+ (NSTimeInterval)asynchronousTestTimeout {
|
||||
return _asynchronousTestTimeout;
|
||||
}
|
||||
|
||||
+ (void)setAsynchronousTestTimeout:(NSTimeInterval)timeout {
|
||||
_asynchronousTestTimeout = timeout;
|
||||
}
|
||||
|
||||
@end
|
||||
74
Example/Pods/Expecta/Expecta/ExpectaSupport.h
generated
Normal file
74
Example/Pods/Expecta/Expecta/ExpectaSupport.h
generated
Normal file
@ -0,0 +1,74 @@
|
||||
#import "EXPExpect.h"
|
||||
#import "EXPBlockDefinedMatcher.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
id _EXPObjectify(const char *type, ...);
|
||||
EXPExpect *_EXP_expect(id testCase, int lineNumber, const char *fileName, EXPIdBlock actualBlock);
|
||||
|
||||
void EXPFail(id testCase, int lineNumber, const char *fileName, NSString *message);
|
||||
NSString *EXPDescribeObject(id obj);
|
||||
|
||||
void EXP_prerequisite(EXPBoolBlock block);
|
||||
void EXP_match(EXPBoolBlock block);
|
||||
void EXP_failureMessageForTo(EXPStringBlock block);
|
||||
void EXP_failureMessageForNotTo(EXPStringBlock block);
|
||||
|
||||
#if __has_feature(objc_arc)
|
||||
#define _EXP_release(x)
|
||||
#define _EXP_autorelease(x) (x)
|
||||
|
||||
#else
|
||||
#define _EXP_release(x) [x release]
|
||||
#define _EXP_autorelease(x) [x autorelease]
|
||||
#endif
|
||||
|
||||
// workaround for the categories bug: http://developer.apple.com/library/mac/#qa/qa1490/_index.html
|
||||
#define EXPFixCategoriesBug(name) \
|
||||
__attribute__((constructor)) static void EXPFixCategoriesBug##name() {}
|
||||
|
||||
#define _EXPMatcherInterface(matcherName, matcherArguments) \
|
||||
@interface EXPExpect (matcherName##Matcher) \
|
||||
@property (nonatomic, readonly) void(^ matcherName) matcherArguments; \
|
||||
@end
|
||||
|
||||
#define _EXPMatcherImplementationBegin(matcherName, matcherArguments) \
|
||||
EXPFixCategoriesBug(EXPMatcher##matcherName##Matcher); \
|
||||
@implementation EXPExpect (matcherName##Matcher) \
|
||||
@dynamic matcherName;\
|
||||
- (void(^) matcherArguments) matcherName { \
|
||||
EXPBlockDefinedMatcher *matcher = [[EXPBlockDefinedMatcher alloc] init]; \
|
||||
[[[NSThread currentThread] threadDictionary] setObject:matcher forKey:@"EXP_currentMatcher"]; \
|
||||
__block id actual = self.actual; \
|
||||
__block void (^prerequisite)(EXPBoolBlock block) = ^(EXPBoolBlock block) { EXP_prerequisite(block); }; \
|
||||
__block void (^match)(EXPBoolBlock block) = ^(EXPBoolBlock block) { EXP_match(block); }; \
|
||||
__block void (^failureMessageForTo)(EXPStringBlock block) = ^(EXPStringBlock block) { EXP_failureMessageForTo(block); }; \
|
||||
__block void (^failureMessageForNotTo)(EXPStringBlock block) = ^(EXPStringBlock block) { EXP_failureMessageForNotTo(block); }; \
|
||||
prerequisite(nil); match(nil); failureMessageForTo(nil); failureMessageForNotTo(nil); \
|
||||
void (^matcherBlock) matcherArguments = [^ matcherArguments { \
|
||||
{
|
||||
|
||||
#define _EXPMatcherImplementationEnd \
|
||||
} \
|
||||
[self applyMatcher:matcher to:&actual]; \
|
||||
[[[NSThread currentThread] threadDictionary] removeObjectForKey:@"EXP_currentMatcher"]; \
|
||||
} copy]; \
|
||||
_EXP_release(matcher); \
|
||||
return _EXP_autorelease(matcherBlock); \
|
||||
} \
|
||||
@end
|
||||
|
||||
#define _EXPMatcherAliasImplementation(newMatcherName, oldMatcherName, matcherArguments) \
|
||||
EXPFixCategoriesBug(EXPMatcher##newMatcherName##Matcher); \
|
||||
@implementation EXPExpect (newMatcherName##Matcher) \
|
||||
@dynamic newMatcherName;\
|
||||
- (void(^) matcherArguments) newMatcherName { \
|
||||
return [self oldMatcherName]; \
|
||||
}\
|
||||
@end
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
176
Example/Pods/Expecta/Expecta/ExpectaSupport.m
generated
Normal file
176
Example/Pods/Expecta/Expecta/ExpectaSupport.m
generated
Normal file
@ -0,0 +1,176 @@
|
||||
#import "ExpectaSupport.h"
|
||||
#import "NSValue+Expecta.h"
|
||||
#import "NSObject+Expecta.h"
|
||||
#import "EXPUnsupportedObject.h"
|
||||
#import "EXPFloatTuple.h"
|
||||
#import "EXPDoubleTuple.h"
|
||||
#import "EXPDefines.h"
|
||||
#import <objc/runtime.h>
|
||||
|
||||
@interface NSObject (ExpectaXCTestRecordFailure)
|
||||
|
||||
// suppress warning
|
||||
- (void)recordFailureWithDescription:(NSString *)description inFile:(NSString *)filename atLine:(NSUInteger)lineNumber expected:(BOOL)expected;
|
||||
|
||||
@end
|
||||
|
||||
id _EXPObjectify(const char *type, ...) {
|
||||
va_list v;
|
||||
va_start(v, type);
|
||||
id obj = nil;
|
||||
if(strcmp(type, @encode(char)) == 0) {
|
||||
char actual = (char)va_arg(v, int);
|
||||
obj = @(actual);
|
||||
} else if(strcmp(type, @encode(_Bool)) == 0) {
|
||||
_Static_assert(sizeof(_Bool) <= sizeof(int), "Expected _Bool to be subject to vararg type promotion");
|
||||
_Bool actual = (_Bool)va_arg(v, int);
|
||||
obj = @(actual);
|
||||
} else if(strcmp(type, @encode(double)) == 0) {
|
||||
double actual = (double)va_arg(v, double);
|
||||
obj = @(actual);
|
||||
} else if(strcmp(type, @encode(float)) == 0) {
|
||||
float actual = (float)va_arg(v, double);
|
||||
obj = @(actual);
|
||||
} else if(strcmp(type, @encode(int)) == 0) {
|
||||
int actual = (int)va_arg(v, int);
|
||||
obj = @(actual);
|
||||
} else if(strcmp(type, @encode(long)) == 0) {
|
||||
long actual = (long)va_arg(v, long);
|
||||
obj = @(actual);
|
||||
} else if(strcmp(type, @encode(long long)) == 0) {
|
||||
long long actual = (long long)va_arg(v, long long);
|
||||
obj = @(actual);
|
||||
} else if(strcmp(type, @encode(short)) == 0) {
|
||||
short actual = (short)va_arg(v, int);
|
||||
obj = @(actual);
|
||||
} else if(strcmp(type, @encode(unsigned char)) == 0) {
|
||||
unsigned char actual = (unsigned char)va_arg(v, unsigned int);
|
||||
obj = @(actual);
|
||||
} else if(strcmp(type, @encode(unsigned int)) == 0) {
|
||||
unsigned int actual = (int)va_arg(v, unsigned int);
|
||||
obj = @(actual);
|
||||
} else if(strcmp(type, @encode(unsigned long)) == 0) {
|
||||
unsigned long actual = (unsigned long)va_arg(v, unsigned long);
|
||||
obj = @(actual);
|
||||
} else if(strcmp(type, @encode(unsigned long long)) == 0) {
|
||||
unsigned long long actual = (unsigned long long)va_arg(v, unsigned long long);
|
||||
obj = @(actual);
|
||||
} else if(strcmp(type, @encode(unsigned short)) == 0) {
|
||||
unsigned short actual = (unsigned short)va_arg(v, unsigned int);
|
||||
obj = @(actual);
|
||||
} else if(strstr(type, @encode(EXPBasicBlock)) != NULL) {
|
||||
// @encode(EXPBasicBlock) returns @? as of clang 4.1.
|
||||
// This condition must occur before the test for id/class type,
|
||||
// otherwise blocks will be treated as vanilla objects.
|
||||
id actual = va_arg(v, EXPBasicBlock);
|
||||
obj = [[actual copy] autorelease];
|
||||
} else if((strstr(type, @encode(id)) != NULL) || (strstr(type, @encode(Class)) != 0)) {
|
||||
id actual = va_arg(v, id);
|
||||
obj = actual;
|
||||
} else if(strcmp(type, @encode(__typeof__(nil))) == 0) {
|
||||
obj = nil;
|
||||
} else if(strstr(type, "ff}{") != NULL) { //TODO: of course this only works for a 2x2 e.g. CGRect
|
||||
obj = [[[EXPFloatTuple alloc] initWithFloatValues:(float *)va_arg(v, float[4]) size:4] autorelease];
|
||||
} else if(strstr(type, "=ff}") != NULL) {
|
||||
obj = [[[EXPFloatTuple alloc] initWithFloatValues:(float *)va_arg(v, float[2]) size:2] autorelease];
|
||||
} else if(strstr(type, "=ffff}") != NULL) {
|
||||
obj = [[[EXPFloatTuple alloc] initWithFloatValues:(float *)va_arg(v, float[4]) size:4] autorelease];
|
||||
} else if(strstr(type, "dd}{") != NULL) { //TODO: same here
|
||||
obj = [[[EXPDoubleTuple alloc] initWithDoubleValues:(double *)va_arg(v, double[4]) size:4] autorelease];
|
||||
} else if(strstr(type, "=dd}") != NULL) {
|
||||
obj = [[[EXPDoubleTuple alloc] initWithDoubleValues:(double *)va_arg(v, double[2]) size:2] autorelease];
|
||||
} else if(strstr(type, "=dddd}") != NULL) {
|
||||
obj = [[[EXPDoubleTuple alloc] initWithDoubleValues:(double *)va_arg(v, double[4]) size:4] autorelease];
|
||||
} else if(type[0] == '{') {
|
||||
EXPUnsupportedObject *actual = [[[EXPUnsupportedObject alloc] initWithType:@"struct"] autorelease];
|
||||
obj = actual;
|
||||
} else if(type[0] == '(') {
|
||||
EXPUnsupportedObject *actual = [[[EXPUnsupportedObject alloc] initWithType:@"union"] autorelease];
|
||||
obj = actual;
|
||||
} else {
|
||||
void *actual = va_arg(v, void *);
|
||||
obj = (actual == NULL ? nil :[NSValue valueWithPointer:actual]);
|
||||
}
|
||||
if([obj isKindOfClass:[NSValue class]] && ![obj isKindOfClass:[NSNumber class]]) {
|
||||
[(NSValue *)obj set_EXP_objCType:type];
|
||||
}
|
||||
va_end(v);
|
||||
return obj;
|
||||
}
|
||||
|
||||
EXPExpect *_EXP_expect(id testCase, int lineNumber, const char *fileName, EXPIdBlock actualBlock) {
|
||||
return [EXPExpect expectWithActualBlock:actualBlock testCase:testCase lineNumber:lineNumber fileName:fileName];
|
||||
}
|
||||
|
||||
void EXPFail(id testCase, int lineNumber, const char *fileName, NSString *message) {
|
||||
NSLog(@"%s:%d %@", fileName, lineNumber, message);
|
||||
NSString *reason = [NSString stringWithFormat:@"%s:%d %@", fileName, lineNumber, message];
|
||||
NSException *exception = [NSException exceptionWithName:@"Expecta Error" reason:reason userInfo:nil];
|
||||
|
||||
if(testCase && [testCase respondsToSelector:@selector(recordFailureWithDescription:inFile:atLine:expected:)]){
|
||||
[testCase recordFailureWithDescription:message
|
||||
inFile:@(fileName)
|
||||
atLine:lineNumber
|
||||
expected:NO];
|
||||
} else {
|
||||
[exception raise];
|
||||
}
|
||||
}
|
||||
|
||||
NSString *EXPDescribeObject(id obj) {
|
||||
if(obj == nil) {
|
||||
return @"nil/null";
|
||||
} else if([obj isKindOfClass:[NSValue class]] && ![obj isKindOfClass:[NSNumber class]]) {
|
||||
const char *type = [(NSValue *)obj _EXP_objCType];
|
||||
if(type) {
|
||||
if(strcmp(type, @encode(SEL)) == 0) {
|
||||
return [NSString stringWithFormat:@"@selector(%@)", NSStringFromSelector([obj pointerValue])];
|
||||
} else if(strcmp(type, @encode(Class)) == 0) {
|
||||
return NSStringFromClass([obj pointerValue]);
|
||||
}
|
||||
}
|
||||
}
|
||||
NSString *description = [obj description];
|
||||
if([obj isKindOfClass:[NSArray class]]) {
|
||||
NSMutableArray *arr = [NSMutableArray arrayWithCapacity:[obj count]];
|
||||
for(id o in obj) {
|
||||
[arr addObject:EXPDescribeObject(o)];
|
||||
}
|
||||
description = [NSString stringWithFormat:@"(%@)", [arr componentsJoinedByString:@", "]];
|
||||
} else if([obj isKindOfClass:[NSSet class]] || [obj isKindOfClass:[NSOrderedSet class]]) {
|
||||
NSMutableArray *arr = [NSMutableArray arrayWithCapacity:[obj count]];
|
||||
for(id o in obj) {
|
||||
[arr addObject:EXPDescribeObject(o)];
|
||||
}
|
||||
description = [NSString stringWithFormat:@"{(%@)}", [arr componentsJoinedByString:@", "]];
|
||||
} else if([obj isKindOfClass:[NSDictionary class]]) {
|
||||
NSMutableArray *arr = [NSMutableArray arrayWithCapacity:[obj count]];
|
||||
for(id k in obj) {
|
||||
id v = obj[k];
|
||||
[arr addObject:[NSString stringWithFormat:@"%@ = %@;",EXPDescribeObject(k), EXPDescribeObject(v)]];
|
||||
}
|
||||
description = [NSString stringWithFormat:@"{%@}", [arr componentsJoinedByString:@" "]];
|
||||
} else if([obj isKindOfClass:[NSAttributedString class]]) {
|
||||
description = [obj string];
|
||||
} else {
|
||||
description = [description stringByReplacingOccurrencesOfString:@"\n" withString:@"\\n"];
|
||||
}
|
||||
return description;
|
||||
}
|
||||
|
||||
void EXP_prerequisite(EXPBoolBlock block) {
|
||||
[[[NSThread currentThread] threadDictionary][@"EXP_currentMatcher"] setPrerequisiteBlock:block];
|
||||
}
|
||||
|
||||
void EXP_match(EXPBoolBlock block) {
|
||||
[[[NSThread currentThread] threadDictionary][@"EXP_currentMatcher"] setMatchBlock:block];
|
||||
}
|
||||
|
||||
void EXP_failureMessageForTo(EXPStringBlock block) {
|
||||
[[[NSThread currentThread] threadDictionary][@"EXP_currentMatcher"] setFailureMessageForToBlock:block];
|
||||
}
|
||||
|
||||
void EXP_failureMessageForNotTo(EXPStringBlock block) {
|
||||
[[[NSThread currentThread] threadDictionary][@"EXP_currentMatcher"] setFailureMessageForNotToBlock:block];
|
||||
}
|
||||
|
||||
4
Example/Pods/Expecta/Expecta/Matchers/EXPMatcherHelpers.h
generated
Normal file
4
Example/Pods/Expecta/Expecta/Matchers/EXPMatcherHelpers.h
generated
Normal file
@ -0,0 +1,4 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
BOOL EXPIsValuePointer(NSValue *value);
|
||||
BOOL EXPIsNumberFloat(NSNumber *number);
|
||||
9
Example/Pods/Expecta/Expecta/Matchers/EXPMatcherHelpers.m
generated
Normal file
9
Example/Pods/Expecta/Expecta/Matchers/EXPMatcherHelpers.m
generated
Normal file
@ -0,0 +1,9 @@
|
||||
#import "EXPMatcherHelpers.h"
|
||||
|
||||
BOOL EXPIsValuePointer(NSValue *value) {
|
||||
return [value objCType][0] == @encode(void *)[0];
|
||||
}
|
||||
|
||||
BOOL EXPIsNumberFloat(NSNumber *number) {
|
||||
return strcmp([number objCType], @encode(float)) == 0;
|
||||
}
|
||||
7
Example/Pods/Expecta/Expecta/Matchers/EXPMatchers+beCloseTo.h
generated
Normal file
7
Example/Pods/Expecta/Expecta/Matchers/EXPMatchers+beCloseTo.h
generated
Normal file
@ -0,0 +1,7 @@
|
||||
#import "Expecta.h"
|
||||
|
||||
EXPMatcherInterface(_beCloseToWithin, (id expected, id within));
|
||||
EXPMatcherInterface(beCloseToWithin, (id expected, id within));
|
||||
|
||||
#define beCloseTo(expected) _beCloseToWithin(EXPObjectify((expected)), nil)
|
||||
#define beCloseToWithin(expected, range) _beCloseToWithin(EXPObjectify((expected)), EXPObjectify((range)))
|
||||
49
Example/Pods/Expecta/Expecta/Matchers/EXPMatchers+beCloseTo.m
generated
Normal file
49
Example/Pods/Expecta/Expecta/Matchers/EXPMatchers+beCloseTo.m
generated
Normal file
@ -0,0 +1,49 @@
|
||||
#import "EXPMatchers+beCloseTo.h"
|
||||
#import "EXPMatcherHelpers.h"
|
||||
|
||||
EXPMatcherImplementationBegin(_beCloseToWithin, (id expected, id within)) {
|
||||
prerequisite(^BOOL{
|
||||
return [actual isKindOfClass:[NSNumber class]] &&
|
||||
[expected isKindOfClass:[NSNumber class]] &&
|
||||
([within isKindOfClass:[NSNumber class]] || (within == nil));
|
||||
});
|
||||
|
||||
match(^BOOL{
|
||||
double actualValue = [actual doubleValue];
|
||||
double expectedValue = [expected doubleValue];
|
||||
|
||||
if (within != nil) {
|
||||
double withinValue = [within doubleValue];
|
||||
double lowerBound = expectedValue - withinValue;
|
||||
double upperBound = expectedValue + withinValue;
|
||||
return (actualValue >= lowerBound) && (actualValue <= upperBound);
|
||||
} else {
|
||||
double diff = fabs(actualValue - expectedValue);
|
||||
actualValue = fabs(actualValue);
|
||||
expectedValue = fabs(expectedValue);
|
||||
double largest = (expectedValue > actualValue) ? expectedValue : actualValue;
|
||||
return (diff <= largest * FLT_EPSILON);
|
||||
}
|
||||
});
|
||||
|
||||
failureMessageForTo(^NSString *{
|
||||
if (within) {
|
||||
return [NSString stringWithFormat:@"expected %@ to be close to %@ within %@",
|
||||
EXPDescribeObject(actual), EXPDescribeObject(expected), EXPDescribeObject(within)];
|
||||
} else {
|
||||
return [NSString stringWithFormat:@"expected %@ to be close to %@",
|
||||
EXPDescribeObject(actual), EXPDescribeObject(expected)];
|
||||
}
|
||||
});
|
||||
|
||||
failureMessageForNotTo(^NSString *{
|
||||
if (within) {
|
||||
return [NSString stringWithFormat:@"expected %@ not to be close to %@ within %@",
|
||||
EXPDescribeObject(actual), EXPDescribeObject(expected), EXPDescribeObject(within)];
|
||||
} else {
|
||||
return [NSString stringWithFormat:@"expected %@ not to be close to %@",
|
||||
EXPDescribeObject(actual), EXPDescribeObject(expected)];
|
||||
}
|
||||
});
|
||||
}
|
||||
EXPMatcherImplementationEnd
|
||||
3
Example/Pods/Expecta/Expecta/Matchers/EXPMatchers+beFalsy.h
generated
Normal file
3
Example/Pods/Expecta/Expecta/Matchers/EXPMatchers+beFalsy.h
generated
Normal file
@ -0,0 +1,3 @@
|
||||
#import "Expecta.h"
|
||||
|
||||
EXPMatcherInterface(beFalsy, (void));
|
||||
24
Example/Pods/Expecta/Expecta/Matchers/EXPMatchers+beFalsy.m
generated
Normal file
24
Example/Pods/Expecta/Expecta/Matchers/EXPMatchers+beFalsy.m
generated
Normal file
@ -0,0 +1,24 @@
|
||||
#import "EXPMatchers+beFalsy.h"
|
||||
#import "EXPMatcherHelpers.h"
|
||||
|
||||
EXPMatcherImplementationBegin(beFalsy, (void)) {
|
||||
match(^BOOL{
|
||||
if([actual isKindOfClass:[NSNumber class]]) {
|
||||
return ![(NSNumber *)actual boolValue];
|
||||
} else if([actual isKindOfClass:[NSValue class]]) {
|
||||
if(EXPIsValuePointer((NSValue *)actual)) {
|
||||
return ![(NSValue *)actual pointerValue];
|
||||
}
|
||||
}
|
||||
return !actual;
|
||||
});
|
||||
|
||||
failureMessageForTo(^NSString *{
|
||||
return [NSString stringWithFormat:@"expected: a falsy value, got: %@, which is truthy", EXPDescribeObject(actual)];
|
||||
});
|
||||
|
||||
failureMessageForNotTo(^NSString *{
|
||||
return [NSString stringWithFormat:@"expected: a non-falsy value, got: %@, which is falsy", EXPDescribeObject(actual)];
|
||||
});
|
||||
}
|
||||
EXPMatcherImplementationEnd
|
||||
6
Example/Pods/Expecta/Expecta/Matchers/EXPMatchers+beGreaterThan.h
generated
Normal file
6
Example/Pods/Expecta/Expecta/Matchers/EXPMatchers+beGreaterThan.h
generated
Normal file
@ -0,0 +1,6 @@
|
||||
#import "Expecta.h"
|
||||
|
||||
EXPMatcherInterface(_beGreaterThan, (id expected));
|
||||
EXPMatcherInterface(beGreaterThan, (id expected));
|
||||
|
||||
#define beGreaterThan(expected) _beGreaterThan(EXPObjectify((expected)))
|
||||
20
Example/Pods/Expecta/Expecta/Matchers/EXPMatchers+beGreaterThan.m
generated
Normal file
20
Example/Pods/Expecta/Expecta/Matchers/EXPMatchers+beGreaterThan.m
generated
Normal file
@ -0,0 +1,20 @@
|
||||
#import "EXPMatchers+beGreaterThan.h"
|
||||
#import "EXPMatcherHelpers.h"
|
||||
|
||||
EXPMatcherImplementationBegin(_beGreaterThan, (id expected)) {
|
||||
match(^BOOL{
|
||||
if ([actual respondsToSelector:@selector(compare:)]) {
|
||||
return [actual compare:expected] == NSOrderedDescending;
|
||||
}
|
||||
return NO;
|
||||
});
|
||||
|
||||
failureMessageForTo(^NSString *{
|
||||
return [NSString stringWithFormat:@"expected: %@ to be greater than %@", EXPDescribeObject(actual), EXPDescribeObject(expected)];
|
||||
});
|
||||
|
||||
failureMessageForNotTo(^NSString *{
|
||||
return [NSString stringWithFormat:@"expected: %@ not to be greater than %@", EXPDescribeObject(actual), EXPDescribeObject(expected)];
|
||||
});
|
||||
}
|
||||
EXPMatcherImplementationEnd
|
||||
6
Example/Pods/Expecta/Expecta/Matchers/EXPMatchers+beGreaterThanOrEqualTo.h
generated
Normal file
6
Example/Pods/Expecta/Expecta/Matchers/EXPMatchers+beGreaterThanOrEqualTo.h
generated
Normal file
@ -0,0 +1,6 @@
|
||||
#import "Expecta.h"
|
||||
|
||||
EXPMatcherInterface(_beGreaterThanOrEqualTo, (id expected));
|
||||
EXPMatcherInterface(beGreaterThanOrEqualTo, (id expected));
|
||||
|
||||
#define beGreaterThanOrEqualTo(expected) _beGreaterThanOrEqualTo(EXPObjectify((expected)))
|
||||
20
Example/Pods/Expecta/Expecta/Matchers/EXPMatchers+beGreaterThanOrEqualTo.m
generated
Normal file
20
Example/Pods/Expecta/Expecta/Matchers/EXPMatchers+beGreaterThanOrEqualTo.m
generated
Normal file
@ -0,0 +1,20 @@
|
||||
#import "EXPMatchers+beGreaterThanOrEqualTo.h"
|
||||
#import "EXPMatcherHelpers.h"
|
||||
|
||||
EXPMatcherImplementationBegin(_beGreaterThanOrEqualTo, (id expected)) {
|
||||
match(^BOOL{
|
||||
if ([actual respondsToSelector:@selector(compare:)]) {
|
||||
return [actual compare:expected] != NSOrderedAscending;
|
||||
}
|
||||
return NO;
|
||||
});
|
||||
|
||||
failureMessageForTo(^NSString *{
|
||||
return [NSString stringWithFormat:@"expected: %@ to be greater than or equal to %@", EXPDescribeObject(actual), EXPDescribeObject(expected)];
|
||||
});
|
||||
|
||||
failureMessageForNotTo(^NSString *{
|
||||
return [NSString stringWithFormat:@"expected: %@ not to be greater than or equal to %@", EXPDescribeObject(actual), EXPDescribeObject(expected)];
|
||||
});
|
||||
}
|
||||
EXPMatcherImplementationEnd
|
||||
10
Example/Pods/Expecta/Expecta/Matchers/EXPMatchers+beIdenticalTo.h
generated
Normal file
10
Example/Pods/Expecta/Expecta/Matchers/EXPMatchers+beIdenticalTo.h
generated
Normal file
@ -0,0 +1,10 @@
|
||||
#import "Expecta.h"
|
||||
|
||||
EXPMatcherInterface(_beIdenticalTo, (void *expected));
|
||||
EXPMatcherInterface(beIdenticalTo, (void *expected)); // to aid code completion
|
||||
|
||||
#if __has_feature(objc_arc)
|
||||
#define beIdenticalTo(expected) _beIdenticalTo((__bridge void*)expected)
|
||||
#else
|
||||
#define beIdenticalTo(expected) _beIdenticalTo(expected)
|
||||
#endif
|
||||
24
Example/Pods/Expecta/Expecta/Matchers/EXPMatchers+beIdenticalTo.m
generated
Normal file
24
Example/Pods/Expecta/Expecta/Matchers/EXPMatchers+beIdenticalTo.m
generated
Normal file
@ -0,0 +1,24 @@
|
||||
#import "EXPMatchers+equal.h"
|
||||
#import "EXPMatcherHelpers.h"
|
||||
|
||||
EXPMatcherImplementationBegin(_beIdenticalTo, (void *expected)) {
|
||||
match(^BOOL{
|
||||
if(actual == expected) {
|
||||
return YES;
|
||||
} else if([actual isKindOfClass:[NSValue class]] && EXPIsValuePointer((NSValue *)actual)) {
|
||||
if([(NSValue *)actual pointerValue] == expected) {
|
||||
return YES;
|
||||
}
|
||||
}
|
||||
return NO;
|
||||
});
|
||||
|
||||
failureMessageForTo(^NSString *{
|
||||
return [NSString stringWithFormat:@"expected: <%p>, got: <%p>", expected, actual];
|
||||
});
|
||||
|
||||
failureMessageForNotTo(^NSString *{
|
||||
return [NSString stringWithFormat:@"expected: not <%p>, got: <%p>", expected, actual];
|
||||
});
|
||||
}
|
||||
EXPMatcherImplementationEnd
|
||||
6
Example/Pods/Expecta/Expecta/Matchers/EXPMatchers+beInTheRangeOf.h
generated
Normal file
6
Example/Pods/Expecta/Expecta/Matchers/EXPMatchers+beInTheRangeOf.h
generated
Normal file
@ -0,0 +1,6 @@
|
||||
#import "Expecta.h"
|
||||
|
||||
EXPMatcherInterface(_beInTheRangeOf, (id expectedLowerBound, id expectedUpperBound));
|
||||
EXPMatcherInterface(beInTheRangeOf, (id expectedLowerBound, id expectedUpperBound));
|
||||
|
||||
#define beInTheRangeOf(expectedLowerBound, expectedUpperBound) _beInTheRangeOf(EXPObjectify((expectedLowerBound)), EXPObjectify((expectedUpperBound)))
|
||||
30
Example/Pods/Expecta/Expecta/Matchers/EXPMatchers+beInTheRangeOf.m
generated
Normal file
30
Example/Pods/Expecta/Expecta/Matchers/EXPMatchers+beInTheRangeOf.m
generated
Normal file
@ -0,0 +1,30 @@
|
||||
#import "EXPMatchers+beInTheRangeOf.h"
|
||||
#import "EXPMatcherHelpers.h"
|
||||
|
||||
EXPMatcherImplementationBegin(_beInTheRangeOf, (id expectedLowerBound, id expectedUpperBound)) {
|
||||
match(^BOOL{
|
||||
if ([actual respondsToSelector:@selector(compare:)]) {
|
||||
NSComparisonResult compareLowerBound = [expectedLowerBound compare: actual];
|
||||
NSComparisonResult compareUpperBound = [expectedUpperBound compare: actual];
|
||||
if (compareLowerBound == NSOrderedSame) {
|
||||
return YES;
|
||||
}
|
||||
if (compareUpperBound == NSOrderedSame) {
|
||||
return YES;
|
||||
}
|
||||
if ((compareLowerBound == NSOrderedAscending) && (compareUpperBound == NSOrderedDescending)) {
|
||||
return YES;
|
||||
}
|
||||
}
|
||||
return NO;
|
||||
});
|
||||
|
||||
failureMessageForTo(^NSString *{
|
||||
return [NSString stringWithFormat:@"expected: %@ to be in the range [%@, %@] (inclusive)", EXPDescribeObject(actual), EXPDescribeObject(expectedLowerBound), EXPDescribeObject(expectedUpperBound)];
|
||||
});
|
||||
|
||||
failureMessageForNotTo(^NSString *{
|
||||
return [NSString stringWithFormat:@"expected: %@ not to be in the range [%@, %@] (inclusive)", EXPDescribeObject(actual), EXPDescribeObject(expectedLowerBound), EXPDescribeObject(expectedUpperBound)];
|
||||
});
|
||||
}
|
||||
EXPMatcherImplementationEnd
|
||||
6
Example/Pods/Expecta/Expecta/Matchers/EXPMatchers+beInstanceOf.h
generated
Normal file
6
Example/Pods/Expecta/Expecta/Matchers/EXPMatchers+beInstanceOf.h
generated
Normal file
@ -0,0 +1,6 @@
|
||||
#import "Expecta.h"
|
||||
|
||||
EXPMatcherInterface(beInstanceOf, (Class expected));
|
||||
EXPMatcherInterface(beAnInstanceOf, (Class expected));
|
||||
EXPMatcherInterface(beMemberOf, (Class expected));
|
||||
EXPMatcherInterface(beAMemberOf, (Class expected));
|
||||
31
Example/Pods/Expecta/Expecta/Matchers/EXPMatchers+beInstanceOf.m
generated
Normal file
31
Example/Pods/Expecta/Expecta/Matchers/EXPMatchers+beInstanceOf.m
generated
Normal file
@ -0,0 +1,31 @@
|
||||
#import "EXPMatchers+beInstanceOf.h"
|
||||
|
||||
EXPMatcherImplementationBegin(beInstanceOf, (Class expected)) {
|
||||
BOOL actualIsNil = (actual == nil);
|
||||
BOOL expectedIsNil = (expected == nil);
|
||||
|
||||
prerequisite(^BOOL{
|
||||
return !(actualIsNil || expectedIsNil);
|
||||
});
|
||||
|
||||
match(^BOOL{
|
||||
return [actual isMemberOfClass:expected];
|
||||
});
|
||||
|
||||
failureMessageForTo(^NSString *{
|
||||
if(actualIsNil) return @"the actual value is nil/null";
|
||||
if(expectedIsNil) return @"the expected value is nil/null";
|
||||
return [NSString stringWithFormat:@"expected: an instance of %@, got: an instance of %@", [expected class], [actual class]];
|
||||
});
|
||||
|
||||
failureMessageForNotTo(^NSString *{
|
||||
if(actualIsNil) return @"the actual value is nil/null";
|
||||
if(expectedIsNil) return @"the expected value is nil/null";
|
||||
return [NSString stringWithFormat:@"expected: not an instance of %@, got: an instance of %@", [expected class], [actual class]];
|
||||
});
|
||||
}
|
||||
EXPMatcherImplementationEnd
|
||||
|
||||
EXPMatcherAliasImplementation(beAnInstanceOf, beInstanceOf, (Class expected));
|
||||
EXPMatcherAliasImplementation(beMemberOf, beInstanceOf, (Class expected));
|
||||
EXPMatcherAliasImplementation(beAMemberOf, beInstanceOf, (Class expected));
|
||||
4
Example/Pods/Expecta/Expecta/Matchers/EXPMatchers+beKindOf.h
generated
Normal file
4
Example/Pods/Expecta/Expecta/Matchers/EXPMatchers+beKindOf.h
generated
Normal file
@ -0,0 +1,4 @@
|
||||
#import "Expecta.h"
|
||||
|
||||
EXPMatcherInterface(beKindOf, (Class expected));
|
||||
EXPMatcherInterface(beAKindOf, (Class expected));
|
||||
29
Example/Pods/Expecta/Expecta/Matchers/EXPMatchers+beKindOf.m
generated
Normal file
29
Example/Pods/Expecta/Expecta/Matchers/EXPMatchers+beKindOf.m
generated
Normal file
@ -0,0 +1,29 @@
|
||||
#import "EXPMatchers+beKindOf.h"
|
||||
|
||||
EXPMatcherImplementationBegin(beKindOf, (Class expected)) {
|
||||
BOOL actualIsNil = (actual == nil);
|
||||
BOOL expectedIsNil = (expected == nil);
|
||||
|
||||
prerequisite(^BOOL{
|
||||
return !(actualIsNil || expectedIsNil);
|
||||
});
|
||||
|
||||
match(^BOOL{
|
||||
return [actual isKindOfClass:expected];
|
||||
});
|
||||
|
||||
failureMessageForTo(^NSString *{
|
||||
if(actualIsNil) return @"the actual value is nil/null";
|
||||
if(expectedIsNil) return @"the expected value is nil/null";
|
||||
return [NSString stringWithFormat:@"expected: a kind of %@, got: an instance of %@, which is not a kind of %@", [expected class], [actual class], [expected class]];
|
||||
});
|
||||
|
||||
failureMessageForNotTo(^NSString *{
|
||||
if(actualIsNil) return @"the actual value is nil/null";
|
||||
if(expectedIsNil) return @"the expected value is nil/null";
|
||||
return [NSString stringWithFormat:@"expected: not a kind of %@, got: an instance of %@, which is a kind of %@", [expected class], [actual class], [expected class]];
|
||||
});
|
||||
}
|
||||
EXPMatcherImplementationEnd
|
||||
|
||||
EXPMatcherAliasImplementation(beAKindOf, beKindOf, (Class expected));
|
||||
6
Example/Pods/Expecta/Expecta/Matchers/EXPMatchers+beLessThan.h
generated
Normal file
6
Example/Pods/Expecta/Expecta/Matchers/EXPMatchers+beLessThan.h
generated
Normal file
@ -0,0 +1,6 @@
|
||||
#import "Expecta.h"
|
||||
|
||||
EXPMatcherInterface(_beLessThan, (id expected));
|
||||
EXPMatcherInterface(beLessThan, (id expected));
|
||||
|
||||
#define beLessThan(expected) _beLessThan(EXPObjectify((expected)))
|
||||
20
Example/Pods/Expecta/Expecta/Matchers/EXPMatchers+beLessThan.m
generated
Normal file
20
Example/Pods/Expecta/Expecta/Matchers/EXPMatchers+beLessThan.m
generated
Normal file
@ -0,0 +1,20 @@
|
||||
#import "EXPMatchers+beLessThan.h"
|
||||
#import "EXPMatcherHelpers.h"
|
||||
|
||||
EXPMatcherImplementationBegin(_beLessThan, (id expected)) {
|
||||
match(^BOOL{
|
||||
if ([actual respondsToSelector:@selector(compare:)]) {
|
||||
return [actual compare:expected] == NSOrderedAscending;
|
||||
}
|
||||
return NO;
|
||||
});
|
||||
|
||||
failureMessageForTo(^NSString *{
|
||||
return [NSString stringWithFormat:@"expected: %@ to be less than %@", EXPDescribeObject(actual), EXPDescribeObject(expected)];
|
||||
});
|
||||
|
||||
failureMessageForNotTo(^NSString *{
|
||||
return [NSString stringWithFormat:@"expected: %@ not to be less than %@", EXPDescribeObject(actual), EXPDescribeObject(expected)];
|
||||
});
|
||||
}
|
||||
EXPMatcherImplementationEnd
|
||||
6
Example/Pods/Expecta/Expecta/Matchers/EXPMatchers+beLessThanOrEqualTo.h
generated
Normal file
6
Example/Pods/Expecta/Expecta/Matchers/EXPMatchers+beLessThanOrEqualTo.h
generated
Normal file
@ -0,0 +1,6 @@
|
||||
#import "Expecta.h"
|
||||
|
||||
EXPMatcherInterface(_beLessThanOrEqualTo, (id expected));
|
||||
EXPMatcherInterface(beLessThanOrEqualTo, (id expected));
|
||||
|
||||
#define beLessThanOrEqualTo(expected) _beLessThanOrEqualTo(EXPObjectify((expected)))
|
||||
20
Example/Pods/Expecta/Expecta/Matchers/EXPMatchers+beLessThanOrEqualTo.m
generated
Normal file
20
Example/Pods/Expecta/Expecta/Matchers/EXPMatchers+beLessThanOrEqualTo.m
generated
Normal file
@ -0,0 +1,20 @@
|
||||
#import "EXPMatchers+beLessThanOrEqualTo.h"
|
||||
#import "EXPMatcherHelpers.h"
|
||||
|
||||
EXPMatcherImplementationBegin(_beLessThanOrEqualTo, (id expected)) {
|
||||
match(^BOOL{
|
||||
if ([actual respondsToSelector:@selector(compare:)]) {
|
||||
return [actual compare:expected] != NSOrderedDescending;
|
||||
}
|
||||
return NO;
|
||||
});
|
||||
|
||||
failureMessageForTo(^NSString *{
|
||||
return [NSString stringWithFormat:@"expected: %@ to be less than or equal to %@", EXPDescribeObject(actual), EXPDescribeObject(expected)];
|
||||
});
|
||||
|
||||
failureMessageForNotTo(^NSString *{
|
||||
return [NSString stringWithFormat:@"expected: %@ not to be less than or equal to %@", EXPDescribeObject(actual), EXPDescribeObject(expected)];
|
||||
});
|
||||
}
|
||||
EXPMatcherImplementationEnd
|
||||
4
Example/Pods/Expecta/Expecta/Matchers/EXPMatchers+beNil.h
generated
Normal file
4
Example/Pods/Expecta/Expecta/Matchers/EXPMatchers+beNil.h
generated
Normal file
@ -0,0 +1,4 @@
|
||||
#import "Expecta.h"
|
||||
|
||||
EXPMatcherInterface(beNil, (void));
|
||||
EXPMatcherInterface(beNull, (void));
|
||||
18
Example/Pods/Expecta/Expecta/Matchers/EXPMatchers+beNil.m
generated
Normal file
18
Example/Pods/Expecta/Expecta/Matchers/EXPMatchers+beNil.m
generated
Normal file
@ -0,0 +1,18 @@
|
||||
#import "EXPMatchers+beNil.h"
|
||||
|
||||
EXPMatcherImplementationBegin(beNil, (void)) {
|
||||
match(^BOOL{
|
||||
return actual == nil;
|
||||
});
|
||||
|
||||
failureMessageForTo(^NSString *{
|
||||
return [NSString stringWithFormat:@"expected: nil/null, got: %@", EXPDescribeObject(actual)];
|
||||
});
|
||||
|
||||
failureMessageForNotTo(^NSString *{
|
||||
return [NSString stringWithFormat:@"expected: not nil/null, got: %@", EXPDescribeObject(actual)];
|
||||
});
|
||||
}
|
||||
EXPMatcherImplementationEnd
|
||||
|
||||
EXPMatcherAliasImplementation(beNull, beNil, (void));
|
||||
4
Example/Pods/Expecta/Expecta/Matchers/EXPMatchers+beSubclassOf.h
generated
Normal file
4
Example/Pods/Expecta/Expecta/Matchers/EXPMatchers+beSubclassOf.h
generated
Normal file
@ -0,0 +1,4 @@
|
||||
#import "Expecta.h"
|
||||
|
||||
EXPMatcherInterface(beSubclassOf, (Class expected));
|
||||
EXPMatcherInterface(beASubclassOf, (Class expected));
|
||||
29
Example/Pods/Expecta/Expecta/Matchers/EXPMatchers+beSubclassOf.m
generated
Normal file
29
Example/Pods/Expecta/Expecta/Matchers/EXPMatchers+beSubclassOf.m
generated
Normal file
@ -0,0 +1,29 @@
|
||||
#import "EXPMatchers+beSubclassOf.h"
|
||||
#import "NSValue+Expecta.h"
|
||||
#import <objc/runtime.h>
|
||||
|
||||
EXPMatcherImplementationBegin(beSubclassOf, (Class expected)) {
|
||||
__block BOOL actualIsClass = YES;
|
||||
|
||||
prerequisite(^BOOL {
|
||||
actualIsClass = class_isMetaClass(object_getClass(actual));
|
||||
return actualIsClass;
|
||||
});
|
||||
|
||||
match(^BOOL{
|
||||
return [actual isSubclassOfClass:expected];
|
||||
});
|
||||
|
||||
failureMessageForTo(^NSString *{
|
||||
if(!actualIsClass) return @"the actual value is not a Class";
|
||||
return [NSString stringWithFormat:@"expected: a subclass of %@, got: a class %@, which is not a subclass of %@", [expected class], actual, [expected class]];
|
||||
});
|
||||
|
||||
failureMessageForNotTo(^NSString *{
|
||||
if(!actualIsClass) return @"the actual value is not a Class";
|
||||
return [NSString stringWithFormat:@"expected: not a subclass of %@, got: a class %@, which is a subclass of %@", [expected class], actual, [expected class]];
|
||||
});
|
||||
}
|
||||
EXPMatcherImplementationEnd
|
||||
|
||||
EXPMatcherAliasImplementation(beASubclassOf, beSubclassOf, (Class expected));
|
||||
4
Example/Pods/Expecta/Expecta/Matchers/EXPMatchers+beSupersetOf.h
generated
Normal file
4
Example/Pods/Expecta/Expecta/Matchers/EXPMatchers+beSupersetOf.h
generated
Normal file
@ -0,0 +1,4 @@
|
||||
#import "Expecta.h"
|
||||
|
||||
EXPMatcherInterface(beSupersetOf, (id subset));
|
||||
|
||||
62
Example/Pods/Expecta/Expecta/Matchers/EXPMatchers+beSupersetOf.m
generated
Normal file
62
Example/Pods/Expecta/Expecta/Matchers/EXPMatchers+beSupersetOf.m
generated
Normal file
@ -0,0 +1,62 @@
|
||||
#import "EXPMatchers+contain.h"
|
||||
|
||||
EXPMatcherImplementationBegin(beSupersetOf, (id subset)) {
|
||||
BOOL actualIsCompatible = [actual isKindOfClass:[NSDictionary class]] || [actual respondsToSelector:@selector(containsObject:)];
|
||||
BOOL subsetIsNil = (subset == nil);
|
||||
|
||||
// For some instances the isKindOfClass: method returns false, even though
|
||||
// they are both actually dictionaries. e.g. Comparing a NSCFDictionary and a
|
||||
// NSDictionary.
|
||||
// Or in cases when you compare NSMutableArray (which implementation is __NSArrayM:NSMutableArray:NSArray)
|
||||
// and NSArray (which implementation is __NSArrayI:NSArray)
|
||||
BOOL bothAreIdenticalCollectionClasses = ([actual isKindOfClass:[NSDictionary class]] && [subset isKindOfClass:[NSDictionary class]]) ||
|
||||
([actual isKindOfClass:[NSArray class]] && [subset isKindOfClass:[NSArray class]]) ||
|
||||
([actual isKindOfClass:[NSSet class]] && [subset isKindOfClass:[NSSet class]]) ||
|
||||
([actual isKindOfClass:[NSOrderedSet class]] && [subset isKindOfClass:[NSOrderedSet class]]);
|
||||
|
||||
BOOL classMatches = bothAreIdenticalCollectionClasses || [subset isKindOfClass:[actual class]];
|
||||
|
||||
prerequisite(^BOOL{
|
||||
return actualIsCompatible && !subsetIsNil && classMatches;
|
||||
});
|
||||
|
||||
match(^BOOL{
|
||||
if(!actualIsCompatible) return NO;
|
||||
|
||||
if([actual isKindOfClass:[NSDictionary class]]) {
|
||||
for (id key in subset) {
|
||||
id actualValue = [actual valueForKey:key];
|
||||
id subsetValue = [subset valueForKey:key];
|
||||
|
||||
if (![subsetValue isEqual:actualValue]) return NO;
|
||||
}
|
||||
} else {
|
||||
for (id object in subset) {
|
||||
if (![actual containsObject:object]) return NO;
|
||||
}
|
||||
}
|
||||
|
||||
return YES;
|
||||
});
|
||||
|
||||
failureMessageForTo(^NSString *{
|
||||
if(!actualIsCompatible) return [NSString stringWithFormat:@"%@ is not an instance of NSDictionary and does not implement -containsObject:", EXPDescribeObject(actual)];
|
||||
|
||||
if(subsetIsNil) return @"the expected value is nil/null";
|
||||
|
||||
if(!classMatches) return [NSString stringWithFormat:@"%@ does not match the class of %@", EXPDescribeObject(subset), EXPDescribeObject(actual)];
|
||||
|
||||
return [NSString stringWithFormat:@"expected %@ to be a superset of %@", EXPDescribeObject(actual), EXPDescribeObject(subset)];
|
||||
});
|
||||
|
||||
failureMessageForNotTo(^NSString *{
|
||||
if(!actualIsCompatible) return [NSString stringWithFormat:@"%@ is not an instance of NSDictionary and does not implement -containsObject:", EXPDescribeObject(actual)];
|
||||
|
||||
if(subsetIsNil) return @"the expected value is nil/null";
|
||||
|
||||
if(!classMatches) return [NSString stringWithFormat:@"%@ does not match the class of %@", EXPDescribeObject(subset), EXPDescribeObject(actual)];
|
||||
|
||||
return [NSString stringWithFormat:@"expected %@ not to be a superset of %@", EXPDescribeObject(actual), EXPDescribeObject(subset)];
|
||||
});
|
||||
}
|
||||
EXPMatcherImplementationEnd
|
||||
3
Example/Pods/Expecta/Expecta/Matchers/EXPMatchers+beTruthy.h
generated
Normal file
3
Example/Pods/Expecta/Expecta/Matchers/EXPMatchers+beTruthy.h
generated
Normal file
@ -0,0 +1,3 @@
|
||||
#import "Expecta.h"
|
||||
|
||||
EXPMatcherInterface(beTruthy, (void));
|
||||
24
Example/Pods/Expecta/Expecta/Matchers/EXPMatchers+beTruthy.m
generated
Normal file
24
Example/Pods/Expecta/Expecta/Matchers/EXPMatchers+beTruthy.m
generated
Normal file
@ -0,0 +1,24 @@
|
||||
#import "EXPMatchers+beTruthy.h"
|
||||
#import "EXPMatcherHelpers.h"
|
||||
|
||||
EXPMatcherImplementationBegin(beTruthy, (void)) {
|
||||
match(^BOOL{
|
||||
if([actual isKindOfClass:[NSNumber class]]) {
|
||||
return !![(NSNumber *)actual boolValue];
|
||||
} else if([actual isKindOfClass:[NSValue class]]) {
|
||||
if(EXPIsValuePointer((NSValue *)actual)) {
|
||||
return !![(NSValue *)actual pointerValue];
|
||||
}
|
||||
}
|
||||
return !!actual;
|
||||
});
|
||||
|
||||
failureMessageForTo(^NSString *{
|
||||
return [NSString stringWithFormat:@"expected: a truthy value, got: %@, which is falsy", EXPDescribeObject(actual)];
|
||||
});
|
||||
|
||||
failureMessageForNotTo(^NSString *{
|
||||
return [NSString stringWithFormat:@"expected: a non-truthy value, got: %@, which is truthy", EXPDescribeObject(actual)];
|
||||
});
|
||||
}
|
||||
EXPMatcherImplementationEnd
|
||||
4
Example/Pods/Expecta/Expecta/Matchers/EXPMatchers+beginWith.h
generated
Normal file
4
Example/Pods/Expecta/Expecta/Matchers/EXPMatchers+beginWith.h
generated
Normal file
@ -0,0 +1,4 @@
|
||||
#import "Expecta.h"
|
||||
|
||||
EXPMatcherInterface(beginWith, (id expected));
|
||||
EXPMatcherInterface(startWith, (id expected));
|
||||
51
Example/Pods/Expecta/Expecta/Matchers/EXPMatchers+beginWith.m
generated
Normal file
51
Example/Pods/Expecta/Expecta/Matchers/EXPMatchers+beginWith.m
generated
Normal file
@ -0,0 +1,51 @@
|
||||
#import "EXPMatchers+beginWith.h"
|
||||
|
||||
EXPMatcherImplementationBegin(beginWith, (id expected)) {
|
||||
BOOL actualIsNil = (actual == nil);
|
||||
BOOL expectedIsNil = (expected == nil);
|
||||
//This condition allows the comparison of an immutable string or ordered collection to the mutable type of the same
|
||||
BOOL actualAndExpectedAreCompatible = (([actual isKindOfClass:[NSString class]] && [expected isKindOfClass:[NSString class]])
|
||||
|| ([actual isKindOfClass:[NSArray class]] && [expected isKindOfClass:[NSArray class]])
|
||||
|| ([actual isKindOfClass:[NSOrderedSet class]] && [expected isKindOfClass:[NSOrderedSet class]]));
|
||||
|
||||
prerequisite(^BOOL {
|
||||
return actualAndExpectedAreCompatible;
|
||||
});
|
||||
|
||||
match(^BOOL {
|
||||
if ([actual isKindOfClass:[NSString class]]) {
|
||||
return [actual hasPrefix:expected];
|
||||
} else if ([actual isKindOfClass:[NSArray class]]) {
|
||||
if ([expected count] > [actual count] || [expected count] == 0) {
|
||||
return NO;
|
||||
}
|
||||
NSArray *subArray = [actual subarrayWithRange:NSMakeRange(0, [expected count])];
|
||||
return [subArray isEqualToArray:expected];
|
||||
} else {
|
||||
if ([expected count] > [actual count] || [expected count] == 0) {
|
||||
return NO;
|
||||
}
|
||||
|
||||
NSOrderedSet *subset = [NSOrderedSet orderedSetWithOrderedSet:actual range:NSMakeRange(0, [expected count]) copyItems:NO];
|
||||
return [subset isEqualToOrderedSet:expected];
|
||||
}
|
||||
});
|
||||
|
||||
failureMessageForTo(^NSString *{
|
||||
if (actualIsNil) return @"the object is nil/null";
|
||||
if (expectedIsNil) return @"the expected value is nil/null";
|
||||
if (!actualAndExpectedAreCompatible) return [NSString stringWithFormat:@"%@ and %@ are not instances of one of %@, %@, or %@", EXPDescribeObject(actual), EXPDescribeObject(expected), [NSString class], [NSArray class], [NSOrderedSet class]];
|
||||
return [NSString stringWithFormat:@"expected: %@ to begin with %@", EXPDescribeObject(actual), EXPDescribeObject(expected)];
|
||||
});
|
||||
|
||||
failureMessageForNotTo(^NSString *{
|
||||
if (actualIsNil) return @"the object is nil/null";
|
||||
if (expectedIsNil) return @"the expected value is nil/null";
|
||||
if (!actualAndExpectedAreCompatible) return [NSString stringWithFormat:@"%@ and %@ are not instances of one of %@, %@, or %@", EXPDescribeObject(actual), EXPDescribeObject(expected), [NSString class], [NSArray class], [NSOrderedSet class]];
|
||||
|
||||
return [NSString stringWithFormat:@"expected: %@ not to begin with %@", EXPDescribeObject(actual), EXPDescribeObject(expected)];
|
||||
});
|
||||
}
|
||||
EXPMatcherImplementationEnd
|
||||
|
||||
EXPMatcherAliasImplementation(startWith, beginWith, (id expected));
|
||||
3
Example/Pods/Expecta/Expecta/Matchers/EXPMatchers+conformTo.h
generated
Normal file
3
Example/Pods/Expecta/Expecta/Matchers/EXPMatchers+conformTo.h
generated
Normal file
@ -0,0 +1,3 @@
|
||||
#import "Expecta.h"
|
||||
|
||||
EXPMatcherInterface(conformTo, (Protocol *expected));
|
||||
33
Example/Pods/Expecta/Expecta/Matchers/EXPMatchers+conformTo.m
generated
Normal file
33
Example/Pods/Expecta/Expecta/Matchers/EXPMatchers+conformTo.m
generated
Normal file
@ -0,0 +1,33 @@
|
||||
#import "EXPMatchers+conformTo.h"
|
||||
#import "NSValue+Expecta.h"
|
||||
#import <objc/runtime.h>
|
||||
|
||||
EXPMatcherImplementationBegin(conformTo, (Protocol *expected)) {
|
||||
BOOL actualIsNil = (actual == nil);
|
||||
BOOL expectedIsNil = (expected == nil);
|
||||
|
||||
prerequisite(^BOOL{
|
||||
return !(actualIsNil || expectedIsNil);
|
||||
});
|
||||
|
||||
match(^BOOL{
|
||||
return [actual conformsToProtocol:expected];
|
||||
});
|
||||
|
||||
failureMessageForTo(^NSString *{
|
||||
if(actualIsNil) return @"the object is nil/null";
|
||||
if(expectedIsNil) return @"the protocol is nil/null";
|
||||
|
||||
NSString *name = NSStringFromProtocol(expected);
|
||||
return [NSString stringWithFormat:@"expected: %@ to conform to %@", actual, name];
|
||||
});
|
||||
|
||||
failureMessageForNotTo(^NSString *{
|
||||
if(actualIsNil) return @"the object is nil/null";
|
||||
if(expectedIsNil) return @"the protocol is nil/null";
|
||||
|
||||
NSString *name = NSStringFromProtocol(expected);
|
||||
return [NSString stringWithFormat:@"expected: %@ not to conform to %@", actual, name];
|
||||
});
|
||||
}
|
||||
EXPMatcherImplementationEnd
|
||||
5
Example/Pods/Expecta/Expecta/Matchers/EXPMatchers+contain.h
generated
Normal file
5
Example/Pods/Expecta/Expecta/Matchers/EXPMatchers+contain.h
generated
Normal file
@ -0,0 +1,5 @@
|
||||
#import "Expecta.h"
|
||||
|
||||
EXPMatcherInterface(_contain, (id expected));
|
||||
EXPMatcherInterface(contain, (id expected)); // to aid code completion
|
||||
#define contain(expected) _contain(EXPObjectify((expected)))
|
||||
38
Example/Pods/Expecta/Expecta/Matchers/EXPMatchers+contain.m
generated
Normal file
38
Example/Pods/Expecta/Expecta/Matchers/EXPMatchers+contain.m
generated
Normal file
@ -0,0 +1,38 @@
|
||||
#import "EXPMatchers+contain.h"
|
||||
|
||||
EXPMatcherImplementationBegin(_contain, (id expected)) {
|
||||
BOOL actualIsCompatible = [actual isKindOfClass:[NSString class]] || [actual conformsToProtocol:@protocol(NSFastEnumeration)];
|
||||
BOOL expectedIsNil = (expected == nil);
|
||||
|
||||
prerequisite(^BOOL{
|
||||
return actualIsCompatible && !expectedIsNil;
|
||||
});
|
||||
|
||||
match(^BOOL{
|
||||
if(actualIsCompatible) {
|
||||
if([actual isKindOfClass:[NSString class]]) {
|
||||
return [(NSString *)actual rangeOfString:[expected description]].location != NSNotFound;
|
||||
} else {
|
||||
for (id object in actual) {
|
||||
if ([object isEqual:expected]) {
|
||||
return YES;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return NO;
|
||||
});
|
||||
|
||||
failureMessageForTo(^NSString *{
|
||||
if(!actualIsCompatible) return [NSString stringWithFormat:@"%@ is not an instance of NSString or NSFastEnumeration", EXPDescribeObject(actual)];
|
||||
if(expectedIsNil) return @"the expected value is nil/null";
|
||||
return [NSString stringWithFormat:@"expected %@ to contain %@", EXPDescribeObject(actual), EXPDescribeObject(expected)];
|
||||
});
|
||||
|
||||
failureMessageForNotTo(^NSString *{
|
||||
if(!actualIsCompatible) return [NSString stringWithFormat:@"%@ is not an instance of NSString or NSFastEnumeration", EXPDescribeObject(actual)];
|
||||
if(expectedIsNil) return @"the expected value is nil/null";
|
||||
return [NSString stringWithFormat:@"expected %@ not to contain %@", EXPDescribeObject(actual), EXPDescribeObject(expected)];
|
||||
});
|
||||
}
|
||||
EXPMatcherImplementationEnd
|
||||
3
Example/Pods/Expecta/Expecta/Matchers/EXPMatchers+endWith.h
generated
Normal file
3
Example/Pods/Expecta/Expecta/Matchers/EXPMatchers+endWith.h
generated
Normal file
@ -0,0 +1,3 @@
|
||||
#import "Expecta.h"
|
||||
|
||||
EXPMatcherInterface(endWith, (id expected));
|
||||
49
Example/Pods/Expecta/Expecta/Matchers/EXPMatchers+endWith.m
generated
Normal file
49
Example/Pods/Expecta/Expecta/Matchers/EXPMatchers+endWith.m
generated
Normal file
@ -0,0 +1,49 @@
|
||||
#import "EXPMatchers+endWith.h"
|
||||
|
||||
EXPMatcherImplementationBegin(endWith, (id expected)) {
|
||||
BOOL actualIsNil = (actual == nil);
|
||||
BOOL expectedIsNil = (expected == nil);
|
||||
//This condition allows the comparison of an immutable string or ordered collection to the mutable type of the same
|
||||
BOOL actualAndExpectedAreCompatible = (([actual isKindOfClass:[NSString class]] && [expected isKindOfClass:[NSString class]])
|
||||
|| ([actual isKindOfClass:[NSArray class]] && [expected isKindOfClass:[NSArray class]])
|
||||
|| ([actual isKindOfClass:[NSOrderedSet class]] && [expected isKindOfClass:[NSOrderedSet class]]));
|
||||
|
||||
prerequisite(^BOOL {
|
||||
return actualAndExpectedAreCompatible;
|
||||
});
|
||||
|
||||
match(^BOOL {
|
||||
if ([actual isKindOfClass:[NSString class]]) {
|
||||
return [actual hasSuffix:expected];
|
||||
} else if ([actual isKindOfClass:[NSArray class]]) {
|
||||
if ([expected count] > [actual count] || [expected count] == 0) {
|
||||
return NO;
|
||||
}
|
||||
NSArray *subArray = [actual subarrayWithRange:NSMakeRange([actual count] - [expected count], [expected count])];
|
||||
return [subArray isEqualToArray:expected];
|
||||
} else {
|
||||
if ([expected count] > [actual count] || [expected count] == 0) {
|
||||
return NO;
|
||||
}
|
||||
|
||||
NSOrderedSet *subset = [NSOrderedSet orderedSetWithOrderedSet:actual range:NSMakeRange([actual count] - [expected count], [expected count]) copyItems:NO];
|
||||
return [subset isEqualToOrderedSet:expected];
|
||||
}
|
||||
});
|
||||
|
||||
failureMessageForTo(^NSString *{
|
||||
if (actualIsNil) return @"the object is nil/null";
|
||||
if (expectedIsNil) return @"the expected value is nil/null";
|
||||
if (!actualAndExpectedAreCompatible) return [NSString stringWithFormat:@"%@ and %@ are not instances of one of %@, %@, or %@", EXPDescribeObject(actual), EXPDescribeObject(expected), [NSString class], [NSArray class], [NSOrderedSet class]];
|
||||
return [NSString stringWithFormat:@"expected: %@ to end with %@", EXPDescribeObject(actual), EXPDescribeObject(expected)];
|
||||
});
|
||||
|
||||
failureMessageForNotTo(^NSString *{
|
||||
if (actualIsNil) return @"the object is nil/null";
|
||||
if (expectedIsNil) return @"the expected value is nil/null";
|
||||
if (!actualAndExpectedAreCompatible) return [NSString stringWithFormat:@"%@ and %@ are not instances of one of %@, %@, or %@", EXPDescribeObject(actual), EXPDescribeObject(expected), [NSString class], [NSArray class], [NSOrderedSet class]];
|
||||
|
||||
return [NSString stringWithFormat:@"expected: %@ not to end with %@", EXPDescribeObject(actual), EXPDescribeObject(expected)];
|
||||
});
|
||||
}
|
||||
EXPMatcherImplementationEnd
|
||||
5
Example/Pods/Expecta/Expecta/Matchers/EXPMatchers+equal.h
generated
Normal file
5
Example/Pods/Expecta/Expecta/Matchers/EXPMatchers+equal.h
generated
Normal file
@ -0,0 +1,5 @@
|
||||
#import "Expecta.h"
|
||||
|
||||
EXPMatcherInterface(_equal, (id expected));
|
||||
EXPMatcherInterface(equal, (id expected)); // to aid code completion
|
||||
#define equal(...) _equal(EXPObjectify((__VA_ARGS__)))
|
||||
38
Example/Pods/Expecta/Expecta/Matchers/EXPMatchers+equal.m
generated
Normal file
38
Example/Pods/Expecta/Expecta/Matchers/EXPMatchers+equal.m
generated
Normal file
@ -0,0 +1,38 @@
|
||||
#import "EXPMatchers+equal.h"
|
||||
#import "EXPMatcherHelpers.h"
|
||||
|
||||
EXPMatcherImplementationBegin(_equal, (id expected)) {
|
||||
match(^BOOL{
|
||||
if((actual == expected) || [actual isEqual:expected]) {
|
||||
return YES;
|
||||
} else if([actual isKindOfClass:[NSNumber class]] && [expected isKindOfClass:[NSNumber class]]) {
|
||||
if([actual isKindOfClass:[NSDecimalNumber class]] || [expected isKindOfClass:[NSDecimalNumber class]]) {
|
||||
NSDecimalNumber *actualDecimalNumber = [NSDecimalNumber decimalNumberWithDecimal:[(NSNumber *) actual decimalValue]];
|
||||
NSDecimalNumber *expectedDecimalNumber = [NSDecimalNumber decimalNumberWithDecimal:[(NSNumber *) expected decimalValue]];
|
||||
return [actualDecimalNumber isEqualToNumber:expectedDecimalNumber];
|
||||
}
|
||||
else {
|
||||
if(EXPIsNumberFloat((NSNumber *)actual) || EXPIsNumberFloat((NSNumber *)expected)) {
|
||||
return [(NSNumber *)actual floatValue] == [(NSNumber *)expected floatValue];
|
||||
}
|
||||
}
|
||||
}
|
||||
return NO;
|
||||
});
|
||||
|
||||
failureMessageForTo(^NSString *{
|
||||
NSString *expectedDescription = EXPDescribeObject(expected);
|
||||
NSString *actualDescription = EXPDescribeObject(actual);
|
||||
|
||||
if (![expectedDescription isEqualToString:actualDescription]) {
|
||||
return [NSString stringWithFormat:@"expected: %@, got: %@", EXPDescribeObject(expected), EXPDescribeObject(actual)];
|
||||
} else {
|
||||
return [NSString stringWithFormat:@"expected (%@): %@, got (%@): %@", NSStringFromClass([expected class]), EXPDescribeObject(expected), NSStringFromClass([actual class]), EXPDescribeObject(actual)];
|
||||
}
|
||||
});
|
||||
|
||||
failureMessageForNotTo(^NSString *{
|
||||
return [NSString stringWithFormat:@"expected: not %@, got: %@", EXPDescribeObject(expected), EXPDescribeObject(actual)];
|
||||
});
|
||||
}
|
||||
EXPMatcherImplementationEnd
|
||||
10
Example/Pods/Expecta/Expecta/Matchers/EXPMatchers+haveCountOf.h
generated
Normal file
10
Example/Pods/Expecta/Expecta/Matchers/EXPMatchers+haveCountOf.h
generated
Normal file
@ -0,0 +1,10 @@
|
||||
#import "Expecta.h"
|
||||
|
||||
EXPMatcherInterface(haveCountOf, (NSUInteger expected));
|
||||
EXPMatcherInterface(haveCount, (NSUInteger expected));
|
||||
EXPMatcherInterface(haveACountOf, (NSUInteger expected));
|
||||
EXPMatcherInterface(haveLength, (NSUInteger expected));
|
||||
EXPMatcherInterface(haveLengthOf, (NSUInteger expected));
|
||||
EXPMatcherInterface(haveALengthOf, (NSUInteger expected));
|
||||
|
||||
#define beEmpty() haveCountOf(0)
|
||||
42
Example/Pods/Expecta/Expecta/Matchers/EXPMatchers+haveCountOf.m
generated
Normal file
42
Example/Pods/Expecta/Expecta/Matchers/EXPMatchers+haveCountOf.m
generated
Normal file
@ -0,0 +1,42 @@
|
||||
#import "EXPMatchers+haveCountOf.h"
|
||||
|
||||
EXPMatcherImplementationBegin(haveCountOf, (NSUInteger expected)) {
|
||||
BOOL actualIsStringy = [actual isKindOfClass:[NSString class]] || [actual isKindOfClass:[NSAttributedString class]];
|
||||
BOOL actualIsCompatible = actualIsStringy || [actual respondsToSelector:@selector(count)];
|
||||
|
||||
prerequisite(^BOOL{
|
||||
return actualIsCompatible;
|
||||
});
|
||||
|
||||
NSUInteger (^count)(id) = ^(id actual) {
|
||||
if(actualIsStringy) {
|
||||
return [actual length];
|
||||
} else {
|
||||
return [actual count];
|
||||
}
|
||||
};
|
||||
|
||||
match(^BOOL{
|
||||
if(actualIsCompatible) {
|
||||
return count(actual) == expected;
|
||||
}
|
||||
return NO;
|
||||
});
|
||||
|
||||
failureMessageForTo(^NSString *{
|
||||
if(!actualIsCompatible) return [NSString stringWithFormat:@"%@ is not an instance of NSString, NSAttributedString, NSArray, NSSet, NSOrderedSet, or NSDictionary", EXPDescribeObject(actual)];
|
||||
return [NSString stringWithFormat:@"expected %@ to have a count of %zi but got %zi", EXPDescribeObject(actual), expected, count(actual)];
|
||||
});
|
||||
|
||||
failureMessageForNotTo(^NSString *{
|
||||
if(!actualIsCompatible) return [NSString stringWithFormat:@"%@ is not an instance of NSString, NSAttributedString, NSArray, NSSet, NSOrderedSet, or NSDictionary", EXPDescribeObject(actual)];
|
||||
return [NSString stringWithFormat:@"expected %@ not to have a count of %zi", EXPDescribeObject(actual), expected];
|
||||
});
|
||||
}
|
||||
EXPMatcherImplementationEnd
|
||||
|
||||
EXPMatcherAliasImplementation(haveCount, haveCountOf, (NSUInteger expected));
|
||||
EXPMatcherAliasImplementation(haveACountOf, haveCountOf, (NSUInteger expected));
|
||||
EXPMatcherAliasImplementation(haveLength, haveCountOf, (NSUInteger expected));
|
||||
EXPMatcherAliasImplementation(haveLengthOf, haveCountOf, (NSUInteger expected));
|
||||
EXPMatcherAliasImplementation(haveALengthOf, haveCountOf, (NSUInteger expected));
|
||||
3
Example/Pods/Expecta/Expecta/Matchers/EXPMatchers+match.h
generated
Normal file
3
Example/Pods/Expecta/Expecta/Matchers/EXPMatchers+match.h
generated
Normal file
@ -0,0 +1,3 @@
|
||||
#import "Expecta.h"
|
||||
|
||||
EXPMatcherInterface(match, (NSString *expected));
|
||||
38
Example/Pods/Expecta/Expecta/Matchers/EXPMatchers+match.m
generated
Normal file
38
Example/Pods/Expecta/Expecta/Matchers/EXPMatchers+match.m
generated
Normal file
@ -0,0 +1,38 @@
|
||||
#import "EXPMatchers+match.h"
|
||||
#import "EXPMatcherHelpers.h"
|
||||
|
||||
EXPMatcherImplementationBegin(match, (NSString *expected)) {
|
||||
BOOL actualIsNil = (actual == nil);
|
||||
BOOL expectedIsNil = (expected == nil);
|
||||
|
||||
__block NSRegularExpression *regex = nil;
|
||||
__block NSError *regexError = nil;
|
||||
|
||||
prerequisite (^BOOL {
|
||||
BOOL nilInput = (actualIsNil || expectedIsNil);
|
||||
if (!nilInput) {
|
||||
regex = [NSRegularExpression regularExpressionWithPattern:expected options:0 error:®exError];
|
||||
}
|
||||
return !nilInput && regex;
|
||||
});
|
||||
|
||||
match(^BOOL {
|
||||
NSRange range = [regex rangeOfFirstMatchInString:actual options:0 range:NSMakeRange(0, [actual length])];
|
||||
return !NSEqualRanges(range, NSMakeRange(NSNotFound, 0));
|
||||
});
|
||||
|
||||
failureMessageForTo(^NSString *{
|
||||
if (actualIsNil) return @"the object is nil/null";
|
||||
if (expectedIsNil) return @"the expression is nil/null";
|
||||
if (regexError) return [NSString stringWithFormat:@"unable to create regular expression from given parameter: %@", [regexError localizedDescription]];
|
||||
return [NSString stringWithFormat:@"expected: %@ to match to %@", EXPDescribeObject(actual), expected];
|
||||
});
|
||||
|
||||
failureMessageForNotTo(^NSString *{
|
||||
if (actualIsNil) return @"the object is nil/null";
|
||||
if (expectedIsNil) return @"the expression is nil/null";
|
||||
if (regexError) return [NSString stringWithFormat:@"unable to create regular expression from given parameter: %@", [regexError localizedDescription]];
|
||||
return [NSString stringWithFormat:@"expected: %@ not to match to %@", EXPDescribeObject(actual), expected];
|
||||
});
|
||||
}
|
||||
EXPMatcherImplementationEnd
|
||||
4
Example/Pods/Expecta/Expecta/Matchers/EXPMatchers+postNotification.h
generated
Normal file
4
Example/Pods/Expecta/Expecta/Matchers/EXPMatchers+postNotification.h
generated
Normal file
@ -0,0 +1,4 @@
|
||||
#import "Expecta.h"
|
||||
|
||||
EXPMatcherInterface(postNotification, (id expectedNotification));
|
||||
EXPMatcherInterface(notify, (id expectedNotification));
|
||||
88
Example/Pods/Expecta/Expecta/Matchers/EXPMatchers+postNotification.m
generated
Normal file
88
Example/Pods/Expecta/Expecta/Matchers/EXPMatchers+postNotification.m
generated
Normal file
@ -0,0 +1,88 @@
|
||||
#import "EXPMatchers+postNotification.h"
|
||||
|
||||
@implementation NSNotification (EXPEquality)
|
||||
|
||||
- (BOOL)exp_isFunctionallyEqualTo:(NSNotification *)otherNotification
|
||||
{
|
||||
if (![otherNotification isKindOfClass:[NSNotification class]]) return NO;
|
||||
|
||||
BOOL namesMatch = [otherNotification.name isEqualToString:self.name];
|
||||
|
||||
BOOL objectsMatch = YES;
|
||||
if (otherNotification.object || self.object) {
|
||||
objectsMatch = [otherNotification.object isEqual:self.object];
|
||||
}
|
||||
|
||||
BOOL userInfoMatches = YES;
|
||||
if (otherNotification.userInfo || self.userInfo) {
|
||||
userInfoMatches = [otherNotification.userInfo isEqual:self.userInfo];
|
||||
}
|
||||
|
||||
return (namesMatch && objectsMatch && userInfoMatches);
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
EXPMatcherImplementationBegin(postNotification, (id expected)){
|
||||
BOOL actualIsNil = (actual == nil);
|
||||
BOOL expectedIsNil = (expected == nil);
|
||||
BOOL isNotification = [expected isKindOfClass:[NSNotification class]];
|
||||
BOOL isName = [expected isKindOfClass:[NSString class]];
|
||||
|
||||
__block NSString *expectedName;
|
||||
__block BOOL expectedNotificationOccurred = NO;
|
||||
__block id observer;
|
||||
|
||||
prerequisite(^BOOL{
|
||||
expectedNotificationOccurred = NO;
|
||||
if (actualIsNil || expectedIsNil) return NO;
|
||||
if (isNotification) {
|
||||
expectedName = [expected name];
|
||||
}else if(isName) {
|
||||
expectedName = expected;
|
||||
}else{
|
||||
return NO;
|
||||
}
|
||||
|
||||
observer = [[NSNotificationCenter defaultCenter] addObserverForName:expectedName object:nil queue:nil usingBlock:^(NSNotification *note){
|
||||
if (isNotification) {
|
||||
expectedNotificationOccurred |= [expected exp_isFunctionallyEqualTo:note];
|
||||
}else{
|
||||
expectedNotificationOccurred = YES;
|
||||
}
|
||||
}];
|
||||
((EXPBasicBlock)actual)();
|
||||
return YES;
|
||||
});
|
||||
|
||||
match(^BOOL{
|
||||
if(expectedNotificationOccurred) {
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:observer];
|
||||
}
|
||||
return expectedNotificationOccurred;
|
||||
});
|
||||
|
||||
failureMessageForTo(^NSString *{
|
||||
if (observer) {
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:observer];
|
||||
}
|
||||
if(actualIsNil) return @"the actual value is nil/null";
|
||||
if(expectedIsNil) return @"the expected value is nil/null";
|
||||
if(!(isNotification || isName)) return @"the actual value is not a notification or string";
|
||||
return [NSString stringWithFormat:@"expected: %@, got: none",expectedName];
|
||||
});
|
||||
|
||||
failureMessageForNotTo(^NSString *{
|
||||
if (observer) {
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:observer];
|
||||
}
|
||||
if(actualIsNil) return @"the actual value is nil/null";
|
||||
if(expectedIsNil) return @"the expected value is nil/null";
|
||||
if(!(isNotification || isName)) return @"the actual value is not a notification or string";
|
||||
return [NSString stringWithFormat:@"expected: none, got: %@", expectedName];
|
||||
});
|
||||
}
|
||||
|
||||
EXPMatcherImplementationEnd
|
||||
|
||||
EXPMatcherAliasImplementation(notify, postNotification, (id expectedNotification))
|
||||
4
Example/Pods/Expecta/Expecta/Matchers/EXPMatchers+raise.h
generated
Normal file
4
Example/Pods/Expecta/Expecta/Matchers/EXPMatchers+raise.h
generated
Normal file
@ -0,0 +1,4 @@
|
||||
#import "Expecta.h"
|
||||
|
||||
EXPMatcherInterface(raise, (NSString *expectedExceptionName));
|
||||
#define raiseAny() raise(nil)
|
||||
30
Example/Pods/Expecta/Expecta/Matchers/EXPMatchers+raise.m
generated
Normal file
30
Example/Pods/Expecta/Expecta/Matchers/EXPMatchers+raise.m
generated
Normal file
@ -0,0 +1,30 @@
|
||||
#import "EXPMatchers+raise.h"
|
||||
#import "EXPDefines.h"
|
||||
|
||||
EXPMatcherImplementationBegin(raise, (NSString *expectedExceptionName)) {
|
||||
__block NSException *exceptionCaught = nil;
|
||||
|
||||
match(^BOOL{
|
||||
BOOL expectedExceptionCaught = NO;
|
||||
@try {
|
||||
((EXPBasicBlock)actual)();
|
||||
} @catch(NSException *e) {
|
||||
exceptionCaught = e;
|
||||
expectedExceptionCaught = (expectedExceptionName == nil) || [[exceptionCaught name] isEqualToString:expectedExceptionName];
|
||||
}
|
||||
return expectedExceptionCaught;
|
||||
});
|
||||
|
||||
failureMessageForTo(^NSString *{
|
||||
return [NSString stringWithFormat:@"expected: %@, got: %@",
|
||||
expectedExceptionName ? expectedExceptionName : @"any exception",
|
||||
exceptionCaught ? [exceptionCaught name] : @"no exception"];
|
||||
});
|
||||
|
||||
failureMessageForNotTo(^NSString *{
|
||||
return [NSString stringWithFormat:@"expected: %@, got: %@",
|
||||
expectedExceptionName ? [NSString stringWithFormat:@"not %@", expectedExceptionName] : @"no exception",
|
||||
exceptionCaught ? [exceptionCaught name] : @"no exception"];
|
||||
});
|
||||
}
|
||||
EXPMatcherImplementationEnd
|
||||
3
Example/Pods/Expecta/Expecta/Matchers/EXPMatchers+raiseWithReason.h
generated
Normal file
3
Example/Pods/Expecta/Expecta/Matchers/EXPMatchers+raiseWithReason.h
generated
Normal file
@ -0,0 +1,3 @@
|
||||
#import "Expecta.h"
|
||||
|
||||
EXPMatcherInterface(raiseWithReason, (NSString *expectedExceptionName, NSString *expectedReason));
|
||||
35
Example/Pods/Expecta/Expecta/Matchers/EXPMatchers+raiseWithReason.m
generated
Normal file
35
Example/Pods/Expecta/Expecta/Matchers/EXPMatchers+raiseWithReason.m
generated
Normal file
@ -0,0 +1,35 @@
|
||||
#import "EXPMatchers+raiseWithReason.h"
|
||||
#import "EXPDefines.h"
|
||||
|
||||
EXPMatcherImplementationBegin(raiseWithReason, (NSString *expectedExceptionName, NSString *expectedReason)) {
|
||||
__block NSException *exceptionCaught = nil;
|
||||
|
||||
match(^BOOL{
|
||||
BOOL expectedExceptionCaught = NO;
|
||||
@try {
|
||||
((EXPBasicBlock)actual)();
|
||||
} @catch(NSException *e) {
|
||||
exceptionCaught = e;
|
||||
expectedExceptionCaught = (((expectedExceptionName == nil) || [[exceptionCaught name] isEqualToString:expectedExceptionName]) &&
|
||||
((expectedReason == nil) || ([[exceptionCaught reason] isEqualToString:expectedReason])));
|
||||
}
|
||||
return expectedExceptionCaught;
|
||||
});
|
||||
|
||||
failureMessageForTo(^NSString *{
|
||||
return [NSString stringWithFormat:@"expected: %@ (%@), got: %@ (%@)",
|
||||
expectedExceptionName ?: @"any exception",
|
||||
expectedReason ?: @"any reason",
|
||||
exceptionCaught ? [exceptionCaught name] : @"no exception",
|
||||
exceptionCaught ? [exceptionCaught reason] : @""];
|
||||
});
|
||||
|
||||
failureMessageForNotTo(^NSString *{
|
||||
return [NSString stringWithFormat:@"expected: %@ (%@), got: %@ (%@)",
|
||||
expectedExceptionName ? [NSString stringWithFormat:@"not %@", expectedExceptionName] : @"no exception",
|
||||
expectedReason ? [NSString stringWithFormat:@"not '%@'", expectedReason] : @"no reason",
|
||||
exceptionCaught ? [exceptionCaught name] : @"no exception",
|
||||
exceptionCaught ? [exceptionCaught reason] : @"no reason"];
|
||||
});
|
||||
}
|
||||
EXPMatcherImplementationEnd
|
||||
3
Example/Pods/Expecta/Expecta/Matchers/EXPMatchers+respondTo.h
generated
Normal file
3
Example/Pods/Expecta/Expecta/Matchers/EXPMatchers+respondTo.h
generated
Normal file
@ -0,0 +1,3 @@
|
||||
#import "Expecta.h"
|
||||
|
||||
EXPMatcherInterface(respondTo, (SEL expected));
|
||||
28
Example/Pods/Expecta/Expecta/Matchers/EXPMatchers+respondTo.m
generated
Normal file
28
Example/Pods/Expecta/Expecta/Matchers/EXPMatchers+respondTo.m
generated
Normal file
@ -0,0 +1,28 @@
|
||||
#import "EXPMatchers+respondTo.h"
|
||||
#import "EXPMatcherHelpers.h"
|
||||
|
||||
EXPMatcherImplementationBegin(respondTo, (SEL expected)) {
|
||||
BOOL actualIsNil = (actual == nil);
|
||||
BOOL expectedIsNull = (expected == NULL);
|
||||
|
||||
prerequisite (^BOOL {
|
||||
return !(actualIsNil || expectedIsNull);
|
||||
});
|
||||
|
||||
match(^BOOL {
|
||||
return [actual respondsToSelector:expected];
|
||||
});
|
||||
|
||||
failureMessageForTo(^NSString *{
|
||||
if (actualIsNil) return @"the object is nil/null";
|
||||
if (expectedIsNull) return @"the selector is null";
|
||||
return [NSString stringWithFormat:@"expected: %@ to respond to %@", EXPDescribeObject(actual), NSStringFromSelector(expected)];
|
||||
});
|
||||
|
||||
failureMessageForNotTo(^NSString *{
|
||||
if (actualIsNil) return @"the object is nil/null";
|
||||
if (expectedIsNull) return @"the selector is null";
|
||||
return [NSString stringWithFormat:@"expected: %@ not to respond to %@", EXPDescribeObject(actual), NSStringFromSelector(expected)];
|
||||
});
|
||||
}
|
||||
EXPMatcherImplementationEnd
|
||||
25
Example/Pods/Expecta/Expecta/Matchers/EXPMatchers.h
generated
Normal file
25
Example/Pods/Expecta/Expecta/Matchers/EXPMatchers.h
generated
Normal file
@ -0,0 +1,25 @@
|
||||
#import "EXPMatchers+beNil.h"
|
||||
#import "EXPMatchers+equal.h"
|
||||
#import "EXPMatchers+beInstanceOf.h"
|
||||
#import "EXPMatchers+beKindOf.h"
|
||||
#import "EXPMatchers+beSubclassOf.h"
|
||||
#import "EXPMatchers+conformTo.h"
|
||||
#import "EXPMatchers+beTruthy.h"
|
||||
#import "EXPMatchers+beFalsy.h"
|
||||
#import "EXPMatchers+contain.h"
|
||||
#import "EXPMatchers+beSupersetOf.h"
|
||||
#import "EXPMatchers+haveCountOf.h"
|
||||
#import "EXPMatchers+beIdenticalTo.h"
|
||||
#import "EXPMatchers+beGreaterThan.h"
|
||||
#import "EXPMatchers+beGreaterThanOrEqualTo.h"
|
||||
#import "EXPMatchers+beLessThan.h"
|
||||
#import "EXPMatchers+beLessThanOrEqualTo.h"
|
||||
#import "EXPMatchers+beInTheRangeOf.h"
|
||||
#import "EXPMatchers+beCloseTo.h"
|
||||
#import "EXPMatchers+raise.h"
|
||||
#import "EXPMatchers+raiseWithReason.h"
|
||||
#import "EXPMatchers+respondTo.h"
|
||||
#import "EXPMatchers+postNotification.h"
|
||||
#import "EXPMatchers+beginWith.h"
|
||||
#import "EXPMatchers+endWith.h"
|
||||
#import "EXPMatchers+match.h"
|
||||
10
Example/Pods/Expecta/Expecta/NSObject+Expecta.h
generated
Normal file
10
Example/Pods/Expecta/Expecta/NSObject+Expecta.h
generated
Normal file
@ -0,0 +1,10 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@interface NSObject (Expecta)
|
||||
|
||||
- (void)recordFailureWithDescription:(NSString *)description
|
||||
inFile:(NSString *)filename
|
||||
atLine:(NSUInteger)lineNumber
|
||||
expected:(BOOL)expected;
|
||||
|
||||
@end
|
||||
7
Example/Pods/Expecta/Expecta/NSValue+Expecta.h
generated
Normal file
7
Example/Pods/Expecta/Expecta/NSValue+Expecta.h
generated
Normal file
@ -0,0 +1,7 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@interface NSValue (Expecta)
|
||||
|
||||
@property (nonatomic) const char *_EXP_objCType;
|
||||
|
||||
@end
|
||||
21
Example/Pods/Expecta/Expecta/NSValue+Expecta.m
generated
Normal file
21
Example/Pods/Expecta/Expecta/NSValue+Expecta.m
generated
Normal file
@ -0,0 +1,21 @@
|
||||
#import "NSValue+Expecta.h"
|
||||
#import <objc/runtime.h>
|
||||
#import "Expecta.h"
|
||||
|
||||
EXPFixCategoriesBug(NSValue_Expecta);
|
||||
|
||||
@implementation NSValue (Expecta)
|
||||
|
||||
static char _EXP_typeKey;
|
||||
|
||||
- (const char *)_EXP_objCType {
|
||||
return [(NSString *)objc_getAssociatedObject(self, &_EXP_typeKey) cStringUsingEncoding:NSASCIIStringEncoding];
|
||||
}
|
||||
|
||||
- (void)set_EXP_objCType:(const char *)_EXP_objCType {
|
||||
objc_setAssociatedObject(self, &_EXP_typeKey,
|
||||
@(_EXP_objCType),
|
||||
OBJC_ASSOCIATION_COPY_NONATOMIC);
|
||||
}
|
||||
|
||||
@end
|
||||
19
Example/Pods/Expecta/LICENSE
generated
Normal file
19
Example/Pods/Expecta/LICENSE
generated
Normal file
@ -0,0 +1,19 @@
|
||||
Copyright (c) 2011-2015 Specta Team - https://github.com/specta
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
293
Example/Pods/Expecta/README.md
generated
Normal file
293
Example/Pods/Expecta/README.md
generated
Normal file
@ -0,0 +1,293 @@
|
||||
#Expecta
|
||||
|
||||
[](https://travis-ci.org/specta/expecta)
|
||||
[](http://cocoadocs.org/docsets/Expecta/)
|
||||
[](http://cocoadocs.org/docsets/Expecta/)
|
||||
[](https://www.apache.org/licenses/LICENSE-2.0.html)
|
||||
|
||||
A matcher framework for Objective-C and Cocoa.
|
||||
|
||||
## Introduction
|
||||
|
||||
The main advantage of using Expecta over other matcher frameworks is that you do not have to specify the data types. Also, the syntax of Expecta matchers is much more readable and does not suffer from parenthesitis.
|
||||
|
||||
```objective-c
|
||||
expect(@"foo").to.equal(@"foo"); // `to` is a syntactic sugar and can be safely omitted.
|
||||
expect(foo).notTo.equal(1);
|
||||
expect([bar isBar]).to.equal(YES);
|
||||
expect(baz).to.equal(3.14159);
|
||||
```
|
||||
|
||||
Expecta is framework-agnostic: it works well with XCTest and XCTest-compatible test frameworks such as [Specta](http://github.com/petejkim/specta/).
|
||||
|
||||
|
||||
## Installation
|
||||
|
||||
You can setup Expecta using [Carthage](https://github.com/Carthage/Carthage), [CocoaPods](http://github.com/CocoaPods/CocoaPods) or [completely manually](#setting-up-manually).
|
||||
|
||||
### Carthage
|
||||
|
||||
1. Add Expecta to your project's `Cartfile.private`:
|
||||
|
||||
```ruby
|
||||
github "specta/expecta" "master"
|
||||
```
|
||||
|
||||
2. Run `carthage update` in your project directory.
|
||||
3. Drag the appropriate **Expecta.framework** for your platform (located in `Carthage/Build/`) into your application’s Xcode project, and add it to your test target(s).
|
||||
|
||||
### CocoaPods
|
||||
|
||||
1. Add Expecta to your project's `Podfile`:
|
||||
|
||||
```ruby
|
||||
target :MyApp do
|
||||
# Your app's dependencies
|
||||
end
|
||||
|
||||
target :MyAppTests do
|
||||
pod 'Expecta', '~> 1.0.0'
|
||||
end
|
||||
```
|
||||
|
||||
2. Run `pod update` or `pod install` in your project directory.
|
||||
|
||||
### Setting Up Manually
|
||||
|
||||
1. Clone Expecta from Github.
|
||||
2. Run `rake` in your project directory to build the frameworks and libraries.
|
||||
3. Add a Cocoa or Cocoa Touch Unit Testing Bundle target to your Xcode project if you don't already have one.
|
||||
4. For **OS X projects**, copy and add `Expecta.framework` in the `Products/osx` folder to your project's test target.
|
||||
|
||||
For **iOS projects**, copy and add `Expecta.framework` in the `Products/ios` folder to your project's test target.
|
||||
|
||||
You can also use `libExpecta.a` if you prefer to link Expecta as a static library — iOS 7.x and below require this.
|
||||
|
||||
6. Add `-ObjC` and `-all_load` to the **Other Linker Flags** build setting for the test target in your Xcode project.
|
||||
7. You can now use Expecta in your test classes by adding the following import:
|
||||
|
||||
```objective-c
|
||||
@import Expecta; // If you're using Expecta.framework
|
||||
|
||||
// OR
|
||||
|
||||
#import <Expecta/Expecta.h> // If you're using the static library, or the framework
|
||||
```
|
||||
|
||||
## Built-in Matchers
|
||||
|
||||
> `expect(x).to.equal(y);` compares objects or primitives x and y and passes if they are identical (==) or equivalent isEqual:).
|
||||
|
||||
> `expect(x).to.beIdenticalTo(y);` compares objects x and y and passes if they are identical and have the same memory address.
|
||||
|
||||
> `expect(x).to.beNil();` passes if x is nil.
|
||||
|
||||
> `expect(x).to.beTruthy();` passes if x evaluates to true (non-zero).
|
||||
|
||||
> `expect(x).to.beFalsy();` passes if x evaluates to false (zero).
|
||||
|
||||
> `expect(x).to.contain(y);` passes if an instance of NSArray or NSString x contains y.
|
||||
|
||||
> `expect(x).to.beSupersetOf(y);` passes if an instance of NSArray, NSSet, NSDictionary or NSOrderedSet x contains all elements of y.
|
||||
|
||||
> `expect(x).to.haveCountOf(y);` passes if an instance of NSArray, NSSet, NSDictionary or NSString x has a count or length of y.
|
||||
|
||||
> `expect(x).to.beEmpty();` passes if an instance of NSArray, NSSet, NSDictionary or NSString x has a count or length of .
|
||||
|
||||
> `expect(x).to.beInstanceOf([Foo class]);` passes if x is an instance of a class Foo.
|
||||
|
||||
> `expect(x).to.beKindOf([Foo class]);` passes if x is an instance of a class Foo or if x is an instance of any class that inherits from the class Foo.
|
||||
|
||||
> `expect([Foo class]).to.beSubclassOf([Bar class]);` passes if the class Foo is a subclass of the class Bar or if it is identical to the class Bar. Use beKindOf() for class clusters.
|
||||
|
||||
> `expect(x).to.beLessThan(y);` passes if `x` is less than `y`.
|
||||
|
||||
> `expect(x).to.beLessThanOrEqualTo(y);` passes if `x` is less than or equal to `y`.
|
||||
|
||||
> `expect(x).to.beGreaterThan(y);` passes if `x` is greater than `y`.
|
||||
|
||||
> `expect(x).to.beGreaterThanOrEqualTo(y);` passes if `x` is greater than or equal to `y`.
|
||||
|
||||
> `expect(x).to.beInTheRangeOf(y,z);` passes if `x` is in the range of `y` and `z`.
|
||||
|
||||
> `expect(x).to.beCloseTo(y);` passes if `x` is close to `y`.
|
||||
|
||||
> `expect(x).to.beCloseToWithin(y, z);` passes if `x` is close to `y` within `z`.
|
||||
|
||||
> `expect(^{ /* code */ }).to.raise(@"ExceptionName");` passes if a given block of code raises an exception named `ExceptionName`.
|
||||
|
||||
> `expect(^{ /* code */ }).to.raiseAny();` passes if a given block of code raises any exception.
|
||||
|
||||
> `expect(x).to.conformTo(y);` passes if `x` conforms to the protocol `y`.
|
||||
|
||||
> `expect(x).to.respondTo(y);` passes if `x` responds to the selector `y`.
|
||||
|
||||
> `expect(^{ /* code */ }).to.notify(@"NotificationName");` passes if a given block of code generates an NSNotification amed `NotificationName`.
|
||||
|
||||
> `expect(^{ /* code */ }).to.notify(notification);` passes if a given block of code generates an NSNotification equal to the passed `notification`.
|
||||
|
||||
> `expect(x).to.beginWith(y);` passes if an instance of NSString, NSArray, or NSOrderedSet `x` begins with `y`. Also liased by `startWith`
|
||||
|
||||
> `expect(x).to.endWith(y);` passes if an instance of NSString, NSArray, or NSOrderedSet `x` ends with `y`.
|
||||
|
||||
> `expect(x).to.match(y);` passes if an instance of NSString `x` matches regular expression (given as NSString) `y` one or more times.
|
||||
|
||||
## Inverting Matchers
|
||||
|
||||
Every matcher's criteria can be inverted by prepending `.notTo` or `.toNot`:
|
||||
|
||||
>`expect(x).notTo.equal(y);` compares objects or primitives x and y and passes if they are *not* equivalent.
|
||||
|
||||
## Asynchronous Testing
|
||||
|
||||
Every matcher can be made to perform asynchronous testing by prepending `.will`, `.willNot` or `after(...)`:
|
||||
|
||||
>`expect(x).will.beNil();` passes if x becomes nil before the default timeout.
|
||||
>
|
||||
>`expect(x).willNot.beNil();` passes if x becomes non-nil before the default timeout.
|
||||
>
|
||||
>`expect(x).after(3).to.beNil();` passes if x becoms nil after 3.0 seconds.
|
||||
>
|
||||
>`expect(x).after(2.5).notTo.equal(42);` passes if x doesn't equal 42 after 2.5 seconds.
|
||||
|
||||
The default timeout is 1.0 second and is used for all matchers if not otherwise specified. This setting can be changed by calling `[Expecta setAsynchronousTestTimeout:x]`, where `x` is the desired timeout in seconds.
|
||||
|
||||
```objective-c
|
||||
describe(@"Foo", ^{
|
||||
beforeAll(^{
|
||||
// All asynchronous matching using `will` and `willNot`
|
||||
// will have a timeout of 2.0 seconds
|
||||
[Expecta setAsynchronousTestTimeout:2];
|
||||
});
|
||||
|
||||
it(@"will not be nil", ^{
|
||||
// Test case where default timeout is used
|
||||
expect(foo).willNot.beNil();
|
||||
});
|
||||
|
||||
it(@"should equal 42 after 3 seconds", ^{
|
||||
// Signle case where timeout differs from the default
|
||||
expect(foo).after(3).to.equal(42);
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
## Forced Failing
|
||||
|
||||
You can fail a test by using the `failure` attribute. This can be used to test branching.
|
||||
|
||||
> `failure(@"This should not happen");` outright fails a test.
|
||||
|
||||
|
||||
## Writing New Matchers
|
||||
|
||||
Writing a new matcher is easy with special macros provided by Expecta. Take a look at how `.beKindOf()` matcher is defined:
|
||||
|
||||
`EXPMatchers+beKindOf.h`
|
||||
|
||||
```objective-c
|
||||
#import "Expecta.h"
|
||||
|
||||
EXPMatcherInterface(beKindOf, (Class expected));
|
||||
// 1st argument is the name of the matcher function
|
||||
// 2nd argument is the list of arguments that may be passed in the function
|
||||
// call.
|
||||
// Multiple arguments are fine. (e.g. (int foo, float bar))
|
||||
|
||||
#define beAKindOf beKindOf
|
||||
```
|
||||
|
||||
`EXPMatchers+beKindOf.m`
|
||||
|
||||
```objective-c
|
||||
#import "EXPMatchers+beKindOf.h"
|
||||
|
||||
EXPMatcherImplementationBegin(beKindOf, (Class expected)) {
|
||||
BOOL actualIsNil = (actual == nil);
|
||||
BOOL expectedIsNil = (expected == nil);
|
||||
|
||||
prerequisite(^BOOL {
|
||||
return !(actualIsNil || expectedIsNil);
|
||||
// Return `NO` if matcher should fail whether or not the result is inverted
|
||||
// using `.Not`.
|
||||
});
|
||||
|
||||
match(^BOOL {
|
||||
return [actual isKindOfClass:expected];
|
||||
// Return `YES` if the matcher should pass, `NO` if it should not.
|
||||
// The actual value/object is passed as `actual`.
|
||||
// Please note that primitive values will be wrapped in NSNumber/NSValue.
|
||||
});
|
||||
|
||||
failureMessageForTo(^NSString * {
|
||||
if (actualIsNil)
|
||||
return @"the actual value is nil/null";
|
||||
if (expectedIsNil)
|
||||
return @"the expected value is nil/null";
|
||||
return [NSString
|
||||
stringWithFormat:@"expected: a kind of %@, "
|
||||
"got: an instance of %@, which is not a kind of %@",
|
||||
[expected class], [actual class], [expected class]];
|
||||
// Return the message to be displayed when the match function returns `YES`.
|
||||
});
|
||||
|
||||
failureMessageForNotTo(^NSString * {
|
||||
if (actualIsNil)
|
||||
return @"the actual value is nil/null";
|
||||
if (expectedIsNil)
|
||||
return @"the expected value is nil/null";
|
||||
return [NSString
|
||||
stringWithFormat:@"expected: not a kind of %@, "
|
||||
"got: an instance of %@, which is a kind of %@",
|
||||
[expected class], [actual class], [expected class]];
|
||||
// Return the message to be displayed when the match function returns `NO`.
|
||||
});
|
||||
}
|
||||
EXPMatcherImplementationEnd
|
||||
```
|
||||
|
||||
## Dynamic Predicate Matchers
|
||||
|
||||
It is possible to add predicate matchers by simply defining the matcher interface, with the matcher implementation being handled at runtime by delegating to the predicate method on your object.
|
||||
|
||||
For instance, if you have the following class:
|
||||
|
||||
```objc
|
||||
@interface LightSwitch : NSObject
|
||||
@property (nonatomic, assign, getter=isTurnedOn) BOOL turnedOn;
|
||||
@end
|
||||
|
||||
@implementation LightSwitch
|
||||
@synthesize turnedOn;
|
||||
@end
|
||||
```
|
||||
|
||||
The normal way to write an assertion that the switch is turned on would be:
|
||||
|
||||
```objc
|
||||
expect([lightSwitch isTurnedOn]).to.beTruthy();
|
||||
```
|
||||
|
||||
However, if we define a custom predicate matcher:
|
||||
|
||||
```objc
|
||||
EXPMatcherInterface(isTurnedOn, (void));
|
||||
```
|
||||
|
||||
(Note: we haven't defined the matcher implementation, just it's interface)
|
||||
|
||||
You can now write your assertion as follows:
|
||||
|
||||
```objc
|
||||
expect(lightSwitch).isTurnedOn();
|
||||
```
|
||||
|
||||
## Contribution Guidelines
|
||||
|
||||
* Please use only spaces and indent 2 spaces at a time.
|
||||
* Please prefix instance variable names with a single underscore (`_`).
|
||||
* Please prefix custom classes and functions defined in the global scope with `EXP`.
|
||||
|
||||
## License
|
||||
|
||||
Copyright (c) 2012-2015 [Specta Team](https://github.com/specta?tab=members). This software is licensed under the [MIT License](http://github.com/specta/specta/raw/master/LICENSE).
|
||||
15
Example/Pods/FBSnapshotTestCase/FBSnapshotTestCase.modulemap
generated
Normal file
15
Example/Pods/FBSnapshotTestCase/FBSnapshotTestCase.modulemap
generated
Normal file
@ -0,0 +1,15 @@
|
||||
framework module FBSnapshotTestCase {
|
||||
umbrella header "FBSnapshotTestCase.h"
|
||||
|
||||
export *
|
||||
module * { export * }
|
||||
|
||||
header "FBSnapshotTestCase.h"
|
||||
header "FBSnapshotTestCasePlatform.h"
|
||||
header "FBSnapshotTestController.h"
|
||||
|
||||
private header "UIImage+Compare.h"
|
||||
private header "UIImage+Diff.h"
|
||||
private header "UIImage+Snapshot.h"
|
||||
}
|
||||
|
||||
37
Example/Pods/FBSnapshotTestCase/FBSnapshotTestCase/Categories/UIImage+Compare.h
generated
Normal file
37
Example/Pods/FBSnapshotTestCase/FBSnapshotTestCase/Categories/UIImage+Compare.h
generated
Normal file
@ -0,0 +1,37 @@
|
||||
//
|
||||
// Created by Gabriel Handford on 3/1/09.
|
||||
// Copyright 2009-2013. All rights reserved.
|
||||
// Created by John Boiles on 10/20/11.
|
||||
// Copyright (c) 2011. All rights reserved
|
||||
// Modified by Felix Schulze on 2/11/13.
|
||||
// Copyright 2013. All rights reserved.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person
|
||||
// obtaining a copy of this software and associated documentation
|
||||
// files (the "Software"), to deal in the Software without
|
||||
// restriction, including without limitation the rights to use,
|
||||
// copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the
|
||||
// Software is furnished to do so, subject to the following
|
||||
// conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be
|
||||
// included in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
// OTHER DEALINGS IN THE SOFTWARE.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
@interface UIImage (Compare)
|
||||
|
||||
- (BOOL)fb_compareWithImage:(UIImage *)image tolerance:(CGFloat)tolerance;
|
||||
|
||||
@end
|
||||
134
Example/Pods/FBSnapshotTestCase/FBSnapshotTestCase/Categories/UIImage+Compare.m
generated
Normal file
134
Example/Pods/FBSnapshotTestCase/FBSnapshotTestCase/Categories/UIImage+Compare.m
generated
Normal file
@ -0,0 +1,134 @@
|
||||
//
|
||||
// Created by Gabriel Handford on 3/1/09.
|
||||
// Copyright 2009-2013. All rights reserved.
|
||||
// Created by John Boiles on 10/20/11.
|
||||
// Copyright (c) 2011. All rights reserved
|
||||
// Modified by Felix Schulze on 2/11/13.
|
||||
// Copyright 2013. All rights reserved.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person
|
||||
// obtaining a copy of this software and associated documentation
|
||||
// files (the "Software"), to deal in the Software without
|
||||
// restriction, including without limitation the rights to use,
|
||||
// copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the
|
||||
// Software is furnished to do so, subject to the following
|
||||
// conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be
|
||||
// included in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
// OTHER DEALINGS IN THE SOFTWARE.
|
||||
//
|
||||
|
||||
#import <FBSnapshotTestCase/UIImage+Compare.h>
|
||||
|
||||
// This makes debugging much more fun
|
||||
typedef union {
|
||||
uint32_t raw;
|
||||
unsigned char bytes[4];
|
||||
struct {
|
||||
char red;
|
||||
char green;
|
||||
char blue;
|
||||
char alpha;
|
||||
} __attribute__ ((packed)) pixels;
|
||||
} FBComparePixel;
|
||||
|
||||
@implementation UIImage (Compare)
|
||||
|
||||
- (BOOL)fb_compareWithImage:(UIImage *)image tolerance:(CGFloat)tolerance
|
||||
{
|
||||
NSAssert(CGSizeEqualToSize(self.size, image.size), @"Images must be same size.");
|
||||
|
||||
CGSize referenceImageSize = CGSizeMake(CGImageGetWidth(self.CGImage), CGImageGetHeight(self.CGImage));
|
||||
CGSize imageSize = CGSizeMake(CGImageGetWidth(image.CGImage), CGImageGetHeight(image.CGImage));
|
||||
|
||||
// The images have the equal size, so we could use the smallest amount of bytes because of byte padding
|
||||
size_t minBytesPerRow = MIN(CGImageGetBytesPerRow(self.CGImage), CGImageGetBytesPerRow(image.CGImage));
|
||||
size_t referenceImageSizeBytes = referenceImageSize.height * minBytesPerRow;
|
||||
void *referenceImagePixels = calloc(1, referenceImageSizeBytes);
|
||||
void *imagePixels = calloc(1, referenceImageSizeBytes);
|
||||
|
||||
if (!referenceImagePixels || !imagePixels) {
|
||||
free(referenceImagePixels);
|
||||
free(imagePixels);
|
||||
return NO;
|
||||
}
|
||||
|
||||
CGContextRef referenceImageContext = CGBitmapContextCreate(referenceImagePixels,
|
||||
referenceImageSize.width,
|
||||
referenceImageSize.height,
|
||||
CGImageGetBitsPerComponent(self.CGImage),
|
||||
minBytesPerRow,
|
||||
CGImageGetColorSpace(self.CGImage),
|
||||
(CGBitmapInfo)kCGImageAlphaPremultipliedLast
|
||||
);
|
||||
CGContextRef imageContext = CGBitmapContextCreate(imagePixels,
|
||||
imageSize.width,
|
||||
imageSize.height,
|
||||
CGImageGetBitsPerComponent(image.CGImage),
|
||||
minBytesPerRow,
|
||||
CGImageGetColorSpace(image.CGImage),
|
||||
(CGBitmapInfo)kCGImageAlphaPremultipliedLast
|
||||
);
|
||||
|
||||
if (!referenceImageContext || !imageContext) {
|
||||
CGContextRelease(referenceImageContext);
|
||||
CGContextRelease(imageContext);
|
||||
free(referenceImagePixels);
|
||||
free(imagePixels);
|
||||
return NO;
|
||||
}
|
||||
|
||||
CGContextDrawImage(referenceImageContext, CGRectMake(0, 0, referenceImageSize.width, referenceImageSize.height), self.CGImage);
|
||||
CGContextDrawImage(imageContext, CGRectMake(0, 0, imageSize.width, imageSize.height), image.CGImage);
|
||||
|
||||
CGContextRelease(referenceImageContext);
|
||||
CGContextRelease(imageContext);
|
||||
|
||||
BOOL imageEqual = YES;
|
||||
|
||||
// Do a fast compare if we can
|
||||
if (tolerance == 0) {
|
||||
imageEqual = (memcmp(referenceImagePixels, imagePixels, referenceImageSizeBytes) == 0);
|
||||
} else {
|
||||
// Go through each pixel in turn and see if it is different
|
||||
const NSInteger pixelCount = referenceImageSize.width * referenceImageSize.height;
|
||||
|
||||
FBComparePixel *p1 = referenceImagePixels;
|
||||
FBComparePixel *p2 = imagePixels;
|
||||
|
||||
NSInteger numDiffPixels = 0;
|
||||
for (int n = 0; n < pixelCount; ++n) {
|
||||
// If this pixel is different, increment the pixel diff count and see
|
||||
// if we have hit our limit.
|
||||
if (p1->raw != p2->raw) {
|
||||
numDiffPixels ++;
|
||||
|
||||
CGFloat percent = (CGFloat)numDiffPixels / pixelCount;
|
||||
if (percent > tolerance) {
|
||||
imageEqual = NO;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
p1++;
|
||||
p2++;
|
||||
}
|
||||
}
|
||||
|
||||
free(referenceImagePixels);
|
||||
free(imagePixels);
|
||||
|
||||
return imageEqual;
|
||||
}
|
||||
|
||||
@end
|
||||
37
Example/Pods/FBSnapshotTestCase/FBSnapshotTestCase/Categories/UIImage+Diff.h
generated
Normal file
37
Example/Pods/FBSnapshotTestCase/FBSnapshotTestCase/Categories/UIImage+Diff.h
generated
Normal file
@ -0,0 +1,37 @@
|
||||
//
|
||||
// Created by Gabriel Handford on 3/1/09.
|
||||
// Copyright 2009-2013. All rights reserved.
|
||||
// Created by John Boiles on 10/20/11.
|
||||
// Copyright (c) 2011. All rights reserved
|
||||
// Modified by Felix Schulze on 2/11/13.
|
||||
// Copyright 2013. All rights reserved.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person
|
||||
// obtaining a copy of this software and associated documentation
|
||||
// files (the "Software"), to deal in the Software without
|
||||
// restriction, including without limitation the rights to use,
|
||||
// copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the
|
||||
// Software is furnished to do so, subject to the following
|
||||
// conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be
|
||||
// included in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
// OTHER DEALINGS IN THE SOFTWARE.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
@interface UIImage (Diff)
|
||||
|
||||
- (UIImage *)fb_diffWithImage:(UIImage *)image;
|
||||
|
||||
@end
|
||||
56
Example/Pods/FBSnapshotTestCase/FBSnapshotTestCase/Categories/UIImage+Diff.m
generated
Normal file
56
Example/Pods/FBSnapshotTestCase/FBSnapshotTestCase/Categories/UIImage+Diff.m
generated
Normal file
@ -0,0 +1,56 @@
|
||||
//
|
||||
// Created by Gabriel Handford on 3/1/09.
|
||||
// Copyright 2009-2013. All rights reserved.
|
||||
// Created by John Boiles on 10/20/11.
|
||||
// Copyright (c) 2011. All rights reserved
|
||||
// Modified by Felix Schulze on 2/11/13.
|
||||
// Copyright 2013. All rights reserved.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person
|
||||
// obtaining a copy of this software and associated documentation
|
||||
// files (the "Software"), to deal in the Software without
|
||||
// restriction, including without limitation the rights to use,
|
||||
// copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the
|
||||
// Software is furnished to do so, subject to the following
|
||||
// conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be
|
||||
// included in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
// OTHER DEALINGS IN THE SOFTWARE.
|
||||
//
|
||||
|
||||
#import <FBSnapshotTestCase/UIImage+Diff.h>
|
||||
|
||||
@implementation UIImage (Diff)
|
||||
|
||||
- (UIImage *)fb_diffWithImage:(UIImage *)image
|
||||
{
|
||||
if (!image) {
|
||||
return nil;
|
||||
}
|
||||
CGSize imageSize = CGSizeMake(MAX(self.size.width, image.size.width), MAX(self.size.height, image.size.height));
|
||||
UIGraphicsBeginImageContextWithOptions(imageSize, YES, 0);
|
||||
CGContextRef context = UIGraphicsGetCurrentContext();
|
||||
[self drawInRect:CGRectMake(0, 0, self.size.width, self.size.height)];
|
||||
CGContextSetAlpha(context, 0.5);
|
||||
CGContextBeginTransparencyLayer(context, NULL);
|
||||
[image drawInRect:CGRectMake(0, 0, image.size.width, image.size.height)];
|
||||
CGContextSetBlendMode(context, kCGBlendModeDifference);
|
||||
CGContextSetFillColorWithColor(context,[UIColor whiteColor].CGColor);
|
||||
CGContextFillRect(context, CGRectMake(0, 0, self.size.width, self.size.height));
|
||||
CGContextEndTransparencyLayer(context);
|
||||
UIImage *returnImage = UIGraphicsGetImageFromCurrentImageContext();
|
||||
UIGraphicsEndImageContext();
|
||||
return returnImage;
|
||||
}
|
||||
|
||||
@end
|
||||
24
Example/Pods/FBSnapshotTestCase/FBSnapshotTestCase/Categories/UIImage+Snapshot.h
generated
Normal file
24
Example/Pods/FBSnapshotTestCase/FBSnapshotTestCase/Categories/UIImage+Snapshot.h
generated
Normal file
@ -0,0 +1,24 @@
|
||||
/*
|
||||
* Copyright (c) 2015, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*
|
||||
*/
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
@interface UIImage (Snapshot)
|
||||
|
||||
/// Uses renderInContext: to get a snapshot of the layer.
|
||||
+ (UIImage *)fb_imageForLayer:(CALayer *)layer;
|
||||
|
||||
/// Uses renderInContext: to get a snapshot of the view layer.
|
||||
+ (UIImage *)fb_imageForViewLayer:(UIView *)view;
|
||||
|
||||
/// Uses drawViewHierarchyInRect: to get a snapshot of the view and adds the view into a window if needed.
|
||||
+ (UIImage *)fb_imageForView:(UIView *)view;
|
||||
|
||||
@end
|
||||
62
Example/Pods/FBSnapshotTestCase/FBSnapshotTestCase/Categories/UIImage+Snapshot.m
generated
Normal file
62
Example/Pods/FBSnapshotTestCase/FBSnapshotTestCase/Categories/UIImage+Snapshot.m
generated
Normal file
@ -0,0 +1,62 @@
|
||||
/*
|
||||
* Copyright (c) 2015, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*
|
||||
*/
|
||||
|
||||
#import <FBSnapshotTestCase/UIImage+Snapshot.h>
|
||||
|
||||
@implementation UIImage (Snapshot)
|
||||
|
||||
+ (UIImage *)fb_imageForLayer:(CALayer *)layer
|
||||
{
|
||||
CGRect bounds = layer.bounds;
|
||||
NSAssert1(CGRectGetWidth(bounds), @"Zero width for layer %@", layer);
|
||||
NSAssert1(CGRectGetHeight(bounds), @"Zero height for layer %@", layer);
|
||||
|
||||
UIGraphicsBeginImageContextWithOptions(bounds.size, NO, 0);
|
||||
CGContextRef context = UIGraphicsGetCurrentContext();
|
||||
NSAssert1(context, @"Could not generate context for layer %@", layer);
|
||||
CGContextSaveGState(context);
|
||||
[layer layoutIfNeeded];
|
||||
[layer renderInContext:context];
|
||||
CGContextRestoreGState(context);
|
||||
|
||||
UIImage *snapshot = UIGraphicsGetImageFromCurrentImageContext();
|
||||
UIGraphicsEndImageContext();
|
||||
return snapshot;
|
||||
}
|
||||
|
||||
+ (UIImage *)fb_imageForViewLayer:(UIView *)view
|
||||
{
|
||||
[view layoutIfNeeded];
|
||||
return [self fb_imageForLayer:view.layer];
|
||||
}
|
||||
|
||||
+ (UIImage *)fb_imageForView:(UIView *)view
|
||||
{
|
||||
CGRect bounds = view.bounds;
|
||||
NSAssert1(CGRectGetWidth(bounds), @"Zero width for view %@", view);
|
||||
NSAssert1(CGRectGetHeight(bounds), @"Zero height for view %@", view);
|
||||
|
||||
UIWindow *window = view.window;
|
||||
if (window == nil) {
|
||||
window = [[UIWindow alloc] initWithFrame:bounds];
|
||||
[window addSubview:view];
|
||||
[window makeKeyAndVisible];
|
||||
}
|
||||
|
||||
UIGraphicsBeginImageContextWithOptions(bounds.size, NO, 0);
|
||||
[view layoutIfNeeded];
|
||||
[view drawViewHierarchyInRect:view.bounds afterScreenUpdates:YES];
|
||||
|
||||
UIImage *snapshot = UIGraphicsGetImageFromCurrentImageContext();
|
||||
UIGraphicsEndImageContext();
|
||||
return snapshot;
|
||||
}
|
||||
|
||||
@end
|
||||
200
Example/Pods/FBSnapshotTestCase/FBSnapshotTestCase/FBSnapshotTestCase.h
generated
Normal file
200
Example/Pods/FBSnapshotTestCase/FBSnapshotTestCase/FBSnapshotTestCase.h
generated
Normal file
@ -0,0 +1,200 @@
|
||||
/*
|
||||
* Copyright (c) 2015, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*
|
||||
*/
|
||||
|
||||
#import <FBSnapshotTestCase/FBSnapshotTestCasePlatform.h>
|
||||
|
||||
#import <QuartzCore/QuartzCore.h>
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
#import <XCTest/XCTest.h>
|
||||
|
||||
/*
|
||||
There are three ways of setting reference image directories.
|
||||
|
||||
1. Set the preprocessor macro FB_REFERENCE_IMAGE_DIR to a double quoted
|
||||
c-string with the path.
|
||||
2. Set an environment variable named FB_REFERENCE_IMAGE_DIR with the path. This
|
||||
takes precedence over the preprocessor macro to allow for run-time override.
|
||||
3. Keep everything unset, which will cause the reference images to be looked up
|
||||
inside the bundle holding the current test, in the
|
||||
Resources/ReferenceImages_* directories.
|
||||
*/
|
||||
#ifndef FB_REFERENCE_IMAGE_DIR
|
||||
#define FB_REFERENCE_IMAGE_DIR ""
|
||||
#endif
|
||||
|
||||
/**
|
||||
Similar to our much-loved XCTAssert() macros. Use this to perform your test. No need to write an explanation, though.
|
||||
@param view The view to snapshot
|
||||
@param identifier An optional identifier, used if there are multiple snapshot tests in a given -test method.
|
||||
@param suffixes An NSOrderedSet of strings for the different suffixes
|
||||
@param tolerance The percentage of pixels that can differ and still count as an 'identical' view
|
||||
*/
|
||||
#define FBSnapshotVerifyViewWithOptions(view__, identifier__, suffixes__, tolerance__) \
|
||||
FBSnapshotVerifyViewOrLayerWithOptions(View, view__, identifier__, suffixes__, tolerance__)
|
||||
|
||||
#define FBSnapshotVerifyView(view__, identifier__) \
|
||||
FBSnapshotVerifyViewWithOptions(view__, identifier__, FBSnapshotTestCaseDefaultSuffixes(), 0)
|
||||
|
||||
|
||||
/**
|
||||
Similar to our much-loved XCTAssert() macros. Use this to perform your test. No need to write an explanation, though.
|
||||
@param layer The layer to snapshot
|
||||
@param identifier An optional identifier, used is there are multiple snapshot tests in a given -test method.
|
||||
@param suffixes An NSOrderedSet of strings for the different suffixes
|
||||
@param tolerance The percentage of pixels that can differ and still count as an 'identical' layer
|
||||
*/
|
||||
#define FBSnapshotVerifyLayerWithOptions(layer__, identifier__, suffixes__, tolerance__) \
|
||||
FBSnapshotVerifyViewOrLayerWithOptions(Layer, layer__, identifier__, suffixes__, tolerance__)
|
||||
|
||||
#define FBSnapshotVerifyLayer(layer__, identifier__) \
|
||||
FBSnapshotVerifyLayerWithOptions(layer__, identifier__, FBSnapshotTestCaseDefaultSuffixes(), 0)
|
||||
|
||||
|
||||
#define FBSnapshotVerifyViewOrLayerWithOptions(what__, viewOrLayer__, identifier__, suffixes__, tolerance__) \
|
||||
{ \
|
||||
NSString *referenceImageDirectory = [self getReferenceImageDirectoryWithDefault:(@ FB_REFERENCE_IMAGE_DIR)]; \
|
||||
XCTAssertNotNil(referenceImageDirectory, @"Missing value for referenceImagesDirectory - Set FB_REFERENCE_IMAGE_DIR as Environment variable in your scheme.");\
|
||||
XCTAssertTrue((suffixes__.count > 0), @"Suffixes set cannot be empty %@", suffixes__); \
|
||||
\
|
||||
BOOL testSuccess__ = NO; \
|
||||
NSError *error__ = nil; \
|
||||
NSMutableArray *errors__ = [NSMutableArray array]; \
|
||||
\
|
||||
if (self.recordMode) { \
|
||||
\
|
||||
NSString *referenceImagesDirectory__ = [NSString stringWithFormat:@"%@%@", referenceImageDirectory, suffixes__.firstObject]; \
|
||||
BOOL referenceImageSaved__ = [self compareSnapshotOf ## what__ :(viewOrLayer__) referenceImagesDirectory:referenceImagesDirectory__ identifier:(identifier__) tolerance:(tolerance__) error:&error__]; \
|
||||
if (!referenceImageSaved__) { \
|
||||
[errors__ addObject:error__]; \
|
||||
} \
|
||||
} else { \
|
||||
\
|
||||
for (NSString *suffix__ in suffixes__) { \
|
||||
NSString *referenceImagesDirectory__ = [NSString stringWithFormat:@"%@%@", referenceImageDirectory, suffix__]; \
|
||||
BOOL referenceImageAvailable = [self referenceImageRecordedInDirectory:referenceImagesDirectory__ identifier:(identifier__) error:&error__]; \
|
||||
\
|
||||
if (referenceImageAvailable) { \
|
||||
BOOL comparisonSuccess__ = [self compareSnapshotOf ## what__ :(viewOrLayer__) referenceImagesDirectory:referenceImagesDirectory__ identifier:(identifier__) tolerance:(tolerance__) error:&error__]; \
|
||||
[errors__ removeAllObjects]; \
|
||||
if (comparisonSuccess__) { \
|
||||
testSuccess__ = YES; \
|
||||
break; \
|
||||
} else { \
|
||||
[errors__ addObject:error__]; \
|
||||
} \
|
||||
} else { \
|
||||
[errors__ addObject:error__]; \
|
||||
} \
|
||||
} \
|
||||
} \
|
||||
XCTAssertTrue(testSuccess__, @"Snapshot comparison failed: %@", errors__.firstObject); \
|
||||
XCTAssertFalse(self.recordMode, @"Test ran in record mode. Reference image is now saved. Disable record mode to perform an actual snapshot comparison!"); \
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
The base class of view snapshotting tests. If you have small UI component, it's often easier to configure it in a test
|
||||
and compare an image of the view to a reference image that write lots of complex layout-code tests.
|
||||
|
||||
In order to flip the tests in your subclass to record the reference images set @c recordMode to @c YES.
|
||||
|
||||
@attention When recording, the reference image directory should be explicitly
|
||||
set, otherwise the images may be written to somewhere inside the
|
||||
simulator directory.
|
||||
|
||||
For example:
|
||||
@code
|
||||
- (void)setUp
|
||||
{
|
||||
[super setUp];
|
||||
self.recordMode = YES;
|
||||
}
|
||||
@endcode
|
||||
*/
|
||||
@interface FBSnapshotTestCase : XCTestCase
|
||||
|
||||
/**
|
||||
When YES, the test macros will save reference images, rather than performing an actual test.
|
||||
*/
|
||||
@property (readwrite, nonatomic, assign) BOOL recordMode;
|
||||
|
||||
/**
|
||||
When @c YES appends the name of the device model and OS to the snapshot file name.
|
||||
The default value is @c NO.
|
||||
*/
|
||||
@property (readwrite, nonatomic, assign, getter=isDeviceAgnostic) BOOL deviceAgnostic;
|
||||
|
||||
/**
|
||||
When YES, renders a snapshot of the complete view hierarchy as visible onscreen.
|
||||
There are several things that do not work if renderInContext: is used.
|
||||
- UIVisualEffect #70
|
||||
- UIAppearance #91
|
||||
- Size Classes #92
|
||||
|
||||
@attention If the view does't belong to a UIWindow, it will create one and add the view as a subview.
|
||||
*/
|
||||
@property (readwrite, nonatomic, assign) BOOL usesDrawViewHierarchyInRect;
|
||||
|
||||
- (void)setUp NS_REQUIRES_SUPER;
|
||||
- (void)tearDown NS_REQUIRES_SUPER;
|
||||
|
||||
/**
|
||||
Performs the comparison or records a snapshot of the layer if recordMode is YES.
|
||||
@param layer The Layer to snapshot
|
||||
@param referenceImagesDirectory The directory in which reference images are stored.
|
||||
@param identifier An optional identifier, used if there are multiple snapshot tests in a given -test method.
|
||||
@param tolerance The percentage difference to still count as identical - 0 mean pixel perfect, 1 means I don't care
|
||||
@param errorPtr An error to log in an XCTAssert() macro if the method fails (missing reference image, images differ, etc).
|
||||
@returns YES if the comparison (or saving of the reference image) succeeded.
|
||||
*/
|
||||
- (BOOL)compareSnapshotOfLayer:(CALayer *)layer
|
||||
referenceImagesDirectory:(NSString *)referenceImagesDirectory
|
||||
identifier:(NSString *)identifier
|
||||
tolerance:(CGFloat)tolerance
|
||||
error:(NSError **)errorPtr;
|
||||
|
||||
/**
|
||||
Performs the comparison or records a snapshot of the view if recordMode is YES.
|
||||
@param view The view to snapshot
|
||||
@param referenceImagesDirectory The directory in which reference images are stored.
|
||||
@param identifier An optional identifier, used if there are multiple snapshot tests in a given -test method.
|
||||
@param tolerance The percentage difference to still count as identical - 0 mean pixel perfect, 1 means I don't care
|
||||
@param errorPtr An error to log in an XCTAssert() macro if the method fails (missing reference image, images differ, etc).
|
||||
@returns YES if the comparison (or saving of the reference image) succeeded.
|
||||
*/
|
||||
- (BOOL)compareSnapshotOfView:(UIView *)view
|
||||
referenceImagesDirectory:(NSString *)referenceImagesDirectory
|
||||
identifier:(NSString *)identifier
|
||||
tolerance:(CGFloat)tolerance
|
||||
error:(NSError **)errorPtr;
|
||||
|
||||
/**
|
||||
Checks if reference image with identifier based name exists in the reference images directory.
|
||||
@param referenceImagesDirectory The directory in which reference images are stored.
|
||||
@param identifier An optional identifier, used if there are multiple snapshot tests in a given -test method.
|
||||
@param errorPtr An error to log in an XCTAssert() macro if the method fails (missing reference image, images differ, etc).
|
||||
@returns YES if reference image exists.
|
||||
*/
|
||||
- (BOOL)referenceImageRecordedInDirectory:(NSString *)referenceImagesDirectory
|
||||
identifier:(NSString *)identifier
|
||||
error:(NSError **)errorPtr;
|
||||
|
||||
/**
|
||||
Returns the reference image directory.
|
||||
|
||||
Helper function used to implement the assert macros.
|
||||
|
||||
@param dir directory to use if environment variable not specified. Ignored if null or empty.
|
||||
*/
|
||||
- (NSString *)getReferenceImageDirectoryWithDefault:(NSString *)dir;
|
||||
|
||||
@end
|
||||
136
Example/Pods/FBSnapshotTestCase/FBSnapshotTestCase/FBSnapshotTestCase.m
generated
Normal file
136
Example/Pods/FBSnapshotTestCase/FBSnapshotTestCase/FBSnapshotTestCase.m
generated
Normal file
@ -0,0 +1,136 @@
|
||||
/*
|
||||
* Copyright (c) 2015, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*
|
||||
*/
|
||||
|
||||
#import <FBSnapshotTestCase/FBSnapshotTestCase.h>
|
||||
#import <FBSnapshotTestCase/FBSnapshotTestController.h>
|
||||
|
||||
@implementation FBSnapshotTestCase
|
||||
{
|
||||
FBSnapshotTestController *_snapshotController;
|
||||
}
|
||||
|
||||
#pragma mark - Overrides
|
||||
|
||||
- (void)setUp
|
||||
{
|
||||
[super setUp];
|
||||
_snapshotController = [[FBSnapshotTestController alloc] initWithTestName:NSStringFromClass([self class])];
|
||||
}
|
||||
|
||||
- (void)tearDown
|
||||
{
|
||||
_snapshotController = nil;
|
||||
[super tearDown];
|
||||
}
|
||||
|
||||
- (BOOL)recordMode
|
||||
{
|
||||
return _snapshotController.recordMode;
|
||||
}
|
||||
|
||||
- (void)setRecordMode:(BOOL)recordMode
|
||||
{
|
||||
NSAssert1(_snapshotController, @"%s cannot be called before [super setUp]", __FUNCTION__);
|
||||
_snapshotController.recordMode = recordMode;
|
||||
}
|
||||
|
||||
- (BOOL)isDeviceAgnostic
|
||||
{
|
||||
return _snapshotController.deviceAgnostic;
|
||||
}
|
||||
|
||||
- (void)setDeviceAgnostic:(BOOL)deviceAgnostic
|
||||
{
|
||||
NSAssert1(_snapshotController, @"%s cannot be called before [super setUp]", __FUNCTION__);
|
||||
_snapshotController.deviceAgnostic = deviceAgnostic;
|
||||
}
|
||||
|
||||
- (BOOL)usesDrawViewHierarchyInRect
|
||||
{
|
||||
return _snapshotController.usesDrawViewHierarchyInRect;
|
||||
}
|
||||
|
||||
- (void)setUsesDrawViewHierarchyInRect:(BOOL)usesDrawViewHierarchyInRect
|
||||
{
|
||||
NSAssert1(_snapshotController, @"%s cannot be called before [super setUp]", __FUNCTION__);
|
||||
_snapshotController.usesDrawViewHierarchyInRect = usesDrawViewHierarchyInRect;
|
||||
}
|
||||
|
||||
#pragma mark - Public API
|
||||
|
||||
- (BOOL)compareSnapshotOfLayer:(CALayer *)layer
|
||||
referenceImagesDirectory:(NSString *)referenceImagesDirectory
|
||||
identifier:(NSString *)identifier
|
||||
tolerance:(CGFloat)tolerance
|
||||
error:(NSError **)errorPtr
|
||||
{
|
||||
return [self _compareSnapshotOfViewOrLayer:layer
|
||||
referenceImagesDirectory:referenceImagesDirectory
|
||||
identifier:identifier
|
||||
tolerance:tolerance
|
||||
error:errorPtr];
|
||||
}
|
||||
|
||||
- (BOOL)compareSnapshotOfView:(UIView *)view
|
||||
referenceImagesDirectory:(NSString *)referenceImagesDirectory
|
||||
identifier:(NSString *)identifier
|
||||
tolerance:(CGFloat)tolerance
|
||||
error:(NSError **)errorPtr
|
||||
{
|
||||
return [self _compareSnapshotOfViewOrLayer:view
|
||||
referenceImagesDirectory:referenceImagesDirectory
|
||||
identifier:identifier
|
||||
tolerance:tolerance
|
||||
error:errorPtr];
|
||||
}
|
||||
|
||||
- (BOOL)referenceImageRecordedInDirectory:(NSString *)referenceImagesDirectory
|
||||
identifier:(NSString *)identifier
|
||||
error:(NSError **)errorPtr
|
||||
{
|
||||
NSAssert1(_snapshotController, @"%s cannot be called before [super setUp]", __FUNCTION__);
|
||||
_snapshotController.referenceImagesDirectory = referenceImagesDirectory;
|
||||
UIImage *referenceImage = [_snapshotController referenceImageForSelector:self.invocation.selector
|
||||
identifier:identifier
|
||||
error:errorPtr];
|
||||
|
||||
return (referenceImage != nil);
|
||||
}
|
||||
|
||||
- (NSString *)getReferenceImageDirectoryWithDefault:(NSString *)dir
|
||||
{
|
||||
NSString *envReferenceImageDirectory = [NSProcessInfo processInfo].environment[@"FB_REFERENCE_IMAGE_DIR"];
|
||||
if (envReferenceImageDirectory) {
|
||||
return envReferenceImageDirectory;
|
||||
}
|
||||
if (dir && dir.length > 0) {
|
||||
return dir;
|
||||
}
|
||||
return [[NSBundle bundleForClass:self.class].resourcePath stringByAppendingPathComponent:@"ReferenceImages"];
|
||||
}
|
||||
|
||||
|
||||
#pragma mark - Private API
|
||||
|
||||
- (BOOL)_compareSnapshotOfViewOrLayer:(id)viewOrLayer
|
||||
referenceImagesDirectory:(NSString *)referenceImagesDirectory
|
||||
identifier:(NSString *)identifier
|
||||
tolerance:(CGFloat)tolerance
|
||||
error:(NSError **)errorPtr
|
||||
{
|
||||
_snapshotController.referenceImagesDirectory = referenceImagesDirectory;
|
||||
return [_snapshotController compareSnapshotOfViewOrLayer:viewOrLayer
|
||||
selector:self.invocation.selector
|
||||
identifier:identifier
|
||||
tolerance:tolerance
|
||||
error:errorPtr];
|
||||
}
|
||||
|
||||
@end
|
||||
44
Example/Pods/FBSnapshotTestCase/FBSnapshotTestCase/FBSnapshotTestCasePlatform.h
generated
Normal file
44
Example/Pods/FBSnapshotTestCase/FBSnapshotTestCase/FBSnapshotTestCasePlatform.h
generated
Normal file
@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Copyright (c) 2015, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*
|
||||
*/
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
Returns a Boolean value that indicates whether the snapshot test is running in 64Bit.
|
||||
This method is a convenience for creating the suffixes set based on the architecture
|
||||
that the test is running.
|
||||
|
||||
@returns @c YES if the test is running in 64bit, otherwise @c NO.
|
||||
*/
|
||||
BOOL FBSnapshotTestCaseIs64Bit(void);
|
||||
|
||||
/**
|
||||
Returns a default set of strings that is used to append a suffix based on the architectures.
|
||||
@warning Do not modify this function, you can create your own and use it with @c FBSnapshotVerifyViewWithOptions()
|
||||
|
||||
@returns An @c NSOrderedSet object containing strings that are appended to the reference images directory.
|
||||
*/
|
||||
NSOrderedSet *FBSnapshotTestCaseDefaultSuffixes(void);
|
||||
|
||||
/**
|
||||
Returns a fully «normalized» file name.
|
||||
Strips punctuation and spaces and replaces them with @c _. Also appends the device model, running OS and screen size to the file name.
|
||||
|
||||
@returns An @c NSString object containing the passed @c fileName with the device model, OS and screen size appended at the end.
|
||||
*/
|
||||
NSString *FBDeviceAgnosticNormalizedFileName(NSString *fileName);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
49
Example/Pods/FBSnapshotTestCase/FBSnapshotTestCase/FBSnapshotTestCasePlatform.m
generated
Normal file
49
Example/Pods/FBSnapshotTestCase/FBSnapshotTestCase/FBSnapshotTestCasePlatform.m
generated
Normal file
@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Copyright (c) 2015, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*
|
||||
*/
|
||||
|
||||
#import <FBSnapshotTestCase/FBSnapshotTestCasePlatform.h>
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
BOOL FBSnapshotTestCaseIs64Bit(void)
|
||||
{
|
||||
#if __LP64__
|
||||
return YES;
|
||||
#else
|
||||
return NO;
|
||||
#endif
|
||||
}
|
||||
|
||||
NSOrderedSet *FBSnapshotTestCaseDefaultSuffixes(void)
|
||||
{
|
||||
NSMutableOrderedSet *suffixesSet = [[NSMutableOrderedSet alloc] init];
|
||||
[suffixesSet addObject:@"_32"];
|
||||
[suffixesSet addObject:@"_64"];
|
||||
if (FBSnapshotTestCaseIs64Bit()) {
|
||||
return [suffixesSet reversedOrderedSet];
|
||||
}
|
||||
return [suffixesSet copy];
|
||||
}
|
||||
|
||||
NSString *FBDeviceAgnosticNormalizedFileName(NSString *fileName)
|
||||
{
|
||||
UIDevice *device = [UIDevice currentDevice];
|
||||
CGSize screenSize = [[UIApplication sharedApplication] keyWindow].bounds.size;
|
||||
NSString *os = device.systemVersion;
|
||||
|
||||
fileName = [NSString stringWithFormat:@"%@_%@%@_%.0fx%.0f", fileName, device.model, os, screenSize.width, screenSize.height];
|
||||
|
||||
NSMutableCharacterSet *invalidCharacters = [NSMutableCharacterSet new];
|
||||
[invalidCharacters formUnionWithCharacterSet:[NSCharacterSet whitespaceCharacterSet]];
|
||||
[invalidCharacters formUnionWithCharacterSet:[NSCharacterSet punctuationCharacterSet]];
|
||||
NSArray *validComponents = [fileName componentsSeparatedByCharactersInSet:invalidCharacters];
|
||||
fileName = [validComponents componentsJoinedByString:@"_"];
|
||||
|
||||
return fileName;
|
||||
}
|
||||
151
Example/Pods/FBSnapshotTestCase/FBSnapshotTestCase/FBSnapshotTestController.h
generated
Normal file
151
Example/Pods/FBSnapshotTestCase/FBSnapshotTestCase/FBSnapshotTestController.h
generated
Normal file
@ -0,0 +1,151 @@
|
||||
/*
|
||||
* Copyright (c) 2015, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*
|
||||
*/
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
typedef NS_ENUM(NSInteger, FBSnapshotTestControllerErrorCode) {
|
||||
FBSnapshotTestControllerErrorCodeUnknown,
|
||||
FBSnapshotTestControllerErrorCodeNeedsRecord,
|
||||
FBSnapshotTestControllerErrorCodePNGCreationFailed,
|
||||
FBSnapshotTestControllerErrorCodeImagesDifferentSizes,
|
||||
FBSnapshotTestControllerErrorCodeImagesDifferent,
|
||||
};
|
||||
/**
|
||||
Errors returned by the methods of FBSnapshotTestController use this domain.
|
||||
*/
|
||||
extern NSString *const FBSnapshotTestControllerErrorDomain;
|
||||
|
||||
/**
|
||||
Errors returned by the methods of FBSnapshotTestController sometimes contain this key in the `userInfo` dictionary.
|
||||
*/
|
||||
extern NSString *const FBReferenceImageFilePathKey;
|
||||
|
||||
/**
|
||||
Provides the heavy-lifting for FBSnapshotTestCase. It loads and saves images, along with performing the actual pixel-
|
||||
by-pixel comparison of images.
|
||||
Instances are initialized with the test class, and directories to read and write to.
|
||||
*/
|
||||
@interface FBSnapshotTestController : NSObject
|
||||
|
||||
/**
|
||||
Record snapshots.
|
||||
*/
|
||||
@property (readwrite, nonatomic, assign) BOOL recordMode;
|
||||
|
||||
/**
|
||||
When @c YES appends the name of the device model and OS to the snapshot file name.
|
||||
The default value is @c NO.
|
||||
*/
|
||||
@property (readwrite, nonatomic, assign, getter=isDeviceAgnostic) BOOL deviceAgnostic;
|
||||
|
||||
/**
|
||||
Uses drawViewHierarchyInRect:afterScreenUpdates: to draw the image instead of renderInContext:
|
||||
*/
|
||||
@property (readwrite, nonatomic, assign) BOOL usesDrawViewHierarchyInRect;
|
||||
|
||||
/**
|
||||
The directory in which referfence images are stored.
|
||||
*/
|
||||
@property (readwrite, nonatomic, copy) NSString *referenceImagesDirectory;
|
||||
|
||||
/**
|
||||
@param testClass The subclass of FBSnapshotTestCase that is using this controller.
|
||||
@returns An instance of FBSnapshotTestController.
|
||||
*/
|
||||
- (instancetype)initWithTestClass:(Class)testClass;
|
||||
|
||||
/**
|
||||
Designated initializer.
|
||||
@param testName The name of the tests.
|
||||
@returns An instance of FBSnapshotTestController.
|
||||
*/
|
||||
- (instancetype)initWithTestName:(NSString *)testName;
|
||||
|
||||
/**
|
||||
Performs the comparison of the layer.
|
||||
@param layer The Layer to snapshot.
|
||||
@param selector The test method being run.
|
||||
@param identifier An optional identifier, used is there are muliptle snapshot tests in a given -test method.
|
||||
@param error An error to log in an XCTAssert() macro if the method fails (missing reference image, images differ, etc).
|
||||
@returns YES if the comparison (or saving of the reference image) succeeded.
|
||||
*/
|
||||
- (BOOL)compareSnapshotOfLayer:(CALayer *)layer
|
||||
selector:(SEL)selector
|
||||
identifier:(NSString *)identifier
|
||||
error:(NSError **)errorPtr;
|
||||
|
||||
/**
|
||||
Performs the comparison of the view.
|
||||
@param view The view to snapshot.
|
||||
@param selector The test method being run.
|
||||
@param identifier An optional identifier, used is there are muliptle snapshot tests in a given -test method.
|
||||
@param error An error to log in an XCTAssert() macro if the method fails (missing reference image, images differ, etc).
|
||||
@returns YES if the comparison (or saving of the reference image) succeeded.
|
||||
*/
|
||||
- (BOOL)compareSnapshotOfView:(UIView *)view
|
||||
selector:(SEL)selector
|
||||
identifier:(NSString *)identifier
|
||||
error:(NSError **)errorPtr;
|
||||
|
||||
/**
|
||||
Performs the comparison of a view or layer.
|
||||
@param view The view or layer to snapshot.
|
||||
@param selector The test method being run.
|
||||
@param identifier An optional identifier, used is there are muliptle snapshot tests in a given -test method.
|
||||
@param tolerance The percentage of pixels that can differ and still be considered 'identical'
|
||||
@param error An error to log in an XCTAssert() macro if the method fails (missing reference image, images differ, etc).
|
||||
@returns YES if the comparison (or saving of the reference image) succeeded.
|
||||
*/
|
||||
- (BOOL)compareSnapshotOfViewOrLayer:(id)viewOrLayer
|
||||
selector:(SEL)selector
|
||||
identifier:(NSString *)identifier
|
||||
tolerance:(CGFloat)tolerance
|
||||
error:(NSError **)errorPtr;
|
||||
|
||||
/**
|
||||
Loads a reference image.
|
||||
@param selector The test method being run.
|
||||
@param identifier The optional identifier, used when multiple images are tested in a single -test method.
|
||||
@param errorPtr An error, if this methods returns nil, the error will be something useful.
|
||||
@returns An image.
|
||||
*/
|
||||
- (UIImage *)referenceImageForSelector:(SEL)selector
|
||||
identifier:(NSString *)identifier
|
||||
error:(NSError **)errorPtr;
|
||||
|
||||
/**
|
||||
Performs a pixel-by-pixel comparison of the two images with an allowable margin of error.
|
||||
@param referenceImage The reference (correct) image.
|
||||
@param image The image to test against the reference.
|
||||
@param tolerance The percentage of pixels that can differ and still be considered 'identical'
|
||||
@param errorPtr An error that indicates why the comparison failed if it does.
|
||||
@returns YES if the comparison succeeded and the images are the same(ish).
|
||||
*/
|
||||
- (BOOL)compareReferenceImage:(UIImage *)referenceImage
|
||||
toImage:(UIImage *)image
|
||||
tolerance:(CGFloat)tolerance
|
||||
error:(NSError **)errorPtr;
|
||||
|
||||
/**
|
||||
Saves the reference image and the test image to `failedOutputDirectory`.
|
||||
@param referenceImage The reference (correct) image.
|
||||
@param testImage The image to test against the reference.
|
||||
@param selector The test method being run.
|
||||
@param identifier The optional identifier, used when multiple images are tested in a single -test method.
|
||||
@param errorPtr An error that indicates why the comparison failed if it does.
|
||||
@returns YES if the save succeeded.
|
||||
*/
|
||||
- (BOOL)saveFailedReferenceImage:(UIImage *)referenceImage
|
||||
testImage:(UIImage *)testImage
|
||||
selector:(SEL)selector
|
||||
identifier:(NSString *)identifier
|
||||
error:(NSError **)errorPtr;
|
||||
@end
|
||||
356
Example/Pods/FBSnapshotTestCase/FBSnapshotTestCase/FBSnapshotTestController.m
generated
Normal file
356
Example/Pods/FBSnapshotTestCase/FBSnapshotTestCase/FBSnapshotTestController.m
generated
Normal file
@ -0,0 +1,356 @@
|
||||
/*
|
||||
* Copyright (c) 2015, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*
|
||||
*/
|
||||
|
||||
#import <FBSnapshotTestCase/FBSnapshotTestController.h>
|
||||
#import <FBSnapshotTestCase/FBSnapshotTestCasePlatform.h>
|
||||
#import <FBSnapshotTestCase/UIImage+Compare.h>
|
||||
#import <FBSnapshotTestCase/UIImage+Diff.h>
|
||||
#import <FBSnapshotTestCase/UIImage+Snapshot.h>
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
NSString *const FBSnapshotTestControllerErrorDomain = @"FBSnapshotTestControllerErrorDomain";
|
||||
NSString *const FBReferenceImageFilePathKey = @"FBReferenceImageFilePathKey";
|
||||
|
||||
typedef NS_ENUM(NSUInteger, FBTestSnapshotFileNameType) {
|
||||
FBTestSnapshotFileNameTypeReference,
|
||||
FBTestSnapshotFileNameTypeFailedReference,
|
||||
FBTestSnapshotFileNameTypeFailedTest,
|
||||
FBTestSnapshotFileNameTypeFailedTestDiff,
|
||||
};
|
||||
|
||||
@implementation FBSnapshotTestController
|
||||
{
|
||||
NSString *_testName;
|
||||
NSFileManager *_fileManager;
|
||||
}
|
||||
|
||||
#pragma mark - Initializers
|
||||
|
||||
- (instancetype)initWithTestClass:(Class)testClass;
|
||||
{
|
||||
return [self initWithTestName:NSStringFromClass(testClass)];
|
||||
}
|
||||
|
||||
- (instancetype)initWithTestName:(NSString *)testName
|
||||
{
|
||||
if (self = [super init]) {
|
||||
_testName = [testName copy];
|
||||
_deviceAgnostic = NO;
|
||||
|
||||
_fileManager = [[NSFileManager alloc] init];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
#pragma mark - Overrides
|
||||
|
||||
- (NSString *)description
|
||||
{
|
||||
return [NSString stringWithFormat:@"%@ %@", [super description], _referenceImagesDirectory];
|
||||
}
|
||||
|
||||
#pragma mark - Public API
|
||||
|
||||
- (BOOL)compareSnapshotOfLayer:(CALayer *)layer
|
||||
selector:(SEL)selector
|
||||
identifier:(NSString *)identifier
|
||||
error:(NSError **)errorPtr
|
||||
{
|
||||
return [self compareSnapshotOfViewOrLayer:layer
|
||||
selector:selector
|
||||
identifier:identifier
|
||||
tolerance:0
|
||||
error:errorPtr];
|
||||
}
|
||||
|
||||
- (BOOL)compareSnapshotOfView:(UIView *)view
|
||||
selector:(SEL)selector
|
||||
identifier:(NSString *)identifier
|
||||
error:(NSError **)errorPtr
|
||||
{
|
||||
return [self compareSnapshotOfViewOrLayer:view
|
||||
selector:selector
|
||||
identifier:identifier
|
||||
tolerance:0
|
||||
error:errorPtr];
|
||||
}
|
||||
|
||||
- (BOOL)compareSnapshotOfViewOrLayer:(id)viewOrLayer
|
||||
selector:(SEL)selector
|
||||
identifier:(NSString *)identifier
|
||||
tolerance:(CGFloat)tolerance
|
||||
error:(NSError **)errorPtr
|
||||
{
|
||||
if (self.recordMode) {
|
||||
return [self _recordSnapshotOfViewOrLayer:viewOrLayer selector:selector identifier:identifier error:errorPtr];
|
||||
} else {
|
||||
return [self _performPixelComparisonWithViewOrLayer:viewOrLayer selector:selector identifier:identifier tolerance:tolerance error:errorPtr];
|
||||
}
|
||||
}
|
||||
|
||||
- (UIImage *)referenceImageForSelector:(SEL)selector
|
||||
identifier:(NSString *)identifier
|
||||
error:(NSError **)errorPtr
|
||||
{
|
||||
NSString *filePath = [self _referenceFilePathForSelector:selector identifier:identifier];
|
||||
UIImage *image = [UIImage imageWithContentsOfFile:filePath];
|
||||
if (nil == image && NULL != errorPtr) {
|
||||
BOOL exists = [_fileManager fileExistsAtPath:filePath];
|
||||
if (!exists) {
|
||||
*errorPtr = [NSError errorWithDomain:FBSnapshotTestControllerErrorDomain
|
||||
code:FBSnapshotTestControllerErrorCodeNeedsRecord
|
||||
userInfo:@{
|
||||
FBReferenceImageFilePathKey: filePath,
|
||||
NSLocalizedDescriptionKey: @"Unable to load reference image.",
|
||||
NSLocalizedFailureReasonErrorKey: @"Reference image not found. You need to run the test in record mode",
|
||||
}];
|
||||
} else {
|
||||
*errorPtr = [NSError errorWithDomain:FBSnapshotTestControllerErrorDomain
|
||||
code:FBSnapshotTestControllerErrorCodeUnknown
|
||||
userInfo:nil];
|
||||
}
|
||||
}
|
||||
return image;
|
||||
}
|
||||
|
||||
- (BOOL)compareReferenceImage:(UIImage *)referenceImage
|
||||
toImage:(UIImage *)image
|
||||
tolerance:(CGFloat)tolerance
|
||||
error:(NSError **)errorPtr
|
||||
{
|
||||
if (CGSizeEqualToSize(referenceImage.size, image.size)) {
|
||||
BOOL imagesEqual = [referenceImage fb_compareWithImage:image tolerance:tolerance];
|
||||
if (NULL != errorPtr) {
|
||||
*errorPtr = [NSError errorWithDomain:FBSnapshotTestControllerErrorDomain
|
||||
code:FBSnapshotTestControllerErrorCodeImagesDifferent
|
||||
userInfo:@{
|
||||
NSLocalizedDescriptionKey: @"Images different",
|
||||
}];
|
||||
}
|
||||
return imagesEqual;
|
||||
}
|
||||
if (NULL != errorPtr) {
|
||||
*errorPtr = [NSError errorWithDomain:FBSnapshotTestControllerErrorDomain
|
||||
code:FBSnapshotTestControllerErrorCodeImagesDifferentSizes
|
||||
userInfo:@{
|
||||
NSLocalizedDescriptionKey: @"Images different sizes",
|
||||
NSLocalizedFailureReasonErrorKey: [NSString stringWithFormat:@"referenceImage:%@, image:%@",
|
||||
NSStringFromCGSize(referenceImage.size),
|
||||
NSStringFromCGSize(image.size)],
|
||||
}];
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (BOOL)saveFailedReferenceImage:(UIImage *)referenceImage
|
||||
testImage:(UIImage *)testImage
|
||||
selector:(SEL)selector
|
||||
identifier:(NSString *)identifier
|
||||
error:(NSError **)errorPtr
|
||||
{
|
||||
NSData *referencePNGData = UIImagePNGRepresentation(referenceImage);
|
||||
NSData *testPNGData = UIImagePNGRepresentation(testImage);
|
||||
|
||||
NSString *referencePath = [self _failedFilePathForSelector:selector
|
||||
identifier:identifier
|
||||
fileNameType:FBTestSnapshotFileNameTypeFailedReference];
|
||||
|
||||
NSError *creationError = nil;
|
||||
BOOL didCreateDir = [_fileManager createDirectoryAtPath:[referencePath stringByDeletingLastPathComponent]
|
||||
withIntermediateDirectories:YES
|
||||
attributes:nil
|
||||
error:&creationError];
|
||||
if (!didCreateDir) {
|
||||
if (NULL != errorPtr) {
|
||||
*errorPtr = creationError;
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
|
||||
if (![referencePNGData writeToFile:referencePath options:NSDataWritingAtomic error:errorPtr]) {
|
||||
return NO;
|
||||
}
|
||||
|
||||
NSString *testPath = [self _failedFilePathForSelector:selector
|
||||
identifier:identifier
|
||||
fileNameType:FBTestSnapshotFileNameTypeFailedTest];
|
||||
|
||||
if (![testPNGData writeToFile:testPath options:NSDataWritingAtomic error:errorPtr]) {
|
||||
return NO;
|
||||
}
|
||||
|
||||
NSString *diffPath = [self _failedFilePathForSelector:selector
|
||||
identifier:identifier
|
||||
fileNameType:FBTestSnapshotFileNameTypeFailedTestDiff];
|
||||
|
||||
UIImage *diffImage = [referenceImage fb_diffWithImage:testImage];
|
||||
NSData *diffImageData = UIImagePNGRepresentation(diffImage);
|
||||
|
||||
if (![diffImageData writeToFile:diffPath options:NSDataWritingAtomic error:errorPtr]) {
|
||||
return NO;
|
||||
}
|
||||
|
||||
NSLog(@"If you have Kaleidoscope installed you can run this command to see an image diff:\n"
|
||||
@"ksdiff \"%@\" \"%@\"", referencePath, testPath);
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
#pragma mark - Private API
|
||||
|
||||
- (NSString *)_fileNameForSelector:(SEL)selector
|
||||
identifier:(NSString *)identifier
|
||||
fileNameType:(FBTestSnapshotFileNameType)fileNameType
|
||||
{
|
||||
NSString *fileName = nil;
|
||||
switch (fileNameType) {
|
||||
case FBTestSnapshotFileNameTypeFailedReference:
|
||||
fileName = @"reference_";
|
||||
break;
|
||||
case FBTestSnapshotFileNameTypeFailedTest:
|
||||
fileName = @"failed_";
|
||||
break;
|
||||
case FBTestSnapshotFileNameTypeFailedTestDiff:
|
||||
fileName = @"diff_";
|
||||
break;
|
||||
default:
|
||||
fileName = @"";
|
||||
break;
|
||||
}
|
||||
fileName = [fileName stringByAppendingString:NSStringFromSelector(selector)];
|
||||
if (0 < identifier.length) {
|
||||
fileName = [fileName stringByAppendingFormat:@"_%@", identifier];
|
||||
}
|
||||
|
||||
if (self.isDeviceAgnostic) {
|
||||
fileName = FBDeviceAgnosticNormalizedFileName(fileName);
|
||||
}
|
||||
|
||||
if ([[UIScreen mainScreen] scale] > 1) {
|
||||
fileName = [fileName stringByAppendingFormat:@"@%.fx", [[UIScreen mainScreen] scale]];
|
||||
}
|
||||
fileName = [fileName stringByAppendingPathExtension:@"png"];
|
||||
return fileName;
|
||||
}
|
||||
|
||||
- (NSString *)_referenceFilePathForSelector:(SEL)selector
|
||||
identifier:(NSString *)identifier
|
||||
{
|
||||
NSString *fileName = [self _fileNameForSelector:selector
|
||||
identifier:identifier
|
||||
fileNameType:FBTestSnapshotFileNameTypeReference];
|
||||
NSString *filePath = [_referenceImagesDirectory stringByAppendingPathComponent:_testName];
|
||||
filePath = [filePath stringByAppendingPathComponent:fileName];
|
||||
return filePath;
|
||||
}
|
||||
|
||||
- (NSString *)_failedFilePathForSelector:(SEL)selector
|
||||
identifier:(NSString *)identifier
|
||||
fileNameType:(FBTestSnapshotFileNameType)fileNameType
|
||||
{
|
||||
NSString *fileName = [self _fileNameForSelector:selector
|
||||
identifier:identifier
|
||||
fileNameType:fileNameType];
|
||||
NSString *folderPath = NSTemporaryDirectory();
|
||||
if (getenv("IMAGE_DIFF_DIR")) {
|
||||
folderPath = @(getenv("IMAGE_DIFF_DIR"));
|
||||
}
|
||||
NSString *filePath = [folderPath stringByAppendingPathComponent:_testName];
|
||||
filePath = [filePath stringByAppendingPathComponent:fileName];
|
||||
return filePath;
|
||||
}
|
||||
|
||||
- (BOOL)_performPixelComparisonWithViewOrLayer:(id)viewOrLayer
|
||||
selector:(SEL)selector
|
||||
identifier:(NSString *)identifier
|
||||
tolerance:(CGFloat)tolerance
|
||||
error:(NSError **)errorPtr
|
||||
{
|
||||
UIImage *referenceImage = [self referenceImageForSelector:selector identifier:identifier error:errorPtr];
|
||||
if (nil != referenceImage) {
|
||||
UIImage *snapshot = [self _imageForViewOrLayer:viewOrLayer];
|
||||
BOOL imagesSame = [self compareReferenceImage:referenceImage toImage:snapshot tolerance:tolerance error:errorPtr];
|
||||
if (!imagesSame) {
|
||||
[self saveFailedReferenceImage:referenceImage
|
||||
testImage:snapshot
|
||||
selector:selector
|
||||
identifier:identifier
|
||||
error:errorPtr];
|
||||
}
|
||||
return imagesSame;
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (BOOL)_recordSnapshotOfViewOrLayer:(id)viewOrLayer
|
||||
selector:(SEL)selector
|
||||
identifier:(NSString *)identifier
|
||||
error:(NSError **)errorPtr
|
||||
{
|
||||
UIImage *snapshot = [self _imageForViewOrLayer:viewOrLayer];
|
||||
return [self _saveReferenceImage:snapshot selector:selector identifier:identifier error:errorPtr];
|
||||
}
|
||||
|
||||
- (BOOL)_saveReferenceImage:(UIImage *)image
|
||||
selector:(SEL)selector
|
||||
identifier:(NSString *)identifier
|
||||
error:(NSError **)errorPtr
|
||||
{
|
||||
BOOL didWrite = NO;
|
||||
if (nil != image) {
|
||||
NSString *filePath = [self _referenceFilePathForSelector:selector identifier:identifier];
|
||||
NSData *pngData = UIImagePNGRepresentation(image);
|
||||
if (nil != pngData) {
|
||||
NSError *creationError = nil;
|
||||
BOOL didCreateDir = [_fileManager createDirectoryAtPath:[filePath stringByDeletingLastPathComponent]
|
||||
withIntermediateDirectories:YES
|
||||
attributes:nil
|
||||
error:&creationError];
|
||||
if (!didCreateDir) {
|
||||
if (NULL != errorPtr) {
|
||||
*errorPtr = creationError;
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
didWrite = [pngData writeToFile:filePath options:NSDataWritingAtomic error:errorPtr];
|
||||
if (didWrite) {
|
||||
NSLog(@"Reference image save at: %@", filePath);
|
||||
}
|
||||
} else {
|
||||
if (nil != errorPtr) {
|
||||
*errorPtr = [NSError errorWithDomain:FBSnapshotTestControllerErrorDomain
|
||||
code:FBSnapshotTestControllerErrorCodePNGCreationFailed
|
||||
userInfo:@{
|
||||
FBReferenceImageFilePathKey: filePath,
|
||||
}];
|
||||
}
|
||||
}
|
||||
}
|
||||
return didWrite;
|
||||
}
|
||||
|
||||
- (UIImage *)_imageForViewOrLayer:(id)viewOrLayer
|
||||
{
|
||||
if ([viewOrLayer isKindOfClass:[UIView class]]) {
|
||||
if (_usesDrawViewHierarchyInRect) {
|
||||
return [UIImage fb_imageForView:viewOrLayer];
|
||||
} else {
|
||||
return [UIImage fb_imageForViewLayer:viewOrLayer];
|
||||
}
|
||||
} else if ([viewOrLayer isKindOfClass:[CALayer class]]) {
|
||||
return [UIImage fb_imageForLayer:viewOrLayer];
|
||||
} else {
|
||||
[NSException raise:@"Only UIView and CALayer classes can be snapshotted" format:@"%@", viewOrLayer];
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
@end
|
||||
66
Example/Pods/FBSnapshotTestCase/FBSnapshotTestCase/SwiftSupport.swift
generated
Normal file
66
Example/Pods/FBSnapshotTestCase/FBSnapshotTestCase/SwiftSupport.swift
generated
Normal file
@ -0,0 +1,66 @@
|
||||
/*
|
||||
* Copyright (c) 2015, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*
|
||||
*/
|
||||
|
||||
public extension FBSnapshotTestCase {
|
||||
public func FBSnapshotVerifyView(view: UIView, identifier: String = "", suffixes: NSOrderedSet = FBSnapshotTestCaseDefaultSuffixes(), file: String = __FILE__, line: UInt = __LINE__) {
|
||||
FBSnapshotVerifyViewOrLayer(view, identifier: identifier, suffixes: suffixes)
|
||||
}
|
||||
|
||||
public func FBSnapshotVerifyLayer(layer: CALayer, identifier: String = "", suffixes: NSOrderedSet = FBSnapshotTestCaseDefaultSuffixes(), file: String = __FILE__, line: UInt = __LINE__) {
|
||||
FBSnapshotVerifyViewOrLayer(layer, identifier: identifier, suffixes: suffixes)
|
||||
}
|
||||
|
||||
private func FBSnapshotVerifyViewOrLayer(viewOrLayer: AnyObject, identifier: String = "", suffixes: NSOrderedSet = FBSnapshotTestCaseDefaultSuffixes(), file: String = __FILE__, line: UInt = __LINE__) {
|
||||
let envReferenceImageDirectory = self.getReferenceImageDirectoryWithDefault(FB_REFERENCE_IMAGE_DIR)
|
||||
var error: NSError?
|
||||
var comparisonSuccess = false
|
||||
|
||||
if let envReferenceImageDirectory = envReferenceImageDirectory {
|
||||
for suffix in suffixes {
|
||||
let referenceImagesDirectory = "\(envReferenceImageDirectory)\(suffix)"
|
||||
if viewOrLayer.isKindOfClass(UIView) {
|
||||
do {
|
||||
try compareSnapshotOfView(viewOrLayer as! UIView, referenceImagesDirectory: referenceImagesDirectory, identifier: identifier, tolerance: 0)
|
||||
comparisonSuccess = true
|
||||
} catch let error1 as NSError {
|
||||
error = error1
|
||||
comparisonSuccess = false
|
||||
}
|
||||
} else if viewOrLayer.isKindOfClass(CALayer) {
|
||||
do {
|
||||
try compareSnapshotOfLayer(viewOrLayer as! CALayer, referenceImagesDirectory: referenceImagesDirectory, identifier: identifier, tolerance: 0)
|
||||
comparisonSuccess = true
|
||||
} catch let error1 as NSError {
|
||||
error = error1
|
||||
comparisonSuccess = false
|
||||
}
|
||||
} else {
|
||||
assertionFailure("Only UIView and CALayer classes can be snapshotted")
|
||||
}
|
||||
|
||||
assert(recordMode == false, message: "Test ran in record mode. Reference image is now saved. Disable record mode to perform an actual snapshot comparison!", file: file, line: line)
|
||||
|
||||
if comparisonSuccess || recordMode {
|
||||
break
|
||||
}
|
||||
|
||||
assert(comparisonSuccess, message: "Snapshot comparison failed: \(error)", file: file, line: line)
|
||||
}
|
||||
} else {
|
||||
XCTFail("Missing value for referenceImagesDirectory - Set FB_REFERENCE_IMAGE_DIR as Environment variable in your scheme.")
|
||||
}
|
||||
}
|
||||
|
||||
func assert(assertion: Bool, message: String, file: String, line: UInt) {
|
||||
if !assertion {
|
||||
XCTFail(message, file: file, line: line)
|
||||
}
|
||||
}
|
||||
}
|
||||
29
Example/Pods/FBSnapshotTestCase/LICENSE
generated
Normal file
29
Example/Pods/FBSnapshotTestCase/LICENSE
generated
Normal file
@ -0,0 +1,29 @@
|
||||
BSD License
|
||||
|
||||
For the FBSnapshotTestCase software
|
||||
|
||||
Copyright (c) 2013, Facebook, Inc.
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
* Neither the name Facebook nor the names of its contributors may be used to
|
||||
endorse or promote products derived from this software without specific
|
||||
prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
97
Example/Pods/FBSnapshotTestCase/README.md
generated
Normal file
97
Example/Pods/FBSnapshotTestCase/README.md
generated
Normal file
@ -0,0 +1,97 @@
|
||||
FBSnapshotTestCase
|
||||
======================
|
||||
|
||||
[](https://travis-ci.org/facebook/ios-snapshot-test-case) [](http://cocoadocs.org/docsets/FBSnapshotTestCase/)
|
||||
|
||||
What it does
|
||||
------------
|
||||
|
||||
A "snapshot test case" takes a configured `UIView` or `CALayer` and uses the
|
||||
`renderInContext:` method to get an image snapshot of its contents. It
|
||||
compares this snapshot to a "reference image" stored in your source code
|
||||
repository and fails the test if the two images don't match.
|
||||
|
||||
Why?
|
||||
----
|
||||
|
||||
At Facebook we write a lot of UI code. As you might imagine, each type of
|
||||
feed story is rendered using a subclass of `UIView`. There are a lot of edge
|
||||
cases that we want to handle correctly:
|
||||
|
||||
- What if there is more text than can fit in the space available?
|
||||
- What if an image doesn't match the size of an image view?
|
||||
- What should the highlighted state look like?
|
||||
|
||||
It's straightforward to test logic code, but less obvious how you should test
|
||||
views. You can do a lot of rectangle asserts, but these are hard to understand
|
||||
or visualize. Looking at an image diff shows you exactly what changed and how
|
||||
it will look to users.
|
||||
|
||||
We developed `FBSnapshotTestCase` to make snapshot tests easy.
|
||||
|
||||
Installation with CocoaPods
|
||||
---------------------------
|
||||
|
||||
1. Add the following lines to your Podfile:
|
||||
|
||||
```
|
||||
target "Tests" do
|
||||
pod 'FBSnapshotTestCase'
|
||||
end
|
||||
```
|
||||
|
||||
If you support iOS 7 use `FBSnapshotTestCase/Core` instead, which doesn't contain Swift support.
|
||||
|
||||
Replace "Tests" with the name of your test project.
|
||||
|
||||
2. There are [three ways](https://github.com/facebook/ios-snapshot-test-case/blob/master/FBSnapshotTestCase/FBSnapshotTestCase.h#L19-L29) of setting reference image directories, the recommended one is to define `FB_REFERENCE_IMAGE_DIR` in your scheme. This should point to the directory where you want reference images to be stored. At Facebook, we normally use this:
|
||||
|
||||
|Name|Value|
|
||||
|:---|:----|
|
||||
|`FB_REFERENCE_IMAGE_DIR`|`$(SOURCE_ROOT)/$(PROJECT_NAME)Tests/ReferenceImages`|
|
||||
|
||||
|
||||

|
||||
|
||||
Creating a snapshot test
|
||||
------------------------
|
||||
|
||||
1. Subclass `FBSnapshotTestCase` instead of `XCTestCase`.
|
||||
2. From within your test, use `FBSnapshotVerifyView`.
|
||||
3. Run the test once with `self.recordMode = YES;` in the test's `-setUp`
|
||||
method. (This creates the reference images on disk.)
|
||||
4. Remove the line enabling record mode and run the test.
|
||||
|
||||
Features
|
||||
--------
|
||||
|
||||
- Automatically names reference images on disk according to test class and
|
||||
selector.
|
||||
- Prints a descriptive error message to the console on failure. (Bonus:
|
||||
failure message includes a one-line command to see an image diff if
|
||||
you have [Kaleidoscope](http://www.kaleidoscopeapp.com) installed.)
|
||||
- Supply an optional "identifier" if you want to perform multiple snapshots
|
||||
in a single test method.
|
||||
- Support for `CALayer` via `FBSnapshotVerifyLayer`.
|
||||
- `usesDrawViewHierarchyInRect` to handle cases like `UIVisualEffect`, `UIAppearance` and Size Classes.
|
||||
- `isDeviceAgnostic` to allow appending the device model (`iPhone`, `iPad`, `iPod Touch`, etc), OS version and screen size to the images (allowing to have multiple tests for the same «snapshot» for different `OS`s and devices).
|
||||
|
||||
Notes
|
||||
-----
|
||||
|
||||
Your unit test must be an "application test", not a "logic test." (That is, it
|
||||
must be run within the Simulator so that it has access to UIKit.) In Xcode 5
|
||||
and later new projects only offer application tests, but older projects will
|
||||
have separate targets for the two types.
|
||||
|
||||
Authors
|
||||
-------
|
||||
|
||||
`FBSnapshotTestCase` was written at Facebook by
|
||||
[Jonathan Dann](https://facebook.com/j.p.dann) with significant contributions by
|
||||
[Todd Krabach](https://facebook.com/toddkrabach).
|
||||
|
||||
License
|
||||
-------
|
||||
|
||||
`FBSnapshotTestCase` is BSD-licensed. See `LICENSE`.
|
||||
24
Example/Pods/Local Podspecs/STTweetTextView.podspec.json
generated
Normal file
24
Example/Pods/Local Podspecs/STTweetTextView.podspec.json
generated
Normal file
@ -0,0 +1,24 @@
|
||||
{
|
||||
"name": "STTweetTextView",
|
||||
"version": "0.1.0",
|
||||
"summary": "STTweetTextView.",
|
||||
"homepage": "https://github.com/<GITHUB_USERNAME>/STTweetTextView",
|
||||
"license": "MIT",
|
||||
"authors": {
|
||||
"Giuseppe Nucifora": "me@giuseppenucifora.com"
|
||||
},
|
||||
"source": {
|
||||
"git": "https://github.com/<GITHUB_USERNAME>/STTweetTextView.git",
|
||||
"tag": "0.1.0"
|
||||
},
|
||||
"platforms": {
|
||||
"ios": "7.0"
|
||||
},
|
||||
"requires_arc": true,
|
||||
"source_files": "Pod/Classes/**/*",
|
||||
"resource_bundles": {
|
||||
"STTweetTextView": [
|
||||
"Pod/Assets/*.png"
|
||||
]
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user