Skip to content
This repository has been archived by the owner on May 19, 2018. It is now read-only.

Commit

Permalink
Initial commit.
Browse files Browse the repository at this point in the history
  • Loading branch information
robrix committed Sep 18, 2009
0 parents commit b28ee04
Show file tree
Hide file tree
Showing 7 changed files with 550 additions and 0 deletions.
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
.DS_Store
*.tmproj
*.xcodeproj/*.pbxuser
*.xcodeproj/*.mode*
build
9 changes: 9 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
Copyright (c) 2007-2009, Monochrome Industries
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 of Monochrome Industries 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 OWNER 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.
92 changes: 92 additions & 0 deletions README.mdown
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
#RXAssertions

(Cleverly assertive macros for your testing enjoyment.)

The key ideas here are simple:

1. _Simple API._ Fewer assertion macros are better.
2. _Simple asserting._ The macros should give you a smart enough error message that you don’t need to provide your own.
3. _Doesn’t replace `STAssert*`_. If you need to customize the message being logged, you can fall back to the `STAssert*` macros exactly where you need to.


#Assertion Macros

All of these log source for the condition/expression as appropriate. Anything else that gets logged is noted alongside.

- **`RXAssert(condition)`**

Asserts that `condition` is true.

- **`RXAssertFalse(condition)`**

Asserts that `condition` is false.

- **`RXAssertEquals(actual, expected)`**

Asserts that the actual value is equal to the expected value; if it’s not, it logs what it actually was.

This is valid for use with objects, scalars (including floats; see RXRound for more), structs, and really anything that you’ve registered a comparator for. See `RXAssertionHelper` for more info.

- **`RXAssertNotEquals(actual, unexpected)`**

Asserts that the actual value is not equal to the unexpected value; if it is, it logs what it unexpectedly was.

- **`RXAssertNil(object)`**

Asserts that the object is nil. If it isn’t, it logs what it actually was.

- **`RXAssertNotNil(object)`**

Asserts that the object is not nil.


#Helper Macros

These are helpful macros which are used by RXAssertions or which are intended for your use.

- **`RXUnionCast(value, type)`**

Casts value to type using an on-the-fly union. This is safe for use with strict aliasing.

- **`RXRound(value, place)`**

Rounds value to place, e.g. `RXRound(M_PI, 0.01)` will result in `3.14`. You can use this with `RXAssertEquals` to easily test against floating point fixtures without worrying about IEEE float precision problems:

RXAssertEquals(RXRound([thing returnPotentiallyImpreciseFloat], 0.01), 1.23);

However, see `+[RXAssertionHelper floatingPointComparisonAccuracy]` for an alternative.


#`RXAssertionHelper`

True to its name, `RXAssertionHelper` helps the assertion macros with some of their tasks, namely:

- **Comparing values of arbitrary type.**

`+registerComparisonFunction:forObjCType:` and `+compareValue:withValue:ofObjCType:` are used to add new comparators.

RXAssertions ships with comparators for signed and unsigned integers of 8, 16, 32, and 64 bits of width; `float`s and `double`s; objects and classes; `NSPoint`s (and `CGPoint`s via the same function); and arbitrary pointers (and thus, unsupported types will be compared with pointer equality, which will presumably fail every time).

To add other comparators, simply write the comparison function (matching the `RXAssertionHelperComparisonFunction` typedef) and register it with `RXAssertionHelper` somewhere convenient before you call `RXAssertEquals` with values of this type, perhaps in your test suite’s `+initialize` method.

There is currently no support for comparing values of wildly different types! The expected value is assigned to a variable of the actual value’s type. If there is sufficient need for comparing `CGAffineTransform`s with tree frogs, this policy can be revised.

- **Describing values of arbitrary type.**

`+registerDescriptionFunction:forObjCType` and `+descriptionForValue:ofObjCType:` are used to return an `NSString` instance describing the passed-in value.

These are widely used in the assertion macros for logging of actual and expected values. RXAssertions includes descriptors for signed and unsigned integers of 8, 16, 32, and 64 bits of width; `float`s and `double`s; objects and classes; `NSPoint`s (and `CGPoint`s via the same function); and arbitrary pointers (which includes pointers to any unsupported types—so if you see hex dumps of your compared values, you will want to add a descriptor).

Adding a descriptor is even simpler than adding a comparator; a `RXAssertionHelperDescriptionFunction` takes a reference to the value to be described, casts it (presumably using `RXUnionCast`) to the expected type, and builds an `NSString` from it.

- **Controlling the accuracy at which `RXAssertEquals` compares `float` and `double` values.**

`+[RXAssertionHelper floatingPointComparisonAccuracy]` and `+[RXAssertionHelper setFloatingPointComparisonAccuracy:]` are used to get and set the floating point accuracy, by default 0, used by the comparators for `float` and `double` values, and thus by `RXAssertEquals`.

There is no support for managing separate `float` and `double` accuracies; if this would simplify things for you, it would be a simple change to make.


#What’s Missing

- Exception interaction of any kind. Continue using the `STAssert*` macros for exceptions until this is implemented.
- Comparators and descriptors for `long double`, `CGSize`, `CGRect`, `CGAffineTransform`, `CFType` instances, and many other things. Patches welcome.
52 changes: 52 additions & 0 deletions RXAssertions.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
// RXAssertions.h
// Created by Rob Rix on 2009-08-20
// Copyright 2009 Decimus Software, Inc.

#import <SenTestingKit/SenTestingKit.h>

// Assertion macros that don’t require you to describe the assertion. Perfect for use with intention-revealing code.

#define RXAssert(_expression) STAssertTrue(_expression, @"%s was unexpectedly false.", #_expression)
#define RXAssertFalse(_expression) STAssertFalse(_expression, @"%s was unexpectedly true.", #_expression)


// casts the expected value to the type of the actual value. will fail (and rightly so) if you try crazy casts like struct to pointer.
#define RXAssertEquals(_actual, _expected) {\
__typeof__(_actual) __actual = (_actual), __expected = (__typeof__(_actual))(_expected);\
if(![RXAssertionHelper compareValue: &__actual withValue: &__expected ofObjCType: @encode(__typeof__(_actual))]) {\
STFail(@"%s has value %@, not expected value %@.", #_actual, [RXAssertionHelper descriptionForValue: &__actual ofObjCType: @encode(__typeof__(_actual))], [RXAssertionHelper descriptionForValue: &__expected ofObjCType: @encode(__typeof__(_actual))]);\
}\
}
#define RXAssertNotEquals(_actual, _expected) {\
__typeof__(_actual) __actual = (_actual), __expected = (__typeof__(_actual))(_expected);\
if([RXAssertionHelper compareValue: &__actual withValue: &__expected ofObjCType: @encode(__typeof__(_actual))]) {\
STFail(@"%s has unexpected value %@.", #_actual, [RXAssertionHelper descriptionForValue: &__actual ofObjCType: @encode(__typeof__(_actual))]);\
}\
}

#define RXAssertNil(_thing) {\
__typeof__(_thing) __thing = (_thing);\
STAssertNil(__thing, @"%s was unexpectedly %@, not nil.", #_thing, __thing);\
}
#define RXAssertNotNil(_thing) STAssertNotNil(_thing, @"%s was unexpectedly nil.", #_thing)


#define RXUnionCast(x, toType) (((union{__typeof__(x) a; toType b;})x).b)
#define RXRound(value, place) (round((value) / (place)) * (place))


typedef BOOL (*RXAssertionHelperComparisonFunction)(const void *aRef, const void *bRef);
typedef NSString *(*RXAssertionHelperDescriptionFunction)(const void *ref);

@interface RXAssertionHelper : NSObject

+(void)registerComparisonFunction:(RXAssertionHelperComparisonFunction)comparator forObjCType:(const char *)type;
+(BOOL)compareValue:(const void *)aRef withValue:(const void *)bRef ofObjCType:(const char *)type;

+(void)registerDescriptionFunction:(RXAssertionHelperDescriptionFunction)descriptor forObjCType:(const char *)type;
+(NSString *)descriptionForValue:(const void *)ref ofObjCType:(const char *)type;

+(double)floatingPointComparisonAccuracy;
+(void)setFloatingPointComparisonAccuracy:(double)epsilon;

@end
197 changes: 197 additions & 0 deletions RXAssertions.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,197 @@
// RXAssertions.m
// Created by Rob Rix on 2009-08-20
// Copyright 2009 Decimus Software, Inc.

#import <math.h>
#import "RXAssertions.h"

static NSMutableDictionary *RXAssertionHelperComparisonFunctions = nil;
static NSMutableDictionary *RXAssertionHelperDescriptionFunctions = nil;

static double RXAssertionHelperFloatingPointComparisonAccuracy = 0.0;

BOOL RXAssertionHelperInt8Comparison(const void *a, const void *b) {
return (*(RXUnionCast(a, const uint8_t *))) == (*(RXUnionCast(b, const uint8_t *)));
}

BOOL RXAssertionHelperInt16Comparison(const void *a, const void *b) {
return (*(RXUnionCast(a, const uint16_t *))) == (*(RXUnionCast(b, const uint16_t *)));
}

BOOL RXAssertionHelperInt32Comparison(const void *a, const void *b) {
return (*(RXUnionCast(a, const uint32_t *))) == (*(RXUnionCast(b, const uint32_t *)));
}

BOOL RXAssertionHelperInt64Comparison(const void *a, const void *b) {
return (*(RXUnionCast(a, const uint64_t *))) == (*(RXUnionCast(b, const uint64_t *)));
}

BOOL RXAssertionHelperFloatComparison(const void *a, const void *b) {
double _a = *RXUnionCast(a, const float *), _b = *RXUnionCast(b, const float *);
return isless(MAX(_a, _b) - MIN(_a, _b), RXAssertionHelperFloatingPointComparisonAccuracy);
}

BOOL RXAssertionHelperDoubleComparison(const void *a, const void *b) {
double _a = *RXUnionCast(a, const double *), _b = *RXUnionCast(b, const double *);
return isless(MAX(_a, _b) - MIN(_a, _b), RXAssertionHelperFloatingPointComparisonAccuracy);
}

BOOL RXAssertionHelperObjectComparison(const void *a, const void *b) {
const id _a = *RXUnionCast(a, const id *), _b = *RXUnionCast(b, const id *);
return (_a == _b) || [_a isEqual: _b];
}

BOOL RXAssertionHelperNSPointComparison(const void *a, const void *b) {
return NSEqualPoints(*RXUnionCast(a, const NSPoint *), *RXUnionCast(b, const NSPoint *));
}


NSString *RXAssertionHelperHexadecimalDescription(const void *ref) {
return [NSString stringWithFormat: @"%x", *RXUnionCast(ref, const void **)];
}

NSString *RXAssertionHelperInt8Description(const void *ref) {
return [NSString stringWithFormat: @"%d", *RXUnionCast(ref, const int8_t *)];
}

NSString *RXAssertionHelperUInt8Description(const void *ref) {
return [NSString stringWithFormat: @"%u", *RXUnionCast(ref, const uint8_t *)];
}

NSString *RXAssertionHelperInt16Description(const void *ref) {
return [NSString stringWithFormat: @"%d", *RXUnionCast(ref, const int16_t *)];
}

NSString *RXAssertionHelperUInt16Description(const void *ref) {
return [NSString stringWithFormat: @"%u", *RXUnionCast(ref, const uint16_t *)];
}

NSString *RXAssertionHelperInt32Description(const void *ref) {
return [NSString stringWithFormat: @"%d", *RXUnionCast(ref, const int32_t *)];
}

NSString *RXAssertionHelperUInt32Description(const void *ref) {
return [NSString stringWithFormat: @"%u", *RXUnionCast(ref, const uint32_t *)];
}

NSString *RXAssertionHelperInt64Description(const void *ref) {
return [NSString stringWithFormat: @"%d", *RXUnionCast(ref, const int64_t *)];
}

NSString *RXAssertionHelperUInt64Description(const void *ref) {
return [NSString stringWithFormat: @"%u", *RXUnionCast(ref, const uint64_t *)];
}

NSString *RXAssertionHelperFloatDescription(const void *ref) {
return [NSString stringWithFormat: @"%f", *RXUnionCast(ref, const float *)];
}

NSString *RXAssertionHelperDoubleDescription(const void *ref) {
return [NSString stringWithFormat: @"%f", *RXUnionCast(ref, const double *)];
}

NSString *RXAssertionHelperObjectDescription(const void *ref) {
return [NSString stringWithFormat: @"%@", *RXUnionCast(ref, const id *)];
}

NSString *RXAssertionHelperNSPointDescription(const void *ref) {
return NSStringFromPoint(*RXUnionCast(ref, const NSPoint *));
}


@implementation RXAssertionHelper

+(void)initialize {
if(!RXAssertionHelperComparisonFunctions) {
RXAssertionHelperComparisonFunctions = [[NSMutableDictionary alloc] init];
}
if(!RXAssertionHelperDescriptionFunctions) {
RXAssertionHelperDescriptionFunctions = [[NSMutableDictionary alloc] init];
}

#ifdef __LP64__
[self registerComparisonFunction: RXAssertionHelperInt64Comparison forObjCType: @encode(void *)];
#else
[self registerComparisonFunction: RXAssertionHelperInt32Comparison forObjCType: @encode(void *)];
#endif
[self registerComparisonFunction: RXAssertionHelperInt8Comparison forObjCType: @encode(int8_t)];
[self registerComparisonFunction: RXAssertionHelperInt8Comparison forObjCType: @encode(uint8_t)];
[self registerComparisonFunction: RXAssertionHelperInt16Comparison forObjCType: @encode(int16_t)];
[self registerComparisonFunction: RXAssertionHelperInt16Comparison forObjCType: @encode(uint16_t)];
[self registerComparisonFunction: RXAssertionHelperInt32Comparison forObjCType: @encode(int32_t)];
[self registerComparisonFunction: RXAssertionHelperInt32Comparison forObjCType: @encode(uint32_t)];
[self registerComparisonFunction: RXAssertionHelperInt64Comparison forObjCType: @encode(int64_t)];
[self registerComparisonFunction: RXAssertionHelperInt64Comparison forObjCType: @encode(uint64_t)];
[self registerComparisonFunction: RXAssertionHelperFloatComparison forObjCType: @encode(float)];
[self registerComparisonFunction: RXAssertionHelperDoubleComparison forObjCType: @encode(double)];
[self registerComparisonFunction: RXAssertionHelperObjectComparison forObjCType: @encode(id)];
[self registerComparisonFunction: RXAssertionHelperObjectComparison forObjCType: @encode(Class)];
[self registerComparisonFunction: RXAssertionHelperNSPointComparison forObjCType: @encode(NSPoint)];
[self registerComparisonFunction: RXAssertionHelperNSPointComparison forObjCType: @encode(CGPoint)];

[self registerDescriptionFunction: RXAssertionHelperHexadecimalDescription forObjCType: @encode(void *)];
[self registerDescriptionFunction: RXAssertionHelperInt8Description forObjCType: @encode(int8_t)];
[self registerDescriptionFunction: RXAssertionHelperUInt8Description forObjCType: @encode(uint8_t)];
[self registerDescriptionFunction: RXAssertionHelperInt16Description forObjCType: @encode(int16_t)];
[self registerDescriptionFunction: RXAssertionHelperUInt16Description forObjCType: @encode(uint16_t)];
[self registerDescriptionFunction: RXAssertionHelperInt32Description forObjCType: @encode(int32_t)];
[self registerDescriptionFunction: RXAssertionHelperUInt32Description forObjCType: @encode(uint32_t)];
[self registerDescriptionFunction: RXAssertionHelperInt64Description forObjCType: @encode(int64_t)];
[self registerDescriptionFunction: RXAssertionHelperUInt64Description forObjCType: @encode(uint64_t)];
[self registerDescriptionFunction: RXAssertionHelperFloatDescription forObjCType: @encode(float)];
[self registerDescriptionFunction: RXAssertionHelperDoubleDescription forObjCType: @encode(double)];
[self registerDescriptionFunction: RXAssertionHelperObjectDescription forObjCType: @encode(id)];
[self registerDescriptionFunction: RXAssertionHelperObjectDescription forObjCType: @encode(Class)];
[self registerDescriptionFunction: RXAssertionHelperNSPointDescription forObjCType: @encode(NSPoint)];
[self registerDescriptionFunction: RXAssertionHelperNSPointDescription forObjCType: @encode(CGPoint)];
}


+(NSString *)keyForObjCType:(const char *)type {
return [NSString stringWithFormat: @"%s", type];
}

+(RXAssertionHelperComparisonFunction)comparisonFunctionForObjCType:(const char *)type {
return [[RXAssertionHelperComparisonFunctions objectForKey: [self keyForObjCType: type]] pointerValue];
}

+(void)registerComparisonFunction:(RXAssertionHelperComparisonFunction)comparator forObjCType:(const char *)type {
[RXAssertionHelperComparisonFunctions setObject: [NSValue valueWithPointer: comparator] forKey: [self keyForObjCType: type]];
}

+(BOOL)compareValue:(const void *)aRef withValue:(const void *)bRef ofObjCType:(const char *)type {
RXAssertionHelperComparisonFunction function =
[self comparisonFunctionForObjCType: type]
?:
#ifdef __LP64__
RXAssertionHelperInt64Comparison;
#else
RXAssertionHelperInt32Comparison;
#endif
return function(aRef, bRef);
}


+(RXAssertionHelperDescriptionFunction)descriptionFunctionForObjCType:(const char *)type {
return [[RXAssertionHelperDescriptionFunctions objectForKey: [self keyForObjCType: type]] pointerValue];
}

+(void)registerDescriptionFunction:(RXAssertionHelperDescriptionFunction)descriptor forObjCType:(const char *)type {
[RXAssertionHelperDescriptionFunctions setObject: [NSValue valueWithPointer: descriptor] forKey: [self keyForObjCType: type]];
}

+(NSString *)descriptionForValue:(const void *)ref ofObjCType:(const char *)type {
RXAssertionHelperDescriptionFunction function = [self descriptionFunctionForObjCType: type] ?: RXAssertionHelperHexadecimalDescription;
return function(ref);
}


+(double)floatingPointComparisonAccuracy {
return RXAssertionHelperFloatingPointComparisonAccuracy;
}

+(void)setFloatingPointComparisonAccuracy:(double)epsilon {
RXAssertionHelperFloatingPointComparisonAccuracy = epsilon;
}

@end
7 changes: 7 additions & 0 deletions RXAssertions.pch
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
// RXAssertions_Prefix.pch
// Created by Rob Rix on 2009-09-17
// Copyright 2009 Monochrome Industries

#ifdef __OBJC__
#import <Cocoa/Cocoa.h>
#endif
Loading

0 comments on commit b28ee04

Please sign in to comment.