5 // Created by Wincent Colaiuta on 09 June 2005.
7 // Copyright 2005-2007 Wincent Colaiuta.
8 // This program is free software: you can redistribute it and/or modify
9 // it under the terms of the GNU General Public License as published by
10 // the Free Software Foundation, either version 3 of the License, or
11 // (at your option) any later version.
13 // This program is distributed in the hope that it will be useful,
14 // but WITHOUT ANY WARRANTY; without even the implied warranty of
15 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 // GNU General Public License for more details.
18 // You should have received a copy of the GNU General Public License
19 // along with this program. If not, see <http://www.gnu.org/licenses/>.
22 #import <Foundation/Foundation.h>
24 #import <objc/objc-class.h>
28 /*! Type macros missing from objc-class.h at the time of writing; definition taken from docs file:///Developer/ADC%20Reference%20Library/documentation/Cocoa/Conceptual/ObjectiveC/index.html */
35 /*! Type macros missing from objc-class.h at the time of writing; definition taken from docs file:///Developer/ADC%20Reference%20Library/documentation/Cocoa/Conceptual/ObjectiveC/index.html */
36 #define _C_ULNGLNG 'Q'
42 /*! Type macros missing from objc-class.h at the time of writing; definition taken from docs file:///Developer/ADC%20Reference%20Library/documentation/Cocoa/Conceptual/ObjectiveC/index.html */
47 /*! Macro that compares two scalar values, \p a and \p b, using standard relational operators and returns NSOrderedSame, NSOrderedAscending or NSOrderedDescending. */
48 #define WO_COMPARE_SCALARS(a, b) \
49 ((a) == (b) ? NSOrderedSame : ((a) < (b) ? NSOrderedAscending : NSOrderedDescending))
51 /*! This category adds unit testing methods to the NSValue class. It provides methods for comparing NSValue objects for equality/non-equality and ordering.
53 The base NSValue class adopts a stricter view of equality that makes it hard to compare numeric scalar values (char, int, short, long, long long, unsigned char, unsigned int, unsigned short, unsigned long, unsigned long long, float, double and C99 _Bool) because those values must be of the same type or class. For example, an NSValue that contains an int (10) and another that contains a long (10) would be considered unequal according the default implementation of the isEqualTo: method because they are not of the same type. Likewise an NSValue containing an NSMutableString (@"string") and another containing an NSString (@"string") would also be considered unequal because they are of different classes.
55 This category adds a number of high-level methods for use in unit testing that implement a more flexible comparison method. Any numeric scalar value can be compared to any other scalar value and the values will be appropriately cast before comparison. In cases where GCC would issue a warning (for example, when comparing a signed and an unsigned type) a warning will be printed to the console (and will appear in the Xcode build results).
57 Any object can be compared to any other object for equality provided that it implements the isEqualTo: method. If it does not implement the method then the WOTest_testIsEqualToValue: method will return NO. If both ids are nil then the method returns YES.
59 Objects can be compared to one another for ordering provided that they are of the same class (or at least have a super-subclass relationship) and at least one of the objects implement the compare: method. If neither of these conditions are true then an NSInvalidArgument exception will be raised.
61 Class objects, method selectors (SEL), void types, character strings (char *), arrays, structs, unions and pointers have no special behaviour implemented in this category. If you try to compare them the standard isEqualTo: NSValue method will be used and testing the ordering will raise an NSInvalidArgument exception. */
62 @interface NSValue (WOTest)
65 #pragma mark Value creation methods
67 + (NSValue *)WOTest_valueWithChar:(char)aChar;
69 + (NSValue *)WOTest_valueWithInt:(int)anInt;
71 + (NSValue *)WOTest_valueWithShort:(short)aShort;
73 + (NSValue *)WOTest_valueWithLong:(long)aLong;
75 + (NSValue *)WOTest_valueWithLongLong:(long long)aLongLong;
77 + (NSValue *)WOTest_valueWithUnsignedChar:(unsigned char)anUnsignedChar;
79 + (NSValue *)WOTest_valueWithUnsignedInt:(unsigned int)anUnsignedInt;
81 + (NSValue *)WOTest_valueWithUnsignedShort:(unsigned short)anUnsignedShort;
83 + (NSValue *)WOTest_valueWithUnsignedLong:(unsigned long)anUnsignedLong;
85 + (NSValue *)WOTest_valueWithUnsignedLongLong:(unsigned long long)anUnsignedLongLong;
87 + (NSValue *)WOTest_valueWithFloat:(float)aFloat;
89 + (NSValue *)WOTest_valueWithDouble:(double)aDouble;
91 + (NSValue *)WOTest_valueWithC99Bool:(_Bool)aC99Bool;
93 + (NSValue *)WOTest_valueWithConstantCharacterString:(const char *)aConstantCharString;
95 + (NSValue *)WOTest_valueWithCharacterString:(char *)aCharacterString;
97 /*! Unlike Cocoa's valueWithNonretainedObject method, which is equivalent to calling:
100 NSValue *theValue = [NSValue value:&anObject withObjCType:@encode(void *)];
103 This method is equivalent to invoking:
106 NSValue *theValue = [NSValue value:&anObject withObjCType:@encode(id)];
109 The object is not retained.
112 + (NSValue *)WOTest_valueWithObject:(id)anObject;
114 + (NSValue *)WOTest_valueWithClass:(Class)aClass;
116 + (NSValue *)WOTest_valueWithSelector:(SEL)aSelector;
119 #pragma mark Parsing type strings
122 \name Parsing type strings
126 /*! Used to determine the maximum number of bytes needed to store the type represented by \p typeString when it is embedded inside a composite type (a struct, array or union). It is possible that compiler pragmas or flags may lead to data being aligned differently and packed in a more compact form, but this method is guaranteed to return the maximum amount of space that would be required to store the type under the least-compact alignment conditions. The alignments and embedding rules are taken from "The alignments taken from Apple's "Mac OS X ABI Function Call Guide". Throws an exception if \p typeString is nil. */
127 + (size_t)WOTest_maximumEmbeddedSizeForType:(NSString *)typeString;
129 /*! This method returns the minimum buffer size required to safely store an object of the type represented by \p typeString. Because it is impossible to know if any compiler pragmas or flags were used to change the default alignment behaviour of composite types (structs, arrays, unions), this method bases its calculations on the space that would be required if the least-packed alignment were chosen. As such the values returned by this function may be greater than or equal to those returned by the sizeof compiler directive, but never less than. Throws an exception if \p typeString is nil. */
130 + (size_t)WOTest_sizeForType:(NSString *)typeString;
132 /*! Returns YES if \p typeString contains a numeric scalar value (char, int, short, long, long long, unsigned char, unsigned int, unsigned short, unsigned long, unsigned long long, float, double, C99 _Bool). Returns NO if the receiver contains any other type, object or pointer (id, Class, SEL, void, char *, as well as arrays, structures and pointers). */
133 + (BOOL)WOTest_typeIsNumericScalar:(NSString *)typeString;
135 /*! Returns YES if \p typeString represents a compound type (struct, union or array). */
136 + (BOOL)WOTest_typeIsCompound:(NSString *)typeString;
138 + (BOOL)WOTest_typeIsChar:(NSString *)typeString;
140 + (BOOL)WOTest_typeIsInt:(NSString *)typeString;
142 + (BOOL)WOTest_typeIsShort:(NSString *)typeString;
144 + (BOOL)WOTest_typeIsLong:(NSString *)typeString;
146 + (BOOL)WOTest_typeIsLongLong:(NSString *)typeString;
148 + (BOOL)WOTest_typeIsUnsignedChar:(NSString *)typeString;
150 + (BOOL)WOTest_typeIsUnsignedInt:(NSString *)typeString;
152 + (BOOL)WOTest_typeIsUnsignedShort:(NSString *)typeString;
154 + (BOOL)WOTest_typeIsUnsignedLong:(NSString *)typeString;
156 + (BOOL)WOTest_typeIsUnsignedLongLong:(NSString *)typeString;
158 + (BOOL)WOTest_typeIsFloat:(NSString *)typeString;
160 + (BOOL)WOTest_typeIsDouble:(NSString *)typeString;
162 + (BOOL)WOTest_typeIsC99Bool:(NSString *)typeString;
164 + (BOOL)WOTest_typeIsVoid:(NSString *)typeString;
166 + (BOOL)WOTest_typeIsConstantCharacterString:(NSString *)typeString;
168 + (BOOL)WOTest_typeIsCharacterString:(NSString *)typeString;
170 + (BOOL)WOTest_typeIsObject:(NSString *)typeString;
172 + (BOOL)WOTest_typeIsClass:(NSString *)typeString;
174 + (BOOL)WOTest_typeIsSelector:(NSString *)typeString;
176 + (BOOL)WOTest_typeIsPointerToVoid:(NSString *)typeString;
178 + (BOOL)WOTest_typeIsPointer:(NSString *)typeString;
180 + (BOOL)WOTest_typeIsArray:(NSString *)typeString;
182 + (BOOL)WOTest_typeIsStruct:(NSString *)typeString;
184 + (BOOL)WOTest_typeIsUnion:(NSString *)typeString;
186 + (BOOL)WOTest_typeIsBitfield:(NSString *)typeString;
188 + (BOOL)WOTest_typeIsUnknown:(NSString *)typeString;
193 #pragma mark High-level test methods
196 \name High-level test methods
200 /*! Compares the receiver with \p aValue for equality and returns YES if equal, NO otherwise. When both the receiver and \p aValue are objects tries to send an isEqualTo: message to the receiver; if the receiver does not implement that selector then the default NSValue isEqualToValue: method is invoked. When both the receiver and \p aValue represent numeric scalars casting is performed as already described. There is one special case where an object to numeric scalar comparison is permitted and that is when the numeric value is both the same size and value as a nil pointer (in other words an int with value 0), such as the relatively common case in which the caller might try to compare an object to nil (in this case the C preprocessor will have substituted "0" for "nil" which means that the NSValue object will record it as an representing an integer rather than an id). This special case is necessary so that test macros such as the following work as expected:
203 WO_TEST_EQUAL(nil, theObject); // looks like comparing an id with an id
204 WO_TEST_NOT_EQUAL(otherObject, nil); // looks like comparing an id with an id
206 // which are equivalent to:
207 WO_TEST_EQUAL(0, theObject); // actually comparing an int with an id
208 WO_TEST_NOT_EQUAL(otherObject, 0); // actually comparing an id with an int
211 Note that the following example would work even without this special handling because in this case there is enough information present at compile time to prevent the nil value from being interpreted as an int:
214 NSString *aString = nil;
215 WO_TEST_EQUAL(aString, otherString); // comparing an id with and id
218 Special case handling notwithstanding, attempting to compare a non-zero int with an object will raise an exception:
221 WO_TEST_EQUAL(25, aString); // raises
224 In all other cases the default NSValue isEqualToValue: method is used as a fallback.
228 <td>Compare value:</td>
233 <td>numeric scalar</td>
234 <td>numeric scalar</td>
235 <td>automated casting takes place prior to comparison, warning printed to console at runtime if risky casts are required (for example signed and unsigned) </td>
240 <td>compared using isEqual: if right-handle value implements that method, otherwise default NSValue isEqualToValue: behaviour used </td>
244 <td>numeric scalar</td>
245 <td>comparison allowed if and only if the numeric scalar value is equivalent to "nil", otherwise throws exception </td>
248 <td>numeric scalar</td>
250 <td>comparison allowed if and only if the numeric scalar value is equivalent to "nil", otherwise throws exception </td>
254 <td>numeric scalar </td>
255 <td>comparison allowed if and only if the numeric scalar value is equivalent to "nil", otherwise throws exception </td>
258 <td>numeric scalar </td>
260 <td>comparison allowed if any only if the numeric scalar value is equivalent to "nil", otherwise throws exception </td>
265 <td>if possible creates NSString representations of the two values and compares them; if comparison not possible falls back to default NSValue isEqualToValue: behaviour</td>
270 <td>if possible creates NSString representations of the two values and compares them; if comparison not possible falls back to default NSValue isEqualToValue: behaviour</td>
273 <td>const char* </td>
275 <td>if possible creates NSString representations of the two values and compares them; if comparison not possible falls back to default NSValue isEqualToValue: behaviour</td>
279 <td>const char* </td>
280 <td>if possible creates NSString representations of the two values and compares them; if comparison not possible falls back to default NSValue isEqualToValue: behaviour</td>
285 - (BOOL)WOTest_testIsEqualToValue:(NSValue *)aValue;
287 // TODO: write tests that make use of these methods? so far only use the isEqual variant
288 - (BOOL)WOTest_testIsGreaterThanValue:(NSValue *)aValue;
290 - (BOOL)WOTest_testIsLessThanValue:(NSValue *)aValue;
292 - (BOOL)WOTest_testIsNotGreaterThanValue:(NSValue *)aValue;
294 - (BOOL)WOTest_testIsNotLessThanValue:(NSValue *)aValue;
296 /*! The table below indicates the warnings that GCC issues when trying to compare numeric scalars of different types. "-" indicates that no warning is issued and the compiler performs an implicit cast. "W" indicates that the compiler issues a warning about signed/unsigned comparison; in this case WOTest performs an explicit cast and prints a warning to the console (visible in the Xcode build results window) at runtime.
306 <td>unsigned char </td>
307 <td>unsigned int</td>
308 <td>unsigned short </td>
309 <td>unsigned long </td>
310 <td>unsigned long long </td>
396 <td>unsigned char </td>
412 <td>unsigned int </td>
428 <td>unsigned short </td>
444 <td>unsigned long </td>
460 <td>unsigned long long </td>
525 Table corresponds to the warnings produced by the version of GCC 4.0 that ships with Apple's Xcode 2.1, "powerpc-apple-darwin8-gcc-4.0.0 (GCC) 4.0.0 (Apple Computer, Inc. build 5026)". */
526 - (NSComparisonResult)WOTest_compare:(NSValue *)aValue;
531 #pragma mark Utility methods
533 //! \name Utility methods
536 //! Returns the buffer size necessary to hold the contents of the receiver. For use in conjunction with the getValue method.
537 - (size_t)WOTest_bufferSize;
539 //! Prints a warning to the console about a signed-to-unsigned comparison together with information about the last known location (file and line).
540 - (void)WOTest_printSignCompareWarning:(NSString *)warning;
545 #pragma mark Convenience methods
548 \name Convenience methods
552 /*! Returns the Objective-C type of the receiver as an NSString. */
553 - (NSString *)WOTest_objCTypeString;
555 /*! Returns a human-readable description of the receiver. */
556 - (NSString *)WOTest_description;
561 #pragma mark Identifying generic types
564 \name Identifying generic types
568 /*! Returns YES if the receiver contains a numeric scalar value (char, int, short, long, long long, unsigned char, unsigned int, unsigned short, unsigned long, unsigned long long, float, double, C99 _Bool). Returns NO if the receiver contains any other type, object or pointer (id, Class, SEL, void, char *, as well as arrays, structures and pointers). */
569 - (BOOL)WOTest_isNumericScalar;
571 - (BOOL)WOTest_isPointer;
573 - (BOOL)WOTest_isArray;
575 /*! Returns the count of items in the array stored by the receiver. Raises an exception if the receiver does not store an array. */
576 - (unsigned)WOTest_arrayCount;
578 /*! Returns the type string of the elements in the array stored by the receiver. Raises an exception if the receiver does not store an array. */
579 - (NSString *)WOTest_arrayType;
581 - (BOOL)WOTest_isStruct;
583 - (BOOL)WOTest_isUnion;
585 - (BOOL)WOTest_isBitfield;
587 /*! True if the receiver contains a value of type "unknown" (indicated by "?"). Function pointers, for example, are encoded with this type by the \@encode() compiler directive. */
588 - (BOOL)WOTest_isUnknown;
593 #pragma mark Identifying and retrieving specific types
596 \name Identifying and retrieving specific types
600 /*! Returns YES if the receiver contains a char. */
601 - (BOOL)WOTest_isChar;
603 /*! Returns YES if the receiver contains an int. */
604 - (BOOL)WOTest_isInt;
606 /*! Returns YES if the receiver contains a short. */
607 - (BOOL)WOTest_isShort;
609 /*! Returns YES if the receiver contains a long. */
610 - (BOOL)WOTest_isLong;
612 /*! Returns YES if the receiver contains a long long. */
613 - (BOOL)WOTest_isLongLong;
615 /*! Returns YES if the receiver contains an unsigned char. */
616 - (BOOL)WOTest_isUnsignedChar;
618 /*! Returns YES if the receiver contains an unsigned int. */
619 - (BOOL)WOTest_isUnsignedInt;
621 /*! Returns YES if the receiver contains an unsigned short. */
622 - (BOOL)WOTest_isUnsignedShort;
624 /*! Returns YES if the receiver contains an unsigned long. */
625 - (BOOL)WOTest_isUnsignedLong;
627 /*! Returns YES if the receiver contains an unsigned long long. */
628 - (BOOL)WOTest_isUnsignedLongLong;
630 /*! Returns YES if the receiver contains a float. */
631 - (BOOL)WOTest_isFloat;
633 /*! Returns YES if the receiver contains a double. */
634 - (BOOL)WOTest_isDouble;
636 /*! Returns YES if the receiver contains a C99 _Bool. */
637 - (BOOL)WOTest_isC99Bool;
639 /*! Returns YES if the receiver contains a void. */
640 - (BOOL)WOTest_isVoid;
642 /*! Returns YES if the receiver contains a constant character string (const char *). */
643 - (BOOL)WOTest_isConstantCharacterString;
645 /*! Returns YES if the receiver contains a character string (char *). */
646 - (BOOL)WOTest_isCharacterString;
648 /*! Returns YES if the receiver contains an (id or statically typed) object. */
649 - (BOOL)WOTest_isObject;
651 /*! Returns YES if the receiver contains a Class object. */
652 - (BOOL)WOTest_isClass;
654 /*! Returns YES if the receiver contains a method selector (SEL). */
655 - (BOOL)WOTest_isSelector;
657 /*! Returns YES if the receiver contains a pointer to void. */
658 - (BOOL)WOTest_isPointerToVoid;
660 /*! If the receiver was created to hold a char-sized data item, returns that item as a char. Otherwise, the result is undefined. */
661 - (char)WOTest_charValue;
663 /*! If the receiver was created to hold an int-sized data item, returns that item as an int. Otherwise, the result is undefined. */
664 - (int)WOTest_intValue;
666 /*! If the receiver was created to hold a short-sized data item, returns that item as a short. Otherwise, the result is undefined. */
667 - (short)WOTest_shortValue;
669 /*! If the receiver was created to hold a long-sized data item, returns that item as a long. Otherwise, the result is undefined. */
670 - (long)WOTest_longValue;
672 /*! If the receiver was created to hold a long long-sized data item, returns that item as a long long. Otherwise, the result is undefined. */
673 - (long long)WOTest_longLongValue;
675 /*! If the receiver was created to hold an unsigned char-sized data item, returns that item as an unsigned char. Otherwise, the result is undefined. */
676 - (unsigned char)WOTest_unsignedCharValue;
678 /*! If the receiver was created to hold an unsigned int-sized data item, returns that item as an unsigned int. Otherwise, the result is undefined. */
679 - (unsigned int)WOTest_unsignedIntValue;
681 /*! If the receiver was created to hold an unsigned short-sized data item, returns that item as an unsigned short. Otherwise, the result is undefined. */
682 - (unsigned short)WOTest_unsignedShortValue;
684 /*! If the receiver was created to hold an unsigned long-sized data item, returns that item as an unsigned long. Otherwise, the result is undefined. */
685 - (unsigned long)WOTest_unsignedLongValue;
687 /*! If the receiver was created to hold an unsigned long long-sized data item, returns that item as an unsigned long long. Otherwise, the result is undefined. */
688 - (unsigned long long)WOTest_unsignedLongLongValue;
690 /*! If the receiver was created to hold a float-sized data item, returns that item as a float. Otherwise, the result is undefined. */
691 - (float)WOTest_floatValue;
693 /*! If the receiver was created to hold a double-sized data item, returns that item as a double. Otherwise, the result is undefined. */
694 - (double)WOTest_doubleValue;
696 /*! If the receiver was created to hold a C99 _Bool-sized data item, returns that item as a C99 _Bool. Otherwise, the result is undefined. */
697 - (_Bool)WOTest_C99BoolValue;
699 /*! If the receiver was created to hold a constant character string-sized data item, returns that item as a constant character string. Otherwise, the result is undefined. */
700 - (const char *)WOTest_constantCharacterStringValue;
702 /*! If the receiver was created to hold a character string-sized data item, returns that item as a character string. Otherwise, the result is undefined. */
703 - (char *)WOTest_characterStringValue;
705 /*! If the receiver was created to hold an id-sized data item, returns that item as an id. Otherwise, the result is undefined. */
706 - (id)WOTest_objectValue;
708 /*! If the receiver was created to hold a Class-sized data item, returns that item as a Class. Otherwise, the result is undefined. */
709 - (Class)WOTest_classValue;
711 /*! If the receiver was created to hold a SEL-sized data item, returns that item as a SEL. Otherwise, the result is undefined. */
712 - (SEL)WOTest_selectorValue;
714 - (void *)WOTest_pointerToVoidValue;
716 /*! Returns YES if the receiver is an array of characters (of type char). This method looks for encoding strings of the form "[1c]", "[2c]", "[3c]" and so forth. There are no guarantees that such arrays are null-terminated. Such encodings are produced when passing constant strings to the WOTest macros as illustrated below:
719 WO_TEST_NOT_EQUAL("foo", "bar");
722 When encoded as NSValues the two constant strings in the example above would be encoded as having type "[4c]" (3 chars plus a terminating byte). */
723 - (BOOL)WOTest_isCharArray;
725 /*! If WOTest_isCharacterString returns YES then this method attempts to return an NSString representation of the content of the receiver; returns nil if no representation could be produced. If WOTest_isCharArray returns YES then attempts to do the same, returning nil if not possible. */
726 - (NSString *)WOTest_stringValue;
731 #pragma mark Low-level test methods
734 \name Low-level test methods
738 - (NSComparisonResult)WOTest_compareWithChar:(char)other;
740 - (NSComparisonResult)WOTest_compareWithInt:(int)other;
742 - (NSComparisonResult)WOTest_compareWithShort:(short)other;
744 - (NSComparisonResult)WOTest_compareWithLong:(long)other;
746 - (NSComparisonResult)WOTest_compareWithLongLong:(long long)other;
748 - (NSComparisonResult)WOTest_compareWithUnsignedChar:(unsigned char)other;
750 - (NSComparisonResult)WOTest_compareWithUnsignedInt:(unsigned int)other;
752 - (NSComparisonResult)WOTest_compareWithUnsignedShort:(unsigned short)other;
754 - (NSComparisonResult)WOTest_compareWithUnsignedLong:(unsigned long)other;
756 - (NSComparisonResult)WOTest_compareWithUnsignedLongLong:(unsigned long long)other;
758 - (NSComparisonResult)WOTest_compareWithFloat:(float)other;
760 - (NSComparisonResult)WOTest_compareWithDouble:(double)other;
762 - (NSComparisonResult)WOTest_compareWithC99Bool:(_Bool)other;